diff -u --recursive --new-file v2.1.124/linux/CREDITS linux/CREDITS
--- v2.1.124/linux/CREDITS	Mon Oct  5 13:13:34 1998
+++ linux/CREDITS	Thu Oct  8 17:25:16 1998
@@ -93,7 +93,7 @@
 S: Canada
 
 N: Ralf Baechle
-E: ralf@gnu.ai.mit.edu
+E: ralf@gnu.org
 P: 1024/AF7B30C1 CF 97 C2 CC 6D AE A7 FE  C8 BA 9C FC 88 DE 32 C3
 D: Linux/MIPS port
 D: Linux/68k hacker
@@ -566,15 +566,11 @@
 S: USA
 
 N: Jim Freeman
-E: jfree@caldera.com
+E: jfree@sovereign.org
 W: http://www.sovereign.org/
 D: Initial GPL'd Frame Relay driver
 D: Dynamic PPP devices
 D: Sundry modularizations (PPP, IPX, ...) and fixes
-S: Caldera, Inc.
-S: 240 West Center Street
-S: Orem, Utah 84059-1920
-S: USA
 
 N: Bob Frey
 E: bobf@advansys.com
@@ -1383,7 +1379,7 @@
 D: improved memory detection code.
 
 N: Vojtech Pavlik
-E: vojtech@atrey.karlin.mff.cuni.cz
+E: vojtech@ucw.cz
 D: Joystick driver
 D: arcnet-hardware readme
 D: Minor ARCnet hacking
diff -u --recursive --new-file v2.1.124/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.124/linux/Documentation/Configure.help	Mon Oct  5 13:13:35 1998
+++ linux/Documentation/Configure.help	Thu Oct  8 17:25:16 1998
@@ -225,7 +225,8 @@
   If you say Y here, you will use the full-featured IDE driver to
   control up to four IDE interfaces, each being able to serve a
   "master" and a "slave" device, for a total of up to eight IDE
-  disk/cdrom/tape/floppy drives.
+  disk/cdrom/tape/floppy drives.  People with SCSI-only systems
+  should say 'N' here.
 
   Useful information about large (>540MB) IDE disks, sound card IDE
   ports, module support, and other topics, is contained in
@@ -256,6 +257,8 @@
   more detailed information, read the Disk-HOWTO, available via FTP
   (user: anonymous) from ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO.
 
+  People with SCSI-only systems can say 'N' here.
+
 Use old disk-only driver on primary interface
 CONFIG_BLK_DEV_HD_IDE
   There are two drivers for MFM/RLL/IDE disks. Most people use just
@@ -270,11 +273,13 @@
   addresses. Normally, just say N here; you will then use the new
   driver for all 4 interfaces.
 
+  People with SCSI-only systems can say 'N' here.
+
 Include IDE/ATA-2 DISK support
 CONFIG_BLK_DEV_IDEDISK
   This will include enhanced support for MFM/RLL/IDE hard disks. If you
   have a MFM/RLL/IDE disk, and there is no special reason to use the
-  old hard disk driver instead, say Y.  
+  old hard disk driver instead, say Y.  If all your disks are SCSI, say 'N'.
 
   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),
@@ -313,7 +318,7 @@
 CONFIG_BLK_DEV_IDETAPE
   If you have an IDE tape drive using the ATAPI protocol, say Y.
   ATAPI is a newer protocol used by IDE tape and CDROM drives, similar
-  to the SCSI protocol.  
+  to the SCSI protocol (but say 'N' if your tape is a SCSI device).
 
   If you say Y here, the tape drive will be identified at boot time
   along with other IDE devices, as "hdb" or "hdc", or something
@@ -363,7 +368,7 @@
   If both this SCSI emulation and native ATAPI support are compiled
   into the kernel, the native support will be used.
 
-  If unsure, say N.
+  People with SCSI-only systems should say 'N' here.  If unsure, say N.
 
 CMD640 chipset bugfix/support
 CONFIG_BLK_DEV_CMD640
@@ -387,7 +392,9 @@
 
   The CMD640 chip is also used on add-in cards by Acculogic, and on
   the "CSA-6400E PCI to IDE controller" that some people have. For
-  details, read Documentation/ide.txt. If unsure, say Y.
+  details, read Documentation/ide.txt. 
+
+  People with SCSI-only systems should say 'N' here.  If unsure, say Y.
 
 CMD640 enhanced support
 CONFIG_BLK_DEV_CMD640_ENHANCED
@@ -405,14 +412,18 @@
   severe data corruption under many conditions.  Say Y here to include
   code which automatically detects and corrects the problem under
   Linux.  This may slow disk throughput by a few percent, but at least
-  things will operate 100% reliably. If unsure, say Y.
+  things will operate 100% reliably. 
+
+  People with SCSI-only systems can say 'N' here.  If unsure, say Y.
 
 Generic PCI IDE chipset support
 CONFIG_BLK_DEV_IDEPCI
   Enable this for PCI systems which use IDE drive(s).
   This option helps the IDE driver to automatically detect and
   configure all PCI-based IDE interfaces in your system.
-  It is safe to say Y to this question.
+
+  People with SCSI-only systems should say 'N' here.   It is safe to
+  say 'Y' to this question.
 
 Generic PCI bus-master DMA support
 CONFIG_BLK_DEV_IDEDMA
@@ -468,6 +479,8 @@
   chipsets.  Most of these also require special kernel boot parameters
   to actually turn on the support at runtime.
 
+  People with SCSI-only systems should say 'N' here. 
+
 Generic 4 drives/port support
 CONFIG_BLK_DEV_4DRIVES
   Certain older chipsets, including the Tekram 690CD, use a
@@ -1479,13 +1492,6 @@
   display that complies with the generic VGA standard. Virtually
   everyone wants that. Say Y.
 
-Use 64KB of VGA video RAM (aka: Maximum VGA Scrollback)
-CONFIG_VGA_GET_64KB
-  Use 64K rather than 32K of video RAM. This doesn't actually work
-  on all "VGA" controllers. If your vga card can do it, then you 
-  can say Y here to get more scrollback buffer (shift-pgup) on VGA
-  consoles.
-
 Video mode selection support
 CONFIG_VIDEO_SELECT
   This enables support for text mode selection on kernel startup. If
@@ -1507,20 +1513,26 @@
   hardware. It represents the frame buffer of some video hardware and
   allows application software to access the graphics hardware through
   a well-defined interface, so the software doesn't need to know
-  anything about the low-level (hardware register) stuff. This works
-  across the different architectures supported by Linux and makes the
-  implementation of application programs easier and more portable; at
-  this point, an X server exists which uses the frame buffer device
-  exclusively.
+  anything about the low-level (hardware register) stuff. 
 
+  Frame buffer devices work identically across the different
+  architectures supported by Linux and make the implementation of
+  application programs easier and more portable; at this point, an X
+  server exists which uses the frame buffer device exclusively.
+  On several non-X86 architectures, the frame buffer device is the
+  only way to use the graphics hardware.
+ 
   The device is accessed through special device nodes, usually located
   in the /dev directory, i.e. /dev/fb*.
 
-  Please read the file Documentation/fb/framebuffer.txt for more
-  information.
-
-  If you want to play with it, say Y here and to the driver for your
-  graphics board, below. If unsure, say N.
+  You need an utility program called fbset to make full use of frame
+  buffer devices. Please read the file
+  Documentation/fb/framebuffer.txt for more information.
+
+  If you want to play with it, say Y here and also to the driver for
+  your graphics board, below. If unsure, say N, unless you are
+  compiling a kernel for a non-X86 architecture, in which case you
+  should say Y.
 
 Acorn VIDC support
 CONFIG_FB_ACORN
@@ -1559,33 +1571,34 @@
 
 Amiga CyberVision support
 CONFIG_FB_CYBER
-  This enables support for the Cybervision 64 graphics card from Phase5.
-  Please note that its use is not all that intuitive (i.e. if you have
-  any questions, be sure to ask!).  Say N unless you have a Cybervision
-  64 or plan to get one before you next recompile the kernel.         
-  Please note that this driver DOES NOT support the Cybervision 64 3D
-  card, as they use incompatible video chips.           
+  This enables support for the Cybervision 64 graphics card from
+  Phase5. Please note that its use is not all that intuitive (i.e. if
+  you have any questions, be sure to ask!). Say N unless you have a
+  Cybervision 64 or plan to get one before you next recompile the
+  kernel. Please note that this driver DOES NOT support the
+  Cybervision 64 3D card, as they use incompatible video chips.
 
 Amiga CyberVision3D support (EXPERIMENTAL)
 CONFIG_FB_VIRGE
-  This enables support for the Cybervision 64/3D graphics card from Phase5.
-  Please note that its use is not all that intuitive (i.e. if you have
-  any questions, be sure to ask!).  Say N unless you have a Cybervision
-  64/3D or plan to get one before you next recompile the kernel.         
-  Please note that this driver DOES NOT support the older Cybervision 64
-  card, as they use incompatible video chips.           
+  This enables support for the Cybervision 64/3D graphics card from
+  Phase5. Please note that its use is not all that intuitive (i.e. if
+  you have any questions, be sure to ask!). Say N unless you have a
+  Cybervision 64/3D or plan to get one before you next recompile the
+  kernel. Please note that this driver DOES NOT support the older
+  Cybervision 64 card, as they use incompatible video chips.
 
 Amiga RetinaZ3 support (EXPERIMENTAL)
 CONFIG_FB_RETINAZ3
-  This enables support for the Retina Z3 graphics card. Say N unless you
-  have a Retina Z3 or plan to get one before you next recompile the kernel.
+  This enables support for the Retina Z3 graphics card. Say N unless
+  you have a Retina Z3 or plan to get one before you next recompile
+  the kernel.
 
 Amiga CLgen driver (EXPERIMENTAL)
 CONFIG_FB_CLGEN
-  This enables support for Cirrus Logic GD542x/543x based boards on Amiga:
-  SD64, Piccolo, Picasso II/II+, Picasso IV, or EGS Spectrum.  Say N
-  unless you have such a graphics board or plan to get one before you next
-  recompile the kernel.
+  This enables support for Cirrus Logic GD542x/543x based boards on
+  Amiga: SD64, Piccolo, Picasso II/II+, Picasso IV, or EGS Spectrum.
+  Say N unless you have such a graphics board or plan to get one
+  before you next recompile the kernel.
 
 Atari native chipset support
 CONFIG_FB_ATARI
@@ -1686,13 +1699,14 @@
 
 Virtual Frame Buffer support (ONLY FOR TESTING!)
 CONFIG_FB_VIRTUAL
-  This is a `virtual' frame buffer device.  It operates on a chunk of
-  unswapable kernel memory instead of on the memory of a graphics board.
-  This means you cannot see any output sent to this frame buffer device,
-  while it does consume precious memory.  The main use of this frame
-  buffer device is testing and debugging the frame buffer subsystem. Do
-  NOT enable it for normal systems! To protect the innocent, it has to
-  be enabled explicitly on boot time using the kernel option `video=vfb:'.
+  This is a `virtual' frame buffer device. It operates on a chunk of
+  unswapable kernel memory instead of on the memory of a graphics
+  board. This means you cannot see any output sent to this frame
+  buffer device, while it does consume precious memory. The main use
+  of this frame buffer device is testing and debugging the frame
+  buffer subsystem. Do NOT enable it for normal systems! To protect
+  the innocent, it has to be enabled explicitly at boot time using the
+  kernel option `video=vfb:'.
 
   This driver is also available as a module ( = code which can be
   inserted and removed from the running kernel whenever you want).
@@ -1709,12 +1723,12 @@
   drivers. Note that they are used for text console output only; they are
   NOT needed for graphical applications.
 
-  If you do not enable this option, the needed low level drivers are
-  automatically enabled, depending on what frame buffer devices you
-  selected. This is recommended for most users.
+  If you say N here, the needed low level drivers are automatically
+  enabled, depending on what frame buffer devices you selected above.
+  This is recommended for most users.
 
-  If you enable this option, you have more fine-grained control over which
-  low level drivers are enabled. You can e.g. leave out low level drivers
+  If you say Y here, you have more fine-grained control over which low
+  level drivers are enabled. You can e.g. leave out low level drivers
   for color depths you do not intend to use for text consoles.
 
   Low level frame buffer console drivers can be modules ( = code which
@@ -1788,13 +1802,13 @@
 Mac variable bpp packed pixels support
 CONFIG_FBCON_MAC
   This is the low level frame buffer console driver for 1/2/4/8/16/32
-  bits per pixel packed pixels on Mac. It supports variable fontwidths
+  bits per pixel packed pixels on Mac. It supports variable font widths
   for low resolution screens.
   
 VGA characters/attributes support
 CONFIG_FBCON_VGA
-  This is the low level frame buffer console driver for VGA text mode, as
-  used by vgafb.
+  This is the low level frame buffer console driver for VGA text mode;
+  it is used if you said Y to "VGA chipset support (text only)" above.
 
 Parallel-port support
 CONFIG_PARPORT
@@ -2182,7 +2196,7 @@
   server".  This makes the local computers think they are talking to
   the remote end, while in fact they are connected to the local
   proxy. Redirection is activated by defining special input firewall
-  rules (using the ipfwadm utility) and/or by doing an appropriate
+  rules (using the ipchains utility) and/or by doing an appropriate
   bind() system call.
 
 IP: masquerading
@@ -2825,12 +2839,12 @@
 
 Serial port KISS driver for AX.25
 CONFIG_MKISS
-  KISS is a protocol used to send IP traffic over AX.25 radio
-  connections, somewhat similar to SLIP for telephone lines. Say Y
-  here if you intend to send Internet traffic over amateur radio,
-  using some device connected to your machine's serial port. In that
-  case, you also have to say Y to "Amateur Radio AX.25 Level 2"
-  support.  
+  KISS is a protocol used for the exchange of data between a computer
+  and a Terminal Node Controller (a small embedded system commonly
+  used for networking over AX25 amateur radio connections).
+  Although KISS is less advanced than the 6pack protocol, it has
+  the advantage that it is already supported by most modern TNCs
+  without the need for a firmware upgrade.
 
   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),
@@ -5549,6 +5563,19 @@
   module, say M here and read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt.
 
+Novell/Eagle/Microdyne NE3210 EISA support
+CONFIG_NE3210
+  If you have a network (Ethernet) card of this type, say Y and read
+  the Ethernet-HOWTO, available via FTP (user: anonymous) in
+  ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO. Note that this driver
+  will NOT WORK for NE3200 cards as they are completely different.
+
+  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 ne3210.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt.
+
 Apricot Xen-II on board Ethernet
 CONFIG_APRICOT
   If you have a network (Ethernet) controller of this type, say Y and
@@ -5666,6 +5693,12 @@
   module, say M here and read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt.
 
+VIA Rhine support
+CONFIG_VIA_RHINE
+  If you have a VIA "rhine" based network card (Rhine-I (3043) or
+  Rhine-2 (VT86c100A)), say Y here. To build this driver as a module
+  say M.
+
 Zenith Z-Note support
 CONFIG_ZNET
   The Zenith Z-Note notebook computer has a built-in network
@@ -8028,21 +8061,94 @@
   The module will be called nvram.o. If you want to compile it as a
   module, say M here and read Documentation/modules.txt.
 
-PC joystick support
+Joystick support
 CONFIG_JOYSTICK
-  If you have a PC compatible analog or digital joystick, you can say
-  Y here. If you then create a character special file under /dev with
-  major number 15 and minor number 0 or 1 (for the two joystick ports)
-  using mknod ("man mknod"), you can read the status of the buttons
-  and the x and y coordinates from that file. Please read the file
+  If you have a joystick, you can say Y here to enable generic
+  joystick support. You will also need to say Y or M to at least one
+  of the hardware specific joystick drivers. This will make the
+  joysticks available under /dev/jsX devices. Please read the file
   Documentation/joystick.txt which contains more information and the
-  location of the joystick package that you'll need.  
+  location of the joystick package that you'll need.
 
   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 joystick.o. If you want to compile it as a
   module, say M here and read Documentation/modules.txt.
 
+Classic PC analog joysticks
+CONFIG_JOY_ANALOG
+  Say Y here if you have an analog joystick or gamepad that connects
+  to the PC gameport. This supports many different types, including
+  joysticks with throttle control, and with CHF / FCS / 6-button
+  extensions. For more information on how to use the driver please
+  read Documentation/joystick.txt
+
+FPGaming and MadCatz A3D controllers
+CONFIG_JOY_ASSASIN
+  Say Y here if you have an FPGaming Assasin 3D, MadCatz Panther or
+  MadCatz Panther XL. For more information on how to use the driver
+  please read Documentation/joystick.txt
+
+Gravis GrIP gamepads
+CONFIG_JOY_GRAVIS
+  Say Y here if you have a Gravis GamePad Pro. For more information
+  on how to use the driver please read Documentation/joystick.txt
+
+PDPI Lightning L4 gamecards
+CONFIG_JOY_LIGHTNING
+  Say Y here if you have a PDPI Lightning L4 gamecard and an analog
+  joystick or gamepad connected to it. For more information on how to
+  use the driver please read Documentation/joystick.txt
+
+Logitech Digital joysticks
+CONFIG_JOY_LOGITECH
+  Say Y here if you have a Logitech WingMan Extreme Digital,
+  Logitech ThunderPad Digital or Logitech CyberMan 2. For more
+  information on how to use the driver please read
+  Documentation/joystick.txt
+
+Microsoft SideWinder, Genius Digital joysticks
+CONFIG_JOY_SIDEWINDER
+  Say Y here if you have a Microsoft SideWinder 3d Pro, Microsoft
+  SideWinder Precision Pro, Microsoft SideWinder Force Feedback Pro,
+  Microsoft Sidewinder GamePad or Genius Flight2000 F-23 Digital. For
+  more information on how to use the driver please read
+  Documentation/joystick.txt
+
+ThrustMaster DirectConnect (BSP) joysticks
+CONFIG_JOY_THRUSTMASTER
+  Say Y here if you have a ThrustMaster Millenium 3D Inceptor, ThrustMaster
+  3D Rage Pad, or ThrustMaster WCS III. For more information on how to use the
+  driver please read Documentation/joystick.txt
+
+NES, SNES, PSX, Multisystem gamepads
+CONFIG_JOY_CONSOLE
+  Say Y here if you have a Nintendo Entertainment System gamepad,
+  Super Nintendo Entertainment System gamepad, Sony PlayStation
+  gamepad or a Multisystem - Atari, Amiga, Commodore, Amstrad CPC
+  joystick. For more information on how to use the driver please read
+  Documentation/joystick.txt
+
+Sega, Multisystem gamepads
+CONFIG_JOY_DB9
+  Say Y here if you have a Sega Master System gamepad, Sega Genesis
+  gamepad, Sega Saturn gamepad, or a Multisystem - Atari, Amiga,
+  Commodore, Amstrad CPC joystick. For more information on how to use
+  the driver please read Documentation/joystick.txt
+
+TurboGraFX Multisystem joystick interface
+CONFIG_JOY_TURBOGRAFX
+  Say Y here if you have the TurboGraFX interface by Steffen Schwenke, and
+  want to use it with Multiststem - Atari, Amiga, Commodore, Amstrad CPC
+  joystick. For more information on how to use the driver please read
+  Documentation/joystick-parport.txt
+
+Amiga joysticks
+CONFIG_JOY_AMIGA
+  Say Y here if you have an Amiga with a digital joystick connected
+  to it. For more information on how to use the driver please read
+  Documentation/joystick.txt
+
 Atomwide Serial Support
 CONFIG_ATOMWIDE_SERIAL
   If you have an Atomwide Serial card for an Acorn system, say Y to
@@ -8988,15 +9094,6 @@
   and CD32.  If you intend to run Linux on any of these systems, say Y;
   otherwise say N.
 
-Amiga Cybervision support
-CONFIG_FB_CYBER
-  This enables support for the Cybervision 64 graphics card from Phase5.
-  Please note that its use is not all that intuitive (i.e. if you have
-  any questions, be sure to ask!).  Say N unless you have a Cybervision
-  64 or plan to get one before you next recompile the kernel.
-  Please note that this driver DOES NOT support the Cybervision 64 3D
-  card at present, as they use incompatible video chips.
-
 Amiga GSP (TMS340x0) support
 CONFIG_AMIGA_GSP
   Include support for Amiga graphics cards that use the Texas
@@ -9124,6 +9221,11 @@
   1260 accelerator, and the optional SCSI module, say Y.  Otherwise,
   say N.
 
+Blizzard PowerUP 603e+ SCSI support
+CONFIG_BLZ603EPLUS_SCSI
+  If you have an Amiga 1200 with a Phase5 Blizzard PowerUP 603e+
+  accelerator, say Y.  Otherwise, say N.
+
 Fastlane SCSI support
 CONFIG_FASTLANE_SCSI
   If you have the Phase5 Fastlane Z3 SCSI controller, or plan to use
@@ -9165,6 +9267,16 @@
   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 is called ariadne.o. If you want to compile it as
+  a module, say M here and read Documentation/modules.txt.
+
+Ariadne II support
+CONFIG_ARIADNE2
+  If you have a VillageTronics Ariadne II Ethernet adapter, say Y.
+  Otherwise, say N.
+
+  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 is called ariadne2.o. If you want to compile it as
   a module, say M here and read Documentation/modules.txt.
 
 A2065 support
diff -u --recursive --new-file v2.1.124/linux/Documentation/fb/internals.txt linux/Documentation/fb/internals.txt
--- v2.1.124/linux/Documentation/fb/internals.txt	Wed Jun 24 22:54:01 1998
+++ linux/Documentation/fb/internals.txt	Mon Oct  5 13:39:20 1998
@@ -2,7 +2,7 @@
 This is a first start for some documentation about frame buffer device
 internals.
 
-Geert Uytterhoeven <Geert.Uytterhoeven@cs.kuleuven.ac.be>, 10 June 1998
+Geert Uytterhoeven <Geert.Uytterhoeven@cs.kuleuven.ac.be>, 21 July 1998
 
 --------------------------------------------------------------------------------
 
@@ -68,14 +68,13 @@
 
 True color (FB_VISUAL_TRUECOLOR)
 --------------------------------
-The pixel value is broken up into red, green, and blue fields, each of which 
-are looked up in separate red, green, and blue lookup tables.
+The pixel value is broken up into red, green, and blue fields.
 
 
 Direct color (FB_VISUAL_DIRECTCOLOR)
 ------------------------------------
-The pixel value is broken up into red, green, and blue fields. This is what
-most people mean when then say `true color'.
+The pixel value is broken up into red, green, and blue fields, each of which 
+are looked up in separate red, green, and blue lookup tables.
 
 
 Grayscale displays
diff -u --recursive --new-file v2.1.124/linux/Documentation/fb/vesafb.txt linux/Documentation/fb/vesafb.txt
--- v2.1.124/linux/Documentation/fb/vesafb.txt	Mon Oct  5 13:13:35 1998
+++ linux/Documentation/fb/vesafb.txt	Mon Oct  5 13:39:20 1998
@@ -79,20 +79,17 @@
 by comma.  Accepted options:
 
 invers	- no comment...
-
-redraw	- scroll by redrawing the affected part of the screen.
-	  This is the default.
+redraw	- scroll by redrawing the affected part of the screen
 ypan    - enable display panning using the VESA protected mode
 	  interface.  This enables the Shift-PgUp scrollback 
 	  thing and greatly speeds up fullscreen scrolling.
 	  It is slower than "redraw" when scrolling only a halve
-	  screen.  Seems not to work with some BIOSes.
+	  screen.  This is the default.
 ywrap	- If your gfx board supports wrap-around, use this one
 	  instead of ypan. 
-
-vgapal	- Use the standard vga registers for palette changes.
-	  This is the default.
-pmipal	- Use the protected mode interface for palette changes.
+nopal	- Don't use the protected mode interface for palette
+	  changes.  vesafb will try the standard vga registers
+	  instead.
 
 
 Have fun!
diff -u --recursive --new-file v2.1.124/linux/Documentation/i386/zero-page.txt linux/Documentation/i386/zero-page.txt
--- v2.1.124/linux/Documentation/i386/zero-page.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/i386/zero-page.txt	Mon Oct  5 12:59:25 1998
@@ -0,0 +1,73 @@
+Summary of empty_zero_page layout (kernel point of view)
+     ( collected by Hans Lermen and Martin Mares )
+ 
+The contents of empty_zero_page are used to pass parameters from the
+16-bit realmode code of the kernel to the 32-bit part. References/settings
+to it mainly are in:
+
+  arch/i386/boot/setup.S
+  arch/i386/boot/video.S
+  arch/i386/kernel/head.S
+  arch/i386/kernel/setup.c
+ 
+
+Offset	Type		Description
+------  ----		-----------
+    0	32 bytes	struct screen_info, SCREEN_INFO
+			ATTENTION, overlaps the following !!!
+    2	unsigned short	EXT_MEM_K, extended memory size in Kb (from int 0x15)
+ 0x20	unsigned short	CL_MAGIC, commandline magic number (=0xA33F)
+ 0x22	unsigned short	CL_OFFSET, commandline offset
+			Address of commandline is calculated:
+			  0x90000 + contents of CL_OFFSET
+			(only taken, when CL_MAGIC = 0xA33F)
+ 0x40	20 bytes	struct apm_bios_info, APM_BIOS_INFO
+ 0x80	16 bytes	hd0-disk-parameter from intvector 0x41
+ 0x90	16 bytes	hd1-disk-parameter from intvector 0x46
+
+ 0xa0	16 bytes	System description table truncated to 16 bytes.
+			( struct sys_desc_table_struct )
+ 0xb0 - 0x1df		Free. Add more parameters here if you really need them.
+
+0x1e0	unsigned long	ALT_MEM_K, alternative mem check, in Kb
+0x1f1	char		size of setup.S, number of sectors
+0x1f2	unsigned short	MOUNT_ROOT_RDONLY (if !=0)
+0x1f4	unsigned short	size of compressed kernel-part in the
+			(b)zImage-file (in 16 byte units, rounded up)
+0x1f6	unsigned short	swap_dev (unused AFAIK)
+0x1f8	unsigned short	RAMDISK_FLAGS
+0x1fa	unsigned short	VGA-Mode (old one)
+0x1fc	unsigned short	ORIG_ROOT_DEV (high=Major, low=minor)
+0x1ff	char		AUX_DEVICE_INFO
+
+0x200	short jump to start of setup code aka "reserved" field.
+0x202	4 bytes		Signature for SETUP-header, ="HdrS"
+0x206	unsigned short	Version number of header format
+			Current version is 0x0201...
+0x208	8 bytes		(used by setup.S for communication with boot loaders,
+			 look there)
+0x210	char		LOADER_TYPE, = 0, old one
+			else it is set by the loader:
+			0xTV: T=0 for LILO
+				1 for Loadlin
+				2 for bootsect-loader
+				3 for SYSLINUX
+				4 for ETHERBOOT
+				V = version
+0x211	char		loadflags:
+			bit0 = 1: kernel is loaded high (bzImage)
+			bit7 = 1: Heap and pointer (see below) set by boot
+				  loader.
+0x212	unsigned short	(setup.S)
+0x214	unsigned long	KERNEL_START, where the loader started the kernel
+0x218	unsigned long	INITRD_START, address of loaded ramdisk image
+0x21c	unsigned long	INITRD_SIZE, size in bytes of ramdisk image
+0x220	4 bytes		(setup.S)
+0x224	unsigned short	setup.S heap end pointer
+0x226 - 0x7ff		setup.S code.
+
+0x800	string, 2K max	COMMAND_LINE, the kernel commandline as
+			copied using CL_OFFSET.
+			Note: this will be copied once more by setup.c
+			into a local buffer which is only 256 bytes long.
+			( #define COMMAND_LINE_SIZE 256 )
diff -u --recursive --new-file v2.1.124/linux/Documentation/joystick-api.txt linux/Documentation/joystick-api.txt
--- v2.1.124/linux/Documentation/joystick-api.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/joystick-api.txt	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,308 @@
+		      Joystick API Documentation                -*-Text-*-
+
+		       Ragnar Hojland Espinosa
+		     <ragnar@lightside.ddns.org>
+
+			      7 Aug 1998
+
+
+1. Initialization
+~~~~~~~~~~~~~~~~~
+
+Open the joystick device following the usual semantics (that is, with open).
+Since the driver now reports events instead of polling for changes,
+immediately after the open it will issue a series of synthetic events
+(JS_EVENT_INIT) that you can read to check the initial state of the
+joystick.
+
+By default, the device is opened in blocking mode.
+
+	int fd = open ("/dev/js0", O_RDONLY);
+
+
+2. Event Reading
+~~~~~~~~~~~~~~~~
+
+	struct js_event e;
+	read (fd, &e, sizeof(struct js_event));
+
+where js_event is defined as
+
+	struct js_event {
+		__u32 time;     /* event timestamp in miliseconds */
+		__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
+you wanted to read more than one event per read as described in section 3.1.
+
+
+2.1 js_event.type
+~~~~~~~~~~~~~~~~~
+
+The possible values of ``type'' are
+
+	#define JS_EVENT_BUTTON         0x01    /* button pressed/released */
+	#define JS_EVENT_AXIS           0x02    /* joystick moved */
+	#define JS_EVENT_INIT           0x80    /* initial state of device */
+
+As mentioned above, the driver will issue synthetic JS_EVENT_INIT ORed
+events on open. That is, if it's issuing a INIT BUTTON event, the
+current type value will be
+
+	int type = JS_EVENT_BUTTON | JS_EVENT_INIT;	/* 0x81 */
+
+If you choose not to differentiate between synthetic or real events
+you can turn off the JS_EVENT_INIT bits
+
+	type &= ~JS_EVENT_INIT;				/* 0x01 */
+
+
+2.2 js_event.number
+~~~~~~~~~~~~~~~~~~~
+
+The values of ``number'' correspond to the axis or button that
+generated the event. Note that they carry separate numeration (that
+is, you have both an axis 0 and a button 0). Generally,
+
+			number
+	1st Axis X	0
+	1st Axis Y	1
+	2nd Axis X	2
+	2nd Axis Y	3
+	...and so on
+
+Hats vary from one joystick type to another. Some can be moved in 8
+directions, some only in 4, however, the driver always reports a hat as two
+independent axis, even if the hardware doesn't allow independent movement.
+
+
+2.3 js_event.value
+~~~~~~~~~~~~~~~~~~
+
+For an axis, ``value'' is a signed integer between -32767 and +32767
+representing the position of the joystick along that axis. If you
+don't read a 0 when the joystick is `dead', or if it doesn't span the
+full range, you should recalibrate (with, for example, jscal).
+
+For a button, ``value'' for a press button event is 1 and for a release
+button event is 0.
+
+Though this
+
+	if (js_event.type == JS_EVENT_BUTTON) {
+	   buttons_state ^= (1 << js_event.number);
+	}
+
+may work well if you handle JS_EVENT_INIT events separately,
+
+	if (js_event.type & ~JS_EVENT_INIT == JS_EVENT_BUTTON) {
+	   if (js_event.value)
+	      buttons_state |= (1 << js_event.number);
+	   else
+	      buttons_state &= ~(1 << js_event.number);
+	}
+
+is much safer since it can't lose sync with the driver. As you would
+have to write a separate handler for JS_EVENT_INIT events in the first
+snippet, this ends up being shorter.
+
+
+2.4 js_event.time
+~~~~~~~~~~~~~~~~~
+The time an event was generated is stored in ``js_event.time''. It's a time
+in miliseconds since ... well, since sometime in the past.  This eases the
+task of detecting double clicks, figuring out if movement of axis and button
+presses happened at the same time, and similar.
+
+
+3. Reading
+~~~~~~~~~~
+
+If you open the device in blocking mode, a read will block (that is,
+wait) forever until an event is generated and effectively read. There
+are two alternatives if you can't afford to wait forever (which is,
+admittedly, a long time;)
+
+	a) use select to wait until there's data to be read on fd, or
+	   until it timeouts. There's a good example on the select(2)
+	   man page.
+
+	b) open the device in non-blocking mode (O_NONBLOCK)
+
+
+3.1 O_NONBLOCK
+~~~~~~~~~~~~~~
+
+If read returns -1 when reading in O_NONBLOCK mode, this isn't
+necessarily a "real" error (check errno(3)); it can just mean there
+are no events pending to be read on the driver queue. You should read
+all events on the queue (that is, until you get a -1).
+
+For example,
+
+	while (1) {
+	   while (read (fd, &e, sizeof(struct js_event)) > 0) {
+	      process_event (e);
+	   }
+	   /* EAGAIN is returned when the queue is empty */
+	   if (errno != EAGAIN) {
+	      /* error */
+	   }
+	   /* do something interesting with processed events */
+	}
+
+One reason for emptying the queue is that if it gets full you'll start
+missing events since the queue is finite, and older events will get
+overwritten.
+
+The other reason is that you want to know all what happened, and not
+delay the processing till later.
+
+Why can get the queue full? Because you don't empty the queue as
+mentioned, or because too much time elapses from one read to another
+and too many events to store in the queue get generated. Note that
+high system load may contribute to space those reads even more.
+
+If time between reads is enough to fill the queue and loose an event,
+the driver will switch to startup mode and next time you read it,
+synthetic events (JS_EVENT_INIT) will be generated to inform you of
+the actual state of the joystick.
+
+[As for version 1.2.8, the queue is circular and able to hold 64
+ events. You can increment this size bumping up JS_BUFF_SIZE in
+ joystick.h and recompiling the driver.]
+
+
+In the above code, you might as well want to read more than one event
+at a time using the typical read(2) functionality. For that, you would
+replace the read above with something like
+
+	struct js_event mybuffer[0xff];
+	int i = read (fd, mybuffer, sizeof(struct mybuffer));
+
+In this case, read would return -1 if the queue was empty, or some
+other value in which the number of events read would be i /
+sizeof(js_event)  Again, if the buffer was full, it's a good idea to
+process the events and keep reading it until you empty the driver queue.
+
+
+4. IOCTLs
+~~~~~~~~~
+
+The joystick driver defines the following ioctl(2) operations.
+
+				/* function			3rd arg  */
+	#define JSIOCGAXES	/* get number of axes		char	 */
+	#define JSIOCGBUTTONS	/* get number of buttons	char	 */
+	#define JSIOCGVERSION	/* get driver version		int	 */
+	#define JSIOCGNAME(len) /* get identifier string	char	 */
+	#define JSIOCSCORR	/* set correction values	&js_corr */
+	#define JSIOCGCORR	/* get correction values	&js_corr */
+
+For example, to read the number of axes
+
+	char number_of_axes;
+	ioctl (fd, JSIOCGAXES, &number_of_axes);
+
+
+4.1 JSIOGCVERSION
+~~~~~~~~~~~~~~~~~
+
+JSIOGCVERSION is a good way to check in run-time whether the running
+driver is 1.0+ and supports the event interface. If it is not, the
+IOCTL will fail. For a compile-time decision, you can test the
+JS_VERSION symbol
+
+	#ifdef JS_VERSION
+	#if JS_VERSION > 0xsomething
+
+4.2 JSIOCGNAME
+~~~~~~~~~~~~~~
+
+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
+buffer provided by the application asking for the name. It is used to avoid
+possible overrun should the name be too long.
+
+	char name[128];
+	if (ioctl(fd, JSIOCGNAME(sizeof(name)), name) < 0)
+		strncpy(name, "Unknown", sizeof(name));
+	printf("Name: %s\n", name);
+
+4.3 JSIOC[SG]CORR
+~~~~~~~~~~~~~~~~~
+
+For usage on JSIOC[SG]CORR I suggest you to look into jscal.c  They are
+not needed in a normal program, only in joystick calibration software
+such as jscal or kcmjoy. These IOCTLs and data types aren't considered
+to be in the stable part of the API, and therefore may change without
+warning in following releases of the driver.
+
+Both JSIOCSCORR and JSIOCGCORR expect &js_corr to be able to hold
+information for all axis. That is, struct js_corr corr[MAX_AXIS];
+
+struct js_corr is defined as
+
+	struct js_corr {
+		__s32 coef[8];
+		__u16 prec;
+		__u16 type;
+	};
+
+and ``type''
+
+	#define JS_CORR_NONE            0x00    /* returns raw values */
+	#define JS_CORR_BROKEN          0x01    /* broken line */
+
+
+5. Backward compatibility
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The 0.x joystick driver API is quite limited and its usage is deprecated.
+The driver offers backward compatibility, though. Here's a quick summary:
+
+	struct JS_DATA_TYPE js;
+	while (1) {
+	   if (read (fd, &js, JS_RETURN) != JS_RETURN) {
+	      /* error */
+	   }
+	   usleep (1000);
+	}
+
+As you can figure out from the example, the read returns immediately,
+with the actual state of the joystick.
+
+	struct JS_DATA_TYPE {
+		int buttons;    /* immediate button state */
+		int x;          /* immediate x axis value */
+		int y;          /* immediate y axis value */
+	};
+
+and JS_RETURN is defined as
+
+	#define JS_RETURN       sizeof(struct JS_DATA_TYPE)
+
+To test the state of the buttons,
+
+	first_button_state  = js.buttons & 1;
+	second_button_state = js.buttons & 2;
+
+The axis values do not have a defined range in the original 0.x driver,
+except for that the values are non-negative. The 1.2.8+ drivers use a
+fixed range for reporting the values, 0 being the minimum, 128 the
+center, and 255 maximum value.
+
+
+6. Final Notes
+~~~~~~~~~~~~~~
+
+____/|	Comments, additions, and specially corrections are welcome.
+\ o.O|	Documentation valid for at least version 1.2.8 of the joystick
+ =(_)=	driver and as usual, the ultimate source for documentation is
+   U	to "Use The Source Luke" or, at your convenience, Vojtech ;)
+
+					- Ragnar
+EOF
diff -u --recursive --new-file v2.1.124/linux/Documentation/joystick-parport.txt linux/Documentation/joystick-parport.txt
--- v2.1.124/linux/Documentation/joystick-parport.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/joystick-parport.txt	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,515 @@
+		  Linux Joystick parport drivers v1.2 BETA
+		  (c) 1998 Vojtech Pavlik <vojtech@ucw.cz>
+	       (c) 1998 Andree Borrmann <a.borrmann@tu-bs.de>
+----------------------------------------------------------------------------
+
+0. Disclaimer
+~~~~~~~~~~~~~
+  Any information in this file is provided as-is, without any guarantee that
+it will be true. So, use it at your own risk. The possible damages that can
+happen include burning your parallel port, and/or the sticks and joystick
+and maybe even more. Like when a lightning kills you it is not our problem.
+
+1. Intro
+~~~~~~~~
+  The joystick parport drivers are used for joysticks and gamepads not
+originally designed for PCs and other computers Linux runs on. Because of
+that, PCs usually lack the right ports to connect these devices to. Parallel
+port, because of its ability to change single bits at will, and providing
+both output and input bits is the most suitable port on the PC for
+connecting such devices.
+
+2. Devices supported
+~~~~~~~~~~~~~~~~~~~~
+  Many console and 8-bit coputer gamepads and joysticks are supported. The
+following subsections discuss usage of each.
+
+2.1 NES and SNES
+~~~~~~~~~~~~~~~~
+  The Nintendo Entertainment System and Super Nintendo Entertainment System
+gamepads are widely available, and easy to get. Also, they are quite easy to
+connect to a PC, and don't need much processing speed (108 us for NES and
+165 us for SNES, compared to about 1000 us for PC gamepads) to communicate
+with them.
+
+  All NES and SNES use the same synchronous serial protocol, clocked from
+the computer's side (and thus timing insensitive). To allow up to 5 NES
+and/or SNES gamepads connected to the parallel port at once, the output
+lines of the parallel port are shared, while one of 5 available input lines
+is assigned to each gamepad.
+
+  This protocol is handled by the joy-console.c driver, so that's the one
+you'll use for NES and SNES gamepads.
+
+  The main problem with PC parallel ports is that they don't have +5V power
+source on any of their pins. So, if you want a reliable source of power
+for your pads, use either keyboard or joystick port, and make a pass-through
+cable.
+
+  If you want to use the parallel port only, you can take the power is from
+some data pin. For most gamepad and parport implementations only one pin is
+needed, and I'd recommend pin 9 for that, the highest data bit. On the other
+hand, if you are not planning to use anything else than NES / SNES on the
+port, anything between and including pin 4 and pin 9 will work.
+
+(pin 9) -----> Power
+
+  Unfortunately, there are pads that need a lot more of power, and parallel
+ports that can't give much current through the data pins. If this is your
+case, you'll need to use diodes (as a prevention of destroying your parallel
+port), and combine the currents of two or more data bits together.
+
+	   Diodes
+(pin 9) ----|>|-------+------> Power
+		      |
+(pin 8) ----|>|-------+
+		      |
+(pin 7) ----|>|-------+
+		      |
+ <and so on>          :
+		      |
+(pin 4) ----|>|-------+
+
+  Ground is quite easy. On PC's parallel port the ground is on any of the
+pins from pin 18 to pin 25. So use any pin of these you like for the ground.
+
+(pin 18) -----> Ground
+
+  NES and SNES pads have two input bits, Clock and Latch, which drive the
+serial transfer. These are connected to pins 2 and 3 of the parallel port,
+respectively.
+
+(pin 2) -----> Clock
+(pin 3) -----> Latch
+
+  And the last thing is the NES / SNES data wire. Only that isn't shared and
+each pad needs its own data pin. The parallel port pins are:
+
+(pin 10) -----> Pad 1 data
+(pin 11) -----> Pad 2 data
+(pin 12) -----> Pad 3 data
+(pin 13) -----> Pad 4 data
+(pin 15) -----> Pad 5 data
+
+  Note that pin 14 is not used, since it is not an input pin on the parallel
+port.
+
+  This is everything you need on the PC's side of the connection, now on to
+the gamepads side. The NES and SNES have different connectors. Also, there
+are quite a lot of NES clones, and because Nintendo used proprietary
+connectors for their machines, the cloners couldn't and used standard D-Cannon
+connectors. Anyway, if you've got a gamepad, and it has buttons A, B, Turbo
+A, Turbo B, Select and Start, and is connected through 5 wires, then it is
+either a NES or NES clone and will work with this connection. SNES gamepads
+also use 5 wires, but have more buttons. They will work as well, of course.
+
+Pinout for NES gamepads                 Pinout for SNES gamepads
+
+	   +----> Power                   +-----------------------\
+	   |                            7 | o  o  o  o |  x  x  o  | 1
+ 5 +---------+  7                         +-----------------------/
+   | x  x  o   \                            |  |  |  |          |
+   | o  o  o  o |                           |  |  |  |          +-> Ground
+ 4 +------------+ 1                         |  |  |  +------------> Data
+     |  |  |  |                             |  |  +---------------> Latch
+     |  |  |  +-> Ground                    |  +------------------> Clock
+     |  |  +----> Clock                     +---------------------> Power
+     |  +-------> Latch
+     +----------> Data
+
+Pinout for NES clone (db9) gamepads     Pinout for NES clone (db15) gamepads
+
+	+---------> Clock                    +-----------------> Data
+	| +-------> Latch                    |             +---> Ground
+	| | +-----> Data                     |             |
+	| | |                              ___________________
+    _____________                        8 \ o x x x x x x o / 1
+  5 \ x o o o x / 1                         \ o x x o x x o /
+     \ x o x o /                          15 `~~~~~~~~~~~~~' 9
+    9 `~~~~~~~' 6                             |     |     |
+	 |   |                                |     |     +----> Clock
+	 |   +----> Power                     |     +----------> Latch
+	 +--------> Ground                    +----------------> Power
+
+2.2 Multisystem joysticks
+~~~~~~~~~~~~~~~~~~~~~~~~~
+  In the era of 8-bit machines, there was something like de-facto standard
+for joystick ports. They were all digital, and all used D-Cannon 9 pin
+connectors (db9). Because of that, a single joystick could be used without
+hassle on Atari (130, 800XE, 800XL, 2600, 7200), Amiga, Commodore C64,
+Amstrad CPC, Sinclair ZX Spectrum and many other machines. That's why these
+joysticks are called "Multisystem".
+
+  Now their pinout:
+
+      +---------> Right
+      | +-------> Left
+      | | +-----> Down
+      | | | +---> Up
+      | | | |
+  _____________
+5 \ x o o o o / 1
+   \ x o x o /
+  9 `~~~~~~~' 6
+       |   |
+       |   +----> Button
+       +--------> Ground
+
+  However, as time passed, extension to this standard developed, and these
+were not compatible with each other:
+
+
+  Atari 130, 800(XL/XE)                    MSX
+
+					 +-----------> Power
+      +---------> Right                  | +---------> Right
+      | +-------> Left                   | | +-------> Left
+      | | +-----> Down                   | | | +-----> Down
+      | | | +---> Up                     | | | | +---> Up
+      | | | |                            | | | | |
+  _____________                        _____________
+5 \ x o o o o / 1                    5 \ o o o o o / 1
+   \ x o o o /                          \ o o o o /
+  9 `~~~~~~~' 6                        9 `~~~~~~~' 6
+       | | |                              | | | |
+       | | +----> Button                  | | | +----> Button 1
+       | +------> Power                   | | +------> Button 2
+       +--------> Ground                  | +--------> Output 3
+					  +----------> Ground
+
+       Amstrad CPC                         Commodore C64
+
+					 +-----------> Analog Y
+      +---------> Right                  | +---------> Right
+      | +-------> Left                   | | +-------> Left
+      | | +-----> Down                   | | | +-----> Down
+      | | | +---> Up                     | | | | +---> Up
+      | | | |                            | | | | |
+  _____________                        _____________
+5 \ x o o o o / 1                    5 \ o o o o o / 1
+   \ x o o o /                          \ o o o o /
+  9 `~~~~~~~' 6                        9 `~~~~~~~' 6
+       | | |                              | | | |
+       | | +----> Button 1                | | | +----> Button
+       | +------> Button 2                | | +------> Power
+       +--------> Ground                  | +--------> Ground
+					  +----------> Analog X
+
+  And there were many others.
+
+2.2.1 Multisystem joysticks using joy-db9.c
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  For the Multisystem joysticks, and their derivatives, the joy-db9.c driver
+was written. It allows only one joystick / gamepad per parallel port, but
+the interface is easy to build and works with almost anything.
+
+  For the basic 1-button Multisystem joystick you connect its wires to the
+parallel port like this:
+
+(pin  1) -----> Power
+(pin 18) -----> Ground
+
+(pin  2) -----> Up
+(pin  3) -----> Down
+(pin  4) -----> Left
+(pin  5) -----> Right
+(pin  6) -----> Button 1
+
+  However, if the joystick is switch based (eg. clicks when you move it),
+you might or might not, depending on your parallel port, need 10 kOhm pullup
+resistors on each of the direction and button signals, like this:
+
+(pin 2) ------------+------> Up
+	  Resistor  |
+(pin 1) --[10kOhm]--+
+
+  Try without, and if it doesn't work, add them. For TTL based joysticks /
+gamepads the pullups are not needed.
+
+  For joysticks with two buttons you connect the second button to pin 7 on
+the parallel port.
+
+(pin 7) -----> Button 2
+
+2.2.1 Multisystem joysticks using joy-console.c
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  For some people just one joystick per parallel port is not enough, and/or
+want to use them on one parallel port together with NES/SNES/PSX pads. This
+is possible using the joy-console.c. It supports up to 5 devices of the
+above types, including 1 and 2 buttons Multisystem joysticks.
+
+  However, there is nothing for free. To allow more sticks to be used at
+once, you need the sticks to be purely switch based (that is non-TTL), and
+not to need power. Just a plain simple six switches inside. If your
+joystick can do more (eg. turbofire) you'll need to disable it totally first
+if you want to use joy-console.c.
+
+  Also, the connection is a bit more complex. You'll need a bunch of diodes,
+and one pullup resistor. First, you connect the Directions and the button
+the same as for joy-db9, however with the diodes inbetween.
+
+	    Diodes
+(pin 2) -----|<|----> Up
+(pin 3) -----|<|----> Down
+(pin 4) -----|<|----> Left
+(pin 5) -----|<|----> Right
+(pin 6) -----|<|----> Button 1
+
+  For two button sticks you also connect the other button.
+
+(pin 7) -----|<|----> Button 2
+
+  And finally, you connect the Ground wire of the joystick, like done in
+this little schematic to Power and Data on the parallel port, as described
+for the NES / SNES pads in section 2.1 of this file - that is, one data pin
+for each joystick. The power source is shared.
+
+Data    ------------+-----> Ground
+	  Resistor  |
+Power   --[10kOhm]--+
+
+  And that's all, here we go!
+
+2.2.3 Multisystem joysticks using joy-turbografx.c
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  The TurboGraFX interface, designed by
+
+	Steffen Schwenke <schwenke@burg-halle.de>
+
+  allows up to 7 Multisystem joysticks connected to the parallel port. In
+Steffen's version, there is support for up to 5 buttons per joystick.
+However, since this doesn't work reliably on all parallel ports, the
+joy-turbografx.c driver supports only one button per joystick. For more
+information on how to build the interface, see
+
+	http://www2.burg-halle.de/~schwenke/parport.html
+
+2.3 Sony Playstation
+~~~~~~~~~~~~~~~~~~~~
+
+  WARNING: PSX support is experimental, and at the moment doesn't seem to
+work for most people. If you like adventure, you can try yourself.
+
+  The PSX controller is supported by the joy-console.c.
+
+Pinout of the PSX controller:
+
+  +---------+---------+---------+
+9 | o  o  o | o  o  o | o  o  o | 1               parallel
+   \________|_________|________/                  port pins
+    |  |      |  |  |   |
+    |  |      |  |  |   +-------->  Clock    ---  (1)
+    |  |      |  |  +------------>  Select   ---  (17)
+    |  |      |  +--------------->  Power    ---  (16)
+    |  |      +------------------>  Ground   ---  (18-25)
+    |  +------------------------->  Command  ---  (14)
+    +---------------------------->  Data     ---  (10,11,12,13,15) one only...
+
+  You may have to add pull up/down resistors. Maybe your pad also won't like
+the 5V (PSX uses 3.7V).
+
+  Currently the driver supports only ONE psx pad and only one type of
+controller: The normal PSX controller. NEGCON support is planned for the
+next release. ANALOG controller may be too (I do not recommend to connect
+the "force feedback"/"rumble pack" version... it (may) use too much
+power...)
+
+2.4 Sega
+~~~~~~~~
+  All the Sega controllers are more or less based on the standard 2-button
+Multisystem joystick. However, since they don't use switches and use TTL
+logic, the only driver useable with them is the joy-db9.c driver.
+
+2.4.1 Sega Master System
+~~~~~~~~~~~~~~~~~~~~~~~~
+  The SMS gamepads are almost exactly the same as normal 2-button
+Multisystem joysticks. Set the driver to Multi2 mode, use the corresponding
+parallel port pins, and the following schematic:
+
+    +-----------> Power
+    | +---------> Right
+    | | +-------> Left
+    | | | +-----> Down
+    | | | | +---> Up
+    | | | | |
+  _____________
+5 \ o o o o o / 1
+   \ o o x o /
+  9 `~~~~~~~' 6
+     | |   |
+     | |   +----> Button 1
+     | +--------> Ground
+     +----------> Button 2
+
+2.4.2 Sega Genesis aka MegaDrive
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  The Sega Genesis (in Europe sold as Sega MegaDrive) pads are an extension
+to the Sega Master System pads. They use more buttons (3+1, 5+1, 6+1).  Use
+the following schematic:
+
+    +-----------> Power
+    | +---------> Right
+    | | +-------> Left
+    | | | +-----> Down
+    | | | | +---> Up
+    | | | | |
+  _____________
+5 \ o o o o o / 1
+   \ o o o o /
+  9 `~~~~~~~' 6
+     | | | |
+     | | | +----> Button 1
+     | | +------> Select
+     | +--------> Ground
+     +----------> Button 2
+
+  The Select pin goes to pin 14 on the parallel port.
+
+(pin 14) -----> Select
+
+  The rest is the same as for Multi2 joysticks using joy-db9.c
+
+2.4.3 Sega Saturn
+~~~~~~~~~~~~~~~~~
+  Sega Saturn has eight buttons, and to transfer that, without hacks like
+Genesis 6 pads use, it needs one more select pin. Anyway, it is still
+handled by the joy-db9.c driver. Its pinout is very different from anything
+else.  Use this schematic:
+
+    +-----------> Select 1
+    | +---------> Power
+    | | +-------> Up
+    | | | +-----> Down
+    | | | | +---> Ground
+    | | | | |
+  _____________
+5 \ o o o o o / 1
+   \ o o o o /
+  9 `~~~~~~~' 6
+     | | | |
+     | | | +----> Select 2
+     | | +------> Right
+     | +--------> Left
+     +----------> Power
+
+  Select 1 is pin 14 on the parallel port, Select 2 is pin 16 on the
+parallel port.
+
+(pin 14) -----> Select 1
+(pin 16) -----> Select 2
+
+  The other pins (Up, Down, Right, Left, Power, Ground) are the same as for
+Multi joysticks using joy-db9.c
+
+3. The drivers
+~~~~~~~~~~~~~~
+  There are three drivers for the parallel port interfaces. Each, as
+described above, allows to connect a different group of joysticks and pads.
+Here are described their command lines:
+
+3.1 joy-console.c
+~~~~~~~~~~~~~~~~~
+  Using joy-console.c you can connect up to five devices to one parallel
+port. It uses the following kernel/module command line:
+
+	js_console=port,pad1,pad2,pad3,pad4,pad5
+
+  Where 'port' is either the address of the parallel port the joystick/pad
+is connected to (eg. 0x378), or, if you are using the parport driver of 2.1+
+Linux kernels, the number of the parport interface (eg. 0 for parport0).
+
+  And 'pad1' to 'pad5' are pad types connected to different data input pins
+(10,11,12,13,15), as described in section 2.1 of this file.
+
+  The types are:
+
+	Type | Joystick/Pad
+	--------------------
+	  0  | None
+	  1  | SNES pad
+	  2  | NES pad
+	  4  | Multisystem 1-button joystick
+	  5  | Multisystem 2-button joystick
+	  6  | Sony PSX controller
+
+  The exact type of the PSX controller type is autoprobed, so you must have
+your controller plugged in before initializing.
+
+  Should you want to use more than one of parallel ports at once, you can
+use js_console_2 and js_db9_3 as additional command line parameters for two
+more parallel ports.
+
+  Changes:
+    v0.1 : First version (SNES only)
+    v0.2 : X/Y directions were exchanged...
+    v0.3 : Adaptation for kernel 2.1
+    v0.4 : Adaptation for joystick-1.2.6
+	   - added open/close callbacks
+    v0.5 : Renamed to "joy-console" because I have added
+	   PSX controller support.
+    v0.6 : NES support
+    v0.7V: Added "multi system" support
+    v0.8 : Bugfixed PSX driver...
+    v0.9V: Changed multi system support
+	   Added Multi2 support
+	   Fixed parport handling
+	   Cleaned up
+    v0.10: Fixed PSX buttons 7 and 8
+
+3.2 joy-db9.c
+~~~~~~~~~~~~~
+  Apart from making an interface, there is nothing difficult on using the
+joy-db9.c driver. It uses the following kernel/module command line:
+
+	js_db9=port,type
+
+  Where 'port' is either the address of the parallel port the joystick/pad
+is connected to (eg. 0x378), or, if you are using the parport driver of 2.1+
+Linux kernels, the number of the parport interface (eg. 0 for parport0).
+
+  'Type' is the type of joystick or pad attached:
+
+	Type | Joystick/Pad
+	--------------------
+	  0  | None
+	  1  | Multisystem 1-button joystick
+	  2  | Multisystem 2-button joystick
+	  3  | Genesis pad (3+1 buttons)
+	  5  | Genesis pad (5+1 buttons)
+	  6  | Genesis pad (6+1 buttons)
+	  7  | Saturn pad
+
+  Should you want to use more than one of these joysticks/pads at once, you
+can use js_db9_2 and js_db9_3 as additional command line parameters for two
+more joysticks/pads.
+
+  Changes:
+    v0.1 : First version
+    v0.2 : Changed kernel parameter format
+    v0.3V: Added Sega Saturn support
+	   Fixed parport and PS/2 mode handling
+	   Cleaned up
+
+3.3 joy-turbografx.c
+~~~~~~~~~~~~~~~~~~~~
+  The joy-turbografx.c driver uses a very simple kernel/module command line:
+
+	js_tg=port,count
+
+  Where 'port' is either the address of the parallel port the interface is
+connected to (eg. 0x378), or, if you are using the parport driver of 2.1+
+Linux kernels, the number of the parport interface (eg. 0 for parport0).
+
+  'Count' is the number of Multisystem, 1-button joysticks connected to the
+interface.
+
+  Should you want to use more than one of these interfaces at once, you can
+use js_tg_2 and js_tg_3 as additional command line parameters for two more
+interfaces.
+
+  Changes:
+    v0.1V: First version
+
+3.4 End
+~~~~~~~
+  That's all, folks! Have fun!
diff -u --recursive --new-file v2.1.124/linux/Documentation/joystick.txt linux/Documentation/joystick.txt
--- v2.1.124/linux/Documentation/joystick.txt	Thu May 14 19:47:37 1998
+++ linux/Documentation/joystick.txt	Thu Oct  8 17:25:16 1998
@@ -1,213 +1,566 @@
-			PC Joystick driver v1.0.9
-	(c) 1997 Vojtech Pavlik <vojtech@atrey.karlin.mff.cuni.cz>
+		     Linux Joystick driver v1.2.11 BETA
+	       (c) 1996-1998 Vojtech Pavlik <vojtech@ucw.cz>
 ----------------------------------------------------------------------------
 
+0. Disclaimer
+~~~~~~~~~~~~~
+  This is a BETA release. It probably will work well for you, but it doesn't
+have to.
+
+  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@ucw.cz>, or by paper mail: Vojtech Pavlik,
+Ucitelska 1576, Prague 8, 182 00 Czech Republic
+
+  For your convenience, the GNU General Public Licens version 2 is included
+in the package: See the file COPYING.
+
 1. Intro
 ~~~~~~~~
-  The PC Joystick driver for Linux provides support for analog (variable
-resistor based) and digital (switch based) joysticks connected via the PC
-game port. It can support up to 2 joysticks.
+  The joystick driver for Linux provides support for a variety of joysticks
+and similar devices.
+
+  These currently include various analog joysticks (both variable resistor
+based and microswitch+resistor based), following IBM PC joystick standard,
+with extensions like additional hats and buttons compatible with CH
+Flightstick Pro, ThrustMaster FCS or PC 6 or 8 button gamepads.
+
+  In addition to these it also supports some of the new PC joysticks that
+use porprietary digital protocols to communicate over the gameport,
+currently by FPGaming, Genius, Gravis, Logitech, MadCatz and Microsoft.
+ThrustMaster and Creative Labs protocols are in the works.
+
+  The driver also includes support for many gamepads and joysticks that were
+used by various non-PC computers and game consoles. These include Multi
+system joysticks (Atari, Amiga, Commodore, Amstrad), Sega gamepads (Master
+System, Genesis, Saturn), Nintendo gamepads (NES, SNES), Sony gamepads (PSX).
+Support for N64, Atari Jaguar, Atari 2600, NES FourScore, SNES MultiTap,
+Pegasus, PSX NegCon and others might be added later.
+
+  Last, but not least there is also native Amiga joystick support for the
+Amiga linux port.
 
   Should you encounter any problems while using the driver, or joysticks
 this driver can't make complete use of, I'm very interested in hearing about
 them. Bug reports and success stories are also welcome.
 
-2. Usage
-~~~~~~~~
-  If you enable the joystick driver in the kernel configuration, all
-connected joysticks should be found automatically. The driver can detect
-standard two-axis two-button, and three-axis four-button joysticks only. If
-that isn't your case, you can pass the joystick driver the following kernel
-command line arguments:
-
-js=0xXX,0xYY
-
-  Where XX and YY are bit masks for the two joysticks, with the bits
-representing which buttons and axes of the joystick are present:
-
-Bit | Explanation
------------------
- 0  | Axis 0
- 1  | Axis 1
- 2  | Axis 2
- 3  | Axis 3
- 4  | Button 0
- 5  | Button 1
- 6  | Button 2
- 7  | Button 3
-
-  Another method of using the driver is loading it as a module. For that,
-select `M' for this driver in the kernel configuration and insert the
-module:
-
-insmod joystick.o js=0xXX,0xYY
-
-  To enable autoloading/-unloading of the joystick module, you have to add
-these lines to /etc/conf.modules:
-
-alias char-major-15 joystick
-options joystick js=0xXX,0xYY
+  The joystick package is available at the following FTP site:
 
-  To enable the user space programs to read the joystick device, you have to
-create the device files using mknod (man mknod for more info):
+	ftp://atrey.karlin.mff.cuni.cz/pub/linux/joystick/joystick-1.2.x.tar.gz
 
-mknod /dev/js0 c 15 0
-mknod /dev/js1 c 15 1
+  And a homepage of the driver is at:
 
-3. Calibration
-~~~~~~~~~~~~~~
-  As of version 1.0 the calibration routines used in the joystick driver are
-worth using. The idea of calibration is that you have to calibrate the
-joystick only once, and then set the calibration at boot-time, thus removing
-the need of re-calibrating it in each program that uses it.
+	http://atrey.karlin.mff.cuni.cz/~vojtech/joystick/
 
-  For calibration, use the jscal program, contained in the joystick package
-which is available at:
+  There is also a mailing list for the driver at:
 
-ftp://atrey.karlin.mff.cuni.cz/pub/linux/joystick/joystick-1.0.9.tar.gz
+	listproc@atrey.karlin.mff.cuni.cz
 
-4. Programming Interface
+send "subscribe linux-joystick Your Name" to subscribe to it.
+
+2. Usage
+~~~~~~~~
+  To use the driver as a standalone module, without patching the kernel, you
+first need to edit the Makefile to meet your needs (namely whether you use
+MODVERSIONS). Then you compile the driver by typing
+
+	make
+
+  And after compiling you first
+
+	insmod joystick.o
+
+  and then
+
+	insmod joy-something.o
+
+  where 'something' is the type of your joystick. See below for more precise
+explanation. Of course, you can insmod more than one hardware specific
+module at once, if you have more than one joystick installed.
+
+  If you want to have the driver compiled into your kernel, you first need
+to patch the kernel. For that, use either kernel-2.0.35.patch, or
+kernel-2.1.115.patch files, that are included in the joystick package, and
+using the command
+
+	patch -Esp1 < /usr/src/joystick-1.2.x/kernel-2.x.y.patch
+
+in
+
+	/usr/src/linux
+
+apply the patch to your kernel. To compile joystick support into the kernel,
+use the kernel configuration scripts, and answer 'Y' to Joystick support and
+also to at least one of the hardware specific options.
+
+  After patching the kernel you can also compile it as a module, answering
+'M' to all joystick support you want to have modules for. It is possible to
+have the main joystick driver compiled into the kernel and the hardware
+dependent drivers as modules.
+
+  After you're done with installation of the driver itself, you'll need to
+create the joystick device files in /dev so that applications can use them.
+This is done by typing
+
+	make devs
+
+in the joystick driver's directory. For manual creation of the joystick
+devices, check the Documentation/devices.txt file in the Linux source tree.
+
+2.1 Analog joysticks
+~~~~~~~~~~~~~~~~~~~~
+  The joy-analog.c uses the standard analog inputs of the gameport, and
+thus supports all standard joysticks. However the only types that can be
+autodetected are:
+
+* 2-axis, 4-button joystick
+* 3-axis, 4-button joystick
+* Two 2-axis, 2-buttons joysticks on an Y-cable
+
+  For other joystick types (more/less axes, hats, and buttons) support
+you'll need to specify the types either on the kernel command line or on the
+module command line, when inserting joy-analog.o into the kernel. The
+parameters are:
+
+	js_an=p0,m0,n0,p1,m1,n1 ...
+
+  Where 'p' is the port number, eg. 0x201, which is the standard address.
+'m' and 'n' are joystick 0 and joystick 1 bitmasks for the specified
+joystick port. The bits in the bitmasks mean:
+
+	Bit |   2^n | Meaning
+	----------------------------------
+	 0  |     1 | Axis X1
+	 1  |     2 | Axis Y1
+	 2  |     4 | Axis X2
+	 3  |     8 | Axis Y2
+	 4  |    16 | Button A
+	 5  |    32 | Button B
+	 6  |    64 | Button C
+	 7  |   128 | Button D
+	 8  |   256 | CHF Buttons X and Y
+	 9  |   512 | CHF Hat 1
+	10  |  1024 | CHF Hat 2
+	11  |  2048 | FCS Hat
+	12  |  4096 | PXY Button X
+	13  |  8192 | PXY Button Y
+	14  | 16384 | PXY Button U
+	15  | 32768 | PXY Button V
+
+(CHF = CH Flightstick Pro, FCS = ThrustMaster FCS, PXY - Pad with 6 or 8 buttons)
+
+  Following is a table of joysticks for which the 'm' values are known. If
+you have any additions/corrections to it, e-mail me.
+
+	Joystick				| 'm' value
+	----------------------------------------------------
+	Genius Flight2000 F-12			| 0x00f3
+	Genius Flight2000 F-21			| 0x08f7
+	Genius Flight2000 F-22			| 0x02ff
+	Genius GameHunter G-06			| 0xf0f3
+	Genius MaxFire G-07			| 0xf0f3   
+	Genius PowerStation			| 0xf0f3
+	Laing #1 PC SuperPad			| 0xf0f3
+	Microsoft SideWinder Standard		| 0x003b
+	QuickShot QS-201 SuperWarrior		| 0x00fb
+
+  In case you have one of the joystick in the table below, and it doesn't
+work with a specific driver in digital mode for some reason, you can use
+them in analog mode with the joy-analog driver as well. However, digital
+operation is always better.
+
+	Joystick				| 'm' value
+	----------------------------------------------------
+	Gravis GamePad Pro - analog mode	| 0x00f3
+	Genius Flight2000 F-23 - CHF mode	| 0x02ff
+	Genius Flight2000 F-23 - FCS mode	| 0x08f7
+	Microsoft SideWinder 3D Pro - CHF mode	| 0x02ff
+	Microsoft SideWinder 3D Pro - FCS mode	| 0x08f7
+
+  An example that would configure the driver to use two two axis, two button
+joysticks connected to port 0x201, a single four button four axis joystick
+connected to port 0x202, a four axis, six button and two hat CHF compatible
+joystick on 0x203, and a two axis four button FCS compatible joystick with a
+single hat on 0x207:
+
+	js_an=0x201,0x33,0xcc,0x202,0xff,0,0x203,0x7ff,0,0x207,0x8f3,0
+
+  If you can't sum bits into hex numbers in your head easily, you can simply
+sum the values in the 2^n column decimally and use that number instead.
+Using this method you'd get a command line:
+
+	js_an=0x201,51,204,0x202,255,0,0x203,2047,0,0x207,2291,0
+
+  And it would do the same as the above explained command line. Use
+whichever way you like best.
+
+2.2 Microsoft SideWinder and Genius Digital joysticks
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  SideWinder and Genius Digital joysticks are supported by the
+joy-sidewinder.c module. All currently supported joysticks:
+
+* SideWinder 3d Pro
+* SideWinder Precision Pro 
+* SideWinder Force Feedback Pro
+* SideWinder Game Pad (up to four, chained together)
+* Genius Flight2000 Digital F-23
+
+  are autodetected, and thus no module parameters are needed.
+
+  The SideWinder Standard is not a digital joystick, and thus is supported
+by the analog driver described above. SideWinder FreeStyle Pro and
+SideWinder Force Feedback Wheel are not supported yet.
+
+2.3 Logitech Digital joysticks
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  Logitech Digital joysticks are supported by the joy-logitech.c module. It
+currently supports these devices:
+
+* Logitech Wingman Extreme Digital
+* Logitech CyberMan 2
+* Logitech ThunderPad Digital
+
+  All three are autodetected, and thus no parameters to the module are
+needed.
+
+  Logitech Wingman is not a digital joystick and is handled by the analog
+driver described above. Logitech Wingman Warrior communicates through a
+serial port and is not supported yet. Logitech Wingman Force, Wingman
+Formula, Wingman Formula Force, Wingman Gamepad, Wingman Interceptor are USB
+joysticks, with optional serial port connection, and are not supported yet.
+
+2.4 Gravis GrIP
+~~~~~~~~~~~~~~~
+  Gravis GrIP gamepads are supported by the joy-gravis.c module. It
+currently supports only:
+
+* Gravis GamePad Pro
+* Gravis Xterminator
+
+  Both the pads are be autodetected, and you can even use any combination of
+up to two these pads either chained together or using an Y-cable on a single
+gameport.
+
+  Gravis Blackhawk Digital, and GrIP MultiPort pads are not supported yet.
+GrIP MultiPort support is in the works. Gravis Xcalibur, ArcadeXtreme,
+GamePad Pro/M are joysticks/pads that probably never reached mass
+production. 
+
+2.5 FPGaming A3D and MadCatz A3D
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  The Assasin 3D protocol created by FPGaming, is used both by FPGaming
+themselves and is licensed to MadCatz. A3D devices are supported by the
+joy-assasin.c module. It currently supports:
+
+* FPGaming Assasin 3D
+* MadCatz Panther
+* MadCatz Panther XL
+
+  All these devices are autodetected. Because the Assasin 3D and the Panther
+allow connecting analog joysticks to them, these are supported in this
+driver, too. The driver uses the js_as parameter for the analog joysticks,
+which has the same syntax as js_an for the analog driver.
+
+  The trackball support is far from perfect at this stage of development,
+but should be well usable.
+
+2.6 ThrustMaster DirectConnect (BSP)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  The TM DirectConnect (BSP) protocol is supported by the joy-thrustmaster.c
+module. It currently supports:
+
+* ThrustMaster Millenium 3D Inceptor
+* ThrustMaster 3D Rage Pad
+* ThrustMaster Weapon Control System III
+
+  All these drvices are autodetected, and thus no parameters to the module
+are needed.
+
+  The only tested device was the Millenium. Rage Pad might work, and
+WCS III won't, because important parts of code for that are missing.
+
+2.7 PDPI Lightning 4 gamecards
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  PDPI Lightning 4 gamecards are supported by the joy-lightning.c module.
+This driver is only for analog joysticks connected to the card - if you want
+to use some of the digital devices, you need to use its specific driver. The
+card will work in legacy mode with them, though.
+
+  Since all features of analog joysticks can't be detected, this driver
+needs a command line:
+
+	js_l4=p0,m0,n0,p1,m1,n1,....
+
+  As you can see, it's very similar to the analog drivers command line.
+Actually it is the same except for the meaning of p0. p0 in this case is the
+port the joystick is attached to:
+
+	  p  | Port
+	----------------------------
+	  0  | Primary card, port 1
+	  1  | Primary card, port 2
+	  2  | Primary card, port 3
+	  3  | Primary card, port 4
+	  4  | Secondary card, port 1
+	  5  | Secondary card, port 2
+	  6  | Secondary card, port 3
+	  7  | Secondary card, port 4
+
+  Two cards maximum are allowed in one system, because of the card's design.
+
+  See the description of analog joystick driver for explanations of m0 and
+n0 values.
+
+2.8 Amiga
+~~~~~~~~~
+  Amiga joysticks, connected to an Amiga, are supported by the joy-amiga.c
+driver. Since they can't be autodetected, the driver has a command line.
+
+	js_am=a,b
+
+  a and b define the joysticks connected to the JOY0DAT and JOY1DAT ports of
+the Amiga.
+
+	Value | Joystick type
+	---------------------
+	  0   | None
+	  1   | 1-button digital joystick
+
+  No more joystick types are supported now, but that should change in the
+future if I get an Amiga in the reach of my fingers.
+
+2.9 Game console and 8-bit pads and joysticks
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+See joystick-parport.txt for more info.
+
+3. Troubleshooting
+~~~~~~~~~~~~~~~~~~
+  There is quite a high probability that you run into some problems. For
+testing whether the driver works, if in doubt, use the jstest utility in
+some of its modes. The most useful modes are "normal" - for the 1.x
+interface, and "old" for the "0.x" interface. You run it by typing:
+
+	jstest --normal /dev/js0
+	jstest --old    /dev/js0
+
+  If your trouble stems from the fact the drivers can't detect the attached
+joystick, and/or you decide you need my help (which I will gladly provide),
+please use the joydump utility first. It's created just by typing
+
+	make joydump.o
+
+  in the driver's directory. It is run then by typing
+
+	insmod joydump.o
+
+  in the same directory. It will return a 'device busy' or 'initialization
+failed' error. This is perfectly okay. It has already done it's job. The
+results can be found in the system log. Please send me the results along
+with your problem report.
+
+  Oh, and read the FAQ! :)
+
+4. FAQ
+~~~~~~
+ Q: The driver doesn't find any joysticks connected to my soundcard with the
+message "joy-something: no joysticks found" and "joy-something.o:
+init_module: Device or resource busy." or "Initialization of joy-something
+failed"  What could be the cause?
+  A: The most common cause is that the joystick port on your soundcard is
+not enabled. If it is an ISA PnP card, you'll need isapnptools to configure
+the gameport. Non-PnP cards usually use some option to the sound driver -
+see the sound driver docs and source and enable the port.
+
+  Q: Any access to the joystick devices gives me "Operation not supported by
+device". What am I doing wrong?
+  A: You're running a 2.0 kernel and you forgot to insmod the hardware
+specific module. You not only need the joystick.o, but also one of the other
+joy-*.o files (most usually joy-analog.o), as described in this document,
+section 2. If you are not using modules, then you didn't say 'Y' to any of
+the hardware-specific questions. Again, see section 2. If you did select
+the specific support, and you still get this message, check that you
+selected the right one, and if it still doesn't work, go to the previous
+FAQ.
+
+  Q: Everything is fine, except I get "No such device" error when I try to
+do anything with /dev/js0. What's the cause?
+  A: You're running a 2.1 kernel and you want to read the previous FAQ.
+
+  Q: Upon 'insmod joystick.o' I get a LOT of unresolved symbols, including
+printk and others. Why?
+  A: You either don't have your kernel compiled with module support. If
+that's the cause, re-compile your kernel with module support switched on.
+Or, you use versioned symbols, and don't have -DMODVERSIONS in the joystick
+driver Makefile, or vice versa. Correct the situation by either removing or
+adding -DMODVERSIONS to the Makefile.
+
+  Q: Running 'jstest 1' or 'jscal 1' doesn't work, and returns with "File
+not found" error. What is the problem?
+  A: The command line interface for these tools is different from what
+version 0.8.0 used. You have to specify the whole device name, eg. 'jstest
+/dev/js0'.
+
+  Q: Running 'jstest /dev/js0' results in "File not found" error. What's the
+cause?
+  A: The device files don't exist. Run 'make devs'.
+
+  Q: Is it possible to connect my old Atari/Commodore/Amiga/console joystick
+or pad that uses a 9-pin D-type cannon connector to the serial port of my
+PC?
+  A: Yes, it is possible, but it'll burn your serial port or the pad. It
+won't work, of course.
+
+5. Calibration
+~~~~~~~~~~~~~~
+  For most joysticks you won't need any manual calibration, since the
+joystick should be autocalibrated by the driver automagically. However, with
+some analog joysticks, that either do not use linear resistors, or if you
+want better precision, you can use the jscal program included in the
+joystick package to set better correction coefficients than what the driver
+would choose itself.
+
+6. Programming Interface
 ~~~~~~~~~~~~~~~~~~~~~~~~
   The 1.0 driver uses a new, event based approach to the joystick driver.
 Instead of the user program polling for the joystick values, the joystick
-driver now reports only any changes of its state. See joystick.h and
-jstest.c included in the joystick package for more information. The joystick
-device can be used in either blocking or nonblocking mode and supports
-select() calls.
-
-  For backward compatibility the old interface is still included, but will
-be dropped in the future.
+driver now reports only any changes of its state. See joystick-api.txt,
+joystick.h and jstest.c included in the joystick package for more
+information. The joystick device can be used in either blocking or
+nonblocking mode and supports select() calls.
+
+  For backward compatibility the old (v0.x) interface is still included.
+Any call to the joystick driver using the old interface will return values
+that are compatible to the old interface.  This interface is still limited
+to 2 axes, and applications using it usually decode only 2 buttons, although
+the driver provides up to 32.
 
-5. Credits
+7. Credits
 ~~~~~~~~~~
-  Thanks to the following authors that contributed to the joystick driver
-development:
+  Thanks to the following people who contributed code, ideas or specifications
+to the joystick driver development:
 
-      0.1-0.5		Arthur C. Smith <asmith@cbnewsd.att.com>
-      0.5		Eyal Lebedinsky	<eyal@eyal.emu.id.au>
-      0.6		Jeff Tranter	<tranter@software.mitel.com>
-      0.7 		Carlos Puchol	<cpg@cs.utexas.edu>
-      0.7.1-0.8		Matt Rhoten	<mrhoten@oz.net>
-      0.7.3		Dan Fandrich	<dan@fch.wimsey.bc.ca>
-      0.7.3		Sverker Wilberg	<sverkerw@manila.docs.uu.se>
-      0.8		Hal Maney	<maney@norden.com>
-      0.8		Bernd Schmidt	<crux@pool.informatik.rwth-aachen.de>
-      0.9		Alan Cox	<alan@lxorguk.ukuu.org.uk>
-      0.9.0-1.0.9	Vojtech Pavlik  <vojtech@atrey.karlin.mff.cuni.cz>
-
-6. Change Log
-~~~~~~~~~~~~~
-  The current (1.0.x) version was originally based on the 0.7.3 version of
-the joystick driver, which caused some inconsistencies in version numbering.
-The following log documents all changes done to the driver by the above
-contributors:
-
-Version 0.1	Original version
-		Works but lacks multi-joystick support
-Version 0.2	Added multi-joystick support (minor 0 and 1)
-		Added delay between measuring joystick axis
-		Added scaling ioctl
-Version 0.3	Modified scaling to use ints to prevent kernel
-		panics 8-)
-Version 0.4	Linux 0.99.6 and fixed race condition in js_read.
-		After looking at a schematic of a joystick card
-		it became apparent that any write to the joystick
-		port started ALL the joystick one shots. If the
-		one that we are reading is short enough and the
-		first one to be read, the second one will return
-		bad data if its one shot has not expired when
-		the joystick port is written for the second time.
-		Thus solves the mystery delay problem in 0.2!
-Version 0.5	Upgraded the driver to the 0.99.9 kernel, added
-		joystick support to the make config options,
-		updated the driver to return the buttons as
-		positive logic, and read both axis at once
-		and added some new ioctls.
-Version 0.6	Made necessary changes to work with 0.99.15
-		kernel (and hopefully 1.0). Also did some
-		cleanup: indented code, fixed some typos, wrote
-		man page, etc ...
-Version 0.7	Support for modules
-Version 0.7.1	Fix bug in reading button state of js1
-		Add include so module compiles under recent kernels
-Version 0.7.3	Include directives changed for joystick.h
-		Separated out joystick detection/counting, cleanup
-		Fix for detection of 3-axis joysticks
-		Better detection announcement
-		Added I/O port registration, cleaned up code
-Version 0.8	New read loop
-		Cleaned up #includes to allow #include of joystick.h with
-		gcc -Wall and from g++
-		Made js_init fail if it finds zero joysticks
-		General source/comment cleanup
-		Use of MOD_(INC|DEC)_USE_COUNT
-		Changes to compile correctly under 1.3 in kernel or as module
-Version 0.9	Ported to 2.1.x
-		Reformatted to resemble Linux coding standard
-		Removed semaphore bug (we can dump the lot I think)
-		Fixed xntp timer adjust during joystick timer0 bug
-		Changed variable names to lower case. Kept binary compatibility.
-		Better ioctl names. Kept binary compatibility.
-		Removed 'save_busy'. Just set busy to 1.
-Version 0.9.0	Based on 0.7.3
-		New read function that allows two axes to have the same value
-		New joystick calibration code
-		Real support for 3-axis joysticks
-		CPU speed independent timeouts
-		Reads may happen even for unwhole record size => cat /dev/js0 works
-		Correct error for lseek
-		/dev/js? can be read simultaneously by several processes
-Version 0.9.1	IOCTLs now obey general Linux IOCTL rules ('j' letter assigned)
-		Use of verify_area result codes
-		Fuzz correction added
-		Semaphore and many cli()'s removed
-		Fix for TurboFire joysticks - read buttons always
-		Fix for broken joysticks - return with -ENODEV only if joystick
-		completely disconnected
-		Fix in read function to allow zero results
-		Broken line correction added for broken joysticks (eg. JB-500)
-		Timeouts back separated for easier setting
-		Some fixes and cleanups in read function
-Version 0.9.2	Fixed a typo causing nothing to be working
-Version 1.0.0	Event approach started
-Version 1.0.1	Complete rewrite
-		Compiles but doesn't work
-Version 1.0.2	Works, many bugs fixed, more yet to come
-Version 1.0.3	Tail cutting logic changes & fixes
-		Fix in js_do_bh - no more zero values for axes
-		Lost event changes & fixes
-Version 1.0.4	Kernel command line & module configuration support
-		Better cli()/sti() handling
-		Linux 2.1.25 select => poll changes
-Version 1.0.5	Fixes in calibration routines
-		Better jscal
-Version 1.0.6	Backward compatibility with old js driver added
-		Init value after recalibration bug fixed
-		Using KERN_* printk() codes
-		Finally leaving ALPHA and going beta
-		Cosmetic changes
-Version 1.0.7	Readme update
-		Linux 2.1.89 poll update
-		Compatibility mode fix - refresh data after open
-		Cosmetic changes & cleanup
-Version 1.0.8	Changing the __u16 data to __s16 in event packet
-		The range is now -32727..0..32767
-		Broken line correction needs 2 coefs less
-		Fix to report number of buttons correctly
-		Removed various trailing spaces
-Version 1.0.9	Fix for compatibility mode - don't do correction
-		Fix max number of module parameters
+  0.1-0.5	Arthur C. Smith		<asmith@cbnewsd.att.com>
+  0.5		Eyal Lebedinsky		<eyal@eyal.emu.id.au>
+  0.6		Jeff Tranter		<tranter@software.mitel.com>
+  0.7		Carlos Puchol		<cpg@cs.utexas.edu>
+  0.7.1-0.8	Matt Rhoten		<mrhoten@oz.net>
+  0.7.3		Dan Fandrich		<dan@fch.wimsey.bc.ca>
+  0.7.3		Sverker Wilberg		<sverkerw@manila.docs.uu.se>
+  0.8		Hal Maney		<maney@norden.com>
+  0.8		Bernd Schmidt		<crux@pool.informatik.rwth-aachen.de>
+  0.9		Alan Cox		<alan@lxorguk.ukuu.org.uk>
+  1.1.0		John Markus Bjorndalen	<johnm@cs.uit.no>
+  1.1.0		Boris Muehmer		<mhs@cybernet-ag.de>
+  1.1.0		Robert W. Grubbs	<rwgrubbs@vt.edu>
+  1.1.0		Pete Chown		<pete.chown@skygate.co.uk>
+  1.1.0		Benji York		<benji@cookeville.com>
+  1.1.3		Leslie F. Donaldson	<donaldlf@cs.rose-hulman.edu>
+  1.2.0		Eng-Jon Ong		<ongej@dcs.qmw.ac.uk>
+  1.2.8		Ragnar Hojland Espinosa	<ragnar@lightside.ddns.org>
+  1.2.3-1.2.9	Andree Borrmann		<a.borrmann@tu-bs.de>
+  1.1.0-1.2.9	Brian Gerst		<bgerst@quark.vpplus.com>
+  0.9.0-1.2.11	Vojtech Pavlik		<vojtech@ucw.cz>
+
+  If you think you should be in this list and are not, it's possible that
+I forgot to include you - contact me and I'll correct the error. :)
+
+  Thanks to KYE Systems Europe, who provided me with driver sources for the
+Genius Flight2000 Digital F-23, which happens to be compatible with
+Microsoft SideWinder 3d Pro.
+
+  Thanks to ThrustMaster Inc. who provided me with docs for their digital
+protocol, and to Trystan A Larey-williams <axe@mail.wsu.edu>, who wrote an
+attempt of a driver for them.
+
+  Thanks to Creative Labs Europe, and Ifor Powell <ipowell@cle.creaf.com>,
+who provided me with docs for their first generation gamepad.
+
+  Special thanks go to FP-Gaming, Inc. and James C Barnes <james@fpgaming.com>,
+who provided me with help and detailed information about the Assasin 3D
+protocol and devices, and even sent me a Panther and Panther XL for testing,
+along with cool T-shirts.
+
+  Special thanks to PDPI, Mike Pelkey <pdpi@email.msn.com> and Brand Kvavle
+<BrandK@blackdiamond.com>, for providing me with documentation and example
+code for their L4 gamecard, and sending me the card to test my driver with
+it.
+
+  Thanks to everyone else who helped me develop this package of drivers!
+
+  No thanks to Microsoft, Logitech, and Gravis, who don't release a word
+about their hardware .... :(
+
+8. ChangeLog
+~~~~~~~~~~~~
+  See the ChangeLog file for the log of changes.
 
-7. To do
+9. To do
 ~~~~~~~~
-  Sooner or later I'll get to these:
+  Sooner or later I'll do these:
 
-		Make an optional read routine using Pentium timers for better precision (*)
-		Support for hats and more buttons ala CH Flightstick (*)
-		Support for hats ala TM FCS (*)
-		Include support for MS SideWinder digital mode (*)
-		Create patches for most common programs using joystick
-		Support for cards with HW speed compensation
-		Support for more than one joystick port
+		Fix possible races at open/close
+		Fix possible read/timer races
+		Make the driver SMP-friendly
+		Support joysticks on Linux/m68k/Atari
+		Support joysticks on Linux/FM-TOWNS (?)
+		Support even more joystick types
+
+10. Current driver status
+~~~~~~~~~~~~~~~~~~~~~~~~~
+  OK means tested and not touched till this driver revision, unknown means
+that the driver was changed since last test, broken means doesn't work,
+incomplete means can't work, because vital parts of support are missing.
+
+joystick.c:		2.1.x kernel interface	- OK
+joy-amiga.c:		Multi1 stick		- unknown
+joy-analog.c:		standard joysticks	- OK
+			FCS hats		- OK
+			CHF hats & buttons	- OK
+			XY buttons		- OK
+			UV buttons		- OK
+joy-assasin.c:		MadCatz Panther XL	- OK
+			MadCatz PXL rudder	- OK
+			MadCatz Panther		- OK
+			FPG Assasin 3D		- OK
+joy-console.c:		NES pad			- OK
+			SNES pad		- OK
+			Multi1 stick		- OK
+			Multi2 stick		- OK
+			PSX			- SW OK, HW unreliable
+joy-db9.c:		Multi1 stick		- OK
+			Multi2 stick		- OK
+			Sega Genesis pad	- OK
+			Sega Genesis 5 pad	- OK
+			Sega Genesis 6 pad	- OK
+			Sega Saturn pad		- unknown
+joy-gravis.c		Gravis GamePad Pro	- OK
+			Gravis Xterminator	- OK
+joy-lightning.c		PDPI Lightning 4	- OK
+joy-logitech.c		WingMan Extreme Digital	- OK
+			CyberMan 2		- OK
+			Thunder Pad Digital	- unknown
+joy-sidewinder.c	SW 3D Pro		- OK
+			Genius F-23		- OK
+			SW GP			- OK
+			SW PP			- OK
+			SW FFP			- OK
+joy-thrustmaster.c	Millenium 3D Inceptor	- OK
+			3D-Rage pad		- unknown
+			WCS III			- incomplete
+joy-turbografx.c	Multi1 stick		- OK
 
- (*) - Already in the development 1.1 version.
+Please help me and send me success / failure reports for the drivers,
+I need to know what works, and what needs to be debugged. Thank you.
diff -u --recursive --new-file v2.1.124/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.124/linux/MAINTAINERS	Mon Sep 28 10:51:32 1998
+++ linux/MAINTAINERS	Thu Oct  8 16:40:05 1998
@@ -57,8 +57,7 @@
 W: Web-page with status/info
 S: Status, one of the following:
 
-	Supported:	Someone is actually paid to look after this (wildly
-			improbable).
+	Supported:	Someone is actually paid to look after this.
 	Maintained:	Someone actually looks after it.
 	Odd Fixes:	It has a maintainer but they don't have time to do
 			much other than throw the odd patch in. See below..
@@ -142,8 +141,8 @@
 S:	Maintained
 
 AX.25 NETWORK LAYER
-P:	Jon Naylor
-M:	jsn@cs.nott.ac.uk
+P:	Matthias Welwarsky
+M:	dg2fef@afthd.tu-darmstadt.de
 L:	linux-hams@vger.rutgers.edu
 S:	Maintained
 
@@ -358,6 +357,12 @@
 L:	autofs@linux.kernel.org
 S:	Maintained
 
+LAPB module
+P:	Henner Eisen
+M:	eis@baty.hanse.de
+L:	linux-x25@vger.rutgers.edu
+S:	Maintained
+
 LINUX FOR POWERPC (PREP)
 P:	Cort Dougan
 M:	cort@cs.nmt.edu
@@ -384,6 +389,12 @@
 L:	linux-mac68k@wave.lm.com
 S:	As time permits [Michael confess, you are the mac68k maintainer 8)]
 
+M68K ON HP9000/300
+P:	Philip Blundell
+M:	philb@gnu.org
+W:	http://www.tazenda.demon.co.uk/phil/linux-hp
+S:	Maintained
+
 MENUCONFIG:
 P:	Michael Elizabeth Chastain
 M:	mec@shout.net
@@ -423,8 +434,8 @@
 S:	Maintained
 
 NETROM NETWORK LAYER
-P:	Jon Naylor
-M:	jsn@cs.nott.ac.uk
+P:	Tomi Manninen 
+M:	Tomi.Manninen@hut.fi
 L:	linux-hams@vger.rutgers.edu
 S:	Maintained
 
@@ -529,6 +540,12 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
+ROSE NETWORK LAYER
+P:	Frederic Rible
+M:	frible@teaser.fr
+L:	linux-hams@vger.rutgers.edu
+S:	Maintained
+
 RISCOM8 DRIVER:
 P:	Dmitry Gorodchanin
 M:	pgmdsg@ibi.com
@@ -677,6 +694,12 @@
 P:	Miroslav Zagorac
 M:	zaga@fly.cc.fer.hr
 L:	linux-scsi@vger.rutgers.edu
+S:	Maintained
+
+X.25 NETWORK LAYER
+P:	Henner Eisen
+M:	eis@baty.hanse.de
+L:	linux-x25@vger.rutgers.edu
 S:	Maintained
 
 Z8530 DRIVER FOR AX.25
diff -u --recursive --new-file v2.1.124/linux/Makefile linux/Makefile
--- v2.1.124/linux/Makefile	Mon Oct  5 13:13:35 1998
+++ linux/Makefile	Tue Oct  6 08:53:46 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 124
+SUBLEVEL = 125
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
@@ -35,6 +35,7 @@
 AR	=$(CROSS_COMPILE)ar
 NM	=$(CROSS_COMPILE)nm
 STRIP	=$(CROSS_COMPILE)strip
+OBJDUMP	=$(CROSS_COMPILE)objdump
 MAKE	=make
 GENKSYMS=/sbin/genksyms
 
@@ -155,6 +156,10 @@
 DRIVERS := $(DRIVERS) drivers/zorro/zorro.a
 endif
 
+ifeq ($(CONFIG_FC4),y)
+DRIVERS := $(DRIVERS) drivers/fc4/fc4.a
+endif
+
 ifdef CONFIG_PPC
 DRIVERS := $(DRIVERS) drivers/macintosh/macintosh.a
 endif
@@ -316,11 +321,10 @@
 	if [ -f VIDEO_MODULES ]; then inst_mod VIDEO_MODULES video; fi; \
 	if [ -f FC4_MODULES   ]; then inst_mod FC4_MODULES   fc4;   fi; \
 	\
-	rm -f /tmp/.misc.$$$$ /tmp/.allmods.$$$$; \
-	ls *.o > /tmp/.allmods.$$$$; \
-	echo $$MODULES | tr ' ' '\n' | sort | comm -23 /tmp/.allmods.$$$$ - > /tmp/.misc.$$$$; \
-	if [ -s /tmp/.misc.$$$$ ]; then inst_mod /tmp/.misc.$$$$ misc; fi; \
-	rm -f /tmp/.misc.$$$$ /tmp/.allmods.$$$$; \
+	ls *.o > $$MODLIB/.allmods; \
+	echo $$MODULES | tr ' ' '\n' | sort | comm -23 $$MODLIB/.allmods - > $$MODLIB/.misc; \
+	if [ -s $$MODLIB/.misc ]; then inst_mod $$MODLIB/.misc misc; fi; \
+	rm -f $$MODLIB/.misc $$MODLIB/.allmods; \
 	)
 
 # modules disabled....
diff -u --recursive --new-file v2.1.124/linux/arch/arm/kernel/signal.c linux/arch/arm/kernel/signal.c
--- v2.1.124/linux/arch/arm/kernel/signal.c	Wed Sep  9 14:51:04 1998
+++ linux/arch/arm/kernel/signal.c	Thu Oct  8 09:32:22 1998
@@ -21,17 +21,6 @@
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 
-void checksignals(void)
-{
-	sigset_t *blocked = &current->blocked;
-	unsigned long mask = blocked->sig[0] | sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT);
-	mask &= blocked->sig[1];
-	if (~mask) {
-		printk("Bad signal mask\n");
-		__backtrace();
-	}
-}
-
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
 #define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn))
diff -u --recursive --new-file v2.1.124/linux/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- v2.1.124/linux/arch/i386/boot/setup.S	Sat Sep  5 16:46:40 1998
+++ linux/arch/i386/boot/setup.S	Mon Oct  5 12:59:25 1998
@@ -145,7 +145,7 @@
 	jne	bad_sig
 	jmp	good_sig1
 
-! Routine to print ASCII string at DS:SI
+! Routine to print ASCIIz string at DS:SI
 
 prtstr:	lodsb
 	and	al,al
@@ -345,7 +345,7 @@
 	mov ds,ax
 	mov ds,ax
 	xor ax,ax
-	mov [0x220], ax		! set table length to 0
+	mov [0xa0], ax		! set table length to 0
 	mov ah, #0xc0
 	stc
 	int 0x15		! puts feature table at es:bx
@@ -357,9 +357,13 @@
 	sub ax, #DELTA_INITSEG	! aka #INITSEG
 	mov es,ax
 	mov si,bx
-	mov di,#0x220
+	mov di,#0xa0
 	mov cx,(si)
 	add cx,#2		! table length is a short
+	cmp cx,#0x10
+	jc sysdesc_ok
+	mov cx,#0x10		! we keep only first 16 bytes
+sysdesc_ok:
 	rep
 	movsb
 	pop ds
diff -u --recursive --new-file v2.1.124/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.124/linux/arch/i386/defconfig	Mon Oct  5 13:13:35 1998
+++ linux/arch/i386/defconfig	Thu Oct  8 09:19:26 1998
@@ -193,6 +193,7 @@
 CONFIG_EEXPRESS_PRO100=y
 # CONFIG_NE2K_PCI is not set
 # CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
 # CONFIG_NET_POCKET is not set
 # CONFIG_FDDI is not set
 # CONFIG_DLCI is not set
diff -u --recursive --new-file v2.1.124/linux/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c
--- v2.1.124/linux/arch/i386/kernel/apm.c	Thu Sep 17 17:53:34 1998
+++ linux/arch/i386/kernel/apm.c	Wed Oct  7 18:52:05 1998
@@ -2,6 +2,8 @@
  * APM BIOS driver for Linux
  * Copyright 1994-1998 Stephen Rothwell
  *                     (Stephen.Rothwell@canb.auug.org.au)
+ * Development of this driver was funded by NEC Australia P/L
+ *	and NEC 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
@@ -27,6 +29,7 @@
  * Feb 1998, Version 1.3
  * Feb 1998, Version 1.4
  * Aug 1998, Version 1.5
+ * Sep 1998, Version 1.6
  *
  * History:
  *    0.6b: first version in official kernel, Linux 1.3.46
@@ -51,7 +54,12 @@
  *         C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87
  *    1.5: Fix segment register reloading (in case of bad segments saved
  *         across BIOS call).
- *         Stephen ROthwell
+ *         Stephen Rothwell
+ *    1.6: Cope with complier/assembler differences.
+ *         Only try to turn off the first display device.
+ *         Fix OOPS at power off with no APM BIOS by Jan Echternach
+ *                   <echter@informatik.uni-rostock.de>
+ *         Stephen Rothwell
  *
  * APM 1.1 Reference:
  *
@@ -214,7 +222,8 @@
 /*
  * Save a segment register away
  */
-#define savesegment(seg, where)	__asm__ __volatile__("movl %%" #seg ",%0" : "=m" (where))
+#define savesegment(seg, where) \
+		__asm__ __volatile__("movl %%" #seg ",%0" : "=m" (where))
 
 /*
  * Forward declarations
@@ -264,7 +273,7 @@
 
 static struct timer_list	apm_timer;
 
-static char			driver_version[] = "1.5";	/* no spaces */
+static char			driver_version[] = "1.6";	/* no spaces */
 
 #ifdef APM_DEBUG
 static char *	apm_event_name[] = {
@@ -350,105 +359,95 @@
  * can be zeroed before the call. Unfortunately, we can't do anything
  * about the stack segment/pointer.  Also, we tell the compiler that
  * everything could change.
+ *
+ * Also, we KNOW that for the non error case of apm_bios_call, there
+ * is no useful data returned in the low order 8 bits of eax.
  */
-
-static inline int apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in,
-	u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi)
-{
-	unsigned int old_fs, old_gs;
-	int	error;
-
-#ifdef APM_ZERO_SEGS
-	savesegment(fs, old_fs);
-	savesegment(gs, old_gs);
-#endif
-	__asm__ __volatile__(
-		"pushfl\n\t"
 #ifdef APM_NOINTS
-		"cli\n\t"
+#	define APM_DO_CLI	__cli()
+#else
+#	define APM_DO_CLI
 #endif
 #ifdef APM_ZERO_SEGS
-		"pushl %%ds\n\t"
-		"pushl %%es\n\t"
-		"movl %w9,%%ds\n\t"
-		"movl %w9,%%es\n\t"
-		"movl %w9,%%fs\n\t"
-		"movl %w9,%%gs\n\t"
+#	define APM_DO_SAVE_SEGS \
+		savesegment(fs, saved_fs); \
+		savesegment(gs, saved_gs)
+#	define APM_DO_ZERO_SEGS \
+		"pushl %%ds\n\t" \
+		"pushl %%es\n\t" \
+		"xorl %%edx, %%edx\n\t" \
+		"mov %%dx, %%ds\n\t" \
+		"mov %%dx, %%es\n\t" \
+		"mov %%dx, %%fs\n\t" \
+		"mov %%dx, %%gs\n\t"
+#	define APM_DO_POP_SEGS \
+		"popl %%es\n\t" \
+		"popl %%ds\n\t"
+#	define APM_DO_RESTORE_SEGS \
+		loadsegment(fs, saved_fs); \
+		loadsegment(gs, saved_gs)
+#else
+#	define APM_DO_SAVE_SEGS
+#	define APM_DO_ZERO_SEGS
+#	define APM_DO_POP_SEGS
+#	define APM_DO_RESTORE_SEGS
 #endif
+
+static inline u8 apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in,
+	u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi)
+{
+	unsigned int	saved_fs;
+	unsigned int	saved_gs;
+	unsigned long	flags;
+
+	__save_flags(flags);
+	APM_DO_CLI;
+	APM_DO_SAVE_SEGS;
+	__asm__ __volatile__(APM_DO_ZERO_SEGS
 		"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
-		"movl $0, %%edi\n\t"
-		"jnc 1f\n\t"
-		"movl $1, %%edi\n"
-		"1:\tpopl %%es\n\t"
-		"popl %%ds\n\t"
-		"popfl\n\t"
+		"setc %%al\n\t"
+		APM_DO_POP_SEGS
 		:  "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx),
-		   "=S" (*esi), "=D" (error)
+		   "=S" (*esi)
 		: "a" (eax_in), "b" (ebx_in), "c" (ecx_in)
-#ifdef APM_ZERO_SEGS
-		  , "r" (0)
-#endif
-		: "ax", "bx", "cx", "dx", "si", "di", "bp", "memory");
-#ifdef APM_ZERO_SEGS
-	loadsegment(fs, old_fs);
-	loadsegment(gs, old_gs);
-#endif
-	return error;
+		: "ax", "bx", "cx", "dx", "si", "di", "bp", "memory", "cc");
+	APM_DO_RESTORE_SEGS;
+	__restore_flags(flags);
+	return *eax & 0xff;
 }
 
 /*
  * This version only returns one value (usually an error code)
  */
 
-static inline int apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in, u32 *eax)
+static inline u8 apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in,
+	u32 *eax)
 {
-	unsigned int old_fs, old_gs;
-	int	error;
+	u8		error;
+	unsigned int	saved_fs;
+	unsigned int	saved_gs;
+	unsigned long	flags;
 
-#ifdef APM_ZERO_SEGS
-	savesegment(fs, old_fs);
-	savesegment(gs, old_gs);
-#endif
-	__asm__ __volatile__(
-		"pushfl\n\t"
-#ifdef APM_NOINTS
-		"cli\n\t"
-#endif
-#ifdef APM_ZERO_SEGS
-		"pushl %%ds\n\t"
-		"pushl %%es\n\t"
-		"movl %w5,%%ds\n\t"
-		"movl %w5,%%es\n\t"
-		"movl %w5,%%fs\n\t"
-		"movl %w5,%%gs\n\t"
-#endif
+	__save_flags(flags);
+	APM_DO_CLI;
+	APM_DO_SAVE_SEGS;
+	__asm__ __volatile__(APM_DO_ZERO_SEGS
 		"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
-		"movl $0, %%edi\n\t"
-		"jnc 1f\n\t"
-		"movl $1, %%edi\n"
-		"1:\tpopl %%es\n\t"
-		"popl %%ds\n\t"
-		"popfl\n\t"
-		: "=a" (*eax), "=D" (error)
+		"setc %%bl\n\t"
+		APM_DO_POP_SEGS
+		: "=a" (*eax), "=b" (error)
 		: "a" (eax_in), "b" (ebx_in), "c" (ecx_in)
-#ifdef APM_ZERO_SEGS
-		  , "r" (0)
-#endif
-		: "ax", "bx", "cx", "dx", "si", "di", "bp", "memory");
-#ifdef APM_ZERO_SEGS
-	loadsegment(fs, old_fs);
-	loadsegment(gs, old_gs);
-#endif
+		: "ax", "bx", "cx", "dx", "si", "di", "bp", "memory", "cc");
+	APM_DO_RESTORE_SEGS;
+	__restore_flags(flags);
 	return error;
 }
 
 static int apm_driver_version(u_short *val)
 {
-	int	error;
 	u32	eax;
 
-	error = apm_bios_call_simple(0x530e, 0, *val, &eax);
-	if (error)
+	if (apm_bios_call_simple(0x530e, 0, *val, &eax))
 		return (eax >> 8) & 0xff;
 	*val = eax;
 	return APM_SUCCESS;
@@ -456,14 +455,12 @@
 
 static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info)
 {
-	int	error;
 	u32	eax;
 	u32	ebx;
 	u32	ecx;
 	u32	dummy;
 
-	error = apm_bios_call(0x530b, 0, 0, &eax, &ebx, &ecx, &dummy, &dummy);
-	if (error)
+	if (apm_bios_call(0x530b, 0, 0, &eax, &ebx, &ecx, &dummy, &dummy))
 		return (eax >> 8) & 0xff;
 	*event = ebx;
 	if (apm_bios_info.version < 0x0102)
@@ -473,27 +470,31 @@
 	return APM_SUCCESS;
 }
 
-static int set_power_state(u_short what, u_short state)
+static inline int set_power_state(u_short what, u_short state)
 {
-	int	error;
 	u32	eax;
 
-	error = apm_bios_call_simple(0x5307, what, state, &eax);
-	if (error)
+	if (apm_bios_call_simple(0x5307, what, state, &eax))
 		return (eax >> 8) & 0xff;
 	return APM_SUCCESS;
 }
 
-int apm_set_power_state(u_short state)
+static int apm_set_power_state(u_short state)
 {
 	return set_power_state(0x0001, state);
 }
 
+void apm_power_off(void)
+{
+	if (apm_enabled)
+		(void) apm_set_power_state(APM_STATE_OFF);
+}
+
 #ifdef CONFIG_APM_DISPLAY_BLANK
 /* Called by apm_display_blank and apm_display_unblank when apm_enabled. */
 static int apm_set_display_power_state(u_short state)
 {
-	return set_power_state(0x01ff, state);
+	return set_power_state(0x0100, state);
 }
 #endif
 
@@ -501,12 +502,11 @@
 /* Called by apm_setup if apm_enabled will be true. */
 static int apm_enable_power_management(void)
 {
-	int	error;
 	u32	eax;
 
-	error = apm_bios_call_simple(0x5308,
-			(apm_bios_info.version > 0x100) ? 0x0001 : 0xffff, 1, &eax);
-	if (error)
+	if (apm_bios_call_simple(0x5308,
+			(apm_bios_info.version > 0x100) ? 0x0001 : 0xffff,
+			1, &eax))
 		return (eax >> 8) & 0xff;
 	return APM_SUCCESS;
 }
@@ -514,15 +514,13 @@
 
 static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
 {
-	int	error;
 	u32	eax;
 	u32	ebx;
 	u32	ecx;
 	u32	edx;
 	u32	dummy;
 
-	error = apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy);
-	if (error)
+	if (apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy))
 		return (eax >> 8) & 0xff;
 	*status = ebx;
 	*bat = ecx;
@@ -536,7 +534,6 @@
 				  u_short *bat, u_short *life, u_short *nbat)
 {
 	u_short status;
-	int	error;
 	u32	eax;
 	u32	ebx;
 	u32	ecx;
@@ -551,8 +548,8 @@
 		return apm_get_power_status(&status, bat, life);
 	}
 
-	error = apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax, &ebx, &ecx, &edx, &esi);
-	if (error)
+	if (apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax,
+			&ebx, &ecx, &edx, &esi))
 		return (eax >> 8) & 0xff;
 	*bat = ecx;
 	*life = edx;
@@ -563,11 +560,9 @@
 
 static int apm_engage_power_management(u_short device)
 {
-	int	error;
 	u32	eax;
 
-	error = apm_bios_call_simple(0x530f, device, 1, &eax);
-	if (error)
+	if (apm_bios_call_simple(0x530f, device, 1, &eax))
 		return (eax >> 8) & 0xff;
 	return APM_SUCCESS;
 }
@@ -875,14 +870,12 @@
 int apm_do_idle(void)
 {
 #ifdef CONFIG_APM_CPU_IDLE
-	int	error;
 	u32	dummy;
 
 	if (!apm_enabled)
 		return 0;
 
-	error = apm_bios_call_simple(0x5305, 0, 0, &dummy);
-	if (error)
+	if (apm_bios_call_simple(0x5305, 0, 0, &dummy))
 		return 0;
 
 	clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
diff -u --recursive --new-file v2.1.124/linux/arch/i386/kernel/io_apic.c linux/arch/i386/kernel/io_apic.c
--- v2.1.124/linux/arch/i386/kernel/io_apic.c	Mon Oct  5 13:13:35 1998
+++ linux/arch/i386/kernel/io_apic.c	Tue Oct  6 16:44:00 1998
@@ -103,8 +103,10 @@
 
 /*
  * This is performance-critical, we want to do it O(1)
+ *
+ * the indexing order of this array favors 1:1 mappings
+ * between pins and IRQs.
  */
-static int irq_2_pin[NR_IRQS];
 
 static inline unsigned int io_apic_read(unsigned int reg)
 {
@@ -119,6 +121,15 @@
 }
 
 /*
+ * Re-write a value: to be used for read-modify-write
+ * cycles where the read already set up the index register.
+ */
+static inline void io_apic_modify(unsigned int value)
+{
+	*(IO_APIC_BASE+4) = value;
+}
+
+/*
  * Synchronize the IO-APIC and the CPU by doing
  * a dummy read from the IO-APIC
  */
@@ -128,58 +139,68 @@
 }
 
 /*
- * We disable IO-APIC IRQs by setting their 'destination CPU mask' to
- * zero. Trick by Ramesh Nalluri.
+ * Rough estimation of how many shared IRQs there are, can
+ * be changed anytime.
  */
-static inline void disable_IO_APIC_irq(unsigned int irq)
-{
-	int pin = irq_2_pin[irq];
-	struct IO_APIC_route_entry entry;
+#define MAX_PLUS_SHARED_IRQS NR_IRQS
+#define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + NR_IRQS)
 
-	if (pin != -1) {
-		*(((int *)&entry) + 1) = io_apic_read(0x11 + pin * 2);
-		entry.dest.logical.logical_dest = 0x0;
-		io_apic_write(0x11 + 2 * pin, *(((int *)&entry) + 1));
-		io_apic_sync();
-	}
-}
+static struct irq_pin_list {
+	int pin, next;
+} irq_2_pin[PIN_MAP_SIZE];
 
-static inline void enable_IO_APIC_irq(unsigned int irq)
+/*
+ * The common case is 1:1 IRQ<->pin mappings. Sometimes there are
+ * shared ISA-space IRQs, so we have to support them. We are super
+ * fast in the common case, and fast for shared ISA-space IRQs.
+ */
+static void add_pin_to_irq(unsigned int irq, int pin)
 {
-	int pin = irq_2_pin[irq];
-	struct IO_APIC_route_entry entry;
+	static int first_free_entry = NR_IRQS;
+	struct irq_pin_list *entry = irq_2_pin + irq;
 
-	if (pin != -1) {
-		*(((int *)&entry) + 1) = io_apic_read(0x11 + pin * 2);
-		entry.dest.logical.logical_dest = 0xff;
-		io_apic_write(0x11 + 2 * pin, *(((int *)&entry) + 1));
-	}
-}
+	while (entry->next)
+		entry = irq_2_pin + entry->next;
 
-static inline void mask_IO_APIC_irq(unsigned int irq)
-{
-	int pin = irq_2_pin[irq];
-	struct IO_APIC_route_entry entry;
-
-	if (pin != -1) {
-		*(((int *)&entry) + 0) = io_apic_read(0x10 + pin * 2);
-		entry.mask = 1;
-		io_apic_write(0x10 + 2 * pin, *(((int *)&entry) + 0));
-		io_apic_sync();
-	}
+	if (entry->pin != -1) {
+		entry->next = first_free_entry;
+		entry = irq_2_pin + entry->next;
+		if (++first_free_entry >= PIN_MAP_SIZE)
+			panic("io_apic.c: whoops");
+	}
+	entry->pin = pin;
+}
+
+#define DO_ACTION(name,R,ACTION, FINAL)					\
+									\
+static void name##_IO_APIC_irq(unsigned int irq)			\
+{									\
+	int pin;							\
+	struct irq_pin_list *entry = irq_2_pin + irq;			\
+									\
+	for (;;) {							\
+		unsigned int reg;					\
+		pin = entry->pin;					\
+		if (pin == -1)						\
+			break;						\
+		reg = io_apic_read(0x10 + R + pin*2);			\
+		reg ACTION;						\
+		io_apic_modify(reg);					\
+		if (!entry->next)					\
+			break;						\
+		entry = irq_2_pin + entry->next;			\
+	}								\
+	FINAL;								\
 }
 
-static inline void unmask_IO_APIC_irq(unsigned int irq)
-{
-	int pin = irq_2_pin[irq];
-	struct IO_APIC_route_entry entry;
-
-	if (pin != -1) {
-		*(((int *)&entry) + 0) = io_apic_read(0x10 + pin * 2);
-		entry.mask = 0;
-		io_apic_write(0x10 + 2 * pin, *(((int *)&entry) + 0));
-	}
-}
+/*
+ * We disable IO-APIC IRQs by setting their 'destination CPU mask' to
+ * zero. Trick by Ramesh Nalluri.
+ */
+DO_ACTION( disable, 1, &= 0x00ffffff, io_apic_sync())		/* destination = 0x00 */
+DO_ACTION( enable,  1, |= 0xff000000, )				/* destination = 0xff */
+DO_ACTION( mask,    0, |= 0x00010000, io_apic_sync())		/* mask = 1 */
+DO_ACTION( unmask,  0, &= 0xfffeffff, )				/* mask = 0 */
 
 static void __init clear_IO_APIC_pin(unsigned int pin)
 {
@@ -577,7 +598,7 @@
 		}
 
 		irq = pin_2_irq(idx,pin);
-		irq_2_pin[irq] = pin;
+		add_pin_to_irq(irq, pin);
 
 		if (!IO_APIC_IRQ(irq))
 			continue;
@@ -702,10 +723,20 @@
 		);
 	}
 
-	printk("IRQ to pin mappings:\n");
-	for (i = 0; i < NR_IRQS; i++)
-		printk("%d->%d ", i, irq_2_pin[i]);
-	printk("\n");
+	printk(KERN_DEBUG "IRQ to pin mappings:\n");
+	for (i = 0; i < NR_IRQS; i++) {
+		struct irq_pin_list *entry = irq_2_pin + i;
+		if (entry->pin < 0)
+			continue;
+		printk(KERN_DEBUG "IRQ%d ", i);
+		for (;;) {
+			printk("-> %d", entry->pin);
+			if (!entry->next)
+				break;
+			entry = irq_2_pin + entry->next;
+		}
+		printk("\n");
+	}
 
 	printk(".................................... done.\n");
 
@@ -716,8 +747,10 @@
 {
 	int i, pin;
 
-	for (i = 0; i < NR_IRQS; i++)
-		irq_2_pin[i] = -1;
+	for (i = 0; i < PIN_MAP_SIZE; i++) {
+		irq_2_pin[i].pin = -1;
+		irq_2_pin[i].next = 0;
+	}
 	if (!pirqs_enabled)
 		for (i = 0; i < MAX_PIRQS; i++)
 			pirq_entries[i] =- 1;
diff -u --recursive --new-file v2.1.124/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v2.1.124/linux/arch/i386/kernel/process.c	Mon Oct  5 13:13:35 1998
+++ linux/arch/i386/kernel/process.c	Wed Oct  7 18:52:06 1998
@@ -398,7 +398,7 @@
 void machine_power_off(void)
 {
 #if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
-	apm_set_power_state(APM_STATE_OFF);
+	apm_power_off();
 #endif
 }
 
diff -u --recursive --new-file v2.1.124/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.1.124/linux/arch/i386/kernel/setup.c	Thu Sep 17 17:53:34 1998
+++ linux/arch/i386/kernel/setup.c	Mon Oct  5 12:59:26 1998
@@ -86,13 +86,12 @@
  * This is set up by the setup-routine at boot-time
  */
 #define PARAM	((unsigned char *)empty_zero_page)
+#define SCREEN_INFO (*(struct screen_info *) (PARAM+0))
 #define EXT_MEM_K (*(unsigned short *) (PARAM+2))
 #define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0))
-#ifdef CONFIG_APM
-#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64))
-#endif
+#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40))
 #define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80))
-#define SCREEN_INFO (*(struct screen_info *) (PARAM+0))
+#define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0xa0))
 #define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2))
 #define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8))
 #define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC))
@@ -101,7 +100,6 @@
 #define KERNEL_START (*(unsigned long *) (PARAM+0x214))
 #define INITRD_START (*(unsigned long *) (PARAM+0x218))
 #define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c))
-#define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0x220))
 #define COMMAND_LINE ((char *) (PARAM+2048))
 #define COMMAND_LINE_SIZE 256
 
diff -u --recursive --new-file v2.1.124/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.1.124/linux/arch/i386/kernel/smp.c	Mon Oct  5 13:13:35 1998
+++ linux/arch/i386/kernel/smp.c	Mon Oct  5 13:19:44 1998
@@ -736,7 +736,6 @@
 	/*  Must be done before calibration delay is computed  */
 	mtrr_init_secondary_cpu ();
 #endif
-	stts();
 	smp_callin();
 	while (!smp_commenced)
 		barrier();
@@ -753,6 +752,13 @@
 	struct thread_struct * p = &current->tss;
 
 	/*
+	 * Load up the LDT and the task register.
+	 */
+	asm volatile("lldt %%ax": :"a" (p->ldt));
+	asm volatile("ltr %%ax": :"a" (p->tr));
+	stts();
+
+	/*
 	 * We don't actually need to load the full TSS,
 	 * basically just the stack pointer and the eip.
 	 *
@@ -760,8 +766,7 @@
 	 * to release it as part of the "reschedule" return.
 	 */
 	spin_lock(&scheduler_lock);
-	asm volatile("lldt %%ax": :"a" (p->ldt));
-	asm volatile("ltr %%ax": :"a" (p->tr));
+
 	asm volatile(
 		"movl %0,%%esp\n\t"
 		"jmp *%1"
diff -u --recursive --new-file v2.1.124/linux/arch/i386/lib/delay.c linux/arch/i386/lib/delay.c
--- v2.1.124/linux/arch/i386/lib/delay.c	Sun Dec 21 17:27:18 1997
+++ linux/arch/i386/lib/delay.c	Tue Oct  6 08:55:47 1998
@@ -5,7 +5,9 @@
  *	Copyright (C) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
  *
  *	The __delay function must _NOT_ be inlined as its execution time
- *	depends wildly on alignment on many x86 processors.
+ *	depends wildly on alignment on many x86 processors. The additional
+ *	jump magic is needed to get the timing stable on all the CPU's
+ *	we have to worry about.
  */
 
 #include <linux/sched.h>
@@ -18,7 +20,11 @@
 void __delay(unsigned long loops)
 {
 	__asm__ __volatile__(
- 		"1:\tdecl %0\n\tjns 1b"
+		"\tjmp 1f\n"
+		".align 16\n"
+		"1:\tjmp 2f\n"
+		".align 16\n"
+		"2:\tdecl %0\n\tjns 2b"
 		:/* no outputs */
 		:"a" (loops)
 		:"ax");
diff -u --recursive --new-file v2.1.124/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c
--- v2.1.124/linux/arch/i386/mm/fault.c	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/mm/fault.c	Thu Oct  8 14:20:02 1998
@@ -103,8 +103,13 @@
 
 	tsk = current;
 	mm = tsk->mm;
-	if (in_interrupt())
-		die("page fault from irq handler",regs,error_code);
+
+	/*
+	 * If we're in an interrupt or have no user
+	 * context, we must not take the fault..
+	 */
+	if (in_interrupt() || mm == &init_mm)
+		goto no_context;
 
 	down(&mm->mmap_sem);
 
@@ -194,6 +199,7 @@
 		}
 	}
 
+no_context:
 	/* Are we prepared to handle this kernel fault?  */
 	if ((fixup = search_exception_table(regs->eip)) != 0) {
 		regs->eip = fixup;
@@ -235,8 +241,6 @@
 		page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT];
 		printk(KERN_ALERT "*pte = %08lx\n", page);
 	}
-	lock_kernel();
 	die("Oops", regs, error_code);
 	do_exit(SIGKILL);
-	unlock_kernel();
 }
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/amiga/amiints.c linux/arch/m68k/amiga/amiints.c
--- v2.1.124/linux/arch/m68k/amiga/amiints.c	Mon Aug  3 12:45:44 1998
+++ linux/arch/m68k/amiga/amiints.c	Mon Oct  5 13:54:39 1998
@@ -288,12 +288,14 @@
 	}
 
 	if (irq >= IRQ_AMIGA_CIAB) {
+		cia_set_irq(&ciab_base, (1 << (irq - IRQ_AMIGA_CIAB)));
 		cia_able_irq(&ciab_base, CIA_ICR_SETCLR |
 		             (1 << (irq - IRQ_AMIGA_CIAB)));
 		return;
 	}
 
 	if (irq >= IRQ_AMIGA_CIAA) {
+		cia_set_irq(&ciaa_base, (1 << (irq - IRQ_AMIGA_CIAA)));
 		cia_able_irq(&ciaa_base, CIA_ICR_SETCLR |
 		             (1 << (irq - IRQ_AMIGA_CIAA)));
 		return;
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/amiga/config.c linux/arch/m68k/amiga/config.c
--- v2.1.124/linux/arch/m68k/amiga/config.c	Sat Sep  5 16:46:40 1998
+++ linux/arch/m68k/amiga/config.c	Mon Oct  5 13:54:39 1998
@@ -398,6 +398,28 @@
   /* ensure that the DMA master bit is set */
   custom.dmacon = DMAF_SETCLR | DMAF_MASTER;
 
+  /* don't use Z2 RAM as system memory on Z3 capable machines */
+  if (AMIGAHW_PRESENT(ZORRO3)) {
+    int i, j;
+    u32 disabled_z2mem = 0;
+    for (i = 0; i < m68k_num_memory; i++)
+      if (m68k_memory[i].addr < 16*1024*1024) {
+	if (i == 0) {
+	  /* don't cut off the branch we're sitting on */
+	  printk("Warning: kernel runs in Zorro II memory\n");
+	  continue;
+	}
+	disabled_z2mem += m68k_memory[i].size;
+	m68k_num_memory--;
+	for (j = i; j < m68k_num_memory; j++)
+	  m68k_memory[j] = m68k_memory[j+1];
+	i--;
+      }
+    if (disabled_z2mem)
+      printk("%dK of Zorro II memory will not be used as system memory\n",
+	     disabled_z2mem>>10);
+  }
+
   /* initialize chipram allocator */
   amiga_chip_init ();
 
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/atari/ataints.c linux/arch/m68k/atari/ataints.c
--- v2.1.124/linux/arch/m68k/atari/ataints.c	Tue Jun 23 10:01:20 1998
+++ linux/arch/m68k/atari/ataints.c	Mon Oct  5 13:54:39 1998
@@ -419,6 +419,7 @@
                       unsigned long flags, const char *devname, void *dev_id)
 {
 	int vector;
+	unsigned long oflags = flags;
 
 	/*
 	 * The following is a hack to make some PCI card drivers work,
@@ -427,9 +428,14 @@
 
 	flags &= ~SA_SHIRQ;
 
+	if (flags == SA_INTERRUPT) {
+		printk ("%s: SA_INTERRUPT changed to IRQ_TYPE_SLOW for %s\n",
+			__FUNCTION__, devname);
+		flags = IRQ_TYPE_SLOW;
+	}
 	if (flags < IRQ_TYPE_SLOW || flags > IRQ_TYPE_PRIO) {
-		printk ("%s: Bad irq type %ld requested from %s\n",
-		        __FUNCTION__, flags, devname);
+		printk ("%s: Bad irq type 0x%lx <0x%lx> requested from %s\n",
+		        __FUNCTION__, flags, oflags, devname);
 		return -EINVAL;
 	}
 	if (!IS_VALID_INTNO(irq)) {
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/atari/stram.c linux/arch/m68k/atari/stram.c
--- v2.1.124/linux/arch/m68k/atari/stram.c	Sat Sep  5 16:46:40 1998
+++ linux/arch/m68k/atari/stram.c	Mon Oct  5 13:54:39 1998
@@ -273,7 +273,7 @@
 
 	/* determine whether kernel code resides in ST-RAM (then ST-RAM is the
 	 * first memory block at virtual 0x0) */
-	stram_start = phys_to_virt( 0 );
+	stram_start = (unsigned long)phys_to_virt(0);
 	kernel_in_stram = (stram_start == 0);
 
 	for( i = 0; i < m68k_num_memory; ++i ) {
@@ -1465,9 +1465,10 @@
 	for( p = alloc_list; p; p = p->next ) {
 		if (len + 50 >= PAGE_SIZE)
 			break;
-		PRINT_PROC( "0x%08lx-0x%08lx: %s (",
-					virt_to_phys(p->start),
-					virt_to_phys(p->start+p->size-1), p->owner );
+		PRINT_PROC("0x%08lx-0x%08lx: %s (",
+			   virt_to_phys((void *)p->start),
+			   virt_to_phys((void *)p->start+p->size-1),
+			   p->owner);
 		if (p->flags & BLOCK_STATIC)
 			PRINT_PROC( "static)\n" );
 		else if (p->flags & BLOCK_GFP)
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/config.in linux/arch/m68k/config.in
--- v2.1.124/linux/arch/m68k/config.in	Sat Sep  5 16:46:40 1998
+++ linux/arch/m68k/config.in	Mon Oct  5 13:54:39 1998
@@ -32,6 +32,7 @@
 bool 'Macintosh support' CONFIG_MAC
 if [ "$CONFIG_MAC" = "y" ]; then
   define_bool CONFIG_NUBUS y
+  define_bool CONFIG_M68K_L2_CACHE y
 fi
 bool 'Apollo support' CONFIG_APOLLO
 bool 'VME (Motorola and BVM) support' CONFIG_VME
@@ -155,6 +156,7 @@
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
     bool 'A4091 SCSI support' CONFIG_A4091_SCSI
     bool 'WarpEngine SCSI support' CONFIG_WARPENGINE_SCSI
+    bool 'Blizzard PowerUP 603e+ SCSI' CONFIG_BLZ603EPLUS_SCSI
     bool 'Cyberstorm Mk III SCSI support' CONFIG_CYBERSTORMIII_SCSI
 #    bool 'GVP Turbo 040/060 SCSI support' CONFIG_GVP_TURBO_SCSI
   fi
@@ -171,6 +173,7 @@
 fi
 if [ "$CONFIG_MAC" = "y" ]; then
   bool 'MAC NCR5380 SCSI' CONFIG_MAC_SCSI
+  bool 'MAC NCR53c9[46] SCSI' CONFIG_SCSI_MAC_ESP
 fi
 #dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI
 
@@ -211,6 +214,7 @@
 tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
 if [ "$CONFIG_ZORRO" = "y" ]; then
   tristate 'Ariadne support' CONFIG_ARIADNE
+  tristate 'Ariadne II support' CONFIG_ARIADNE2
   tristate 'A2065 support' CONFIG_A2065
   tristate 'Hydra support' CONFIG_HYDRA
 fi
@@ -222,6 +226,8 @@
 fi
 if [ "$CONFIG_MAC" = "y" ]; then
   bool 'Mac NS 8390 based ethernet cards' CONFIG_DAYNAPORT
+  bool 'AV Macintosh onboard MACE ethernet' CONFIG_MACMACE
+  bool 'Macintosh onboard SONIC ethernet' CONFIG_MACSONIC
 fi
 if [ "$CONFIG_VME" = "y" -a "$CONFIG_MVME16x" = "y" ]; then
   bool 'MVME16x Ethernet support' CONFIG_MVME16x_NET
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/kernel/entry.S linux/arch/m68k/kernel/entry.S
--- v2.1.124/linux/arch/m68k/kernel/entry.S	Sat Sep  5 16:46:40 1998
+++ linux/arch/m68k/kernel/entry.S	Mon Oct  5 13:54:39 1998
@@ -396,7 +396,7 @@
 .data
 ALIGN
 SYMBOL_NAME_LABEL(sys_call_table)
-	.long SYMBOL_NAME(sys_setup)		/* 0 */
+	.long SYMBOL_NAME(sys_ni_syscall)	/* 0  -  old "setup()" system call*/
 	.long SYMBOL_NAME(sys_exit)
 	.long SYMBOL_NAME(sys_fork)
 	.long SYMBOL_NAME(sys_read)
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/kernel/m68k_ksyms.c linux/arch/m68k/kernel/m68k_ksyms.c
--- v2.1.124/linux/arch/m68k/kernel/m68k_ksyms.c	Tue Aug 18 22:02:03 1998
+++ linux/arch/m68k/kernel/m68k_ksyms.c	Thu Oct  8 09:31:52 1998
@@ -7,6 +7,7 @@
 #include <linux/elfcore.h>
 #include <linux/in6.h>
 #include <linux/interrupt.h>
+#include <linux/config.h>
 
 #include <asm/setup.h>
 #include <asm/machdep.h>
@@ -30,9 +31,13 @@
 EXPORT_SYMBOL(m68k_is040or060);
 EXPORT_SYMBOL(cache_push);
 EXPORT_SYMBOL(cache_clear);
+#ifndef CONFIG_SINGLE_MEMORY_CHUNK
 EXPORT_SYMBOL(mm_vtop);
 EXPORT_SYMBOL(mm_ptov);
 EXPORT_SYMBOL(mm_end_of_chunk);
+#endif
+EXPORT_SYMBOL(mm_vtop_fallback);
+EXPORT_SYMBOL(m68k_memory);
 EXPORT_SYMBOL(kernel_map);
 EXPORT_SYMBOL(m68k_debug_device);
 EXPORT_SYMBOL(dump_fpu);
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/kernel/process.c linux/arch/m68k/kernel/process.c
--- v2.1.124/linux/arch/m68k/kernel/process.c	Sat Sep  5 16:46:40 1998
+++ linux/arch/m68k/kernel/process.c	Wed Oct  7 18:52:07 1998
@@ -95,9 +95,6 @@
 
 void machine_power_off(void)
 {
-#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
-	apm_set_power_state(APM_STATE_OFF);
-#endif
 }
 
 void show_regs(struct pt_regs * regs)
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/kernel/traps.c linux/arch/m68k/kernel/traps.c
--- v2.1.124/linux/arch/m68k/kernel/traps.c	Tue Feb 17 13:12:45 1998
+++ linux/arch/m68k/kernel/traps.c	Mon Oct  5 13:54:39 1998
@@ -452,7 +452,7 @@
 #endif
 
 	    errorcode = (mmusr & MMU_I) ? 0 : 1;
-	    if (!(ssw & RW) || ssw & RM)
+	    if (!(ssw & RW) || (ssw & RM))
 		    errorcode |= 2;
 
 	    if (mmusr & (MMU_I | MMU_WP)) {
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mac/adb-bus.c linux/arch/m68k/mac/adb-bus.c
--- v2.1.124/linux/arch/m68k/mac/adb-bus.c	Wed Aug 26 11:37:33 1998
+++ linux/arch/m68k/mac/adb-bus.c	Mon Oct  5 13:54:39 1998
@@ -245,13 +245,16 @@
 		 	via_write(via1, vIFR, SR_INT);
 
 			/* get those pesky clock ticks we missed while booting */
-			for ( i = 0; i < 30; i++) {
+			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
  			 */
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mac/config.c linux/arch/m68k/mac/config.c
--- v2.1.124/linux/arch/m68k/mac/config.c	Tue Aug 18 22:02:03 1998
+++ linux/arch/m68k/mac/config.c	Mon Oct  5 13:54:39 1998
@@ -83,9 +83,9 @@
 
 /* Mac specific timer functions */
 extern unsigned long mac_gettimeoffset (void);
-extern void mac_gettod (int *, int *, int *, int *, int *, int *);
-extern int mac_hwclk (int, struct hwclk_time *);
-extern int mac_set_clock_mmss (unsigned long);
+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 via_init_clock(void (*func)(int, void *, struct pt_regs *));
 
 extern void (*kd_mksound)(unsigned int, unsigned int);
@@ -123,7 +123,7 @@
 	mac_reset();
 }
 
-void mac_sched_init(void (*vector)(int, void *, struct pt_regs *))
+static void mac_sched_init(void (*vector)(int, void *, struct pt_regs *))
 {
 	via_init_clock(vector);
 }
@@ -135,7 +135,7 @@
  * the system time.
  */
 
-void mac_gettod (int *yearp, int *monp, int *dayp,
+static void mac_gettod (int *yearp, int *monp, int *dayp,
 		 int *hourp, int *minp, int *secp)
 {
 	unsigned long time;
@@ -192,7 +192,7 @@
  * TBI: read and write hwclock
  */
 
-int mac_hwclk( int op, struct hwclk_time *t )
+static int mac_hwclk( int op, struct hwclk_time *t )
 {
     return 0;
 }
@@ -201,7 +201,7 @@
  * TBI: set minutes/seconds in hwclock
  */
 
-int mac_set_clock_mmss (unsigned long nowtime)
+static int mac_set_clock_mmss (unsigned long nowtime)
 {
     short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
 
@@ -280,10 +280,26 @@
     return(unknown);
 }
 
+/*
+ *	Flip into 24bit mode for an instant - flushes the L2 cache card. We
+ *	have to disable interrupts for this. Our IRQ handlers will crap 
+ *	themselves if they take an IRQ in 24bit mode!
+ */
+
+static void mac_cache_card_flush(int writeback)
+{
+	unsigned long flags;
+	save_flags(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);
+}
+
 __initfunc(void config_mac(void))
 {
 
-    if (MACH_IS_ATARI || MACH_IS_AMIGA) {
+    if (!MACH_IS_MAC) {
       printk("ERROR: no Mac, but config_mac() called!! \n");
     }
     
@@ -338,7 +354,17 @@
      
     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)
+    {
+    	mach_l2_flush = mac_cache_card_flush;
+    }
     /* goes on forever if timers broken */
 #ifdef MAC_DEBUG_SOUND
     mac_mksound(1000,10);
@@ -349,7 +375,6 @@
      */
 
     nubus_sweep_video();
-
 }	
 
 
@@ -411,23 +436,23 @@
 	{	MAC_MODEL_LCIII,"LC III", MAC_ADB_IISI,	MAC_VIA_IIci,	MAC_SCSI_OLD,	MAC_IDE_NONE,	MAC_SCC_II,	MAC_ETHER_NONE,	MAC_NUBUS},
 
 	/*
-	 *	Quadra (only 68030 ones will actually work!). Not much odd. Video is at
-	 *	0xF9000000, via is like a MacII. We label it differently as some of the
-	 *	stuff connected to VIA2 seems different. Better SCSI chip and ???? onboard ethernet
-	 *	in all cases using a NatSemi SONIC. The 700, 900 and 950 have some I/O chips in the wrong
-	 *	place to confuse us. The 840AV seems to have a scsi location of its own
+	 *	Quadra. Video is at 0xF9000000, via is like a MacII. We label it differently 
+	 *	as some of the stuff connected to VIA2 seems different. Better SCSI chip and 
+	 *	onboard ethernet using a NatSemi SONIC except the 660AV and 840AV which use an 
+	 *	AMD 79C940 (MACE).
+	 *	The 700, 900 and 950 have some I/O chips in the wrong place to
+	 *	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_SONIC,	MAC_NUBUS},
+
+	{	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_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},
-	/* Does the 840AV have ethernet ??? documents seem to indicate its not quite a
-	   Quadra in this respect ? */
-	{	MAC_MODEL_Q840, "Quadra 840AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE,   MAC_SCC_II,	MAC_ETHER_NONE,		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},
@@ -440,7 +465,7 @@
 	{	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_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_QUADRA, 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},
@@ -452,8 +477,8 @@
 	 *	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_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_C650, "Centris 650",   MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_C610, "Centris 610",   MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_C650, "Centris 650",   MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	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},
 
 	/*
@@ -467,10 +492,10 @@
 	{	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_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 190cs", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_PB,   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},
 
 	/*
@@ -535,8 +560,8 @@
 	printk (" Penguin bootinfo data:\n");
 	printk (" Video: addr 0x%lx row 0x%lx depth %lx dimensions %ld x %ld\n", 
 		mac_bi_data.videoaddr, mac_bi_data.videorow, 
-		mac_bi_data.videodepth, (int) (mac_bi_data.dimensions & 0xFFFF), 
-		(int) (mac_bi_data.dimensions >> 16)); 
+		mac_bi_data.videodepth, mac_bi_data.dimensions & 0xFFFF, 
+		mac_bi_data.dimensions >> 16); 
 	printk (" Videological 0x%lx phys. 0x%lx, SCC at 0x%lx \n",
 		mac_bi_data.videological, mac_orig_videoaddr, 
 		mac_bi_data.sccbase); 
@@ -576,8 +601,7 @@
 	  break;
 
 	}
-
-
+	via_configure_base();
 }
 
 void mac_report_hardware(void)
@@ -589,6 +613,8 @@
 {
 	strcpy(str,"Macintosh ");
 	strcat(str, macintosh_config->name);
+	if(mach_l2_flush && !(via_read(via2, vBufB)&VIA2B_vCDis))
+		strcat(str, "(+L2 cache)");
 }
 
 /*
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mac/debug.c linux/arch/m68k/mac/debug.c
--- v2.1.124/linux/arch/m68k/mac/debug.c	Tue Jun 23 10:01:21 1998
+++ linux/arch/m68k/mac/debug.c	Mon Oct  5 13:54:39 1998
@@ -249,7 +249,6 @@
  * TODO: serial debug code
  */
 
-#define SCC_BAS (0x50F04000)
 struct SCC
  {
   u_char cha_b_ctrl;
@@ -260,7 +259,8 @@
   u_char char_dummy3;
   u_char cha_a_data;
  };
-# define scc ((*(volatile struct SCC*)SCC_BAS))
+
+# define scc (*((volatile struct SCC*)mac_bi_data.sccbase))
 
 /* Flag that serial port is already initialized and used */
 int mac_SCC_init_done = 0;
@@ -268,6 +268,8 @@
  * not be repeated; used by kgdb */
 int mac_SCC_reset_done = 0;
 
+static int scc_port = -1;
+
 static struct console mac_console_driver = {
 	"debug",
 	NULL,			/* write */
@@ -282,20 +284,18 @@
 	NULL
 };
 
-static int scc_port;
-
 /* Mac: loops_per_sec min. 1900000 ^= .5 us; MFPDELAY was 0.6 us*/
 
-#define US 1
+#define uSEC 1
 
 static inline void mac_sccb_out (char c)
 {
     int i;
     do {
-	for( i = US; i > 0; --i )
+	for( i = uSEC; i > 0; --i )
 		barrier();
     } while (!(scc.cha_b_ctrl & 0x04)); /* wait for tx buf empty */
-    for( i = US; i > 0; --i )
+    for( i = uSEC; i > 0; --i )
 	barrier();
     scc.cha_b_data = c;
 }
@@ -304,10 +304,10 @@
 {
     int i;
     do {
-	for( i = US; i > 0; --i )
+	for( i = uSEC; i > 0; --i )
 		barrier();
     } while (!(scc.cha_a_ctrl & 0x04)); /* wait for tx buf empty */
-    for( i = US; i > 0; --i )
+    for( i = uSEC; i > 0; --i )
 	barrier();
     scc.cha_a_data = c;
 }
@@ -337,10 +337,10 @@
 {
     int i;
     do {
-	for( i = US; i > 0; --i )
+	for( i = uSEC; i > 0; --i )
 		barrier();
     } while( !(scc.cha_b_ctrl & 0x01) ); /* wait for rx buf filled */
-    for( i = US; i > 0; --i )
+    for( i = uSEC; i > 0; --i )
 	barrier();
     return( scc.cha_b_data );
 }
@@ -349,10 +349,10 @@
 {
     int i;
     do {
-	for( i = US; i > 0; --i )
+	for( i = uSEC; i > 0; --i )
 		barrier();
     } while( !(scc.cha_a_ctrl & 0x01) ); /* wait for rx buf filled */
-    for( i = US; i > 0; --i )
+    for( i = uSEC; i > 0; --i )
 	barrier();
     return( scc.cha_a_data );
 }
@@ -365,10 +365,10 @@
     do {						\
 	int i;						\
 	scc.cha_b_ctrl = (reg);				\
-	for( i = US; i > 0; --i )			\
+	for( i = uSEC; i > 0; --i )			\
 		barrier();				\
 	scc.cha_b_ctrl = (val);				\
-	for( i = US; i > 0; --i )			\
+	for( i = uSEC; i > 0; --i )			\
 		barrier();				\
     } while(0)
 
@@ -376,10 +376,10 @@
     do {						\
 	int i;						\
 	scc.cha_a_ctrl = (reg);				\
-	for( i = US; i > 0; --i )			\
+	for( i = uSEC; i > 0; --i )			\
 		barrier();				\
 	scc.cha_a_ctrl = (val);				\
-	for( i = US; i > 0; --i )			\
+	for( i = uSEC; i > 0; --i )			\
 		barrier();				\
     } while(0)
 
@@ -389,7 +389,7 @@
 #define LONG_DELAY()				\
     do {					\
 	int i;					\
-	for( i = 60*US; i > 0; --i )		\
+	for( i = 60*uSEC; i > 0; --i )		\
 	    barrier();				\
     } while(0)
     
@@ -399,19 +399,21 @@
 void mac_init_scc_port( int cflag, int port )
 #endif
 {
-    extern int mac_SCC_reset_done;
-    static int clksrc_table[9] =
-	/* reg 11: 0x50 = BRG, 0x00 = RTxC, 0x28 = TRxC */
-    	{ 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00 };
-    static int brgsrc_table[9] =
-	/* reg 14: 0 = RTxC, 2 = PCLK */
-    	{ 2, 2, 2, 2, 2, 2, 0, 2, 2 };
-    static int clkmode_table[9] =
-	/* reg 4: 0x40 = x16, 0x80 = x32, 0xc0 = x64 */
-    	{ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x80 };
-    static int div_table[9] =
-	/* reg12 (BRG low) */
-    	{ 208, 138, 103, 50, 24, 11, 1, 0, 0 };
+	extern int mac_SCC_reset_done;
+
+	/*
+	 * baud rates: 1200, 1800, 2400, 4800, 9600, 19.2k, 38.4k, 57.6k, 115.2k
+	 */
+
+	static int clksrc_table[9] =
+		/* reg 11: 0x50 = BRG, 0x00 = RTxC, 0x28 = TRxC */
+    		{ 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00 };
+	static int clkmode_table[9] =
+		/* reg 4: 0x40 = x16, 0x80 = x32, 0xc0 = x64 */
+    		{ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x80 };
+	static int div_table[9] =
+		/* reg12 (BRG low) */
+    		{ 94, 62, 46, 22, 10, 4, 1, 0, 0 };
 
     int baud = cflag & CBAUD;
     int clksrc, clkmode, div, reg3, reg5;
@@ -426,12 +428,10 @@
     clkmode = clkmode_table[baud];
     div     = div_table[baud];
 
-    reg3 = (cflag & CSIZE) == CS8 ? 0xc0 : 0x40;
-    reg5 = (cflag & CSIZE) == CS8 ? 0x60 : 0x20 | 0x82 /* assert DTR/RTS */;
+    reg3 = (((cflag & CSIZE) == CS8) ? 0xc0 : 0x40);
+    reg5 = (((cflag & CSIZE) == CS8) ? 0x60 : 0x20) | 0x82 /* assert DTR/RTS */;
 
-#if 0    
-    if (port) {
-#endif
+    if (port == 1) {
 	    (void)scc.cha_b_ctrl;	/* reset reg pointer */
 	    SCCB_WRITE( 9, 0xc0 );	/* reset */
 	    LONG_DELAY();		/* extra delay after WR9 access */
@@ -442,17 +442,14 @@
 	    SCCB_WRITE( 5, reg5 );
 	    SCCB_WRITE( 9, 0 );		/* no interrupts */
 	    LONG_DELAY();		/* extra delay after WR9 access */
-	    SCCB_WRITE( 10, 0 );		/* NRZ mode */
+	    SCCB_WRITE( 10, 0 );	/* NRZ mode */
 	    SCCB_WRITE( 11, clksrc );	/* main clock source */
 	    SCCB_WRITE( 12, div );	/* BRG value */
 	    SCCB_WRITE( 13, 0 );		/* BRG high byte */
-	    SCCB_WRITE( 14, brgsrc_table[baud] );
-	    SCCB_WRITE( 14, brgsrc_table[baud] | (div ? 1 : 0) );
+	    SCCB_WRITE( 14, 1 );
 	    SCCB_WRITE( 3, reg3 | 1 );
 	    SCCB_WRITE( 5, reg5 | 8 );
-#if 0
-    } else {
-#endif
+    } else if (port == 0) {
 	    (void)scc.cha_a_ctrl;	/* reset reg pointer */
 	    SCCA_WRITE( 9, 0xc0 );	/* reset */
 	    LONG_DELAY();		/* extra delay after WR9 access */
@@ -463,17 +460,15 @@
 	    SCCA_WRITE( 5, reg5 );
 	    SCCA_WRITE( 9, 0 );		/* no interrupts */
 	    LONG_DELAY();		/* extra delay after WR9 access */
-	    SCCA_WRITE( 10, 0 );		/* NRZ mode */
+	    SCCA_WRITE( 10, 0 );	/* NRZ mode */
 	    SCCA_WRITE( 11, clksrc );	/* main clock source */
 	    SCCA_WRITE( 12, div );	/* BRG value */
 	    SCCA_WRITE( 13, 0 );		/* BRG high byte */
-	    SCCA_WRITE( 14, brgsrc_table[baud] );
-	    SCCA_WRITE( 14, brgsrc_table[baud] | (div ? 1 : 0) );
+	    SCCA_WRITE( 14, 1 );
 	    SCCA_WRITE( 3, reg3 | 1 );
 	    SCCA_WRITE( 5, reg5 | 8 );
-#if 0
     }
-#endif
+
     mac_SCC_reset_done = 1;
     mac_SCC_init_done = 1;
 }
@@ -486,18 +481,20 @@
     return;
 #endif
 #ifdef DEBUG_SERIAL
-    if (!strcmp( m68k_debug_device, "ser" )) {
-	strcpy( m68k_debug_device, "ser1" );
-    }
-    if (!strcmp( m68k_debug_device, "ser1" )) {
-	/* ST-MFP Modem1 serial port */
+    if (   !strcmp( m68k_debug_device, "ser"  )
+        || !strcmp( m68k_debug_device, "ser1" )) {
+	/* Mac modem port */
 	mac_init_scc_port( B9600|CS8, 0 );
 	mac_console_driver.write = mac_scca_console_write;
+	mac_console_driver.wait_key = mac_scca_console_wait_key;
+	scc_port = 0;
     }
     else if (!strcmp( m68k_debug_device, "ser2" )) {
-	/* SCC Modem2 serial port */
+	/* Mac printer port */
 	mac_init_scc_port( B9600|CS8, 1 );
 	mac_console_driver.write = mac_sccb_console_write;
+	mac_console_driver.wait_key = mac_sccb_console_wait_key;
+	scc_port = 1;
     }
     if (mac_console_driver.write)
 	register_console(&mac_console_driver);
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mac/macboing.c linux/arch/m68k/mac/macboing.c
--- v2.1.124/linux/arch/m68k/mac/macboing.c	Tue Feb 17 13:12:45 1998
+++ linux/arch/m68k/mac/macboing.c	Mon Oct  5 13:54:39 1998
@@ -27,12 +27,52 @@
 	unsigned long flags;
 	int samples=512;
 
+	if (macintosh_config->ident == MAC_MODEL_C660
+	 || macintosh_config->ident == 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 
+		 * [678]100.  Because Singer and AWACS may have a similar hardware 
+		 * interface, this would imply that the code in drivers/sound/dmasound.c 
+		 * for AWACS could be used as a basis for Singer support.  All we have to
+		 * do is figure out how to do DMA on the 660AV/840AV through the PSC and 
+		 * figure out where the Singer hardware sits in memory. (I'd look in the
+		 * vicinity of the AWACS location in a Power Mac [678]100 first, or the 
+		 * current location of the Apple Sound Chip--ASC--in other Macs.)  The 
+		 * Power Mac [678]100 info can be found in MkLinux Mach kernel sources.
+		 *
+		 * Quoted from Apple's Tech Info Library, article number 16405:
+		 *   "Among desktop Macintosh computers, only the 660AV, 840AV, and Power
+		 *   Macintosh models have 16-bit audio input and output capability 
+		 *   because of the AT&T DSP3210 hardware circuitry and the 16-bit Singer
+		 *   codec circuitry in the AVs.  The Audio Waveform Amplifier and
+		 *   Converter (AWAC) chip in the Power Macintosh performs the same 
+		 *   16-bit I/O functionality.  The PowerBook 500 series computers
+		 *   support 16-bit stereo output, but only mono input."
+		 *
+		 *   http://til.info.apple.com/techinfo.nsf/artnum/n16405
+		 *
+		 * --David Kilzer
+		 */
+
+		return;
+	}
+	
 	if(!inited)
 	{
 		int i=0;
 		int j=0;
 		int k=0;
 		int l=0;
+
+		/*
+		 *	The IIfx strikes again!
+		 */
+		 
+		if(macintosh_config->ident==MAC_MODEL_IIFX)
+			asc_base=(void *)0x50010000;
+
 		for(i=0;i<samples;i++)
 		{
 			asc_base[i]=sine_data[j];
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mac/macints.c linux/arch/m68k/mac/macints.c
--- v2.1.124/linux/arch/m68k/mac/macints.c	Tue Aug 18 22:02:03 1998
+++ linux/arch/m68k/mac/macints.c	Tue Oct  6 09:46:13 1998
@@ -216,7 +216,10 @@
 
 static void via_do_nubus(int slot, void *via, struct pt_regs *regs);
 
-/*#define DEBUG_VIA*/
+/* #define DEBUG_MACINTS */
+/* #define DEBUG_NUBUS_INT */
+/* #define DEBUG_VIA */
+/* #define DEBUG_VIA_NUBUS */
 
 void mac_init_IRQ(void)
 {
@@ -273,13 +276,8 @@
 	 *		Currently, one interrupt per channel is used, solely
 	 *		to pass the correct async_info as parameter!
 	 */
-#if 0	/* want to install debug/SCC shutup routine until SCC init */
-	sys_request_irq(4, mac_SCC_handler, IRQ_FLG_STD, "INT4", mac_SCC_handler);
-#else
+
 	sys_request_irq(4, mac_debug_handler, IRQ_FLG_STD, "INT4", mac_debug_handler);
-#endif
-	/* Alan uses IRQ 5 for SCC ?? */
-	sys_request_irq(5, mac_debug_handler, IRQ_FLG_STD, "INT5", mac_debug_handler);
 
 	/* level 6 */
 	sys_request_irq(6, mac_bang, IRQ_FLG_LOCK, "offswitch", mac_bang);
@@ -289,25 +287,25 @@
 
 	/* 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;
+		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;
@@ -337,16 +335,16 @@
 	via_table[2]     =  NULL;
 	via_table[3]     =  NULL;
 	
-	handler_table[2] =   &rbv_handler[0];
+	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];
+	handler_table[7] =  &nubus_handler[0];
 
-	param_table[2]   =   &rbv_param[0];
+	param_table[2]   =  &rbv_param[0];
 	param_table[3]   =  &scc_param[0];
-	param_table[7]   = &nubus_param[0];
+	param_table[7]   =  &nubus_param[0];
 
 	mac_irqs[2]	 =  &rbv_irqs[0];
 	mac_irqs[3]	 =  &scc_irqs[0];
@@ -356,7 +354,8 @@
 	 *	AV Macs: shutup the PSC ints
 	 */
 	if (macintosh_config->ident == MAC_MODEL_C660
-	 || macintosh_config->ident == MAC_MODEL_Q840) {
+	 || macintosh_config->ident == MAC_MODEL_Q840) 
+	{
 		psc_init();
 
 		handler_table[2] = &psc3_handler[0];
@@ -463,9 +462,15 @@
 		 * 980429 MS: RBV is ok, OSS seems to be differentt
 		 */
 		if (!via2_is_oss)
-			/* CB2 (IRQ) indep. interrupt input, positive edge */
-			/* CA2 (DRQ) indep. interrupt input, positive edge */
-			via_write(via, vPCR, 0x66);
+			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 */
@@ -614,7 +619,7 @@
 	        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 if (srcidx >= SRC_VIA2)
+	else if (srcidx > SRC_VIA2)
 	        via_write(via, (0x104 + 0x10*srcidx), 
 	        	via_read(via, (0x104 + 0x10*srcidx))|0x80|(1<<(irqidx)));
 	else
@@ -636,7 +641,11 @@
 		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 if (srcidx >= SRC_VIA2)
+	/*
+	 *	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
@@ -677,7 +686,7 @@
 		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)
+	else if (srcidx > SRC_VIA2)
 	        pending |= via_read(via, (0x100 + 0x10*srcidx))&(1<<irqidx);
 	else
 		pending |= via_read(via, vIFR)&(1<<irqidx);
@@ -1159,7 +1168,7 @@
 	 * limited verbosity for RBV interrupts (add more if needed)
 	 */
 	if ( events != 1<<3 )		/* SCSI IRQ */
-		printk("oss_irq: irq %d events %x %x %x !\n", irq, srcidx+1, 
+		printk("oss_irq: irq %d srcidx+1 %d events %x %x %x !\n", irq, srcidx+1, 
 			events, adb_ev, nub_ev);
 #endif
 
@@ -1313,7 +1322,7 @@
 	 * limited verbosity for RBV interrupts (add more if needed)
 	 */
 	if ( srcidx == 1 && events != 1<<3 && events != 1<<1 )		/* SCSI IRQ */
-		printk("psc_irq: irq %d events %x !\n", irq, srcidx+1, events);
+		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 
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mac/mackeyb.c linux/arch/m68k/mac/mackeyb.c
--- v2.1.124/linux/arch/m68k/mac/mackeyb.c	Sat Sep  5 16:46:40 1998
+++ linux/arch/m68k/mac/mackeyb.c	Mon Oct  5 13:54:39 1998
@@ -51,6 +51,7 @@
 
 extern struct kbd_struct kbd_table[];
 
+extern void adb_bus_init(void);
 extern void handle_scancode(unsigned char);
 extern void put_queue(int);
 
@@ -59,9 +60,9 @@
 static void keyboard_input(unsigned char *, int, struct pt_regs *);
 static void mouse_input(unsigned char *, int, struct pt_regs *);
 /* Hook for mouse driver */
-void (*adb_mouse_interrupt_hook) (unsigned char *, int);
+void (*adb_mouse_interrupt_hook) (char *, int);
 /* The mouse driver - for debugging */
-extern void mac_mouse_interrupt(char *);
+extern void adb_mouse_interrupt(char *, int);
 /* end keyb */
 
 /* this map indicates which keys shouldn't autorepeat. */
@@ -652,6 +653,8 @@
 
 	printk("Configuring keyboard:\n");
 
+	udelay(3000);
+
 	/* 
 	 * turn on all leds - the keyboard driver will turn them back off 
 	 * via mac_kbd_leds if everything works ok!
@@ -676,6 +679,8 @@
 #if 1
 	printk("configuring coding mode ...\n");
 
+	udelay(3000);
+
 	/* 
 	 * get the keyboard to send separate codes for
 	 * left and right shift, control, option keys. 
@@ -697,6 +702,8 @@
 #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
@@ -732,11 +739,6 @@
 	printk("keyboard init done\n");
 
 	return 0;
-}
-
-/* for "kbd-reset" cmdline param */
-__initfunc(void mac_kbd_reset_setup(char *str, int *ints))
-{
 }
 
 /* for "kbd-reset" cmdline param */
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mac/psc.h linux/arch/m68k/mac/psc.h
--- v2.1.124/linux/arch/m68k/mac/psc.h	Tue Jun 23 10:01:21 1998
+++ linux/arch/m68k/mac/psc.h	Wed Dec 31 16:00:00 1969
@@ -1,18 +0,0 @@
-/*
- *	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).
- */
- 
-#define PSCBASE		0x50F31000
-
-#define pIFR3		0x130
-#define pIFR4		0x140
-#define pIFR5		0x150
-#define pIFR6		0x160
-
-#define pIER3		0x134
-#define pIER4		0x144
-#define pIER5		0x154
-#define pIER6		0x164
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mac/via6522.c linux/arch/m68k/mac/via6522.c
--- v2.1.124/linux/arch/m68k/mac/via6522.c	Tue Aug 18 22:02:03 1998
+++ linux/arch/m68k/mac/via6522.c	Tue Oct  6 09:46:13 1998
@@ -8,16 +8,21 @@
 #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 "psc.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;
@@ -38,9 +43,8 @@
 #define MAC_CLOCK_HIGH		(MAC_CLOCK_TICK>>8)
 
 
-void via_init_clock(void (*func)(int, void *, struct pt_regs *))
+void via_configure_base(void)
 {
-	unsigned char c;
 
 	switch(macintosh_config->via_type)
 	{
@@ -66,6 +70,13 @@
 			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);
 
@@ -179,29 +190,28 @@
 }
 
 /*
- * get time offset between scheduling timer ticks
- * Code stolen from arch/m68k/atari/time.c; underflow check probably
- * wrong.
+ * 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;
+	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;
+	/* 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;
+	ticks = MAC_CLOCK_TICK - ticks;
+	ticks = ticks * 10000L / MAC_CLOCK_TICK;
 
-  return ticks + offset;
+	return ticks + offset;
 }
 
 /*
@@ -218,27 +228,64 @@
 }
 
 /*
- *	The power switch - yes its software!
+ *	The power switch - yes it's software!
  */
- 
+
 void mac_poweroff(void)
 {
-#if 0
+
 	/*
-	 * Powerdown, for the Macs that support it
+	 * MAC_ADB_IISI may need to be moved up here if it doesn't actually
+	 * work using the ADB packet method.  --David Kilzer
 	 */
-	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);
+
+	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();
+		}
 	}
-#endif
-	/* We should never make it this far... */
-	/* XXX - delay do we need to spin here ? */
-	while(1);	/* Just in case .. */
 }
 
 /* 
@@ -247,28 +294,65 @@
  */
 void mac_reset(void)
 {
-	unsigned long flags;
-	unsigned long *reset_hook;
+	/*
+	 * MAC_ADB_IISI may need to be moved up here if it doesn't actually
+	 * work using the ADB packet method.  --David Kilzer
+	 */
 
-	save_flags(flags);
-	cli();
+	if (macintosh_config->adb_type == MAC_ADB_II)
+	{
+		unsigned long flags;
+		unsigned long *reset_hook;
 
-#if 0	/* need ROMBASE in booter */
-#if 0	/* works on some */
-	rom_reset = (boot_info.bi_mac.rombase + 0xa);
-#else	/* testing, doesn't work on SE/30 either */
-	reset_hook = (unsigned long *) (boot_info.bi_mac.rombase + 0x4);
-	printk("ROM reset hook: %p\n", *reset_hook);
-	rom_reset = *reset_hook;
-#endif
-	rom_reset();
+		save_flags(flags);
+		cli();
+
+		/* need ROMBASE in booter */
+
+		/* 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
-	restore_flags(flags);
 
-	/* We never make it this far... */
-	printk(" reboot failed, reboot manually!\n");
-	/* XXX - delay do we need to spin here ? */
-	while(1);	/* Just in case .. */
+		rom_reset();
+
+		restore_flags(flags);
+
+		/* We never make it this far... */
+		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();
+		}
+	}
 }
 
 /*
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mac/via6522.h linux/arch/m68k/mac/via6522.h
--- v2.1.124/linux/arch/m68k/mac/via6522.h	Tue Feb 17 13:12:45 1998
+++ linux/arch/m68k/mac/via6522.h	Mon Oct  5 13:54:39 1998
@@ -6,6 +6,9 @@
  *	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
 
@@ -45,17 +48,32 @@
  *	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
-#define VIA2B_vCDis	0x01
+#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];
 }
 
@@ -109,3 +127,5 @@
 extern void via2_irq(int, void *, struct pt_regs *);
 
 extern void via_setup_keyboard(void);
+
+#endif /* _ASM_VIA6522_H_ */
diff -u --recursive --new-file v2.1.124/linux/arch/m68k/mm/memory.c linux/arch/m68k/mm/memory.c
--- v2.1.124/linux/arch/m68k/mm/memory.c	Sat Sep  5 16:46:40 1998
+++ linux/arch/m68k/mm/memory.c	Mon Oct  5 13:54:39 1998
@@ -18,6 +18,7 @@
 #include <asm/system.h>
 #include <asm/traps.h>
 #include <asm/io.h>
+#include <asm/machdep.h>
 #ifdef CONFIG_AMIGA
 #include <asm/amigahw.h>
 #endif
@@ -554,6 +555,14 @@
  * this?). So we have to push first and then additionally to invalidate.
  */
 
+#ifdef CONFIG_M68K_L2_CACHE
+/*
+ *	Jes was worried about performance (urhh ???) so its optional
+ */
+ 
+extern void (*mach_l2_flush)(int) = NULL;
+#endif
+ 
 /*
  * cache_clear() semantics: Clear any cache entries for the area in question,
  * without writing back dirty entries first. This is useful if the data will
@@ -593,6 +602,10 @@
 		      "movec %/d0,%/cacr"
 		      : : "i" (FLUSH_I_AND_D)
 		      : "d0");
+#ifdef CONFIG_M68K_L2_CACHE
+    if(mach_l2_flush)
+    	mach_l2_flush(0);
+#endif
 }
 
 
@@ -641,6 +654,10 @@
 		      "movec %/d0,%/cacr"
 		      : : "i" (FLUSH_I)
 		      : "d0");
+#ifdef CONFIG_M68K_L2_CACHE
+    if(mach_l2_flush)
+    	mach_l2_flush(1);
+#endif
 }
 
 
diff -u --recursive --new-file v2.1.124/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.1.124/linux/arch/ppc/config.in	Mon Oct  5 13:13:36 1998
+++ linux/arch/ppc/config.in	Mon Oct  5 13:00:33 1998
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.71 1998/09/18 13:25:17 cort Exp $
+# $Id: config.in,v 1.72 1998/10/01 14:09:40 cort Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -27,7 +27,7 @@
 	 CHRP		CONFIG_CHRP \
 	 PowerMac/PReP/CHRP	CONFIG_ALL_PPC \
   	 APUS           CONFIG_APUS \
-	 MBX		CONFIG_MBX" PReP
+	 MBX		CONFIG_MBX" PowerMac
 endmenu
 
 if [ "$CONFIG_MBX" = "y" ];then
@@ -184,10 +184,10 @@
 tristate 'Sound card support' CONFIG_SOUND
 if [ "$CONFIG_SOUND" != "n" ]; then
   if [ "$CONFIG_APUS" = "y" -o "$CONFIG_PMAC" = "y" ]; then
-    tristate 'Amiga or PowerMac DMA sound support' CONFIG_DMASOUND
+     tristate 'Amiga or PowerMac DMA sound support' CONFIG_DMASOUND
   fi
   if [ "$CONFIG_PREP" = "y" -o "$CONFIG_CHRP" = "y" ]; then
-    source drivers/sound/Config.in
+     source drivers/sound/Config.in
   fi
 fi
 
diff -u --recursive --new-file v2.1.124/linux/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c
--- v2.1.124/linux/arch/ppc/kernel/setup.c	Mon Oct  5 13:13:36 1998
+++ linux/arch/ppc/kernel/setup.c	Wed Oct  7 18:52:08 1998
@@ -226,10 +226,7 @@
 	case _MACH_prep:
 		machine_restart(NULL);
 	case _MACH_apus:
-#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
-		apm_set_power_state(APM_STATE_OFF);
 		for (;;);
-#endif
 	}
 	for (;;);
 #else /* CONFIG_MBX */
diff -u --recursive --new-file v2.1.124/linux/arch/sparc/kernel/sun4d_smp.c linux/arch/sparc/kernel/sun4d_smp.c
--- v2.1.124/linux/arch/sparc/kernel/sun4d_smp.c	Mon Oct  5 13:13:37 1998
+++ linux/arch/sparc/kernel/sun4d_smp.c	Mon Oct  5 12:28:08 1998
@@ -8,6 +8,7 @@
 
 #include <asm/head.h>
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/tasks.h>
diff -u --recursive --new-file v2.1.124/linux/arch/sparc64/kernel/sparc64_ksyms.c linux/arch/sparc64/kernel/sparc64_ksyms.c
--- v2.1.124/linux/arch/sparc64/kernel/sparc64_ksyms.c	Mon Oct  5 13:13:38 1998
+++ linux/arch/sparc64/kernel/sparc64_ksyms.c	Mon Oct  5 10:04:35 1998
@@ -1,4 +1,4 @@
-/* $Id: sparc64_ksyms.c,v 1.41 1998/10/04 08:44:16 davem Exp $
+/* $Id: sparc64_ksyms.c,v 1.42 1998/10/05 03:18:50 davem Exp $
  * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -62,6 +62,7 @@
 extern void *__memscan_generic(void *, int, size_t);
 extern int __memcmp(const void *, const void *, __kernel_size_t);
 extern int __strncmp(const char *, const char *, __kernel_size_t);
+extern __kernel_size_t __strlen(const char *);
 extern char saved_command_line[];
 extern char *getname32(u32 name);
 extern void linux_sparc_syscall(void);
diff -u --recursive --new-file v2.1.124/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v2.1.124/linux/drivers/block/ide-tape.c	Wed Sep  9 14:51:07 1998
+++ linux/drivers/block/ide-tape.c	Wed Oct  7 15:54:44 1998
@@ -45,7 +45,7 @@
  * flag, can be configured by issuing an ioctl to the block device interface,
  * as any other ide device.
  *
- * Our own ide-tape ioctl's can can be issued to either the block device or
+ * Our own ide-tape ioctl's can be issued to either the block device or
  * the character device interface.
  *
  * Maximal throughput with minimal bus load will usually be achieved in the
@@ -276,7 +276,7 @@
  *		loop which checks if the pipeline is empty, and if it is, we
  *		increase the maximum number of stages as necessary until we
  *		reach the optimum value which just manages to keep the tape
- *		busy with with minimum allocated memory or until we reach
+ *		busy with minimum allocated memory or until we reach
  *		IDETAPE_MAX_PIPELINE_STAGES.
  *
  * Concerning (2):
diff -u --recursive --new-file v2.1.124/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.1.124/linux/drivers/block/ide.c	Thu Sep 17 17:53:35 1998
+++ linux/drivers/block/ide.c	Wed Oct  7 15:54:44 1998
@@ -1434,9 +1434,9 @@
  * This function issues a special IDE device request
  * onto the request queue.
  *
- * If action is ide_wait, then then rq is queued at the end of
- * the request queue, and the function sleeps until it has been
- * processed.  This is for use when invoked from an ioctl handler.
+ * If action is ide_wait, then the rq is queued at the end of the
+ * request queue, and the function sleeps until it has been processed.
+ * This is for use when invoked from an ioctl handler.
  *
  * If action is ide_preempt, then the rq is queued at the head of
  * the request queue, displacing the currently-being-processed
diff -u --recursive --new-file v2.1.124/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.1.124/linux/drivers/block/ide.h	Thu Sep 17 17:53:35 1998
+++ linux/drivers/block/ide.h	Thu Oct  8 21:31:26 1998
@@ -611,9 +611,9 @@
  * This function issues a special IDE device request
  * onto the request queue.
  *
- * If action is ide_wait, then then rq is queued at the end of
- * the request queue, and the function sleeps until it has been
- * processed.  This is for use when invoked from an ioctl handler.
+ * If action is ide_wait, then the rq is queued at the end of the
+ * request queue, and the function sleeps until it has been processed.
+ * This is for use when invoked from an ioctl handler.
  *
  * If action is ide_preempt, then the rq is queued at the head of
  * the request queue, displacing the currently-being-processed
diff -u --recursive --new-file v2.1.124/linux/drivers/block/md.c linux/drivers/block/md.c
--- v2.1.124/linux/drivers/block/md.c	Wed Aug 26 11:37:35 1998
+++ linux/drivers/block/md.c	Tue Oct  6 08:56:36 1998
@@ -696,7 +696,6 @@
     RO_IOCTLS(inode->i_rdev,arg);
     
     default:
-    printk ("Unknown md_ioctl %d\n", cmd);
     return -EINVAL;
   }
 
diff -u --recursive --new-file v2.1.124/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.124/linux/drivers/char/Config.in	Mon Sep 28 10:51:33 1998
+++ linux/drivers/char/Config.in	Thu Oct  8 17:25:16 1998
@@ -124,8 +124,10 @@
   fi
 fi
 tristate '/dev/nvram support' CONFIG_NVRAM
-tristate 'PC joystick support' CONFIG_JOYSTICK
-
+tristate 'Joystick support' CONFIG_JOYSTICK
+if [ "$CONFIG_JOYSTICK" != "n" ]; then
+  source drivers/char/joystick/Config.in
+fi
 mainmenu_option next_comment
 comment 'Ftape, the floppy tape device driver'
 tristate 'Ftape (QIC-80/Travan) support' CONFIG_FTAPE
diff -u --recursive --new-file v2.1.124/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.1.124/linux/drivers/char/Makefile	Mon Oct  5 13:13:39 1998
+++ linux/drivers/char/Makefile	Thu Oct  8 17:25:16 1998
@@ -11,7 +11,7 @@
 
 SUB_DIRS     := 
 MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS) ftape
+ALL_SUB_DIRS := $(SUB_DIRS) ftape joystick
 
 #
 # This file contains the font map for the default (hardware) font
@@ -159,10 +159,12 @@
 endif
 
 ifeq ($(CONFIG_JOYSTICK),y)
-L_OBJS += joystick.o
+L_OBJS += joystick/js.o
+SUB_DIRS += joystick
+MOD_SUB_DIRS += joystick
 else
   ifeq ($(CONFIG_JOYSTICK),m)
-  M_OBJS += joystick.o
+  MOD_SUB_DIRS += joystick
   endif
 endif
 
diff -u --recursive --new-file v2.1.124/linux/drivers/char/console.c linux/drivers/char/console.c
--- v2.1.124/linux/drivers/char/console.c	Mon Oct  5 13:13:39 1998
+++ linux/drivers/char/console.c	Wed Oct  7 11:18:31 1998
@@ -99,6 +99,7 @@
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/bitops.h>
 
 #include <asm/linux_logo.h>
 
@@ -1953,23 +1954,29 @@
 	}
 }
 
+#ifdef CONFIG_VT_CONSOLE
+
 /*
  *	Console on virtual terminal
+ *
+ * NOTE NOTE NOTE! This code can do no global locking. In particular,
+ * we can't disable interrupts or bottom half handlers globally, because
+ * we can be called from contexts that hold critical spinlocks, and
+ * trying do get a global lock at this point will lead to deadlocks.
  */
 
-#ifdef CONFIG_VT_CONSOLE
 void vt_console_print(struct console *co, const char * b, unsigned count)
 {
 	int currcons = fg_console;
 	unsigned char c;
-	static int printing = 0;
+	static unsigned long printing = 0;
 	const ushort *start;
 	ushort cnt = 0;
 	ushort myx = x;
 
-	if (!printable || printing)
-		return;	 /* console not yet initialized */
-	printing = 1;
+	/* console busy or not yet initialized */
+	if (!printable || test_and_set_bit(0, &printing))
+		return;
 
 	if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1))
 		currcons = kmsg_redirect - 1;
@@ -1981,7 +1988,7 @@
 	}
 
 	if (vcmode != KD_TEXT)
-		return;
+		goto quit;
 
 	/* undraw cursor first */
 	if (IS_FG)
@@ -1991,11 +1998,8 @@
 
 	/* Contrived structure to try to emulate original need_wrap behaviour
 	 * Problems caused when we have need_wrap set on '\n' character */
-	disable_bh(CONSOLE_BH);	
 	while (count--) {
-		enable_bh(CONSOLE_BH);
 		c = *b++;
-		disable_bh(CONSOLE_BH);
 		if (c == 10 || c == 13 || c == 8 || need_wrap) {
 			if (cnt > 0) {
 				if (IS_VISIBLE)
@@ -2037,12 +2041,11 @@
 			need_wrap = 1;
 		}
 	}
-	enable_bh(CONSOLE_BH);
 	set_cursor(currcons);
 	poke_blanked_console();
 
 quit:
-	printing = 0;
+	clear_bit(0, &printing);
 }
 
 static kdev_t vt_console_device(struct console *c)
diff -u --recursive --new-file v2.1.124/linux/drivers/char/i2c.c linux/drivers/char/i2c.c
--- v2.1.124/linux/drivers/char/i2c.c	Sun Jul 26 11:57:15 1998
+++ linux/drivers/char/i2c.c	Tue Oct  6 09:39:49 1998
@@ -21,7 +21,7 @@
 #define I2C_DEBUG(x)  if (i2c_debug) (x)
 
 static int scan      = 0;
-static int verbose   = 1;
+static int verbose   = 0;
 static int i2c_debug = 0;
 
 MODULE_PARM(scan,"i");
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/Config.in linux/drivers/char/joystick/Config.in
--- v2.1.124/linux/drivers/char/joystick/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/Config.in	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,19 @@
+#
+# Joystick lowlevel driver configuration
+#
+
+dep_tristate '   Classic PC analog joysticks' CONFIG_JOY_ANALOG $CONFIG_JOYSTICK
+dep_tristate '   FPGaming and MadCatz A3D controllers' CONFIG_JOY_ASSASIN $CONFIG_JOYSTICK
+dep_tristate '   Gravis GrIP gamepads' CONFIG_JOY_GRAVIS $CONFIG_JOYSTICK
+dep_tristate '   Logitech Digital joysticks' CONFIG_JOY_LOGITECH $CONFIG_JOYSTICK
+dep_tristate '   Microsoft SideWinder, Genius Digital joysticks' CONFIG_JOY_SIDEWINDER $CONFIG_JOYSTICK
+dep_tristate '   ThrustMaster DirectConnect (BSP) joysticks' CONFIG_JOY_THRUSTMASTER $CONFIG_JOYSTICK
+dep_tristate '   PDPI Lightning L4 gamecards' CONFIG_JOY_LIGHTNING $CONFIG_JOYSTICK
+if [ "$CONFIG_PARPORT" != "n" ]; then
+  dep_tristate '   NES, SNES, PSX, Multisystem gamepads' CONFIG_JOY_CONSOLE $CONFIG_JOYSTICK
+  dep_tristate '   Sega, Multisystem gamepads' CONFIG_JOY_DB9 $CONFIG_JOYSTICK
+  dep_tristate '   TurboGraFX Multisystem joystick interface' CONFIG_JOY_TURBOGRAFX $CONFIG_JOYSTICK
+fi  
+if [ "$CONFIG_AMIGA" = "y" ]; then
+  dep_tristate '   Amiga joysticks' CONFIG_JOY_AMIGA $CONFIG_JOYSTICK
+fi
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/Makefile linux/drivers/char/joystick/Makefile
--- v2.1.124/linux/drivers/char/joystick/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/Makefile	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,112 @@
+#
+# Makefile for the joystick 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..
+#
+
+O_TARGET := js.o
+O_OBJS   :=
+M_OBJS   :=
+
+ifeq ($(CONFIG_JOYSTICK),y)
+O_OBJS += joystick.o
+else
+  ifeq ($(CONFIG_JOYSTICK),m)
+  M_OBJS += joystick.o
+  endif
+endif
+
+ifeq ($(CONFIG_JOY_AMIGA),y)
+O_OBJS += joy-amiga.o
+else
+  ifeq ($(CONFIG_JOY_AMIGA),m)
+  M_OBJS += joy-amiga.o
+  endif
+endif
+
+ifeq ($(CONFIG_JOY_ANALOG),y)
+O_OBJS += joy-analog.o
+else
+  ifeq ($(CONFIG_JOY_ANALOG),m)
+  M_OBJS += joy-analog.o
+  endif
+endif
+
+ifeq ($(CONFIG_JOY_ASSASIN),y)
+O_OBJS += joy-assasin.o
+else
+  ifeq ($(CONFIG_JOY_ASSASIN),m)
+  M_OBJS += joy-assasin.o
+  endif
+endif
+
+ifeq ($(CONFIG_JOY_CONSOLE),y)
+O_OBJS += joy-console.o
+else
+  ifeq ($(CONFIG_JOY_CONSOLE),m)
+  M_OBJS += joy-console.o
+  endif
+endif
+
+ifeq ($(CONFIG_JOY_DB9),y)
+O_OBJS += joy-db9.o
+else
+  ifeq ($(CONFIG_JOY_DB9),m)
+  M_OBJS += joy-db9.o
+  endif
+endif
+
+ifeq ($(CONFIG_JOY_GRAVIS),y)
+O_OBJS += joy-gravis.o
+else
+  ifeq ($(CONFIG_JOY_GRAVIS),m)
+  M_OBJS += joy-gravis.o
+  endif
+endif
+
+ifeq ($(CONFIG_JOY_LIGHTNING),y)
+O_OBJS += joy-lightning.o
+else
+  ifeq ($(CONFIG_JOY_LIGHTNING),m)
+  M_OBJS += joy-lightning.o
+  endif
+endif
+
+ifeq ($(CONFIG_JOY_LOGITECH),y)
+O_OBJS += joy-logitech.o
+else
+  ifeq ($(CONFIG_JOY_LOGITECH),m)
+  M_OBJS += joy-logitech.o
+  endif
+endif
+
+ifeq ($(CONFIG_JOY_SIDEWINDER),y)
+O_OBJS += joy-sidewinder.o
+else
+  ifeq ($(CONFIG_JOY_SIDEWINDER),m)
+  M_OBJS += joy-sidewinder.o
+  endif
+endif
+
+ifeq ($(CONFIG_JOY_THRUSTMASTER),y)
+O_OBJS += joy-thrustmaster.o
+else 
+  ifeq ($(CONFIG_JOY_THRUSTMASTER),m)
+  M_OBJS += joy-thrustmaster.o
+  endif
+endif
+
+ifeq ($(CONFIG_JOY_TURBOGRAFX),y)
+O_OBJS += joy-turbografx.o
+else 
+  ifeq ($(CONFIG_JOY_TURBOGRAFX),m)
+  M_OBJS += joy-turbografx.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-amiga.c linux/drivers/char/joystick/joy-amiga.c
--- v2.1.124/linux/drivers/char/joystick/joy-amiga.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/joy-amiga.c	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,157 @@
+/*
+ *  joy-amiga.c  Version 1.2
+ *
+ *  Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * microswitch based joystick connected to Amiga joystick port.
+ */
+
+/*
+ * 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@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/amigahw.h>
+
+static struct js_port* js_am_port __initdata = NULL;
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_PARM(js_am, "1-2i");
+
+static int js_am[]={0,0};
+
+/*
+ * js_am_read() reads and Amiga joystick data.
+ */
+
+static int js_am_read(void *info, int **axes, int **buttons)
+{
+	int data = 0;
+
+	switch (*(int*)info) {
+		case 0:
+			data = ~custom.joy0dat;
+			buttons[0][0] = (~ciaa.pra >> 6) & 1;
+			break;
+
+		case 1:
+			data = ~custom.joy1dat;
+			buttons[0][0] = (~ciaa.pra >> 7) & 1;
+			break;
+
+		default:
+			return -1;
+	}
+
+	axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1);
+	data = ~(data ^ (data << 1));
+	axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1);
+
+	return 0;
+}
+
+/*
+ * js_am_open() is a callback from the file open routine.
+ */
+
+static int js_am_open(struct js_dev *jd)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_am_close() is a callback from the file release routine.
+ */
+
+static int js_am_close(struct js_dev *jd)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_am_init_corr() initializes correction values of
+ * Amiga joysticks.
+ */
+
+static void __init js_am_init_corr(struct js_corr **corr)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		corr[0][i].type = JS_CORR_BROKEN;
+		corr[0][i].prec = 0;
+		corr[0][i].coef[0] = 0;
+		corr[0][i].coef[1] = 0;
+		corr[0][i].coef[2] = (1 << 29);
+		corr[0][i].coef[3] = (1 << 29);
+	}
+}
+
+#ifndef MODULE
+void __init js_am_setup(char *str, int *ints)
+{
+	int i;
+	for (i = 0; i <= ints[0] && i < 2; i++) js_am[i] = ints[i+1];
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_am_init(void)
+#endif
+{
+	int i;
+
+	for (i = 0; i < 2; i++)
+		if (js_am[i]) {
+			js_am_port = js_register_port(js_am_port, &i, 1, sizeof(int), js_am_read);
+			printk(KERN_INFO "js%d: Amiga joystick at joy%ddat\n",
+				js_register_device(js_am_port, 0, 2, 1, "Amiga joystick", js_am_open, js_am_close), i);
+			js_am_init_corr(js_am_port->corr);
+		}
+	if (js_am_port) return 0;
+
+#ifdef MODULE
+	printk(KERN_WARNING "joy-amiga: no joysticks specified\n");
+#endif
+
+	return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	while (js_am_port) {
+		if (js_am_port->devs[0])
+			js_unregister_device(js_am_port->devs[0]);
+		js_am_port = js_unregister_port(js_am_port);
+	}
+}
+#endif
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-analog.c linux/drivers/char/joystick/joy-analog.c
--- v2.1.124/linux/drivers/char/joystick/joy-analog.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/joy-analog.c	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,208 @@
+/*
+ *  joy-analog.c  Version 1.2
+ *
+ *  Copyright (c) 1996-1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * up to two analog (or CHF/FCS) joysticks on a single joystick port.
+ */
+
+/*
+ * 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@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#define JS_AN_MAX_TIME 3000
+
+static int js_an_port_list[] __initdata = {0x201, 0};
+static struct js_port* js_an_port __initdata = NULL;
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_PARM(js_an, "2-24i");
+
+static int js_an[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0};
+
+#include "joy-analog.h"
+
+/*
+ * js_an_read() reads analog joystick data.
+ */
+
+static int js_an_read(void *xinfo, int **axes, int **buttons)
+{
+	struct js_an_info *info = xinfo;
+	unsigned char buf[4];
+	int time[4];
+	unsigned char u, v, a;
+	unsigned int t, t1;
+	int i, j;
+	int timeout;
+	int io = info->io;
+
+	timeout = (JS_AN_MAX_TIME * js_time_speed_a) >> 10;
+
+	info->buttons = (~inb(io) & JS_AN_BUTTONS_STD) >> 4;
+
+	i = 0;
+	u = a = ((info->mask[0] | info->mask[1]) & JS_AN_AXES_STD) | (info->extensions & JS_AN_HAT_FCS)
+	      | ((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2) | ((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4);
+
+	outb(inb(io),io);
+	t = js_get_time_a();
+	do {
+		v = inb(io) & a;
+		t1 = js_get_time_a();
+		if (u ^ v) {
+			time[i] = js_delta_a(t1,t);
+			buf[i] = u ^ v;
+			u = v;
+			i++;
+		}
+	} while (v && js_delta_a(t1,t) < timeout);
+
+	for (--i; i >= 0; i--)
+		for (j = 0; j < 4; j++)
+			if (buf[i] & (1 << j)) info->axes[j] = (time[i] << 10) / js_time_speed_a;
+
+	js_an_decode(info, axes, buttons);
+
+	return 0;
+}
+
+/*
+ * js_an_open() is a callback from the file open routine.
+ */
+
+static int js_an_open(struct js_dev *jd)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_an_close() is a callback from the file release routine.
+ */
+
+static int js_an_close(struct js_dev *jd)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_an_probe() probes for analog joysticks.
+ */
+
+static struct js_port __init *js_an_probe(int io, int mask0, int mask1, struct js_port *port)
+{
+	struct js_an_info info;
+	int i, numdev;
+	unsigned char u;
+
+	if (io < 0) return port;
+
+	if (check_region(io, 1)) return port;
+
+	if (((u = inb(io)) & 3) == 3) return port;
+	outb(u,io);
+	u = inb(io);
+	udelay(JS_AN_MAX_TIME);
+	u = (inb(io) ^ u) & u;
+
+	if (!u) return port;
+	if (u & 0xf0) return port;
+
+	if ((numdev = js_an_probe_devs(&info, u, mask0, mask1, port)) <= 0)
+		return port;
+
+	info.io = io;
+	request_region(info.io, 1, "joystick (analog)");
+	port = js_register_port(port, &info, numdev, sizeof(struct js_an_info), js_an_read);
+
+	for (i = 0; i < numdev; i++)
+		printk(KERN_INFO "js%d: %s at %#x\n",
+			js_register_device(port, i, js_an_axes(i, &info), js_an_buttons(i, &info),
+				js_an_name(i, &info), js_an_open, js_an_close),
+			js_an_name(i, &info), info.io);
+
+	js_an_read(port->info, port->axes, port->buttons);
+	js_an_init_corr(port->info, port->axes, port->corr, 8);
+
+	return port;
+}
+
+#ifndef MODULE
+void __init js_an_setup(char *str, int *ints)
+{
+	int i;
+	for (i = 0; i <= ints[0] && i < 24; i++) js_an[i] = ints[i+1];
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_an_init(void)
+#endif
+{
+	int i;
+
+	if (js_an[0] >= 0) {
+		for (i = 0; (js_an[i*3] >= 0) && i < 8; i++)
+			js_an_port = js_an_probe(js_an[i*3], js_an[i*3+1], js_an[i*3+2], js_an_port);
+	} else {
+		for (i = 0; js_an_port_list[i]; i++)
+			js_an_port = js_an_probe(js_an_port_list[i], 0, 0, js_an_port);
+	}
+	if (js_an_port) return 0;
+
+#ifdef MODULE
+	printk(KERN_WARNING "joy-analog: no joysticks found\n");
+#endif
+
+	return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	int i;
+	struct js_an_info *info;
+
+	while (js_an_port) {
+		for (i = 0; i < js_an_port->ndevs; i++)
+			if (js_an_port->devs[i])
+				js_unregister_device(js_an_port->devs[i]);
+		info = js_an_port->info;
+		release_region(info->io, 1);
+		js_an_port = js_unregister_port(js_an_port);
+	}
+
+}
+#endif
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-analog.h linux/drivers/char/joystick/joy-analog.h
--- v2.1.124/linux/drivers/char/joystick/joy-analog.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/joy-analog.h	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,297 @@
+/*
+ *  joy-analog.h  Version 1.2
+ *
+ *  Copyright (c) 1996-1998 Vojtech Pavlik
+ */
+
+/*
+ * This file is designed to be included in any joystick driver
+ * that communicates with standard analog joysticks. This currently
+ * is: joy-analog.c, joy-assasin.c, and joy-lightning.c
+ */
+
+/*
+ * 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@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#define JS_AN_AXES_STD		0x0f
+#define JS_AN_BUTTONS_STD	0xf0
+
+#define JS_AN_BUTTONS_CHF	0x01
+#define JS_AN_HAT1_CHF		0x02
+#define JS_AN_HAT2_CHF		0x04
+#define JS_AN_ANY_CHF		0x07
+#define JS_AN_HAT_FCS		0x08
+#define JS_AN_HATS_ALL		0x0e
+#define JS_AN_BUTTON_PXY_X	0x10
+#define JS_AN_BUTTON_PXY_Y	0x20
+#define JS_AN_BUTTON_PXY_U	0x40
+#define JS_AN_BUTTON_PXY_V	0x80
+#define JS_AN_BUTTONS_PXY_XY	0x30
+#define JS_AN_BUTTONS_PXY_UV	0xc0
+#define JS_AN_BUTTONS_PXY	0xf0
+
+static struct {
+	int x;
+	int y;
+} js_an_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1, 0}, { 0, 1}, {-1, 0}};
+
+struct js_an_info {
+	int io;
+	unsigned char mask[2];
+	unsigned int extensions;
+	int axes[4];
+	int initial[4];
+	unsigned char buttons;
+};
+
+/*
+ * js_an_decode() decodes analog joystick data.
+ */
+
+static void js_an_decode(struct js_an_info *info, int **axes, int **buttons)
+{
+	int i, j, k;
+	int hat1, hat2, hat3;
+
+	hat1 = hat2 = hat3 = 0;
+	if (info->mask[0] & JS_AN_BUTTONS_STD) buttons[0][0] = 0;
+	if (info->mask[1] & JS_AN_BUTTONS_STD) buttons[1][0] = 0;
+
+	if (info->extensions & JS_AN_ANY_CHF) {
+		switch (info->buttons) {
+			case 0x1: buttons[0][0] = 0x01; break;
+			case 0x2: buttons[0][0] = 0x02; break;
+			case 0x4: buttons[0][0] = 0x04; break;
+			case 0x8: buttons[0][0] = 0x08; break;
+			case 0x5: buttons[0][0] = 0x10; break;
+			case 0x9: buttons[0][0] = 0x20; break;
+			case 0xf: hat1 = 1; break;
+			case 0xb: hat1 = 2; break;
+			case 0x7: hat1 = 3; break;
+			case 0x3: hat1 = 4; break;
+			case 0xe: hat2 = 1; break;
+			case 0xa: hat2 = 2; break;
+			case 0x6: hat2 = 3; break;
+			case 0xc: hat2 = 4; break;
+		}
+		k = info->extensions & JS_AN_BUTTONS_CHF ? 6 : 4;
+	} else {
+		for (i = 1; i >= 0; i--)
+			for (j = k = 0; j < 4; j++)
+				if (info->mask[i] & (0x10 << j))
+					buttons[i][0] |= ((info->buttons >> j) & 1) << k++;
+	}
+
+	if (info->extensions & JS_AN_BUTTON_PXY_X)
+		buttons[0][0] |= (info->axes[2] < (info->initial[2] >> 1)) << k++;
+	if (info->extensions & JS_AN_BUTTON_PXY_Y)
+		buttons[0][0] |= (info->axes[3] < (info->initial[3] >> 1)) << k++;
+	if (info->extensions & JS_AN_BUTTON_PXY_U)
+		buttons[0][0] |= (info->axes[2] > (info->initial[2] + (info->initial[2] >> 1))) << k++;
+	if (info->extensions & JS_AN_BUTTON_PXY_V)
+		buttons[0][0] |= (info->axes[3] > (info->initial[3] + (info->initial[3] >> 1))) << k++;
+
+	if (info->extensions & JS_AN_HAT_FCS)
+		for (j = 0; j < 4; j++)
+			if (info->axes[3] < ((info->initial[3] * ((j << 1) + 1)) >> 3)) {
+				hat3 = j + 1;
+				break;
+			}
+
+	for (i = 1; i >= 0; i--)
+		for (j = k = 0; j < 4; j++)
+			if (info->mask[i] & (1 << j))
+				axes[i][k++] = info->axes[j];
+
+	if (info->extensions & JS_AN_HAT1_CHF) {
+		axes[0][k++] = js_an_hat_to_axis[hat1].x;
+		axes[0][k++] = js_an_hat_to_axis[hat1].y;
+	}
+	if (info->extensions & JS_AN_HAT2_CHF) {
+		axes[0][k++] = js_an_hat_to_axis[hat2].x;
+		axes[0][k++] = js_an_hat_to_axis[hat2].y;
+	}
+	if (info->extensions & JS_AN_HAT_FCS) {
+		axes[0][k++] = js_an_hat_to_axis[hat3].x;
+		axes[0][k++] = js_an_hat_to_axis[hat3].y;
+	}
+}
+
+/*
+ * js_an_count_bits() counts set bits in a byte.
+ */
+
+static inline int js_an_count_bits(unsigned long c)
+{
+	int i = 0;
+	while (c) {
+		i += c & 1;
+		c >>= 1;
+	}
+	return i;
+}
+
+/*
+ * js_an_init_corr() initializes the correction values for
+ * analog joysticks.
+ */
+
+static void __init js_an_init_corr(struct js_an_info *info, int **axes, struct js_corr **corr, int prec)
+{
+	int i, j, t;
+
+	for (i = 0; i < 2; i++)
+	for (j = 0; j < js_an_count_bits(info->mask[i] & 0xf); j++) {
+
+		if ((j == 2 && (info->mask[i] & 0xb) == 0xb) ||
+		    (j == 3 && (info->mask[i] & 0xf) == 0xf)) {
+			t = (axes[i][0] + axes[i][1]) >> 1;
+		} else {
+			t = axes[i][j];
+		}
+
+		corr[i][j].type = JS_CORR_BROKEN;
+		corr[i][j].prec = prec;
+		corr[i][j].coef[0] = t - (t >> 3);
+		corr[i][j].coef[1] = t + (t >> 3);
+		corr[i][j].coef[2] = (1 << 29) / (t - (t >> 2) + 1);
+		corr[i][j].coef[3] = (1 << 29) / (t - (t >> 2) + 1);
+	}
+
+	i = js_an_count_bits(info->mask[0] & 0xf);
+
+	for (j = i; j < i + (js_an_count_bits(info->extensions & JS_AN_HATS_ALL) << 1); j++) {
+		corr[0][j].type = JS_CORR_BROKEN;
+		corr[0][j].prec = 0;
+		corr[0][j].coef[0] = 0;
+		corr[0][j].coef[1] = 0;
+		corr[0][j].coef[2] = (1 << 29);
+		corr[0][j].coef[3] = (1 << 29);
+	}
+
+	for (i = 0; i < 4; i++)
+		info->initial[i] = info->axes[i];
+}
+
+
+/*
+ * js_an_probe_devs() probes for analog joysticks.
+ */
+
+static int __init js_an_probe_devs(struct js_an_info *info, int exist, int mask0, int mask1, struct js_port *port)
+{
+	info->mask[0] = info->mask[1] = info->extensions = 0;
+
+	if (mask0 || mask1) {
+		info->mask[0] = mask0 & (exist | 0xf0);
+		info->mask[1] = mask1 & (exist | 0xf0) & ~info->mask[0];
+		info->extensions = (mask0 >> 8) & ((exist & JS_AN_HAT_FCS) | ((exist << 2) & JS_AN_BUTTONS_PXY_XY) |
+					((exist << 4) & JS_AN_BUTTONS_PXY_UV) | JS_AN_ANY_CHF);
+		if (info->extensions & JS_AN_BUTTONS_PXY) {
+			info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2);
+			info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4);
+			info->mask[1] = 0;
+		}
+		if (info->extensions & JS_AN_HAT_FCS) {
+			info->mask[0] &= ~JS_AN_HAT_FCS;
+			info->mask[1] = 0;
+			info->extensions &= ~(JS_AN_BUTTON_PXY_Y | JS_AN_BUTTON_PXY_U);
+		}
+		if (info->extensions & JS_AN_ANY_CHF) {
+			info->mask[0] |= 0xf0;
+			info->mask[1] = 0;
+		}
+		if (!(info->mask[0] | info->mask[1])) return -1;
+	} else {
+		switch (exist) {
+			case 0x0:
+				return -1;
+			case 0x3:
+				info->mask[0] = 0xf3; /* joystick 0, assuming 4-button */
+				break;
+			case 0xb:
+				info->mask[0] = 0xfb; /* 3-axis, 4-button joystick */
+				break;
+			case 0xc:
+				info->mask[0] = 0xcc; /* joystick 1 */
+				break;
+			case 0xf:
+				info->mask[0] = 0x33; /* joysticks 0 and 1 */
+				info->mask[1] = 0xcc;
+				break;
+			default:
+				printk(KERN_WARNING "joy-analog: Unknown joystick device detected "
+					"(data=%#x), contact <vojtech@ucw.cz>\n", exist);
+				return -1;
+		}
+	}
+
+	return !!info->mask[0] + !!info->mask[1];
+}
+
+/*
+ * js_an_axes() returns the number of axes for an analog joystick.
+ */
+
+static inline int js_an_axes(int i, struct js_an_info *info)
+{
+	return js_an_count_bits(info->mask[i] & 0x0f) + js_an_count_bits(info->extensions & JS_AN_HATS_ALL) * 2;
+}
+
+/*
+ * js_an_buttons() returns the number of buttons for an analog joystick.
+ */
+
+static inline int js_an_buttons(int i, struct js_an_info *info)
+{
+	return js_an_count_bits(info->mask[i] & 0xf0) +
+	       (info->extensions & JS_AN_BUTTONS_CHF) * 2 +
+	       js_an_count_bits(info->extensions & JS_AN_BUTTONS_PXY);
+}
+
+/*
+ * js_an_name() constructs a name for an analog joystick.
+ */
+
+static char js_an_name_buf[128] __initdata = "";
+
+static char __init *js_an_name(int i, struct js_an_info *info)
+{
+
+	sprintf(js_an_name_buf, "Analog %d-axis %d-button",
+		js_an_count_bits(info->mask[i] & 0x0f),
+		js_an_buttons(i, info));
+
+	if (info->extensions & JS_AN_HATS_ALL)
+		sprintf(js_an_name_buf, "%s %d-hat",
+			js_an_name_buf,
+			js_an_count_bits(info->extensions & JS_AN_HATS_ALL));
+
+	strcat(js_an_name_buf, " joystick");
+
+	if (info->extensions)
+		sprintf(js_an_name_buf, "%s with%s%s%s extensions",
+			js_an_name_buf,
+			info->extensions & JS_AN_ANY_CHF ? " CHF" : "",
+			info->extensions & JS_AN_HAT_FCS ? " FCS" : "",
+			info->extensions & JS_AN_BUTTONS_PXY ? " XY-button" : "");
+
+	return js_an_name_buf;
+}
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-assasin.c linux/drivers/char/joystick/joy-assasin.c
--- v2.1.124/linux/drivers/char/joystick/joy-assasin.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/joy-assasin.c	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,423 @@
+/*
+ *  joy-assasin.c  Version 1.2
+ *
+ *  Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * joysticks using FP-Gaming's Assasin 3D protocol.
+ */
+
+/*
+ * 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@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#define JS_AS_MAX_START		250
+#define JS_AS_MAX_STROBE	50
+#define JS_AS_MAX_TIME		2400
+#define JS_AS_MAX_LENGTH	40
+
+#define JS_AS_MODE_A3D		1	/* Assasin 3D */
+#define JS_AS_MODE_PAN		2	/* Panther */
+#define JS_AS_MODE_OEM		3	/* Panther OEM version */
+#define JS_AS_MODE_PXL		4	/* Panther XL */
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_PARM(js_as, "2-24i");
+
+static int js_as[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0};
+
+static int js_as_port_list[] __initdata = {0x201, 0};
+static struct js_port* js_as_port __initdata = NULL;
+
+#include "joy-analog.h"
+
+struct js_as_info {
+	int io;
+	char mode;
+	char rudder;
+	struct js_an_info an;
+};
+
+/*
+ * js_as_read_packet() reads an Assasin 3D packet.
+ */
+
+static int js_as_read_packet(int io, int length, char *data)
+{
+	unsigned char u, v;
+	int i;
+	unsigned int t, t1;
+	unsigned long flags;
+
+	int start = (js_time_speed * JS_AS_MAX_START) >> 10;
+	int strobe = (js_time_speed * JS_AS_MAX_STROBE) >> 10;
+
+	i = 0;
+
+	__save_flags(flags);
+	__cli();
+	outb(inb(io),io);
+
+	u = inb(io);
+	t = js_get_time();
+
+	do {
+		v = inb(io);
+		t1 = js_get_time();
+	} while (u == v && js_delta(t1, t) < start);
+
+	t = t1;
+
+	do {
+		v = inb(io);
+		t1 = js_get_time();
+		if ((u ^ v) & u & 0x10) {
+			data[i++] = v >> 5;
+			t = t1;
+		}
+		u = v;
+	} while (i < length && js_delta(t1,t) < strobe);
+
+	__restore_flags(flags);
+
+	return i;
+}
+
+/*
+ * js_as_csum() computes checksum of triplet packet
+ */
+
+static int js_as_csum(char *data, int count)
+{
+	int i, csum = 0;
+	for (i = 0; i < count - 2; i++) csum += data[i];
+	return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]);
+}
+
+/*
+ * js_as_read() reads and analyzes A3D joystick data.
+ */
+
+static int js_as_read(void *xinfo, int **axes, int **buttons)
+{
+	struct js_as_info *info = xinfo;
+	char data[JS_AS_MAX_LENGTH];
+
+	switch (info->mode) {
+
+		case JS_AS_MODE_A3D:
+		case JS_AS_MODE_OEM:
+		case JS_AS_MODE_PAN:
+
+			if (js_as_read_packet(info->io, 29, data) != 29) return -1;
+			if (data[0] != info->mode) return -1;
+			if (js_as_csum(data, 29)) return -1;
+
+			axes[0][0] = ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7);
+			axes[0][1] = ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7);
+
+			buttons[0][0] = (data[2] << 2) | (data[3] >> 1);
+
+			info->an.axes[0] = ((char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128;
+			info->an.axes[1] = ((char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128;
+			info->an.axes[2] = ((char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128;
+			info->an.axes[3] = ((char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128;
+
+			info->an.buttons = ((data[3] << 3) | data[4]) & 0xf;
+
+			js_an_decode(&info->an, axes + 1, buttons + 1);
+
+			return 0;
+
+		case JS_AS_MODE_PXL:
+
+			if (js_as_read_packet(info->io, 33, data) != 33) return -1;
+			if (data[0] != info->mode) return -1;
+			if (js_as_csum(data, 33)) return -1;
+
+			axes[0][0] = ((char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128;
+			axes[0][1] = ((char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128;
+			info->an.axes[0] = ((char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128;
+			axes[0][2] = ((char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128;
+
+			axes[0][3] = ( data[5]       & 1) - ((data[5] >> 2) & 1);
+			axes[0][4] = ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1);
+			axes[0][5] = ((data[4] >> 1) & 1) - ( data[3]       & 1);
+			axes[0][6] = ((data[4] >> 2) & 1) - ( data[4]       & 1);
+
+			axes[0][7] = ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7);
+			axes[0][8] = ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7);
+
+			buttons[0][0] = (data[2] << 8) | ((data[3] & 6) << 5) | (data[7] << 3) | data[8];
+
+			if (info->rudder) axes[1][0] = info->an.axes[0];
+
+			return 0;
+
+		default:
+			printk("Error.\n");
+			return -1;
+	}
+}
+
+/*
+ * js_as_open() is a callback from the file open routine.
+ */
+
+static int js_as_open(struct js_dev *jd)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_as_close() is a callback from the file release routine.
+ */
+
+static int js_as_close(struct js_dev *jd)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_as_pxl_init_corr() initializes the correction values for
+ * the Panther XL.
+ */
+
+static void __init js_as_pxl_init_corr(struct js_corr **corr, int **axes)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		corr[0][i].type = JS_CORR_BROKEN;
+		corr[0][i].prec = 0;
+		corr[0][i].coef[0] = axes[0][i] - 4;
+		corr[0][i].coef[1] = axes[0][i] + 4;
+		corr[0][i].coef[2] = (1 << 29) / (127 - 32);
+		corr[0][i].coef[3] = (1 << 29) / (127 - 32);
+	}
+
+	corr[0][2].type = JS_CORR_BROKEN;
+	corr[0][2].prec = 0;
+	corr[0][2].coef[0] = 127 - 4;
+	corr[0][2].coef[1] = 128 + 4;
+	corr[0][2].coef[2] = (1 << 29) / (127 - 6);
+	corr[0][2].coef[3] = (1 << 29) / (127 - 6);
+
+	for (i = 3; i < 7; i++) {
+		corr[0][i].type = JS_CORR_BROKEN;
+		corr[0][i].prec = 0;
+		corr[0][i].coef[0] = 0;
+		corr[0][i].coef[1] = 0;
+		corr[0][i].coef[2] = (1 << 29);
+		corr[0][i].coef[3] = (1 << 29);
+	}
+
+	for (i = 7; i < 9; i++) {
+		corr[0][i].type = JS_CORR_BROKEN;
+		corr[0][i].prec = -1;
+		corr[0][i].coef[0] = 0;
+		corr[0][i].coef[1] = 0;
+		corr[0][i].coef[2] = (104 << 14);
+		corr[0][i].coef[3] = (104 << 14);
+	}
+}
+
+/*
+ * js_as_as_init_corr() initializes the correction values for
+ * the Panther and Assasin.
+ */
+
+static void __init js_as_as_init_corr(struct js_corr **corr)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		corr[0][i].type = JS_CORR_BROKEN;
+		corr[0][i].prec = -1;
+		corr[0][i].coef[0] = 0;
+		corr[0][i].coef[1] = 0;
+		corr[0][i].coef[2] = (104 << 14);
+		corr[0][i].coef[3] = (104 << 14);
+	}
+}
+
+/*
+ * js_as_rudder_init_corr() initializes the correction values for
+ * the Panther XL connected rudder.
+ */
+
+static void __init js_as_rudder_init_corr(struct js_corr **corr, int **axes)
+{
+	corr[1][0].type = JS_CORR_BROKEN;
+	corr[1][0].prec = 0;
+	corr[1][0].coef[0] = axes[1][0] - (axes[1][0] >> 3);
+	corr[1][0].coef[1] = axes[1][0] + (axes[1][0] >> 3);
+	corr[1][0].coef[2] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1);
+	corr[1][0].coef[3] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1);
+}
+
+/*
+ * js_as_probe() probes for A3D joysticks.
+ */
+
+static struct js_port __init *js_as_probe(int io, int mask0, int mask1, struct js_port *port)
+{
+	struct js_as_info iniinfo;
+	struct js_as_info *info = &iniinfo;
+	char *name;
+	char data[JS_AS_MAX_LENGTH];
+	unsigned char u;
+	int i;
+	int numdev;
+
+	memset(info, 0, sizeof(struct js_as_info));
+
+	if (io < 0) return port;
+
+	if (check_region(io, 1)) return port;
+	if (((u = inb(io)) & 3) == 3) return port;
+	outb(u,io);
+	if (!((inb(io) ^ u) & ~u & 0xf)) return port;
+
+	if (js_as_read_packet(io, 1, data) != 1) return port;
+
+	if (data[0] && data[0] <= 4) {
+		info->mode = data[0];
+		info->io = io;
+		request_region(io, 1, "joystick (assasin)");
+		port = js_register_port(port, info, 3, sizeof(struct js_as_info), js_as_read);
+		info = port->info;
+	} else {
+		printk(KERN_WARNING "joy-assasin: unknown joystick device detected "
+			"(io=%#x, id=%d), contact <vojtech@ucw.cz>\n", io, data[0]);
+		return port;
+	}
+
+	udelay(JS_AS_MAX_TIME);
+
+	if (info->mode == JS_AS_MODE_PXL) {
+			printk(KERN_INFO "js%d: MadCatz Panther XL at %#x\n",
+				js_register_device(port, 0, 9, 9, "MadCatz Panther XL", js_as_open, js_as_close),
+				info->io);
+			js_as_read(port->info, port->axes, port->buttons);
+			js_as_pxl_init_corr(port->corr, port->axes);
+			if (info->an.axes[0] < 254) {
+			printk(KERN_INFO "js%d: Analog rudder on MadCatz Panther XL\n",
+				js_register_device(port, 1, 1, 0, "Analog rudder", js_as_open, js_as_close));
+				info->rudder = 1;
+				port->axes[1][0] = info->an.axes[0];
+				js_as_rudder_init_corr(port->corr, port->axes);
+			}
+			return port;
+	}
+
+	switch (info->mode) {
+		case JS_AS_MODE_A3D: name = "FP-Gaming Assasin 3D"; break;
+		case JS_AS_MODE_PAN: name = "MadCatz Panther"; break;
+		case JS_AS_MODE_OEM: name = "OEM Assasin 3D"; break;
+		default: name = "This cannot happen"; break;
+	}
+
+	printk(KERN_INFO "js%d: %s at %#x\n",
+		js_register_device(port, 0, 2, 3, name, js_as_open, js_as_close),
+		name, info->io);
+
+	js_as_as_init_corr(port->corr);
+
+	js_as_read(port->info, port->axes, port->buttons);
+
+	for (i = u = 0; i < 4; i++) if (info->an.axes[i] < 254) u |= 1 << i;
+
+	if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0)
+		return port;
+
+	for (i = 0; i < numdev; i++)
+		printk(KERN_INFO "js%d: %s on %s\n",
+			js_register_device(port, i + 1, js_an_axes(i, &info->an), js_an_buttons(i, &info->an),
+				js_an_name(i, &info->an), js_as_open, js_as_close),
+			js_an_name(i, &info->an), name);
+
+	js_an_decode(&info->an, port->axes + 1, port->buttons + 1);
+	js_an_init_corr(&info->an, port->axes + 1, port->corr + 1, 0);
+
+	return port;
+}
+
+#ifndef MODULE
+void __init js_as_setup(char *str, int *ints)
+{
+	int i;
+	for (i = 0; i <= ints[0] && i < 24; i++) js_as[i] = ints[i+1];
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_as_init(void)
+#endif
+{
+	int i;
+
+	if (js_as[0] >= 0) {
+		for (i = 0; (js_as[i*3] >= 0) && i < 8; i++)
+			js_as_port = js_as_probe(js_as[i*3], js_as[i*3+1], js_as[i*3+2], js_as_port);
+	} else {
+		for (i = 0; js_as_port_list[i]; i++) js_as_port = js_as_probe(js_as_port_list[i], 0, 0, js_as_port);
+	}
+	if (js_as_port) return 0;
+
+#ifdef MODULE
+	printk(KERN_WARNING "joy-assasin: no joysticks found\n");
+#endif
+
+	return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	int i;
+	struct js_as_info *info;
+
+	while (js_as_port) {
+		for (i = 0; i < js_as_port->ndevs; i++)
+			if (js_as_port->devs[i])
+				js_unregister_device(js_as_port->devs[i]);
+		info = js_as_port->info;
+		release_region(info->io, 1);
+		js_as_port = js_unregister_port(js_as_port);
+	}
+
+}
+#endif
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-console.c linux/drivers/char/joystick/joy-console.c
--- v2.1.124/linux/drivers/char/joystick/joy-console.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/joy-console.c	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,625 @@
+/*
+ * joy-console.c  Version 0.10
+ *
+ * Copyright (c) 1998 Andree Borrmann
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * console (NES, SNES, Multi1, Multi2, PSX) gamepads connected
+ * via parallel port. Up to five such controllers can be
+ * connected to one parallel port.
+ */
+
+/*
+ * 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 <asm/io.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+
+
+MODULE_AUTHOR("Andree Borrmann <A.Borrmann@tu-bs.de>");
+MODULE_PARM(js_console, "2-6i");
+MODULE_PARM(js_console2,"2-6i");
+MODULE_PARM(js_console3,"2-6i");
+
+
+#define JS_NO_PAD	0
+#define JS_SNES_PAD	1
+#define JS_NES_PAD	2
+#define JS_NES4_PAD	3
+#define JS_MULTI_STICK	4
+#define JS_MULTI2_STICK	5
+#define JS_PSX_PAD	6
+
+#define JS_MAX_PAD	JS_PSX_PAD
+
+struct js_console_info {
+#ifdef USE_PARPORT
+	struct pardevice *port;	/* parport device */
+	int use;		/* use count */
+	int wanted;		/* parport wanted */
+#else
+	int port;		/* hw port */
+#endif
+	int pads;		/* total number of pads */
+	int snes;		/* SNES pads */
+	int nes;		/* NES pads */
+	int multi;		/* Multi joysticks */
+	int multi2;		/* Multi joysticks with 2 buttons */
+	int psx;		/* Normal PSX controllers */
+	int negcon;		/* PSX NEGCON controllers */
+};
+
+static struct js_port* js_console_port = NULL;
+
+static int js_console[] __initdata = { -1, 0, 0, 0, 0, 0 };
+static int js_console2[] __initdata = { -1, 0, 0, 0, 0, 0 };
+static int js_console3[] __initdata = { -1, 0, 0, 0, 0, 0 };
+
+static int status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 };
+
+/*
+ * NES/SNES support.
+ */
+
+#define JS_NES_DELAY	6	/* Delay between bits - 6us */
+
+#define JS_NES_LENGTH	8	/* The NES pads use 8 bits of data */
+
+#define JS_NES_A	0
+#define JS_NES_B	1
+#define JS_NES_START	2
+#define JS_NES_SELECT	3
+#define JS_NES_UP	4
+#define JS_NES_DOWN	5
+#define JS_NES_LEFT	6
+#define JS_NES_RIGHT	7
+
+#define JS_SNES_LENGTH	12	/* The SNES true length is 16, but the last 4 bits are unused */
+
+#define JS_SNES_B	0
+#define JS_SNES_Y	1
+#define JS_SNES_START	2
+#define JS_SNES_SELECT	3
+#define JS_SNES_UP	4
+#define JS_SNES_DOWN	5
+#define JS_SNES_LEFT	6
+#define JS_SNES_RIGHT	7
+#define JS_SNES_A	8
+#define JS_SNES_X	9
+#define JS_SNES_L	10
+#define JS_SNES_R	11
+
+#define JS_NES_POWER	0xf8
+#define JS_NES_CLOCK	0x01
+#define JS_NES_LATCH	0x02
+
+/*
+ * js_nes_read_packet() reads a NES/SNES packet.
+ * Each pad uses one bit per byte. So all pads connected to
+ * this port are read in parallel.
+ */
+
+static void js_nes_read_packet(struct js_console_info *info, int length, unsigned char *data)
+{
+	int i;
+
+	JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK + JS_NES_LATCH, info->port);
+	udelay(JS_NES_DELAY * 2);
+	JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK, info->port);
+
+	for (i = 0; i < length; i++) {
+		udelay(JS_NES_DELAY);
+		JS_PAR_DATA_OUT(JS_NES_POWER, info->port);
+		data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT;
+		udelay(JS_NES_DELAY);
+		JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK, info->port);
+	}
+}
+
+/*
+ * Multisystem joystick support
+ */
+
+#define JS_MULTI_LENGTH		5	/* Multi system joystick packet lenght is 5 */
+#define JS_MULTI2_LENGTH	6	/* One more bit for one more button */
+
+#define JS_MULTI_UP		0
+#define JS_MULTI_DOWN		1
+#define JS_MULTI_LEFT		2
+#define JS_MULTI_RIGHT		3
+#define JS_MULTI_BUTTON		4
+#define JS_MULTI_BUTTON2	5
+
+/*
+ * js_multi_read_packet() reads a Multisystem joystick packet.
+ */
+
+static void js_multi_read_packet(struct js_console_info *info, int length, unsigned char *data)
+{
+	int i;
+
+	for (i = 0; i < length; i++) {
+		JS_PAR_DATA_OUT(~(1 << i), info->port);
+		data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT;
+	}
+}
+
+/*
+ * PSX support
+ */
+
+#define JS_PSX_DELAY	10
+
+#define JS_PSX_LENGTH	8
+
+#define JS_PSX_NORMAL	0x41
+#define JS_PSX_NEGCON	0x23
+#define JS_PSX_MOUSE	0x12
+
+#define JS_PSX_SELBUT	0x01
+#define JS_PSX_START	0x08
+#define JS_PSX_UP	0x10
+#define JS_PSX_RIGHT	0x20
+#define JS_PSX_DOWN	0x40
+#define JS_PSX_LEFT	0x80
+
+#define JS_PSX_CLOCK	0x01
+#define JS_PSX_COMMAND	0x02
+#define JS_PSX_POWER	0xf8
+#define JS_PSX_NOPOWER	0x04
+#define JS_PSX_SELECT	0x08
+
+#define JS_PSX_CTRL_OUT(X,Y)	JS_PAR_CTRL_OUT((X)^0x0f, Y)
+
+/*
+ * js_psx_command() writes 8bit command and reads 8bit data from
+ * the psx pad.
+ */
+
+static int js_psx_command(struct js_console_info *info, int b)
+{
+	int i, cmd, ret=0;
+
+	cmd = (b&1)?JS_PSX_COMMAND:0;
+	for (i=0; i<8; i++) {
+		JS_PSX_CTRL_OUT(cmd, info->port);
+		udelay(JS_PSX_DELAY);
+		ret |= ((JS_PAR_STATUS(info->port) ^ JS_PAR_STATUS_INVERT ) & info->psx) ? (1<<i) : 0;
+		cmd = (b&1)?JS_PSX_COMMAND:0;
+		JS_PSX_CTRL_OUT(JS_PSX_CLOCK | cmd, info->port);
+		udelay(JS_PSX_DELAY);
+		b >>= 1;
+	}
+	return ret;
+}
+
+/*
+ * js_psx_read_packet() reads a whole psx packet and returns
+ * device identifier code.
+ */
+
+static int js_psx_read_packet(struct js_console_info *info, int length, unsigned char *data)
+{
+	int i, ret;
+	unsigned long flags;
+
+	__save_flags(flags);
+	__cli();
+
+	JS_PAR_DATA_OUT(JS_PSX_POWER, info->port);
+
+	JS_PSX_CTRL_OUT(JS_PSX_CLOCK | JS_PSX_SELECT, info->port);	/* Select pad */
+	udelay(JS_PSX_DELAY*2);
+	js_psx_command(info, 0x01);					/* Access pad */
+	ret = js_psx_command(info, 0x42);				/* Get device id */
+	if (js_psx_command(info, 0)=='Z')				/* okay? */
+		for (i=0; i<length; i++)
+			data[i]=js_psx_command(info, 0);
+	else ret = -1;
+
+	JS_PSX_CTRL_OUT(JS_PSX_SELECT | JS_PSX_CLOCK, info->port);
+	__restore_flags(flags);
+
+	return ret;
+}
+
+
+/*
+ * js_console_read() reads and analyzes console pads data.
+ */
+
+#define JS_MAX_LENGTH JS_SNES_LENGTH
+
+static int js_console_read(void *xinfo, int **axes, int **buttons)
+{
+	struct js_console_info *info = xinfo;
+	unsigned char data[JS_MAX_LENGTH];
+
+	int i, s;
+	int n = 0;
+
+/*
+ * NES and SNES pads
+ */
+
+	if (info->nes || info->snes) {
+
+		js_nes_read_packet(info, info->snes ? JS_SNES_LENGTH : JS_NES_LENGTH, data);
+
+		for (i = 0; i < 5; i++) {
+			s = status_bit[i];
+			if (info->nes & s) {
+				axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0);
+				axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP]  &s?1:0);
+
+				buttons[n][0] = ((data[JS_NES_A]    &s)?1:0) | ((data[JS_NES_B]     &s)?2:0)
+					      | ((data[JS_NES_START]&s)?4:0) | ((data[JS_NES_SELECT]&s)?8:0);
+
+				n++;
+			} else
+			if (info->snes & s) {
+				axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0);
+				axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP]  &s?1:0);
+
+				buttons[n][0] = ((data[JS_SNES_A]    &s)?0x01:0) | ((data[JS_SNES_B]     &s)?0x02:0)
+					      | ((data[JS_SNES_X]    &s)?0x04:0) | ((data[JS_SNES_Y]     &s)?0x08:0)
+					      | ((data[JS_SNES_L]    &s)?0x10:0) | ((data[JS_SNES_R]     &s)?0x20:0)
+					      | ((data[JS_SNES_START]&s)?0x40:0) | ((data[JS_SNES_SELECT]&s)?0x80:0);
+				n++;
+			}
+		}
+	}
+
+/*
+ * Multi and Multi2 joysticks
+ */
+
+	if (info->multi || info->multi2) {
+
+		js_multi_read_packet(info, info->multi2 ? JS_MULTI2_LENGTH : JS_MULTI_LENGTH, data);
+
+		for (i = 0; i < 5; i++) {
+			s = status_bit[i];
+			if (info->multi & s) {
+				axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0);
+				axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP]  &s?1:0);
+
+				buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0;
+
+				n++;
+			} else
+			if (info->multi2 & s) {
+				axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0);
+				axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP]  &s?1:0);
+
+				buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0 | (data[JS_MULTI_BUTTON2]&s)?2:0;
+
+				n++;
+			}
+		}
+	}
+
+/*
+ * PSX controllers
+ */
+
+	if (info->psx && (js_psx_read_packet(info, 2, data) == JS_PSX_NORMAL)) {	/* FIXME? >1 PSX pads? */
+
+		axes[n][0] = (data[0]&JS_PSX_RIGHT?0:1) - (data[0]&JS_PSX_LEFT?0:1);
+		axes[n][1] = (data[0]&JS_PSX_DOWN ?0:1) - (data[0]&JS_PSX_UP  ?0:1);
+
+		buttons[n][0] = ((~data[1]&0xf)<<4) | ((~data[1]&0xf0)>>4) |
+				(data[0]&JS_PSX_START?0:0x200) | (data[0]&JS_PSX_SELBUT?0:0x100);
+
+		n++;
+	}
+
+	return -(n != info->pads);
+}
+
+/*
+ * open callback: claim parport.
+ */
+
+int js_console_open(struct js_dev *dev)
+{
+	MOD_INC_USE_COUNT;
+
+#ifdef USE_PARPORT
+	{
+		struct js_console_info *info = dev->port->info;
+		if (!info->use && parport_claim(info->port)) {
+			printk(KERN_WARNING "joy-console: parport busy!\n");	/* port currently not available ... */
+			info->wanted++;						/* we'll claim it on wakeup */
+			return 0;
+		}
+		info->use++;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * close callback: release parport
+ */
+
+int js_console_close(struct js_dev *dev)
+{
+#ifdef USE_PARPORT
+	struct js_console_info *info = dev->port->info;
+
+	if (!--info->use)
+		parport_release(info->port);
+#endif
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+/*
+ * parport wakeup callback: claim the port!
+ */
+
+#ifdef USE_PARPORT
+static void js_console_wakeup(void *v)
+{
+	struct js_console_info *info = js_console_port->info;	/* FIXME! We can have more than 1 port! */
+
+	if (!info->use && info->wanted)
+	{
+		parport_claim(info->port);
+		info->use++;
+		info->wanted--;
+	}
+}
+#endif
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	struct js_console_info *info;
+	int i;
+
+	while (js_console_port) {
+		for (i = 0; i < js_console_port->ndevs; i++)
+			if (js_console_port->devs[i])
+				js_unregister_device(js_console_port->devs[i]);
+		info = js_console_port->info;
+#ifdef USE_PARPORT
+		parport_unregister_device(info->port);
+#else
+		release_region(info->port, 3);
+#endif
+		js_console_port = js_unregister_port(js_console_port);
+	}
+}
+#endif
+
+/*
+ * js_console_init_corr() initializes correction values of
+ * console gamepads.
+ */
+
+static void __init js_console_init_corr(int num_axes, struct js_corr *corr)
+{
+	int i;
+
+	for (i = 0; i < num_axes; i++) {
+		corr[i].type = JS_CORR_BROKEN;
+		corr[i].prec = 0;
+		corr[i].coef[0] = 0;
+		corr[i].coef[1] = 0;
+		corr[i].coef[2] = (1 << 29);
+		corr[i].coef[3] = (1 << 29);
+	}
+}
+
+/*
+ * js_console_probe() probes for console gamepads.
+ * Only PSX pads can really be probed for.
+ */
+
+static struct js_port __init *js_console_probe(int *config, struct js_port *port)
+{
+	char *name[5];
+	int i, psx, axes[5], buttons[5];
+	unsigned char data[2];			/* used for PSX probe */
+	struct js_console_info info;
+
+	memset(&info, 0, sizeof(struct js_console_info));
+
+	if (config[0] < 0) return port;
+
+#ifdef USE_PARPORT
+	{
+		struct parport *pp;
+
+		if (config[0] > 0x10)
+			for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next);
+		else
+			for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--;
+
+		if (!pp) {
+			printk(KERN_ERR "joy-console: no such parport\n");
+			return port;
+		}
+
+		info.port = parport_register_device(pp, "joystick (console)", NULL, js_console_wakeup, NULL, 0, NULL);
+		info.wanted = 0;
+		info.use = 0;
+	}
+
+	if (parport_claim(info.port))
+	{
+		parport_unregister_device(info.port);	/* port currently not available ... */
+		return port;
+	}
+#else
+	info.port = config[0];
+	if (check_region(info.port, 3)) return port;
+	request_region(info.port, 3, "joystick (console pad)");
+#endif
+
+	for (i = 0; i < 5; i++)
+		switch(config[i+1]) {
+
+			case JS_NO_PAD:
+
+				break;
+
+			case JS_SNES_PAD:
+
+				axes[info.pads]    = 2;
+				buttons[info.pads] = 8;
+				name[info.pads]    = "SNES pad";
+				info.snes |= status_bit[i];
+				info.pads++;
+				break;
+
+			case JS_NES_PAD:
+
+				axes[info.pads]    = 2;
+				buttons[info.pads] = 4;
+				name[info.pads]    = "NES pad";
+				info.nes |= status_bit[i];
+				info.pads++;
+				break;
+
+			case JS_MULTI_STICK:
+
+				axes[info.pads]    = 2;
+				buttons[info.pads] = 1;
+				name[info.pads]    = "Multisystem joystick";
+				info.multi |= status_bit[i];
+				info.pads++;
+				break;
+
+			case JS_MULTI2_STICK:
+
+				axes[info.pads]    = 2;
+				buttons[info.pads] = 2;
+				name[info.pads]    = "Multisystem joystick (2 fire)";
+				info.multi |= status_bit[i];
+				info.pads++;
+				break;
+
+			case JS_PSX_PAD:
+
+				info.psx |= status_bit[i];
+				psx = js_psx_read_packet(&info, 2, data);
+				psx = js_psx_read_packet(&info, 2, data);
+				info.psx &= ~status_bit[i];
+
+				switch(psx) {
+					case JS_PSX_NORMAL:
+						axes[info.pads]    = 2;
+						buttons[info.pads] = 10;
+						name[info.pads]    = "PSX controller";
+						info.psx |= status_bit[i];
+						info.pads++;
+						break;
+					case JS_PSX_NEGCON:
+						printk(KERN_WARNING "joy-console: NegCon not yet supported...\n");
+						break;
+					case JS_PSX_MOUSE:
+						printk(KERN_WARNING "joy-console: PSX mouse not supported...\n");
+						break;
+					case -1:
+						printk(KERN_ERR "joy-console: no PSX controller found...\n");
+						break;
+					default:
+						printk(KERN_WARNING "joy-console: unknown PSX controller 0x%x\n", psx);
+				}
+				break;
+
+			default:
+
+				printk(KERN_WARNING "joy-console: pad type %d unknown\n", config[i+1]);
+		}
+
+	if (!info.pads) {
+#ifdef USE_PARPORT
+		parport_release(info.port);
+		parport_unregister_device(info.port);
+#else
+		release_region(info.port, 3);
+#endif
+		return port;
+	}
+
+	port = js_register_port(port, &info, info.pads, sizeof(struct js_console_info), js_console_read);
+
+	for (i = 0; i < info.pads; i++) {
+#ifdef USE_PARPORT
+		printk(KERN_INFO "js%d: %s on %s\n",
+			js_register_device(port, i, axes[i], buttons[i], name[i], js_console_open, js_console_close),
+			name[i], info.port->port->name);
+#else
+		printk(KERN_INFO "js%d: %s at %#x\n",
+			js_register_device(port, i, axes[i], buttons[i], name[i], js_console_open, js_console_close),
+			name[i], info.port);
+#endif
+
+		js_console_init_corr(axes[i], port->corr[i]);
+	}
+
+#ifdef USE_PARPORT
+	parport_release(info.port);
+#endif
+	return port;
+}
+
+#ifndef MODULE
+void __init js_console_setup(char *str, int *ints)
+{
+	int i;
+
+	if (!strcmp(str,"js_console"))
+		for (i = 0; i <= ints[0] && i < 6; i++) js_console[i] = ints[i+1];
+	if (!strcmp(str,"js_console2"))
+		for (i = 0; i <= ints[0] && i < 6; i++) js_console2[i] = ints[i+1];
+	if (!strcmp(str,"js_console3"))
+		for (i = 0; i <= ints[0] && i < 6; i++) js_console3[i] = ints[i+1];
+
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_console_init(void)
+#endif
+{
+	js_console_port = js_console_probe(js_console, js_console_port);
+	js_console_port = js_console_probe(js_console2, js_console_port);
+	js_console_port = js_console_probe(js_console3, js_console_port);
+
+	if (js_console_port) return 0;
+
+#ifdef MODULE
+	printk(KERN_WARNING "joy-console: no joysticks specified\n");
+#endif
+	return -ENODEV;
+}
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-db9.c linux/drivers/char/joystick/joy-db9.c
--- v2.1.124/linux/drivers/char/joystick/joy-db9.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/joy-db9.c	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,453 @@
+/*
+ * joy-db9.c  Version 0.3V
+ *
+ * Copyright (c) 1998 Andree Borrmann
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * console (Atari, Amstrad, Commodore, Amiga, Sega) joysticks
+ * and gamepads connected to the parallel port.
+ */
+
+/*
+ * 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 <asm/io.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+
+MODULE_AUTHOR("Andree Borrmann <A.Borrmann@tu-bs.de>");
+MODULE_PARM(js_db9, "2i");
+MODULE_PARM(js_db9_2, "0-2i");
+MODULE_PARM(js_db9_3, "0-2i");
+
+#define JS_MULTI_STICK	0x01
+#define JS_MULTI2_STICK 0x02
+#define JS_GENESIS_PAD  0x03
+#define JS_GENESIS5_PAD 0x05
+#define JS_GENESIS6_PAD	0x06
+#define JS_SATURN_PAD	0x07
+
+#define JS_MAX_PAD	(JS_SATURN_PAD + 1)
+
+#define JS_DB9_UP	0x01
+#define JS_DB9_DOWN	0x02
+#define JS_DB9_LEFT	0x04
+#define JS_DB9_RIGHT	0x08
+#define JS_DB9_FIRE1	0x10
+#define JS_DB9_FIRE2	0x20
+#define JS_DB9_FIRE3	0x40
+#define JS_DB9_FIRE4	0x80
+
+#define JS_DB9_NORMAL	0x22
+#define JS_DB9_NOSELECT	0x20
+
+#define JS_DB9_SATURN0	0x20
+#define JS_DB9_SATURN1	0x22
+#define JS_DB9_SATURN2	0x24
+#define JS_DB9_SATURN3	0x26
+
+#define JS_GENESIS6_DELAY	14
+
+static struct js_port* js_db9_port = NULL;
+
+static int js_db9[] __initdata = { -1, 0 };
+static int js_db9_2[] __initdata = { -1, 0 };
+static int js_db9_3[] __initdata = { -1, 0 };
+
+struct js_db9_info {
+#ifdef USE_PARPORT
+	struct pardevice *port;	/* parport device */
+	int wanted;		/* parport wanted */
+#else
+	int port;		/* hw port */
+#endif
+	int use;		/* use count */
+	int mode;		/* pad mode */
+};
+
+/*
+ * js_db9_read() reads and analyzes db9 joystick data.
+ */
+
+static int js_db9_read(void *xinfo, int **axes, int **buttons)
+{
+	struct js_db9_info *info = xinfo;
+	int data;
+
+	switch(info->mode)
+	{
+	  case JS_MULTI_STICK:
+
+		data = JS_PAR_DATA_IN(info->port);
+
+		axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP  ?0:1);
+		axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1);
+
+		buttons[0][0] = (data&JS_DB9_FIRE1?0:1);
+
+		break;
+
+	  case JS_MULTI2_STICK:
+
+		data=JS_PAR_DATA_IN(info->port);
+
+		axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP  ?0:1);
+		axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1);
+
+		buttons[0][0] = (data&JS_DB9_FIRE1?0:1) | (data&JS_DB9_FIRE2?0:2);
+
+		break;
+
+	  case JS_GENESIS_PAD:
+
+		JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port);
+		data = JS_PAR_DATA_IN(info->port);
+
+		axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP  ?0:1);
+		axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1);
+
+		buttons[0][0] = (data&JS_DB9_FIRE1?0:2) | (data&JS_DB9_FIRE2?0:4);
+
+		JS_PAR_CTRL_OUT(JS_DB9_NORMAL,info->port);
+		data=JS_PAR_DATA_IN(info->port);
+
+		buttons[0][0] |= (data&JS_DB9_FIRE1?0:1) | (data&JS_DB9_FIRE2?0:8);
+
+		break;
+
+	  case JS_GENESIS5_PAD:
+
+		JS_PAR_CTRL_OUT(JS_DB9_NOSELECT,info->port);
+		data=JS_PAR_DATA_IN(info->port);
+
+		axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP  ?0:1);
+		axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1);
+
+		buttons[0][0] = (data&JS_DB9_FIRE1?0:0x02) | (data&JS_DB9_FIRE2?0:0x04);
+
+		JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port);
+		data=JS_PAR_DATA_IN(info->port);
+
+		buttons[0][0] |= (data&JS_DB9_FIRE1?0:0x01) | (data&JS_DB9_FIRE2?0:0x08) |
+				 (data&JS_DB9_LEFT ?0:0x10) | (data&JS_DB9_RIGHT?0:0x20);
+		break;
+
+	  case JS_GENESIS6_PAD:
+
+		JS_PAR_CTRL_OUT(JS_DB9_NOSELECT,info->port); /* 1 */
+		udelay(JS_GENESIS6_DELAY);
+		data=JS_PAR_DATA_IN(info->port);
+
+		axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP  ?0:1);
+		axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1);
+
+		buttons[0][0] = (data&JS_DB9_FIRE1?0:0x02) | (data&JS_DB9_FIRE2?0:0x04);
+
+		JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port);
+		udelay(JS_GENESIS6_DELAY);
+		data=JS_PAR_DATA_IN(info->port);
+
+		buttons[0][0] |= (data&JS_DB9_FIRE1?0:0x01) | (data&JS_DB9_FIRE2?0:0x08);
+
+		JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 2 */
+		udelay(JS_GENESIS6_DELAY);
+		JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port);
+		udelay(JS_GENESIS6_DELAY);
+		JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 3 */
+		udelay(JS_GENESIS6_DELAY);
+		JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port);
+		data=JS_PAR_DATA_IN(info->port);
+
+		buttons[0][0] |= (data&JS_DB9_LEFT?0:0x10) | (data&JS_DB9_DOWN?0:0x20) | (data&JS_DB9_UP?0:0x40);
+
+		udelay(JS_GENESIS6_DELAY);
+		JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 4 */
+		udelay(JS_GENESIS6_DELAY);
+		JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port);
+
+		break;
+
+	  case JS_SATURN_PAD:
+
+		JS_PAR_CTRL_OUT(JS_DB9_SATURN0, info->port);
+		data = JS_PAR_DATA_IN(info->port);
+
+		buttons[0][0] = (data&JS_DB9_UP  ?0:0x20) | (data&JS_DB9_DOWN ?0:0x10) |
+				(data&JS_DB9_LEFT?0:0x08) | (data&JS_DB9_RIGHT?0:0x40);
+
+		JS_PAR_CTRL_OUT(JS_DB9_SATURN2, info->port);
+		data = JS_PAR_DATA_IN(info->port);
+
+		axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP  ?0:1);
+		axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1);
+
+		JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port);
+		data = JS_PAR_DATA_IN(info->port);
+
+		buttons[0][0] |= (data&JS_DB9_UP  ?0:0x02) | (data&JS_DB9_DOWN ?0:0x04) |
+				 (data&JS_DB9_LEFT?0:0x01) | (data&JS_DB9_RIGHT?0:0x80);
+
+
+		break;
+
+	  default:
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * js_db9_enable_ps2() enables PS/2 capabilities on a parallel port and
+ * switches data lines to input mode. We should use parport_change_mode() for
+ * that if parport present - unfortunately that does nothing and only contains
+ * a FIXME comment.
+ */
+
+static inline void js_db9_enable_ps2(struct js_db9_info *info)
+{
+#ifdef USE_PARPORT
+	int io = info->port->port->base;
+#else
+	int io = info->port;
+#endif
+
+	outb(0x35,io+0x402);		/* enable PS/2 mode: */
+	outb(JS_DB9_NORMAL,io+2);	/* reverse direction, enable Select signal: */
+}
+
+/*
+ * js_db9_disable_ps2() disables PS/2 capabilities on a parallel port
+ * and restores it to standard mode.
+ */
+
+static inline void js_db9_disable_ps2(struct js_db9_info *info)
+{
+#ifdef USE_PARPORT
+	int io = info->port->port->base;
+#else
+	int io = info->port;
+#endif
+
+	outb(0,io+2);		/* normal direction */
+	outb(0x15,io+0x402);	/* enable normal mode */
+}
+
+/*
+ * open callback: claim parport.
+ */
+
+int js_db9_open(struct js_dev *dev)
+{
+	struct js_db9_info *info = dev->port->info;
+
+	MOD_INC_USE_COUNT;
+	if (!info->use) {
+
+#ifdef USE_PARPORT
+		if (parport_claim(info->port)) {
+			printk(KERN_WARNING "joy-db9: parport busy\n");		/* port currently not available ... */
+			info->wanted++;						/* we'll claim it on wakeup */
+			return 0;
+		}
+#endif
+		js_db9_enable_ps2(info);
+	}
+	info->use++;
+	return 0;
+}
+
+/*
+ * close callback: release parport
+ */
+
+int js_db9_close(struct js_dev *dev)
+{
+	struct js_db9_info *info = dev->port->info;
+
+	MOD_DEC_USE_COUNT;
+	info->use--;
+
+	if (!info->use) {
+		js_db9_disable_ps2(info);
+#ifdef USE_PARPORT
+		parport_release(info->port);
+#endif
+	}
+	return 0;
+}
+
+/*
+ * parport wakeup callback: claim the port!
+ */
+
+#ifdef USE_PARPORT
+static void js_db9_wakeup(void *v)
+{
+	struct js_db9_info *info = js_db9_port->info;	/* FIXME! We can have more than 1 port! */
+
+	if (!info->use && info->wanted)
+	{
+		parport_claim(info->port);
+		js_db9_enable_ps2(info);
+		info->use++;
+		info->wanted--;
+	}
+}
+#endif
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	struct js_db9_info *info;
+
+	while (js_db9_port) {
+		js_unregister_device(js_db9_port->devs[0]);
+		info = js_db9_port->info;
+#ifdef USE_PARPORT
+		parport_unregister_device(info->port);
+#else
+		release_region(info->port, 3);
+		release_region(info->port+0x402, 1);
+#endif
+		js_db9_port = js_unregister_port(js_db9_port);
+	}
+
+}
+#endif
+
+/*
+ * js_db9_init_corr() initializes correction values of
+ * db9 gamepads.
+ */
+
+static void __init js_db9_init_corr(struct js_corr **corr)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		corr[0][i].type = JS_CORR_BROKEN;
+		corr[0][i].prec = 0;
+		corr[0][i].coef[0] = 0;
+		corr[0][i].coef[1] = 0;
+		corr[0][i].coef[2] = (1 << 29);
+		corr[0][i].coef[3] = (1 << 29);
+	}
+}
+
+/*
+ * js_db9_probe() probes for db9 gamepads.
+ */
+
+static struct js_port __init *js_db9_probe(int *config, struct js_port *port)
+{
+	struct js_db9_info info;
+	char buttons[JS_MAX_PAD] = {0,1,2,4,0,6,7,8};
+	char *name[JS_MAX_PAD] = {NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad",
+					NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad"};
+
+	if (config[0] < 0) return port;
+	if (config[1] < 0 || config[1] >= JS_MAX_PAD || !name[config[1]]) return port;
+
+#ifdef USE_PARPORT
+	{
+		struct parport *pp;
+
+		if (config[0] > 0x10)
+			for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next);
+		else
+			for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--;
+
+		if (!pp) {
+			printk(KERN_ERR "joy-db9: no such parport\n");
+			return port;
+		}
+
+		if (!(pp->modes & (PARPORT_MODE_PCPS2 | PARPORT_MODE_PCECPPS2))) {
+			printk(KERN_ERR "js-db9: specified parport is not bidirectional\n");
+			return port;
+		}
+
+		info.port = parport_register_device(pp, "joystick (db9)", NULL, js_db9_wakeup, NULL, 0, NULL);
+		info.wanted = 0;
+	}
+#else
+	info.port = config[0];
+	if (check_region(info.port, 3) || check_region(info.port+0x402,1)) return port;
+	request_region(info.port, 3, "joystick (db9)");
+	request_region(info.port+0x402, 1, "joystick (db9)");
+#endif
+
+	info.mode = config[1];
+	info.use = 0;
+
+	port = js_register_port(port, &info, 1, sizeof(struct js_db9_info), js_db9_read);
+
+#ifdef USE_PARPORT
+	printk(KERN_INFO "js%d: %s on %s\n",
+		js_register_device(port, 0, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close),
+		name[info.mode], info.port->port->name);
+#else
+	printk(KERN_INFO "js%d: %s at %#x\n",
+		js_register_device(port, 0, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close),
+		name[info.mode], info.port);
+#endif
+
+	js_db9_init_corr(port->corr);
+
+	return port;
+}
+
+#ifndef MODULE
+void __init js_db9_setup(char *str, int *ints)
+{
+	int i;
+
+	if (!strcmp(str,"js_db9"))
+		for (i = 0; i <= ints[0] && i < 2; i++) js_db9[i] = ints[i+1];
+	if (!strcmp(str,"js_db9_2"))
+		for (i = 0; i <= ints[0] && i < 2; i++) js_db9_2[i] = ints[i+1];
+	if (!strcmp(str,"js_db9_3"))
+		for (i = 0; i <= ints[0] && i < 2; i++) js_db9_3[i] = ints[i+1];
+
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_db9_init(void)
+#endif
+{
+	js_db9_port = js_db9_probe(js_db9, js_db9_port);
+	js_db9_port = js_db9_probe(js_db9_2, js_db9_port);
+	js_db9_port = js_db9_probe(js_db9_3, js_db9_port);
+
+	if (js_db9_port) return 0;
+
+#ifdef MODULE
+	printk(KERN_WARNING "joy-db9: no joysticks specified\n");
+#endif
+	return -ENODEV;
+}
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-gravis.c linux/drivers/char/joystick/joy-gravis.c
--- v2.1.124/linux/drivers/char/joystick/joy-gravis.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/joy-gravis.c	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,356 @@
+/*
+ *  joy-gravis.c  Version 1.2
+ *
+ *  Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * Gravis GrIP digital joystick family.
+ */
+
+/*
+ * 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@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#define JS_GR_MODE_GPP		1
+#define JS_GR_LENGTH_GPP	24
+#define JS_GR_STROBE_GPP	75
+
+#define JS_GR_MODE_XT		2
+#define JS_GR_LENGTH_XT		4
+#define JS_GR_STROBE_XT		30
+#define JS_GR_MAX_CHUNKS_XT	10	
+#define JS_GR_MAX_BITS_XT	30	
+
+static int js_gr_port_list[] __initdata = {0x201, 0};
+static struct js_port* js_gr_port __initdata = NULL;
+
+struct js_gr_info {
+	int io;
+	unsigned char mode[2];
+};
+
+/*
+ * js_gr_gpp_read_packet() reads a Gravis GamePad Pro packet.
+ */
+
+static int js_gr_gpp_read_packet(int io, int shift, unsigned int *data)
+{
+	unsigned int t, t1;
+	unsigned char u, v;
+	int i;
+	unsigned long flags;
+
+	int strobe = (js_time_speed * JS_GR_STROBE_GPP) >> 10;
+
+	i = 0;
+	data[0] = 0;
+
+	__save_flags(flags);
+	__cli();
+	u = inb(io) >> shift;
+	t = js_get_time();
+
+	do {
+		v = (inb(io) >> shift) & 3;
+		t1 = js_get_time();
+		if ((u ^ v) & u & 1) {
+			data[0] |= (v >> 1) << i++;
+			t = t1;
+		}
+		u = v;
+	} while (i < JS_GR_LENGTH_GPP && js_delta(t1,t) < strobe);
+
+	__restore_flags(flags);
+
+	if (i < JS_GR_LENGTH_GPP) return -1;
+
+	for (i = 0; i < JS_GR_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++)
+		data[0] = data[0] >> 1 | (data[0] & 1) << 23;
+
+	return -(i == JS_GR_LENGTH_GPP);
+}
+
+/*
+ * js_gr_xt_read_packet() reads a Gravis Xterminator packet.
+ */
+
+static int js_gr_xt_read_packet(int io, int shift, unsigned int *data)
+{
+	unsigned int t, t1;
+	unsigned char u, v, w;
+	unsigned int i, j, buf, crc;
+	unsigned long flags;
+	char status;
+
+	int strobe = (js_time_speed * JS_GR_STROBE_XT) >> 10;
+
+	data[0] = data[1] = data[2] = data[3] = 0;
+	status = buf = i = j = 0;
+
+	__save_flags(flags);
+	__cli();
+
+	v = w = (inb(io) >> shift) & 3;
+	t = js_get_time();
+
+	do {
+		u = (inb(io) >> shift) & 3;
+		t1 = js_get_time();
+
+		if (u ^ v) {
+
+			if ((u ^ v) & 1) {
+				buf = (buf << 1) | (u >> 1);
+				i++;
+			} else 
+
+			if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) {
+				if (i == 20) {
+					crc = buf ^ (buf >> 7) ^ (buf >> 14);
+					if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) {
+						data[buf >> 18] = buf >> 4;
+						status |= 1 << (buf >> 18);
+					}
+					j++;
+				}
+				buf = 0;
+				i = 0;
+			}
+
+			t = t1;
+			w = v;
+			v = u;
+		}
+
+	} while (status != 0xf && i < JS_GR_MAX_BITS_XT && j < JS_GR_MAX_CHUNKS_XT && js_delta(t1,t) < strobe);
+
+	__restore_flags(flags);
+
+	return -(status != 0xf);
+}
+
+/*
+ * js_gr_read() reads and analyzes GrIP joystick data.
+ */
+
+static int js_gr_read(void *xinfo, int **axes, int **buttons)
+{
+	struct js_gr_info *info = xinfo;
+	unsigned int data[JS_GR_LENGTH_XT];
+	int i;
+
+	for (i = 0; i < 2; i++)
+		switch (info->mode[i]) {
+
+			case JS_GR_MODE_GPP:
+
+				if (js_gr_gpp_read_packet(info->io, (i << 1) + 4, data)) return -1;
+
+				axes[i][0] = ((data[0] >> 15) & 1) - ((data[0] >> 16) & 1);
+				axes[i][1] = ((data[0] >> 13) & 1) - ((data[0] >> 12) & 1);
+
+				data[0] = ((data[0] >> 6) & 0x37) | (data[0] & 0x08) | ((data[0] << 1) & 0x40) |
+				       ((data[0] << 5) & 0x80) | ((data[0] << 8) & 0x300);
+
+				buttons[i][0] = (data[0] & 0xfc) | ((data[0] >> 1) & 0x101) | ((data[0] << 1) & 0x202);
+
+				break;
+
+			case JS_GR_MODE_XT:
+
+				if (js_gr_xt_read_packet(info->io, (i << 1) + 4, data)) return -1;
+
+				axes[i][0] =       (data[0] >> 2) & 0x3f;
+				axes[i][1] = 63 - ((data[0] >> 8) & 0x3f);
+				axes[i][2] = (data[1] >> 2) & 0x3f;
+				axes[i][3] = (data[1] >> 8) & 0x3f;
+				axes[i][4] = (data[2] >> 8) & 0x3f;
+				axes[i][5] = ((data[2] >> 1) & 1) - ( data[2]       & 1);
+				axes[i][6] = ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1);
+				axes[i][7] = ((data[2] >> 5) & 1) - ((data[2] >> 4) & 1);
+				axes[i][8] = ((data[2] >> 6) & 1) - ((data[2] >> 7) & 1);
+
+				buttons[i][0] = (data[3] >> 3) & 0x7ff;
+
+				break;
+
+			default:
+				break;
+
+		}
+
+
+	return 0;
+}
+
+/*
+ * js_gr_open() is a callback from the file open routine.
+ */
+
+static int js_gr_open(struct js_dev *jd)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_gr_close() is a callback from the file release routine.
+ */
+
+static int js_gr_close(struct js_dev *jd)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_gr_init_corr() initializes correction values of
+ * GrIP joysticks.
+ */
+
+static void __init js_gr_init_corr(int mode, struct js_corr *corr)
+{
+	int i;
+
+	switch (mode) {
+
+		case JS_GR_MODE_GPP:
+
+			for (i = 0; i < 2; i++) {
+				corr[i].type = JS_CORR_BROKEN;
+				corr[i].prec = 0;
+				corr[i].coef[0] = 0;
+				corr[i].coef[1] = 0;
+				corr[i].coef[2] = (1 << 29);
+				corr[i].coef[3] = (1 << 29);
+			}
+
+			break;
+
+		case JS_GR_MODE_XT:
+
+			for (i = 0; i < 5; i++) {
+				corr[i].type = JS_CORR_BROKEN;
+				corr[i].prec = 0;
+				corr[i].coef[0] = 31 - 4;
+				corr[i].coef[1] = 32 + 4;
+				corr[i].coef[2] = (1 << 29) / (32 - 14);
+				corr[i].coef[3] = (1 << 29) / (32 - 14);
+			}
+
+			for (i = 5; i < 9; i++) {
+				corr[i].type = JS_CORR_BROKEN;
+				corr[i].prec = 0;
+				corr[i].coef[0] = 0;
+				corr[i].coef[1] = 0;
+				corr[i].coef[2] = (1 << 29);
+				corr[i].coef[3] = (1 << 29);
+			}
+
+			break;
+
+	}
+}
+
+/*
+ * js_gr_probe() probes fro GrIP joysticks.
+ */
+
+static struct js_port __init *js_gr_probe(int io, struct js_port *port)
+{
+	struct js_gr_info info;
+	char *names[] = { NULL, "Gravis GamePad Pro", "Gravis Xterminator" };
+	char axes[] = { 0, 2, 9 };
+	char buttons[] = { 0, 10, 11 };
+	unsigned int data[JS_GR_LENGTH_XT];
+	int i;
+
+	if (check_region(io, 1)) return port;
+
+	info.mode[0] = info.mode[1] = 0;
+
+	for (i = 0; i < 2; i++) {
+		if (!js_gr_gpp_read_packet(io, (i << 1) + 4, data)) info.mode[i] = JS_GR_MODE_GPP;
+		if (!js_gr_xt_read_packet(io, (i << 1) + 4, data)) info.mode[i] = JS_GR_MODE_XT;
+	}
+
+	if (!info.mode[0] && !info.mode[1]) return port;
+
+	info.io = io;
+
+	request_region(io, 1, "joystick (gravis)");
+	port = js_register_port(port, &info, 2, sizeof(struct js_gr_info), js_gr_read);
+
+	for (i = 0; i < 2; i++)
+		if (info.mode[i]) {
+			printk(KERN_INFO "js%d: %s at %#x\n",
+				js_register_device(port, i, axes[info.mode[i]], buttons[info.mode[i]],
+					names[info.mode[i]], js_gr_open, js_gr_close),
+				names[info.mode[i]], io);
+			js_gr_init_corr(info.mode[i], port->corr[i]);
+		}
+
+	return port;
+}
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_gr_init(void)
+#endif
+{
+	int *p;
+
+	for (p = js_gr_port_list; *p; p++) js_gr_port = js_gr_probe(*p, js_gr_port);
+	if (js_gr_port) return 0;
+
+#ifdef MODULE
+	printk(KERN_WARNING "joy-gravis: no joysticks found\n");
+#endif
+
+	return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	int i;
+	struct js_gr_info *info;
+
+	while (js_gr_port) {
+		for (i = 0; i < js_gr_port->ndevs; i++)
+			if (js_gr_port->devs[i])
+				js_unregister_device(js_gr_port->devs[i]);
+		info = js_gr_port->info;
+		release_region(info->io, 1);
+		js_gr_port = js_unregister_port(js_gr_port);
+	}
+}
+#endif
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-joydump.c linux/drivers/char/joystick/joy-joydump.c
--- v2.1.124/linux/drivers/char/joystick/joy-joydump.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/joy-joydump.c	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,172 @@
+/*
+ *  joydump.c  Version 1.2
+ *
+ *  Copyright (c) 1996-1998 Vojtech Pavlik
+ */
+
+/*
+ * This is just a very simple driver that can dump the data
+ * out of the joystick port into the syslog ...
+ */
+
+/*
+ * 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@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+
+#define JS_PORT 0x201
+
+#ifdef __i386__
+static unsigned int get_time(void)
+{
+	unsigned int x;
+	__asm__ __volatile__ ( "rdtsc" : "=A" (x) );
+	return x;
+}
+#else
+
+#ifdef	__alpha__
+static unsigned int get_time(void)
+{
+	unsigned int x;
+	__asm__ __volatile__ ( "rpcc %0" : "=r" (x) );
+	return x;
+}
+#else
+
+#error "Sorry, you need a precise timer for this - define some in joydump.c"
+
+#endif
+#endif
+
+#define BUF_SIZE 128
+
+struct datatype {
+	unsigned int time;
+	unsigned char data;
+};
+
+static struct datatype buf[BUF_SIZE];
+
+int init_module(void)
+{
+	int i, j;
+	unsigned int t, t1;
+	int timeout, div;
+	unsigned long flags;
+	unsigned char u;
+
+	printk(KERN_INFO "joydump: ,------------------- START ------------------.\n");
+
+/*
+ * Calibrate.
+ */
+
+	__save_flags(flags);
+	__cli();
+	t = get_time();
+	udelay(1000);
+	t1 = get_time();
+	__restore_flags(flags);
+
+	timeout = t1 - t;
+	div = timeout / 1000;
+
+	printk(KERN_INFO "joydump: | Timer ticks at %d.%02d MHz.                 |\n",
+		timeout / 1000, timeout / 10 % 100);
+
+	__save_flags(flags);
+	__cli();
+	t = get_time();
+	inb(JS_PORT); inb(JS_PORT); inb(JS_PORT); inb(JS_PORT); inb(JS_PORT);
+	inb(JS_PORT); inb(JS_PORT); inb(JS_PORT); inb(JS_PORT); inb(JS_PORT);
+	t1 = get_time();
+	__restore_flags(flags);
+
+	printk(KERN_INFO "joydump: | I/O at %#x takes %d.%02d us.                |\n",
+		JS_PORT, (t1 - t) / div / 10, (t1 - t) * 10 / div % 100);
+
+	timeout *= 20;
+
+/*
+ * Gather data.
+ */
+
+	__save_flags(flags);
+	__cli();
+
+	u = inb(JS_PORT);
+	t = t1 = get_time();
+	buf[0].data = u;
+	buf[0].time = t1;
+	i = 1;
+
+	outb(u,JS_PORT);
+
+	while (i < BUF_SIZE && t1 - t < timeout) {
+
+		t1 = get_time();
+		buf[i].data = inb(JS_PORT);
+
+		if (buf[i].data != u) {
+			u = buf[i].data;
+			buf[i].time = t1;
+			i++;
+		}
+	}
+
+	__restore_flags(flags);
+
+/*
+ * Dump data.
+ */
+
+
+	t = i;
+
+	printk(KERN_INFO "joydump: >------------------- DATA -------------------<\n");
+	printk(KERN_INFO "joydump: | index: %3d delta: %3d.%02d us data: ", 0, 0, 0);
+	for (j = 7; j >= 0; j--)
+		printk("%d",(buf[0].data >> j) & 1);
+	printk(" |\n");
+	for (i = 1; i < t; i++) {
+		printk(KERN_INFO "joydump: | index: %3d delta: %3d.%02d us data: ", i,
+			(buf[i].time - buf[i-1].time) / div, (buf[i].time - buf[i-1].time) * 100 / div % 100);
+		for (j = 7; j >= 0; j--)
+			printk("%d",(buf[i].data >> j) & 1);
+		printk(" |\n");
+	}
+
+	printk(KERN_INFO "joydump: `-------------------- END -------------------'\n");
+
+	return -1;
+}
+
+void cleanup_module(void)
+{
+}
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-lightning.c linux/drivers/char/joystick/joy-lightning.c
--- v2.1.124/linux/drivers/char/joystick/joy-lightning.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/joy-lightning.c	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,340 @@
+/*
+ *  joy-lightning.c  Version 1.2
+ *
+ *  Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * PDPI Lightning 4 gamecards and analog joysticks connected
+ * to them.
+ */
+
+/*
+ * 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@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#define JS_L4_PORT		0x201
+#define JS_L4_SELECT_ANALOG	0xa4
+#define JS_L4_SELECT_DIGITAL	0xa5
+#define JS_L4_SELECT_SECONDARY	0xa6
+#define JS_L4_CMD_ID		0x80
+#define JS_L4_CMD_GETCAL	0x92
+#define JS_L4_CMD_SETCAL	0x93
+#define JS_L4_ID		0x04
+#define JS_L4_BUSY		0x01
+#define JS_L4_TIMEOUT		80	/* 80 us */
+
+static struct js_port* __initdata js_l4_port = NULL;
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_PARM(js_l4, "2-24i");
+
+static int js_l4[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0};
+
+#include "joy-analog.h"
+
+struct js_l4_info {
+	int port;
+	struct js_an_info an;
+};
+
+/*
+ * js_l4_wait_ready() waits for the L4 to become ready.
+ */
+
+static int js_l4_wait_ready(void)
+{
+	unsigned int t, t1, timeout;
+	timeout = (JS_L4_TIMEOUT * js_time_speed) >> 10;
+	t = t1 = js_get_time();
+	while ((inb(JS_L4_PORT) & JS_L4_BUSY) && (js_delta(t1 = js_get_time(), t) < timeout));
+	return -(js_delta(t1, t) >= timeout);
+}
+
+/*
+ * js_l4_read() reads data from the Lightning 4.
+ */
+
+static int js_l4_read(void *xinfo, int **axes, int **buttons)
+{
+	struct js_l4_info *info = xinfo;
+	int i;
+	unsigned char status;
+
+	outb(JS_L4_SELECT_ANALOG, JS_L4_PORT);
+	outb(JS_L4_SELECT_DIGITAL + (info->port >> 2), JS_L4_PORT);
+
+	if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1;
+	outb(info->port & 3, JS_L4_PORT);
+
+	if (js_l4_wait_ready()) return -1;
+	status = inb(JS_L4_PORT);
+
+	for (i = 0; i < 4; i++)
+		if (status & (1 << i)) {
+			if (js_l4_wait_ready()) return -1;
+			info->an.axes[i] = inb(JS_L4_PORT);
+		}
+
+	if (status & 0x10) {
+		if (js_l4_wait_ready()) return -1;
+		info->an.buttons = inb(JS_L4_PORT);
+	}
+
+	js_an_decode(&info->an, axes, buttons);
+
+	outb(JS_L4_SELECT_ANALOG, JS_L4_PORT);
+
+	return 0;
+}
+
+#if 0
+
+/*
+ * js_l4_setcal() programs the L4 with calibration values.
+ */
+
+static int js_l4_setcal(int port, int *calib)
+{
+	int i;
+
+	outb(JS_L4_SELECT_ANALOG, JS_L4_PORT);
+	outb(JS_L4_SELECT_DIGITAL + (port >> 2), JS_L4_PORT);
+
+	if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1;
+
+	outb(JS_L4_CMD_SETCAL, JS_L4_PORT);
+	if (js_l4_wait_ready()) return -1;
+
+        outb(port & 3, JS_L4_PORT);
+	if (js_l4_wait_ready()) return -1;
+
+	for (i = 0; i < 4; i++) {
+		outb(calib[0], JS_L4_PORT);
+		if (js_l4_wait_ready()) return -1;
+	}
+
+	outb(JS_L4_SELECT_ANALOG, JS_L4_PORT);
+
+	return 0;
+}
+
+
+static void js_l4_calibrate(struct js_l4_info *info)
+{
+	int calib[4];
+	int i;
+
+	if ((info->an.extensions & JS_AN_BUTTON_PXY_X) && !(info->an.extensions & JS_AN_BUTTON_PXY_U))
+		info->an.axes[2] >>= 2;						/* Pad button X */
+
+	if ((info->an.extensions & JS_AN_BUTTON_PXY_Y) && !(info->an.extensions & JS_AN_BUTTON_PXY_V))
+		info->an.axes[3] >>= 2;						/* Pad button Y */
+
+	if (info->an.extensions & JS_AN_HAT_FCS) 
+		info->an.axes[3] >>= 2;						/* FCS hat */
+
+	if (((info->an.mask[0] & 0xb) == 0xb) ||
+	    ((info->an.mask[1] & 0xb) == 0xb))
+		info->an.axes[3] = (info->an.axes[0] + info->an.axes[1]) >> 1;	/* Throttle */
+
+	for (i = 0; i < 4; i++)
+		calib[i] = (info->an.axes[i] * 255) / 100;
+	
+	for (i = 0; i < 4; i++)
+		if (calib[i] > 255) calib[i] = 255;
+
+	js_l4_setcal(info->port, calib);
+
+}
+	
+#endif
+
+/*
+ * js_l4_open() is a callback from the file open routine.
+ */
+
+static int js_l4_open(struct js_dev *jd)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_l4_close() is a callback from the file release routine.
+ */
+
+static int js_l4_close(struct js_dev *jd)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_l4_probe() probes for joysticks on the L4 cards.
+ */
+
+static struct js_port __init *js_l4_probe(int cards, int l4port, int mask0, int mask1, struct js_port *port)
+{
+	struct js_l4_info iniinfo;
+	struct js_l4_info *info = &iniinfo;
+#if 0
+	int calib[4] = {255, 255, 255, 255};
+#endif
+	int i, numdev;
+	unsigned char u;
+
+	if (l4port < 0) return port;
+	if (~cards & (1 << (l4port >> 2))) return port;
+
+	memset(info, 0, sizeof(struct js_l4_info));
+	info->port = l4port;
+
+#if 0
+	js_l4_setcal(info->port, calib);
+#endif
+	if (js_l4_read(info, NULL, NULL)) return port;
+
+	for (i = u = 0; i < 4; i++) if (info->an.axes[i] < 253) u |= 1 << i;
+
+	if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0)
+		return port;
+
+	port = js_register_port(port, info, numdev, sizeof(struct js_l4_info), js_l4_read);
+
+	for (i = 0; i < numdev; i++)
+		printk(KERN_INFO "js%d: %s on L4 port %d\n",
+			js_register_device(port, i, js_an_axes(i, &info->an), js_an_buttons(i, &info->an),
+				js_an_name(i, &info->an), js_l4_open, js_l4_close),
+			js_an_name(i, &info->an), info->port);
+
+	info = port->info;
+
+#if 0
+	js_l4_calibrate(info);
+#endif
+	js_l4_read(info, port->axes, port->buttons);
+	js_an_init_corr(&info->an, port->axes, port->corr, 0);
+
+	return port;
+}
+
+/*
+ * js_l4_card_probe() probes for presence of the L4 card(s).
+ */
+
+static int __init js_l4_card_probe(void)
+{
+	int i, cards = 0;
+	unsigned char rev = 0;
+
+	if (check_region(JS_L4_PORT, 1)) return 0;
+
+	for (i = 0; i < 2; i++) {
+
+		outb(JS_L4_SELECT_ANALOG, JS_L4_PORT);
+		outb(JS_L4_SELECT_DIGITAL + i, JS_L4_PORT);		/* Select card 0-1 */
+
+		if (inb(JS_L4_PORT) & JS_L4_BUSY) continue;
+		outb(JS_L4_CMD_ID, JS_L4_PORT);				/* Get card ID & rev */
+
+		if (js_l4_wait_ready()) continue;
+		if (inb(JS_L4_PORT) != JS_L4_SELECT_DIGITAL + i) continue;
+
+		if (js_l4_wait_ready()) continue;
+		if (inb(JS_L4_PORT) != JS_L4_ID) continue;
+
+		if (js_l4_wait_ready()) continue;
+		rev = inb(JS_L4_PORT);
+
+		cards |= (1 << i);
+
+		printk(KERN_INFO "js: PDPI Lightning 4 %s card (ports %d-%d) firmware v%d.%d found at %#x\n",
+			i ? "secondary" : "primary", (i << 2), (i << 2) + 3, rev >> 4, rev & 0xf, JS_L4_PORT);
+	}
+
+	outb(JS_L4_SELECT_ANALOG, JS_L4_PORT);
+
+	return cards;
+}
+
+#ifndef MODULE
+void __init js_l4_setup(char *str, int *ints)
+{
+	int i;
+	for (i = 0; i <= ints[0] && i < 24; i++) js_l4[i] = ints[i+1];
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_l4_an_init(void)
+#endif
+{
+	int i, cards;
+
+	cards = js_l4_card_probe();
+
+	if (js_l4[0] >= 0) {
+		for (i = 0; (js_l4[i*3] >= 0) && i < 8; i++)
+			js_l4_port = js_l4_probe(cards, js_l4[i*3], js_l4[i*3+1], js_l4[i*3+2], js_l4_port);
+	} else {
+		for (i = 0; i < (cards << 2); i++)
+			js_l4_port = js_l4_probe(cards, i, 0, 0, js_l4_port);
+	}
+
+	if (js_l4_port == NULL) {
+#ifdef MODULE
+		printk(KERN_WARNING "joy-lightning: no joysticks found\n");
+#endif
+		return -ENODEV;
+	}
+
+	request_region(JS_L4_PORT, 1, "joystick (lightning)");
+
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	int i;
+
+	while (js_l4_port) {
+		for (i = 0; i < js_l4_port->ndevs; i++)
+			if (js_l4_port->devs[i])
+				js_unregister_device(js_l4_port->devs[i]);
+		js_l4_port = js_unregister_port(js_l4_port);
+	}
+	outb(JS_L4_SELECT_ANALOG, JS_L4_PORT);
+	release_region(JS_L4_PORT, 1);
+
+}
+#endif
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-logitech.c linux/drivers/char/joystick/joy-logitech.c
--- v2.1.124/linux/drivers/char/joystick/joy-logitech.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/joy-logitech.c	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,371 @@
+/*
+ *  joy-logitech.c  Version 1.2
+ *
+ *  Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * Logitech Digital joystick family.
+ */
+
+/*
+ * 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@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#define JS_LT_MAX_START		250
+#define JS_LT_MAX_STROBE	25
+#define JS_LT_MAX_LENGTH	72
+
+#define JS_LT_MAX_DELAY		12000
+
+#define JS_LT_MODE_WMED		1
+#define JS_LT_MODE_CM2		2
+#define JS_LT_MODE_TPD		3
+
+static int js_lt_seq_init[] __initdata = { 6000, 11000, 7000, 9000, 6000, 11000, 7000, 9000, 0 };
+static int js_lt_seq_reset[] __initdata = { 2000, 3000, 2000, 3000, 0 };
+
+static int js_lt_port_list[] __initdata = {0x201, 0};
+static struct js_port* js_lt_port __initdata = NULL;
+
+static struct {
+	int x;
+	int y;
+} js_lt_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+struct js_lt_info {
+	int io;
+	unsigned char mode;
+};
+
+/*
+ * js_lt_read_packet() reads a Logitech packet.
+ */
+
+static int js_lt_read_packet(int io, __u64 *data)
+{
+
+	static unsigned char buf[JS_LT_MAX_LENGTH];
+	unsigned char u, v, w, mask = 0;
+	int i;
+	unsigned long flags;
+	unsigned int t, t1;
+
+	int start = (js_time_speed * JS_LT_MAX_START) >> 10;
+	int strobe = (js_time_speed * JS_LT_MAX_STROBE) >> 10;
+
+	u = inb(io) >> 4;
+
+	if (u == 0xc) mask = 0x10;
+	if (u == 0x0) mask = 0x50;
+	if (!mask) return 0;
+
+	i = 0;
+
+	__save_flags(flags);
+	__cli();
+
+	outb(u,io);
+
+	u = inb(io);
+	t = js_get_time();
+
+	if ((u & 0xc) != 0xc) mask = 0x10;
+
+	do {
+		u = inb(io);
+		t1 = js_get_time();
+	} while ((((u >> 1) ^ u) & mask) != mask && js_delta(t1,t) < start);
+
+	t = t1;
+
+	do {
+		v = inb(io);
+		t1 = js_get_time();
+		w = u ^ v;
+		if ((((w >> 1) ^ w) & mask) == mask) {
+			buf[i++] = w;
+			t = t1;
+			u = v;
+		}
+	} while (i < JS_LT_MAX_LENGTH && js_delta(t1,t) < strobe);
+
+	__restore_flags(flags);
+
+	t = i;
+	*data = 0;
+
+	if (mask == 0x10) {
+		for (i = 0; i < t; i++)
+			*data = ((buf[i] >> 5) & 1) | (*data << 1);
+		return t;
+	}
+	if (mask == 0x50) {
+		for (i = 0; i < t; i++)
+			*data = ((__u64)(buf[i] & 0x20) << (t - 5)) | (buf[i] >> 7) | (*data << 1);
+		return t << 1;
+	}
+	return 0;
+}
+
+/*
+ * js_lt_reverse() reverses the order of bits in a byte.
+ */
+
+static unsigned char js_lt_reverse(unsigned char u)
+{
+	u = ((u & 0x0f) << 4) | ((u >> 4) & 0x0f);
+	u = ((u & 0x33) << 2) | ((u >> 2) & 0x33);
+	u = ((u & 0x55) << 1) | ((u >> 1) & 0x55);
+	return u;
+}
+
+/*
+ * js_lt_read() reads and analyzes Logitech joystick data.
+ */
+
+static int js_lt_read(void *xinfo, int **axes, int **buttons)
+{
+	struct js_lt_info *info = xinfo;
+	__u64 data;
+	int hat;
+
+	switch (info->mode) {
+
+		case JS_LT_MODE_TPD:
+
+			if (js_lt_read_packet(info->io, &data) != 20) return -1;
+
+			axes[0][0] = ((data >> 6) & 1) - ((data >> 4) & 1);
+			axes[0][1] = ((data >> 5) & 1) - ((data >> 7) & 1);
+
+			buttons[0][0] = js_lt_reverse((data & 0x0f) | ((data >> 4) & 0xf0));
+
+			return 0;
+
+		case JS_LT_MODE_WMED:
+
+			if (js_lt_read_packet(info->io, &data) != 42) return -1;
+			if ((hat = data & 0xf) > 8) return -1;
+
+			axes[0][0] = (data >> 26) & 0xff;
+			axes[0][1] = (data >> 18) & 0xff;
+			axes[0][2] = (data >> 10) & 0xff;
+			axes[0][3] = js_lt_hat_to_axis[hat].x;
+			axes[0][4] = js_lt_hat_to_axis[hat].y;
+
+			buttons[0][0] = js_lt_reverse((data >> 2) & 0xfc);
+
+			return 0;
+
+		case JS_LT_MODE_CM2:
+
+			if (js_lt_read_packet(info->io, &data) != 64) return -1;
+
+			axes[0][0] = (data >> 48) & 0xff;
+			axes[0][1] = (data >> 40) & 0xff;
+			axes[0][2] = (data >> 32) & 0xff;
+			axes[0][3] = (data >> 24) & 0xff;
+			axes[0][4] = (data >> 16) & 0xff;
+			axes[0][5] = (data >>  8) & 0xff;
+
+			buttons[0][0] = js_lt_reverse(data & 0xff);
+
+			return 0;
+	}
+
+	return -1;
+}
+
+/*
+ * js_lt_open() is a callback from the file open routine.
+ */
+
+static int js_lt_open(struct js_dev *jd)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_lt_close() is a callback from the file release routine.
+ */
+
+static int js_lt_close(struct js_dev *jd)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_lt_trigger_sequence() sends a trigger & delay sequence
+ * to reset/initialize a Logitech joystick.
+ */
+
+static void __init js_lt_trigger_sequence(int io, int *seq)
+{
+	while (*seq) {
+		outb(inb(io),io);
+		udelay(*seq++);
+	}
+}
+
+/*
+ * js_lt_init_corr() initializes the correction values for
+ * Logitech joysticks.
+ */
+
+static void __init js_lt_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr)
+{
+	int j;
+
+	for (j = 0; j < num_axes; j++) {
+		corr[0][j].type = JS_CORR_BROKEN;
+		corr[0][j].prec = 2;
+		corr[0][j].coef[0] = axes[0][j] - 8;
+		corr[0][j].coef[1] = axes[0][j] + 8;
+		corr[0][j].coef[2] = (1 << 29) / (127 - 32);
+		corr[0][j].coef[3] = (1 << 29) / (127 - 32);
+	}
+
+	switch (mode) {
+		case JS_LT_MODE_TPD:  j = 0; break;
+		case JS_LT_MODE_WMED: j = 3; break;
+		case JS_LT_MODE_CM2:  j = 6; break;
+		default:	      j = 0; break;
+	}
+
+	for (; j < num_axes; j++) {
+		corr[0][j].type = JS_CORR_BROKEN;
+		corr[0][j].prec = 0;
+		corr[0][j].coef[0] = 0;
+		corr[0][j].coef[1] = 0;
+		corr[0][j].coef[2] = (1 << 29);
+		corr[0][j].coef[3] = (1 << 29);
+	}
+
+}
+
+/*
+ * js_lt_probe() probes for Logitech type joysticks.
+ */
+
+static struct js_port __init *js_lt_probe(int io, struct js_port *port)
+{
+	struct js_lt_info info;
+	char *name;
+	int axes, buttons, i;
+	__u64 data;
+	unsigned char u;
+
+	if (check_region(io, 1)) return port;
+
+	if (((u = inb(io)) & 3) == 3) return port;
+	outb(u,io);
+	if (!((inb(io) ^ u) & ~u & 0xf)) return port;
+
+	if (!(i = js_lt_read_packet(io, &data))) {
+		udelay(JS_LT_MAX_DELAY);
+		js_lt_trigger_sequence(io, js_lt_seq_reset);
+		js_lt_trigger_sequence(io, js_lt_seq_init);
+		i = js_lt_read_packet(io, &data);
+	}
+
+	switch (i) {
+		case 0:
+			return port;
+		case 20:
+			info.mode = JS_LT_MODE_TPD;
+			axes = 2; buttons = 8; name = "Logitech ThunderPad Digital";
+			break;
+		case 42:
+			info.mode = JS_LT_MODE_WMED;
+			axes = 5; buttons = 6; name = "Logitech WingMan Extreme Digital";
+			break;
+		case 64:
+			info.mode = JS_LT_MODE_CM2;
+			axes = 6; buttons = 8; name = "Logitech CyberMan 2";
+			break;
+		case 72:
+		case 144:
+			return port;
+		default:
+			printk(KERN_WARNING "joy-logitech: unknown joystick device detected "
+				"(io=%#x, count=%d, data=0x%08x%08x), contact <vojtech@ucw.cz>\n",
+				io, i, (int)(data >> 32), (int)(data & 0xffffffff));
+			return port;
+	}
+
+	info.io = io;
+
+	request_region(io, 1, "joystick (logitech)");
+	port = js_register_port(port, &info, 1, sizeof(struct js_lt_info), js_lt_read);
+	printk(KERN_INFO "js%d: %s at %#x\n",
+		js_register_device(port, 0, axes, buttons, name, js_lt_open, js_lt_close), name, io);
+
+	udelay(JS_LT_MAX_DELAY);
+
+	js_lt_read(port->info, port->axes, port->buttons);
+	js_lt_init_corr(axes, info.mode, port->axes, port->corr);
+
+	return port;
+}
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_lt_init(void)
+#endif
+{
+	int *p;
+
+	for (p = js_lt_port_list; *p; p++) js_lt_port = js_lt_probe(*p, js_lt_port);
+	if (js_lt_port) return 0;
+
+#ifdef MODULE
+	printk(KERN_WARNING "joy-logitech: no joysticks found\n");
+#endif
+
+	return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	struct js_lt_info *info;
+
+	while (js_lt_port) {
+		js_unregister_device(js_lt_port->devs[0]);
+		info = js_lt_port->info;
+		release_region(info->io, 1);
+		js_lt_port = js_unregister_port(js_lt_port);
+	}
+}
+#endif
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-sidewinder.c linux/drivers/char/joystick/joy-sidewinder.c
--- v2.1.124/linux/drivers/char/joystick/joy-sidewinder.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/joy-sidewinder.c	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,471 @@
+/*
+ *  joy-sidewinder.c  Version 1.2
+ *
+ *  Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * Microsoft SideWinder digital joystick family.
+ */
+
+/*
+ * 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@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#define JS_SW_MAX_START		250
+#define JS_SW_MIN_STROBE	25
+#define JS_SW_EXT_STROBE	45
+#define JS_SW_MIN_TIME		1500
+#define JS_SW_MAX_TIME		4000
+
+#define JS_SW_MAX_LENGTH	72
+
+#define JS_SW_MODE_3DP		1
+#define JS_SW_MODE_PP		2
+#define JS_SW_MODE_GP		3
+
+static int js_sw_port_list[] __initdata = {0x201, 0};
+static struct js_port* js_sw_port __initdata = NULL;
+
+static struct {
+	int x;
+	int y;
+} js_sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+struct js_sw_info {
+	int io;
+	unsigned char mode;
+	unsigned char number;
+	unsigned char optimize;
+};
+
+/*
+ * js_sw_init_digital() switches a SideWinder into digital mode.
+ * It currently switches to 2-bit mode only (FIXME).
+ */
+
+static void __init js_sw_init_digital(int io)
+{
+	unsigned int t;
+	unsigned int timeout = (js_time_speed * JS_SW_MAX_TIME) >> 10;
+	int delays[] = {100, 850, 400, 0};
+	int i = 0;
+	unsigned long flags;
+
+	__save_flags(flags);
+	__cli();
+	do {
+		outb(inb(io),io);
+		t = js_get_time();
+		while ((inb(io) & 1) && (js_delta(js_get_time(),t) < timeout));
+		udelay(delays[i]);
+	} while (delays[i++]);
+	__restore_flags(flags);
+
+	return;
+}
+
+/*
+ * js_sw_read_packet() reads a SideWinder packet.
+ */
+
+static int js_sw_read_packet(int io, int l1, int l2, int strobe, __u64 *data)
+{
+	static unsigned char buf[JS_SW_MAX_LENGTH];
+	unsigned char u, v;
+	int i;
+	unsigned long flags;
+	unsigned int t, t1;
+
+	int length = l1 < l2 ? l2 : l1;
+	int start = (js_time_speed * JS_SW_MAX_START) >> 10;
+	strobe = (js_time_speed * strobe) >> 10;
+
+	i = 0;
+
+	__save_flags(flags);
+	__cli();
+	outb(inb(io),io);
+
+	v = inb(io);
+	t = js_get_time();
+
+	do {
+		u = v;
+		v = inb(io);
+		t1 = js_get_time();
+	} while (!((u ^ v) & u & 0x10) && js_delta(t1, t) < start);
+
+	t = t1;
+
+	do {
+		v = inb(io);
+		t1 = js_get_time();
+		if ((u ^ v) & v & 0x10) {
+			buf[i++] = v >> 5;
+			t = t1;
+		}
+		u = v;
+	} while (i < length && js_delta(t1,t) < strobe);
+
+	__restore_flags(flags);
+
+	*data = 0;
+	t = i;
+
+	if (t == l1) {
+		for (i = 0; i < t; i++)
+			*data |= (__u64) (buf[i] & 1) << i;
+		return t;
+	}
+	if (t == l2) {
+		for (i = 0; i < t; i++)
+			*data |= (__u64) buf[i] << (3 * i);
+		return t * 3;
+	}
+	return t;
+}
+
+/*
+ * js_sw_parity computes parity of __u64
+ */
+
+static int js_sw_parity(__u64 t)
+{
+	t ^= t >> 32;
+	t ^= t >> 16;
+	t ^= t >> 8;
+	t ^= t >> 4;
+	t ^= t >> 2;
+	t ^= t >> 1;
+	return t & 1;
+}
+
+/*
+ * js_sw_csum() computes checksum of nibbles in __u64
+ */
+
+static int js_sw_csum(__u64 t)
+{
+	char sum = 0;
+	while (t) {
+		sum += t & 0xf;
+		t >>= 4;
+	}
+	return sum & 0xf;
+}
+
+/*
+ * js_sw_read() reads and analyzes SideWinder joystick data.
+ */
+
+static int js_sw_read(void *xinfo, int **axes, int **buttons)
+{
+	struct js_sw_info *info = xinfo;
+	__u64 data;
+	int hat, i;
+
+	switch (info->mode) {
+
+		case JS_SW_MODE_3DP:
+
+			if (info->optimize) {
+				i = js_sw_read_packet(info->io, -1, 22, JS_SW_EXT_STROBE, &data);
+			} else {
+				i = js_sw_read_packet(info->io, 64, 66, JS_SW_EXT_STROBE, &data);
+				if (i == 192) info->optimize = 1;
+			}
+
+			if (i < 60) {
+				js_sw_init_digital(info->io);
+				info->optimize = 0;
+				return -1;
+			}
+
+			if (((data & 0x8080808080808080ULL) ^ 0x80) || js_sw_csum(data) ||
+				(hat = ((data >> 3) & 0x08) | ((data >> 60) & 0x07)) > 8) {
+				info->optimize = 0;
+				return -1;
+			}
+
+			axes[0][0] = ((data <<  4) & 0x380) | ((data >> 16) & 0x07f);
+			axes[0][1] = ((data <<  7) & 0x380) | ((data >> 24) & 0x07f);
+			axes[0][2] = ((data >> 28) & 0x180) | ((data >> 40) & 0x07f);
+			axes[0][3] = ((data >> 25) & 0x380) | ((data >> 48) & 0x07f);
+			axes[0][4] = js_sw_hat_to_axis[hat].x;
+			axes[0][5] = js_sw_hat_to_axis[hat].y;
+			buttons[0][0] = ((~data >> 31) & 0x80) | ((~data >> 8) & 0x7f);
+
+			return 0;
+
+		case JS_SW_MODE_PP:
+
+			if (js_sw_read_packet(info->io, 48, 16, JS_SW_EXT_STROBE, &data) != 48) return -1;
+			if (!js_sw_parity(data) || (hat = (data >> 42) & 0xf) > 8) return -1;
+
+			axes[0][0] = (data >>  9) & 0x3ff;
+			axes[0][1] = (data >> 19) & 0x3ff;
+			axes[0][2] = (data >> 29) & 0x07f;
+			axes[0][3] = (data >> 36) & 0x03f;
+			axes[0][4] = js_sw_hat_to_axis[hat].x;
+			axes[0][5] = js_sw_hat_to_axis[hat].y;
+			buttons[0][0] = ~data & 0x1ff;
+
+			return 0;
+
+		case JS_SW_MODE_GP:
+
+			if (js_sw_read_packet(info->io, 15 * info->number, 5 * info->number,
+				JS_SW_EXT_STROBE, &data) != 15 * info->number) return -1;
+			if (js_sw_parity(data)) return -1;
+
+			for (i = 0; i < info->number; i++) {
+				axes[i][0] = ((data >> 3) & 1) - ((data >> 2) & 1);
+				axes[i][1] = ( data       & 1) - ((data >> 1) & 1);
+				buttons[i][0] = (~data >> 4) & 0x3ff;
+				data >>= 15;
+			}
+
+			return 0;
+
+		default:
+			return -1;
+	}
+}
+
+/*
+ * js_sw_open() is a callback from the file open routine.
+ */
+
+static int js_sw_open(struct js_dev *jd)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_sw_close() is a callback from the file release routine.
+ */
+
+static int js_sw_close(struct js_dev *jd)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_sw_init_corr() initializes the correction values for
+ * SideWinders.
+ */
+
+static void __init js_sw_init_corr(int num_axes, int mode, int number, struct js_corr **corr)
+{
+	int i, j;
+
+	for (i = 0; i < number; i++) {
+
+		for (j = 0; j < num_axes; j++) {
+			corr[i][j].type = JS_CORR_BROKEN;
+			corr[i][j].prec = 8;
+			corr[i][j].coef[0] = 511 - 32;
+			corr[i][j].coef[1] = 512 + 32;
+			corr[i][j].coef[2] = (1 << 29) / (511 - 32);
+			corr[i][j].coef[3] = (1 << 29) / (511 - 32);
+		}
+
+		switch (mode) {
+
+			case JS_SW_MODE_3DP:
+
+				corr[i][2].type = JS_CORR_BROKEN;
+				corr[i][2].prec = 4;
+				corr[i][2].coef[0] = 255 - 16;
+				corr[i][2].coef[1] = 256 + 16;
+				corr[i][2].coef[2] = (1 << 29) / (255 - 16);
+				corr[i][2].coef[3] = (1 << 29) / (255 - 16);
+
+				j = 4;
+
+			break;
+
+			case JS_SW_MODE_PP:
+
+				corr[i][2].type = JS_CORR_BROKEN;
+				corr[i][2].prec = 1;
+				corr[i][2].coef[0] = 63 - 4;
+				corr[i][2].coef[1] = 64 + 4;
+				corr[i][2].coef[2] = (1 << 29) / (63 - 4);
+				corr[i][2].coef[3] = (1 << 29) / (63 - 4);
+
+				corr[i][3].type = JS_CORR_BROKEN;
+				corr[i][3].prec = 0;
+				corr[i][3].coef[0] = 31 - 2;
+				corr[i][3].coef[1] = 32 + 2;
+				corr[i][3].coef[2] = (1 << 29) / (31 - 2);
+				corr[i][3].coef[3] = (1 << 29) / (31 - 2);
+
+				j = 4;
+
+			break;
+
+			default:
+
+				j = 0;
+
+		}
+
+		for (; j < num_axes; j++) {
+			corr[i][j].type = JS_CORR_BROKEN;
+			corr[i][j].prec = 0;
+			corr[i][j].coef[0] = 0;
+			corr[i][j].coef[1] = 0;
+			corr[i][j].coef[2] = (1 << 29);
+			corr[i][j].coef[3] = (1 << 29);
+		}
+	}
+}
+
+/*
+ * js_sw_probe() probes for SideWinder type joysticks.
+ */
+
+static struct js_port __init *js_sw_probe(int io, struct js_port *port)
+{
+	struct js_sw_info info;
+	char *name;
+	int i, j, axes, buttons;
+	__u64 data;
+	unsigned char u;
+
+
+	if (check_region(io, 1)) return port;
+	if (((u = inb(io)) & 3) == 3) return port;
+	outb(u,io);
+	if (!((inb(io) ^ u) & ~u & 0xf)) return port;
+
+	i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data);
+
+	if (!i) {
+		udelay(JS_SW_MIN_TIME);
+		js_sw_init_digital(io);
+		udelay(JS_SW_MAX_TIME);
+		i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data);
+	}
+
+	switch (i) {
+		case 0:
+			return port;
+		case 5:
+		case 10:
+		case 15:
+		case 20:
+		case 30:
+		case 45:
+		case 60:
+			info.mode = JS_SW_MODE_GP;
+			outb(inb(io),io);						/* Kick into 3-bit mode */
+			udelay(JS_SW_MAX_TIME);
+			i = js_sw_read_packet(io, 60, -1, JS_SW_EXT_STROBE, &data);	/* Get total length */
+			udelay(JS_SW_MIN_TIME);
+			j = js_sw_read_packet(io, 15, -1, JS_SW_MIN_STROBE, &data);	/* Get subpacket length */
+			if (!i || !j) {
+				printk(KERN_WARNING "joy-sidewinder: SideWinder GamePad detected (%d,%d),"
+							" but not idenfitied.\n", i, j);
+				return port;
+			}
+			info.number = i / j;
+			axes = 2; buttons = 10; name = "SideWinder GamePad";
+			break;
+		case 16:
+		case 48:
+			info.mode = JS_SW_MODE_PP; info.number = 1;
+			axes = 6; buttons = 9; name = "SideWinder Precision Pro";
+			break;
+		case 64:
+		case 66:
+			info.mode = JS_SW_MODE_3DP; info.number = 1; info.optimize = 0;
+			axes = 6; buttons = 8; name = "SideWinder 3D Pro";
+			break;
+		case 72:
+			return port;
+		default:
+			printk(KERN_WARNING "joy-sidewinder: unknown joystick device detected "
+				"(io=%#x, count=%d, data=0x%08x%08x), contact <vojtech@ucw.cz>\n",
+				io, i, (int)(data >> 32), (int)(data & 0xffffffff));
+			return port;
+	}
+
+	info.io = io;
+
+	request_region(io, 1, "joystick (sidewinder)");
+	port = js_register_port(port, &info, info.number, sizeof(struct js_sw_info), js_sw_read);
+	for (i = 0; i < info.number; i++)
+		printk(KERN_INFO "js%d: %s at %#x\n",
+			js_register_device(port, i, axes, buttons, name, js_sw_open, js_sw_close), name, io);
+	js_sw_init_corr(axes, info.mode, info.number, port->corr);
+
+	return port;
+}
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_sw_init(void)
+#endif
+{
+	int *p;
+
+	for (p = js_sw_port_list; *p; p++) js_sw_port = js_sw_probe(*p, js_sw_port);
+	if (js_sw_port) return 0;
+
+#ifdef MODULE
+	printk(KERN_WARNING "joy-sidewinder: no joysticks found\n");
+#endif
+
+	return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	int i;
+	struct js_sw_info *info;
+
+	while (js_sw_port) {
+		for (i = 0; i < js_sw_port->ndevs; i++)
+			if (js_sw_port->devs[i])
+				js_unregister_device(js_sw_port->devs[i]);
+		info = js_sw_port->info;
+		release_region(info->io, 1);
+		js_sw_port = js_unregister_port(js_sw_port);
+	}
+
+}
+#endif
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-thrustmaster.c linux/drivers/char/joystick/joy-thrustmaster.c
--- v2.1.124/linux/drivers/char/joystick/joy-thrustmaster.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/joy-thrustmaster.c	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,330 @@
+/*
+ *  joy-thrustmaster.c  Version 1.2
+ *
+ *  Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * ThrustMaster DirectConnect (BSP) joystick family.
+ */
+
+/*
+ * 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@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#define JS_TM_MAX_START		200
+#define JS_TM_MAX_STROBE	15
+#define JS_TM_MAX_LENGTH	13
+
+#define JS_TM_MODE_M3DI		1
+#define JS_TM_MODE_3DRP		2
+#define JS_TM_MODE_WCS3		4
+
+#define JS_TM_MODE_MAX		5	/* Last mode + 1 */
+
+#define JS_TM_BYTE_A0		0
+#define JS_TM_BYTE_A1		1
+#define JS_TM_BYTE_A2		3
+#define JS_TM_BYTE_A3		4
+#define JS_TM_BYTE_A4		6
+#define JS_TM_BYTE_A5		7
+
+#define JS_TM_BYTE_D0		2
+#define JS_TM_BYTE_D1		5
+#define JS_TM_BYTE_D2		8
+#define JS_TM_BYTE_D3		9
+
+#define JS_TM_BYTE_ID		10
+#define JS_TM_BYTE_REV		11
+#define JS_TM_BYTE_DEF		12
+
+static int js_tm_port_list[] __initdata = {0x201, 0};
+static struct js_port* js_tm_port __initdata = NULL;
+
+struct js_tm_info {
+	int io;
+	unsigned char mode;
+};
+
+static int js_tm_id_to_def[JS_TM_MODE_MAX] = {0x00, 0x42, 0x22, 0x00, 0x00};
+
+/*
+ * js_tm_read_packet() reads a ThrustMaster packet.
+ */
+
+static int js_tm_read_packet(int io, unsigned char *data)
+{
+	unsigned int t, t1;
+	unsigned char u, v, error;
+	int i, j;
+	unsigned long flags;
+
+	int start = (js_time_speed * JS_TM_MAX_START) >> 10;
+	int strobe = (js_time_speed * JS_TM_MAX_STROBE) >> 10;
+
+	error = 0;
+	i = j = 0;
+
+	__save_flags(flags);
+	__cli();
+	outb(inb(io),io);
+
+	t = js_get_time();
+
+	do {
+		u = inb(io);
+		t1 = js_get_time();
+	} while ((u & 1) && js_delta(t1, t) < start);
+
+	t = t1;
+	u >>= 4;
+
+	do {
+		v = inb(io) >> 4;
+		t1 = js_get_time();
+		if ((u ^ v) & u & 2) {
+			if (j) {
+				if (j < 9) {				/* Data bit */
+					data[i] |= (~v & 1) << (j - 1);
+					j++;
+				} else {				/* Stop bit */
+					error |= v & 1;
+					j = 0;
+					i++;
+				}
+			} else {					/* Start bit */
+				error |= ~v & 1;
+				j++;
+			}
+			t = t1;
+		}
+		u = v;
+	} while (!error && i < JS_TM_MAX_LENGTH && js_delta(t1,t) < strobe);
+
+	__restore_flags(flags);
+
+	return -(i != JS_TM_MAX_LENGTH);
+}
+
+/*
+ * js_tm_read() reads and analyzes ThrustMaster joystick data.
+ */
+
+static int js_tm_read(void *xinfo, int **axes, int **buttons)
+{
+	struct js_tm_info *info = xinfo;
+	unsigned char data[JS_TM_MAX_LENGTH];
+
+	if (js_tm_read_packet(info->io, data)) {
+		printk(KERN_WARNING "joy-thrustmaster: failed to read data packet\n");
+		return -1;
+	}
+	if (data[JS_TM_BYTE_ID] != info->mode) {
+		printk(KERN_WARNING "joy-thrustmaster: ID (%d) != mode (%d)\n",
+			data[JS_TM_BYTE_ID], info->mode);
+		return -1;
+	}
+	if (data[JS_TM_BYTE_DEF] != js_tm_id_to_def[info->mode]) {
+		printk(KERN_WARNING "joy-thrustmaster: DEF (%d) != def(mode) (%d)\n",
+			data[JS_TM_BYTE_DEF], js_tm_id_to_def[info->mode]);
+		return -1;
+	}
+
+	switch (info->mode) {
+
+		case JS_TM_MODE_M3DI:
+
+			axes[0][0] = data[JS_TM_BYTE_A0];
+			axes[0][1] = data[JS_TM_BYTE_A1];
+			axes[0][2] = data[JS_TM_BYTE_A2];
+			axes[0][3] = data[JS_TM_BYTE_A3];
+
+			axes[0][4] = ((data[JS_TM_BYTE_D0] >> 3) & 1) - ((data[JS_TM_BYTE_D0] >> 1) & 1);
+			axes[0][5] = ((data[JS_TM_BYTE_D0] >> 2) & 1) - ( data[JS_TM_BYTE_D0]       & 1);
+
+			buttons[0][0] = ((data[JS_TM_BYTE_D0] >> 6) & 0x01) | ((data[JS_TM_BYTE_D0] >> 3) & 0x06)
+				      | ((data[JS_TM_BYTE_D0] >> 4) & 0x08) | ((data[JS_TM_BYTE_D1] >> 2) & 0x30);
+
+			return 0;
+
+		case JS_TM_MODE_3DRP:
+
+			axes[0][0] = data[JS_TM_BYTE_A0];
+			axes[0][1] = data[JS_TM_BYTE_A1];
+
+			buttons[0][0] = ( data[JS_TM_BYTE_D0]       & 0x3f) | ((data[JS_TM_BYTE_D1] << 6) & 0xc0)
+				      | (( ((int) data[JS_TM_BYTE_D0]) << 2) & 0x300);
+
+			return 0;
+
+	}
+
+	return -1;
+}
+
+/*
+ * js_tm_open() is a callback from the file open routine.
+ */
+
+static int js_tm_open(struct js_dev *jd)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_tm_close() is a callback from the file release routine.
+ */
+
+static int js_tm_close(struct js_dev *jd)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_tm_init_corr() initializes the correction values for
+ * ThrustMaster joysticks.
+ */
+
+static void __init js_tm_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr)
+{
+	int j;
+
+	for (j = 0; j < num_axes; j++) {
+		corr[0][j].type = JS_CORR_BROKEN;
+		corr[0][j].prec = 0;
+		corr[0][j].coef[0] = 127 - 2;
+		corr[0][j].coef[1] = 128 + 2;
+		corr[0][j].coef[2] = (1 << 29) / (127 - 4);
+		corr[0][j].coef[3] = (1 << 29) / (127 - 4);
+	}
+
+	switch (mode) {
+		case JS_TM_MODE_M3DI: j = 4; break;
+		case JS_TM_MODE_3DRP: j = 2; break;
+		default:	      j = 0; break;
+	}
+
+	for (; j < num_axes; j++) {
+		corr[0][j].type = JS_CORR_BROKEN;
+		corr[0][j].prec = 0;
+		corr[0][j].coef[0] = 0;
+		corr[0][j].coef[1] = 0;
+		corr[0][j].coef[2] = (1 << 29);
+		corr[0][j].coef[3] = (1 << 29);
+	}
+
+}
+
+/*
+ * js_tm_probe() probes for ThrustMaster type joysticks.
+ */
+
+static struct js_port __init *js_tm_probe(int io, struct js_port *port)
+{
+	struct js_tm_info info;
+	char *names[JS_TM_MODE_MAX] = { NULL, "ThrustMaster Millenium 3D Inceptor",
+						"ThrustMaster Rage 3D Gamepad", NULL, "ThrustMaster WCS III" };
+	char axes[JS_TM_MODE_MAX] = { 0, 6, 2, 0, 0 };
+	char buttons[JS_TM_MODE_MAX] = { 0, 5, 10, 0, 0 };
+
+	unsigned char data[JS_TM_MAX_LENGTH];
+	unsigned char u;
+
+	if (check_region(io, 1)) return port;
+
+	if (((u = inb(io)) & 3) == 3) return port;
+	outb(u,io);
+	if (!((inb(io) ^ u) & ~u & 0xf)) return port;
+
+	if(js_tm_read_packet(io, data)) {
+		printk(KERN_WARNING "joy-thrustmaster: probe - can't read packet\n");
+		return port;
+	}
+
+	info.io = io;
+	info.mode = data[JS_TM_BYTE_ID];
+
+	if (!info.mode) return port;
+
+	if (info.mode >= JS_TM_MODE_MAX || !names[info.mode]) {
+		printk(KERN_WARNING "joy-thrustmaster: unknown device detected "
+				    "(io=%#x, id=%d), contact <vojtech@ucw.cz>\n",
+					io, info.mode);
+		return port;
+	}
+
+	if (data[JS_TM_BYTE_DEF] != js_tm_id_to_def[info.mode]) {
+		printk(KERN_WARNING "joy-thrustmaster: wrong DEF (%d) for ID %d - should be %d\n",
+				data[JS_TM_BYTE_DEF], info.mode, js_tm_id_to_def[info.mode]);
+	}
+
+	request_region(io, 1, "joystick (thrustmaster)");
+	port = js_register_port(port, &info, 1, sizeof(struct js_tm_info), js_tm_read);
+	printk(KERN_INFO "js%d: %s revision %d at %#x\n",
+		js_register_device(port, 0, axes[info.mode], buttons[info.mode],
+				names[info.mode], js_tm_open, js_tm_close), names[info.mode], data[JS_TM_BYTE_REV], io);
+	js_tm_init_corr(axes[info.mode], info.mode, port->axes, port->corr);
+
+	return port;
+}
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_tm_init(void)
+#endif
+{
+	int *p;
+
+	for (p = js_tm_port_list; *p; p++) js_tm_port = js_tm_probe(*p, js_tm_port);
+	if (js_tm_port) return 0;
+
+#ifdef MODULE
+	printk(KERN_WARNING "joy-thrustmaster: no joysticks found\n");
+#endif
+
+	return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	struct js_tm_info *info;
+
+	while (js_tm_port) {
+		js_unregister_device(js_tm_port->devs[0]);
+		info = js_tm_port->info;
+		release_region(info->io, 1);
+		js_tm_port = js_unregister_port(js_tm_port);
+	}
+}
+#endif
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joy-turbografx.c linux/drivers/char/joystick/joy-turbografx.c
--- v2.1.124/linux/drivers/char/joystick/joy-turbografx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/joy-turbografx.c	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,282 @@
+/*
+ *  joy-turbografx.c  Version 1.2
+ *
+ *  Copyright (c) 1998 Vojtech Pavlik
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * Steffen Schwenke's <schwenke@burg-halle.de> TurboGraFX parallel port
+ * interface.
+ */
+
+/*
+ * 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@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_PARM(js_tg, "2i");
+MODULE_PARM(js_tg_2, "0-2i");
+MODULE_PARM(js_tg_3, "0-2i");
+
+#define JS_TG_BUTTON	3
+#define JS_TG_UP	4
+#define JS_TG_DOWN	5
+#define JS_TG_LEFT	6
+#define JS_TG_RIGHT	7
+
+static struct js_port* js_tg_port = NULL;
+
+static int js_tg[] __initdata = { -1, 0 };
+static int js_tg_2[] __initdata = { -1, 0 };
+static int js_tg_3[] __initdata = { -1, 0 };
+
+struct js_tg_info {
+#ifdef USE_PARPORT
+	struct pardevice *port;	/* parport device */
+	int use;		/* use count */
+	int wanted;		/* parport wanted */
+#else
+	int port;		/* hw port */
+#endif
+	int count;		/* number of joysticks */
+};
+
+/*
+ * js_tg_read() reads and analyzes tg joystick data.
+ */
+
+static int js_tg_read(void *xinfo, int **axes, int **buttons)
+{
+	struct js_tg_info *info = xinfo;
+	int data, i;
+
+	for (i = 0; i < info->count; i++) {
+
+		JS_PAR_DATA_OUT(~(1 << i), info->port);
+		data = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT;
+
+		axes[i][0] = ((data >> JS_TG_RIGHT) & 1) - ((data >> JS_TG_LEFT) & 1);
+		axes[i][1] = ((data >> JS_TG_DOWN ) & 1) - ((data >> JS_TG_UP  ) & 1);
+
+		buttons[i][0] = (data >> JS_TG_BUTTON) & 1;
+
+	}
+
+	return 0;
+}
+
+/*
+ * open callback: claim parport.
+ */
+
+int js_tg_open(struct js_dev *dev)
+{
+	MOD_INC_USE_COUNT;
+
+#ifdef USE_PARPORT
+	{
+		struct js_tg_info *info = dev->port->info;
+		if (!info->use && parport_claim(info->port)) {
+			printk(KERN_WARNING "joy-tg: parport busy\n");		/* port currently not available ... */
+			info->wanted++;						/* we'll claim it on wakeup */
+			return 0;
+		}
+		info->use++;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * close callback: release parport
+ */
+
+int js_tg_close(struct js_dev *dev)
+{
+#ifdef USE_PARPORT
+	struct js_tg_info *info = dev->port->info;
+
+	if (!--info->use)
+		parport_release(info->port);
+#endif
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+/*
+ * parport wakeup callback: claim the port!
+ */
+
+#ifdef USE_PARPORT
+static void js_tg_wakeup(void *v)
+{
+	struct js_tg_info *info = js_tg_port->info;	/* FIXME! We can have more than 1 port! */
+
+	if (!info->use && info->wanted)
+	{
+		parport_claim(info->port);
+		info->use++;
+		info->wanted--;
+	}
+}
+#endif
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	struct js_tg_info *info;
+	int i;
+
+	while (js_tg_port) {
+		for (i = 0; i < js_tg_port->ndevs; i++)
+			if (js_tg_port->devs[i])
+				js_unregister_device(js_tg_port->devs[i]);
+		info = js_tg_port->info;
+#ifdef USE_PARPORT
+		parport_unregister_device(info->port);
+#else
+		release_region(info->port, 3);
+#endif
+		js_tg_port = js_unregister_port(js_tg_port);
+	}
+}
+#endif
+
+/*
+ * js_tg_init_corr() initializes correction values of
+ * tg gamepads.
+ */
+
+static void __init js_tg_init_corr(int count, struct js_corr **corr)
+{
+	int i, j;
+
+	for (i = 0; i < count; i++)
+		for (j = 0; j < 2; j++) {
+			corr[i][j].type = JS_CORR_BROKEN;
+			corr[i][j].prec = 0;
+			corr[i][j].coef[0] = 0;
+			corr[i][j].coef[1] = 0;
+			corr[i][j].coef[2] = (1 << 29);
+			corr[i][j].coef[3] = (1 << 29);
+		}
+}
+
+/*
+ * js_tg_probe() probes for tg gamepads.
+ */
+
+static struct js_port __init *js_tg_probe(int *config, struct js_port *port)
+{
+	struct js_tg_info info;
+	int i;
+
+	if (config[0] < 0) return port;
+	if (config[1] < 1 || config[1] > 8) return port;
+
+#ifdef USE_PARPORT
+	{
+		struct parport *pp;
+
+		if (config[0] > 0x10)
+			for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next);
+		else
+			for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--;
+
+		if (!pp) {
+			printk(KERN_ERR "joy-tg: no such parport\n");
+			return port;
+		}
+
+		info.port = parport_register_device(pp, "joystick (turbografx)", NULL, js_tg_wakeup, NULL, 0, NULL);
+		info.wanted = 0;
+		info.use = 0;
+	}
+#else
+	info.port = config[0];
+	if (check_region(info.port, 3)) return port;
+	request_region(info.port, 3, "joystick (turbografx)");
+#endif
+
+	info.count = config[1];
+
+	port = js_register_port(port, &info, info.count, sizeof(struct js_tg_info), js_tg_read);
+
+
+	for (i = 0; i < info.count; i++)
+#ifdef USE_PARPORT
+		printk(KERN_INFO "js%d: Multisystem joystick on %s\n",
+			js_register_device(port, i, 2, 1, "Multisystem joystick", js_tg_open, js_tg_close),
+			info.port->port->name);
+#else
+		printk(KERN_INFO "js%d: Multisystem joystick at %#x\n",
+			js_register_device(port, i, 2, 1, "Multisystem joystick", js_tg_open, js_tg_close),
+			info.port);
+#endif
+
+	js_tg_init_corr(info.count, port->corr);
+
+	return port;
+}
+
+#ifndef MODULE
+void __init js_tg_setup(char *str, int *ints)
+{
+	int i;
+
+	if (!strcmp(str,"js_tg"))
+		for (i = 0; i <= ints[0] && i < 2; i++) js_tg[i] = ints[i+1];
+	if (!strcmp(str,"js_tg_2"))
+		for (i = 0; i <= ints[0] && i < 2; i++) js_tg_2[i] = ints[i+1];
+	if (!strcmp(str,"js_tg_3"))
+		for (i = 0; i <= ints[0] && i < 2; i++) js_tg_3[i] = ints[i+1];
+
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_tg_init(void)
+#endif
+{
+	js_tg_port = js_tg_probe(js_tg, js_tg_port);
+	js_tg_port = js_tg_probe(js_tg_2, js_tg_port);
+	js_tg_port = js_tg_probe(js_tg_3, js_tg_port);
+
+	if (js_tg_port) return 0;
+
+#ifdef MODULE
+	printk(KERN_WARNING "joy-tg: no joysticks specified\n");
+#endif
+	return -ENODEV;
+}
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick/joystick.c linux/drivers/char/joystick/joystick.c
--- v2.1.124/linux/drivers/char/joystick/joystick.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/joystick/joystick.c	Thu Oct  8 17:25:16 1998
@@ -0,0 +1,1223 @@
+/*
+ * joystick.c  Version 1.2
+ *
+ * Copyright (c) 1996-1998 Vojtech Pavlik
+ */
+
+/*
+ * This is the main joystick driver for Linux. It doesn't support any
+ * devices directly, rather is lets you use sub-modules to do that job. See
+ * Documentation/joystick.txt 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., 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@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+#include <asm/spinlock.h>
+#include <linux/poll.h>
+#endif
+
+/*
+ * Configurable parameters.
+ */
+
+#define JS_REFRESH_TIME		HZ/50	/* Time between two reads of joysticks (20ms) */
+
+/*
+ * Buffer macros.
+ */
+
+#define ROT(A,B,C)	((((A)<(C))&&(((B)>(A))&&((B)<(C))))||(((A)>(C))&&(((B)>(A))||((B)<(C)))))
+#define GOF(X)		(((X)==JS_BUFF_SIZE-1)?0:(X)+1)
+#define GOB(X)		((X)?(X)-1:JS_BUFF_SIZE-1)
+#define DIFF(X,Y)	((X)>(Y)?(X)-(Y):(Y)-(X))
+
+/*
+ * Global variables.
+ */
+
+static struct JS_DATA_SAVE_TYPE js_comp_glue;
+static struct js_port  *js_port  = NULL;
+static struct js_dev   *js_dev   = NULL;
+static struct timer_list js_timer;
+spinlock_t js_lock = SPIN_LOCK_UNLOCKED;
+static int js_use_count = 0;
+
+/*
+ * Exported variables.
+ */
+
+unsigned int js_time_speed = 0;
+js_time_func js_get_time;
+js_delta_func js_delta;
+
+unsigned int js_time_speed_a = 0;
+js_time_func js_get_time_a;
+js_delta_func js_delta_a;
+
+/*
+ * Module info.
+ */
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_SUPPORTED_DEVICE("js");
+
+/*
+ * js_get_time_*() are different functions to get current time.
+ * js_delta_*() are functions to compute time difference.
+ */
+
+#ifdef __i386__
+
+static unsigned int js_get_time_rdtsc(void)
+{
+	unsigned int x;
+	__asm__ __volatile__ ( "rdtsc" : "=A" (x) );
+	return x;
+}
+
+static unsigned int js_get_time_pit(void)
+{
+	unsigned long flags;
+	unsigned int x;
+
+	__save_flags(flags);
+	__cli();
+	outb(0, 0x43);
+	x = inb(0x40);
+	x |= inb(0x40) << 8;
+	__restore_flags(flags);
+
+	return x;
+}
+
+static int js_delta_pit(unsigned int x, unsigned int y)
+{
+	return y - x + ( y < x ? 1193180L / HZ : 0 );
+}
+
+static unsigned int js_get_time_counter(void)
+{
+	static int time_counter = 0;
+	return time_counter++;
+}
+
+#else
+#ifdef __alpha__
+
+static unsigned int js_get_time_rpcc(void)
+{
+	unsigned int x;
+	__asm__ __volatile__ ( "rpcc %0" : "=r" (x) );
+	return x;
+}
+
+#else
+
+#ifndef MODULE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+static unsigned int js_get_time_system(void)
+{
+	static struct timeval js_tv;
+	get_fast_time(&js_tv);
+	return js_tv.tv_sec * 1000000L + js_tv.tv_usec;
+}
+#endif
+#endif
+
+#endif
+#endif
+
+static int js_delta_normal(unsigned int x, unsigned int y)
+{
+	return x - y;
+}
+
+/*
+ * js_calibrate_time() calibrates a given timer.
+ */
+
+static int __init js_calibrate_time(js_time_func get_time, js_delta_func delta)
+{
+	unsigned int t1, t2, t3;
+	unsigned long flags;
+
+	__save_flags(flags);
+	__cli();
+	t1 = get_time();
+	udelay(1000);
+	t2 = get_time();
+	t3 = get_time();
+	__restore_flags(flags);
+
+	return delta(t2, t1) - delta(t3, t2);
+}
+
+/*
+ * js_calibrate_time_counter() calibrates the counter timer, which can't
+ * be calibrated using the above function.
+ */
+
+#ifdef __i386__
+
+static int __init js_calibrate_time_counter(void)
+{
+	unsigned int i, j, t1, t2, t3;
+
+	j = jiffies; do { inb(0x201); t1 = js_get_time_counter(); } while (j == jiffies);
+	j = jiffies; do { inb(0x201); t2 = js_get_time_counter(); } while (j == jiffies);
+
+	j = (t2 - t1) * HZ / 1000;
+
+	t1 = js_get_time_pit();
+	for (i = 0; i < 1000; i++) {
+		inb(0x201);
+		js_get_time_counter();
+	}
+	t2 = js_get_time_pit();
+	t3 = js_get_time_pit();
+
+	i = 1193180L / (js_delta_pit(t2, t1) - js_delta_pit(t3, t2));
+
+	if (DIFF(i,j) > 5)
+		printk(KERN_WARNING "js: Counter timer calibration unsure,"
+			" pass1 (0.%d MHz) and pass2 (0.%d MHz) differ.\n", j, i);
+
+	return (i + j) >> 1;
+}
+
+#endif
+
+/*
+ * js_setup_time chooses the best available timers
+ * on the system and calibrates them.
+ */
+
+static int __init js_setup_time(void)
+{
+	int t;
+	char *name, *name_a;
+
+	name = "";
+	name_a = "";
+	js_time_speed = 0;
+	js_time_speed_a = 0;
+
+#ifdef __i386__
+
+	t = js_calibrate_time(js_get_time_pit, js_delta_pit);
+
+	if (DIFF(t, 1193) > 5)
+		printk(KERN_WARNING "js: Measured PIT speed is %d.%03d MHz, but should be 1.193 MHz.\n"
+		       KERN_WARNING "js: This is probably caused by wrong BogoMIPS value. It is: %ld, should be: %ld.\n",
+			t / 1000, t % 1000, loops_per_sec / 500000, loops_per_sec / (t * 500000 / 1193));
+
+	if (JS_HAS_RDTSC && (t = js_calibrate_time(js_get_time_rdtsc, js_delta_normal)) > 0) {
+
+		js_time_speed_a = t;
+		js_get_time_a = js_get_time_rdtsc;
+		js_delta_a = js_delta_normal;
+		js_time_speed = t;
+		js_get_time = js_get_time_rdtsc;
+		js_delta = js_delta_normal;
+		name = "RDTSC";
+
+	} else {
+
+		js_time_speed_a = t;
+		js_get_time_a = js_get_time_pit;
+		js_delta_a = js_delta_pit;
+		name_a = "PIT";
+
+		t = js_calibrate_time_counter();
+
+		js_time_speed = t;
+		js_get_time = js_get_time_counter;
+		js_delta = js_delta_normal;
+		name = "counter";
+
+	}
+
+#else
+#ifdef __alpha__
+
+	t = js_calibrate_time(js_get_time_rpcc, js_delta_normal);
+
+	js_time_speed_a = t;
+	js_get_time_a = js_get_time_rpcc;
+	js_delta_a = js_delta_normal;
+	js_time_speed = t;
+	js_get_time = js_get_time_rpcc;
+	js_delta = js_delta_normal;
+	name = "RPCC";
+
+#else
+
+#ifndef MODULE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+	t = js_calibrate_time(js_get_time_system, js_delta_normal);
+
+	js_time_speed_a = t;
+	js_get_time_a = js_get_time_system;
+	js_delta_a = js_delta_normal;
+	js_time_speed = t;
+	js_get_time = js_get_time_system;
+	js_delta = js_delta_normal;
+	name = "system";
+#endif
+#endif
+
+#endif
+#endif
+
+	printk(KERN_INFO "js: Version %d.%d.%d ",
+		JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff);
+
+	if (js_time_speed_a <= 0 || js_time_speed <= 0) {
+		printk("\n");
+		return -1;
+	}
+
+	printk("using ");
+
+	if (js_time_speed > 10000) {
+		t = js_time_speed / 1000 + (js_time_speed % 1000 >= 500);
+		printk("%d MHz ", t);
+	} else {
+		t = js_time_speed / 10 + (js_time_speed % 10 >= 5);
+		printk("%d.%02d MHz ", t / 100, t % 100);
+	}
+
+	if (js_get_time_a != js_get_time) {
+		t = js_time_speed_a / 10 + (js_time_speed_a % 10 >= 5);
+		printk("%s timer and %d.%02d MHz %s timer.\n",
+			name, t / 100, t % 100, name_a);
+	} else {
+		printk("%s timer.\n", name);
+	}
+
+	return 0;
+}
+
+
+/*
+ * js_correct() performs correction of raw joystick data.
+ */
+
+static int js_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;
+}
+
+/*
+ * js_button() returns value of button number i.
+ */
+
+static inline int js_button(int *buttons, int i)
+{
+	return (buttons[i >> 5] >> (i & 0x1f)) & 1;
+}
+
+
+/*
+ * js_add_event() adds an event to the buffer. This requires additional
+ * queue post-processing done by js_sync_buff.
+ */
+
+static void js_add_event(struct js_dev *jd, __u32 time, __u8 type, __u8 number, __s16 value)
+{
+	jd->buff[jd->ahead].time = time;
+	jd->buff[jd->ahead].type = type;
+	jd->buff[jd->ahead].number = number;
+	jd->buff[jd->ahead].value = value;
+	if (++jd->ahead == JS_BUFF_SIZE) jd->ahead = 0;
+}
+
+/*
+ * js_flush_data() does the same as js_process_data, except for that it doesn't
+ * generate any events - it just copies the data from new to cur.
+ */
+
+static void js_flush_data(struct js_dev *jd)
+{
+	int i;
+
+	for (i = 0; i < ((jd->num_buttons - 1) >> 5) + 1; i++)
+		jd->cur.buttons[i] = jd->new.buttons[i];
+	for (i = 0; i < jd->num_axes; i++)
+		jd->cur.axes[i] = jd->new.axes[i];
+}
+
+/*
+ * js_process_data() finds changes in button states and axis positions and adds
+ * them as events to the buffer.
+ */
+
+static void js_process_data(struct js_dev *jd)
+{
+	int i, t;
+
+	for (i = 0; i < jd->num_buttons; i++)
+	if ((t = js_button(jd->new.buttons, i)) != js_button(jd->cur.buttons, i)) {
+		js_add_event(jd, jiffies, JS_EVENT_BUTTON, i, t);
+		jd->cur.buttons[i >> 5] ^= (1 << (i & 0x1f));
+	}
+
+	for (i = 0; i < jd->num_axes; i++) {
+		t = js_correct(jd->new.axes[i], &jd->corr[i]);
+		if (((jd->corr[i].prec == -1) && t) ||
+			((DIFF(jd->new.axes[i], jd->cur.axes[i]) > jd->corr[i].prec) &&
+			(t != js_correct(jd->cur.axes[i], &jd->corr[i])))) {
+			js_add_event(jd, jiffies, JS_EVENT_AXIS, i, t);
+			jd->cur.axes[i] = jd->new.axes[i];
+		}
+	}
+}
+
+/*
+ * js_sync_buff() checks for all overflows caused by recent additions to the buffer.
+ * These happen only if some process is reading the data too slowly. It
+ * wakes up any process waiting for data.
+ */
+
+static void js_sync_buff(struct js_dev *jd)
+{
+	struct js_list *curl = jd->list;
+
+	if (jd->bhead != jd->ahead) {
+		if(ROT(jd->bhead, jd->tail, jd->ahead) || (jd->tail == jd->bhead)) {
+			while (curl) {
+				if (ROT(jd->bhead, curl->tail, jd->ahead) || (curl->tail == jd->bhead)) {
+					curl->tail = jd->ahead;
+					curl->startup = 0;
+				}
+				curl = curl->next;
+			}
+			jd->tail = jd->ahead;
+		}
+		jd->bhead = jd->ahead;
+		wake_up_interruptible(&jd->wait);
+	}
+}
+
+/*
+ * js_do_timer() acts as an interrupt replacement. It reads the data
+ * from all ports and then generates events for all devices.
+ */
+
+static void js_do_timer(unsigned long data)
+{
+	struct js_port *curp = js_port;
+	struct js_dev *curd = js_dev;
+	unsigned long flags;
+
+	while (curp) {
+		curp->read(curp->info, curp->axes, curp->buttons);
+		curp = curp->next;
+	}
+
+	spin_lock_irqsave(&js_lock, flags);
+
+	while (curd) {
+		if (data) {
+			js_process_data(curd);
+			js_sync_buff(curd);
+		} else {
+			js_flush_data(curd);
+		}
+		curd = curd->next;
+	}
+
+	spin_unlock_irqrestore(&js_lock, flags);
+
+	js_timer.expires = jiffies + JS_REFRESH_TIME;
+	add_timer(&js_timer);
+}
+
+/*
+ * js_read() copies one or more entries from jsd[].buff to user
+ * space.
+ */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+#else
+static int js_read(struct inode *inode, struct file *file, char *buf, int count)
+#endif
+{
+	struct wait_queue wait = { current, NULL };
+	struct js_event *buff = (void *) buf;
+	struct js_list *curl;
+	struct js_dev *jd;
+	unsigned long blocks = count / sizeof(struct js_event);
+	int written = 0;
+	int new_tail, orig_tail;
+	int retval = 0;
+	unsigned long flags;
+
+	curl = file->private_data;
+	jd = curl->dev;
+	orig_tail = curl->tail;
+
+/*
+ * Check user data.
+ */
+
+	if (!blocks)
+		return -EINVAL;
+
+/*
+ * Lock it.
+ */
+
+	spin_lock_irqsave(&js_lock, flags);
+
+/*
+ * Handle (non)blocking i/o.
+ */
+	if (count != sizeof(struct JS_DATA_TYPE)) {
+
+		if (GOF(curl->tail) == jd->bhead && curl->startup == jd->num_axes + jd->num_buttons) {
+
+			add_wait_queue(&jd->wait, &wait);
+			current->state = TASK_INTERRUPTIBLE;
+
+			while (GOF(curl->tail) == jd->bhead) {
+
+				if (file->f_flags & O_NONBLOCK) {
+					retval = -EAGAIN;
+					break;
+				}
+				if (signal_pending(current)) {
+					retval = -ERESTARTSYS;
+					break;
+				}
+
+				spin_unlock_irqrestore(&js_lock, flags);
+				schedule();
+				spin_lock_irqsave(&js_lock, flags);
+
+			}
+
+			current->state = TASK_RUNNING;
+			remove_wait_queue(&jd->wait, &wait);
+		}
+
+		if (retval) {
+			spin_unlock_irqrestore(&js_lock, flags);
+			return retval;
+		}
+
+/*
+ * Initial state.
+ */
+
+		while (curl->startup < jd->num_axes + jd->num_buttons && written < blocks && !retval) {
+
+			struct js_event tmpevent;
+
+			if (curl->startup < jd->num_buttons) {
+				tmpevent.type = JS_EVENT_BUTTON | JS_EVENT_INIT;
+				tmpevent.value = js_button(jd->cur.buttons, curl->startup);
+				tmpevent.number = curl->startup;
+			} else {
+				tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT;
+				tmpevent.value = js_correct(jd->cur.axes[curl->startup - jd->num_buttons],
+								&jd->corr[curl->startup - jd->num_buttons]);
+				tmpevent.number = curl->startup - jd->num_buttons;
+			}
+
+			tmpevent.time = jiffies * (1000/HZ);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+			if (copy_to_user(&buff[written], &tmpevent, sizeof(struct js_event)))
+				retval = -EFAULT;
+#else
+			if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event))))
+				memcpy_tofs(&buff[written], &tmpevent, sizeof(struct js_event));
+#endif
+
+			curl->startup++;
+			written++;
+		}
+
+/*
+ * Buffer data.
+ */
+
+		while ((jd->bhead != (new_tail = GOF(curl->tail))) && (written < blocks) && !retval) {
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+			if (copy_to_user(&buff[written], &jd->buff[new_tail], sizeof(struct js_event)))
+				retval = -EFAULT;
+			if (put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time))
+				retval = -EFAULT;
+#else
+			if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event)))) {
+				memcpy_tofs(&buff[written], &jd->buff[new_tail], sizeof(struct js_event));
+				put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time);
+			}
+#endif
+			curl->tail = new_tail;
+			written++;
+		}
+	}
+
+	else
+
+/*
+ * Handle version 0.x compatibility.
+ */
+
+	{
+		struct JS_DATA_TYPE data;
+
+		data.buttons = jd->new.buttons[0];
+		data.x = jd->num_axes < 1 ? 0 :
+			((js_correct(jd->new.axes[0], &jd->corr[0]) / 256) + 128) >> js_comp_glue.JS_CORR.x;
+		data.y = jd->num_axes < 2 ? 0 :
+			((js_correct(jd->new.axes[1], &jd->corr[1]) / 256) + 128) >> js_comp_glue.JS_CORR.y;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+		retval = copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
+#else
+		if (!(retval = verify_area(VERIFY_WRITE, buf, sizeof(struct JS_DATA_TYPE)))) {
+			memcpy_tofs(buf, &data, sizeof(struct JS_DATA_TYPE));
+		}
+#endif
+
+		curl->startup = 0;
+		curl->tail = GOB(jd->bhead);
+		if (!retval) retval = sizeof(struct JS_DATA_TYPE);
+	}
+
+/*
+ * Check main tail and move it.
+ */
+
+	if (orig_tail == jd->tail) {
+		new_tail = curl->tail;
+		curl = jd->list;
+		while (curl && curl->tail != jd->tail) {
+			if (ROT(jd->bhead, new_tail, curl->tail) ||
+				(jd->bhead == curl->tail)) new_tail = curl->tail;
+			curl = curl->next;
+		}
+		if (!curl) jd->tail = new_tail;
+	}
+
+	spin_unlock_irqrestore(&js_lock, flags);
+
+	return retval ? retval : written * sizeof(struct js_event);
+}
+
+/*
+ * js_poll() does select() support.
+ */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+
+static unsigned int js_poll(struct file *file, poll_table *wait)
+{
+	struct js_list *curl = file->private_data;
+	unsigned long flags;
+	int retval = 0;
+	poll_wait(file, &curl->dev->wait, wait);
+	spin_lock_irqsave(&js_lock, flags);	
+	if (GOF(curl->tail) != curl->dev->bhead ||
+		curl->startup < curl->dev->num_axes + curl->dev->num_buttons) retval = POLLIN | POLLRDNORM;
+	spin_unlock_irqrestore(&js_lock, flags);
+	return retval;
+}
+
+#else
+
+static int js_select(struct inode *inode, struct file *file, int sel_type, select_table *wait)
+{
+	struct js_list *curl = file->private_data;
+	if (sel_type == SEL_IN) {
+		if (GOF(curl->tail) != curl->dev->bhead) return 1;
+		select_wait(&curl->dev->wait, wait);
+	}
+	return 0;
+}
+
+#endif
+
+/*
+ * js_ioctl handles misc ioctl calls.
+ */
+
+static int js_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct js_list *curl;
+	struct js_dev *jd;
+	int len;
+
+	curl = file->private_data;
+	jd = curl->dev;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+
+	switch (cmd) {
+
+/*
+ * 0.x compatibility
+ */
+
+		case JS_SET_CAL:
+			return copy_from_user(&js_comp_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, &js_comp_glue.JS_CORR,
+				sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
+		case JS_SET_TIMEOUT:
+			return get_user(js_comp_glue.JS_TIMEOUT, (int *) arg);
+		case JS_GET_TIMEOUT:
+			return put_user(js_comp_glue.JS_TIMEOUT, (int *) arg);
+		case JS_SET_TIMELIMIT:
+			return get_user(js_comp_glue.JS_TIMELIMIT, (long *) arg);
+		case JS_GET_TIMELIMIT:
+			return put_user(js_comp_glue.JS_TIMELIMIT, (long *) arg);
+		case JS_SET_ALL:
+			return copy_from_user(&js_comp_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, &js_comp_glue,
+						sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0;
+
+/*
+ * 1.x ioctl calls
+ */
+
+		case JSIOCGVERSION:
+			return put_user(JS_VERSION, (__u32 *) arg);
+		case JSIOCGAXES:
+			return put_user(jd->num_axes, (__u8 *) arg);
+		case JSIOCGBUTTONS:
+			return put_user(jd->num_buttons, (__u8 *) arg);
+		case JSIOCSCORR:
+			return copy_from_user(jd->corr, (struct js_corr *) arg,
+						sizeof(struct js_corr) * jd->num_axes) ? -EFAULT : 0;
+		case JSIOCGCORR:
+			return copy_to_user((struct js_corr *) arg, jd->corr,
+						sizeof(struct js_corr) * jd->num_axes) ? -EFAULT : 0;
+		default:
+			if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
+				len = strlen(jd->name) + 1;
+				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+				if (copy_to_user((char *) arg, jd->name, len)) return -EFAULT;
+				return len;
+			}
+	}
+
+#else
+
+	switch (cmd) {
+
+/*
+ * 0.x compatibility
+ */
+
+		case JS_SET_CAL:
+			if (verify_area(VERIFY_READ, (struct JS_DATA_TYPE *) arg,
+				sizeof(struct JS_DATA_TYPE))) return -EFAULT;
+			memcpy_fromfs(&js_comp_glue.JS_CORR, (struct JS_DATA_SAVE_TYPE *) arg,
+				sizeof(struct JS_DATA_TYPE));
+			return 0;
+		case JS_GET_CAL:
+			if (verify_area(VERIFY_WRITE, (struct JS_DATA_TYPE *) arg,
+				sizeof(struct JS_DATA_TYPE))) return -EFAULT;
+			memcpy_tofs((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue.JS_CORR,
+				sizeof(struct JS_DATA_TYPE));
+			return 0;
+		case JS_SET_TIMEOUT:
+			if (verify_area(VERIFY_READ, (int *) arg, sizeof(int))) return -EFAULT;
+			js_comp_glue.JS_TIMEOUT = get_user((int *) arg);
+			return 0;
+		case JS_GET_TIMEOUT:
+			if (verify_area(VERIFY_WRITE, (int *) arg, sizeof(int))) return -EFAULT;
+			put_user(js_comp_glue.JS_TIMEOUT, (int *) arg);
+			return 0;
+		case JS_SET_TIMELIMIT:
+			if (verify_area(VERIFY_READ, (long *) arg, sizeof(long))) return -EFAULT;
+			js_comp_glue.JS_TIMELIMIT = get_user((long *) arg);
+			return 0;
+		case JS_GET_TIMELIMIT:
+			if (verify_area(VERIFY_WRITE, (long *) arg, sizeof(long))) return -EFAULT;
+			put_user(js_comp_glue.JS_TIMELIMIT, (long *) arg);
+			return 0;
+		case JS_SET_ALL:
+			if (verify_area(VERIFY_READ, (struct JS_DATA_SAVE_TYPE *) arg,
+				sizeof(struct JS_DATA_SAVE_TYPE))) return -EFAULT;
+			memcpy_fromfs(&js_comp_glue, (struct JS_DATA_SAVE_TYPE *) arg,
+				sizeof(struct JS_DATA_SAVE_TYPE));
+			return 0;
+		case JS_GET_ALL:
+			if (verify_area(VERIFY_WRITE, (struct JS_DATA_SAVE_TYPE *) arg,
+				sizeof(struct JS_DATA_SAVE_TYPE))) return -EFAULT;
+			memcpy_tofs((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue,
+				sizeof(struct JS_DATA_SAVE_TYPE));
+			return 0;
+
+/*
+ * 1.x ioctl calls
+ */
+
+		case JSIOCGVERSION:
+			if (verify_area(VERIFY_WRITE, (__u32 *) arg, sizeof(__u32))) return -EFAULT;
+			put_user(JS_VERSION, (__u32 *) arg);
+			return 0;
+		case JSIOCGAXES:
+			if (verify_area(VERIFY_WRITE, (__u8 *) arg, sizeof(__u8))) return -EFAULT;
+			put_user(jd->num_axes, (__u8 *) arg);
+			return 0;
+		case JSIOCGBUTTONS:
+			if (verify_area(VERIFY_WRITE, (__u8 *) arg, sizeof(__u8))) return -EFAULT;
+			put_user(jd->num_buttons, (__u8 *) arg);
+			return 0;
+		case JSIOCSCORR:
+			if (verify_area(VERIFY_READ, (struct js_corr *) arg,
+				sizeof(struct js_corr) * jd->num_axes)) return -EFAULT;
+			memcpy_fromfs(jd->corr, (struct js_corr *) arg,
+				sizeof(struct js_corr) * jd->num_axes);
+			return 0;
+		case JSIOCGCORR:
+			if (verify_area(VERIFY_WRITE, (struct js_corr *) arg,
+				sizeof(struct js_corr) * jd->num_axes)) return -EFAULT;
+			memcpy_tofs((struct js_corr *) arg,
+				jd->corr, sizeof(struct js_corr) * jd->num_axes);
+			return 0;
+		default:
+			if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
+				len = strlen(jd->name) + 1;
+				if (verify_area(VERIFY_WRITE, (char *) arg, len)) return -EFAULT;
+				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+				memcpy_tofs((char *) arg, jd->name, len);
+				return len;
+			}
+	}
+
+#endif
+
+	return -EINVAL;
+}
+
+/*
+ * js_open() performs necessary initialization and adds
+ * an entry to the linked list.
+ */
+
+static int js_open(struct inode *inode, struct file *file)
+{
+	struct js_list *curl;
+	struct js_dev *jd = js_dev;
+	int i = MINOR(inode->i_rdev);
+	unsigned long flags;
+
+	if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR)
+		return -EINVAL;
+
+	while (i > 0 && jd) {
+		jd = jd->next;
+		i--;
+	}
+
+	if (!jd) return -ENODEV;
+
+	jd->open(jd);
+
+	MOD_INC_USE_COUNT;
+	if (!js_use_count++) js_do_timer(0);
+
+	curl = jd->list;
+
+	spin_lock_irqsave(&js_lock, flags);
+
+	jd->list = kmalloc(sizeof(struct js_list), GFP_KERNEL);
+	jd->list->next = curl;
+	jd->list->dev = jd;
+	jd->list->startup = 0;
+	jd->list->tail = GOB(jd->bhead);
+
+	spin_unlock_irqrestore(&js_lock, flags);
+
+	file->private_data = jd->list;
+
+	return 0;
+}
+
+/*
+ * js_release() removes an entry from list and deallocates memory
+ * used by it.
+ */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+static int js_release(struct inode *inode, struct file *file)
+#else
+static void js_release(struct inode *inode, struct file *file)
+#endif
+{
+	struct js_list *curl = file->private_data;
+	struct js_dev *jd = curl->dev;
+	struct js_list **curp = &jd->list;
+	int new_tail;
+	unsigned long flags;
+
+	spin_lock_irqsave(&js_lock, flags);
+
+	while (*curp && (*curp != curl)) curp = &((*curp)->next);
+	*curp = (*curp)->next;
+
+	if (jd->list)
+	if (curl->tail == jd->tail) {
+		curl = jd->list;
+		new_tail = curl->tail;
+		while (curl && curl->tail != jd->tail) {
+			if (ROT(jd->bhead, new_tail, curl->tail) ||
+			       (jd->bhead == curl->tail)) new_tail = curl->tail;
+			curl = curl->next;
+		}
+		if (!curl) jd->tail = new_tail;
+	}
+
+	spin_unlock_irqrestore(&js_lock, flags);
+
+	kfree(file->private_data);
+
+	if (!--js_use_count) del_timer(&js_timer);
+	MOD_DEC_USE_COUNT;
+
+	jd->close(jd);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+	return 0;
+#endif
+}
+
+/*
+ * js_dump_mem() dumps all data structures in memory.
+ * It's used for debugging only.
+ */
+
+#if 0
+static void js_dump_mem(void)
+{
+
+	struct js_port *curp = js_port;
+	struct js_dev *curd = js_dev;
+	int i;
+
+	printk(",--- Dumping Devices:\n");
+	printk("| js_dev = %x\n", (int) js_dev);
+
+	while (curd) {
+		printk("|  %s-device %x, next %x axes %d, buttons %d, port %x - %#x\n",
+			curd->next ? "|":"`",
+			(int) curd, (int) curd->next, curd->num_axes, curd->num_buttons, (int) curd->port, curd->port->io);
+		curd = curd->next;
+	}
+
+	printk(">--- Dumping ports:\n");
+	printk("| js_port = %x\n", (int) js_port);
+
+	while (curp) {
+		printk("|  %s-port %x, next %x, io %#x, devices %d\n",
+			curp->next ? "|":"`",
+			(int) curp, (int) curp->next, curp->io, curp->ndevs);
+		for (i = 0; i < curp->ndevs; i++) {
+			curd = curp->devs[i];
+			if (curd)
+			printk("|  %s %s-device %x, next %x axes %d, buttons %d, port %x\n",
+				curp->next ? "|":" ", (i < curp->ndevs-1) ? "|":"`",
+				(int) curd, (int) curd->next, curd->num_axes, curd->num_buttons, (int) curd->port);
+			else
+			printk("|  %s %s-device %x, not there\n",
+				curp->next ? "|":" ", (i < curp->ndevs-1) ? "|":"`", (int) curd);
+
+		}
+		curp = curp->next;
+	}
+
+	printk("`--- Done\n");
+}
+#endif
+
+
+struct js_port *js_register_port(struct js_port *port,
+				void *info, int devs, int infos, js_read_func read)
+{
+	struct js_port **ptrp = &js_port;
+	struct js_port *curp;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&js_lock, flags);
+
+	while (*ptrp) ptrp=&((*ptrp)->next);
+	*ptrp = curp = kmalloc(sizeof(struct js_port), GFP_KERNEL);
+
+	curp->next = NULL;
+	curp->prev = port;
+	curp->read = read;
+	curp->ndevs = devs;
+
+	curp->devs = kmalloc(devs * sizeof(struct js_dev*), GFP_KERNEL);
+
+	for (i = 0; i < devs; i++)
+		curp->devs[i] = NULL;
+
+	curp->axes = kmalloc(devs * sizeof(int*), GFP_KERNEL);
+	curp->buttons = kmalloc(devs * sizeof(int*), GFP_KERNEL);
+	curp->corr = kmalloc(devs * sizeof(struct js_corr*), GFP_KERNEL);
+
+	if (infos) {
+		curp->info = kmalloc(infos, GFP_KERNEL);
+		memcpy(curp->info, info, infos);
+	} else {
+		curp->info = NULL;
+	}
+
+	spin_unlock_irqrestore(&js_lock, flags);
+
+	return curp;
+}
+
+struct js_port *js_unregister_port(struct js_port *port)
+{
+	struct js_port **curp = &js_port;
+	struct js_port *prev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&js_lock, flags);
+
+	while (*curp && (*curp != port)) curp = &((*curp)->next);
+	*curp = (*curp)->next;
+
+	prev = port->prev;
+	kfree(port->devs);
+	kfree(port->axes);
+	kfree(port->buttons);
+	kfree(port->corr);
+	if (port->info) kfree(port->info);
+	kfree(port);
+
+	spin_unlock_irqrestore(&js_lock, flags);
+
+	return prev;
+}
+
+int js_register_device(struct js_port *port, int number, int axes, int buttons, char *name,
+					js_ops_func open, js_ops_func close)
+{
+	struct js_dev **ptrd = &js_dev;
+	struct js_dev *curd;
+	int i = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&js_lock, flags);
+
+	while (*ptrd) {
+		ptrd=&(*ptrd)->next;
+		i++;
+	}
+
+	*ptrd = curd = kmalloc(sizeof(struct js_dev), GFP_KERNEL);
+
+	curd->next = NULL;
+	curd->list = NULL;
+	curd->port = port;
+	curd->wait = NULL;
+	curd->open = open;
+	curd->close = close;
+
+	curd->ahead = 0;
+	curd->bhead = 0;
+	curd->tail = JS_BUFF_SIZE - 1;
+	curd->num_axes = axes;
+	curd->num_buttons = buttons;
+
+	curd->name = kmalloc(strlen(name) + 1, GFP_KERNEL);
+	strcpy(curd->name, name);
+
+	curd->cur.axes = kmalloc(axes * sizeof(int), GFP_KERNEL);
+	curd->cur.buttons = kmalloc((((buttons - 1) >> 5) + 1) * sizeof(int), GFP_KERNEL);
+	curd->new.axes = kmalloc(axes * sizeof(int), GFP_KERNEL);
+	curd->new.buttons = kmalloc((((buttons -1 ) >> 5) + 1) * sizeof(int), GFP_KERNEL);
+	curd->corr = kmalloc(axes * sizeof(struct js_corr), GFP_KERNEL);
+
+	port->devs[number] = curd;
+	port->axes[number] = curd->new.axes;
+	port->buttons[number] = curd->new.buttons;
+	port->corr[number] = curd->corr;
+
+	spin_unlock_irqrestore(&js_lock, flags);	
+
+	return i;
+}
+
+void js_unregister_device(struct js_dev *dev)
+{
+	struct js_dev **curd = &js_dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&js_lock, flags);
+
+	while (*curd && (*curd != dev)) curd = &((*curd)->next);
+	*curd = (*curd)->next;
+
+	spin_unlock_irqrestore(&js_lock, flags);	
+
+	kfree(dev->cur.axes);
+	kfree(dev->new.axes);
+	kfree(dev->cur.buttons);
+	kfree(dev->new.buttons);
+	kfree(dev->corr);
+	kfree(dev->name);
+	kfree(dev);
+}
+
+/*
+ * The operations structure.
+ */
+
+static struct file_operations js_fops =
+{
+	read:		js_read,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+	poll:		js_poll,
+#else
+	select:		js_select,
+#endif
+	ioctl:		js_ioctl,
+	open:		js_open,
+	release:	js_release,
+};
+
+/*
+ * js_init() registers the driver and calls the probe function.
+ * also initializes some crucial variables.
+ */
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init js_init(void)
+#endif
+{
+	int result;
+
+	js_setup_time();
+
+	if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) {
+		printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR);
+		return -EBUSY;
+	}
+
+	spin_lock_init(&js_lock);
+
+	init_timer(&js_timer);
+	js_timer.function = js_do_timer;
+	js_timer.data = 1;
+
+	memset(&js_comp_glue, 0, sizeof(struct JS_DATA_SAVE_TYPE));
+	js_comp_glue.JS_TIMEOUT = JS_DEF_TIMEOUT;
+	js_comp_glue.JS_TIMELIMIT = JS_DEF_TIMELIMIT;
+
+#ifdef MODULE
+	result = 0;
+#else
+	result = -ENODEV;
+#ifdef CONFIG_JOY_LIGHTNING
+	if (!js_l4_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_SIDEWINDER
+	if (!js_sw_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_ASSASIN
+	if (!js_as_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_LOGITECH
+	if (!js_lt_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_THRUSTMASTER
+	if (!js_tm_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_GRAVIS
+	if (!js_gr_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_ANALOG
+	if (!js_an_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_CONSOLE
+	if (!js_console_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_DB9
+	if (!js_db9_init()) result = 0;
+#endif
+#ifdef CONFIG_JOY_AMIGA
+	if (!js_am_init()) result = 0;
+#endif
+	if (result) printk(KERN_ERR "js: no joysticks found\n");
+#endif
+
+	return result;
+}
+
+/*
+ * cleanup_module() handles module removal.
+ */
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	del_timer(&js_timer);
+	if (unregister_chrdev(JOYSTICK_MAJOR, "js"))
+		printk(KERN_ERR "js: can't unregister device\n");
+}
+#endif
diff -u --recursive --new-file v2.1.124/linux/drivers/char/joystick.c linux/drivers/char/joystick.c
--- v2.1.124/linux/drivers/char/joystick.c	Thu Aug 27 19:56:29 1998
+++ linux/drivers/char/joystick.c	Wed Dec 31 16:00:00 1969
@@ -1,861 +0,0 @@
-/*
- * linux/drivers/char/joystick.c  Version 1.0.9
- * Copyright (C) 1996-1998 Vojtech Pavlik
- */
-
-/*
- * This is joystick driver for Linux. It supports up to two analog joysticks
- * on a PC compatible machine. See Documentation/joystick.txt for changelog
- * and credits.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/malloc.h>
-#include <linux/poll.h>
-#include <linux/major.h>
-#include <linux/joystick.h>
-#include <asm/io.h>
-
-#define PIT_HZ			1193180L	/* PIT clock is 1.19318 MHz */
-
-#define JS_MAXTIME		PIT_HZ/250	/* timeout for read (4 ms) */
-
-#define JS_TIMER_PERIOD		HZ/50		/* button valid time (20 ms) */
-#define JS_BH_MIN_PERIOD	HZ/25		/* axis min valid time (40 ms) */
-#define JS_BH_MAX_PERIOD	HZ/25*2		/* axis max valid time (80 ms) */
-
-#define JS_FIFO_SIZE		16		/* number of FIFO entries */
-#define JS_BUFF_SIZE		32 		/* output buffer size */
-#define JS_RETRIES		4		/* number of retries */
-#define JS_DEF_PREC		16		/* initial precision for all axes */
-
-#define JS_NUM			2		/* number of joysticks */
-
-#define JS_AXES 		0x0f		/* bit mask for all axes */
-#define JS_BUTTONS		0xf0		/* bit mask for all buttons */
-
-#define PIT_MODE 		0x43		/* timer mode port */
-#define PIT_DATA 		0x40		/* timer 0 data port */
-#define JS_PORT 		0x201		/* joystick port */
-
-#define JS_TRIGGER		0xff		/* triggers one-shots */
-#define PIT_READ_TIMER		0x00		/* to read timer 0 */
-
-#define DELTA(X,Y,Z)	((X)-(Y)+(((X)>=(Y))?0:Z))				/* cyclic delta */
-#define DELTA_T(X,Y)	DELTA((X),(Y),(PIT_HZ/HZ))				/* for time measurement */
-#define DELTA_TX(X,Y,Z)	DELTA_T((X),((Y)&0xFF)|(((Z)&0xFF)<<8))
-#define ROT(A,B,C)	((((A)<(C))&&(((B)>(A))&&((B)<(C))))||(((A)>(C))&&(((B)>(A))||((B)<(C)))))
-#define GOF(X)		(((X)==JS_BUFF_SIZE-1)?0:(X)+1)
-#define GOFF(X)		(((X)==JS_FIFO_SIZE-1)?0:(X)+1)
-#define GOB(X)		((X)?(X)-1:JS_BUFF_SIZE-1)
-
-struct js_data {
-	int ahead;
-	int bhead;
-	int tail;
-	struct js_event buff[JS_BUFF_SIZE];
-	struct js_list *list;
-	struct wait_queue *wait;
-	unsigned int exist;
-};
-
-struct js_axis {
-	int value;
-	struct js_corr corr;
-};
-
-struct js_list {
-	struct js_list *next;			/* next-in-list pointer */
-	unsigned long time;			/* when the device was open */
-	int tail;				/* a tail for js_buff */
-	char startup;
-};
-
-struct js_fifo {
-	unsigned long time;
-	unsigned long event;
-};
-
-static struct js_data jsd[JS_NUM];			/* joystick data */
-static struct timer_list js_timer;			/* joystick timer */
-
-static unsigned char js_fifo_head = 0;			/* head of the fifo */
-static unsigned char js_fifo_tail = JS_FIFO_SIZE - 1;	/* tail of the fifo */
-static struct js_fifo js_fifo[JS_FIFO_SIZE];		/* the fifo */
-
-static unsigned char js_last_buttons = 0;		/* last read button state */
-static unsigned long js_bh_time = 0;			/* last read axis time */
-static unsigned long js_mark_time = 0;
-
-static unsigned char js_axes_exist;			/* all axes that exist */
-static unsigned char js_buttons_exist;			/* all buttons that exist */
-
-static struct js_axis js_axis[4];
-static unsigned int js_buttons = 0;
-
-MODULE_AUTHOR("Vojtech Pavlik <vojtech@atrey.karlin.mff.cuni.cz>");
-MODULE_SUPPORTED_DEVICE("js");
-MODULE_PARM(js, "1-2b");
-
-static char js[] = {0, 0};
-
-/*
- * get_pit() returns the immediate state of PIT0. Must be run
- * with interrupts disabled.
- */
-
-static inline int get_pit(void)
-{
-	int t, flags;
-
-	save_flags(flags);
-	cli();
-	outb(PIT_READ_TIMER, PIT_MODE);
-	t = inb(PIT_DATA);
-	t |= (int) inb(PIT_DATA) << 8;
-	restore_flags(flags);
-	return t;
-}
-
-/*
- * count_bits() counts set bits in a byte.
- */
-
-static int count_bits(unsigned int c)
-{
-	int i = 0;
-	while (c) {
-		i += c & 1;
-		c >>= 1;
-	}
-	return i;
-}
-
-/*
- * js_correct() performs correction of raw joystick data.
- */
-
-static int js_correct(int value, struct js_corr *corr)
-{
-	int t;
-
-	if (corr->type == JS_CORR_NONE) return value;
-	t = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : value - corr->coef[1]) : value - corr->coef[0];
-
-	switch (corr->type) {
-	case JS_CORR_BROKEN:
-		t = t < 0 ? ((corr->coef[2] * t) >> 14) : ((corr->coef[3] * t) >> 14);
-		break;
-	default:
-		return 0;
-	}
-
-	if (t < -32767) return -32767;
-	if (t >  32767) return  32767;
-
-	return t;
-}
-
-/*
- * js_compare() compares two close axis values and decides
- * whether they are "same".
- */
-
-static int js_compare(int x, int y, int prec)
-{
-	return (x < y + prec) && (y < x + prec);
-}
-
-/*
- * js_probe() probes for joysticks
- */
-
-inline int js_probe(void)
-{
-	int t;
-
-	outb(JS_TRIGGER, JS_PORT);
-	t = get_pit();
-	while (DELTA_T(t, get_pit()) < JS_MAXTIME);
-	t = inb(JS_PORT);
-
-	if (js[0] || js[1]) {
-		jsd[0].exist = js[0] & ~(t & JS_AXES);
-		jsd[1].exist = js[1] & ~(t & JS_AXES);
-	} else
-	switch (t & JS_AXES) {
-		case 0x0c: jsd[0].exist = 0x33; jsd[1].exist = 0x00; break;	/* joystick 0 connected */
-		case 0x03: jsd[0].exist = 0xcc; jsd[1].exist = 0x00; break;	/* joystick 1 connected */
-		case 0x04: jsd[0].exist = 0xfb; jsd[1].exist = 0x00; break;	/* 3-axis joystick connected */
-		case 0x00: jsd[0].exist = 0x33; jsd[1].exist = 0xcc; break;	/* joysticks 0 and 1 connected */
-		default:   jsd[0].exist = 0x00; jsd[1].exist = 0x00; return -1;	/* no joysticks */
-	}
-
-	js_axes_exist = (jsd[0].exist | jsd[1].exist) & JS_AXES;
-	js_buttons_exist = (jsd[0].exist | jsd[1].exist) & JS_BUTTONS;
-
-	return 0;
-}
-
-/*
- * js_do_timer() controls the action by adding entries to the event
- * fifo each time a button changes its state or axis valid time
- * expires.
- */
-
-static void js_do_timer(unsigned long data)
-{
-	int t = ~inb(JS_PORT) & js_buttons_exist;
-	if ((js_last_buttons != t) && (js_fifo_head != js_fifo_tail)) {
-		js_fifo[js_fifo_head].event = js_last_buttons = t;
-		js_fifo[js_fifo_head].time = jiffies;
-		js_fifo_head++;
-		if (js_fifo_head == JS_FIFO_SIZE) js_fifo_head = 0;
-		if (!js_mark_time) {
-			js_mark_time = jiffies;
-			mark_bh(JS_BH);
-		}
-	}
-	else
- 	if ((jiffies > js_bh_time + JS_BH_MAX_PERIOD) && !js_mark_time) {
-		js_mark_time = jiffies;
-		mark_bh(JS_BH);
-	}
-	js_timer.expires = jiffies + JS_TIMER_PERIOD;
-	add_timer(&js_timer);
-}
-
-
-/*
- * Put an event in the buffer. This requires additional queue processing
- * done by js_sync_buff, otherwise the buffer will be corrupted.
- */
-
-static void js_add_event(int i, __u32 time, __u8 type, __u8 number, __u16 value)
-{
-	int ahead = jsd[i].ahead++;
-	jsd[i].buff[ahead].time = time;
-	jsd[i].buff[ahead].type = type;
-	jsd[i].buff[ahead].number = number;
-	jsd[i].buff[ahead].value = value;
-	if (jsd[i].ahead == JS_BUFF_SIZE) jsd[i].ahead=0;
-}
-
-/*
- * This checks for all owerflows caused by recent additions to the buffer.
- * It does anything only if some processes are reading the data too slowly.
- */
-
-static void js_sync_buff(void)
-{
-	int i;
-
-	for (i = 0; i < JS_NUM; i++)
-	if (jsd[i].list)
-	if (jsd[i].bhead != jsd[i].ahead)	{
-		if (ROT(jsd[i].bhead, jsd[i].tail, jsd[i].ahead) || (jsd[i].tail == jsd[i].bhead)) {
-			struct js_list *curl;
-			curl = jsd[i].list;
-			while (curl) {
-				if (ROT(jsd[i].bhead, curl->tail, jsd[i].ahead) || (curl->tail == jsd[i].bhead)) {
-					curl->tail = jsd[i].ahead;
-					curl->startup = jsd[i].exist;
-				}
-				curl = curl->next;
-			}
-			jsd[i].tail = jsd[i].ahead;
-		}
-		jsd[i].bhead = jsd[i].ahead;
-		wake_up_interruptible(&jsd[i].wait);
-	}
-}
-
-/*
- * js_do_bh() does the main processing and adds events to output buffers.
- */
-
-static void js_do_bh(void)
-{
-
-	int i, j, k;
-	unsigned int t;
-
-	if (jiffies > js_bh_time + JS_BH_MIN_PERIOD) {
-
-		unsigned int old_axis[4];
-		unsigned int t_low, t_high;
-		unsigned int flags, joy_state;
-		unsigned int t1l, t1h, jsm;
-		unsigned char jss;
-		unsigned char again;
-		unsigned char retries = 0;
-
-		for (i = 0; i < 4; i++)
-			old_axis[i] = js_axis[i].value;
-
-		do {
-			i = 0;
-			again = 0;
-			t_low = 0;
-			t_high = 0;
-			joy_state = JS_AXES;
-
-/*
- * Measure the axes.
- */
-
-			save_flags(flags);
-			cli();						/* no interrupts */
-			outb(JS_TRIGGER, JS_PORT);			/* trigger one-shots */
-			outb(PIT_READ_TIMER, PIT_MODE);			/* read timer */
-			t = (t1l = inb(PIT_DATA)) |
-			    (t1h = inb(PIT_DATA)) << 8;
-			restore_flags(flags);
-
-			do {
-				jss = inb(JS_PORT);
-				if ((jss ^ joy_state) & js_axes_exist) {
-					t_low = (t_low << 8) | t1l;
-					t_high = (t_high << 8) | t1h;
-					joy_state = (joy_state << 8) | jss;
-					i++;
-				}
-
-				cli();
-				outb(PIT_READ_TIMER, PIT_MODE);
-				t1l = inb(PIT_DATA);
-				t1h = inb(PIT_DATA);
-				restore_flags(flags);
-
-			} while ((jss & js_axes_exist) && (DELTA_TX(t, t1l, t1h) < JS_MAXTIME));
-
-/*
- * Process the gathered axis data in joy_state.
- */
-
-			joy_state ^= ((joy_state >> 8) | 0xff000000L);	/* More magic */
-
-			for (; i > 0; i--) {
-				for (j = 0; j < 4; j++)
-				if (joy_state & js_axes_exist & (1 << j)) {
-					jsm = DELTA_TX(t, t_low, t_high);
-					if (!js_compare(jsm, js_axis[j].value, js_axis[j].corr.prec)) {
-						if (jsm < js_axis[j].value || !retries)
-							js_axis[j].value = jsm;
-						again = 1;
-					}
-				}
-				joy_state = joy_state >> 8;
-				t_low = t_low >> 8;
-				t_high = t_high >> 8;
-			}
-
-		} while (retries++ < JS_RETRIES && again);
-
-/*
- * Check if joystick lost.
- */
-
-		for (i = 0; i < JS_NUM; i++) {
-
-			if (jsd[i].exist && ((jss & jsd[i].exist & JS_AXES) == (jsd[i].exist & JS_AXES))) {
-				printk(KERN_WARNING "js%d: joystick lost.\n", i);
-				js_buttons_exist &= ~jsd[i].exist;
-				js_axes_exist &= ~jsd[i].exist;
-				jsd[i].exist = 0;
-				wake_up_interruptible(&jsd[i].wait);
-			}
-
-			if ((jss & jsd[i].exist & JS_AXES)) {
-				printk(KERN_WARNING "js%d: joystick broken. Check cables.\n", i);
-			}
-
-		}
-
-/*
- * Put changed axes into output buffer.
- */
-
-		if (retries > 1)
-		for (i = 0; i < JS_NUM; i++)
-		if (jsd[i].list) {
-			k = 0;
-			for (j = 0; j < 4; j++)
-			if ((1 << j) & jsd[i].exist) {
-				if ((t = js_correct(js_axis[j].value, &js_axis[j].corr)) !=
-					js_correct(old_axis[j], &js_axis[j].corr))
-					js_add_event(i, js_mark_time, JS_EVENT_AXIS, k, t);
-				k++;
-			}
-		}
-		js_bh_time = jiffies;
-	}
-	js_mark_time = 0;
-
-/*
- * And now process the button fifo.
- */
-
-	while (js_fifo_head != (t = GOFF(js_fifo_tail))) {
-		for (i = 0; i < JS_NUM; i++)
-		if (jsd[i].list) {
-			k = 0;
-			for (j = 4; j < 8; j++)
-			if ((1 << j) & jsd[i].exist) {
-				if ((1 << j) & (js_buttons ^ js_fifo[t].event))
-					js_add_event(i, js_fifo[t].time, JS_EVENT_BUTTON, k, (js_fifo[t].event >> j) & 1);
-				k++;
-			}
-		}
-		js_buttons = js_fifo[js_fifo_tail = t].event;
-	}
-
-/*
- * Synchronize the buffer.
- */
-
-	js_sync_buff();
-
-}
-
-/*
- * js_lseek() just returns with error.
- */
-
-static loff_t js_lseek(struct file *file, loff_t offset, int origin)
-{
-	return -ESPIPE;
-}
-
-/*
- * js_read() copies one or more entries from jsd[].buff to user
- * space.
- */
-
-static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos)
-{
-	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
-	struct wait_queue wait = { current, NULL };
-	struct js_list *curl = file->private_data;
-	struct js_event *buff = (void *) buf;
-	unsigned long blocks = count / sizeof(struct js_event);
-	unsigned long i = 0, j;
-	int t, u = curl->tail;
-	int retval = 0;
-
-/*
- * Check user data.
- */
-
-	if (MAJOR(file->f_dentry->d_inode->i_rdev) != JOYSTICK_MAJOR)
-		return -EINVAL;
-	if (file->f_pos < 0)
-		return -EINVAL;
-	if (!blocks)
-		return -EINVAL;
-	if (!curl)
-		return -EINVAL;
-
-	if (minor > JS_NUM)
-		return -ENODEV;
-	if (!jsd[minor].exist)
-		return -ENODEV;
-
-/*
- * Handle (non)blocking i/o.
- */
-
-
-	if ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup && count != sizeof(struct JS_DATA_TYPE))
-		|| (curl->startup && !js_bh_time)) {
-
-		add_wait_queue(&jsd[minor].wait, &wait);
-		current->state = TASK_INTERRUPTIBLE;
-
-		while ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup && count != sizeof(struct JS_DATA_TYPE))
-			|| (curl->startup && !js_bh_time)) {
-
-			if (file->f_flags & O_NONBLOCK) {
-				retval = -EAGAIN;
-				break;
-			}
-			if (signal_pending(current)) {
-				retval = -ERESTARTSYS;
-				break;
-			}
-			schedule();
-			if (!jsd[minor].exist) {
-				retval = -ENODEV;
-				break;
-			}
-		}
-		current->state = TASK_RUNNING;
-		remove_wait_queue(&jsd[minor].wait, &wait);
-	}
-
-	if (retval) return retval;
-
-/*
- * Do the i/o.
- */
-	if (count != sizeof(struct JS_DATA_TYPE)) {
-
-		if (curl->startup) {
-			struct js_event tmpevent;
-/*
- * Initial button state.
- */
-
-			t = 0;
-			for (j = 0; j < 4 && (i < blocks) && !retval; j++)
-			if (jsd[minor].exist & (1 << j)) {
-				if (curl->startup & (1 << j)) {
-					tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT;
-					tmpevent.number = t;
-					tmpevent.value = js_correct(js_axis[j].value, &js_axis[j].corr);
-				if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event)))
-						retval = -EFAULT;
-					if (put_user((__u32)((jiffies - curl->time) * (1000/HZ)), &buff[i].time))
-						retval = -EFAULT;
-					curl->startup &= ~(1 << j);
-					i++;
-				}
-				t++;
-			}
-
-/*
- * Initial axis state.
- */
-
-			t = 0;
-			for (j = 4; j < 8 && (i < blocks) && !retval; j++)
-			if (jsd[minor].exist & (1 << j)) {
-				if (curl->startup & (1 << j)) {
-					tmpevent.type = JS_EVENT_BUTTON | JS_EVENT_INIT;
-					tmpevent.number = t;
-					tmpevent.value = (js_buttons >> j) & 1;
-					if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event)))
-						retval = -EFAULT;
-					if (put_user((__u32)((jiffies - curl->time) * (1000/HZ)), &buff[i].time))
-						retval = -EFAULT;
-					curl->startup &= ~(1 << j);
-					i++;
-				}
-				t++;
-			}
-		}
-
-/*
- * Buffer data.
- */
-
-		while ((jsd[minor].ahead != (t = GOF(curl->tail))) && (i < blocks) && !retval) {
-			if (copy_to_user(&buff[i], &jsd[minor].buff[t], sizeof(struct js_event)))
-				retval = -EFAULT;
-			if (put_user((__u32)((jsd[minor].buff[t].time - curl->time) * (1000/HZ)), &buff[i].time))
-				retval = -EFAULT;
-			curl->tail = t;
-			i++;
-		}
-
-	}
-
-	else
-
-/*
- * Handle version 0.x compatibility.
- */
-
-	{
-		struct JS_DATA_TYPE *bufo = (void *) buf;
-		int buttons = 0;
-
-		while (~jsd[minor].exist & (1<<i)) i++;
-		copy_to_user(&bufo->x, &js_axis[i].value, sizeof(int));
-
-		i++;
-		while (~jsd[minor].exist & (1<<i)) i++;
-		copy_to_user(&bufo->y, &js_axis[i].value, sizeof(int));
-
-		i = 0;
-		for (j = 4; j < 8; j++)
-		if ((1 << j) & jsd[minor].exist)
-			buttons |= (!!(js_last_buttons & (1 << j))) << (i++);
-		copy_to_user(&bufo->buttons, &buttons, sizeof(int));
-
-		curl->startup = 0;
-		curl->tail = GOB(jsd[minor].ahead);
-		retval = sizeof(struct JS_DATA_TYPE);
-	}
-
-/*
- * Check main tail and move it.
- */
-
-	if (u == jsd[minor].tail) {
-		t = curl->tail;
-		curl = jsd[minor].list;
-		while (curl && curl->tail != jsd[minor].tail) {
-			if (ROT(jsd[minor].ahead, t, curl->tail) ||
-				(jsd[minor].ahead == curl->tail)) t = curl->tail;
-			curl = curl->next;
-		}
-		if (!curl) jsd[minor].tail = t;
-	}
-
-	return retval ? retval : i*sizeof(struct js_event);
-}
-
-/*
- * js_poll() does select() support.
- */
-
-static unsigned int js_poll(struct file *file, poll_table *wait)
-{
-	struct js_list *curl;
-	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
- 	curl = file->private_data;
-
-	poll_wait(file, &jsd[minor].wait, wait);
-	if (GOF(curl->tail) != jsd[minor].ahead)
-		return POLLIN | POLLRDNORM;
-	return 0;
-}
-
-/*
- * js_ioctl handles misc ioctl calls.
- */
-
-static int js_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
-{
-	unsigned int minor = MINOR(inode->i_rdev);
-	int i, j;
-
-	if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR)
-		return -EINVAL;
-	if (minor > JS_NUM)
-		return -ENODEV;
-	if (!jsd[minor].exist)
-		return -ENODEV;
-
-	switch (cmd) {
-	case JSIOCGVERSION:
-		if(put_user(JS_VERSION, (__u32 *) arg)) return -EFAULT;
-		break;
-	case JSIOCGAXES:
-		if(put_user(count_bits(jsd[minor].exist & JS_AXES), (__u8 *) arg)) return -EFAULT;
-		break;
-	case JSIOCGBUTTONS:
-		if(put_user(count_bits(jsd[minor].exist & JS_BUTTONS), (__u8 *) arg)) return -EFAULT;
-		break;
-	case JSIOCSCORR:
-		j = 0;
-		for (i = 0; i < 4; i++)
-		if ((1 << i) & jsd[minor].exist) {
-			if (copy_from_user(&js_axis[i].corr, (void *) arg + j * sizeof(struct js_corr),
-				sizeof(struct js_corr))) return -EFAULT;
-			j++;
-		}
-		js_bh_time = 0;
-		break;
-	case JSIOCGCORR:
-		j = 0;
-		for (i = 0; i < 4; i++)
-		if ((1 << i) & jsd[minor].exist) {
-			if (copy_to_user((void *) arg + j * sizeof(struct js_corr), &js_axis[i].corr,
-				sizeof(struct js_corr))) return -EFAULT;
-			j++;
-		}
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-/*
- * js_open() performs necessary initialization and adds
- * an entry to the linked list.
- */
-
-static int js_open(struct inode *inode, struct file *file)
-{
-	unsigned int minor = MINOR(inode->i_rdev);
-	struct js_list *curl;
-	int t;
-
-	if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR)
-		return -EINVAL;
-	if (minor > JS_NUM)
-		return -ENODEV;
-	if (!jsd[minor].exist) {
-		js_probe();
-		if (jsd[minor].exist) printk(KERN_INFO "js%d: %d-axis %d-button joystick at %#x\n",
-			minor, count_bits(jsd[minor].exist & JS_AXES), count_bits(jsd[minor].exist & JS_BUTTONS), JS_PORT);
-		else return -ENODEV;
-	}
-
-	MOD_INC_USE_COUNT;
-
-	if (!jsd[0].list && !jsd[1].list) {
-		js_timer.expires = jiffies + JS_TIMER_PERIOD;
-		add_timer(&js_timer);
-	}
-
-	curl = jsd[minor].list;
-	jsd[minor].list = kmalloc(sizeof(struct js_list), GFP_KERNEL);
-	jsd[minor].list->next = curl;
-	jsd[minor].list->startup = jsd[minor].exist;
-	jsd[minor].list->tail = t = GOB(jsd[minor].ahead);
-	jsd[minor].list->time = jiffies;
-
-	file->private_data = jsd[minor].list;
-
-	return 0;
-}
-
-/*
- * js_release() removes an entry from list and deallocates memory
- * used by it.
- */
-
-static int js_release(struct inode *inode, struct file *file)
-{
-	unsigned int minor = MINOR(inode->i_rdev);
-	struct js_list **curp, *curl;
-	int t;
-
-	curp = &jsd[minor].list;
-	curl = file->private_data;
-
-	while (*curp && (*curp != curl)) curp = &((*curp)->next);
- 	*curp = (*curp)->next;
-
-	if (jsd[minor].list) {
-		if (curl->tail == jsd[minor].tail) {
-			curl = jsd[minor].list;
-			t = curl->tail;
-			while (curl && curl->tail != jsd[minor].tail) {
-				if (ROT(jsd[minor].ahead, t, curl->tail) ||
-					(jsd[minor].ahead == curl->tail)) t = curl->tail;
-				curl = curl->next;
-			}
-			if (!curl) jsd[minor].tail = t;
-		}
-	}
-
-	kfree(file->private_data);
-	if (!jsd[0].list && !jsd[1].list) del_timer(&js_timer);
-
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-/*
- * The operations structure.
- */
-
-static struct file_operations js_fops =
-{
-	js_lseek,		/* js_lseek */
-	js_read,		/* js_read */
-	NULL,			/* js_write */
-	NULL,			/* js_readdir */
-	js_poll,		/* js_poll */
-	js_ioctl,		/* js_ioctl */
-	NULL,			/* js_mmap */
-	js_open,		/* js_open */
-	NULL,			/* js_flush */
-	js_release,		/* js_release */
-	NULL			/* js_sync */
-};
-
-/*
- * js_setup() parses kernel command line parametres.
- */
-
-#ifndef MODULE
-__initfunc(void js_setup(char *str, int *ints))
-
-{
-	js[0] = ((ints[0] > 0) ? ints[1] : 0 );
-	js[1] = ((ints[0] > 1) ? ints[2] : 0 );
-}
-#endif
-
-/*
- * js_init() registres the driver and calls the probe function.
- * also initializes some crucial variables.
- */
-
-#ifdef MODULE
-int init_module(void)
-#else
-__initfunc(int js_init(void))
-#endif
-{
-	int i;
-
-	if (check_region(JS_PORT, 1)) {
-		printk(KERN_ERR "js: port %#x already in use\n", JS_PORT);
-		return -EBUSY;
-	}
-
-	if (js_probe() < 0) {
-		printk(KERN_INFO "js: no joysticks found\n");
-		return -ENODEV;
-	}
-
-	if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) {
-		printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR);
-		return -EBUSY;
-	}
-
-	for (i = 0; i < JS_NUM; i++) {
-		if (jsd[i].exist) printk(KERN_INFO "js%d: %d-axis %d-button joystick at %#x\n",
-			i, count_bits(jsd[i].exist & JS_AXES), count_bits(jsd[i].exist & JS_BUTTONS), JS_PORT);
-		jsd[i].ahead = jsd[i].bhead = 0;
-		jsd[i].tail = JS_BUFF_SIZE - 1;
-		jsd[i].list = NULL;
-		jsd[i].wait = NULL;
-		memset(jsd[i].buff, 0, JS_BUFF_SIZE * sizeof(struct js_event));
-	}
-
-	for (i = 0; i < 4; i++) {
-		js_axis[i].corr.type = JS_CORR_NONE;
-		js_axis[i].corr.prec = JS_DEF_PREC;
-	}
-
-	request_region(JS_PORT, 1, "js");
-	init_bh(JS_BH, &js_do_bh);
-	enable_bh(JS_BH);
-	init_timer(&js_timer);
-	js_timer.function = js_do_timer;
-
-	return 0;
-}
-
-/*
- * cleanup_module() handles module removal.
- */
-
-#ifdef MODULE
-void cleanup_module(void)
-{
-	if (MOD_IN_USE)
-		printk(KERN_NOTICE "js: device busy, remove delayed\n");
-	else {
-		del_timer(&js_timer);
-		disable_bh(JS_BH);
-		if (unregister_chrdev(JOYSTICK_MAJOR, "js"))
-			printk(KERN_ERR "js: module cleanup failed\n");
-		release_region(JS_PORT, 1);
-	}
-}
-#endif
diff -u --recursive --new-file v2.1.124/linux/drivers/char/mac_SCC.c linux/drivers/char/mac_SCC.c
--- v2.1.124/linux/drivers/char/mac_SCC.c	Tue Jun 23 10:01:22 1998
+++ linux/drivers/char/mac_SCC.c	Wed Oct  7 09:26:37 1998
@@ -67,6 +67,7 @@
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <asm/bitops.h>
+#include <asm/hwtest.h>
 
 #include "mac_SCC.h"
 
@@ -1300,6 +1301,12 @@
 	/* testing: fix up broken 24 bit addresses (ClassicII) */
 	if ((mac_bi_data.sccbase & 0x00FFFFFF) == mac_bi_data.sccbase)
 		mac_bi_data.sccbase |= 0x50000000;
+		
+	if ( !hwreg_present((void *)mac_bi_data.sccbase))
+	{
+		printk(KERN_WARNING "z8530: Serial devices not accessible. Check serial switch.\n");
+		return;
+	}
 
 	for(n=0;n<2;n++)
 	{
@@ -1459,11 +1466,7 @@
 	printk("Mac68K Z8530 serial driver version 1.01\n");
 
 	/* SCC present at all? */
-	if (MACH_IS_ATARI || MACH_IS_AMIGA 
-#if 0
-	    || !(MACHW_PRESENT(SCC) || MACHW_PRESENT(ST_ESCC))
-#endif
-	   )
+	if (!MACH_IS_MAC)
 		return( -ENODEV );
 
 	if (zs_chain == 0)
diff -u --recursive --new-file v2.1.124/linux/drivers/char/radio-sf16fmi.c linux/drivers/char/radio-sf16fmi.c
--- v2.1.124/linux/drivers/char/radio-sf16fmi.c	Wed Aug 26 11:37:37 1998
+++ linux/drivers/char/radio-sf16fmi.c	Tue Oct  6 09:39:49 1998
@@ -26,7 +26,7 @@
 {
 	int port;
         int curvol; /* 1 or 0 */
-        unsigned long curfreq; /* RSF16_PREC * freq in MHz */
+        unsigned long curfreq; /* freq in kHz */
         __u32 flags;
 };
 
diff -u --recursive --new-file v2.1.124/linux/drivers/char/random.c linux/drivers/char/random.c
--- v2.1.124/linux/drivers/char/random.c	Wed Aug 26 11:37:37 1998
+++ linux/drivers/char/random.c	Mon Oct  5 10:04:34 1998
@@ -1250,7 +1250,7 @@
 		}
 #if HASH_BUFFER_SIZE & 1	/* There's a middle word to deal with */
 		x = tmp[HASH_BUFFER_SIZE/2];
-		add_entropy_words(r, x, (__u32)buf);
+		add_entropy_words(r, x, (__u32)((unsigned long)buf));
 		x ^= (x >> 16);		/* Fold it in half */
 		((__u16 *)tmp)[HASH_BUFFER_SIZE-1] = (__u16)x;
 #endif
diff -u --recursive --new-file v2.1.124/linux/drivers/char/saa5249.c linux/drivers/char/saa5249.c
--- v2.1.124/linux/drivers/char/saa5249.c	Sat Sep  5 16:46:40 1998
+++ linux/drivers/char/saa5249.c	Tue Oct  6 09:39:49 1998
@@ -149,7 +149,6 @@
 	if(device->bus->id!=I2C_BUSID_BT848)
 		return -EINVAL;
 	
-	printk(KERN_DEBUG "saa5249_attach: bus %p\n", device->bus);
 	strcpy(device->name, IF_NAME);
 	
 	/*
@@ -205,7 +204,6 @@
 static int saa5249_detach(struct i2c_device *device)
 {
 	struct video_device *vd=device->data;
-	printk(KERN_DEBUG "saa5249_detach\n");
 	video_unregister_device(vd);
 	kfree(vd->priv);
 	kfree(vd);
@@ -215,7 +213,6 @@
 static int saa5249_command(struct i2c_device *device,
 			     unsigned int cmd, void *arg)
 {
-	printk(KERN_DEBUG "saa5249_command\n");
 	return -EINVAL;
 }
 
@@ -621,11 +618,9 @@
 	struct saa5249_device *t=vd->priv;
 	int pgbuf;
 
-	printk("t=%p\n",t);
 	if (t->bus==NULL) 
 		return -ENODEV;
 
-	printk("Do i2c %p\n",t->bus);
 	if (i2c_senddata(t, CCTWR, 0, 0, -1) ||		/* Select R11 */
 						/* Turn off parity checks (we do this ourselves) */
 		i2c_senddata(t, CCTWR, 1, disp_modes[t->disp_mode][0], 0, -1) ||
@@ -636,7 +631,6 @@
 		return -EIO;
 	}
 
-	printk("clean\n");
 	for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) 
 	{
 		memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
@@ -648,7 +642,6 @@
 		t->is_searching[pgbuf] = FALSE;
 	}
 	t->virtual_mode=FALSE;
-	printk("Go\n");
 	MOD_INC_USE_COUNT;
 	return 0;
 }
diff -u --recursive --new-file v2.1.124/linux/drivers/char/serial167.c linux/drivers/char/serial167.c
--- v2.1.124/linux/drivers/char/serial167.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/serial167.c	Mon Oct  5 14:12:12 1998
@@ -0,0 +1,2984 @@
+/*
+ * linux/drivers/char/serial167.c
+ *
+ * Driver for MVME166/7 board serial ports, which are via a CD2401.
+ * Based very much on cyclades.c.
+ *
+ * MVME166/7 work by Richard Hirst [richard@sleepie.demon.co.uk]
+ *
+ * ==============================================================
+ *
+ * static char rcsid[] =
+ * "$Revision: 1.36.1.4 $$Date: 1995/03/29 06:14:14 $";
+ *
+ *  linux/kernel/cyclades.c
+ *
+ * Maintained by Marcio Saito (cyclades@netcom.com) and
+ * Randolph Bentson (bentson@grieg.seaslug.org)
+ *
+ * Much of the design and some of the code came from serial.c
+ * which was copyright (C) 1991, 1992  Linus Torvalds.  It was
+ * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92,
+ * and then fixed as suggested by Michael K. Johnson 12/12/92.
+ *
+ * This version does not support shared irq's.
+ *
+ * This module exports the following rs232 io functions:
+ *   int cy_init(void);
+ *   int  cy_open(struct tty_struct *tty, struct file *filp);
+ *
+ * $Log: cyclades.c,v $
+ * Revision 1.36.1.4  1995/03/29  06:14:14  bentson
+ * disambiguate between Cyclom-16Y and Cyclom-32Ye;
+ *
+ * 200 lines of changes record removed - RGH 11-10-95, starting work on
+ * converting this to drive serial ports on mvme166 (cd2401).
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/interrupt.h>
+#include <linux/serial.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/serial167.h>
+#include <linux/delay.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/mvme16xhw.h>
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/version.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+
+#define cy_put_user	put_user
+
+static unsigned long cy_get_user(unsigned long *addr)
+{
+	unsigned long result = 0;
+	int error = get_user (result, addr);
+	if (error)
+		printk ("serial167: cy_get_user: error == %d\n", error);
+	return result;
+}
+
+#define SERIAL_PARANOIA_CHECK
+#undef  SERIAL_DEBUG_OPEN
+#undef  SERIAL_DEBUG_THROTTLE
+#undef  SERIAL_DEBUG_OTHER
+#undef  SERIAL_DEBUG_IO
+#undef  SERIAL_DEBUG_COUNT
+#undef  SERIAL_DEBUG_DTR
+#undef  CYCLOM_16Y_HACK
+#define  CYCLOM_ENABLE_MONITORING
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+#define WAKEUP_CHARS 256
+
+#define STD_COM_FLAGS (0)
+
+#define SERIAL_TYPE_NORMAL  1
+#define SERIAL_TYPE_CALLOUT 2
+
+
+DECLARE_TASK_QUEUE(tq_cyclades);
+
+struct tty_driver cy_serial_driver, cy_callout_driver;
+extern int serial_console;
+static struct cyclades_port *serial_console_info = NULL;
+static unsigned int serial_console_cflag = 0;
+u_char initial_console_speed;
+
+/* Base address of cd2401 chip on mvme166/7 */
+
+#define BASE_ADDR (0xfff45000)
+#define pcc2chip	((volatile u_char *)0xfff42000)
+#define PccSCCMICR	0x1d
+#define PccSCCTICR	0x1e
+#define PccSCCRICR	0x1f
+#define PccTPIACKR	0x25
+#define PccRPIACKR	0x27
+#define PccIMLR		0x3f
+
+/* This is the per-port data structure */
+struct cyclades_port cy_port[] = {
+      /* CARD#  */
+        {-1 },      /* ttyS0 */
+        {-1 },      /* ttyS1 */
+        {-1 },      /* ttyS2 */
+        {-1 },      /* ttyS3 */
+};
+#define NR_PORTS        (sizeof(cy_port)/sizeof(struct cyclades_port))
+
+static int serial_refcount;
+
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+
+/*
+ * 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;
+
+/*
+ * This is used to look up the divisor speeds and the timeouts
+ * We're normally limited to 15 distinct baud rates.  The extra
+ * are accessed via settings in info->flags.
+ *         0,     1,     2,     3,     4,     5,     6,     7,     8,     9,
+ *        10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
+ *                                                  HI            VHI
+ */
+static int baud_table[] = {
+           0,    50,    75,   110,   134,   150,   200,   300,   600,  1200,
+        1800,  2400,  4800,  9600, 19200, 38400, 57600, 76800,115200,150000,
+        0};
+
+#if 0
+static char baud_co[] = {  /* 25 MHz clock option table */
+        /* value =>    00    01   02    03    04 */
+        /* divide by    8    32   128   512  2048 */
+        0x00,  0x04,  0x04,  0x04,  0x04,  0x04,  0x03,  0x03,  0x03,  0x02,
+        0x02,  0x02,  0x01,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00};
+
+static char baud_bpr[] = {  /* 25 MHz baud rate period table */
+        0x00,  0xf5,  0xa3,  0x6f,  0x5c,  0x51,  0xf5,  0xa3,  0x51,  0xa3,
+        0x6d,  0x51,  0xa3,  0x51,  0xa3,  0x51,  0x36,  0x29,  0x1b,  0x15};
+#endif
+
+/* I think 166 brd clocks 2401 at 20MHz.... */
+
+/* These values are written directly to tcor, and >> 5 for writing to rcor */
+static u_char baud_co[] = {  /* 20 MHz clock option table */
+        0x00,  0x80,  0x80,  0x80,  0x80,  0x80,  0x80,  0x60,  0x60,  0x40,
+        0x40,  0x40,  0x20,  0x20,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00};
+
+/* These values written directly to tbpr/rbpr */
+static u_char baud_bpr[] = {  /* 20 MHz baud rate period table */
+        0x00,  0xc0,  0x80,  0x58,  0x6c,  0x40,  0xc0,  0x81,  0x40,  0x81,
+        0x57,  0x40,  0x81,  0x40,  0x81,  0x40,  0x2b,  0x20,  0x15,  0x10};
+
+static u_char baud_cor4[] = {  /* receive threshold */
+        0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,
+        0x0a,  0x0a,  0x0a,  0x09,  0x09,  0x08,  0x08,  0x08,  0x08,  0x07};
+
+
+
+static void shutdown(struct cyclades_port *);
+static int startup (struct cyclades_port *);
+static void cy_throttle(struct tty_struct *);
+static void cy_unthrottle(struct tty_struct *);
+static void config_setup(struct cyclades_port *);
+extern void console_print(const char *);
+#ifdef CYCLOM_SHOW_STATUS
+static void show_status(int);
+#endif
+
+#ifdef CONFIG_REMOTE_DEBUG
+static void debug_setup(void);
+void queueDebugChar (int c);
+int getDebugChar(void);
+
+#define DEBUG_PORT	1
+#define DEBUG_LEN	256
+
+typedef struct {
+	int	in;
+	int	out;
+	unsigned char	buf[DEBUG_LEN];
+} debugq;
+
+debugq debugiq;
+#endif
+
+/*
+ * I have my own version of udelay(), as it is needed when initialising
+ * the chip, before the delay loop has been calibrated.  Should probably
+ * reference one of the vmechip2 or pccchip2 counter for an accurate
+ * delay, but this wild guess will do for now.
+ */
+
+void my_udelay (long us)
+{
+	u_char x;
+	volatile u_char *p = &x;
+	int i;
+
+	while (us--)
+		for (i = 100; i; i--)
+			x |= *p;
+}
+
+static inline int
+serial_paranoia_check(struct cyclades_port *info,
+			dev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+    static const char *badmagic =
+	"Warning: bad magic number for serial struct (%d, %d) in %s\n";
+    static const char *badinfo =
+	"Warning: null cyclades_port for (%d, %d) in %s\n";
+    static const char *badrange =
+	"Warning: cyclades_port out of range for (%d, %d) in %s\n";
+
+    if (!info) {
+	printk(badinfo, MAJOR(device), MINOR(device), routine);
+	return 1;
+    }
+
+    if( (long)info < (long)(&cy_port[0])
+    || (long)(&cy_port[NR_PORTS]) < (long)info ){
+	printk(badrange, MAJOR(device), MINOR(device), routine);
+	return 1;
+    }
+
+    if (info->magic != CYCLADES_MAGIC) {
+	printk(badmagic, MAJOR(device), MINOR(device), routine);
+	return 1;
+    }
+#endif
+	return 0;
+} /* serial_paranoia_check */
+
+#if 0
+/* The following diagnostic routines allow the driver to spew
+   information on the screen, even (especially!) during interrupts.
+ */
+void
+SP(char *data){
+  unsigned long flags;
+    save_flags(flags); cli();
+        console_print(data);
+    restore_flags(flags);
+}
+char scrn[2];
+void
+CP(char data){
+  unsigned long flags;
+    save_flags(flags); cli();
+        scrn[0] = data;
+        console_print(scrn);
+    restore_flags(flags);
+}/* CP */
+
+void CP1(int data) { (data<10)?  CP(data+'0'): CP(data+'A'-10); }/* CP1 */
+void CP2(int data) { CP1((data>>4) & 0x0f); CP1( data & 0x0f); }/* CP2 */
+void CP4(int data) { CP2((data>>8) & 0xff); CP2(data & 0xff); }/* CP4 */
+void CP8(long data) { CP4((data>>16) & 0xffff); CP4(data & 0xffff); }/* CP8 */
+#endif
+
+/* This routine waits up to 1000 micro-seconds for the previous
+   command to the Cirrus chip to complete and then issues the
+   new command.  An error is returned if the previous command
+   didn't finish within the time limit.
+ */
+u_short
+write_cy_cmd(volatile u_char *base_addr, u_char cmd)
+{
+  unsigned long flags;
+  volatile int  i;
+
+    save_flags(flags); cli();
+	/* Check to see that the previous command has completed */
+	for(i = 0 ; i < 100 ; i++){
+	    if (base_addr[CyCCR] == 0){
+		break;
+	    }
+	    my_udelay(10L);
+	}
+	/* if the CCR never cleared, the previous command
+	    didn't finish within the "reasonable time" */
+	if ( i == 10 ) {
+	    restore_flags(flags);
+	    return (-1);
+	}
+
+	/* Issue the new command */
+	base_addr[CyCCR] = cmd;
+    restore_flags(flags);
+    return(0);
+} /* write_cy_cmd */
+
+
+/* cy_start and cy_stop provide software output flow control as a
+   function of XON/XOFF, software CTS, and other such stuff. */
+
+static void
+cy_stop(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
+  int channel;
+  unsigned long flags;
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_stop ttyS%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_stop"))
+	return;
+	
+    channel = info->line;
+
+    save_flags(flags); cli();
+        base_addr[CyCAR] = (u_char)(channel); /* index channel */
+        base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy);
+    restore_flags(flags);
+
+    return;
+} /* cy_stop */
+
+static void
+cy_start(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
+  int channel;
+  unsigned long flags;
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_start ttyS%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_start"))
+	return;
+	
+    channel = info->line;
+
+    save_flags(flags); cli();
+        base_addr[CyCAR] = (u_char)(channel);
+        base_addr[CyIER] |= CyTxMpty;
+    restore_flags(flags);
+
+    return;
+} /* cy_start */
+
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver
+ * (also known as the "bottom half").  This can be called any
+ * number of times for any channel without harm.
+ */
+static inline void
+cy_sched_event(struct cyclades_port *info, int event)
+{
+    info->event |= 1 << event; /* remember what kind of event and who */
+    queue_task(&info->tqueue, &tq_cyclades); /* it belongs to */
+    mark_bh(CYCLADES_BH);                       /* then trigger event */
+} /* cy_sched_event */
+
+
+/* The real interrupt service routines are called
+   whenever the card wants its hand held--chars
+   received, out buffer empty, modem change, etc.
+ */
+static void
+cd2401_rxerr_interrupt(int irq, void *dev_id, struct pt_regs *fp)
+{
+    struct tty_struct *tty;
+    struct cyclades_port *info;
+    volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
+    unsigned char err, rfoc;
+    int channel;
+    char data;
+
+    /* determine the channel and change to that context */
+    channel = (u_short ) (base_addr[CyLICR] >> 2);
+    info = &cy_port[channel];
+    info->last_active = jiffies;
+
+    if ((err = base_addr[CyRISR]) & CyTIMEOUT) {
+	/* This is a receive timeout interrupt, ignore it */
+	base_addr[CyREOIR] = CyNOTRANS;
+	return;
+    }
+
+    /* Read a byte of data if there is any - assume the error
+     * is associated with this character */
+
+    if ((rfoc = base_addr[CyRFOC]) != 0)
+	data = base_addr[CyRDR];
+    else
+	data = 0;
+
+    /* if there is nowhere to put the data, discard it */
+    if(info->tty == 0) {
+	base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;
+	return;
+    }
+    else { /* there is an open port for this data */
+	tty = info->tty;
+	if(err & info->ignore_status_mask){
+	    base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;
+	    return;
+	}
+	if (tty->flip.count < TTY_FLIPBUF_SIZE){
+	    tty->flip.count++;
+	    if (err & info->read_status_mask){
+		if(err & CyBREAK){
+		    *tty->flip.flag_buf_ptr++ = TTY_BREAK;
+		    *tty->flip.char_buf_ptr++ = data;
+		    if (info->flags & ASYNC_SAK){
+			do_SAK(tty);
+		    }
+		}else if(err & CyFRAME){
+		    *tty->flip.flag_buf_ptr++ = TTY_FRAME;
+		    *tty->flip.char_buf_ptr++ = data;
+		}else if(err & CyPARITY){
+		    *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+		    *tty->flip.char_buf_ptr++ = data;
+		}else if(err & CyOVERRUN){
+		    *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+		    *tty->flip.char_buf_ptr++ = 0;
+		    /*
+		       If the flip buffer itself is
+		       overflowing, we still loose
+		       the next incoming character.
+		     */
+		    if(tty->flip.count < TTY_FLIPBUF_SIZE){
+			tty->flip.count++;
+			*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+			*tty->flip.char_buf_ptr++ = data;
+		    }
+		/* These two conditions may imply */
+		/* a normal read should be done. */
+		/* else if(data & CyTIMEOUT) */
+		/* else if(data & CySPECHAR) */
+		}else{
+		    *tty->flip.flag_buf_ptr++ = 0;
+		    *tty->flip.char_buf_ptr++ = 0;
+		}
+	    }else{
+		*tty->flip.flag_buf_ptr++ = 0;
+		*tty->flip.char_buf_ptr++ = 0;
+	    }
+	}else{
+	    /* there was a software buffer overrun
+	       and nothing could be done about it!!! */
+	}
+    }
+    queue_task(&tty->flip.tqueue, &tq_timer);
+    /* end of service */
+    base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;
+} /* cy_rxerr_interrupt */
+
+static void
+cd2401_modem_interrupt(int irq, void *dev_id, struct pt_regs *fp)
+{
+    struct cyclades_port *info;
+    volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
+    int channel;
+    int mdm_change;
+    int mdm_status;
+
+
+    /* determine the channel and change to that context */
+    channel = (u_short ) (base_addr[CyLICR] >> 2);
+    info = &cy_port[channel];
+    info->last_active = jiffies;
+
+    mdm_change = base_addr[CyMISR];
+    mdm_status = base_addr[CyMSVR1];
+
+    if(info->tty == 0){ /* nowhere to put the data, ignore it */
+	;
+    }else{
+	if((mdm_change & CyDCD)
+	&& (info->flags & ASYNC_CHECK_CD)){
+	    if(mdm_status & CyDCD){
+/* CP('!'); */
+		cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP);
+	    }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE)
+		     &&(info->flags & ASYNC_CALLOUT_NOHUP))){
+/* CP('@'); */
+		cy_sched_event(info, Cy_EVENT_HANGUP);
+	    }
+	}
+	if((mdm_change & CyCTS)
+	&& (info->flags & ASYNC_CTS_FLOW)){
+	    if(info->tty->stopped){
+		if(mdm_status & CyCTS){
+		    /* !!! cy_start isn't used because... */
+		    info->tty->stopped = 0;
+        	    base_addr[CyIER] |= CyTxMpty;
+		    cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
+		}
+	    }else{
+		if(!(mdm_status & CyCTS)){
+		    /* !!! cy_stop isn't used because... */
+		    info->tty->stopped = 1;
+        	    base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy);
+		}
+	    }
+	}
+	if(mdm_status & CyDSR){
+	}
+    }
+    base_addr[CyMEOIR] = 0;
+} /* cy_modem_interrupt */
+
+static void
+cd2401_tx_interrupt(int irq, void *dev_id, struct pt_regs *fp)
+{
+    struct cyclades_port *info;
+    volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
+    int channel;
+    int char_count, saved_cnt;
+    int outch;
+
+    /* determine the channel and change to that context */
+    channel = (u_short ) (base_addr[CyLICR] >> 2);
+
+#ifdef CONFIG_REMOTE_DEBUG
+    if (channel == DEBUG_PORT) {
+	panic ("TxInt on debug port!!!");
+    }
+#endif
+
+    info = &cy_port[channel];
+
+    /* validate the port number (as configured and open) */
+    if( (channel < 0) || (NR_PORTS <= channel) ){
+	base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy);
+	base_addr[CyTEOIR] = CyNOTRANS;
+	return;
+    }
+    info->last_active = jiffies;
+    if(info->tty == 0){
+	base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy);
+        if (info->xmit_cnt < WAKEUP_CHARS) {
+	    cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
+        }
+	base_addr[CyTEOIR] = CyNOTRANS;
+	return;
+    }
+
+    /* load the on-chip space available for outbound data */
+    saved_cnt = char_count = base_addr[CyTFTC];
+
+    if(info->x_char) { /* send special char */
+	outch = info->x_char;
+	base_addr[CyTDR] = outch;
+	char_count--;
+	info->x_char = 0;
+    }
+
+    if (info->x_break){
+	/*  The Cirrus chip requires the "Embedded Transmit
+	    Commands" of start break, delay, and end break
+	    sequences to be sent.  The duration of the
+	    break is given in TICs, which runs at HZ
+	    (typically 100) and the PPR runs at 200 Hz,
+	    so the delay is duration * 200/HZ, and thus a
+	    break can run from 1/100 sec to about 5/4 sec.
+	    Need to check these values - RGH 141095.
+	 */
+	base_addr[CyTDR] = 0; /* start break */
+	base_addr[CyTDR] = 0x81;
+	base_addr[CyTDR] = 0; /* delay a bit */
+	base_addr[CyTDR] = 0x82;
+	base_addr[CyTDR] = info->x_break*200/HZ;
+	base_addr[CyTDR] = 0; /* terminate break */
+	base_addr[CyTDR] = 0x83;
+	char_count -= 7;
+	info->x_break = 0;
+    }
+
+    while (char_count > 0){
+	if (!info->xmit_cnt){
+	    base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy);
+	    break;
+	}
+	if (info->xmit_buf == 0){
+	    base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy);
+	    break;
+	}
+	if (info->tty->stopped || info->tty->hw_stopped){
+	    base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy);
+	    break;
+	}
+	/* Because the Embedded Transmit Commands have been
+	   enabled, we must check to see if the escape
+	   character, NULL, is being sent.  If it is, we
+	   must ensure that there is room for it to be
+	   doubled in the output stream.  Therefore we
+	   no longer advance the pointer when the character
+	   is fetched, but rather wait until after the check
+	   for a NULL output character. (This is necessary
+	   because there may not be room for the two chars
+	   needed to send a NULL.
+	 */
+	outch = info->xmit_buf[info->xmit_tail];
+	if( outch ){
+	    info->xmit_cnt--;
+	    info->xmit_tail = (info->xmit_tail + 1)
+				      & (PAGE_SIZE - 1);
+	    base_addr[CyTDR] = outch;
+	    char_count--;
+	}else{
+	    if(char_count > 1){
+		info->xmit_cnt--;
+		info->xmit_tail = (info->xmit_tail + 1)
+					  & (PAGE_SIZE - 1);
+		base_addr[CyTDR] = outch;
+		base_addr[CyTDR] = 0;
+		char_count--;
+		char_count--;
+	    }else{
+		break;
+	    }
+	}
+    }
+
+    if (info->xmit_cnt < WAKEUP_CHARS) {
+	cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
+    }
+    base_addr[CyTEOIR] = (char_count != saved_cnt) ? 0 : CyNOTRANS;
+} /* cy_tx_interrupt */
+
+static void
+cd2401_rx_interrupt(int irq, void *dev_id, struct pt_regs *fp)
+{
+    struct tty_struct *tty;
+    struct cyclades_port *info;
+    volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
+    int channel;
+    char data;
+    int char_count;
+    int save_cnt;
+
+    /* determine the channel and change to that context */
+    channel = (u_short ) (base_addr[CyLICR] >> 2);
+    info = &cy_port[channel];
+    info->last_active = jiffies;
+    save_cnt = char_count = base_addr[CyRFOC];
+
+#ifdef CONFIG_REMOTE_DEBUG
+    if (channel == DEBUG_PORT) {
+	while (char_count--) {
+            data = base_addr[CyRDR];
+	    queueDebugChar(data);
+	}
+    }
+    else
+#endif
+    /* if there is nowhere to put the data, discard it */
+    if(info->tty == 0){
+	while(char_count--){
+	    data = base_addr[CyRDR];
+	}
+    }else{ /* there is an open port for this data */
+	tty = info->tty;
+	/* load # characters available from the chip */
+
+#ifdef CYCLOM_ENABLE_MONITORING
+	++info->mon.int_count;
+	info->mon.char_count += char_count;
+	if (char_count > info->mon.char_max)
+	    info->mon.char_max = char_count;
+	info->mon.char_last = char_count;
+#endif
+	while(char_count--){
+	    data = base_addr[CyRDR];
+	    if (tty->flip.count >= TTY_FLIPBUF_SIZE){
+		continue;
+	    }
+	    tty->flip.count++;
+	    *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+	    *tty->flip.char_buf_ptr++ = data;
+#ifdef CYCLOM_16Y_HACK
+	    udelay(10L);
+#endif
+        }
+	queue_task(&tty->flip.tqueue, &tq_timer);
+    }
+    /* end of service */
+    base_addr[CyREOIR] = save_cnt ? 0 : CyNOTRANS;
+} /* cy_rx_interrupt */
+
+/*
+ * 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
+ * cy_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 cy_sched_event(), and they get done here.
+ *
+ * This is done through one level of indirection--the task queue.
+ * When a hardware interrupt service routine wants service by the
+ * driver's bottom half, it enqueues the appropriate tq_struct (one
+ * per port) to the tq_cyclades work queue and sets a request flag
+ * via mark_bh for processing that queue.  When the time is right,
+ * do_cyclades_bh is called (because of the mark_bh) and it requests
+ * that the work queue be processed.
+ *
+ * Although this may seem unwieldy, it gives the system a way to
+ * pass an argument (in this case the pointer to the cyclades_port
+ * structure) to the bottom half of the driver.  Previous kernels
+ * had to poll every port to see if that port needed servicing.
+ */
+static void
+do_cyclades_bh(void)
+{
+    run_task_queue(&tq_cyclades);
+} /* do_cyclades_bh */
+
+static void
+do_softint(void *private_)
+{
+  struct cyclades_port *info = (struct cyclades_port *) private_;
+  struct tty_struct    *tty;
+
+    tty = info->tty;
+    if (!tty)
+	return;
+
+    if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) {
+	tty_hangup(info->tty);
+	wake_up_interruptible(&info->open_wait);
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|
+			     ASYNC_CALLOUT_ACTIVE);
+    }
+    if (test_and_clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) {
+	wake_up_interruptible(&info->open_wait);
+    }
+    if (test_and_clear_bit(Cy_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);
+    }
+} /* do_softint */
+
+
+/* This is called whenever a port becomes active;
+   interrupts are enabled and DTR & RTS are turned on.
+ */
+static int
+startup(struct cyclades_port * info)
+{
+  unsigned long flags;
+  volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
+  int channel;
+
+    if (info->flags & ASYNC_INITIALIZED){
+	return 0;
+    }
+
+    if (!info->type){
+	if (info->tty){
+	    set_bit(TTY_IO_ERROR, &info->tty->flags);
+	}
+	return 0;
+    }
+    if (!info->xmit_buf){
+	info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL);
+	if (!info->xmit_buf){
+	    return -ENOMEM;
+	}
+    }
+
+    config_setup(info);
+
+    channel = info->line;
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk("startup channel %d\n", channel);
+#endif
+
+    save_flags(flags); cli();
+	base_addr[CyCAR] = (u_char)channel;
+	write_cy_cmd(base_addr,CyENB_RCVR|CyENB_XMTR);
+
+	base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */
+	base_addr[CyMSVR1] = CyRTS;
+/* CP('S');CP('1'); */
+	base_addr[CyMSVR2] = CyDTR;
+
+#ifdef SERIAL_DEBUG_DTR
+        printk("cyc: %d: raising DTR\n", __LINE__);
+        printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+
+	base_addr[CyIER] |= CyRxData;
+	info->flags |= ASYNC_INITIALIZED;
+
+	if (info->tty){
+	    clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	}
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+    restore_flags(flags);
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk(" done\n");
+#endif
+    return 0;
+} /* startup */
+
+void
+start_xmit( struct cyclades_port *info )
+{
+  unsigned long flags;
+  volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+  int channel;
+
+    channel = info->line;
+    save_flags(flags); cli();
+	base_addr[CyCAR] = channel;
+	base_addr[CyIER] |= CyTxMpty;
+    restore_flags(flags);
+} /* start_xmit */
+
+/*
+ * This routine shuts down a serial port; interrupts are disabled,
+ * and DTR is dropped if the hangup on close termio flag is on.
+ */
+static void
+shutdown(struct cyclades_port * info)
+{
+  unsigned long flags;
+  volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+  int channel;
+
+    if (!(info->flags & ASYNC_INITIALIZED)){
+/* CP('$'); */
+	return;
+    }
+
+    channel = info->line;
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk("shutdown channel %d\n", channel);
+#endif
+
+    /* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE
+       SENT BEFORE DROPPING THE LINE !!!  (Perhaps
+       set some flag that is read when XMTY happens.)
+       Other choices are to delay some fixed interval
+       or schedule some later processing.
+     */
+    save_flags(flags); cli();
+	if (info->xmit_buf){
+	    free_page((unsigned long) info->xmit_buf);
+	    info->xmit_buf = 0;
+	}
+
+	base_addr[CyCAR] = (u_char)channel;
+	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+	    base_addr[CyMSVR1] = 0;
+/* CP('C');CP('1'); */
+	    base_addr[CyMSVR2] = 0;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: dropping DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+        }
+	write_cy_cmd(base_addr,CyDIS_RCVR);
+         /* it may be appropriate to clear _XMIT at
+           some later date (after testing)!!! */
+
+	if (info->tty){
+	    set_bit(TTY_IO_ERROR, &info->tty->flags);
+	}
+	info->flags &= ~ASYNC_INITIALIZED;
+    restore_flags(flags);
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk(" done\n");
+#endif
+    return;
+} /* shutdown */
+
+/*
+ * This routine finds or computes the various line characteristics.
+ */
+static void
+config_setup(struct cyclades_port * info)
+{
+  unsigned long flags;
+  volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+  int channel;
+  unsigned cflag;
+  int   i;
+  unsigned char ti, need_init_chan = 0;
+
+    if (!info->tty || !info->tty->termios){
+        return;
+    }
+    if (info->line == -1){
+        return;
+    }
+    cflag = info->tty->termios->c_cflag;
+
+    /* baud rate */
+    i = cflag & CBAUD;
+#ifdef CBAUDEX
+/* Starting with kernel 1.1.65, there is direct support for
+   higher baud rates.  The following code supports those
+   changes.  The conditional aspect allows this driver to be
+   used for earlier as well as later kernel versions.  (The
+   mapping is slightly different from serial.c because there
+   is still the possibility of supporting 75 kbit/sec with
+   the Cyclades board.)
+ */
+    if (i & CBAUDEX) {
+	if (i == B57600)
+	    i = 16;
+	else if(i == B115200) 
+	    i = 18;
+#ifdef B78600
+	else if(i == B78600) 
+	    i = 17;
+#endif
+	else
+	    info->tty->termios->c_cflag &= ~CBAUDEX;
+    }
+#endif
+    if (i == 15) {
+	    if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+		    i += 1;
+	    if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+		    i += 3;
+    }
+    /* Don't ever change the speed of the console port.  It will
+     * run at the speed specified in bootinfo, or at 19.2K */
+    /* Actually, it should run at whatever speed 166Bug was using */
+    /* Note info->timeout isn't used at present */
+    if (info != serial_console_info) {
+	info->tbpr = baud_bpr[i]; /* Tx BPR */
+	info->tco = baud_co[i]; /* Tx CO */
+	info->rbpr = baud_bpr[i]; /* Rx BPR */
+	info->rco = baud_co[i] >> 5; /* Rx CO */
+	if (baud_table[i] == 134) {
+            info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
+            /* get it right for 134.5 baud */
+	} else if (baud_table[i]) {
+            info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
+        /* this needs to be propagated into the card info */
+	} else {
+            info->timeout = 0;
+	}
+    }
+    /* By tradition (is it a standard?) a baud rate of zero
+       implies the line should be/has been closed.  A bit
+       later in this routine such a test is performed. */
+
+    /* byte size and parity */
+    info->cor7 = 0;
+    info->cor6 = 0;
+    info->cor5 = 0;
+    info->cor4 = (info->default_threshold
+		  ? info->default_threshold
+		  : baud_cor4[i]); /* receive threshold */
+    /* Following two lines added 101295, RGH. */
+    /* It is obviously wrong to access CyCORx, and not info->corx here,
+     * try and remember to fix it later! */
+    channel = info->line;
+    base_addr[CyCAR] = (u_char)channel;
+    if (C_CLOCAL(info->tty)) {
+	if (base_addr[CyIER] & CyMdmCh)
+	    base_addr[CyIER] &= ~CyMdmCh; /* without modem intr */
+			       /* ignore 1->0 modem transitions */
+	if (base_addr[CyCOR4] & (CyDSR|CyCTS|CyDCD))
+	    base_addr[CyCOR4] &= ~(CyDSR|CyCTS|CyDCD);
+			       /* ignore 0->1 modem transitions */
+	if (base_addr[CyCOR5] & (CyDSR|CyCTS|CyDCD))
+	    base_addr[CyCOR5] &= ~(CyDSR|CyCTS|CyDCD);
+    } else {
+	if ((base_addr[CyIER] & CyMdmCh) != CyMdmCh)
+	    base_addr[CyIER] |= CyMdmCh; /* with modem intr */
+			       /* act on 1->0 modem transitions */
+	if ((base_addr[CyCOR4] & (CyDSR|CyCTS|CyDCD)) != (CyDSR|CyCTS|CyDCD))
+	    base_addr[CyCOR4] |= CyDSR|CyCTS|CyDCD;
+			       /* act on 0->1 modem transitions */
+	if ((base_addr[CyCOR5] & (CyDSR|CyCTS|CyDCD)) != (CyDSR|CyCTS|CyDCD))
+	    base_addr[CyCOR5] |= CyDSR|CyCTS|CyDCD;
+    }
+    info->cor3 = (cflag & CSTOPB) ? Cy_2_STOP : Cy_1_STOP;
+    info->cor2 = CyETC;
+    switch(cflag & CSIZE){
+    case CS5:
+        info->cor1 = Cy_5_BITS;
+        break;
+    case CS6:
+        info->cor1 = Cy_6_BITS;
+        break;
+    case CS7:
+        info->cor1 = Cy_7_BITS;
+        break;
+    case CS8:
+        info->cor1 = Cy_8_BITS;
+        break;
+    }
+    if (cflag & PARENB){
+        if (cflag & PARODD){
+            info->cor1 |= CyPARITY_O;
+        }else{
+            info->cor1 |= CyPARITY_E;
+        }
+    }else{
+        info->cor1 |= CyPARITY_NONE;
+    }
+	
+    /* CTS flow control flag */
+#if 0
+    /* Don't complcate matters for now! RGH 141095 */
+    if (cflag & CRTSCTS){
+	info->flags |= ASYNC_CTS_FLOW;
+	info->cor2 |= CyCtsAE;
+    }else{
+	info->flags &= ~ASYNC_CTS_FLOW;
+	info->cor2 &= ~CyCtsAE;
+    }
+#endif
+    if (cflag & CLOCAL)
+	info->flags &= ~ASYNC_CHECK_CD;
+    else
+	info->flags |= ASYNC_CHECK_CD;
+
+     /***********************************************
+	The hardware option, CyRtsAO, presents RTS when
+	the chip has characters to send.  Since most modems
+	use RTS as reverse (inbound) flow control, this
+	option is not used.  If inbound flow control is
+	necessary, DTR can be programmed to provide the
+	appropriate signals for use with a non-standard
+	cable.  Contact Marcio Saito for details.
+     ***********************************************/
+
+    channel = info->line;
+
+    save_flags(flags); cli();
+	base_addr[CyCAR] = (u_char)channel;
+
+	/* CyCMR set once only in mvme167_init_serial() */
+	if (base_addr[CyLICR] != channel << 2)
+	    base_addr[CyLICR] = channel << 2;
+	if (base_addr[CyLIVR] != 0x5c)
+	    base_addr[CyLIVR] = 0x5c;
+
+       /* tx and rx baud rate */
+
+	if (base_addr[CyCOR1] != info->cor1)
+	    need_init_chan = 1;
+	if (base_addr[CyTCOR] != info->tco)
+	    base_addr[CyTCOR] = info->tco;
+	if (base_addr[CyTBPR] != info->tbpr)
+	    base_addr[CyTBPR] = info->tbpr;
+	if (base_addr[CyRCOR] != info->rco)
+	    base_addr[CyRCOR] = info->rco;
+	if (base_addr[CyRBPR] != info->rbpr)
+	    base_addr[CyRBPR] = info->rbpr;
+
+	/* set line characteristics  according configuration */
+
+	if (base_addr[CySCHR1] != START_CHAR(info->tty))
+	    base_addr[CySCHR1] = START_CHAR(info->tty);
+	if (base_addr[CySCHR2] != STOP_CHAR(info->tty))
+	    base_addr[CySCHR2] = STOP_CHAR(info->tty);
+	if (base_addr[CySCRL] != START_CHAR(info->tty))
+	    base_addr[CySCRL] = START_CHAR(info->tty);
+	if (base_addr[CySCRH] != START_CHAR(info->tty))
+	    base_addr[CySCRH] = START_CHAR(info->tty);
+	if (base_addr[CyCOR1] != info->cor1)
+	    base_addr[CyCOR1] = info->cor1;
+	if (base_addr[CyCOR2] != info->cor2)
+	    base_addr[CyCOR2] = info->cor2;
+	if (base_addr[CyCOR3] != info->cor3)
+	    base_addr[CyCOR3] = info->cor3;
+	if (base_addr[CyCOR4] != info->cor4)
+	    base_addr[CyCOR4] = info->cor4;
+	if (base_addr[CyCOR5] != info->cor5)
+	    base_addr[CyCOR5] = info->cor5;
+	if (base_addr[CyCOR6] != info->cor6)
+	    base_addr[CyCOR6] = info->cor6;
+	if (base_addr[CyCOR7] != info->cor7)
+	    base_addr[CyCOR7] = info->cor7;
+
+	if (need_init_chan)
+	    write_cy_cmd(base_addr,CyINIT_CHAN);
+
+	base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */
+
+	/* 2ms default rx timeout */
+	ti = info->default_timeout ? info->default_timeout : 0x02;
+	if (base_addr[CyRTPRL] != ti)
+	    base_addr[CyRTPRL] = ti;
+	if (base_addr[CyRTPRH] != 0)
+	    base_addr[CyRTPRH] = 0;
+
+	/* Set up RTS here also ????? RGH 141095 */
+	if(i == 0){ /* baud rate is zero, turn off line */
+	    if ((base_addr[CyMSVR2] & CyDTR) == CyDTR)
+	        base_addr[CyMSVR2] = 0;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: dropping DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+	}else{
+	    if ((base_addr[CyMSVR2] & CyDTR) != CyDTR)
+	        base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: raising DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+	}
+
+	if (info->tty){
+	    clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	}
+
+    restore_flags(flags);
+
+} /* config_setup */
+
+
+static void
+cy_put_char(struct tty_struct *tty, unsigned char ch)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_put_char ttyS%d(0x%02x)\n", info->line, ch);
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_put_char"))
+	return;
+
+    if (!tty || !info->xmit_buf)
+	return;
+
+    save_flags(flags); cli();
+	if (info->xmit_cnt >= PAGE_SIZE - 1) {
+	    restore_flags(flags);
+	    return;
+	}
+
+	info->xmit_buf[info->xmit_head++] = ch;
+	info->xmit_head &= PAGE_SIZE - 1;
+	info->xmit_cnt++;
+    restore_flags(flags);
+} /* cy_put_char */
+
+
+static void
+cy_flush_chars(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+  volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+  int channel;
+				
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_flush_chars ttyS%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_flush_chars"))
+	return;
+
+    if (info->xmit_cnt <= 0 || tty->stopped
+    || tty->hw_stopped || !info->xmit_buf)
+	return;
+
+    channel = info->line;
+
+    save_flags(flags); cli();
+	base_addr[CyCAR] = channel;
+	base_addr[CyIER] |= CyTxMpty;
+    restore_flags(flags);
+} /* cy_flush_chars */
+
+
+/* This routine gets called when tty_write has put something into
+    the write_queue.  If the port is not already transmitting stuff,
+    start it off by enabling interrupts.  The interrupt service
+    routine will then ensure that the characters are sent.  If the
+    port is already active, there is no need to kick it.
+ */
+static int
+cy_write(struct tty_struct * tty, int from_user,
+           const unsigned char *buf, int count)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+  int c, total = 0;
+
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_write ttyS%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_write")){
+	return 0;
+    }
+	
+    if (!tty || !info->xmit_buf || !tmp_buf){
+        return 0;
+    }
+
+    while (1) {
+        save_flags(flags); cli();		
+	c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+			   SERIAL_XMIT_SIZE - info->xmit_head));
+	if (c <= 0){
+	    restore_flags(flags);
+	    break;
+	}
+
+	if (from_user) {
+	    down(&tmp_buf_sem);
+	    copy_from_user(tmp_buf, buf, c);
+	    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);
+	    up(&tmp_buf_sem);
+	} 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 (info->xmit_cnt
+    && !tty->stopped
+    && !tty->hw_stopped ) {
+        start_xmit(info);
+    }
+    return total;
+} /* cy_write */
+
+
+static int
+cy_write_room(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  int	ret;
+				
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_write_room ttyS%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_write_room"))
+	return 0;
+    ret = PAGE_SIZE - info->xmit_cnt - 1;
+    if (ret < 0)
+	ret = 0;
+    return ret;
+} /* cy_write_room */
+
+
+static int
+cy_chars_in_buffer(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+				
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_chars_in_buffer ttyS%d %d\n", info->line, info->xmit_cnt); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer"))
+	return 0;
+
+    return info->xmit_cnt;
+} /* cy_chars_in_buffer */
+
+
+static void
+cy_flush_buffer(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+				
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_flush_buffer ttyS%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_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);
+} /* cy_flush_buffer */
+
+
+/* This routine is called by the upper-layer tty layer to signal
+   that incoming characters should be throttled or that the
+   throttle should be released.
+ */
+static void
+cy_throttle(struct tty_struct * tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+  volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+  int channel;
+
+#ifdef SERIAL_DEBUG_THROTTLE
+  char buf[64];
+	
+    printk("throttle %s: %d....\n", _tty_name(tty, buf),
+	   tty->ldisc.chars_in_buffer(tty));
+    printk("cy_throttle ttyS%d\n", info->line);
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){
+	    return;
+    }
+
+    if (I_IXOFF(tty)) {
+	info->x_char = STOP_CHAR(tty);
+	    /* Should use the "Send Special Character" feature!!! */
+    }
+
+    channel = info->line;
+
+    save_flags(flags); cli();
+	base_addr[CyCAR] = (u_char)channel;
+	base_addr[CyMSVR1] = 0;
+    restore_flags(flags);
+
+    return;
+} /* cy_throttle */
+
+
+static void
+cy_unthrottle(struct tty_struct * tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+  volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+  int channel;
+
+#ifdef SERIAL_DEBUG_THROTTLE
+  char buf[64];
+	
+    printk("throttle %s: %d....\n", _tty_name(tty, buf),
+	   tty->ldisc.chars_in_buffer(tty));
+    printk("cy_unthrottle ttyS%d\n", info->line);
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){
+	    return;
+    }
+
+    if (I_IXOFF(tty)) {
+	info->x_char = START_CHAR(tty);
+	/* Should use the "Send Special Character" feature!!! */
+    }
+
+    channel = info->line;
+
+    save_flags(flags); cli();
+	base_addr[CyCAR] = (u_char)channel;
+	base_addr[CyMSVR1] = CyRTS;
+    restore_flags(flags);
+
+    return;
+} /* cy_unthrottle */
+
+static int
+get_serial_info(struct cyclades_port * info,
+                           struct serial_struct * retinfo)
+{
+  struct serial_struct tmp;
+
+/* CP('g'); */
+    if (!retinfo)
+            return -EFAULT;
+    memset(&tmp, 0, sizeof(tmp));
+    tmp.type = info->type;
+    tmp.line = info->line;
+    tmp.port = info->line;
+    tmp.irq = 0;
+    tmp.flags = info->flags;
+    tmp.baud_base = 0;          /*!!!*/
+    tmp.close_delay = info->close_delay;
+    tmp.custom_divisor = 0;     /*!!!*/
+    tmp.hub6 = 0;               /*!!!*/
+    copy_to_user(retinfo,&tmp,sizeof(*retinfo));
+    return 0;
+} /* get_serial_info */
+
+static int
+set_serial_info(struct cyclades_port * info,
+                           struct serial_struct * new_info)
+{
+  struct serial_struct new_serial;
+  struct cyclades_port old_info;
+
+/* CP('s'); */
+    if (!new_info)
+	    return -EFAULT;
+    copy_from_user(&new_serial,new_info,sizeof(new_serial));
+    old_info = *info;
+
+    if (!suser()) {
+	    if ((new_serial.close_delay != info->close_delay) ||
+		((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) !=
+		 (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)))
+		    return -EPERM;
+	    info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+			   (new_serial.flags & ASYNC_USR_MASK));
+	    goto check_and_exit;
+    }
+
+
+    /*
+     * OK, past this point, all the error checking has been done.
+     * At this point, we start making changes.....
+     */
+
+    info->flags = ((info->flags & ~ASYNC_FLAGS) |
+		    (new_serial.flags & ASYNC_FLAGS));
+    info->close_delay = new_serial.close_delay;
+
+
+check_and_exit:
+    if (info->flags & ASYNC_INITIALIZED){
+	config_setup(info);
+	return 0;
+    }else{
+        return startup(info);
+    }
+} /* set_serial_info */
+
+static int
+get_modem_info(struct cyclades_port * info, unsigned int *value)
+{
+  int channel;
+  volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+  unsigned long flags;
+  unsigned char status;
+  unsigned int result;
+
+    channel = info->line;
+
+    save_flags(flags); cli();
+        base_addr[CyCAR] = (u_char)channel;
+        status = base_addr[CyMSVR1] | base_addr[CyMSVR2];
+    restore_flags(flags);
+
+    result =  ((status  & CyRTS) ? TIOCM_RTS : 0)
+            | ((status  & CyDTR) ? TIOCM_DTR : 0)
+            | ((status  & CyDCD) ? TIOCM_CAR : 0)
+            | ((status  & CyDSR) ? TIOCM_DSR : 0)
+            | ((status  & CyCTS) ? TIOCM_CTS : 0);
+    cy_put_user(result,(unsigned long *) value);
+    return 0;
+} /* get_modem_info */
+
+static int
+set_modem_info(struct cyclades_port * info, unsigned int cmd,
+                          unsigned int *value)
+{
+  int channel;
+  volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+  unsigned long flags;
+  unsigned int arg = cy_get_user((unsigned long *) value);
+
+    channel = info->line;
+
+    switch (cmd) {
+    case TIOCMBIS:
+	if (arg & TIOCM_RTS){
+	    save_flags(flags); cli();
+		base_addr[CyCAR] = (u_char)channel;
+		base_addr[CyMSVR1] = CyRTS;
+	    restore_flags(flags);
+	}
+	if (arg & TIOCM_DTR){
+	    save_flags(flags); cli();
+	    base_addr[CyCAR] = (u_char)channel;
+/* CP('S');CP('2'); */
+	    base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: raising DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+	    restore_flags(flags);
+	}
+	break;
+    case TIOCMBIC:
+	if (arg & TIOCM_RTS){
+	    save_flags(flags); cli();
+		base_addr[CyCAR] = (u_char)channel;
+		base_addr[CyMSVR1] = 0;
+	    restore_flags(flags);
+	}
+	if (arg & TIOCM_DTR){
+	    save_flags(flags); cli();
+	    base_addr[CyCAR] = (u_char)channel;
+/* CP('C');CP('2'); */
+	    base_addr[CyMSVR2] = 0;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: dropping DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+	    restore_flags(flags);
+	}
+	break;
+    case TIOCMSET:
+	if (arg & TIOCM_RTS){
+	    save_flags(flags); cli();
+		base_addr[CyCAR] = (u_char)channel;
+		base_addr[CyMSVR1] = CyRTS;
+	    restore_flags(flags);
+	}else{
+	    save_flags(flags); cli();
+		base_addr[CyCAR] = (u_char)channel;
+		base_addr[CyMSVR1] = 0;
+	    restore_flags(flags);
+	}
+	if (arg & TIOCM_DTR){
+	    save_flags(flags); cli();
+	    base_addr[CyCAR] = (u_char)channel;
+/* CP('S');CP('3'); */
+	    base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: raising DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+	    restore_flags(flags);
+	}else{
+	    save_flags(flags); cli();
+	    base_addr[CyCAR] = (u_char)channel;
+/* CP('C');CP('3'); */
+	    base_addr[CyMSVR2] = 0;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: dropping DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+	    restore_flags(flags);
+	}
+	break;
+    default:
+		return -EINVAL;
+        }
+    return 0;
+} /* set_modem_info */
+
+static void
+send_break( struct cyclades_port * info, int duration)
+{ /* Let the transmit ISR take care of this (since it
+     requires stuffing characters into the output stream).
+   */
+    info->x_break = duration;
+    if (!info->xmit_cnt ) {
+	start_xmit(info);
+    }
+} /* send_break */
+
+static int
+get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon)
+{
+
+   copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor));
+   info->mon.int_count  = 0;
+   info->mon.char_count = 0;
+   info->mon.char_max   = 0;
+   info->mon.char_last  = 0;
+   return 0;
+}
+
+static int
+set_threshold(struct cyclades_port * info, unsigned long value)
+{
+   volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+   int channel;
+   
+   channel = info->line;
+
+   info->cor4 &= ~CyREC_FIFO;
+   info->cor4 |= value & CyREC_FIFO;
+   base_addr[CyCOR4] = info->cor4;
+   return 0;
+}
+
+static int
+get_threshold(struct cyclades_port * info, unsigned long *value)
+{
+   volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+   int channel;
+   unsigned long tmp;
+   
+   channel = info->line;
+
+   tmp = base_addr[CyCOR4] & CyREC_FIFO;
+   cy_put_user(tmp,value);
+   return 0;
+}
+
+static int
+set_default_threshold(struct cyclades_port * info, unsigned long value)
+{
+   info->default_threshold = value & 0x0f;
+   return 0;
+}
+
+static int
+get_default_threshold(struct cyclades_port * info, unsigned long *value)
+{
+   cy_put_user(info->default_threshold,value);
+   return 0;
+}
+
+static int
+set_timeout(struct cyclades_port * info, unsigned long value)
+{
+   volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+   int channel;
+   
+   channel = info->line;
+
+   base_addr[CyRTPRL] = value & 0xff;
+   base_addr[CyRTPRH] = (value >> 8) & 0xff;
+   return 0;
+}
+
+static int
+get_timeout(struct cyclades_port * info, unsigned long *value)
+{
+   volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+   int channel;
+   unsigned long tmp;
+   
+   channel = info->line;
+
+   tmp = base_addr[CyRTPRL];
+   cy_put_user(tmp,value);
+   return 0;
+}
+
+static int
+set_default_timeout(struct cyclades_port * info, unsigned long value)
+{
+   info->default_timeout = value & 0xff;
+   return 0;
+}
+
+static int
+get_default_timeout(struct cyclades_port * info, unsigned long *value)
+{
+   cy_put_user(info->default_timeout,value);
+   return 0;
+}
+
+static int
+cy_ioctl(struct tty_struct *tty, struct file * file,
+            unsigned int cmd, unsigned long arg)
+{
+  int error;
+  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+  int ret_val = 0;
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_ioctl ttyS%d, cmd = %x arg = %lx\n", info->line, cmd, arg); /* */
+#endif
+
+    switch (cmd) {
+        case CYGETMON:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(struct cyclades_monitor));
+            if (error){
+                ret_val = error;
+                break;
+            }
+            ret_val = get_mon_info(info, (struct cyclades_monitor *)arg);
+	    break;
+        case CYGETTHRESH:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(unsigned long));
+            if (error){
+                ret_val = error;
+                break;
+            }
+	    ret_val = get_threshold(info, (unsigned long *)arg);
+ 	    break;
+        case CYSETTHRESH:
+            ret_val = set_threshold(info, (unsigned long)arg);
+	    break;
+        case CYGETDEFTHRESH:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(unsigned long));
+            if (error){
+                ret_val = error;
+                break;
+            }
+	    ret_val = get_default_threshold(info, (unsigned long *)arg);
+ 	    break;
+        case CYSETDEFTHRESH:
+            ret_val = set_default_threshold(info, (unsigned long)arg);
+	    break;
+        case CYGETTIMEOUT:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(unsigned long));
+            if (error){
+                ret_val = error;
+                break;
+            }
+	    ret_val = get_timeout(info, (unsigned long *)arg);
+ 	    break;
+        case CYSETTIMEOUT:
+            ret_val = set_timeout(info, (unsigned long)arg);
+	    break;
+        case CYGETDEFTIMEOUT:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(unsigned long));
+            if (error){
+                ret_val = error;
+                break;
+            }
+	    ret_val = get_default_timeout(info, (unsigned long *)arg);
+ 	    break;
+        case CYSETDEFTIMEOUT:
+            ret_val = set_default_timeout(info, (unsigned long)arg);
+	    break;
+        case TCSBRK:    /* SVID version: non-zero arg --> no break */
+	    ret_val = tty_check_change(tty);
+	    if (ret_val)
+		return ret_val;
+            tty_wait_until_sent(tty,0);
+            if (!arg)
+                send_break(info, HZ/4); /* 1/4 second */
+            break;
+        case TCSBRKP:   /* support for POSIX tcsendbreak() */
+	    ret_val = tty_check_change(tty);
+	    if (ret_val)
+		return ret_val;
+            tty_wait_until_sent(tty,0);
+            send_break(info, arg ? arg*(HZ/10) : HZ/4);
+            break;
+        case TIOCMBIS:
+        case TIOCMBIC:
+        case TIOCMSET:
+            ret_val = set_modem_info(info, cmd, (unsigned int *) arg);
+            break;
+
+/* The following commands are incompletely implemented!!! */
+        case TIOCGSOFTCAR:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(unsigned int *));
+            if (error){
+                ret_val = error;
+                break;
+            }
+            cy_put_user(C_CLOCAL(tty) ? 1 : 0,
+                        (unsigned long *) arg);
+            break;
+        case TIOCSSOFTCAR:
+	    error = verify_area(VERIFY_READ, (void *) arg
+				,sizeof(unsigned long *));
+            if (error){
+                ret_val = error;
+                break;
+            }
+
+            arg = cy_get_user((unsigned long *) arg);
+            tty->termios->c_cflag =
+                    ((tty->termios->c_cflag & ~CLOCAL) |
+                     (arg ? CLOCAL : 0));
+            break;
+        case TIOCMGET:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(unsigned int *));
+            if (error){
+                ret_val = error;
+                break;
+            }
+            ret_val = get_modem_info(info, (unsigned int *) arg);
+            break;
+        case TIOCGSERIAL:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(struct serial_struct));
+            if (error){
+                ret_val = error;
+                break;
+            }
+            ret_val = get_serial_info(info,
+                                   (struct serial_struct *) arg);
+            break;
+        case TIOCSSERIAL:
+            ret_val = set_serial_info(info,
+                                   (struct serial_struct *) arg);
+            break;
+        default:
+	    ret_val = -ENOIOCTLCMD;
+    }
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_ioctl done\n");
+#endif
+
+    return ret_val;
+} /* cy_ioctl */
+
+
+
+
+static void
+cy_set_termios(struct tty_struct *tty, struct termios * old_termios)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_set_termios ttyS%d\n", info->line);
+#endif
+
+    if (tty->termios->c_cflag == old_termios->c_cflag)
+        return;
+    config_setup(info);
+
+    if ((old_termios->c_cflag & CRTSCTS) &&
+        !(tty->termios->c_cflag & CRTSCTS)) {
+            tty->stopped = 0;
+            cy_start(tty);
+    }
+#ifdef tytso_patch_94Nov25_1726
+    if (!(old_termios->c_cflag & CLOCAL) &&
+        (tty->termios->c_cflag & CLOCAL))
+            wake_up_interruptible(&info->open_wait);
+#endif
+
+    return;
+} /* cy_set_termios */
+
+
+static void
+cy_close(struct tty_struct * tty, struct file * filp)
+{
+  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+
+/* CP('C'); */
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_close ttyS%d\n", info->line);
+#endif
+
+    if (!info
+    || serial_paranoia_check(info, tty->device, "cy_close")){
+        return;
+    }
+#ifdef SERIAL_DEBUG_OPEN
+    printk("cy_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("cy_close: bad serial port count; tty->count is 1, "
+	   "info->count is %d\n", info->count);
+	info->count = 1;
+    }
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count - 1);
+#endif
+    if (--info->count < 0) {
+	printk("cy_close: bad serial port count for ttys%d: %d\n",
+	       info->line, info->count);
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: setting count to 0\n", __LINE__);
+#endif
+	info->count = 0;
+    }
+    if (info->count)
+	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;
+    if (info->flags & ASYNC_INITIALIZED)
+	tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
+    shutdown(info);
+    if (tty->driver.flush_buffer)
+	tty->driver.flush_buffer(tty);
+    if (tty->ldisc.flush_buffer)
+	tty->ldisc.flush_buffer(tty);
+    info->event = 0;
+    info->tty = 0;
+    if (tty->ldisc.num != ldiscs[N_TTY].num) {
+	if (tty->ldisc.close)
+	    (tty->ldisc.close)(tty);
+	tty->ldisc = ldiscs[N_TTY];
+	tty->termios->c_line = N_TTY;
+	if (tty->ldisc.open)
+	    (tty->ldisc.open)(tty);
+    }
+    if (info->blocked_open) {
+	if (info->close_delay) {
+	    current->state = TASK_INTERRUPTIBLE;
+	    current->timeout = jiffies + info->close_delay;
+	    schedule();
+	}
+	wake_up_interruptible(&info->open_wait);
+    }
+    info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+		     ASYNC_CLOSING);
+    wake_up_interruptible(&info->close_wait);
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_close done\n");
+#endif
+
+    return;
+} /* cy_close */
+
+/*
+ * cy_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void
+cy_hangup(struct tty_struct *tty)
+{
+  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+	
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_hangup ttyS%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_hangup"))
+	return;
+    
+    shutdown(info);
+#if 0
+    info->event = 0;
+    info->count = 0;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: setting count to 0\n", __LINE__);
+#endif
+    info->tty = 0;
+#endif
+    info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+    wake_up_interruptible(&info->open_wait);
+} /* cy_hangup */
+
+
+
+/*
+ * ------------------------------------------------------------
+ * cy_open() and friends
+ * ------------------------------------------------------------
+ */
+
+static int
+block_til_ready(struct tty_struct *tty, struct file * filp,
+                           struct cyclades_port *info)
+{
+  struct wait_queue wait = { current, NULL };
+  unsigned long flags;
+  int channel;
+  int retval;
+  volatile u_char *base_addr = (u_char *)BASE_ADDR;
+
+    /*
+     * If the device is in the middle of being closed, then block
+     * until it's done, and then try again.
+     */
+    if (info->flags & ASYNC_CLOSING) {
+	interruptible_sleep_on(&info->close_wait);
+	if (info->flags & ASYNC_HUP_NOTIFY){
+	    return -EAGAIN;
+	}else{
+	    return -ERESTARTSYS;
+	}
+    }
+
+    /*
+     * 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, then make the check up front
+     * and then exit.
+     */
+    if (filp->f_flags & O_NONBLOCK) {
+	if (info->flags & ASYNC_CALLOUT_ACTIVE){
+	    return -EBUSY;
+	}
+	info->flags |= ASYNC_NORMAL_ACTIVE;
+	return 0;
+    }
+
+    /*
+     * 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
+     * cy_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
+    info->count--;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count);
+#endif
+    info->blocked_open++;
+
+    channel = info->line;
+
+    while (1) {
+	save_flags(flags); cli();
+	    if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){
+		base_addr[CyCAR] = (u_char)channel;
+		base_addr[CyMSVR1] = CyRTS;
+/* CP('S');CP('4'); */
+		base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+                printk("cyc: %d: raising DTR\n", __LINE__);
+                printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+	    }
+	restore_flags(flags);
+	current->state = TASK_INTERRUPTIBLE;
+	if (tty_hung_up_p(filp)
+	|| !(info->flags & ASYNC_INITIALIZED) ){
+	    if (info->flags & ASYNC_HUP_NOTIFY) {
+		retval = -EAGAIN;
+	    }else{
+		retval = -ERESTARTSYS;
+	    }
+	    break;
+	}
+	save_flags(flags); cli();
+	    base_addr[CyCAR] = (u_char)channel;
+/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */
+	    if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
+	    && !(info->flags & ASYNC_CLOSING)
+	    && (C_CLOCAL(tty)
+	        || (base_addr[CyMSVR1] & CyDCD))) {
+		    restore_flags(flags);
+		    break;
+	    }
+	restore_flags(flags);
+	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++;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count);
+#endif
+    }
+    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;
+} /* block_til_ready */
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * performs the serial-specific initialization for the tty structure.
+ */
+int
+cy_open(struct tty_struct *tty, struct file * filp)
+{
+  struct cyclades_port  *info;
+  int retval, line;
+
+/* CP('O'); */
+    line = MINOR(tty->device) - tty->driver.minor_start;
+    if ((line < 0) || (NR_PORTS <= line)){
+        return -ENODEV;
+    }
+    info = &cy_port[line];
+    if (info->line < 0){
+        return -ENODEV;
+    }
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_open ttyS%d\n", info->line); /* */
+#endif
+    if (serial_paranoia_check(info, tty->device, "cy_open")){
+        return -ENODEV;
+    }
+#ifdef SERIAL_DEBUG_OPEN
+    printk("cy_open ttyS%d, count = %d\n", info->line, info->count);/**/
+#endif
+    info->count++;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count);
+#endif
+    tty->driver_data = info;
+    info->tty = tty;
+
+    if (!tmp_buf) {
+	tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+	if (!tmp_buf){
+	    return -ENOMEM;
+        }
+    }
+
+    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;
+    }
+    /*
+     * Start up serial port
+     */
+    retval = startup(info);
+    if (retval){
+	return retval;
+    }
+
+    retval = block_til_ready(tty, filp, info);
+    if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+	printk("cy_open returning after block_til_ready with %d\n",
+	       retval);
+#endif
+	return retval;
+    }
+
+    info->session = current->session;
+    info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk("cy_open done\n");/**/
+#endif
+    return 0;
+} /* cy_open */
+
+
+
+/*
+ * ---------------------------------------------------------------------
+ * serial167_init() and friends
+ *
+ * serial167_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static void
+show_version(void)
+{
+    printk("MVME166/167 cd2401 driver\n");
+} /* show_version */
+
+/* initialize chips on card -- return number of valid
+   chips (which is number of ports/4) */
+
+/*
+ * This initialises the hardware to a reasonable state.  It should
+ * probe the chip first so as to copy 166-Bug setup as a default for
+ * port 0.  It initialises CMR to CyASYNC; that is never done again, so
+ * as to limit the number of CyINIT_CHAN commands in normal running.
+ *
+ * ... I wonder what I should do if this fails ...
+ */
+
+void
+mvme167_serial_console_setup(int cflag)
+{
+	volatile unsigned char* base_addr = (u_char *)BASE_ADDR;
+	int ch;
+	u_char spd;
+	u_char rcor, rbpr, badspeed = 0;
+	unsigned long flags;
+
+	save_flags(flags); cli();
+
+	/*
+	 * First probe channel zero of the chip, to see what speed has
+	 * been selected.
+	 */
+
+	base_addr[CyCAR] = 0;
+
+	rcor = base_addr[CyRCOR] << 5;
+	rbpr = base_addr[CyRBPR];
+
+	for (spd = 0; spd < sizeof(baud_bpr); spd++)
+		if (rbpr == baud_bpr[spd] && rcor == baud_co[spd])
+			break;
+	if (spd >= sizeof(baud_bpr)) {
+		spd = 14;	/* 19200 */
+		badspeed = 1;	/* Failed to identify speed */
+	}
+	initial_console_speed = spd;
+
+	/* OK, we have chosen a speed, now reset and reinitialise */
+
+        my_udelay(20000L);	/* Allow time for any active o/p to complete */
+        if(base_addr[CyCCR] != 0x00){
+            /* printk(" chip is never idle (CCR != 0)\n"); */
+            return;
+        }
+
+        base_addr[CyCCR] = CyCHIP_RESET;	/* Reset the chip */
+        my_udelay(1000L);
+
+        if(base_addr[CyGFRCR] == 0x00){
+            /* printk(" chip is not responding (GFRCR stayed 0)\n"); */
+            return;
+        }
+
+	/*
+	 * System clock is 20Mhz, divided by 2048, so divide by 10 for a 1.0ms
+	 * tick
+	 */
+
+	base_addr[CyTPR] = 10;
+
+	base_addr[CyPILR1] = 0x01;    /* Interrupt level for modem change */
+	base_addr[CyPILR2] = 0x02;    /* Interrupt level for tx ints */
+	base_addr[CyPILR3] = 0x03;    /* Interrupt level for rx ints */
+
+	/*
+	 * Attempt to set up all channels to something reasonable, and
+	 * bang out a INIT_CHAN command.  We should then be able to limit
+	 * the ammount of fiddling we have to do in normal running.
+	 */
+
+	for (ch = 3; ch >= 0 ; ch--) {
+		base_addr[CyCAR] = (u_char)ch;
+		base_addr[CyIER] = 0;
+		base_addr[CyCMR] = CyASYNC;
+		base_addr[CyLICR] = (u_char)ch << 2;
+		base_addr[CyLIVR] = 0x5c;
+		base_addr[CyTCOR] = baud_co[spd];
+		base_addr[CyTBPR] = baud_bpr[spd];
+		base_addr[CyRCOR] = baud_co[spd] >> 5;
+		base_addr[CyRBPR] = baud_bpr[spd];
+		base_addr[CySCHR1] = 'Q' & 0x1f;
+		base_addr[CySCHR2] = 'X' & 0x1f;
+		base_addr[CySCRL] = 0;
+		base_addr[CySCRH] = 0;
+		base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE;
+		base_addr[CyCOR2] = 0;
+		base_addr[CyCOR3] = Cy_1_STOP;
+		base_addr[CyCOR4] = baud_cor4[spd];
+		base_addr[CyCOR5] = 0;
+		base_addr[CyCOR6] = 0;
+		base_addr[CyCOR7] = 0;
+		base_addr[CyRTPRL] = 2;
+		base_addr[CyRTPRH] = 0;
+	        base_addr[CyMSVR1] = 0;
+	        base_addr[CyMSVR2] = 0;
+		write_cy_cmd(base_addr,CyINIT_CHAN|CyDIS_RCVR|CyDIS_XMTR);
+	}
+
+	/*
+	 * Now do specials for channel zero....
+	 */
+
+        base_addr[CyMSVR1] = CyRTS;
+        base_addr[CyMSVR2] = CyDTR;
+	base_addr[CyIER] = CyRxData;
+	write_cy_cmd(base_addr,CyENB_RCVR|CyENB_XMTR);
+
+	restore_flags(flags);
+
+	my_udelay(20000L);	/* Let it all settle down */
+
+        printk("CD2401 initialised,  chip is rev 0x%02x\n", base_addr[CyGFRCR]);
+	if (badspeed)
+        	printk("  WARNING:  Failed to identify line speed, rcor=%02x,rbpr=%02x\n",
+					rcor >> 5, rbpr);
+} /* serial_console_init */
+
+/* The serial driver boot-time initialization code!
+    Hardware I/O ports are mapped to character special devices on a
+    first found, first allocated manner.  That is, this code searches
+    for Cyclom cards in the system.  As each is found, it is probed
+    to discover how many chips (and thus how many ports) are present.
+    These ports are mapped to the tty ports 64 and upward in monotonic
+    fashion.  If an 8-port card is replaced with a 16-port card, the
+    port mapping on a following card will shift.
+
+    This approach is different from what is used in the other serial
+    device driver because the Cyclom is more properly a multiplexer,
+    not just an aggregation of serial ports on one card.
+
+    If there are more cards with more ports than have been statically
+    allocated above, a warning is printed and the extra ports are ignored.
+ */
+int
+serial167_init(void)
+{
+  struct cyclades_port *info;
+  int good_ports = 0;
+  int port_num = 0;
+  int index;
+  int DefSpeed;
+#ifdef notyet
+  struct sigaction sa;
+#endif
+
+    if (!(mvme16x_config &MVME16x_CONFIG_GOT_CD2401))
+	return 0;
+
+#if 0
+scrn[1] = '\0';
+#endif
+
+    show_version();
+
+    /* Has "console=0,9600n8" been used in bootinfo to change speed? */
+    if (serial_console_cflag)
+	DefSpeed = serial_console_cflag & 0017;
+    else {
+	DefSpeed = initial_console_speed;
+	serial_console_info = &cy_port[0];
+	serial_console_cflag = DefSpeed | CS8;
+#if 0
+	serial_console = 64; /*callout_driver.minor_start*/
+#endif
+    }
+
+    /* Initialize the tty_driver structure */
+    
+    memset(&cy_serial_driver, 0, sizeof(struct tty_driver));
+    cy_serial_driver.magic = TTY_DRIVER_MAGIC;
+    cy_serial_driver.name = "ttyS";
+    cy_serial_driver.major = TTY_MAJOR;
+    cy_serial_driver.minor_start = 64;
+    cy_serial_driver.num = NR_PORTS;
+    cy_serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+    cy_serial_driver.subtype = SERIAL_TYPE_NORMAL;
+    cy_serial_driver.init_termios = tty_std_termios;
+    cy_serial_driver.init_termios.c_cflag =
+	    B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+    cy_serial_driver.flags = TTY_DRIVER_REAL_RAW;
+    cy_serial_driver.refcount = &serial_refcount;
+    cy_serial_driver.table = serial_table;
+    cy_serial_driver.termios = serial_termios;
+    cy_serial_driver.termios_locked = serial_termios_locked;
+    cy_serial_driver.open = cy_open;
+    cy_serial_driver.close = cy_close;
+    cy_serial_driver.write = cy_write;
+    cy_serial_driver.put_char = cy_put_char;
+    cy_serial_driver.flush_chars = cy_flush_chars;
+    cy_serial_driver.write_room = cy_write_room;
+    cy_serial_driver.chars_in_buffer = cy_chars_in_buffer;
+    cy_serial_driver.flush_buffer = cy_flush_buffer;
+    cy_serial_driver.ioctl = cy_ioctl;
+    cy_serial_driver.throttle = cy_throttle;
+    cy_serial_driver.unthrottle = cy_unthrottle;
+    cy_serial_driver.set_termios = cy_set_termios;
+    cy_serial_driver.stop = cy_stop;
+    cy_serial_driver.start = cy_start;
+    cy_serial_driver.hangup = cy_hangup;
+
+    /*
+     * The callout device is just like normal device except for
+     * major number and the subtype code.
+     */
+    cy_callout_driver = cy_serial_driver;
+    cy_callout_driver.name = "cua";
+    cy_callout_driver.major = TTYAUX_MAJOR;
+    cy_callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+
+    if (tty_register_driver(&cy_serial_driver))
+	    panic("Couldn't register Cyclom serial driver\n");
+    if (tty_register_driver(&cy_callout_driver))
+	    panic("Couldn't register Cyclom callout driver\n");
+
+    init_bh(CYCLADES_BH, do_cyclades_bh);
+
+    port_num = 0;
+    info = cy_port;
+    for (index = 0; index < 1; index++) {
+
+	good_ports = 4;
+
+	if(port_num < NR_PORTS){
+	    while( good_ports-- && port_num < NR_PORTS){
+		/*** initialize port ***/
+		info->magic = CYCLADES_MAGIC;
+		info->type = PORT_CIRRUS;
+		info->card = index;
+		info->line = port_num;
+		info->flags = STD_COM_FLAGS;
+		info->tty = 0;
+		info->xmit_fifo_size = 12;
+		info->cor1 = CyPARITY_NONE|Cy_8_BITS;
+		info->cor2 = CyETC;
+		info->cor3 = Cy_1_STOP;
+		info->cor4 = 0x08; /* _very_ small receive threshold */
+		info->cor5 = 0;
+		info->cor6 = 0;
+		info->cor7 = 0;
+		info->tbpr = baud_bpr[DefSpeed]; /* Tx BPR */
+		info->tco = baud_co[DefSpeed]; /* Tx CO */
+		info->rbpr = baud_bpr[DefSpeed]; /* Rx BPR */
+		info->rco = baud_co[DefSpeed] >> 5; /* Rx CO */
+		info->close_delay = 0;
+		info->x_char = 0;
+		info->event = 0;
+		info->count = 0;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: setting count to 0\n", __LINE__);
+#endif
+		info->blocked_open = 0;
+		info->default_threshold = 0;
+		info->default_timeout = 0;
+		info->tqueue.routine = do_softint;
+		info->tqueue.data = info;
+		info->callout_termios =cy_callout_driver.init_termios;
+		info->normal_termios = cy_serial_driver.init_termios;
+		info->open_wait = 0;
+		info->close_wait = 0;
+		/* info->session */
+		/* info->pgrp */
+/*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/
+		info->read_status_mask = CyTIMEOUT| CySPECHAR| CyBREAK
+                                       | CyPARITY| CyFRAME| CyOVERRUN;
+		/* info->timeout */
+
+		printk("ttyS%1d ", info->line);
+		port_num++;info++;
+		if(!(port_num & 7)){
+		    printk("\n               ");
+		}
+	    }
+	}
+	printk("\n");
+    }
+    while( port_num < NR_PORTS){
+	info->line = -1;
+	port_num++;info++;
+    }
+#ifdef CONFIG_REMOTE_DEBUG
+    debug_setup();
+#endif
+    if (request_irq (MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt, 0,
+				"cd2401_errors", cd2401_rxerr_interrupt) ||
+	request_irq (MVME167_IRQ_SER_MODEM, cd2401_modem_interrupt, 0,
+				"cd2401_modem", cd2401_modem_interrupt) ||
+	request_irq (MVME167_IRQ_SER_TX, cd2401_tx_interrupt, 0,
+				"cd2401_txints", cd2401_tx_interrupt) ||
+	request_irq (MVME167_IRQ_SER_RX, cd2401_rx_interrupt, 0,
+				"cd2401_rxints", cd2401_rx_interrupt))
+    {
+	panic ("Couldn't get serial IRQs");
+    }
+
+    /* Now we have registered the interrupt handlers, allow the interrupts */
+
+    pcc2chip[PccSCCMICR] = 0x15;		/* Serial ints are level 5 */
+    pcc2chip[PccSCCTICR] = 0x15;
+    pcc2chip[PccSCCRICR] = 0x15;
+
+    pcc2chip[PccIMLR] = 3;			/* Allow PCC2 ints above 3!? */
+
+    return 0;
+} /* serial167_init */
+
+
+#ifdef CYCLOM_SHOW_STATUS
+static void
+show_status(int line_num)
+{
+  volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+  int channel;
+  struct cyclades_port * info;
+  unsigned long flags;
+
+    info = &cy_port[line_num];
+    channel = info->line;
+    printk("  channel %d\n", channel);/**/
+
+    printk(" cy_port\n");
+    printk("  card line flags = %d %d %x\n",
+                 info->card, info->line, info->flags);
+    printk("  *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n",
+                 (long)info->tty, info->read_status_mask,
+                 info->timeout, info->xmit_fifo_size);
+    printk("  cor1,cor2,cor3,cor4,cor5,cor6,cor7 = %x %x %x %x %x %x %x\n",
+             info->cor1, info->cor2, info->cor3, info->cor4, info->cor5,
+			info->cor6, info->cor7);
+    printk("  tbpr,tco,rbpr,rco = %d %d %d %d\n",
+             info->tbpr, info->tco, info->rbpr, info->rco);
+    printk("  close_delay event count = %d %d %d\n",
+             info->close_delay, info->event, info->count);
+    printk("  x_char blocked_open = %x %x\n",
+             info->x_char, info->blocked_open);
+    printk("  session pgrp open_wait = %lx %lx %lx\n",
+             info->session, info->pgrp, (long)info->open_wait);
+
+
+    save_flags(flags); cli();
+
+/* Global Registers */
+
+	printk(" CyGFRCR %x\n", base_addr[CyGFRCR]);
+	printk(" CyCAR %x\n", base_addr[CyCAR]);
+	printk(" CyRISR %x\n", base_addr[CyRISR]);
+	printk(" CyTISR %x\n", base_addr[CyTISR]);
+	printk(" CyMISR %x\n", base_addr[CyMISR]);
+	printk(" CyRIR %x\n", base_addr[CyRIR]);
+	printk(" CyTIR %x\n", base_addr[CyTIR]);
+	printk(" CyMIR %x\n", base_addr[CyMIR]);
+	printk(" CyTPR %x\n", base_addr[CyTPR]);
+
+	base_addr[CyCAR] = (u_char)channel;
+
+/* Virtual Registers */
+
+#if 0
+	printk(" CyRIVR %x\n", base_addr[CyRIVR]);
+	printk(" CyTIVR %x\n", base_addr[CyTIVR]);
+	printk(" CyMIVR %x\n", base_addr[CyMIVR]);
+	printk(" CyMISR %x\n", base_addr[CyMISR]);
+#endif
+
+/* Channel Registers */
+
+	printk(" CyCCR %x\n", base_addr[CyCCR]);
+	printk(" CyIER %x\n", base_addr[CyIER]);
+	printk(" CyCOR1 %x\n", base_addr[CyCOR1]);
+	printk(" CyCOR2 %x\n", base_addr[CyCOR2]);
+	printk(" CyCOR3 %x\n", base_addr[CyCOR3]);
+	printk(" CyCOR4 %x\n", base_addr[CyCOR4]);
+	printk(" CyCOR5 %x\n", base_addr[CyCOR5]);
+#if 0
+	printk(" CyCCSR %x\n", base_addr[CyCCSR]);
+	printk(" CyRDCR %x\n", base_addr[CyRDCR]);
+#endif
+	printk(" CySCHR1 %x\n", base_addr[CySCHR1]);
+	printk(" CySCHR2 %x\n", base_addr[CySCHR2]);
+#if 0
+	printk(" CySCHR3 %x\n", base_addr[CySCHR3]);
+	printk(" CySCHR4 %x\n", base_addr[CySCHR4]);
+	printk(" CySCRL %x\n", base_addr[CySCRL]);
+	printk(" CySCRH %x\n", base_addr[CySCRH]);
+	printk(" CyLNC %x\n", base_addr[CyLNC]);
+	printk(" CyMCOR1 %x\n", base_addr[CyMCOR1]);
+	printk(" CyMCOR2 %x\n", base_addr[CyMCOR2]);
+#endif
+	printk(" CyRTPRL %x\n", base_addr[CyRTPRL]);
+	printk(" CyRTPRH %x\n", base_addr[CyRTPRH]);
+	printk(" CyMSVR1 %x\n", base_addr[CyMSVR1]);
+	printk(" CyMSVR2 %x\n", base_addr[CyMSVR2]);
+	printk(" CyRBPR %x\n", base_addr[CyRBPR]);
+	printk(" CyRCOR %x\n", base_addr[CyRCOR]);
+	printk(" CyTBPR %x\n", base_addr[CyTBPR]);
+	printk(" CyTCOR %x\n", base_addr[CyTCOR]);
+
+    restore_flags(flags);
+} /* show_status */
+#endif
+
+
+#if 0
+/* Dummy routine in mvme16x/config.c for now */
+
+/* Serial console setup. Called from linux/init/main.c */
+
+void console_setup(char *str, int *ints)
+{
+	char *s;
+	int baud, bits, parity;
+	int cflag = 0;
+
+	/* Sanity check. */
+	if (ints[0] > 3 || ints[1] > 3) return;
+
+	/* Get baud, bits and parity */
+	baud = 2400;
+	bits = 8;
+	parity = 'n';
+	if (ints[2]) baud = ints[2];
+	if ((s = strchr(str, ','))) {
+		do {
+			s++;
+		} while(*s >= '0' && *s <= '9');
+		if (*s) parity = *s++;
+		if (*s) bits   = *s - '0';
+	}
+
+	/* Now construct a cflag setting. */
+	switch(baud) {
+		case 1200:
+			cflag |= B1200;
+			break;
+		case 9600:
+			cflag |= B9600;
+			break;
+		case 19200:
+			cflag |= B19200;
+			break;
+		case 38400:
+			cflag |= B38400;
+			break;
+		case 2400:
+		default:
+			cflag |= B2400;
+			break;
+	}
+	switch(bits) {
+		case 7:
+			cflag |= CS7;
+			break;
+		default:
+		case 8:
+			cflag |= CS8;
+			break;
+	}
+	switch(parity) {
+		case 'o': case 'O':
+			cflag |= PARODD;
+			break;
+		case 'e': case 'E':
+			cflag |= PARENB;
+			break;
+	}
+
+	serial_console_info = &cy_port[ints[1]];
+	serial_console_cflag = cflag;
+	serial_console = ints[1] + 64; /*callout_driver.minor_start*/
+}
+#endif
+
+/*
+ * The following is probably out of date for 2.1.x serial console stuff.
+ *
+ * The console is registered early on from arch/m68k/kernel/setup.c, and
+ * it therefore relies on the chip being setup correctly by 166-Bug.  This
+ * seems reasonable, as the serial port has been used to invoke the system
+ * boot.  It also means that this function must not rely on any data
+ * initialisation performed by serial167_init() etc.
+ *
+ * Of course, once the console has been registered, we had better ensure
+ * that serial167_init() doesn't leave the chip non-functional.
+ */
+
+void serial167_write(struct console *co, const char *str, unsigned count)
+{
+	volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+	unsigned long flags;
+	volatile u_char sink;
+	u_char ier;
+	int port;
+	u_char do_lf = 0;
+	int i = 0;
+
+	save_flags(flags); cli();
+
+	/* Ensure transmitter is enabled! */
+
+	port = 0;
+	base_addr[CyCAR] = (u_char)port;
+	while (base_addr[CyCCR])
+		;
+	base_addr[CyCCR] = CyENB_XMTR;
+
+	ier = base_addr[CyIER];
+	base_addr[CyIER] = CyTxMpty;
+
+	while (1) {
+		if (pcc2chip[PccSCCTICR] & 0x20)
+		{
+			/* We have a Tx int. Acknowledge it */
+			sink = pcc2chip[PccTPIACKR];
+			if ((base_addr[CyLICR] >> 2) == port) {
+				if (i == count) {
+					/* Last char of string is now output */
+					base_addr[CyTEOIR] = CyNOTRANS;
+					break;
+				}
+				if (do_lf) {
+					base_addr[CyTDR] = '\n';
+					str++;
+					i++;
+					do_lf = 0;
+				}
+				else if (*str == '\n') {
+					base_addr[CyTDR] = '\r';
+					do_lf = 1;
+				}
+				else {
+					base_addr[CyTDR] = *str++;
+					i++;
+				}
+				base_addr[CyTEOIR] = 0;
+			}
+			else
+				base_addr[CyTEOIR] = CyNOTRANS;
+		}
+	}
+
+	base_addr[CyIER] = ier;
+
+	restore_flags(flags);
+}
+
+#ifdef CONFIG_REMOTE_DEBUG
+void putDebugChar (int c)
+{
+	volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+	unsigned long flags;
+	volatile u_char sink;
+	u_char ier;
+	int port;
+
+	save_flags(flags); cli();
+
+	/* Ensure transmitter is enabled! */
+
+	port = DEBUG_PORT;
+	base_addr[CyCAR] = (u_char)port;
+	while (base_addr[CyCCR])
+		;
+	base_addr[CyCCR] = CyENB_XMTR;
+
+	ier = base_addr[CyIER];
+	base_addr[CyIER] = CyTxMpty;
+
+	while (1) {
+		if (pcc2chip[PccSCCTICR] & 0x20)
+		{
+			/* We have a Tx int. Acknowledge it */
+			sink = pcc2chip[PccTPIACKR];
+			if ((base_addr[CyLICR] >> 2) == port) {
+				base_addr[CyTDR] = c;
+				base_addr[CyTEOIR] = 0;
+				break;
+			}
+			else
+				base_addr[CyTEOIR] = CyNOTRANS;
+		}
+	}
+
+	base_addr[CyIER] = ier;
+
+	restore_flags(flags);
+}
+
+int getDebugChar()
+{
+	volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+	unsigned long flags;
+	volatile u_char sink;
+	u_char ier;
+	int port;
+	int i, c;
+
+	i = debugiq.out;
+	if (i != debugiq.in) {
+		c = debugiq.buf[i];
+		if (++i == DEBUG_LEN)
+			i = 0;
+		debugiq.out = i;
+		return c;
+	}
+	/* OK, nothing in queue, wait in poll loop */
+
+	save_flags(flags); cli();
+
+	/* Ensure receiver is enabled! */
+
+	port = DEBUG_PORT;
+	base_addr[CyCAR] = (u_char)port;
+#if 0
+	while (base_addr[CyCCR])
+		;
+	base_addr[CyCCR] = CyENB_RCVR;
+#endif
+	ier = base_addr[CyIER];
+	base_addr[CyIER] = CyRxData;
+
+	while (1) {
+		if (pcc2chip[PccSCCRICR] & 0x20)
+		{
+			/* We have a Rx int. Acknowledge it */
+			sink = pcc2chip[PccRPIACKR];
+			if ((base_addr[CyLICR] >> 2) == port) {
+				int cnt = base_addr[CyRFOC];
+				while (cnt-- > 0)
+				{
+					c = base_addr[CyRDR];
+					if (c == 0)
+						printk ("!! debug char is null (cnt=%d) !!", cnt);
+					else
+						queueDebugChar (c);
+				}
+				base_addr[CyREOIR] = 0;
+				i = debugiq.out;
+				if (i == debugiq.in)
+					panic ("Debug input queue empty!");
+				c = debugiq.buf[i];
+				if (++i == DEBUG_LEN)
+					i = 0;
+				debugiq.out = i;
+				break;
+			}
+			else
+				base_addr[CyREOIR] = CyNOTRANS;
+		}
+	}
+
+	base_addr[CyIER] = ier;
+
+	restore_flags(flags);
+
+	return (c);
+}
+
+void queueDebugChar (int c)
+{
+	int i;
+
+	i = debugiq.in;
+	debugiq.buf[i] = c;
+	if (++i == DEBUG_LEN)
+		i = 0;
+	if (i != debugiq.out)
+		debugiq.in = i;
+}
+
+static void
+debug_setup()
+{
+  unsigned long flags;
+  volatile unsigned char *base_addr = (u_char *)BASE_ADDR;
+  int   i, cflag;
+
+    cflag = B19200;
+
+    save_flags(flags); cli();
+
+    for (i = 0; i < 4; i++)
+    {
+	base_addr[CyCAR] = i;
+	base_addr[CyLICR] = i << 2;
+    }
+
+    debugiq.in = debugiq.out = 0;
+
+    base_addr[CyCAR] = DEBUG_PORT;
+
+    /* baud rate */
+    i = cflag & CBAUD;
+
+    base_addr[CyIER] = 0;
+
+    base_addr[CyCMR] = CyASYNC;
+    base_addr[CyLICR] = DEBUG_PORT << 2;
+    base_addr[CyLIVR] = 0x5c;
+
+    /* tx and rx baud rate */
+
+    base_addr[CyTCOR] = baud_co[i];
+    base_addr[CyTBPR] = baud_bpr[i];
+    base_addr[CyRCOR] = baud_co[i] >> 5;
+    base_addr[CyRBPR] = baud_bpr[i];
+
+    /* set line characteristics  according configuration */
+
+    base_addr[CySCHR1] = 0;
+    base_addr[CySCHR2] = 0;
+    base_addr[CySCRL] = 0;
+    base_addr[CySCRH] = 0;
+    base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE;
+    base_addr[CyCOR2] = 0;
+    base_addr[CyCOR3] = Cy_1_STOP;
+    base_addr[CyCOR4] = baud_cor4[i];
+    base_addr[CyCOR5] = 0;
+    base_addr[CyCOR6] = 0;
+    base_addr[CyCOR7] = 0;
+
+    write_cy_cmd(base_addr,CyINIT_CHAN);
+    write_cy_cmd(base_addr,CyENB_RCVR);
+
+    base_addr[CyCAR] = DEBUG_PORT; /* !!! Is this needed? */
+
+    base_addr[CyRTPRL] = 2;
+    base_addr[CyRTPRH] = 0;
+
+    base_addr[CyMSVR1] = CyRTS;
+    base_addr[CyMSVR2] = CyDTR;
+
+    base_addr[CyIER] = CyRxData;
+
+    restore_flags(flags);
+
+} /* debug_setup */
+
+#endif
diff -u --recursive --new-file v2.1.124/linux/drivers/char/sysrq.c linux/drivers/char/sysrq.c
--- v2.1.124/linux/drivers/char/sysrq.c	Mon Sep 28 10:51:33 1998
+++ linux/drivers/char/sysrq.c	Wed Oct  7 18:52:09 1998
@@ -85,7 +85,7 @@
 #ifdef CONFIG_APM
 	case 'o':					    /* O -- power off */
 		printk("Power off\n");
-		apm_set_power_state(APM_STATE_OFF);
+		apm_power_off();
 		break;
 #endif
 	case 's':					    /* S -- emergency sync */
diff -u --recursive --new-file v2.1.124/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.124/linux/drivers/char/tty_io.c	Mon Oct  5 13:13:39 1998
+++ linux/drivers/char/tty_io.c	Mon Oct  5 21:54:35 1998
@@ -390,7 +390,9 @@
 {
 	struct tty_struct *tty = (struct tty_struct *) data;
 	struct file * filp;
+	struct file * cons_filp = NULL;
 	struct task_struct *p;
+	int    closecount = 0, n;
 
 	if (!tty)
 		return;
@@ -407,10 +409,13 @@
 		if (!filp->f_dentry->d_inode)
 			continue;
 		if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV ||
-		    filp->f_dentry->d_inode->i_rdev == SYSCONS_DEV)
+		    filp->f_dentry->d_inode->i_rdev == SYSCONS_DEV) {
+			cons_filp = filp;
 			continue;
+		}
 		if (filp->f_op != &tty_fops)
 			continue;
+		closecount++;
 		tty_fasync(-1, filp, 0);
 		filp->f_op = &hung_up_tty_fops;
 	}
@@ -470,7 +475,17 @@
 	tty->session = 0;
 	tty->pgrp = -1;
 	tty->ctrl_status = 0;
-	if (tty->driver.hangup)
+	/*
+	 *	If one of the devices matches a console pointer, we
+	 *	cannot just call hangup() because that will cause
+	 *	tty->count and state->count to go out of sync.
+	 *	So we just call close() the right number of times.
+	 */
+	if (cons_filp) {
+		if (tty->driver.close)
+			for (n = 0; n < closecount; n++)
+				tty->driver.close(tty, cons_filp);
+	} else if (tty->driver.hangup)
 		(tty->driver.hangup)(tty);
 	unlock_kernel();
 }
@@ -1243,6 +1258,7 @@
 		if (!c)
                         return -ENODEV;
                 device = c->device(c);
+		filp->f_flags |= O_NONBLOCK; /* Don't let /dev/console block */
 		noctty = 1;
 	}
 #ifdef CONFIG_UNIX98_PTYS
@@ -2103,6 +2119,9 @@
 #endif
 #ifdef CONFIG_ROCKETPORT
 	rp_init();
+#endif
+#ifdef CONFIG_MVME16x
+	serial167_init();
 #endif
 #ifdef CONFIG_CYCLADES
 	cy_init();
diff -u --recursive --new-file v2.1.124/linux/drivers/char/videodev.c linux/drivers/char/videodev.c
--- v2.1.124/linux/drivers/char/videodev.c	Wed Aug 26 11:37:37 1998
+++ linux/drivers/char/videodev.c	Tue Oct  6 09:39:49 1998
@@ -333,7 +333,7 @@
 {
 	struct video_init *vfli = video_init_list;
 	
-	printk(KERN_INFO "Linux video capture interface: v0.01 ALPHA\n");
+	printk(KERN_INFO "Linux video capture interface: v1.00\n");
 	if(register_chrdev(VIDEO_MAJOR,"video_capture", &video_fops))
 	{
 		printk("video_dev: unable to get major %d\n", VIDEO_MAJOR);
diff -u --recursive --new-file v2.1.124/linux/drivers/net/3c523.c linux/drivers/net/3c523.c
--- v2.1.124/linux/drivers/net/3c523.c	Thu Sep 17 17:53:36 1998
+++ linux/drivers/net/3c523.c	Wed Oct  7 15:51:45 1998
@@ -1255,8 +1255,9 @@
 /*************************************************************************/
 
 #ifdef MODULE
+static char devicename[9] = { 0, };
 static struct device dev_elmc = {
-	"        " /*"3c523"*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, elmc_probe };
+	devicename /*"3c523"*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, elmc_probe };
 
 static int irq=0;
 static int io=0;
diff -u --recursive --new-file v2.1.124/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.124/linux/drivers/net/Config.in	Mon Oct  5 13:13:39 1998
+++ linux/drivers/net/Config.in	Wed Oct  7 15:51:45 1998
@@ -121,9 +121,11 @@
     tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       tristate 'Mylex EISA LNE390A/B support (EXPERIMENTAL)' CONFIG_LNE390
+      tristate 'Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)' CONFIG_NE3210
     fi
     tristate 'PCI NE2000 support' CONFIG_NE2K_PCI
     tristate 'TI ThunderLAN support' CONFIG_TLAN
+    tristate 'VIA Rhine support' CONFIG_VIA_RHINE
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       tristate 'Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210
       tristate 'SMC EtherPower II (EXPERIMENTAL)' CONFIG_EPIC100
@@ -141,6 +143,17 @@
 bool 'FDDI driver support' CONFIG_FDDI
 if [ "$CONFIG_FDDI" = "y" ]; then
   bool 'Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX
+fi
+
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI
+  if [ "$CONFIG_HIPPI" = "y" ]; then
+    bool 'CERN HIPPI PCI adapter support' CONFIG_CERN_HIPPI
+    bool 'Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER
+    if [ "$CONFIG_ROADRUNNER" != "n" ]; then
+      bool '  Use large TX/RX rings' CONFIG_ROADRUNNER_LARGE_RINGS
+    fi
+  fi
 fi
 
 tristate 'Frame relay DLCI support' CONFIG_DLCI
diff -u --recursive --new-file v2.1.124/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.124/linux/drivers/net/Makefile	Mon Oct  5 13:13:39 1998
+++ linux/drivers/net/Makefile	Wed Oct  7 15:51:45 1998
@@ -279,6 +279,16 @@
   endif
 endif
 
+ifeq ($(CONFIG_NE3210),y)
+L_OBJS += ne3210.o
+CONFIG_8390_BUILTIN = y
+else
+  ifeq ($(CONFIG_NE3210),m)
+  CONFIG_8390_MODULE = y
+  M_OBJS += ne3210.o
+  endif
+endif
+
 ifeq ($(CONFIG_PLIP),y)
 L_OBJS += plip.o
 else
@@ -526,6 +536,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_VIA_RHINE),y)
+L_OBJS += via-rhine.o
+else
+  ifeq ($(CONFIG_VIA_RHINE),m)
+  M_OBJS += via-rhine.o
+  endif
+endif
+
 ifeq ($(CONFIG_ZNET),y)
 L_OBJS += znet.o
 endif
@@ -963,6 +981,25 @@
 else
   ifeq ($(CONFIG_X25_ASY),m)
   M_OBJS += x25_asy.o
+  endif
+endif
+
+#
+# HIPPI adapters
+#
+ifeq ($(CONFIG_CERN_HIPPI),y)
+L_OBJS += cern_hippi.o
+else
+  ifeq ($(CONFIG_CERN_HIPPI),m)
+  M_OBJS += cern_hippi.o
+  endif
+endif
+
+ifeq ($(CONFIG_ROADRUNNER),y)
+L_OBJS += rrunner.o
+else
+  ifeq ($(CONFIG_ROADRUNNER),m)
+  M_OBJS += rrunner.o
   endif
 endif
 
diff -u --recursive --new-file v2.1.124/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.1.124/linux/drivers/net/Space.c	Mon Oct  5 13:13:39 1998
+++ linux/drivers/net/Space.c	Wed Oct  7 15:51:45 1998
@@ -72,6 +72,7 @@
 extern int ac3200_probe(struct device *);
 extern int es_probe(struct device *);
 extern int lne390_probe(struct device *);
+extern int ne3210_probe(struct device *);
 extern int e2100_probe(struct device *);
 extern int ni5010_probe(struct device *);
 extern int ni52_probe(struct device *);
@@ -122,6 +123,7 @@
 
 /* HIPPI boards */
 extern int cern_hippi_probe(struct device *);
+extern int rr_hippi_probe(struct device *);
 
 struct devprobe
 {
@@ -215,6 +217,9 @@
 #ifdef CONFIG_LNE390
 	{lne390_probe, 0},
 #endif
+#ifdef CONFIG_NE3210
+	{ne3210_probe, 0},
+#endif
 	{NULL, 0},
 };
 
@@ -515,6 +520,9 @@
 #ifdef CONFIG_CERN_HIPPI
 	    && cern_hippi_probe(dev)
 #endif
+#ifdef CONFIG_ROADRUNNER
+	    && rr_hippi_probe(dev)
+#endif
 	    && 1 ) {
 		return 1; /* -ENODEV or -EAGAIN would be more accurate. */
 	}
@@ -735,11 +743,11 @@
 
 #ifdef CONFIG_HIPPI
 	static struct device hip3_dev =
-		{"hip3", 0, 0, 0, 0, -1, 0, 0, 0, 0, NEXT_DEV, hippi_probe};
+		{"hip3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, hippi_probe};
 	static struct device hip2_dev =
-		{"hip2", 0, 0, 0, 0, -1, 0, 0, 0, 0, &hip3_dev, hippi_probe};
+		{"hip2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &hip3_dev, hippi_probe};
 	static struct device hip1_dev =
-		{"hip1", 0, 0, 0, 0, -1, 0, 0, 0, 0, &hip2_dev, hippi_probe};
+		{"hip1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &hip2_dev, hippi_probe};
 	static struct device hip0_dev =
 		{"hip0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &hip1_dev, hippi_probe};
 
diff -u --recursive --new-file v2.1.124/linux/drivers/net/ac3200.c linux/drivers/net/ac3200.c
--- v2.1.124/linux/drivers/net/ac3200.c	Tue Jun  9 11:57:29 1998
+++ linux/drivers/net/ac3200.c	Wed Oct  7 15:51:45 1998
@@ -205,10 +205,10 @@
 	 *  the card mem within the region covered by `normal' RAM  !!!
 	 */
 	if (dev->mem_start > 1024*1024) {	/* phys addr > 1MB */
-		if (dev->mem_start < (unsigned long)high_memory) {
+		if (dev->mem_start < virt_to_bus(high_memory)) {
 			printk(KERN_CRIT "ac3200.c: Card RAM overlaps with normal memory!!!\n");
 			printk(KERN_CRIT "ac3200.c: Use EISA SCU to set card memory below 1MB,\n");
-			printk(KERN_CRIT "ac3200.c: or to an address above %p.\n", high_memory);
+			printk(KERN_CRIT "ac3200.c: or to an address above 0x%lx.\n", virt_to_bus(high_memory));
 			printk(KERN_CRIT "ac3200.c: Driver NOT installed.\n");
 			free_irq(dev->irq, dev);
 			kfree(dev->priv);
@@ -225,6 +225,7 @@
 			dev->priv = NULL;
 			return EAGAIN;
 		}
+		ei_status.reg0 = 1;	/* Use as remap flag */
 		printk("ac3200.c: remapped %dkB card memory to virtual address %#lx\n",
 				AC_STOP_PG/4, dev->mem_start);
 	}
@@ -404,6 +405,8 @@
 			/* Someday free_irq may be in ac_close_card() */
 			free_irq(dev->irq, dev);
 			release_region(dev->base_addr, AC_IO_EXTENT);
+			if (ei_status.reg0)
+				iounmap((void *)dev->mem_start);
 			dev->priv = NULL;
 			unregister_netdev(dev);
 			kfree(priv);
diff -u --recursive --new-file v2.1.124/linux/drivers/net/atari_pamsnet.c linux/drivers/net/atari_pamsnet.c
--- v2.1.124/linux/drivers/net/atari_pamsnet.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/net/atari_pamsnet.c	Wed Oct  7 15:51:45 1998
@@ -864,9 +864,10 @@
 
 #ifdef MODULE
 
+static char devicename[9] = { 0, };
 static struct device pam_dev =
 	{
-		"        ",	/* filled in by register_netdev() */
+		devicename,	/* filled in by register_netdev() */
 		0, 0, 0, 0,	/* memory */
 		0, 0,		/* base, irq */
 		0, 0, 0, NULL, pamsnet_probe,
diff -u --recursive --new-file v2.1.124/linux/drivers/net/cops.c linux/drivers/net/cops.c
--- v2.1.124/linux/drivers/net/cops.c	Wed Jun 24 22:54:06 1998
+++ linux/drivers/net/cops.c	Wed Oct  7 15:51:45 1998
@@ -140,7 +140,7 @@
  *      N.B.
  *
  *      The Daystar Digital LT200 boards do not support interrupt-driven
- *      IO.  You must specify 'io=0xff' as a module parameter to invoke
+ *      IO.  You must specify 'irq=0xff' as a module parameter to invoke
  *      polled mode.  I also believe that the port probing logic is quite
  *      dangerous at best and certainly hopeless for a polled card.  Best to 
  *      specify both. - Steve H.
diff -u --recursive --new-file v2.1.124/linux/drivers/net/daynaport.c linux/drivers/net/daynaport.c
--- v2.1.124/linux/drivers/net/daynaport.c	Thu Sep 17 17:53:36 1998
+++ linux/drivers/net/daynaport.c	Mon Oct  5 13:39:20 1998
@@ -50,14 +50,14 @@
 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, const start_page);
+						   const unsigned char *buf, const int start_page);
 
 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,
 						  struct sk_buff *skb, int ring_offset);
 static void sane_block_output(struct device *dev, int count,
-							const unsigned char *buf, const start_page);
+						   const unsigned char *buf, const int start_page);
 
 static void slow_sane_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 						int ring_page);
diff -u --recursive --new-file v2.1.124/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.1.124/linux/drivers/net/de4x5.c	Sat Sep  5 16:46:40 1998
+++ linux/drivers/net/de4x5.c	Mon Oct  5 13:02:58 1998
@@ -409,11 +409,17 @@
 			  Fix lastPCI to correctly work with compiled in
 			   kernels and modules from bug report by 
 			   <Zlatko.Calusic@CARNet.hr> et al.
+      0.542  15-Sep-98    Fix dc2114x_autoconf() to stop multiple messages
+                           when media is unconnected.
+			  Change dev->interrupt to lp->interrupt to ensure
+			   alignment for Alpha's and avoid their unaligned
+			   access traps. This flag is merely for log messages:
+			   should do something more definitive though...
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:V0.541 1998/8/24 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.542 1998/9/15 davies@maniac.ultranet.com\n";
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -744,6 +750,7 @@
 
 struct de4x5_private {
     char adapter_name[80];                  /* Adapter name                 */
+    u_long interrupt;                       /* Aligned ISR flag             */
     struct de4x5_desc rx_ring[NUM_RX_DESC]; /* RX descriptor ring           */
     struct de4x5_desc tx_ring[NUM_TX_DESC]; /* TX descriptor ring           */
     struct sk_buff *tx_skb[NUM_TX_DESC];    /* TX skb for freeing when sent */
@@ -1362,7 +1369,7 @@
 
     dev->tbusy = 0;                         
     dev->start = 1;
-    dev->interrupt = UNMASK_INTERRUPTS;
+    lp->interrupt = UNMASK_INTERRUPTS;
     dev->trans_start = jiffies;
     
     START_DE4X5;
@@ -1506,12 +1513,12 @@
     sti();
 
     /* Test if cache is already locked - requeue skb if so */
-    if (test_and_set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt) 
+    if (test_and_set_bit(0, (void *)&lp->cache.lock) && !lp->interrupt) 
 	return -1;
 
     /* Transmit descriptor ring full or stale skb */
     if (dev->tbusy || lp->tx_skb[lp->tx_new]) {
-	if (dev->interrupt) {
+	if (lp->interrupt) {
 	    de4x5_putb_cache(dev, skb);          /* Requeue the buffer */
 	} else {
 	    de4x5_put_cache(dev, skb);
@@ -1521,7 +1528,7 @@
 	}
     } else if (skb->len > 0) {
 	/* If we already have stuff queued locally, use that first */
-	if (lp->cache.skb && !dev->interrupt) {
+	if (lp->cache.skb && !lp->interrupt) {
 	    de4x5_put_cache(dev, skb);
 	    skb = de4x5_get_cache(dev);
 	}
@@ -1576,10 +1583,11 @@
     lp = (struct de4x5_private *)dev->priv;
     iobase = dev->base_addr;
 	
-    if (test_and_set_bit(MASK_INTERRUPTS, (void*) &dev->interrupt))
-	printk("%s: Re-entering the interrupt handler.\n", dev->name);
-	
     DISABLE_IRQs;                        /* Ensure non re-entrancy */
+
+    if (test_and_set_bit(MASK_INTERRUPTS, (void*) &lp->interrupt))
+	printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
     synchronize_irq();
 	
     for (limit=0; limit<8; limit++) {
@@ -1618,7 +1626,7 @@
 	lp->cache.lock = 0;
     }
 
-    dev->interrupt = UNMASK_INTERRUPTS;
+    lp->interrupt = UNMASK_INTERRUPTS;
     ENABLE_IRQs;
     
     return;
@@ -1748,7 +1756,7 @@
 
     if (TX_BUFFS_AVAIL && dev->tbusy) {  /* Any resources available? */
 	dev->tbusy = 0;                  /* Clear TX busy flag */
-	if (dev->interrupt) mark_bh(NET_BH);
+	if (lp->interrupt) mark_bh(NET_BH);
     }
 	
     return 0;
@@ -2351,7 +2359,7 @@
     s32 imr;
     
     switch (lp->media) {
-      case INIT:
+    case INIT:
 	DISABLE_IRQs;
 	lp->tx_enable = NO;
 	lp->timeout = -1;
@@ -2369,36 +2377,36 @@
 	next_tick = dc21040_autoconf(dev);
 	break;
 	
-      case TP:
+    case TP:
 	next_tick = dc21040_state(dev, 0x8f01, 0xffff, 0x0000, 3000, BNC_AUI, 
 		                                         TP_SUSPECT, test_tp);
 	break;
 	
-      case TP_SUSPECT:
+    case TP_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21040_autoconf);
 	break;
 	
-      case BNC:
-      case AUI:
-      case BNC_AUI:
+    case BNC:
+    case AUI:
+    case BNC_AUI:
 	next_tick = dc21040_state(dev, 0x8f09, 0x0705, 0x0006, 3000, EXT_SIA, 
 		                                  BNC_AUI_SUSPECT, ping_media);
 	break;
 	
-      case BNC_AUI_SUSPECT:
+    case BNC_AUI_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, BNC_AUI, ping_media, dc21040_autoconf);
 	break;
 	
-      case EXT_SIA:
+    case EXT_SIA:
 	next_tick = dc21040_state(dev, 0x3041, 0x0000, 0x0006, 3000, 
 		                              NC, EXT_SIA_SUSPECT, ping_media);
 	break;
 	
-      case EXT_SIA_SUSPECT:
+    case EXT_SIA_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, EXT_SIA, ping_media, dc21040_autoconf);
 	break;
 	
-      case NC:
+    case NC:
 	/* default to TP for all */
 	reset_init_sia(dev, 0x8f01, 0xffff, 0x0000);
 	if (lp->media != lp->c_media) {
@@ -2423,13 +2431,13 @@
     int linkBad;
 
     switch (lp->local_state) {
-      case 0:
+    case 0:
 	reset_init_sia(dev, csr13, csr14, csr15);
 	lp->local_state++;
 	next_tick = 500;
 	break;
 	    
-      case 1:
+    case 1:
 	if (!lp->tx_enable) {
 	    linkBad = fn(dev, timeout);
 	    if (linkBad < 0) {
@@ -2462,7 +2470,7 @@
     int linkBad;
 
     switch (lp->local_state) {
-      case 1:
+    case 1:
 	if (lp->linkOK) {
 	    lp->media = prev_state;
 	} else {
@@ -2471,7 +2479,7 @@
 	}
 	break;
 
-      case 2:
+    case 2:
 	linkBad = fn(dev, timeout);
 	if (linkBad < 0) {
 	    next_tick = linkBad & ~TIMER_CB;
@@ -2505,7 +2513,7 @@
     int next_tick = DE4X5_AUTOSENSE_MS;
     
     switch (lp->media) {
-      case INIT:
+    case INIT:
 	DISABLE_IRQs;
 	lp->tx_enable = NO;
 	lp->timeout = -1;
@@ -2525,7 +2533,7 @@
 	next_tick = dc21041_autoconf(dev);
 	break;
 	
-      case TP_NW:
+    case TP_NW:
 	if (lp->timeout < 0) {
 	    omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */
 	    outl(omr | OMR_FDX, DE4X5_OMR);
@@ -2545,7 +2553,7 @@
 	}
 	break;
 	
-      case ANS:
+    case ANS:
 	if (!lp->tx_enable) {
 	    irqs = STS_LNP;
 	    irq_mask = IMR_LPM;
@@ -2567,11 +2575,11 @@
 	}
 	break;
 	
-      case ANS_SUSPECT:
+    case ANS_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, ANS, test_tp, dc21041_autoconf);
 	break;
 	
-      case TP:
+    case TP:
 	if (!lp->tx_enable) {
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for TP */
@@ -2601,11 +2609,11 @@
 	}
 	break;
 	
-      case TP_SUSPECT:
+    case TP_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21041_autoconf);
 	break;
 	
-      case AUI:
+    case AUI:
 	if (!lp->tx_enable) {
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for AUI */
@@ -2631,13 +2639,13 @@
 	}
 	break;
 	
-      case AUI_SUSPECT:
+    case AUI_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc21041_autoconf);
 	break;
 	
-      case BNC:
+    case BNC:
 	switch (lp->local_state) {
-	  case 0:
+	case 0:
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for BNC */
 		outl(omr & ~OMR_FDX, DE4X5_OMR);
@@ -2653,7 +2661,7 @@
 	    }
 	    break;
 	    
-	  case 1:
+	case 1:
 	    if (!lp->tx_enable) {
 		if ((sts = ping_media(dev, 3000)) < 0) {
 		    next_tick = sts & ~TIMER_CB;
@@ -2673,11 +2681,11 @@
 	}
 	break;
 	
-      case BNC_SUSPECT:
+    case BNC_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc21041_autoconf);
 	break;
 	
-      case NC:
+    case NC:
 	omr = inl(DE4X5_OMR);    /* Set up full duplex for the autonegotiate */
 	outl(omr | OMR_FDX, DE4X5_OMR);
 	reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */
@@ -3081,9 +3089,9 @@
 	      }
 	      next_tick = dc2114x_autoconf(dev);
 	  } else if (((lp->media == _100Mb) && is_100_up(dev)) ||
-	      ((lp->media == _10Mb)  && is_10_up(dev)) ||
-	       (lp->media == TP) ||
-	       (lp->media == BNC) || (lp->media == AUI)) {
+		     (((lp->media == _10Mb) || (lp->media == TP) ||
+		       (lp->media == BNC)   || (lp->media == AUI)) && 
+		      is_10_up(dev))) {
 	      next_tick = dc2114x_autoconf(dev);
 	  } else {
 	      lp->tcount++;
@@ -4049,7 +4057,7 @@
 	    if (data == dev.Sig[0]) {  /* rare case.... */
 		j=1;
 	    } else {
-	j=0;
+		j=0;
 	    }
 	}
     }
@@ -4121,8 +4129,10 @@
     srom_repair(dev, broken);
 
 #ifdef CONFIG_PMAC
-    /* If the address starts with 00 a0, we have to bit-reverse
-       each byte of the address. */
+    /* 
+    ** If the address starts with 00 a0, we have to bit-reverse
+    ** each byte of the address.
+    */
     if (dev->dev_addr[0] == 0 && dev->dev_addr[1] == 0xa0) {
 	for (i = 0; i < ETH_ALEN; ++i) {
 	    int x = dev->dev_addr[i];
diff -u --recursive --new-file v2.1.124/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- v2.1.124/linux/drivers/net/eexpress.c	Wed Jun 24 22:54:06 1998
+++ linux/drivers/net/eexpress.c	Wed Oct  7 15:51:45 1998
@@ -8,6 +8,11 @@
  *
  * Many modifications, and currently maintained, by
  *  Philip Blundell <Philip.Blundell@pobox.com>
+ * Added the Compaq LTE  Alan Cox <alan@redhat.com>
+ *
+ * Note - this driver is experimental still - it has problems on faster
+ * machines. Someone needs to sit down and go through it line by line with
+ * a databook...
  */
 
 /* The EtherExpress 16 is a fairly simple card, based on a shared-memory
@@ -326,7 +331,7 @@
 		if (sum==0xbaba && !eexp_hw_probe(dev,*port))
 			return 0;
 	}
-	return ENODEV;
+	return -ENODEV;
 }
 
 /*
@@ -931,11 +936,13 @@
 	hw_addr[1] = eexp_hw_readeeprom(ioaddr,3);
 	hw_addr[2] = eexp_hw_readeeprom(ioaddr,4);
 
-	if (hw_addr[2]!=0x00aa || ((hw_addr[1] & 0xff00)!=0x0000))
+	/* Standard Address or Compaq LTE Address */
+	if (!((hw_addr[2]==0x00aa && ((hw_addr[1] & 0xff00)==0x0000)) ||
+	      (hw_addr[2]==0x0080 && ((hw_addr[1] & 0xff00)==0x5F00)))) 
 	{
 		printk(" rejected: invalid address %04x%04x%04x\n",
 			hw_addr[2],hw_addr[1],hw_addr[0]);
-		return ENODEV;
+		return -ENODEV;
 	}
 
 	/* Calculate the EEPROM checksum.  Carry on anyway if it's bad,
diff -u --recursive --new-file v2.1.124/linux/drivers/net/ethertap.c linux/drivers/net/ethertap.c
--- v2.1.124/linux/drivers/net/ethertap.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/net/ethertap.c	Wed Oct  7 15:51:45 1998
@@ -7,7 +7,7 @@
  *	on it will attempt to ARP the user space and reply to ARPS from the
  *	user space.
  *
- *	As this is an ethernet device you cau use it for appletalk, IPX etc
+ *	As this is an ethernet device you can use it for appletalk, IPX etc
  *	even for building bridging tunnels.
  */
  
diff -u --recursive --new-file v2.1.124/linux/drivers/net/hp100.c linux/drivers/net/hp100.c
--- v2.1.124/linux/drivers/net/hp100.c	Sun Jun  7 11:16:32 1998
+++ linux/drivers/net/hp100.c	Wed Oct  7 15:54:44 1998
@@ -1144,7 +1144,7 @@
           
           /*
            * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and
-           * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded
+           * 4 bytes for header). We will leave NUM_RXPDLS * 508 (rounded
            * to the next higher 1k boundary) bytes for the rx-pdl's
 	   * Note: For non-etr chips the transmit stop register must be
 	   * programmed on a 1k boundary, i.e. bits 9:0 must be zero. 
diff -u --recursive --new-file v2.1.124/linux/drivers/net/lne390.c linux/drivers/net/lne390.c
--- v2.1.124/linux/drivers/net/lne390.c	Tue Jun  9 11:57:30 1998
+++ linux/drivers/net/lne390.c	Wed Oct  7 15:51:45 1998
@@ -194,7 +194,7 @@
 	}
 	printk(" IRQ %d,", dev->irq);
 
-	if (request_irq(dev->irq, ei_interrupt, 0, "lne390", NULL)) {
+	if (request_irq(dev->irq, ei_interrupt, 0, "lne390", dev)) {
 		printk (" unable to get IRQ %d.\n", dev->irq);
 		kfree(dev->priv);
 		dev->priv = NULL;
@@ -223,10 +223,10 @@
 	   the card mem within the region covered by `normal' RAM  !!!
 	*/
 	if (dev->mem_start > 1024*1024) {	/* phys addr > 1MB */
-		if (dev->mem_start < (unsigned long)high_memory) {
+		if (dev->mem_start < virt_to_bus(high_memory)) {
 			printk(KERN_CRIT "lne390.c: Card RAM overlaps with normal memory!!!\n");
 			printk(KERN_CRIT "lne390.c: Use EISA SCU to set card memory below 1MB,\n");
-			printk(KERN_CRIT "lne390.c: or to an address above %p.\n", high_memory);
+			printk(KERN_CRIT "lne390.c: or to an address above 0x%lx.\n", virt_to_bus(high_memory));
 			printk(KERN_CRIT "lne390.c: Driver NOT installed.\n");
 			free_irq(dev->irq, dev);
 			kfree(dev->priv);
@@ -243,6 +243,7 @@
 			dev->priv = NULL;
 			return EAGAIN;
 		}
+		ei_status.reg0 = 1;	/* Use as remap flag */
 		printk("lne390.c: remapped %dkB card memory to virtual address %#lx\n",
 				LNE390_STOP_PG/4, dev->mem_start);
 	}
@@ -427,6 +428,8 @@
 			void *priv = dev->priv;
 			free_irq(dev->irq, dev);
 			release_region(dev->base_addr, LNE390_IO_EXTENT);
+			if (ei_status.reg0)
+				iounmap((void *)dev->mem_start);
 			dev->priv = NULL;
 			unregister_netdev(dev);
 			kfree(priv);
diff -u --recursive --new-file v2.1.124/linux/drivers/net/ne3210.c linux/drivers/net/ne3210.c
--- v2.1.124/linux/drivers/net/ne3210.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/ne3210.c	Wed Oct  7 15:51:45 1998
@@ -0,0 +1,432 @@
+/*
+	ne3210.c
+
+	Linux driver for Novell NE3210 EISA Network Adapter
+
+	Copyright (C) 1998, Paul Gortmaker.
+
+	This software may be used and distributed according to the terms
+	of the GNU Public License, incorporated herein by reference.
+
+	Information and Code Sources:
+
+	1) Based upon my other EISA 8390 drivers (lne390, es3210, smc-ultra32)
+	2) The existing myriad of other Linux 8390 drivers by Donald Becker.
+	3) Info for getting IRQ and sh-mem gleaned from the EISA cfg file
+
+	The NE3210 is an EISA shared memory NS8390 implementation.  Shared 
+	memory address > 1MB should work with this driver.
+
+	Note that the .cfg file (3/11/93, v1.0) has AUI and BNC switched 
+	around (or perhaps there are some defective/backwards cards ???)
+
+	This driver WILL NOT WORK FOR THE NE3200 - it is completely different
+	and does not use an 8390 at all.
+
+*/
+
+static const char *version =
+	"ne3210.c: Driver revision v0.03, 30/09/98\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+
+int ne3210_probe(struct device *dev);
+int ne3210_probe1(struct device *dev, int ioaddr);
+
+static int ne3210_open(struct device *dev);
+static int ne3210_close(struct device *dev);
+
+static void ne3210_reset_8390(struct device *dev);
+
+static void ne3210_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page);
+static void ne3210_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset);
+static void ne3210_block_output(struct device *dev, int count, const unsigned char *buf, const int start_page);
+
+#define NE3210_START_PG		0x00    /* First page of TX buffer	*/
+#define NE3210_STOP_PG		0x80    /* Last page +1 of RX ring	*/
+
+#define NE3210_ID_PORT		0xc80	/* Same for all EISA cards 	*/
+#define NE3210_IO_EXTENT	0x20
+#define NE3210_SA_PROM		0x16	/* Start of e'net addr.		*/
+#define NE3210_RESET_PORT	0xc84
+#define NE3210_NIC_OFFSET	0x00	/* Hello, the 8390 is *here*	*/
+
+#define NE3210_ADDR0		0x00	/* 3 byte vendor prefix		*/
+#define NE3210_ADDR1		0x00
+#define NE3210_ADDR2		0x1b
+
+#define NE3210_ID	0x0118cc3a	/* 0x3acc = 1110 10110 01100 =  nvl */
+
+#define NE3210_CFG1		0xc84	/* NB: 0xc84 is also "reset" port. */
+#define NE3210_CFG2		0xc90
+
+/*
+ *	You can OR any of the following bits together and assign it
+ *	to NE3210_DEBUG to get verbose driver info during operation.
+ *	Currently only the probe one is implemented.
+ */
+
+#define NE3210_D_PROBE	0x01
+#define NE3210_D_RX_PKT	0x02
+#define NE3210_D_TX_PKT	0x04
+#define NE3210_D_IRQ	0x08
+
+#define NE3210_DEBUG	0x0
+
+static unsigned char irq_map[] __initdata = {15, 12, 11, 10, 9, 7, 5, 3};
+static unsigned int shmem_map[] __initdata = {0xff0, 0xfe0, 0xfff0, 0xd8, 0xffe0, 0xffc0, 0xd0, 0x0};
+
+/*
+ *	Probe for the card. The best way is to read the EISA ID if it
+ *	is known. Then we can check the prefix of the station address
+ *	PROM for a match against the value assigned to Novell.
+ */
+
+__initfunc(int ne3210_probe(struct device *dev))
+{
+	unsigned short ioaddr = dev->base_addr;
+
+	if (ioaddr > 0x1ff)		/* Check a single specified location. */
+		return ne3210_probe1(dev, ioaddr);
+	else if (ioaddr > 0)		/* Don't probe at all. */
+		return ENXIO;
+
+	if (!EISA_bus) {
+#if NE3210_DEBUG & NE3210_D_PROBE
+		printk("ne3210-debug: Not an EISA bus. Not probing high ports.\n");
+#endif
+		return ENXIO;
+	}
+
+	/* EISA spec allows for up to 16 slots, but 8 is typical. */
+	for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+		if (check_region(ioaddr , NE3210_IO_EXTENT))
+			continue;
+		if (ne3210_probe1(dev, ioaddr) == 0)
+			return 0;
+	}
+
+	return ENODEV;
+}
+
+__initfunc(int ne3210_probe1(struct device *dev, int ioaddr))
+{
+	int i;
+	unsigned long eisa_id;
+	const char *ifmap[] = {"UTP", "?", "BNC", "AUI"};
+
+	if (inb_p(ioaddr + NE3210_ID_PORT) == 0xff) return -ENODEV;
+
+#if NE3210_DEBUG & NE3210_D_PROBE
+	printk("ne3210-debug: probe at %#x, ID %#8x\n", ioaddr, inl(ioaddr + NE3210_ID_PORT));
+	printk("ne3210-debug: config regs: %#x %#x\n",
+		inb(ioaddr + NE3210_CFG1), inb(ioaddr + NE3210_CFG2));
+#endif
+
+
+/*	Check the EISA ID of the card. */
+	eisa_id = inl(ioaddr + NE3210_ID_PORT);
+	if (eisa_id != NE3210_ID) {
+		return ENODEV;
+	}
+
+	
+#if 0
+/*	Check the vendor ID as well. Not really required. */
+	if (inb(ioaddr + NE3210_SA_PROM + 0) != NE3210_ADDR0
+		|| inb(ioaddr + NE3210_SA_PROM + 1) != NE3210_ADDR1
+		|| inb(ioaddr + NE3210_SA_PROM + 2) != NE3210_ADDR2 ) {
+		printk("ne3210.c: card not found");
+		for(i = 0; i < ETHER_ADDR_LEN; i++)
+			printk(" %02x", inb(ioaddr + NE3210_SA_PROM + i));
+		printk(" (invalid prefix).\n");
+		return ENODEV;
+	}
+#endif
+
+	if (load_8390_module("ne3210.c"))
+		return -ENOSYS;
+
+	/* We should have a "dev" from Space.c or the static module table. */
+	if (dev == NULL) {
+		printk("ne3210.c: Passed a NULL device.\n");
+		dev = init_etherdev(0, 0);
+	}
+
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk ("ne3210.c: unable to allocate memory for dev->priv!\n");
+		return -ENOMEM;
+	}
+
+	printk("ne3210.c: NE3210 in EISA slot %d, media: %s, addr:",
+		ioaddr/0x1000, ifmap[inb(ioaddr + NE3210_CFG2) >> 6]);
+	for(i = 0; i < ETHER_ADDR_LEN; i++)
+		printk(" %02x", (dev->dev_addr[i] = inb(ioaddr + NE3210_SA_PROM + i)));
+	printk(".\nne3210.c: ");
+
+	/* Snarf the interrupt now. CFG file has them all listed as `edge' with share=NO */
+	if (dev->irq == 0) {
+		unsigned char irq_reg = inb(ioaddr + NE3210_CFG2) >> 3;
+		dev->irq = irq_map[irq_reg & 0x07];
+		printk("using");
+	} else {
+		/* This is useless unless we reprogram the card here too */
+		if (dev->irq == 2) dev->irq = 9;	/* Doh! */
+		printk("assigning");
+	}
+	printk(" IRQ %d,", dev->irq);
+
+	if (request_irq(dev->irq, ei_interrupt, 0, "ne3210", dev)) {
+		printk (" unable to get IRQ %d.\n", dev->irq);
+		kfree(dev->priv);
+		dev->priv = NULL;
+		return EAGAIN;
+	}
+
+	if (dev->mem_start == 0) {
+		unsigned char mem_reg = inb(ioaddr + NE3210_CFG2) & 0x07;
+		dev->mem_start = shmem_map[mem_reg] * 0x1000;
+		printk(" using ");
+	} else {
+		/* Should check for value in shmem_map and reprogram the card to use it */
+		dev->mem_start &= 0xfff8000;
+		printk(" assigning ");
+	}
+
+	printk("%dkB memory at physical address %#lx\n",
+			NE3210_STOP_PG/4, dev->mem_start);
+
+	/*
+	   BEWARE!! Some dain-bramaged EISA SCUs will allow you to put
+	   the card mem within the region covered by `normal' RAM  !!!
+	*/
+	if (dev->mem_start > 1024*1024) {	/* phys addr > 1MB */
+		if (dev->mem_start < virt_to_bus(high_memory)) {
+			printk(KERN_CRIT "ne3210.c: Card RAM overlaps with normal memory!!!\n");
+			printk(KERN_CRIT "ne3210.c: Use EISA SCU to set card memory below 1MB,\n");
+			printk(KERN_CRIT "ne3210.c: or to an address above 0x%lx.\n", virt_to_bus(high_memory));
+			printk(KERN_CRIT "ne3210.c: Driver NOT installed.\n");
+			free_irq(dev->irq, dev);
+			kfree(dev->priv);
+			dev->priv = NULL;
+			return EINVAL;
+		}
+		dev->mem_start = (unsigned long)ioremap(dev->mem_start, NE3210_STOP_PG*0x100);
+		if (dev->mem_start == 0) {
+			printk(KERN_ERR "ne3210.c: Unable to remap card memory above 1MB !!\n");
+			printk(KERN_ERR "ne3210.c: Try using EISA SCU to set memory below 1MB.\n");
+			printk(KERN_ERR "ne3210.c: Driver NOT installed.\n");
+			free_irq(dev->irq, dev);
+			kfree(dev->priv);
+			dev->priv = NULL;
+			return EAGAIN;
+		}
+		ei_status.reg0 = 1;	/* Use as remap flag */
+		printk("ne3210.c: remapped %dkB card memory to virtual address %#lx\n",
+				NE3210_STOP_PG/4, dev->mem_start);
+	}
+
+	dev->mem_end = dev->rmem_end = dev->mem_start
+		+ (NE3210_STOP_PG - NE3210_START_PG)*256;
+	dev->rmem_start = dev->mem_start + TX_PAGES*256;
+
+	/* The 8390 offset is zero for the NE3210 */
+	dev->base_addr = ioaddr;
+	request_region(dev->base_addr, NE3210_IO_EXTENT, "ne3210");
+
+	ei_status.name = "NE3210";
+	ei_status.tx_start_page = NE3210_START_PG;
+	ei_status.rx_start_page = NE3210_START_PG + TX_PAGES;
+	ei_status.stop_page = NE3210_STOP_PG;
+	ei_status.word16 = 1;
+
+	if (ei_debug > 0)
+		printk(version);
+
+	ei_status.reset_8390 = &ne3210_reset_8390;
+	ei_status.block_input = &ne3210_block_input;
+	ei_status.block_output = &ne3210_block_output;
+	ei_status.get_8390_hdr = &ne3210_get_8390_hdr;
+
+	dev->open = &ne3210_open;
+	dev->stop = &ne3210_close;
+	NS8390_init(dev, 0);
+	return 0;
+}
+
+/*
+ *	Reset by toggling the "Board Enable" bits (bit 2 and 0).
+ */
+
+static void ne3210_reset_8390(struct device *dev)
+{
+	unsigned short ioaddr = dev->base_addr;
+
+	outb(0x04, ioaddr + NE3210_RESET_PORT);
+	if (ei_debug > 1) printk("%s: resetting the NE3210...", dev->name);
+
+	mdelay(2);
+
+	ei_status.txing = 0;
+	outb(0x01, ioaddr + NE3210_RESET_PORT);
+	if (ei_debug > 1) printk("reset done\n");
+
+	return;
+}
+
+/*
+ *	Note: In the following three functions is the implicit assumption
+ *	that the associated memcpy will only use "rep; movsl" as long as
+ *	we keep the counts as some multiple of doublewords. This is a
+ *	requirement of the hardware, and also prevents us from using
+ *	eth_io_copy_and_sum() since we can't guarantee it will limit
+ *	itself to doubleword access.
+ */
+
+/*
+ *	Grab the 8390 specific header. Similar to the block_input routine, but
+ *	we don't need to be concerned with ring wrap as the header will be at
+ *	the start of a page, so we optimize accordingly. (A single doubleword.)
+ */
+
+static void
+ne3210_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	unsigned long hdr_start = dev->mem_start + ((ring_page - NE3210_START_PG)<<8);
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+	hdr->count = (hdr->count + 3) & ~3;     /* Round up allocation. */
+}
+
+/*	
+ *	Block input and output are easy on shared memory ethercards, the only
+ *	complication is when the ring buffer wraps. The count will already
+ *	be rounded up to a doubleword value via ne3210_get_8390_hdr() above.
+ */
+
+static void ne3210_block_input(struct device *dev, int count, struct sk_buff *skb,
+						  int ring_offset)
+{
+	unsigned long xfer_start = dev->mem_start + ring_offset - (NE3210_START_PG<<8);
+
+	if (xfer_start + count > dev->rmem_end) {
+		/* Packet wraps over end of ring buffer. */
+		int semi_count = dev->rmem_end - xfer_start;
+		memcpy_fromio(skb->data, xfer_start, semi_count);
+		count -= semi_count;
+		memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+	} else {
+		/* Packet is in one chunk. */
+		memcpy_fromio(skb->data, xfer_start, count);
+	}
+}
+
+static void ne3210_block_output(struct device *dev, int count,
+				const unsigned char *buf, int start_page)
+{
+	unsigned long shmem = dev->mem_start + ((start_page - NE3210_START_PG)<<8);
+
+	count = (count + 3) & ~3;     /* Round up to doubleword */
+	memcpy_toio(shmem, buf, count);
+}
+
+static int ne3210_open(struct device *dev)
+{
+	ei_open(dev);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int ne3210_close(struct device *dev)
+{
+
+	if (ei_debug > 1)
+		printk("%s: Shutting down ethercard.\n", dev->name);
+
+	ei_close(dev);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+#ifdef MODULE
+#define MAX_NE3210_CARDS	4	/* Max number of NE3210 cards per module */
+#define NAMELEN			8	/* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_NE3210_CARDS] = { 0, };
+static struct device dev_ne3210[MAX_NE3210_CARDS] = {
+	{
+		NULL,		/* assign a chunk of namelist[] below */
+		0, 0, 0, 0,
+		0, 0,
+		0, 0, 0, NULL, NULL
+	},
+};
+
+static int io[MAX_NE3210_CARDS] = { 0, };
+static int irq[MAX_NE3210_CARDS]  = { 0, };
+static int mem[MAX_NE3210_CARDS] = { 0, };
+
+MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NE3210_CARDS) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_NE3210_CARDS) "i");
+MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_NE3210_CARDS) "i");
+
+int init_module(void)
+{
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_NE3210_CARDS; this_dev++) {
+		struct device *dev = &dev_ne3210[this_dev];
+		dev->name = namelist+(NAMELEN*this_dev);
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->mem_start = mem[this_dev];
+		dev->init = ne3210_probe;
+		/* Default is to only install one card. */
+		if (io[this_dev] == 0 && this_dev != 0) break;
+		if (register_netdev(dev) != 0) {
+			printk(KERN_WARNING "ne3210.c: No NE3210 card found (i/o = 0x%x).\n", io[this_dev]);
+			if (found != 0) {	/* Got at least one. */
+				lock_8390_module();
+				return 0;
+			}
+			return -ENXIO;
+		}
+		found++;
+	}
+	lock_8390_module();
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_NE3210_CARDS; this_dev++) {
+		struct device *dev = &dev_ne3210[this_dev];
+		if (dev->priv != NULL) {
+			void *priv = dev->priv;
+			free_irq(dev->irq, dev);
+			release_region(dev->base_addr, NE3210_IO_EXTENT);
+			if (ei_status.reg0)
+				iounmap((void *)dev->mem_start);
+			dev->priv = NULL;
+			unregister_netdev(dev);
+			kfree(priv);
+		}
+	}
+	unlock_8390_module();
+}
+#endif /* MODULE */
+
diff -u --recursive --new-file v2.1.124/linux/drivers/net/ni5010.c linux/drivers/net/ni5010.c
--- v2.1.124/linux/drivers/net/ni5010.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/ni5010.c	Wed Oct  7 15:51:45 1998
@@ -771,8 +771,9 @@
 }
 
 #ifdef MODULE
+static char devicename[9] = { 0, };
 static struct device dev_ni5010 = {
-        "          ",
+        devicename,
         0, 0, 0, 0,
         0, 0,
         0, 0, 0, NULL, ni5010_probe };
diff -u --recursive --new-file v2.1.124/linux/drivers/net/ni52.c linux/drivers/net/ni52.c
--- v2.1.124/linux/drivers/net/ni52.c	Wed May 20 19:10:39 1998
+++ linux/drivers/net/ni52.c	Wed Oct  7 15:51:45 1998
@@ -1303,8 +1303,9 @@
 }
 
 #ifdef MODULE
+static char devicename[9] = { 0, };
 static struct device dev_ni52 = {
-	"				",	/* "ni5210": device name inserted by net_init.c */
+	devicename,	/* "ni5210": device name inserted by net_init.c */
 	0, 0, 0, 0,
 	0x300, 9,	 /* I/O address, IRQ */
 	0, 0, 0, NULL, ni52_probe };
diff -u --recursive --new-file v2.1.124/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v2.1.124/linux/drivers/net/ppp.c	Mon Sep 28 10:51:33 1998
+++ linux/drivers/net/ppp.c	Tue Oct  6 09:39:49 1998
@@ -4,7 +4,7 @@
  *  Al Longyear <longyear@netcom.com>
  *  Extensively rewritten by Paul Mackerras <paulus@cs.anu.edu.au>
  *
- *  ==FILEVERSION 980704==
+ *  ==FILEVERSION 981004==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the number above to the
@@ -115,6 +115,7 @@
 static void ppp_output_wakeup(struct ppp *ppp);
 static void ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb);
 static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
+static void ppp_send_frames(struct ppp *ppp);
 static struct sk_buff *ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb);
 
 static struct ppp *ppp_find (int pid_value);
@@ -439,6 +440,8 @@
 		return;
 	if (ppp->backup_tty) {
 		ppp->tty = ppp->backup_tty;
+		if (ppp_tty_push(ppp))
+			ppp_output_wakeup(ppp);
 	} else {
 		ppp->tty = 0;
 		ppp->sc_xfer = 0;
@@ -448,7 +451,6 @@
 
 		ppp_async_release(ppp);
 		ppp_release(ppp);
-		ppp->inuse = 0;
 		MOD_DEC_USE_COUNT;
 	}
 }
@@ -1289,6 +1291,9 @@
 
 	CHECK_PPP_MAGIC(ppp);
 
+	/* ppp_dev_close may be called with tbusy==1 so we must set it to 0 */
+	dev->tbusy=0;
+
 	MOD_DEC_USE_COUNT;
 
 	return 0;
@@ -2113,8 +2118,8 @@
 
 /*
  * Compress and send an frame to the peer.
- * Should be called with dev->tbusy == 1, having been set by the caller.
- * That is, we use dev->tbusy as a lock to prevent reentry of this
+ * Should be called with xmit_busy == 1, having been set by the caller.
+ * That is, we use xmit_busy as a lock to prevent reentry of this
  * procedure.
  */
 static void
@@ -2186,7 +2191,7 @@
 		if (new_skb == NULL) {
 			printk(KERN_ERR "ppp_send_frame: no memory\n");
 			kfree_skb(skb);
-			ppp->dev.tbusy = 0;
+			ppp->xmit_busy = 0;
 			return;
 		}
 
@@ -2215,9 +2220,9 @@
 	ret = ppp_async_send(ppp, skb);
 	if (ret > 0) {
 		/* we can release the lock */
-		ppp->dev.tbusy = 0;
+		ppp->xmit_busy = 0;
 	} else if (ret < 0) {
-		/* this can't happen, since the caller got the tbusy lock */
+		/* can't happen, since the caller got the xmit_busy lock */
 		printk(KERN_ERR "ppp: ppp_async_send didn't accept pkt\n");
 	}
 }
@@ -2276,15 +2281,18 @@
 {
 	struct sk_buff *skb;
 
-	while (!test_and_set_bit(0, &ppp->dev.tbusy)) {
+	while (!test_and_set_bit(0, &ppp->xmit_busy)) {
 		skb = skb_dequeue(&ppp->xmt_q);
 		if (skb == NULL) {
-			ppp->dev.tbusy = 0;
-			mark_bh(NET_BH);
+			ppp->xmit_busy = 0;
 			break;
 		}
 		ppp_send_frame(ppp, skb);
 	}
+	if (!ppp->xmit_busy && ppp->dev.tbusy) {
+		ppp->dev.tbusy = 0;
+		mark_bh(NET_BH);
+	}
 }
 
 /*
@@ -2296,11 +2304,11 @@
 {
 	CHECK_PPP_VOID();
 
-	if (!ppp->dev.tbusy) {
-		printk(KERN_ERR "ppp_output_wakeup called but tbusy==0\n");
+	if (!ppp->xmit_busy) {
+		printk(KERN_ERR "ppp_output_wakeup called but xmit_busy==0\n");
 		return;
 	}
-	ppp->dev.tbusy = 0;
+	ppp->xmit_busy = 0;
 	ppp_send_frames(ppp);
 }
 
@@ -2425,9 +2433,15 @@
 	 * The dev->tbusy field acts as a lock to allow only
 	 * one packet to be processed at a time.  If we can't
 	 * get the lock, try again later.
+	 * We deliberately queue as little as possible inside
+	 * the ppp driver in order to minimize the latency
+	 * for high-priority packets.
 	 */
-	if (test_and_set_bit(0, &dev->tbusy))
+	if (test_and_set_bit(0, &ppp->xmit_busy)) {
+		dev->tbusy = 1;	/* can't take it now */
 		return 1;
+	}
+	dev->tbusy = 0;
 
 	/*
 	 * Put the 4-byte PPP header on the packet.
@@ -2441,7 +2455,8 @@
 			printk(KERN_ERR "%s: skb hdr alloc failed\n",
 			       ppp->name);
 			dev_kfree_skb(skb);
-			dev->tbusy = 0;
+			ppp->xmit_busy = 0;
+			ppp_send_frames(ppp);
 			return 0;
 		}
 		skb_reserve(new_skb, PPP_HDRLEN);
@@ -2457,6 +2472,8 @@
 	hdr[3] = proto;
 
 	ppp_send_frame(ppp, skb);
+	if (!ppp->xmit_busy)
+		ppp_send_frames(ppp);
 	return 0;
 }
 
@@ -2600,6 +2617,7 @@
 
 	ppp->last_xmit	= jiffies;
 	ppp->last_recv  = jiffies;
+	ppp->xmit_busy  = 0;
 
 	/* clear statistics */
 	memset(&ppp->stats, 0, sizeof (struct pppstat));
@@ -2640,6 +2658,12 @@
 		kfree_skb(skb);
 	while ((skb = skb_dequeue(&ppp->xmt_q)) != NULL)
 		kfree_skb(skb);
+
+	ppp->inuse = 0;
+	if (ppp->dev.tbusy) {
+		ppp->dev.tbusy = 0;
+		mark_bh(NET_BH);
+	}
 }
 
 /*
diff -u --recursive --new-file v2.1.124/linux/drivers/net/rrunner.c linux/drivers/net/rrunner.c
--- v2.1.124/linux/drivers/net/rrunner.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/rrunner.c	Wed Oct  7 11:13:49 1998
@@ -0,0 +1,1208 @@
+/*
+ * rrunner.c: Linux driver for the Essential RoadRunner HIPPI board.
+ *
+ * Written 1998 by Jes Sorensen, <Jes.Sorensen@cern.ch>.
+ *
+ * Thanks to Essential Communication for providing us with hardware
+ * and very comprehensive documentation without which I would not have
+ * been able to write this driver. A special thank you to John Gibbon
+ * for sorting out the legal issues, with the NDA, allowing the code to
+ * be released under the GPL.
+ *
+ * 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.
+ */
+
+#define DEBUG 1
+#define RX_DMA_SKBUFF 1
+#define PKT_COPY_THRESHOLD 512
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/hippidevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/cache.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "rrunner.h"
+
+/*
+ * Implementation notes:
+ *
+ * The DMA engine only allows for DMA within physical 64KB chunks of
+ * memory. The current approach of the driver (and stack) is to use
+ * linear blocks of memory for the skbuffs. However, as the data block
+ * is always the first part of the skb and skbs are 2^n aligned so we
+ * are guarantted to get the whole block within one 64KB align 64KB
+ * chunk.
+ *
+ * On the long term, relying on being able to allocate 64KB linear
+ * chunks of memory is not feasible and the skb handling code and the
+ * stack will need to know about I/O vectors or something similar.
+ */
+
+static const char *version = "rrunner.c: v0.06 09/02/98  Jes Sorensen (Jes.Sorensen@cern.ch)\n";
+
+static unsigned int read_eeprom(struct rr_private *rrpriv,
+				unsigned long offset,
+				unsigned char *buf,
+				unsigned long length);
+static u32 read_eeprom_word(struct rr_private *rrpriv,
+			    void * offset);
+static int rr_load_firmware(struct device *dev);
+
+
+__initfunc(int rr_hippi_probe (struct device *dev))
+{
+	static int i = 0;
+	int boards_found = 0;
+	int version_disp;	/* was version info already displayed? */
+	u8 pci_bus;		/* PCI bus number (0-255) */
+	u8 pci_dev_fun;		/* PCI device and function numbers (0-255) */
+	u8 pci_latency;
+	u16 command;		/* PCI Configuration space Command register */
+	unsigned int tmp;
+	u8 irq;
+	struct rr_private *rrpriv;
+
+	if (!pci_present())		/* is PCI BIOS even present? */
+		return -ENODEV;
+
+	version_disp = 0;
+
+	for (; i < 255; i++)
+	{
+		if (pcibios_find_device(PCI_VENDOR_ID_ESSENTIAL,
+					PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER,
+					i, &pci_bus, &pci_dev_fun) != 0)
+			break;
+
+		pcibios_read_config_word(pci_bus, pci_dev_fun,
+					 PCI_COMMAND, &command);
+
+		/* Enable mastering */
+
+		command |= PCI_COMMAND_MASTER;
+		pcibios_write_config_word(pci_bus, pci_dev_fun,
+					  PCI_COMMAND, command);
+
+		if (!(command & PCI_COMMAND_MEMORY)){
+			printk("shared mem not enabled - unable to configure RoadRunner\n");
+			break;
+		}
+
+		/*
+		 * So we found our HIPPI ... time to tell the system.
+		 */
+
+		dev = init_hippi_dev(dev, sizeof(struct rr_private));
+
+		if (dev == NULL)
+			break;
+
+		rrpriv = (struct rr_private *)dev->priv;
+
+		/* Read register base address from
+		   PCI Configuration Space */
+
+		pcibios_read_config_dword(pci_bus, pci_dev_fun,
+					  PCI_BASE_ADDRESS_0, &tmp);
+
+		pcibios_read_config_byte(pci_bus, pci_dev_fun,
+					 PCI_INTERRUPT_LINE, &irq);
+
+		dev->irq = irq;
+		rrpriv->pci_bus = pci_bus;
+		rrpriv->pci_dev_fun = pci_dev_fun;
+		sprintf(rrpriv->name, "RoadRunner serial HIPPI");
+#ifdef __SMP__
+		spin_lock_init(&rrpriv->lock);
+#endif
+
+		dev->open = &rr_open;
+		dev->hard_start_xmit = &rr_start_xmit;
+		dev->stop = &rr_close;
+		dev->get_stats = &rr_get_stats;
+		dev->do_ioctl = &rr_ioctl;
+
+		/*
+		 * Dummy value.
+		 */
+		dev->base_addr = 42;
+
+		/* display version info if adapter is found */
+		if (!version_disp)
+		{
+			/* set display flag to TRUE so that */
+			/* we only display this string ONCE */
+			version_disp = 1;
+			printk(version);
+		}
+
+		printk(KERN_INFO "%s: Essential RoadRunner serial HIPPI at 0x%08x, irq %i\n",
+		       dev->name, tmp, dev->irq);
+
+		pcibios_read_config_byte(pci_bus, pci_dev_fun,
+					 PCI_LATENCY_TIMER, &pci_latency);
+#if 0
+		if (pci_latency <= 48){
+			printk("  PCI latency counter too low (%i), setting to 48 clocks\n", pci_latency);
+			pcibios_write_config_byte(pci_bus, pci_dev_fun,
+						  PCI_LATENCY_TIMER, 48);
+		}
+#else
+		if (pci_latency <= 0x58)
+			pcibios_write_config_byte(pci_bus, pci_dev_fun,
+						  PCI_LATENCY_TIMER, 0x58);
+#endif
+		/*
+		 * Remap the regs into kernel space.
+		 */
+
+		rrpriv->regs = (struct rr_regs *)ioremap(tmp, 0x1000);
+		if (!rrpriv->regs){
+			printk(KERN_ERR "%s:  Unable to map I/O register, RoadRunner %i will be disabled.\n", dev->name, i);
+			break;
+		}
+
+		/*
+		 * Don't access any registes before this point!
+		 */
+#ifdef __BIG_ENDIAN
+		regs->HostCtrl |= NO_SWAP;
+#endif
+		/*
+		 * Need to add a case for little-endian 64-bit hosts here.
+		 */
+
+		rr_init(dev);
+
+		boards_found++;
+
+		/*
+		 * This is bollocks, but we need to tell the net-init
+		 * code that it shall go for the next device.
+		 */
+		dev->base_addr = 0;
+	}
+
+	/*
+	 * If we're at this point we're going through rr_hippi_probe()
+	 * for the first time.  Return success (0) if we've initialized
+	 * 1 or more boards. Otherwise, return failure (-ENODEV).
+	 */
+
+	if (boards_found > 0)
+		return 0;
+	else
+		return -ENODEV;
+}
+
+
+/*
+ * Commands are considered to be slow, thus there is no reason to
+ * inline this.
+ */
+static void rr_issue_cmd(struct rr_private *rrpriv, struct cmd *cmd)
+{
+	struct rr_regs *regs;
+	u32 idx;
+
+	regs = rrpriv->regs;
+	/*
+	 * This is temporary - it will go away in the final version.
+	 * We probably also want to make this function inline.
+	 */
+	if (regs->HostCtrl & NIC_HALTED){
+		printk("issuing command for halted NIC, code 0x%x, HostCtrl %08x\n", cmd->code, regs->HostCtrl);
+		if (regs->Mode & FATAL_ERR)
+			printk("error code %02x\n", regs->Fail1);
+	}
+
+	idx = rrpriv->info->cmd_ctrl.pi;
+
+	regs->CmdRing[idx] = *(u32*)(cmd);
+
+	idx = (idx - 1) % CMD_RING_ENTRIES;
+	rrpriv->info->cmd_ctrl.pi = idx;
+
+	if (regs->Mode & FATAL_ERR)
+		printk("error code %02x\n", regs->Fail1);
+}
+
+
+/*
+ * Reset the board in a sensible manner. The NIC is already halted
+ * when we get here and a spin-lock is held.
+ */
+static int rr_reset(struct device *dev)
+{
+	struct rr_private *rrpriv;
+	struct rr_regs *regs;
+	struct eeprom *hw = NULL;
+	u32 start_pc;
+	int i;
+
+	rrpriv = (struct rr_private *)dev->priv;
+	regs = rrpriv->regs;
+
+	rr_load_firmware(dev);
+
+	regs->TX_state = 0x01000000;
+	regs->RX_state = 0xff800000;
+	regs->AssistState = 0;
+	regs->LocalCtrl = CLEAR_INTA;
+	regs->BrkPt = 0x01;
+	regs->Timer = 0;
+	regs->TimerRef = 0;
+	regs->DmaReadState = RESET_DMA;
+	regs->DmaWriteState = RESET_DMA;
+	regs->DmaWriteHostHi = 0;
+	regs->DmaWriteHostLo = 0;
+	regs->DmaReadHostHi = 0;
+	regs->DmaReadHostLo = 0;
+	regs->DmaReadLen = 0;
+	regs->DmaWriteLen = 0;
+	regs->DmaWriteLcl = 0;
+	regs->DmaWriteIPchecksum = 0;
+	regs->DmaReadLcl = 0;
+	regs->DmaReadIPchecksum = 0;
+	regs->PciState = 0; /* 0x90 for GE? */
+	regs->Mode = SWAP_DATA;
+
+#if 0
+	/*
+	 * Don't worry, this is just black magic.
+	 */
+	regs->RxBase = 0xdf000;
+	regs->RxPrd = 0xdf000;
+	regs->RxCon = 0xdf000;
+	regs->TxBase = 0xce000;
+	regs->TxPrd = 0xce000;
+	regs->TxCon = 0xce000;
+	regs->RxIndPro = 0;
+	regs->RxIndCon = 0;
+	regs->RxIndRef = 0;
+	regs->TxIndPro = 0;
+	regs->TxIndCon = 0;
+	regs->TxIndRef = 0;
+	regs->pad10[0] = 0xcc000;
+	regs->DrCmndPro = 0;
+	regs->DrCmndCon = 0;
+	regs->DwCmndPro = 0;
+	regs->DwCmndCon = 0;
+	regs->DwCmndRef = 0;
+	regs->DrDataPro = 0;
+	regs->DrDataCon = 0;
+	regs->DrDataRef = 0;
+	regs->DwDataPro = 0;
+	regs->DwDataCon = 0;
+	regs->DwDataRef = 0;
+#endif
+
+	regs->MbEvent = 0xffffffff;
+	regs->Event = 0;
+
+	regs->TxPi = 0;
+	regs->IpRxPi = 0;
+
+	regs->EvtCon = 0;
+	regs->EvtPrd = 0;
+
+	rrpriv->info->evt_ctrl.pi = 0;
+
+	for (i = 0; i < CMD_RING_ENTRIES; i++)
+		regs->CmdRing[i] = 0;
+
+	regs->PciState = 0;
+
+	start_pc = read_eeprom_word(rrpriv, &hw->rncd_info.FwStart);
+
+#if (DEBUG > 1)
+	printk("%s: Executing firmware at address 0x%06x\n",
+	       dev->name, start_pc);
+#endif
+
+	regs->Pc = start_pc + 0x800;
+	udelay(5);
+
+	regs->Pc = start_pc;
+
+	return 0;
+}
+
+/*
+ * Read a string from the EEPROM.
+ */
+static unsigned int read_eeprom(struct rr_private *rrpriv,
+				unsigned long offset,
+				unsigned char *buf,
+				unsigned long length)
+{
+	struct rr_regs *regs = rrpriv->regs;
+	u32 misc, io, i;
+
+	io = regs->ExtIo;
+	regs->ExtIo = 0;
+	misc = regs->LocalCtrl;
+	regs->LocalCtrl = 0;
+
+	for (i = 0; i < length; i++){
+		regs->WinBase = (EEPROM_BASE + ((offset+i) << 3));
+		buf[i] = (regs->WinData >> 24) & 0xff;
+	}
+
+	regs->LocalCtrl = misc;
+	regs->ExtIo = io;
+
+	return i;
+}
+
+
+/*
+ * Shortcut to read one word (4 bytes) out of the EEPROM and convert
+ * it to our CPU byte-order.
+ */
+static u32 read_eeprom_word(struct rr_private *rrpriv,
+			    void * offset)
+{
+	u32 word;
+
+	if ((read_eeprom(rrpriv, (unsigned long)offset,
+			 (char *)&word, 4) == 4))
+		return be32_to_cpu(word);
+	return 0;
+}
+
+
+__initfunc(static int rr_init(struct device *dev))
+{
+	struct rr_private *rrpriv;
+	struct rr_regs *regs;
+	u32 sram_size, rev;
+
+	rrpriv = (struct rr_private *)dev->priv;
+	regs = rrpriv->regs;
+
+	rev = regs->FwRev;
+	if (rev > 0x00020024)
+		printk("  Firmware revision: %i.%i.%i\n", (rev >> 16),
+		       ((rev >> 8) & 0xff), (rev & 0xff));
+	else{
+		printk("  Firmware revision too old: %i.%i.%i, please upgrade to 2.0.37 or later.\n",
+		       (rev >> 16), ((rev >> 8) & 0xff), (rev & 0xff));
+		return -EFAULT;
+		
+	}
+
+	printk("  Maximum receive rings %i\n", regs->MaxRxRng);
+
+	sram_size = read_eeprom_word(rrpriv, (void *)8);
+	printk("  SRAM size 0x%06x\n", sram_size);
+
+	return 0;
+}
+
+
+static int rr_init1(struct device *dev)
+{
+	struct rr_private *rrpriv;
+	struct rr_regs *regs;
+	u32 hostctrl;
+	unsigned long myjif, flags, tmp_ptr;
+	struct cmd cmd;
+	short i;
+
+	rrpriv = (struct rr_private *)dev->priv;
+	regs = rrpriv->regs;
+
+	spin_lock_irqsave(&rrpriv->lock, flags);
+
+	hostctrl = regs->HostCtrl;
+	regs->HostCtrl |= HALT_NIC;
+
+	if (hostctrl & PARITY_ERR){
+		printk("%s: Parity error halting NIC - this is serious!\n",
+		       dev->name);
+		spin_unlock_irqrestore(&rrpriv->lock, flags);
+		return -EFAULT;
+	}
+
+
+	memset(rrpriv->rx_ctrl, 0, 256 * sizeof(struct ring_ctrl));
+	memset(rrpriv->info, 0, sizeof(struct rr_info));
+
+	tmp_ptr = virt_to_bus((void *)rrpriv->rx_ctrl);
+#if (BITS_PER_LONG == 64)
+	regs->RxRingHi = (tmp_ptr >> 32);
+#else
+	regs->RxRingHi = 0;
+#endif
+	regs->RxRingLo = ((tmp_ptr) & 0xffffffff);
+
+	tmp_ptr = virt_to_bus((void *)rrpriv->info);
+#if (BITS_PER_LONG == 64)
+	regs->InfoPtrHi = (tmp_ptr >> 32);
+#else
+	regs->InfoPtrHi = 0;
+#endif
+	regs->InfoPtrLo = ((tmp_ptr) & 0xffffffff);
+
+	rrpriv->info->evt_ctrl.entry_size = sizeof(struct event);
+	rrpriv->info->evt_ctrl.entries = EVT_RING_ENTRIES;
+	rrpriv->info->evt_ctrl.mode = 0;
+	rrpriv->info->evt_ctrl.pi = 0;
+	rrpriv->info->evt_ctrl.rngptr = virt_to_bus(rrpriv->evt_ring);
+
+	rrpriv->info->cmd_ctrl.entry_size = sizeof(struct cmd);
+	rrpriv->info->cmd_ctrl.entries = CMD_RING_ENTRIES;
+	rrpriv->info->cmd_ctrl.mode = 0;
+	rrpriv->info->cmd_ctrl.pi = 15;
+
+	for (i = 0; i < CMD_RING_ENTRIES; i++) {
+		regs->CmdRing[i] = 0;
+	}
+
+	for (i = 0; i < TX_RING_ENTRIES; i++) {
+		rrpriv->tx_ring[i].size = 0;
+		rrpriv->tx_ring[i].addr = 0;
+		rrpriv->tx_skbuff[i] = 0;
+	}
+
+	rrpriv->info->tx_ctrl.entry_size = sizeof(struct tx_desc);
+	rrpriv->info->tx_ctrl.entries = TX_RING_ENTRIES;
+	rrpriv->info->tx_ctrl.mode = 0;
+	rrpriv->info->tx_ctrl.pi = 0;
+	rrpriv->info->tx_ctrl.rngptr = virt_to_bus(rrpriv->tx_ring);
+
+	/*
+	 * Set dirty_tx before we start receiving interrupts, otherwise
+	 * the interrupt handler might think it is supposed to process
+	 * tx ints before we are up and running, which may cause a null
+	 * pointer access in the int handler.
+	 */
+	rrpriv->tx_full = 0;
+	rrpriv->cur_rx = 0;
+	rrpriv->dirty_rx = rrpriv->dirty_tx = 0;
+
+	rr_reset(dev);
+
+	regs->IntrTmr = 0x60;
+	regs->WriteDmaThresh = 0x80 | 0x1f;
+	regs->ReadDmaThresh = 0x80 | 0x1f;
+
+	rrpriv->fw_running = 0;
+
+	hostctrl &= ~(HALT_NIC | INVALID_INST_B | PARITY_ERR);
+	regs->HostCtrl = hostctrl;
+
+	spin_unlock_irqrestore(&rrpriv->lock, flags);
+
+	udelay(1000);
+
+	/*
+	 * Now start the FirmWare.
+	 */
+	cmd.code = C_START_FW;
+	cmd.ring = 0;
+	cmd.index = 0;
+
+	rr_issue_cmd(rrpriv, &cmd);
+
+	/*
+	 * Give the FirmWare time to chew on the `get running' command.
+	 */
+	myjif = jiffies + 5 * HZ;
+	while ((jiffies < myjif) && !rrpriv->fw_running);
+
+	for (i = 0; i < RX_RING_ENTRIES; i++) {
+		struct sk_buff *skb;
+
+		rrpriv->rx_ring[i].mode = 0;
+		skb = alloc_skb(dev->mtu + HIPPI_HLEN, GFP_ATOMIC);
+		rrpriv->rx_skbuff[i] = skb;
+		/*
+		 * Sanity test to see if we conflict with the DMA
+		 * limitations of the Roadrunner.
+		 */
+		if ((((unsigned long)skb->data) & 0xfff) > ~65320)
+			printk("skb alloc error\n");
+
+#if (BITS_PER_LONG == 32)
+		rrpriv->rx_ring[i].zero = 0;
+#endif
+		rrpriv->rx_ring[i].addr = virt_to_bus(skb->data);
+		rrpriv->rx_ring[i].size = dev->mtu + HIPPI_HLEN;
+	}
+
+	rrpriv->rx_ctrl[4].entry_size = sizeof(struct rx_desc);
+	rrpriv->rx_ctrl[4].entries = RX_RING_ENTRIES;
+	rrpriv->rx_ctrl[4].mode = 8;
+	rrpriv->rx_ctrl[4].pi = 0;
+	rrpriv->rx_ctrl[4].rngptr = virt_to_bus(rrpriv->rx_ring);
+
+	cmd.code = C_NEW_RNG;
+	cmd.ring = 4;
+	cmd.index = 0;
+	rr_issue_cmd(rrpriv, &cmd);
+
+#if 0
+{
+	u32 tmp;
+	tmp = regs->ExtIo;
+	regs->ExtIo = 0x80;
+	
+	i = jiffies + 1 * HZ;
+	while (jiffies < i);
+	regs->ExtIo = tmp;
+}
+#endif
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+	return 0;
+}
+
+
+/*
+ * All events are considered to be slow (RX/TX ints do not generate
+ * events) and are handled here, outside the main interrupt handler,
+ * to reduce the size of the handler.
+ */
+static u32 rr_handle_event(struct device *dev, u32 prodidx)
+{
+	struct rr_private *rrpriv;
+	struct rr_regs *regs;
+	u32 tmp, eidx;
+#if 0
+	short i;
+#endif
+
+	rrpriv = (struct rr_private *)dev->priv;
+	regs = rrpriv->regs;
+	eidx = rrpriv->info->evt_ctrl.pi;
+
+	while (prodidx != eidx){
+		switch (rrpriv->evt_ring[eidx].code){
+		case E_NIC_UP:
+			tmp = regs->FwRev;
+			printk("%s: Firmware revision %i.%i.%i up and running\n",
+			       dev->name, (tmp >> 16), ((tmp >> 8) & 0xff),
+			       (tmp & 0xff));
+			rrpriv->fw_running = 1;
+			break;
+		case E_LINK_ON:
+			printk("%s: Optical link ON\n", dev->name);
+			break;
+		case E_LINK_OFF:
+			printk("%s: Optical link OFF\n", dev->name);
+			break;
+		case E_RX_IDLE:
+			printk("%s: RX data not moving\n", dev->name);
+			break;
+		case E_WATCHDOG:
+			printk("%s: The watchdog is here to see us\n",
+			       dev->name);
+			break;
+		/*
+		 * TX events.
+		 */
+		case E_CON_REJ:
+			printk("%s: Connection rejected\n", dev->name);
+			rrpriv->stats.tx_aborted_errors++;
+			break;
+		case E_CON_TMOUT:
+			printk("%s: Connection timeout\n", dev->name);
+			break;
+		case E_DISC_ERR:
+			printk("%s: HIPPI disconnect error\n", dev->name);
+			rrpriv->stats.tx_aborted_errors++;
+			break;
+		case E_TX_IDLE:
+			printk("%s: Transmitter idle\n", dev->name);
+			break;
+		case E_TX_LINK_DROP:
+			printk("%s: Link lost during transmit\n", dev->name);
+			rrpriv->stats.tx_aborted_errors++;
+			break;
+		/*
+		 * RX events.
+		 */
+		case E_VAL_RNG:		/* Should be ignored */
+#if (DEBUG > 2)
+			printk("%s: RX ring valid event\n", dev->name);
+#endif
+			regs->IpRxPi = RX_RING_ENTRIES - 1;
+			break;
+		case E_INV_RNG:
+			printk("%s: RX ring invalid event\n", dev->name);
+			break;
+		case E_RX_RNG_OUT:
+			printk("%s: Receive ring full\n", dev->name);
+			break;
+
+		case E_RX_PAR_ERR:
+			printk("%s: Receive parity error.\n", dev->name);
+			break;
+		case E_RX_LLRC_ERR:
+			printk("%s: Receive LLRC error.\n", dev->name);
+			break;
+		case E_PKT_LN_ERR:
+			printk("%s: Receive packet length error.\n",
+			       dev->name);
+			break;
+		default:
+			printk("%s: Unhandled event 0x%02x\n",
+			       dev->name, rrpriv->evt_ring[eidx].code);
+		}
+		eidx = (eidx + 1) % EVT_RING_ENTRIES;
+	}
+
+	rrpriv->info->evt_ctrl.pi = eidx;
+	return eidx;
+}
+
+
+static int rx_int(struct device *dev, u32 rxlimit)
+{
+	struct rr_private *rrpriv = (struct rr_private *)dev->priv;
+	u32 index, pkt_len;
+	struct rr_regs *regs = rrpriv->regs;
+
+	index = rrpriv->cur_rx;
+
+	while(index != rxlimit){
+		pkt_len = rrpriv->rx_ring[index].size;
+#if (DEBUG > 2)
+		printk("index %i, rxlimit %i\n", index, rxlimit);
+		printk("len %x, mode %x\n", pkt_len,
+		       rrpriv->rx_ring[index].mode);
+#endif
+#if 0
+/*
+ * I have never seen this occur
+ */
+		if(!(rrpriv->rx_skbuff[index])){
+			printk("Trying to receive in empty skbuff\n");
+			goto out;
+		}
+#endif
+
+		if (pkt_len > 0){
+			struct sk_buff *skb;
+
+			if (pkt_len < PKT_COPY_THRESHOLD) {
+				skb = alloc_skb(pkt_len, GFP_ATOMIC);
+				if (skb == NULL){
+					printk("%s: Out of memory deferring packet\n", dev->name);
+					rrpriv->stats.rx_dropped++;
+					goto defer;
+				}else
+					memcpy(skb_put(skb, pkt_len),
+					       rrpriv->rx_skbuff[index]->data,
+					       pkt_len);
+			}else{
+				struct sk_buff *newskb;
+
+				newskb = alloc_skb(dev->mtu + HIPPI_HLEN, GFP_ATOMIC);
+				if (newskb){
+					skb = rrpriv->rx_skbuff[index];
+					skb_put(skb, pkt_len);
+					rrpriv->rx_skbuff[index] = newskb;
+					rrpriv->rx_ring[index].addr = virt_to_bus(newskb->data);
+				}else{
+					printk("%s: Out of memory, deferring packet\n", dev->name);
+					rrpriv->stats.rx_dropped++;
+					goto defer;
+				}
+			}
+			skb->dev = dev;
+			skb->protocol = hippi_type_trans(skb, dev);
+
+			netif_rx(skb);		/* send it up */
+
+			rrpriv->stats.rx_packets++;
+			rrpriv->stats.rx_bytes += skb->len;
+		}
+	defer:
+		rrpriv->rx_ring[index].mode = 0;
+		rrpriv->rx_ring[index].size = dev->mtu + HIPPI_HLEN;
+
+		if ((index & 7) == 7)
+			regs->IpRxPi = index;
+
+		index = (index + 1) % RX_RING_ENTRIES;
+	}
+
+	rrpriv->cur_rx = index;
+	return index;
+}
+
+
+static void rr_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
+{
+	struct rr_private *rrpriv;
+	struct rr_regs *regs;
+	struct device *dev = (struct device *)dev_id;
+	u32 prodidx, eidx, txcsmr, rxlimit, txcon;
+	unsigned long flags;
+
+	rrpriv = (struct rr_private *)dev->priv;
+	regs = rrpriv->regs;
+
+	if (!(regs->HostCtrl & RR_INT)){
+#if 0
+		/* These are harmless */
+		printk("%s: spurious interrupt detected\n", dev->name);
+#endif
+		return;
+	}
+
+	if (test_and_set_bit(0, (void*)&dev->interrupt) != 0) {
+		printk("%s: Re-entering the interrupt handler.\n", dev->name);
+		return;
+	}
+
+	spin_lock_irqsave(&rrpriv->lock, flags);
+
+	prodidx = regs->EvtPrd;
+	txcsmr = (prodidx >> 8) & 0xff;
+	rxlimit = (prodidx >> 16) & 0xff;
+	prodidx &= 0xff;
+
+#if (DEBUG > 2)
+	printk("%s: interrupt, prodidx = %i, eidx = %i\n", dev->name,
+	       prodidx, rrpriv->info->evt_ctrl.pi);
+#endif
+
+	txcon = rrpriv->dirty_tx;
+	if (txcsmr != txcon) {
+		do {
+			rrpriv->stats.tx_packets++;
+			rrpriv->stats.tx_bytes +=rrpriv->tx_skbuff[txcon]->len;
+			dev_kfree_skb(rrpriv->tx_skbuff[txcon]);
+
+			rrpriv->tx_skbuff[txcon] = NULL;
+			rrpriv->tx_ring[txcon].size = 0;
+			rrpriv->tx_ring[txcon].addr = 0;
+			rrpriv->tx_ring[txcon].mode = 0;
+
+			txcon = (txcon + 1) % TX_RING_ENTRIES;
+		} while (txcsmr != txcon);
+
+		rrpriv->dirty_tx = txcon;
+		if (rrpriv->tx_full && dev->tbusy &&
+		    (((rrpriv->info->tx_ctrl.pi + 1) % TX_RING_ENTRIES)
+		     != rrpriv->dirty_tx)){
+			rrpriv->tx_full = 0;
+			dev->tbusy = 0;
+			mark_bh(NET_BH);
+		}
+	}
+
+	rx_int(dev, rxlimit);
+
+	eidx = rrpriv->info->evt_ctrl.pi;
+
+	if (prodidx != eidx)
+		eidx = rr_handle_event(dev, prodidx);
+
+	eidx |= ((txcsmr << 8) | (rxlimit << 16));
+	regs->EvtCon = eidx;
+
+	spin_unlock_irqrestore(&rrpriv->lock, flags);
+
+	dev->interrupt = 0;
+}
+
+
+static int rr_open(struct device *dev)
+{
+	struct rr_private *rrpriv;
+	struct rr_regs *regs;
+
+	rrpriv = (struct rr_private *)dev->priv;
+	regs = rrpriv->regs;
+
+#if 0
+	regs->HostCtrl |= (HALT_NIC | RR_CLEAR_INT);
+#endif
+
+	if (request_irq(dev->irq, rr_interrupt, 0, rrpriv->name, dev))
+	{
+		printk(KERN_WARNING "%s: Requested IRQ %d is busy\n",
+		       dev->name, dev->irq);
+		return -EAGAIN;
+	}
+
+	rrpriv->rx_ctrl = kmalloc(256*sizeof(struct ring_ctrl),
+				  GFP_KERNEL | GFP_DMA);
+	rrpriv->info = kmalloc(sizeof(struct rr_info), GFP_KERNEL | GFP_DMA);
+
+	rr_init1(dev);
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+
+static void rr_dump(struct device *dev)
+{
+	struct rr_private *rrpriv;
+	struct rr_regs *regs;
+	u32 index, cons;
+	short i;
+	int len;
+
+	rrpriv = (struct rr_private *)dev->priv;
+	regs = rrpriv->regs;
+
+	printk("%s: dumping NIC TX rings\n", dev->name);
+
+	printk("RxPrd %08x, TxPrd %02x, EvtPrd %08x, TxPi %02x, TxCtrlPi %02x\n",
+	       regs->RxPrd, regs->TxPrd, regs->EvtPrd, regs->TxPi,
+	       rrpriv->info->tx_ctrl.pi);
+
+	printk("Error code 0x%x\n", regs->Fail1);
+
+	index = (((regs->EvtPrd >> 8) & 0xff ) - 1) % EVT_RING_ENTRIES;
+	cons = rrpriv->dirty_tx;
+	printk("TX ring index %i, TX consumer %i\n",
+	       index, cons);
+
+	if (rrpriv->tx_skbuff[index]){
+		len = min(0x80, rrpriv->tx_skbuff[index]->len);
+		printk("skbuff for index %i is valid - dumping data (0x%x bytes - DMA len 0x%x)\n", index, len, rrpriv->tx_ring[index].size);
+		for (i = 0; i < len; i++){
+			if (!(i & 7))
+				printk("\n");
+			printk("%02x ", (unsigned char) rrpriv->tx_skbuff[index]->data[i]);
+		}
+		printk("\n");
+	}
+
+	if (rrpriv->tx_skbuff[cons]){
+		len = min(0x80, rrpriv->tx_skbuff[cons]->len);
+		printk("skbuff for cons %i is valid - dumping data (0x%x bytes - skbuff len 0x%x)\n", cons, len, rrpriv->tx_skbuff[cons]->len);
+		printk("mode 0x%x, size 0x%x,\n phys %08x (virt %08x), skbuff-addr %08x, truesize 0x%x\n",
+		       rrpriv->tx_ring[cons].mode,
+		       rrpriv->tx_ring[cons].size,
+		       rrpriv->tx_ring[cons].addr,
+		       (unsigned int)bus_to_virt(rrpriv->tx_ring[cons].addr),
+		       (unsigned int)rrpriv->tx_skbuff[cons]->data,
+		       (unsigned int)rrpriv->tx_skbuff[cons]->truesize);
+		for (i = 0; i < len; i++){
+			if (!(i & 7))
+				printk("\n");
+			printk("%02x ", (unsigned char)rrpriv->tx_ring[cons].size);
+		}
+		printk("\n");
+	}
+
+	printk("dumping TX ring info:\n");
+	for (i = 0; i < TX_RING_ENTRIES; i++)
+		printk("mode 0x%x, size 0x%x, phys-addr %08x\n",
+		       rrpriv->tx_ring[i].mode,
+		       rrpriv->tx_ring[i].size,
+		       rrpriv->tx_ring[i].addr);
+
+}
+
+
+static int rr_close(struct device *dev)
+{
+	struct rr_private *rrpriv;
+	struct rr_regs *regs;
+	u32 tmp;
+	short i;
+
+	dev->start = 0;
+	set_bit(0, (void*)&dev->tbusy);
+
+	rrpriv = (struct rr_private *)dev->priv;
+	regs = rrpriv->regs;
+
+	tmp = regs->HostCtrl;
+	if (tmp & NIC_HALTED){
+		printk("%s: NIC already halted\n", dev->name);
+		rr_dump(dev);
+	}else
+		tmp |= HALT_NIC;
+	regs->HostCtrl = tmp;
+
+	/*
+	 * Lock to make sure we are not cleaning up while another CPU
+	 * handling interrupts.
+	 */
+	spin_lock(&rrpriv->lock);
+
+	regs->TxPi = 0;
+	regs->IpRxPi = 0;
+
+	regs->EvtCon = 0;
+	regs->EvtPrd = 0;
+
+	for (i = 0; i < CMD_RING_ENTRIES; i++)
+		regs->CmdRing[i] = 0;
+
+	rrpriv->info->tx_ctrl.entries = 0;
+	rrpriv->info->cmd_ctrl.pi = 0;
+	rrpriv->info->evt_ctrl.pi = 0;
+	rrpriv->rx_ctrl[4].entries = 0;
+
+	for (i = 0; i < TX_RING_ENTRIES; i++) {
+		if (rrpriv->tx_skbuff[i]) {
+			rrpriv->tx_ring[i].size = 0;
+			rrpriv->tx_ring[i].addr = 0;
+			dev_kfree_skb(rrpriv->tx_skbuff[i]);
+		}
+	}
+
+	for (i = 0; i < RX_RING_ENTRIES; i++) {
+		if (rrpriv->rx_skbuff[i]) {
+			rrpriv->rx_ring[i].size = 0;
+			rrpriv->rx_ring[i].addr = 0;
+			dev_kfree_skb(rrpriv->rx_skbuff[i]);
+		}
+	}
+
+	kfree(rrpriv->rx_ctrl);
+	kfree(rrpriv->info);
+
+	free_irq(dev->irq, dev);
+	spin_unlock(&rrpriv->lock);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+
+static int rr_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct rr_private *rrpriv = (struct rr_private *)dev->priv;
+	struct rr_regs *regs = rrpriv->regs;
+	struct ring_ctrl *txctrl;
+	unsigned long flags;
+	u32 index, len = skb->len;
+	u32 *ifield;
+	struct sk_buff *new_skb;
+
+	/*
+	 * We probably need to deal with tbusy here to prevent overruns.
+	 */
+
+	if (skb_headroom(skb) < 8){
+		printk("incoming skb too small - reallocating\n");
+		if (!(new_skb = dev_alloc_skb(len + 8))) {
+			dev_kfree_skb(skb);
+			dev->tbusy = 0;
+			return -EBUSY;
+		}
+		skb_reserve(new_skb, 8);
+		skb_put(new_skb, len);
+		memcpy(new_skb->data, skb->data, len);
+		dev_kfree_skb(skb);
+		skb = new_skb;
+	}
+
+	ifield = (u32 *)skb_push(skb, 8);
+
+	ifield[0] = 0;
+	ifield[1] = skb->private.ifield;
+
+	/*
+	 * We don't need the lock before we are actually going to start
+	 * fiddling with the control blocks.
+	 */
+	spin_lock_irqsave(&rrpriv->lock, flags);
+
+	txctrl = &rrpriv->info->tx_ctrl;
+
+	index = txctrl->pi;
+
+	rrpriv->tx_skbuff[index] = skb;
+	rrpriv->tx_ring[index].addr = virt_to_bus(skb->data);
+	rrpriv->tx_ring[index].size = len + 8; /* include IFIELD */
+	rrpriv->tx_ring[index].mode = PACKET_START | PACKET_END;
+	txctrl->pi = (index + 1) % TX_RING_ENTRIES;
+	regs->TxPi = txctrl->pi;
+
+	if (txctrl->pi == rrpriv->dirty_tx){
+		rrpriv->tx_full = 1;
+		set_bit(0, (void*)&dev->tbusy);
+	}
+
+	spin_unlock_irqrestore(&rrpriv->lock, flags);
+
+	dev->trans_start = jiffies;
+	return 0;
+}
+
+
+static struct net_device_stats *rr_get_stats(struct device *dev)
+{
+	struct rr_private *rrpriv;
+
+	rrpriv = (struct rr_private *)dev->priv;
+
+	return(&rrpriv->stats);
+}
+
+
+/*
+ * Read the firmware out of the EEPROM and put it into the SRAM
+ * (or from user space - later)
+ *
+ * This operation requires the NIC to be halted and is performed with
+ * interrupts disabled and with the spinlock hold.
+ */
+static int rr_load_firmware(struct device *dev)
+{
+	struct rr_private *rrpriv;
+	struct rr_regs *regs;
+#if 0
+	unsigned long flags;
+#endif
+	int i, j;
+	u32 localctrl, eptr, sptr, segptr, len, tmp;
+	u32 p2len, p2size, nr_seg, revision, io, sram_size;
+	struct eeprom *hw = NULL;
+
+	rrpriv = (struct rr_private *)dev->priv;
+	regs = rrpriv->regs;
+
+	if (dev->flags & IFF_UP)
+		return -EBUSY;
+
+	if (!(regs->HostCtrl & NIC_HALTED)){
+		printk("%s: Trying to load firmware to a running NIC.\n", 
+		       dev->name);
+		return -EBUSY;
+	}
+
+	localctrl = regs->LocalCtrl;
+	regs->LocalCtrl = 0;
+
+	regs->EvtPrd = 0;
+	regs->RxPrd = 0;
+	regs->TxPrd = 0;
+
+	/*
+	 * First wipe the entire SRAM, otherwise we might run into all
+	 * kinds of trouble ... sigh, this took almost all afternoon
+	 * to track down ;-(
+	 */
+	io = regs->ExtIo;
+	regs->ExtIo = 0;
+	sram_size = read_eeprom_word(rrpriv, (void *)8);
+
+	for (i = 200; i < sram_size / 4; i++){
+		regs->WinBase = i * 4;
+		regs->WinData = 0;
+	}
+	regs->ExtIo = io;
+
+	eptr = read_eeprom_word(rrpriv, &hw->rncd_info.AddrRunCodeSegs);
+	eptr = ((eptr & 0x1fffff) >> 3);
+
+	p2len = read_eeprom_word(rrpriv, (void *)(0x83*4));
+	p2len = (p2len << 2);
+	p2size = read_eeprom_word(rrpriv, (void *)(0x84*4));
+	p2size = ((p2size & 0x1fffff) >> 3);
+
+	if ((eptr < p2size) || (eptr > (p2size + p2len))){
+		printk("%s: eptr is invalid\n", dev->name);
+		goto out;
+	}
+
+	revision = read_eeprom_word(rrpriv, &hw->manf.HeaderFmt);
+
+	if (revision != 1){
+		printk("%s: invalid firmware format (%i)\n",
+		       dev->name, revision);
+		goto out;
+	}
+
+	nr_seg = read_eeprom_word(rrpriv, (void *)eptr);
+	eptr +=4;
+#if (DEBUG > 1)
+	printk("%s: nr_seg %i\n", dev->name, nr_seg);
+#endif
+
+	for (i = 0; i < nr_seg; i++){
+		sptr = read_eeprom_word(rrpriv, (void *)eptr);
+		eptr += 4;
+		len = read_eeprom_word(rrpriv, (void *)eptr);
+		eptr += 4;
+		segptr = read_eeprom_word(rrpriv, (void *)eptr);
+		segptr = ((segptr & 0x1fffff) >> 3);
+		eptr += 4;
+#if (DEBUG > 1)
+		printk("%s: segment %i, sram address %06x, length %04x, segptr %06x\n",
+		       dev->name, i, sptr, len, segptr);
+#endif
+		for (j = 0; j < len; j++){
+			tmp = read_eeprom_word(rrpriv, (void *)segptr);
+			regs->WinBase = sptr;
+			regs->WinData = tmp;
+			segptr += 4;
+			sptr += 4;
+		}
+	}
+
+out:
+	regs->LocalCtrl = localctrl;
+	return 0;
+}
+
+
+static int rr_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+	struct rr_private *rrpriv;
+
+	rrpriv = (struct rr_private *)dev->priv;
+
+	switch(cmd){
+	case SIOCRRPFW:
+		if (!suser())
+			return -EPERM;
+
+		if (rrpriv->fw_running){
+			printk("%s: firmware already running\n", dev->name);
+			return -EPERM;
+		}
+		printk("%s: updating firmware", dev->name);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -pipe -fomit-frame-pointer -fno-strength-reduce -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=686 -c rrunner.c"
+ * End:
+ */
diff -u --recursive --new-file v2.1.124/linux/drivers/net/rrunner.h linux/drivers/net/rrunner.h
--- v2.1.124/linux/drivers/net/rrunner.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/rrunner.h	Wed Oct  7 11:13:49 1998
@@ -0,0 +1,790 @@
+#ifndef _RRUNNER_H_
+#define _RRUNNER_H_
+
+#include<linux/config.h>
+
+#if ((BITS_PER_LONG != 32) && (BITS_PER_LONG != 64))
+#error "BITS_PER_LONG not defined or not valid"
+#endif
+
+
+struct rr_regs {
+
+	u32	pad0[16];
+
+	u32	HostCtrl;
+	u32	LocalCtrl;
+	u32	Pc;
+	u32	BrkPt;
+
+/* Timer increments every 0.97 micro-seconds (unsigned int) */
+	u32	Timer_Hi;
+	u32	Timer;
+	u32	TimerRef;
+	u32	PciState;
+
+	u32	Event;
+	u32	MbEvent;
+
+	u32	WinBase;
+	u32	WinData;
+	u32	RX_state;
+	u32	TX_state;
+
+	u32	Overhead;
+	u32	ExtIo;
+
+	u32	DmaWriteHostHi;
+	u32	DmaWriteHostLo;
+
+	u32	pad1[2];
+
+	u32	DmaReadHostHi;
+	u32	DmaReadHostLo;
+
+	u32	pad2;
+
+	u32	DmaReadLen;
+	u32	DmaWriteState;
+
+	u32	DmaWriteLcl;
+	u32	DmaWriteIPchecksum;
+	u32	DmaWriteLen;
+	u32	DmaReadState;
+	u32	DmaReadLcl;
+	u32	DmaReadIPchecksum;
+	u32	pad3;
+
+	u32	RxBase;
+	u32	RxPrd;
+	u32	RxCon;
+
+	u32	pad4;
+
+	u32	TxBase;
+	u32	TxPrd;
+	u32	TxCon;
+
+	u32	pad5;
+
+	u32	RxIndPro;
+	u32	RxIndCon;
+	u32	RxIndRef;
+
+	u32	pad6;
+
+	u32	TxIndPro;
+	u32	TxIndCon;
+	u32	TxIndRef;
+
+	u32	pad7[17];
+
+	u32	DrCmndPro;
+	u32	DrCmndCon;
+	u32	DrCmndRef;
+
+	u32	pad8;
+
+	u32	DwCmndPro;
+	u32	DwCmndCon;
+	u32	DwCmndRef;
+
+	u32	AssistState;
+
+	u32	DrDataPro;
+	u32	DrDataCon;
+	u32	DrDataRef;
+
+	u32	pad9;
+
+	u32	DwDataPro;
+	u32	DwDataCon;
+	u32	DwDataRef;
+
+	u32	pad10[33];
+
+	u32	EvtCon;
+
+	u32	pad11[5];
+
+	u32	TxPi;
+	u32	IpRxPi;
+
+	u32	pad11a[8];
+
+	u32	CmdRing[16];
+
+/* The ULA is in two registers the high order two bytes of the first
+ * word contain the RunCode features.
+ * ula0		res	res	byte0	byte1
+ * ula1		byte2	byte3	byte4	byte5
+ */
+	u32	Ula0;
+	u32	Ula1;
+
+	u32	RxRingHi;
+	u32	RxRingLo;
+
+	u32	InfoPtrHi;
+	u32	InfoPtrLo;
+
+	u32	Mode;
+
+	u32	ConRetry;
+	u32	ConRetryTmr;
+
+	u32	ConTmout;
+	u32	CtatTmr;
+
+	u32	MaxRxRng;
+
+	u32	IntrTmr;
+	u32	TxDataMvTimeout;
+	u32	RxDataMvTimeout;
+
+	u32	EvtPrd;
+	u32	TraceIdx;
+
+	u32	Fail1;
+	u32	Fail2;
+
+	u32	DrvPrm;
+
+	u32	FilterLA;
+
+	u32	FwRev;
+	u32	FwRes1;
+	u32	FwRes2;
+	u32	FwRes3;
+
+	u32	WriteDmaThresh;
+	u32	ReadDmaThresh;
+
+	u32	pad12[325];
+	u32	Window[512];
+};
+
+/*
+ * Host control register bits.
+ */
+	
+#define RR_INT		0x01
+#define RR_CLEAR_INT	0x02
+#define NO_SWAP		0x04000004
+#define NO_SWAP1	0x00000004
+#define PCI_RESET_NIC	0x08
+#define HALT_NIC	0x10
+#define SSTEP_NIC	0x20
+#define MEM_READ_MULTI	0x40
+#define NIC_HALTED	0x100
+#define HALT_INST	0x200
+#define PARITY_ERR	0x400
+#define INVALID_INST_B	0x800
+#define RR_REV_2	0x20000000
+#define RR_REV_MASK	0xf0000000
+
+/*
+ * Local control register bits.
+ */
+
+#define INTA_STATE		0x01
+#define CLEAR_INTA		0x02
+#define FAST_EEPROM_ACCESS	0x08
+#define ENABLE_EXTRA_SRAM	0x100
+#define ENABLE_EXTRA_DESC	0x200
+#define ENABLE_PARITY		0x400
+#define FORCE_DMA_PARITY_ERROR	0x800
+#define ENABLE_EEPROM_WRITE	0x1000
+#define ENABLE_DATA_CACHE	0x2000
+#define SRAM_LO_PARITY_ERR	0x4000
+#define SRAM_HI_PARITY_ERR	0x8000
+
+/*
+ * PCI state bits.
+ */
+
+#define FORCE_PCI_RESET		0x01
+#define PROVIDE_LENGTH		0x02
+#define MASK_DMA_READ_MAX	0x1C
+#define RBURST_DISABLE		0x00
+#define RBURST_4		0x04
+#define RBURST_16		0x08
+#define RBURST_32		0x0C
+#define RBURST_64		0x10
+#define RBURST_128		0x14
+#define RBURST_256		0x18
+#define RBURST_1024		0x1C
+#define MASK_DMA_WRITE_MAX	0xE0
+#define WBURST_DISABLE		0x00
+#define WBURST_4		0x20
+#define WBURST_16		0x40
+#define WBURST_32		0x60
+#define WBURST_64		0x80
+#define WBURST_128		0xa0
+#define WBURST_256		0xc0
+#define WBURST_1024		0xe0
+#define MASK_MIN_DMA		0xFF00
+#define FIFO_RETRY_ENABLE	0x10000
+
+/*
+ * Event register
+ */
+
+#define DMA_WRITE_DONE		0x10000
+#define DMA_READ_DONE		0x20000
+#define DMA_WRITE_ERR		0x40000
+#define DMA_READ_ERR		0x80000
+
+/*
+ * Receive state
+ *
+ * RoadRunner HIPPI Receive State Register controls and monitors the 
+ * HIPPI receive interface in the NIC. Look at err bits when a HIPPI
+ * receive Error Event occurs. 
+ */
+
+#define ENABLE_NEW_CON		0x01
+#define RESET_RECV		0x02
+#define RECV_ALL		0x00
+#define RECV_1K			0x20
+#define RECV_2K			0x40
+#define RECV_4K			0x60
+#define RECV_8K			0x80
+#define RECV_16K		0xa0
+#define RECV_32K		0xc0
+#define RECV_64K		0xe0
+
+/*
+ * Transmit status.
+ */
+
+#define ENA_XMIT		0x01
+#define PERM_CON		0x02
+
+/*
+ * DMA write state
+ */
+
+#define RESET_DMA		0x01
+#define NO_SWAP_DMA		0x02
+#define DMA_ACTIVE		0x04
+#define THRESH_MASK		0x1F
+#define DMA_ERROR_MASK		0xff000000
+
+/*
+ * Gooddies stored in the ULA registers.
+ */
+
+#define TRACE_ON_WHAT_BIT	0x00020000    /* Traces on */
+#define ONEM_BUF_WHAT_BIT	0x00040000    /* 1Meg vs 256K */
+#define CHAR_API_WHAT_BIT	0x00080000    /* Char API vs network only */
+#define MS_DOS_WHAT_BIT		0x00100000    /* MS_DOS */
+#define CMD_EVT_WHAT_BIT	0x00200000    /* Command event */
+#define LONG_TX_WHAT_BIT	0x00400000
+#define LONG_RX_WHAT_BIT	0x00800000
+#define WHAT_BIT_MASK		0xFFFD0000    /* Feature bit mask */
+
+/*
+ * Mode status
+ */
+
+#define EVENT_OVFL		0x80000000
+#define FATAL_ERR		0x40000000
+#define LOOP_BACK		0x01
+#define MODE_PH			0x02
+#define MODE_FP			0x00
+#define PTR64BIT		0x04
+#define PTR32BIT		0x00
+#define PTR_WD_SWAP		0x08
+#define PTR_WD_NOSWAP		0x00
+#define POST_WARN_EVENT		0x10
+#define ERR_TERM		0x20
+#define DIRECT_CONN		0x40
+#define NO_NIC_WATCHDOG		0x80
+#define SWAP_DATA		0x100
+#define SWAP_CONTROL		0x200
+#define NIC_HALT_ON_ERR		0x400
+#define NIC_NO_RESTART		0x800
+#define HALF_DUP_TX		0x1000
+#define HALF_DUP_RX		0x2000
+
+
+/*
+ * Error codes
+ */
+
+/* Host Error Codes - values of fail1 */
+#define ERR_UNKNOWN_MBOX	0x1001
+#define ERR_UNKNOWN_CMD		0x1002
+#define ERR_MAX_RING		0x1003
+#define ERR_RING_CLOSED		0x1004
+#define ERR_RING_OPEN		0x1005
+/* Firmware internal errors */
+#define ERR_EVENT_RING_FULL	0x01
+#define ERR_DW_PEND_CMND_FULL	0x02
+#define ERR_DR_PEND_CMND_FULL	0x03
+#define ERR_DW_PEND_DATA_FULL	0x04
+#define ERR_DR_PEND_DATA_FULL	0x05
+#define ERR_ILLEGAL_JUMP	0x06
+#define ERR_UNIMPLEMENTED	0x07
+#define ERR_TX_INFO_FULL	0x08
+#define ERR_RX_INFO_FULL	0x09
+#define ERR_ILLEGAL_MODE	0x0A
+#define ERR_MAIN_TIMEOUT	0x0B
+#define ERR_EVENT_BITS		0x0C
+#define ERR_UNPEND_FULL		0x0D
+#define ERR_TIMER_QUEUE_FULL	0x0E
+#define ERR_TIMER_QUEUE_EMPTY	0x0F
+#define ERR_TIMER_NO_FREE	0x10
+#define ERR_INTR_START		0x11
+#define ERR_BAD_STARTUP		0x12
+#define ERR_NO_PKT_END		0x13
+#define ERR_HALTED_ON_ERR	0x14
+/* Hardware NIC Errors */
+#define ERR_WRITE_DMA		0x0101
+#define ERR_READ_DMA		0x0102
+#define ERR_EXT_SERIAL		0x0103
+#define ERR_TX_INT_PARITY	0x0104
+
+
+/*
+ * Event definitions
+ */
+
+#define EVT_RING_ENTRIES	64
+#define EVT_RING_SIZE	(EVT_RING_ENTRIES * sizeof(struct event))
+
+struct event {
+#ifdef __LITTLE_ENDIAN
+	u16     index;
+	u8      ring;
+	u8      code;
+#else
+	u8      code;
+	u8      ring;
+	u16     index;
+#endif
+	u32     timestamp;
+};
+
+/*
+ * General Events
+ */
+
+#define E_NIC_UP	0x01
+#define E_WATCHDOG	0x02
+
+#define E_STAT_UPD	0x04
+#define E_INVAL_CMD	0x05
+#define E_SET_CMD_CONS	0x06
+#define E_LINK_ON	0x07
+#define E_LINK_OFF	0x08
+#define E_INTERN_ERR	0x09
+#define E_HOST_ERR	0x0A
+#define E_STATS_UPDATE	0x0B
+#define E_REJECTING	0x0C
+
+/*
+ * Send  Events
+ */
+#define E_CON_REJ	0x13
+#define E_CON_TMOUT	0x14
+#define E_CON_NC_TMOUT	0x15	/* I  , Connection No Campon Timeout */
+#define E_DISC_ERR	0x16
+#define E_INT_PRTY	0x17
+#define E_TX_IDLE	0x18
+#define E_TX_LINK_DROP	0x19
+#define E_TX_INV_RNG	0x1A
+#define E_TX_INV_BUF	0x1B
+#define E_TX_INV_DSC	0x1C
+
+/*
+ * Destination Events
+ */
+/*
+ * General Receive events
+ */
+#define E_VAL_RNG	0x20
+#define E_RX_RNG_ENER	0x21
+#define E_INV_RNG	0x22
+#define E_RX_RNG_SPC	0x23
+#define E_RX_RNG_OUT	0x24
+#define E_PKT_DISCARD	0x25
+#define E_INFO_EVT	0x27
+
+/*
+ * Data corrupted events
+ */
+#define E_RX_PAR_ERR	0x2B
+#define E_RX_LLRC_ERR	0x2C
+#define E_IP_CKSM_ERR	0x2D
+#define E_DTA_CKSM_ERR	0x2E
+#define E_SHT_BST	0x2F
+
+/*
+ * Data lost events
+ */
+#define E_LST_LNK_ERR	0x30
+#define E_FLG_SYN_ERR	0x31
+#define E_FRM_ERR	0x32
+#define E_RX_IDLE	0x33
+#define E_PKT_LN_ERR	0x34
+#define E_STATE_ERR	0x35
+#define E_UNEXP_DATA	0x3C
+
+/*
+ * Fatal events
+ */
+#define E_RX_INV_BUF	0x36
+#define E_RX_INV_DSC	0x37
+#define E_RNG_BLK	0x38
+
+/*
+ * Warning events
+ */
+#define E_RX_TO		0x39
+#define E_BFR_SPC	0x3A
+#define E_INV_ULP	0x3B
+
+#define E_NOT_IMPLEMENTED 0x40
+
+
+/*
+ * Commands
+ */
+
+#define CMD_RING_ENTRIES	16
+
+struct cmd {
+#ifdef __LITTLE_ENDIAN
+	u16     index;
+	u8      ring;
+	u8      code;
+#else
+	u8      code;
+	u8      ring;
+	u16     index;
+#endif
+};
+
+#define C_START_FW	0x01
+#define C_UPD_STAT	0x02
+#define C_WATCHDOG	0x05
+#define C_DEL_RNG	0x09
+#define C_NEW_RNG	0x0A
+#define C_CONN		0x0D
+
+
+/*
+ * Mode bits
+ */
+
+#define  INTERRUPT		0x02
+#define  TX_IP_CKSUM		0x04
+#define  PACKET_END		0x08
+#define  PACKET_START		0x10
+#define  SAME_IFIELD		0x80
+
+
+/*
+ * TX ring
+ */
+
+#ifdef CONFIG_ROADRUNNER_LARGE_RINGS
+#define TX_RING_ENTRIES	32
+#else
+#define TX_RING_ENTRIES	16
+#endif
+#define TX_RING_SIZE	(TX_RING_ENTRIES * sizeof(struct tx_desc))
+
+struct tx_desc{
+#if (BITS_PER_LONG == 64)
+	u64	addr;
+#else
+        u32	zero;
+	u32	addr;
+#endif
+	u32	res;
+#ifdef __LITTLE_ENDIAN
+	u16	size;
+	u8	pad;
+	u8	mode;
+#else
+	u8	mode;
+	u8	pad;
+	u16	size;
+#endif
+};
+
+
+#ifdef CONFIG_ROADRUNNER_LARGE_RINGS
+#define RX_RING_ENTRIES	32
+#else
+#define RX_RING_ENTRIES 16
+#endif
+#define RX_RING_SIZE	(RX_RING_ENTRIES * sizeof(struct rx_desc))
+
+struct rx_desc{
+#if (BITS_PER_LONG == 64)
+	u64	addr;
+#else
+        u32	zero;
+	u32	addr;
+#endif
+	u32	res;
+#ifdef __LITTLE_ENDIAN
+	u16	size;
+	u8	pad;
+	u8	mode;
+#else
+	u8	mode;
+	u8	pad;
+	u16	size;
+#endif
+};
+
+
+/*
+ * ioctl's
+ */
+
+#define SIOCRRPFW	SIOCDEVPRIVATE		/* put firmware */
+#define SIOCRRGFW	SIOCDEVPRIVATE+1	/* get firmware */
+
+
+struct seg_hdr {
+	u32	seg_start;
+	u32	seg_len;
+	u32	seg_eestart;
+};
+
+
+#define EEPROM_BASE 0x80000000
+
+struct eeprom_boot {
+	u32	key1;
+	u32	key2;
+	u32	sram_size;
+	struct	seg_hdr loader;
+	u32	init_chksum;
+	u32	reserved1;
+};
+
+struct eeprom_manf {
+	u32	HeaderFmt;
+	u32	Firmware;
+	u32	BoardRevision;
+	u32	RoadrunnerRev;
+	char	OpticsPart[8];
+	u32	OpticsRev;
+	u32	pad1;
+	char	SramPart[8];
+	u32	SramRev;
+	u32	pad2;
+	char	EepromPart[8];
+	u32	EepromRev;
+	u32	EepromSize;
+	char	PalPart[8];
+	u32	PalRev;
+	u32	pad3;
+	char	PalCodeFile[12];
+	u32	PalCodeRev;
+	char	BoardULA[8];
+	char	SerialNo[8];
+	char	MfgDate[8];
+	char	MfgTime[8];
+	char	ModifyDate[8];
+	u32	ModCount;
+	u32	pad4[13];
+};
+
+
+struct eeprom_phase_info {
+	char	phase1File[12];
+	u32	phase1Rev;
+	char	phase1Date[8];
+	char	phase2File[12];
+	u32	phase2Rev;
+	char	phase2Date[8];
+	u32	reserved7[4];
+};
+
+struct eeprom_rncd_info {
+	u32	FwStart;
+	u32	FwRev;
+	char	FwDate[8];
+	u32	AddrRunCodeSegs;
+	u32	FileNames;
+	char	File[13][8];
+};
+
+
+/* Phase 1 region (starts are word offset 0x80) */
+struct phase1_hdr{
+	u32	jump;
+	u32	noop;
+	struct seg_hdr phase2Seg;
+};
+
+struct eeprom {
+	struct eeprom_boot	boot;
+	u32			pad1[8];
+	struct eeprom_manf	manf;
+	struct eeprom_phase_info phase_info;
+	struct eeprom_rncd_info	rncd_info;
+	u32			pad2[15];
+	u32			hdr_checksum;
+	struct phase1_hdr	phase1;
+};
+
+
+struct rr_stats {
+	u32	NicTimeStamp;
+	u32	RngCreated;
+	u32	RngDeleted;
+	u32	IntrGen;
+	u32	NEvtOvfl;
+	u32	InvCmd;
+	u32	DmaReadErrs;
+	u32	DmaWriteErrs;
+	u32	StatUpdtT;
+	u32	StatUpdtC;
+	u32	WatchDog;
+	u32	Trace;	
+
+	/* Serial HIPPI */
+	u32	LnkRdyEst;
+	u32	GLinkErr;
+	u32	AltFlgErr;
+	u32	OvhdBit8Sync;
+	u32	RmtSerPrtyErr;
+	u32	RmtParPrtyErr;
+	u32	RmtLoopBk;
+	u32	pad1;
+
+	/* HIPPI tx */
+	u32	ConEst;
+	u32	ConRejS;
+	u32	ConRetry;
+	u32	ConTmOut;
+	u32	SndConDiscon;
+	u32	SndParErr;
+	u32	PktSnt;
+	u32	pad2[2];
+	u32	ShFBstSnt;
+	u64	BytSent;
+	u32	TxTimeout;
+	u32	pad3[3];
+
+	/* HIPPI rx */
+	u32	ConAcc;
+	u32	ConRejdiPrty;
+	u32	ConRejd64b;
+	u32	ConRejdBuf;
+	u32	RxConDiscon;
+	u32	RxConNoData;
+	u32	PktRx;
+	u32	pad4[2];
+	u32	ShFBstRx;
+	u64	BytRx;
+	u32	RxParErr;
+	u32	RxLLRCerr;
+	u32	RxBstSZerr;
+	u32	RxStateErr;
+	u32	RxRdyErr;
+	u32	RxInvULP;
+	u32	RxSpcBuf;
+	u32	RxSpcDesc;
+	u32	RxRngSpc;
+	u32	RxRngFull;
+	u32	RxPktLenErr;
+	u32	RxCksmErr;
+	u32	RxPktDrp;
+	u32	RngLowSpc;
+	u32	RngDataClose;
+	u32	RxTimeout;
+	u32	RxIdle;
+};
+
+
+/*
+ * This struct is shared with the NIC firmware.
+ */
+struct ring_ctrl {
+#if (BITS_PER_LONG == 64)
+	u64	rngptr;
+#else
+	u32	zero;
+	u32	rngptr;
+#endif
+#ifdef __LITTLE_ENDIAN
+	u16	entries;
+	u8	pad;
+	u8	entry_size;
+	u16	pi;
+	u16	mode;
+#else
+	u8	entry_size;
+	u8	pad;
+	u16	entries;
+	u16	mode;
+	u16	pi;
+#endif
+};
+
+struct rr_info {
+	union {
+		struct rr_stats stats;
+		u32 stati[128];
+	} s;
+	struct ring_ctrl	evt_ctrl;
+	struct ring_ctrl	cmd_ctrl;
+	struct ring_ctrl	tx_ctrl;
+	u8			pad[464];
+	u8			trace[3072];
+};
+
+/*
+ * The linux structure for the RoadRunner.
+ *
+ * RX/TX descriptors are put first to make sure they are properly
+ * aligned and do not cross cache-line boundaries.
+ */
+
+struct rr_private
+{
+	struct rx_desc		rx_ring[RX_RING_ENTRIES];
+	struct tx_desc		tx_ring[TX_RING_ENTRIES];
+	struct event		evt_ring[EVT_RING_ENTRIES];
+	struct sk_buff		*tx_skbuff[TX_RING_ENTRIES];
+	struct sk_buff		*rx_skbuff[RX_RING_ENTRIES];
+	struct rr_regs		*regs;		/* Register base */
+	struct ring_ctrl	*rx_ctrl;	/* Receive ring control */
+	struct rr_info		*info;		/* Shared info page */
+	spinlock_t		lock;
+	struct timer_list	timer;
+	u32			cur_rx, cur_cmd, cur_evt;
+	u32			dirty_rx, dirty_tx;
+	u32			tx_full;
+	short			fw_running;
+	u8			pci_bus;	/* PCI bus number */
+	u8			pci_dev_fun;	/* PCI device numbers */
+	char			name[24];	/* The assigned name */
+	struct net_device_stats stats;
+};
+
+
+/*
+ * Prototypes
+ */
+static int rr_init(struct device *dev);
+static int rr_init1(struct device *dev);
+static void rr_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static int rr_open(struct device *dev);
+static int rr_start_xmit(struct sk_buff *skb, struct device *dev);
+static int rr_close(struct device *dev);
+static struct net_device_stats *rr_get_stats(struct device *dev);
+static int rr_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+
+#endif /* _RRUNNER_H_ */
diff -u --recursive --new-file v2.1.124/linux/drivers/net/sdla_x25.c linux/drivers/net/sdla_x25.c
--- v2.1.124/linux/drivers/net/sdla_x25.c	Sat Sep  5 16:46:40 1998
+++ linux/drivers/net/sdla_x25.c	Tue Oct  6 09:39:49 1998
@@ -830,7 +830,7 @@
 {
 	x25_channel_t* chan = dev->priv;
 	if(chan==NULL)
-		return chan;
+		return NULL;
 	return &chan->ifstats;
 }
 
diff -u --recursive --new-file v2.1.124/linux/drivers/net/strip.c linux/drivers/net/strip.c
--- v2.1.124/linux/drivers/net/strip.c	Wed Sep  9 14:51:08 1998
+++ linux/drivers/net/strip.c	Wed Oct  7 15:51:45 1998
@@ -14,7 +14,7 @@
  *		for kernel-based devices like TTY.  It interfaces between a
  *		raw TTY, and the kernel's INET protocol layers (via DDI).
  *
- * Version:	@(#)strip.c	1.2	February 1997
+ * Version:	@(#)strip.c	1.3	July 1997
  *
  * Author:	Stuart Cheshire <cheshire@cs.stanford.edu>
  *
@@ -60,12 +60,18 @@
  *
  *              v1.2 January 1997 (SC)
  *              Put portables list back in
+ *
+ *              v1.3 July 1997 (SC)
+ *              Made STRIP driver set the radio's baud rate automatically.
+ *              It is no longer necessarily to manually set the radio's
+ *              rate permanently to 115200 -- the driver handles setting
+ *              the rate automatically.
  */
 
 #ifdef MODULE
-static const char StripVersion[] = "1.2-STUART.CHESHIRE-MODULAR";
+static const char StripVersion[] = "1.3-STUART.CHESHIRE-MODULAR";
 #else
-static const char StripVersion[] = "1.2-STUART.CHESHIRE";
+static const char StripVersion[] = "1.3-STUART.CHESHIRE";
 #endif
 
 #define TICKLE_TIMERS 0
@@ -84,6 +90,7 @@
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/segment.h>
 #include <asm/bitops.h>
 
 /*
@@ -111,13 +118,12 @@
 #include <linux/if_arp.h>
 #include <linux/if_strip.h>
 #include <linux/proc_fs.h>
+#include <linux/serial.h>
 #include <net/arp.h>
 
-#ifdef CONFIG_INET
 #include <linux/ip.h>
 #include <linux/tcp.h>
 #include <linux/time.h>
-#endif
 
 
 /************************************************************************/
@@ -285,6 +291,7 @@
     int                working;			/* Is radio working correctly?	*/
     int                firmware_level;		/* Message structuring level	*/
     int                next_command;		/* Next periodic command	*/
+    unsigned int       user_baud;		/* The user-selected baud rate  */
     int                mtu;			/* Our mtu (to spot changes!)	*/
     long               watchdog_doprobe;	/* Next time to test the radio	*/
     long               watchdog_doreset;	/* Time to do next reset	*/
@@ -459,7 +466,7 @@
 /* Macros								*/
 
 /* Returns TRUE if text T begins with prefix P */
-#define has_prefix(T,P) (!strncmp((T), (P), sizeof(P)-1))
+#define has_prefix(T,L,P) (((L) >= sizeof(P)-1) && !strncmp((T), (P), sizeof(P)-1))
 
 /* Returns TRUE if text T of length L is equal to string S */
 #define text_equal(T,L,S) (((L) == sizeof(S)-1) && !strncmp((T), (S), sizeof(S)-1))
@@ -498,6 +505,24 @@
     restore_flags(x);
 }
 
+static int arp_query(unsigned char *haddr, u32 paddr, struct device * dev)
+{
+    struct neighbour *neighbor_entry;
+
+    neighbor_entry = neigh_lookup(&arp_tbl, &paddr, dev);
+
+    if (neighbor_entry != NULL)
+    {
+	neighbor_entry->used = jiffies;
+	if (neighbor_entry->nud_state & NUD_VALID)
+	{
+	    memcpy(haddr, neighbor_entry->ha, dev->addr_len);
+	    return 1;
+	}
+    }
+    return 0;
+}
+
 static void DumpData(char *msg, struct strip *strip_info, __u8 *ptr, __u8 *end)
 {
     static const int MAX_DumpData = 80;
@@ -834,6 +859,41 @@
 /* General routines for STRIP						*/
 
 /*
+ * get_baud returns the current baud rate, as one of the constants defined in
+ * termbits.h
+ * If the user has issued a baud rate override using the 'setserial' command
+ * and the logical current rate is set to 38.4, then the true baud rate
+ * currently in effect (57.6 or 115.2) is returned.
+ */
+static unsigned int get_baud(struct tty_struct *tty)
+    {
+    if (!tty || !tty->termios) return(0);
+    if ((tty->termios->c_cflag & CBAUD) == B38400 && tty->driver_data)
+        {
+        struct async_struct *info = (struct async_struct *)tty->driver_data;
+        if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI ) return(B57600);
+        if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) return(B115200);
+        }
+    return(tty->termios->c_cflag & CBAUD);
+    }
+
+/*
+ * set_baud sets the baud rate to the rate defined by baudcode
+ * Note: The rate B38400 should be avoided, because the user may have
+ * issued a 'setserial' speed override to map that to a different speed.
+ * We could achieve a true rate of 38400 if we needed to by cancelling
+ * any user speed override that is in place, but that might annoy the
+ * user, so it is simplest to just avoid using 38400.
+ */
+static void set_baud(struct tty_struct *tty, unsigned int baudcode)
+    {
+    struct termios old_termios = *(tty->termios);
+    tty->termios->c_cflag &= ~CBAUD; /* Clear the old baud setting */
+    tty->termios->c_cflag |= baudcode; /* Set the new baud setting */
+    tty->driver.set_termios(tty, &old_termios);
+    }
+
+/*
  * Convert a string to a Metricom Address.
  */
 
@@ -971,9 +1031,9 @@
 static void strip_unlock(struct strip *strip_info)
 {
     /*
-     * Set the time to go off in one second.
+     * Set the timer to go off in one second.
      */
-    strip_info->idle_timer.expires  = jiffies + 1*HZ;
+    strip_info->idle_timer.expires = jiffies + 1*HZ;
     add_timer(&strip_info->idle_timer);
     if (!test_and_clear_bit(0, (void *)&strip_info->dev.tbusy))
         printk(KERN_ERR "%s: trying to unlock already unlocked device!\n",
@@ -1229,13 +1289,16 @@
 /************************************************************************/
 /* Sending routines							*/
 
-#define InitString "ate0q1dt**starmode"
-
 static void ResetRadio(struct strip *strip_info)
 {
-    static const char s[] = "\r" InitString "\r**";
-
-    /* If the radio isn't working anymore, we should clear the old status information. */
+    struct tty_struct *tty = strip_info->tty;
+    static const char init[] = "ate0q1dt**starmode\r**";
+    StringDescriptor s = { init, sizeof(init)-1 };
+
+    /* 
+     * If the radio isn't working anymore,
+     * we should clear the old status information.
+     */
     if (strip_info->working)
     {
         printk(KERN_INFO "%s: No response: Resetting radio.\n", strip_info->dev.name);
@@ -1258,15 +1321,43 @@
 
     /* Mark radio address as unknown */
     *(MetricomAddress*)&strip_info->true_dev_addr = zero_address;
-    if (!strip_info->manual_dev_addr) *(MetricomAddress*)strip_info->dev.dev_addr = zero_address;
+    if (!strip_info->manual_dev_addr)
+        *(MetricomAddress*)strip_info->dev.dev_addr = zero_address;
     strip_info->working = FALSE;
     strip_info->firmware_level = NoStructure;
     strip_info->next_command   = CompatibilityCommand;
     strip_info->watchdog_doprobe = jiffies + 10 * HZ;
     strip_info->watchdog_doreset = jiffies + 1 * HZ;
-    strip_info->tty->driver.write(strip_info->tty, 0, (char *)s, sizeof(s)-1);
+
+    /* If the user has selected a baud rate above 38.4 see what magic we have to do */
+    if (strip_info->user_baud > B38400)
+        {
+        /*
+         * Subtle stuff: Pay attention :-)
+         * If the serial port is currently at the user's selected (>38.4) rate,
+         * then we temporarily switch to 19.2 and issue the ATS304 command
+         * to tell the radio to switch to the user's selected rate.
+         * If the serial port is not currently at that rate, that means we just
+         * issued the ATS304 command last time through, so this time we restore
+         * the user's selected rate and issue the normal starmode reset string.
+         */
+        if (strip_info->user_baud == get_baud(tty))
+	    {
+	    static const char b0[] = "ate0q1s304=57600\r";
+	    static const char b1[] = "ate0q1s304=115200\r";
+	    static const StringDescriptor baudstring[2] =
+                { { b0, sizeof(b0)-1 }, { b1, sizeof(b1)-1 } };
+	    set_baud(tty, B19200);
+	    if      (strip_info->user_baud == B57600 ) s = baudstring[0];
+	    else if (strip_info->user_baud == B115200) s = baudstring[1];
+	    else s = baudstring[1]; /* For now */
+	    }
+        else set_baud(tty, strip_info->user_baud);
+        }
+
+    tty->driver.write(tty, 0, s.string, s.length);
 #ifdef EXT_COUNTERS
-    strip_info->tx_ebytes += sizeof(s) - 1;
+    strip_info->tx_ebytes += s.length;
 #endif
 }
 
@@ -1356,25 +1447,37 @@
     }
 
     /*
+     * If we're sending to ourselves, discard the packet.
+     * (Metricom radios choke if they try to send a packet to their own address.)
+     */
+    if (!memcmp(haddr.c, strip_info->true_dev_addr.c, sizeof(haddr)))
+    {
+        printk(KERN_ERR "%s: Dropping packet addressed to self\n", strip_info->dev.name);
+        return(NULL);
+    }
+
+    /*
      * If this is a broadcast packet, send it to our designated Metricom
      * 'broadcast hub' radio (First byte of address being 0xFF means broadcast)
      */
     if (haddr.c[0] == 0xFF)
     {
-	    memcpy(haddr.c, strip_info->dev.broadcast, sizeof(haddr));
-	    if (haddr.c[0] == 0xFF)
-	    {
-		    strip_info->tx_dropped++;
-		    return(NULL);
-	    }
+ 	struct in_device *in_dev = strip_info->dev.ip_ptr;
+	/* arp_query returns 1 if it succeeds in looking up the address, 0 if it fails */
+        if (!arp_query(haddr.c, in_dev->ifa_list->ifa_broadcast, &strip_info->dev))
+        {
+            printk(KERN_ERR "%s: Unable to send packet (no broadcast hub configured)\n",
+                strip_info->dev.name);
+            return(NULL);
+        }
+	/*
+	 * If we are the broadcast hub, don't bother sending to ourselves.
+	 * (Metricom radios choke if they try to send a packet to their own address.)
+	 */
+        if (!memcmp(haddr.c, strip_info->true_dev_addr.c, sizeof(haddr))) return(NULL);
     }
 
-    /*
-     * If we're sending to ourselves, discard the packet.
-     * (Metricom radios choke if they try to send a packet to their own address.)
-     */
-    if (!memcmp(haddr.c, strip_info->true_dev_addr.c, sizeof(haddr)))
-    	return(NULL);
+    *ptr++ = 0x0D;
     *ptr++ = '*';
     *ptr++ = hextable[haddr.c[2] >> 4];
     *ptr++ = hextable[haddr.c[2] & 0xF];
@@ -1401,9 +1504,11 @@
 
 static void strip_send(struct strip *strip_info, struct sk_buff *skb)
 {
+    MetricomAddress haddr;
     unsigned char *ptr = strip_info->tx_buff;
     int doreset = (long)jiffies - strip_info->watchdog_doreset >= 0;
     int doprobe = (long)jiffies - strip_info->watchdog_doprobe >= 0 && !doreset;
+    struct in_device *in_dev = strip_info->dev.ip_ptr;
 
     /*
      * 1. If we have a packet, encapsulate it and put it in the buffer
@@ -1503,9 +1608,8 @@
      */
     if (strip_info->working && (long)jiffies - strip_info->gratuitous_arp >= 0 &&
         memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) &&
-        *strip_info->dev.broadcast!=0xFF)
+        arp_query(haddr.c, in_dev->ifa_list->ifa_broadcast, &strip_info->dev))
     {
-	struct in_device *in_dev = strip_info->dev.ip_ptr;
         /*printk(KERN_INFO "%s: Sending gratuitous ARP with interval %ld\n",
             strip_info->dev.name, strip_info->arp_interval / HZ);*/
         strip_info->gratuitous_arp = jiffies + strip_info->arp_interval;
@@ -1513,13 +1617,14 @@
         if (strip_info->arp_interval > MaxARPInterval)
             strip_info->arp_interval = MaxARPInterval;
 	if (in_dev && in_dev->ifa_list)
-        arp_send(ARPOP_REPLY, ETH_P_ARP,
-	    in_dev->ifa_list->ifa_address,/* Target address of ARP packet is our address */
-            &strip_info->dev,		/* Device to send packet on */
-            in_dev->ifa_list->ifa_address,/* Source IP address this ARP packet comes from */
-            NULL,			/* Destination HW address is NULL (broadcast it) */
-            strip_info->dev.dev_addr,	/* Source HW address is our HW address */
-            strip_info->dev.dev_addr);	/* Target HW address is our HW address (redundant) */
+	    arp_send(
+		ARPOP_REPLY, ETH_P_ARP,
+		in_dev->ifa_list->ifa_address, /* Target address of ARP packet is our address */
+		&strip_info->dev,	       /* Device to send packet on */
+		in_dev->ifa_list->ifa_address, /* Source IP address this ARP packet comes from */
+		NULL,			       /* Destination HW address is NULL (broadcast it) */
+		strip_info->dev.dev_addr,      /* Source HW address is our HW address */
+		strip_info->dev.dev_addr);     /* Target HW address is our HW address (redundant) */
     }
 
     /*
@@ -1631,7 +1736,7 @@
 static int strip_rebuild_header(struct sk_buff *skb)
 {
 #ifdef CONFIG_INET
-    STRIP_Header *header = (STRIP_Header *)skb->data;
+    STRIP_Header *header = (STRIP_Header *) skb->data;
 
     /* Arp find returns zero if if knows the address, */
     /* or if it doesn't know the address it sends an ARP packet and returns non-zero */
@@ -1775,28 +1880,28 @@
     strip_info->rx_errors++;
 }
 
-static void RecvErr_Message(struct strip *strip_info, __u8 *sendername, const __u8 *msg)
+static void RecvErr_Message(struct strip *strip_info, __u8 *sendername, const __u8 *msg, u_long len)
 {
-    if (has_prefix(msg, "001")) /* Not in StarMode! */
+    if (has_prefix(msg, len, "001")) /* Not in StarMode! */
     {
         RecvErr("Error Msg:", strip_info);
         printk(KERN_INFO "%s: Radio %s is not in StarMode\n",
             strip_info->dev.name, sendername);
     }
 
-    else if (has_prefix(msg, "002")) /* Remap handle */
+    else if (has_prefix(msg, len, "002")) /* Remap handle */
     {
 	/* We ignore "Remap handle" messages for now */
     }
 
-    else if (has_prefix(msg, "003")) /* Can't resolve name */
+    else if (has_prefix(msg, len, "003")) /* Can't resolve name */
     {
         RecvErr("Error Msg:", strip_info);
         printk(KERN_INFO "%s: Destination radio name is unknown\n",
             strip_info->dev.name);
     }
 
-    else if (has_prefix(msg, "004")) /* Name too small or missing */
+    else if (has_prefix(msg, len, "004")) /* Name too small or missing */
     {
         strip_info->watchdog_doreset = jiffies + LongTime;
 #if TICKLE_TIMERS
@@ -1825,45 +1930,50 @@
         }
         if (strip_info->firmware_level >= StructuredMessages)
         {
+            /*
+             * If this message has a valid checksum on the end, then the call to verify_checksum
+             * will elevate the firmware_level to ChecksummedMessages for us. (The actual return
+             * code from verify_checksum is ignored here.)
+             */
             verify_checksum(strip_info);
             /*
-             * If the radio has structured messages but we don't yet have all our information about it, we should do
-             * probes without delay, until we have gathered all the information
+             * If the radio has structured messages but we don't yet have all our information about it,
+             * we should do probes without delay, until we have gathered all the information
              */
             if (!GOT_ALL_RADIO_INFO(strip_info)) strip_info->watchdog_doprobe = jiffies;
         }
     }
 
-    else if (has_prefix(msg, "005")) /* Bad count specification */
+    else if (has_prefix(msg, len, "005")) /* Bad count specification */
         RecvErr("Error Msg:", strip_info);
 
-    else if (has_prefix(msg, "006")) /* Header too big */
+    else if (has_prefix(msg, len, "006")) /* Header too big */
         RecvErr("Error Msg:", strip_info);
 
-    else if (has_prefix(msg, "007")) /* Body too big */
+    else if (has_prefix(msg, len, "007")) /* Body too big */
     {
         RecvErr("Error Msg:", strip_info);
         printk(KERN_ERR "%s: Error! Packet size too big for radio.\n",
             strip_info->dev.name);
     }
 
-    else if (has_prefix(msg, "008")) /* Bad character in name */
+    else if (has_prefix(msg, len, "008")) /* Bad character in name */
     {
         RecvErr("Error Msg:", strip_info);
         printk(KERN_ERR "%s: Radio name contains illegal character\n",
             strip_info->dev.name);
     }
 
-    else if (has_prefix(msg, "009")) /* No count or line terminator */
+    else if (has_prefix(msg, len, "009")) /* No count or line terminator */
         RecvErr("Error Msg:", strip_info);
 
-    else if (has_prefix(msg, "010")) /* Invalid checksum */
+    else if (has_prefix(msg, len, "010")) /* Invalid checksum */
         RecvErr("Error Msg:", strip_info);
 
-    else if (has_prefix(msg, "011")) /* Checksum didn't match */
+    else if (has_prefix(msg, len, "011")) /* Checksum didn't match */
         RecvErr("Error Msg:", strip_info);
 
-    else if (has_prefix(msg, "012")) /* Failed to transmit packet */
+    else if (has_prefix(msg, len, "012")) /* Failed to transmit packet */
         RecvErr("Error Msg:", strip_info);
 
     else
@@ -1872,9 +1982,11 @@
 
 static void process_AT_response(struct strip *strip_info, __u8 *ptr, __u8 *end)
 {
+    u_long len;
     __u8 *p = ptr;
     while (p < end && p[-1] != 10) p++; /* Skip past first newline character */
     /* Now ptr points to the AT command, and p points to the text of the response. */
+    len = p-ptr;
 
 #if TICKLE_TIMERS
     {
@@ -1885,13 +1997,13 @@
     }
 #endif
 
-    if      (has_prefix(ptr, "ATS300?" )) get_radio_version(strip_info, p, end);
-    else if (has_prefix(ptr, "ATS305?" )) get_radio_address(strip_info, p);
-    else if (has_prefix(ptr, "ATS311?" )) get_radio_neighbours(&strip_info->poletops, p, end);
-    else if (has_prefix(ptr, "ATS319=7")) verify_checksum(strip_info);
-    else if (has_prefix(ptr, "ATS325?" )) get_radio_voltage(strip_info, p, end);
-    else if (has_prefix(ptr, "AT~LA"   )) get_radio_neighbours(&strip_info->portables, p, end);
-    else                                              RecvErr("Unknown AT Response:", strip_info);
+    if      (has_prefix(ptr, len, "ATS300?" )) get_radio_version(strip_info, p, end);
+    else if (has_prefix(ptr, len, "ATS305?" )) get_radio_address(strip_info, p);
+    else if (has_prefix(ptr, len, "ATS311?" )) get_radio_neighbours(&strip_info->poletops, p, end);
+    else if (has_prefix(ptr, len, "ATS319=7")) verify_checksum(strip_info);
+    else if (has_prefix(ptr, len, "ATS325?" )) get_radio_voltage(strip_info, p, end);
+    else if (has_prefix(ptr, len, "AT~LA"   )) get_radio_neighbours(&strip_info->portables, p, end);
+    else                                       RecvErr("Unknown AT Response:", strip_info);
 }
 
 static void process_ACK(struct strip *strip_info, __u8 *ptr, __u8 *end)
@@ -2072,11 +2184,11 @@
 
     if (text_equal(msg, len, "OK"      )) return; /* Ignore 'OK' responses from prior commands */
     if (text_equal(msg, len, "ERROR"   )) return; /* Ignore 'ERROR' messages */
-    if (text_equal(msg, len, InitString)) return; /* Ignore character echo back from the radio */
+    if (has_prefix(msg, len, "ate0q1"  )) return; /* Ignore character echo back from the radio */
 
     /* Catch other error messages */
     /* (This is here for backwards compatibility with old firmware) */
-    if (has_prefix(msg, "ERR_")) { RecvErr_Message(strip_info, NULL, &msg[4]); return; }
+    if (has_prefix(msg, len, "ERR_")) { RecvErr_Message(strip_info, NULL, &msg[4], len-4); return; }
     
     RecvErr("No initial *", strip_info);
 }
@@ -2181,7 +2293,7 @@
       process_Info(strip_info, ptr, end);
     } else if (key.l == ERR_Key.l) {
       strip_info->rx_ebytes += (end - ptr);
-      RecvErr_Message(strip_info, sendername, ptr);
+      RecvErr_Message(strip_info, sendername, ptr, end-ptr);
     } else RecvErr("Unrecognized protocol key", strip_info);
 #else
     if      (key.l == SIP0Key.l) process_IP_packet  (strip_info, &header, ptr, end);
@@ -2189,7 +2301,7 @@
     else if (key.l == ATR_Key.l) process_AT_response(strip_info, ptr, end);
     else if (key.l == ACK_Key.l) process_ACK        (strip_info, ptr, end);
     else if (key.l == INF_Key.l) process_Info       (strip_info, ptr, end);
-    else if (key.l == ERR_Key.l) RecvErr_Message    (strip_info, sendername, ptr);
+    else if (key.l == ERR_Key.l) RecvErr_Message    (strip_info, sendername, ptr, end-ptr);
     else                         RecvErr("Unrecognized protocol key", strip_info);
 #endif
 }
@@ -2305,12 +2417,12 @@
     return 0;
 }
 
-static struct net_device_stats *strip_get_stats(struct device *dev)
+static struct enet_statistics *strip_get_stats(struct device *dev)
 {
-    static struct net_device_stats stats;
+    static struct enet_statistics stats;
     struct strip *strip_info = (struct strip *)(dev->priv);
 
-    memset(&stats, 0, sizeof(struct net_device_stats));
+    memset(&stats, 0, sizeof(struct enet_statistics));
 
     stats.rx_packets     = strip_info->rx_packets;
     stats.tx_packets     = strip_info->tx_packets;
@@ -2353,6 +2465,7 @@
 static int strip_open_low(struct device *dev)
 {
     struct strip *strip_info = (struct strip *)(dev->priv);
+    struct in_device *in_dev = dev->ip_ptr;
 
     if (strip_info->tty == NULL)
         return(-ENODEV);
@@ -2360,19 +2473,27 @@
     if (!allocate_buffers(strip_info))
         return(-ENOMEM);
 
+    strip_info->sx_count = 0;
+    strip_info->tx_left  = 0;
+
     strip_info->discard  = 0;
     strip_info->working  = FALSE;
     strip_info->firmware_level = NoStructure;
     strip_info->next_command   = CompatibilityCommand;
-    strip_info->sx_count = 0;
-    strip_info->tx_left  = 0;
+    strip_info->user_baud      = get_baud(strip_info->tty);
 
+    /*
+     * Needed because address '0' is special
+     */
+
+    if (in_dev->ifa_list->ifa_address == 0)
+        in_dev->ifa_list->ifa_address = ntohl(0xC0A80001);
     dev->tbusy  = 0;
     dev->start  = 1;
 
     printk(KERN_INFO "%s: Initializing Radio.\n", strip_info->dev.name);
     ResetRadio(strip_info);
-    strip_info->idle_timer.expires  = jiffies + 2 * HZ;
+    strip_info->idle_timer.expires = jiffies + 1*HZ;
     add_timer(&strip_info->idle_timer);
     return(0);
 }
@@ -2442,12 +2563,6 @@
     dev->addr_len           = sizeof(MetricomAddress);
 
     /*
-     * Pointer to the interface buffers.
-     */
-
-   dev_init_buffers(dev);
-
-    /*
      * Pointers to interface service routines.
      */
 
@@ -2628,6 +2743,7 @@
     if (!strip_info || strip_info->magic != STRIP_MAGIC)
         return;
 
+    dev_close(&strip_info->dev);
     unregister_netdev(&strip_info->dev);
 
     tty->disc_data = 0;
@@ -2648,7 +2764,6 @@
     unsigned int cmd, unsigned long arg)
 {
     struct strip *strip_info = (struct strip *) tty->disc_data;
-    int err;
 
     /*
      * First make sure we're connected.
@@ -2660,21 +2775,18 @@
     switch(cmd)
     {
         case SIOCGIFNAME:
-            err = verify_area(VERIFY_WRITE, (void*)arg, 16);
-            if (err)
-                return -err;
-            return copy_to_user((void*)arg, strip_info->dev.name,
-                strlen(strip_info->dev.name) + 1)?-EFAULT:0;
-
+	    return copy_to_user((void*)arg, strip_info->dev.name,
+				strlen(strip_info->dev.name) + 1) ? 
+		-EFAULT : 0;
+	    break;
         case SIOCSIFHWADDR:
             {
             MetricomAddress addr;
             printk(KERN_INFO "%s: SIOCSIFHWADDR\n", strip_info->dev.name);
-            if(copy_from_user(&addr, (void*)arg, sizeof(MetricomAddress)))
-            	return -EFAULT;
-            return(set_mac_address(strip_info, &addr));
-            }
-
+	    return copy_from_user(&addr, (void*)arg, sizeof(MetricomAddress)) ?
+		-EFAULT : set_mac_address(strip_info, &addr);
+	    break;
+	    }
         /*
          * Allow stty to read, but not set, the serial port
          */
@@ -2683,9 +2795,10 @@
         case TCGETA:
             return n_tty_ioctl(tty, (struct file *) file, cmd,
                 (unsigned long) arg);
-
+	    break;
         default:
             return -ENOIOCTLCMD;
+	    break;
     }
 }
 
diff -u --recursive --new-file v2.1.124/linux/drivers/net/via-rhine.c linux/drivers/net/via-rhine.c
--- v2.1.124/linux/drivers/net/via-rhine.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/via-rhine.c	Wed Oct  7 15:51:45 1998
@@ -0,0 +1,1314 @@
+/* via-rhine.c: A Linux Ethernet device driver for VIA Rhine family chips. */
+/*
+	Written 1998 by Donald Becker.
+
+	This software may be used and distributed according to the terms
+	of the GNU Public License (GPL), incorporated herein by reference.
+	Drivers derived from this code also fall under the GPL and must retain
+	this authorship and copyright notice.
+
+	This driver is designed for the VIA VT86c100A Rhine-II PCI Fast Ethernet
+	controller.  It also works with the older 3043 Rhine-I chip.
+
+	The author may be reached as becker@cesdis.edu, or
+	Donald Becker
+	312 Severn Ave. #W302
+	Annapolis MD 21403
+
+	Support and updates available at
+	http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html
+*/
+
+static const char *versionA =
+"via-rhine.c:v1.00 9/5/98  Written by Donald Becker\n";
+static const char *versionB =
+"  http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html\n";
+
+/* A few user-configurable values.   These may be modified when a driver
+   module is loaded.*/
+
+static int debug = 1;			/* 1 normal messages, 0 quiet .. 7 verbose. */
+static int max_interrupt_work = 20;
+static int min_pci_latency = 64;
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+   Setting to > 1518 effectively disables this feature. */
+static int rx_copybreak = 0;
+
+/* Used to pass the media type, etc.
+   Both 'options[]' and 'full_duplex[]' should exist for driver
+   interoperability.
+   The media type is usually passed in 'options[]'.
+*/
+#define MAX_UNITS 8		/* More are supported, limit only on options */
+static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
+   The Rhine has a 64 element 8390-like hash table.  */
+static const int multicast_filter_limit = 32;
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency.
+   The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+   Making the Tx ring too large decreases the effectiveness of channel
+   bonding and packet priority.
+   There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE	8
+#define RX_RING_SIZE	16
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (2*HZ)
+
+#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
+
+/* Include files, designed to support most kernel versions 2.0.0 and later. */
+#include <linux/config.h>
+#include <linux/version.h>
+#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+#include <linux/module.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <asm/processor.h>		/* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+/* This driver was written to use PCI memory space, however some boards
+   only work with I/O space accesses. */
+#define VIA_USE_IO
+#ifdef VIA_USE_IO
+#undef readb
+#undef readw
+#undef readl
+#undef writeb
+#undef writew
+#undef writel
+#define readb inb
+#define readw inw
+#define readl inl
+#define writeb outb
+#define writew outw
+#define writel outl
+#endif
+
+/* Kernel compatibility defines, some common to David Hind's PCMCIA package.
+   This is only in the support-all-kernels source code. */
+
+#define RUN_AT(x) (jiffies + (x))
+
+#if (LINUX_VERSION_CODE >= 0x20100)
+char kernel_version[] = UTS_RELEASE;
+#else
+#ifndef __alpha__
+#define ioremap vremap
+#define iounmap vfree
+#endif
+#endif
+#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115
+MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
+MODULE_DESCRIPTION("VIA Rhine PCI Fast Ethernet driver");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(min_pci_latency, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+#endif
+#if LINUX_VERSION_CODE < 0x20123
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#endif
+#if LINUX_VERSION_CODE <= 0x20139
+#define	net_device_stats enet_statistics
+#else
+#define NETSTATS_VER2
+#endif
+#if LINUX_VERSION_CODE < 0x20155  ||  defined(CARDBUS)
+/* Grrrr, the PCI code changed, but did not consider CardBus... */
+#include <linux/bios32.h>
+#define PCI_SUPPORT_VER1
+#else
+#define PCI_SUPPORT_VER2
+#endif
+#if LINUX_VERSION_CODE < 0x20159
+#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE);
+#else
+#define dev_free_skb(skb) dev_kfree_skb(skb);
+#endif
+
+
+/*
+				Theory of Operation
+
+I. Board Compatibility
+
+This driver is designed for the VIA 86c100A Rhine-II PCI Fast Ethernet
+controller.
+
+II. Board-specific settings
+
+Boards with this chip are functional only in a bus-master PCI slot.
+
+Many operational settings are loaded from the EEPROM to the Config word at
+offset 0x78.  This driver assumes that they are correct.
+If this driver is compiled to use PCI memory space operations the EEPROM
+must be configured to enable memory ops.
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+This driver uses two statically allocated fixed-size descriptor lists
+formed into rings by a branch from the final descriptor to the beginning of
+the list.  The ring sizes are set at compile time by RX/TX_RING_SIZE.
+
+IIIb/c. Transmit/Receive Structure
+
+This driver attempts to use a zero-copy receive and transmit scheme.
+
+Alas, all data buffers are required to start on a 32 bit boundary, so
+the driver must often copy transmit packets into bounce buffers.
+
+The driver allocates full frame size skbuffs for the Rx ring buffers at
+open() time and passes the skb->data field to the chip as receive data
+buffers.  When an incoming frame is less than RX_COPYBREAK bytes long,
+a fresh skbuff is allocated and the frame is copied to the new skbuff.
+When the incoming frame is larger, the skbuff is passed directly up the
+protocol stack.  Buffers consumed this way are replaced by newly allocated
+skbuffs in the last phase of netdev_rx().
+
+The RX_COPYBREAK value is chosen to trade-off the memory wasted by
+using a full-sized skbuff for small frames vs. the copying costs of larger
+frames.  New boards are typically used in generously configured machines
+and the underfilled buffers have negligible impact compared to the benefit of
+a single allocation size, so the default value of zero results in never
+copying packets.  When copying is done, the cost is usually mitigated by using
+a combined copy/checksum routine.  Copying also preloads the cache, which is
+most useful with small frames.
+
+Since the VIA chips are only able to transfer data to buffers on 32 bit
+boundaries, the the IP header at offset 14 in an ethernet frame isn't
+longword aligned for further processing.  Copying these unaligned buffers
+has the beneficial effect of 16-byte aligning the IP header.
+
+IIId. Synchronization
+
+The driver runs as two independent, single-threaded flows of control.  One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag.  The other thread is the interrupt handler, which is single
+threaded by the hardware and interrupt handling software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'lp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring.  After reaping the stats, it marks the Tx queue entry as
+empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it
+clears both the tx_full and tbusy flags.
+
+IV. Notes
+
+IVb. References
+
+Preliminary VT86C100A manual from http://www.via.com.tw/
+http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html
+http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+
+IVc. Errata
+
+The VT86C100A manual is not reliable information.
+The chip does not handle unaligned transmit or receive buffers, resulting
+in significant performance degradation for bounce buffer copies on transmit
+and unaligned IP headers on receive.
+The chip does not pad to minimum transmit length.
+
+*/
+
+
+
+/* This table drives the PCI probe routines.  It's mostly boilerplate in all
+   of the drivers, and will likely be provided by some future kernel.
+   Note the matching code -- the first table entry matchs all 56** cards but
+   second only the 1234 card.
+*/
+enum pci_flags_bit {
+	PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+	PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
+};
+struct pci_id_info {
+	const char *name;
+	u16	vendor_id, device_id, device_id_mask, flags;
+	int io_size;
+	struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev,
+							 long ioaddr, int irq, int chip_idx, int fnd_cnt);
+};
+
+static struct device *via_probe1(int pci_bus, int pci_devfn,
+								 struct device *dev, long ioaddr, int irq,
+								 int chp_idx, int fnd_cnt);
+
+static struct pci_id_info pci_tbl[] = {
+	{ "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,
+	  PCI_USES_IO|PCI_USES_MEM|PCI_USES_MASTER, 128, via_probe1},
+	{0,},						/* 0 terminated list. */
+};
+
+
+/* A chip capabilities table, matching the entries in pci_tbl[] above. */
+enum chip_capability_flags {CanHaveMII=1, };
+struct chip_info {
+	int io_size;
+	int flags;
+} static cap_tbl[] = {
+	{128, CanHaveMII, },
+	{128, CanHaveMII, },
+};
+
+
+/* Offsets to the device registers.
+*/
+enum register_offsets {
+	StationAddr=0x00, RxConfig=0x06, TxConfig=0x07, ChipCmd=0x08,
+	IntrStatus=0x0C, IntrEnable=0x0E,
+	MulticastFilter0=0x10, MulticastFilter1=0x14,
+	RxRingPtr=0x18, TxRingPtr=0x1C,
+	MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIConfig=0x6E,
+	MIICmd=0x70, MIIRegAddr=0x71, MIIData=0x72,
+	Config=0x78, RxMissed=0x7C, RxCRCErrs=0x7E,
+};
+
+/* Bits in the interrupt status/mask registers. */
+enum intr_status_bits {
+	IntrRxDone=0x0001, IntrRxErr=0x0004, IntrRxEmpty=0x0020,
+	IntrTxDone=0x0002, IntrTxAbort=0x0008, IntrTxUnderrun=0x0010,
+	IntrPCIErr=0x0040,
+	IntrStatsMax=0x0080, IntrRxEarly=0x0100, IntrMIIChange=0x0200,
+	IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000,
+	IntrTxAborted=0x2000, IntrLinkChange=0x4000,
+	IntrRxWakeUp=0x8000,
+	IntrNormalSummary=0x0003, IntrAbnormalSummary=0x8260,
+};
+
+
+/* The Rx and Tx buffer descriptors. */
+struct rx_desc {
+	u16 rx_status;
+	u16 rx_length;
+	u32 desc_length;
+	u32 addr;
+	u32 next_desc;
+};
+struct tx_desc {
+	u16 tx_status;
+	u16 tx_own;
+	u32 desc_length;
+	u32 addr;
+	u32 next_desc;
+};
+
+/* Bits in *_desc.status */
+enum rx_status_bits {
+	RxDescOwn=0x80000000, RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F};
+enum desc_status_bits {
+	DescOwn=0x8000, DescEndPacket=0x4000, DescIntr=0x1000,
+};
+
+/* Bits in ChipCmd. */
+enum chip_cmd_bits {
+	CmdInit=0x0001, CmdStart=0x0002, CmdStop=0x0004, CmdRxOn=0x0008,
+	CmdTxOn=0x0010, CmdTxDemand=0x0020, CmdRxDemand=0x0040,
+	CmdEarlyRx=0x0100, CmdEarlyTx=0x0200, CmdFDuplex=0x0400,
+	CmdNoTxPoll=0x0800, CmdReset=0x8000,
+};
+
+struct netdev_private {
+	/* Descriptor rings first for alignment. */
+	struct rx_desc rx_ring[RX_RING_SIZE];
+	struct tx_desc tx_ring[TX_RING_SIZE];
+	/* The addresses of receive-in-place skbuffs. */
+	struct sk_buff* rx_skbuff[RX_RING_SIZE];
+	/* The saved address of a sent-in-place packet/buffer, for later free(). */
+	struct sk_buff* tx_skbuff[TX_RING_SIZE];
+	unsigned char *tx_buf[TX_RING_SIZE];	/* Tx bounce buffers */
+	unsigned char *tx_bufs;				/* Tx bounce buffer region. */
+	struct device *next_module;			/* Link for devices of this type. */
+	struct net_device_stats stats;
+	struct timer_list timer;	/* Media monitoring timer. */
+	unsigned char pci_bus, pci_devfn;
+	/* Frequently used values: keep some adjacent for cache effect. */
+	int chip_id;
+	long in_interrupt;			/* Word-long for SMP locks. */
+	struct rx_desc *rx_head_desc;
+	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */
+	unsigned int cur_tx, dirty_tx;
+	unsigned int rx_buf_sz;				/* Based on MTU+slack. */
+	u16 chip_cmd;						/* Current setting for ChipCmd */
+	unsigned int tx_full:1;				/* The Tx queue is full. */
+	/* These values are keep track of the transceiver/media in use. */
+	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
+	unsigned int duplex_lock:1;
+	unsigned int medialock:1;			/* Do not sense media. */
+	unsigned int default_port:4;		/* Last dev->if_port value. */
+	u8 tx_thresh, rx_thresh;
+	/* MII transceiver section. */
+	int mii_cnt;						/* MII device addresses. */
+	u16 advertising;					/* NWay media advertisement */
+	unsigned char phys[2];				/* MII device addresses. */
+};
+
+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 value);
+static int  netdev_open(struct device *dev);
+static void check_duplex(struct device *dev);
+static void netdev_timer(unsigned long data);
+static void tx_timeout(struct device *dev);
+static void init_ring(struct device *dev);
+static int  start_tx(struct sk_buff *skb, struct device *dev);
+static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
+static int  netdev_rx(struct device *dev);
+static void netdev_error(struct device *dev, int intr_status);
+static void set_rx_mode(struct device *dev);
+static struct net_device_stats *get_stats(struct device *dev);
+static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+static int  netdev_close(struct device *dev);
+
+
+
+/* A list of our installed devices, for removing the driver module. */
+static struct device *root_net_dev = NULL;
+
+/* Ideally we would detect all network cards in slot order.  That would
+   be best done a central PCI probe dispatch, which wouldn't work
+   well when dynamically adding drivers.  So instead we detect just the
+   cards we know about in slot order. */
+
+static int pci_etherdev_probe(struct device *dev, struct pci_id_info pci_tbl[])
+{
+	int cards_found = 0;
+	int pci_index = 0;
+	unsigned char pci_bus, pci_device_fn;
+
+	if ( ! pcibios_present())
+		return -ENODEV;
+
+	for (;pci_index < 0xff; pci_index++) {
+		u16 vendor, device, pci_command, new_command;
+		int chip_idx, irq;
+		long pciaddr;
+		long ioaddr;
+
+		if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index,
+								&pci_bus, &pci_device_fn)
+			!= PCIBIOS_SUCCESSFUL)
+			break;
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_VENDOR_ID, &vendor);
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_DEVICE_ID, &device);
+
+		for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++)
+			if (vendor == pci_tbl[chip_idx].vendor_id
+				&& (device & pci_tbl[chip_idx].device_id_mask) ==
+				pci_tbl[chip_idx].device_id)
+				break;
+		if (pci_tbl[chip_idx].vendor_id == 0) 		/* Compiled out! */
+			continue;
+
+		{
+#if defined(PCI_SUPPORT_VER2)
+			struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
+#ifdef VIA_USE_IO
+			pciaddr = pdev->base_address[0];
+#else
+			pciaddr = pdev->base_address[1];
+#endif
+			irq = pdev->irq;
+#else
+			u32 pci_memaddr;
+			u8 pci_irq_line;
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_INTERRUPT_LINE, &pci_irq_line);
+#ifdef VIA_USE_IO
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+									  PCI_BASE_ADDRESS_0, &pci_memaddr);
+			pciaddr = pci_memaddr;
+#else
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+									  PCI_BASE_ADDRESS_1, &pci_memaddr);
+			pciaddr = pci_memaddr;
+#endif
+			irq = pci_irq_line;
+#endif
+		}
+
+		if (debug > 2)
+			printk(KERN_INFO "Found %s at PCI address %#lx, IRQ %d.\n",
+				   pci_tbl[chip_idx].name, pciaddr, irq);
+
+		if (pci_tbl[chip_idx].flags & PCI_USES_IO) {
+			if (check_region(pciaddr, pci_tbl[chip_idx].io_size))
+				continue;
+			ioaddr = pciaddr & ~3;
+		} else if ((ioaddr = (long)ioremap(pciaddr & ~0xf,
+										 pci_tbl[chip_idx].io_size)) == 0) {
+			printk(KERN_INFO "Failed to map PCI address %#lx.\n",
+				   pciaddr);
+			continue;
+		}
+
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_COMMAND, &pci_command);
+		new_command = pci_command | (pci_tbl[chip_idx].flags & 7);
+		if (pci_command != new_command) {
+			printk(KERN_INFO "  The PCI BIOS has not enabled the"
+				   " device at %d/%d!  Updating PCI command %4.4x->%4.4x.\n",
+				   pci_bus, pci_device_fn, pci_command, new_command);
+			pcibios_write_config_word(pci_bus, pci_device_fn,
+									  PCI_COMMAND, new_command);
+		}
+
+		dev = pci_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, ioaddr,
+									   irq, chip_idx, cards_found);
+
+		if (dev  && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) {
+			u8 pci_latency;
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_LATENCY_TIMER, &pci_latency);
+			if (pci_latency < min_pci_latency) {
+				printk(KERN_INFO "  PCI latency timer (CFLT) is "
+					   "unreasonably low at %d.  Setting to %d clocks.\n",
+					   pci_latency, min_pci_latency);
+				pcibios_write_config_byte(pci_bus, pci_device_fn,
+										  PCI_LATENCY_TIMER, min_pci_latency);
+			}
+		}
+		dev = 0;
+		cards_found++;
+	}
+
+	return cards_found ? 0 : -ENODEV;
+}
+
+#ifndef MODULE
+static int via_rhine_probe(struct device *dev)
+{
+	printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
+	return pci_etherdev_probe(dev, pci_tbl);
+}
+#endif
+
+static struct device *via_probe1(int pci_bus, int pci_devfn,
+								 struct device *dev, long ioaddr, int irq,
+								 int chip_id, int card_idx)
+{
+	struct netdev_private *np;
+	int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0;
+
+	dev = init_etherdev(dev, 0);
+
+	printk(KERN_INFO "%s: %s at 0x%lx, ",
+		   dev->name, pci_tbl[chip_id].name, ioaddr);
+
+	/* Ideally we would be read the EEPROM but access may be locked. */
+	for (i = 0; i <6; i++)
+		dev->dev_addr[i] = readb(ioaddr + StationAddr + i);
+	for (i = 0; i < 5; i++)
+			printk("%2.2x:", dev->dev_addr[i]);
+	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
+
+#ifdef VIA_USE_IO
+	request_region(ioaddr, pci_tbl[chip_id].io_size, dev->name);
+#endif
+
+	/* Reset the chip to erase previous misconfiguration. */
+	writew(CmdReset, ioaddr + ChipCmd);
+
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+
+	/* Make certain the descriptor lists are cache-aligned. */
+	np = (void *)(((long)kmalloc(sizeof(*np), GFP_KERNEL) + 31) & ~31);
+	memset(np, 0, sizeof(*np));
+	dev->priv = np;
+
+	np->next_module = root_net_dev;
+	root_net_dev = dev;
+
+	np->pci_bus = pci_bus;
+	np->pci_devfn = pci_devfn;
+	np->chip_id = chip_id;
+
+	if (dev->mem_start)
+		option = dev->mem_start;
+
+	/* The lower four bits are the media type. */
+	if (option > 0) {
+		if (option & 0x200)
+			np->full_duplex = 1;
+		np->default_port = option & 15;
+		if (np->default_port)
+			np->medialock = 1;
+	}
+	if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0)
+		np->full_duplex = 1;
+
+	if (np->full_duplex)
+		np->duplex_lock = 1;
+
+	/* The chip-specific entries in the device structure. */
+	dev->open = &netdev_open;
+	dev->hard_start_xmit = &start_tx;
+	dev->stop = &netdev_close;
+	dev->get_stats = &get_stats;
+	dev->set_multicast_list = &set_rx_mode;
+	dev->do_ioctl = &mii_ioctl;
+
+	if (cap_tbl[np->chip_id].flags & CanHaveMII) {
+		int phy, phy_idx = 0;
+		np->phys[0] = 1;		/* Standard for this chip. */
+		for (phy = 1; phy < 32 && phy_idx < 4; phy++) {
+			int mii_status = mdio_read(dev, phy, 1);
+			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
+				np->phys[phy_idx++] = phy;
+				np->advertising = mdio_read(dev, phy, 4);
+				printk(KERN_INFO "%s: MII PHY found at address %d, status "
+					   "0x%4.4x advertising %4.4x Link %4.4x.\n",
+					   dev->name, phy, mii_status, np->advertising,
+					   mdio_read(dev, phy, 5));
+			}
+		}
+		np->mii_cnt = phy_idx;
+	}
+
+	return dev;
+}
+
+
+/* Read and write over the MII Management Data I/O (MDIO) interface. */
+
+static int mdio_read(struct device *dev, int phy_id, int regnum)
+{
+	long ioaddr = dev->base_addr;
+	int boguscnt = 1024;
+
+	/* Wait for a previous command to complete. */
+	while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0)
+		;
+	writeb(0x00, ioaddr + MIICmd);
+	writeb(phy_id, ioaddr + MIIPhyAddr);
+	writeb(regnum, ioaddr + MIIRegAddr);
+	writeb(0x40, ioaddr + MIICmd);			/* Trigger read */
+	boguscnt = 1024;
+	while ((readb(ioaddr + MIICmd) & 0x40) && --boguscnt > 0)
+		;
+	return readw(ioaddr + MIIData);
+}
+
+static void mdio_write(struct device *dev, int phy_id, int regnum, int value)
+{
+	long ioaddr = dev->base_addr;
+	int boguscnt = 1024;
+
+	/* Wait for a previous command to complete. */
+	while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0)
+		;
+	writeb(0x00, ioaddr + MIICmd);
+	writeb(phy_id, ioaddr + MIIPhyAddr);
+	writeb(regnum, ioaddr + MIIRegAddr);
+	writew(value, ioaddr + MIIData);
+	writeb(0x20, ioaddr + MIICmd);			/* Trigger write. */
+	return;
+}
+
+
+static int netdev_open(struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int i;
+
+	/* Reset the chip. */
+	writew(CmdReset, ioaddr + ChipCmd);
+
+	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev))
+		return -EAGAIN;
+
+	if (debug > 1)
+		printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
+			   dev->name, dev->irq);
+
+	MOD_INC_USE_COUNT;
+
+	init_ring(dev);
+
+	writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr);
+	writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr);
+
+	for (i = 0; i < 6; i++)
+		writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
+
+	/* Initialize other registers. */
+	writew(0x0006, ioaddr + PCIConfig);	/* Tune configuration??? */
+	/* Configure the FIFO thresholds. */
+	writeb(0x20, ioaddr + TxConfig);	/* Initial threshold 32 bytes */
+	np->tx_thresh = 0x20;
+	np->rx_thresh = 0x60;				/* Written in set_rx_mode(). */
+
+	if (dev->if_port == 0)
+		dev->if_port = np->default_port;
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	np->in_interrupt = 0;
+
+	set_rx_mode(dev);
+
+	dev->start = 1;
+
+	/* Enable interrupts by setting the interrupt mask. */
+	writew(IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow| IntrRxDropped|
+		   IntrTxDone | IntrTxAbort | IntrTxUnderrun |
+		   IntrPCIErr | IntrStatsMax | IntrLinkChange | IntrMIIChange,
+		   ioaddr + IntrEnable);
+
+	np->chip_cmd = CmdStart|CmdTxOn|CmdRxOn|CmdNoTxPoll;
+	writew(np->chip_cmd, ioaddr + ChipCmd);
+
+	check_duplex(dev);
+
+	if (debug > 2)
+		printk(KERN_DEBUG "%s: Done netdev_open(), status %4.4x "
+			   "MII status: %4.4x.\n",
+			   dev->name, readw(ioaddr + ChipCmd),
+			   mdio_read(dev, np->phys[0], 1));
+
+	/* Set the timer to check for link beat. */
+	init_timer(&np->timer);
+	np->timer.expires = RUN_AT(1);
+	np->timer.data = (unsigned long)dev;
+	np->timer.function = &netdev_timer;				/* timer handler */
+	add_timer(&np->timer);
+
+	return 0;
+}
+
+static void check_duplex(struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int mii_reg5 = mdio_read(dev, np->phys[0], 5);
+	int duplex;
+
+	if (np->duplex_lock  ||  mii_reg5 == 0xffff)
+		return;
+	duplex = (mii_reg5 & 0x0100) || (mii_reg5 & 0x01C0) == 0x0040;
+	if (np->full_duplex != duplex) {
+		np->full_duplex = duplex;
+		if (debug)
+			printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link"
+				   " partner capability of %4.4x.\n", dev->name,
+				   duplex ? "full" : "half", np->phys[0], mii_reg5);
+		if (duplex)
+			np->chip_cmd |= CmdFDuplex;
+		else
+			np->chip_cmd &= ~CmdFDuplex;
+		writew(np->chip_cmd, ioaddr + ChipCmd);
+	}
+}
+
+static void netdev_timer(unsigned long data)
+{
+	struct device *dev = (struct device *)data;
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int next_tick = 10*HZ;
+
+	if (debug > 3) {
+		printk(KERN_DEBUG "%s: VIA Rhine monitor tick, status %4.4x.\n",
+			   dev->name, readw(ioaddr + IntrStatus));
+	}
+	check_duplex(dev);
+
+	np->timer.expires = RUN_AT(next_tick);
+	add_timer(&np->timer);
+}
+
+static void tx_timeout(struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+
+	printk(KERN_WARNING "%s: Transmit timed out, status %4.4x, PHY status "
+		   "%4.4x, resetting...\n",
+		   dev->name, readw(ioaddr + IntrStatus),
+		   mdio_read(dev, np->phys[0], 1));
+
+  /* Perhaps we should reinitialize the hardware here. */
+  dev->if_port = 0;
+  /* Stop and restart the chip's Tx processes . */
+
+  /* Trigger an immediate transmit demand. */
+
+  dev->trans_start = jiffies;
+  np->stats.tx_errors++;
+  return;
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void init_ring(struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	int i;
+
+	np->tx_full = 0;
+	np->cur_rx = np->cur_tx = 0;
+	np->dirty_rx = np->dirty_tx = 0;
+
+	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+	np->rx_head_desc = &np->rx_ring[0];
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		np->rx_ring[i].rx_status = 0;
+		np->rx_ring[i].rx_length = 0;
+		np->rx_ring[i].desc_length = np->rx_buf_sz;
+		np->rx_ring[i].next_desc = virt_to_bus(&np->rx_ring[i+1]);
+		np->rx_skbuff[i] = 0;
+	}
+	/* Mark the last entry as wrapping the ring. */
+	np->rx_ring[i-1].next_desc = virt_to_bus(&np->rx_ring[0]);
+
+	/* Fill in the Rx buffers. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz);
+		np->rx_skbuff[i] = skb;
+		if (skb == NULL)
+			break;
+		skb->dev = dev;			/* Mark as being used by this device. */
+		np->rx_ring[i].addr = virt_to_bus(skb->tail);
+		np->rx_ring[i].rx_status = 0;
+		np->rx_ring[i].rx_length = DescOwn;
+	}
+	np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		np->tx_skbuff[i] = 0;
+		np->tx_ring[i].tx_own = 0;
+		np->tx_ring[i].desc_length = 0x00e08000;
+		np->tx_ring[i].next_desc = virt_to_bus(&np->tx_ring[i+1]);
+		np->tx_buf[i] = kmalloc(PKT_BUF_SZ, GFP_KERNEL);
+	}
+	np->tx_ring[i-1].next_desc = virt_to_bus(&np->tx_ring[0]);
+
+	return;
+}
+
+static int start_tx(struct sk_buff *skb, struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	unsigned entry;
+
+	/* 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) {
+		if (jiffies - dev->trans_start < TX_TIMEOUT)
+			return 1;
+		tx_timeout(dev);
+		return 1;
+	}
+
+	/* Caution: the write order is important here, set the field
+	   with the "ownership" bits last. */
+
+	/* Calculate the next Tx descriptor entry. */
+	entry = np->cur_tx % TX_RING_SIZE;
+
+	np->tx_skbuff[entry] = skb;
+
+	if ((long)skb->data & 3) {			/* Must use alignment buffer. */
+		if (np->tx_buf[entry] == NULL &&
+			(np->tx_buf[entry] = kmalloc(PKT_BUF_SZ, GFP_KERNEL)) == NULL)
+			return 1;
+		memcpy(np->tx_buf[entry], skb->data, skb->len);
+		np->tx_ring[entry].addr = virt_to_bus(np->tx_buf[entry]);
+	} else
+		np->tx_ring[entry].addr = virt_to_bus(skb->data);
+
+	np->tx_ring[entry].desc_length = 0x00E08000 |
+		(skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN);
+	np->tx_ring[entry].tx_own = DescOwn;
+
+	np->cur_tx++;
+
+	/* Non-x86 Todo: explicitly flush cache lines here. */
+
+	/* Wake the potentially-idle transmit channel. */
+	writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
+
+	if (np->cur_tx - np->dirty_tx < TX_RING_SIZE - 1)
+		clear_bit(0, (void*)&dev->tbusy);		/* Typical path */
+	else
+		np->tx_full = 1;
+	dev->trans_start = jiffies;
+
+	if (debug > 4) {
+		printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
+			   dev->name, np->cur_tx, entry);
+	}
+	return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
+{
+	struct device *dev = (struct device *)dev_instance;
+	struct netdev_private *np;
+	long ioaddr, boguscnt = max_interrupt_work;
+
+	ioaddr = dev->base_addr;
+	np = (struct netdev_private *)dev->priv;
+#if defined(__i386__)
+	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */
+	if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
+			   dev->name);
+		dev->interrupt = 0;	/* Avoid halting machine. */
+		return;
+	}
+#else
+	if (dev->interrupt) {
+		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
+		return;
+	}
+	dev->interrupt = 1;
+#endif
+
+	do {
+		u32 intr_status = readw(ioaddr + IntrStatus);
+
+		/* Acknowledge all of the current interrupt sources ASAP. */
+		writew(intr_status & 0xffff, ioaddr + IntrStatus);
+
+		if (debug > 4)
+			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
+				   dev->name, intr_status);
+
+		if (intr_status == 0)
+			break;
+
+		if (intr_status & (IntrRxDone | IntrRxErr | IntrRxDropped |
+						   IntrRxWakeUp | IntrRxEmpty | IntrRxNoBuf))
+			netdev_rx(dev);
+
+		for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
+			int entry = np->dirty_tx % TX_RING_SIZE;
+			int txstatus;
+			if (np->tx_ring[entry].tx_own)
+				break;
+			txstatus = np->tx_ring[entry].tx_status;
+			if (debug > 6)
+				printk(KERN_DEBUG " Tx scavenge %d status %4.4x.\n",
+					   entry, txstatus);
+			if (txstatus & 0x8000) {
+				if (debug > 1)
+					printk(KERN_DEBUG "%s: Transmit error, Tx status %4.4x.\n",
+						   dev->name, txstatus);
+				np->stats.tx_errors++;
+				if (txstatus & 0x0400) np->stats.tx_carrier_errors++;
+				if (txstatus & 0x0200) np->stats.tx_window_errors++;
+				if (txstatus & 0x0100) np->stats.tx_aborted_errors++;
+				if (txstatus & 0x0080) np->stats.tx_heartbeat_errors++;
+				if (txstatus & 0x0002) np->stats.tx_fifo_errors++;
+#ifdef ETHER_STATS
+				if (txstatus & 0x0100) np->stats.collisions16++;
+#endif
+				/* Transmitter restarted in 'abnormal' handler. */
+			} else {
+#ifdef ETHER_STATS
+				if (txstatus & 0x0001) np->stats.tx_deferred++;
+#endif
+				np->stats.collisions += (txstatus >> 3) & 15;
+#if defined(NETSTATS_VER2)
+				np->stats.tx_bytes += np->tx_ring[entry].desc_length & 0x7ff;
+#endif
+				np->stats.tx_packets++;
+			}
+			/* Free the original skb. */
+			dev_free_skb(np->tx_skbuff[entry]);
+			np->tx_skbuff[entry] = 0;
+		}
+		if (np->tx_full && dev->tbusy
+			&& np->cur_tx - np->dirty_tx < TX_RING_SIZE - 4) {
+			/* The ring is no longer full, clear tbusy. */
+			np->tx_full = 0;
+			clear_bit(0, (void*)&dev->tbusy);
+			mark_bh(NET_BH);
+		}
+
+		/* Abnormal error summary/uncommon events handlers. */
+		if (intr_status & (IntrPCIErr | IntrLinkChange | IntrMIIChange |
+						   IntrStatsMax | IntrTxAbort | IntrTxUnderrun))
+			netdev_error(dev, intr_status);
+
+		if (--boguscnt < 0) {
+			printk(KERN_WARNING "%s: Too much work at interrupt, "
+				   "status=0x%4.4x.\n",
+				   dev->name, intr_status);
+			break;
+		}
+	} while (1);
+
+	if (debug > 3)
+		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+			   dev->name, readw(ioaddr + IntrStatus));
+
+#if defined(__i386__)
+	clear_bit(0, (void*)&dev->interrupt);
+#else
+	dev->interrupt = 0;
+#endif
+	return;
+}
+
+/* This routine is logically part of the interrupt handler, but isolated
+   for clarity and better register allocation. */
+static int netdev_rx(struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	int entry = np->cur_rx % RX_RING_SIZE;
+	int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
+
+	if (debug > 4) {
+		printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n",
+			   entry, np->rx_head_desc->rx_length);
+	}
+
+	/* If EOP is set on the next entry, it's a new packet. Send it up. */
+	while ( ! (np->rx_head_desc->rx_length & DescOwn)) {
+		struct rx_desc *desc = np->rx_head_desc;
+		int data_size = desc->rx_length;
+		u16 desc_status = desc->rx_status;
+
+		if (debug > 4)
+			printk(KERN_DEBUG "  netdev_rx() status is %4.4x.\n",
+				   desc_status);
+		if (--boguscnt < 0)
+			break;
+		if ( (desc_status & (RxWholePkt | RxErr)) !=  RxWholePkt) {
+			if ((desc_status & RxWholePkt) !=  RxWholePkt) {
+				printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
+					   "multiple buffers, entry %#x length %d status %4.4x!\n",
+					   dev->name, np->cur_rx, data_size, desc_status);
+				printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n",
+					   dev->name, np->rx_head_desc,
+					   &np->rx_ring[np->cur_rx % RX_RING_SIZE]);
+				np->stats.rx_length_errors++;
+			} else if (desc_status & RxErr) {
+				/* There was a error. */
+				if (debug > 2)
+					printk(KERN_DEBUG "  netdev_rx() Rx error was %8.8x.\n",
+						   desc_status);
+				np->stats.rx_errors++;
+				if (desc_status & 0x0030) np->stats.rx_length_errors++;
+				if (desc_status & 0x0048) np->stats.rx_fifo_errors++;
+				if (desc_status & 0x0004) np->stats.rx_frame_errors++;
+				if (desc_status & 0x0002) np->stats.rx_crc_errors++;
+			}
+		} else {
+			struct sk_buff *skb;
+			/* Length should omit the CRC */
+			u16 pkt_len = data_size - 4;
+
+			/* Check if the packet is long enough to accept without copying
+			   to a minimally-sized skbuff. */
+			if (pkt_len < rx_copybreak
+				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+				skb->dev = dev;
+				skb_reserve(skb, 2);	/* 16 byte align the IP header */
+#if ! defined(__alpha__) || USE_IP_COPYSUM		/* Avoid misaligned on Alpha */
+				eth_copy_and_sum(skb, bus_to_virt(desc->addr),
+								 pkt_len, 0);
+				skb_put(skb, pkt_len);
+#else
+				memcpy(skb_put(skb,pkt_len), bus_to_virt(desc->addr), pkt_len);
+#endif
+			} else {
+				skb_put(skb = np->rx_skbuff[entry], pkt_len);
+				np->rx_skbuff[entry] = NULL;
+			}
+			skb->protocol = eth_type_trans(skb, dev);
+			netif_rx(skb);
+			dev->last_rx = jiffies;
+			np->stats.rx_packets++;
+		}
+		entry = (++np->cur_rx) % RX_RING_SIZE;
+		np->rx_head_desc = &np->rx_ring[entry];
+	}
+
+	/* Refill the Rx ring buffers. */
+	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
+		struct sk_buff *skb;
+		entry = np->dirty_rx % RX_RING_SIZE;
+		if (np->rx_skbuff[entry] == NULL) {
+			skb = dev_alloc_skb(np->rx_buf_sz);
+			np->rx_skbuff[entry] = skb;
+			if (skb == NULL)
+				break;			/* Better luck next round. */
+			skb->dev = dev;			/* Mark as being used by this device. */
+			np->rx_ring[entry].addr = virt_to_bus(skb->tail);
+		}
+		np->rx_ring[entry].rx_status = 0;
+		np->rx_ring[entry].rx_length = DescOwn;
+	}
+
+	/* Pre-emptively restart Rx engine. */
+	writew(CmdRxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
+	return 0;
+}
+
+static void netdev_error(struct device *dev, int intr_status)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+
+	if (intr_status & (IntrMIIChange | IntrLinkChange)) {
+		if (readb(ioaddr + MIIStatus) & 0x02)
+			/* Link failed, restart autonegotiation. */
+			mdio_write(dev, np->phys[0], 0, 0x3300);
+		else
+			check_duplex(dev);
+		if (debug)
+			printk(KERN_ERR "%s: MII status changed: Autonegotiation "
+				   "advertising %4.4x  partner %4.4x.\n", dev->name,
+			   mdio_read(dev, np->phys[0], 4),
+			   mdio_read(dev, np->phys[0], 5));
+	}
+	if (intr_status & IntrStatsMax) {
+		np->stats.rx_crc_errors	+= readw(ioaddr + RxCRCErrs);
+		np->stats.rx_missed_errors	+= readw(ioaddr + RxMissed);
+		writel(0, RxMissed);
+	}
+	if (intr_status & IntrTxAbort) {
+		/* Stats counted in Tx-done handler, just restart Tx. */
+		writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
+	}
+	if (intr_status & IntrTxUnderrun) {
+		if (np->tx_thresh < 0xE0)
+			writeb(np->tx_thresh += 0x20, ioaddr + TxConfig);
+		if (debug > 1)
+			printk(KERN_INFO "%s: Transmitter underrun, increasing Tx "
+				   "threshold setting to %2.2x.\n", dev->name, np->tx_thresh);
+	}
+	if ((intr_status & ~(IntrLinkChange|IntrStatsMax|IntrTxAbort)) && debug) {
+		printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
+			   dev->name, intr_status);
+		/* Recovery for other fault sources not known. */
+		writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
+	}
+}
+
+static struct enet_statistics *get_stats(struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+
+	/* Nominally we should lock this segment of code for SMP, although
+	   the vulnerability window is very small and statistics are
+	   non-critical. */
+	np->stats.rx_crc_errors	+= readw(ioaddr + RxCRCErrs);
+	np->stats.rx_missed_errors	+= readw(ioaddr + RxMissed);
+	writel(0, RxMissed);
+
+	return &np->stats;
+}
+
+/* The big-endian AUTODIN II ethernet CRC calculation.
+   N.B. Do not use for bulk data, use a table-based routine instead.
+   This is common code and should be moved to net/core/crc.c */
+static unsigned const ethernet_polynomial = 0x04c11db7U;
+static inline u32 ether_crc(int length, unsigned char *data)
+{
+    int crc = -1;
+
+    while(--length >= 0) {
+		unsigned char current_octet = *data++;
+		int bit;
+		for (bit = 0; bit < 8; bit++, current_octet >>= 1) {
+			crc = (crc << 1) ^
+				((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
+		}
+    }
+    return crc;
+}
+
+static void set_rx_mode(struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	u32 mc_filter[2];			/* Multicast hash filter */
+	u8 rx_mode;					/* Note: 0x02=accept runt, 0x01=accept errs */
+
+	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
+		/* Unconditionally log net taps. */
+		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
+		rx_mode = 0x1C;
+	} else if ((dev->mc_count > multicast_filter_limit)
+			   ||  (dev->flags & IFF_ALLMULTI)) {
+		/* Too many to match, or accept all multicasts. */
+		rx_mode = 0x0C;
+	} else {
+		struct dev_mc_list *mclist;
+		int i;
+		memset(mc_filter, 0, sizeof(mc_filter));
+		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+			 i++, mclist = mclist->next) {
+			set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26,
+					mc_filter);
+		}
+		writel(mc_filter[0], ioaddr + MulticastFilter0);
+		writel(mc_filter[1], ioaddr + MulticastFilter1);
+		rx_mode = 0x0C;
+	}
+	writeb(np->rx_thresh | rx_mode, ioaddr + RxConfig);
+}
+
+static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+	u16 *data = (u16 *)&rq->ifr_data;
+
+	switch(cmd) {
+	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
+		data[0] = ((struct netdev_private *)dev->priv)->phys[0] & 0x1f;
+		/* Fall Through */
+	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
+		data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
+		return 0;
+	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
+		if (!suser())
+			return -EPERM;
+		mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int netdev_close(struct device *dev)
+{
+	long ioaddr = dev->base_addr;
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	int i;
+
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	if (debug > 1)
+		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
+			   dev->name, readw(ioaddr + ChipCmd));
+
+	/* Disable interrupts by clearing the interrupt mask. */
+	writew(0x0000, ioaddr + IntrEnable);
+
+	/* Stop the chip's Tx and Rx processes. */
+	writew(CmdStop, ioaddr + ChipCmd);
+
+	del_timer(&np->timer);
+
+	free_irq(dev->irq, dev);
+
+	/* Free all the skbuffs in the Rx queue. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		np->rx_ring[i].rx_length = 0;
+		np->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */
+		if (np->rx_skbuff[i]) {
+#if LINUX_VERSION_CODE < 0x20100
+			np->rx_skbuff[i]->free = 1;
+#endif
+			dev_free_skb(np->rx_skbuff[i]);
+		}
+		np->rx_skbuff[i] = 0;
+	}
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		if (np->tx_skbuff[i])
+			dev_free_skb(np->tx_skbuff[i]);
+		np->tx_skbuff[i] = 0;
+	}
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+	if (debug)					/* Emit version even if no cards detected. */
+		printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
+#ifdef CARDBUS
+	register_driver(&etherdev_ops);
+	return 0;
+#else
+	return pci_etherdev_probe(NULL, pci_tbl);
+#endif
+}
+
+void cleanup_module(void)
+{
+
+#ifdef CARDBUS
+	unregister_driver(&etherdev_ops);
+#endif
+
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	while (root_net_dev) {
+		struct netdev_private *np =
+			(struct netdev_private *)(root_net_dev->priv);
+		unregister_netdev(root_net_dev);
+#ifdef VIA_USE_IO
+		release_region(root_net_dev->base_addr, pci_tbl[np->chip_id].io_size);
+#else
+		iounmap((char *)(root_net_dev->base_addr));
+#endif
+		kfree(root_net_dev);
+		root_net_dev = np->next_module;
+#if 0
+		kfree(np);				/* Assumption: no struct realignment. */
+#endif
+	}
+}
+
+#endif  /* MODULE */
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c via-rhine.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c via-rhine.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -u --recursive --new-file v2.1.124/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- v2.1.124/linux/drivers/net/wavelan.c	Tue Jul 28 14:21:08 1998
+++ linux/drivers/net/wavelan.c	Wed Oct  7 15:51:45 1998
@@ -2950,8 +2950,8 @@
   /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
    * Does it work for everybody, especially old cards? */
   /* Note: WFREQSEL verifies that it is able to read a sensible
-   * frequency from from EEPROM (address 0x00) and that
-   * MMR_FEE_STATUS_ID is 0xA (Xilinx version) or 0xB (Ariadne version).
+   * frequency from EEPROM (address 0x00) and that MMR_FEE_STATUS_ID
+   * is 0xA (Xilinx version) or 0xB (Ariadne version).
    * My test is more crude but does work. */
   if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
        (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
diff -u --recursive --new-file v2.1.124/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c
--- v2.1.124/linux/drivers/pci/oldproc.c	Thu Sep 17 17:53:36 1998
+++ linux/drivers/pci/oldproc.c	Thu Oct  8 08:07:33 1998
@@ -528,6 +528,10 @@
 	DEVICE( ADAPTEC,	ADAPTEC_7883,	"AIC-7883U"),
 	DEVICE( ADAPTEC,	ADAPTEC_7884,	"AIC-7884U"),
 	DEVICE( ADAPTEC,	ADAPTEC_1030,	"ABA-1030 DVB receiver"),
+  DEVICE( ADAPTEC2, ADAPTEC2_2940U2, "AHA-2940U2"),
+  DEVICE( ADAPTEC2, ADAPTEC2_7890, "AIC-7890/1"),
+  DEVICE( ADAPTEC2, ADAPTEC2_3940U2, "AHA-3940U2"),
+  DEVICE( ADAPTEC2, ADAPTEC2_7896, "AIC-7896/7"),
   	DEVICE( ATRONICS,	ATRONICS_2015,	"IDE-2015PL"),
 	DEVICE( TIGERJET,	TIGERJET_300,	"Tiger300 ISDN"),
 	DEVICE( ARK,		ARK_STING,	"Stingray"),
@@ -778,6 +782,7 @@
 	      case PCI_VENDOR_ID_INTEL:		return "Intel";
 	      case PCI_VENDOR_ID_KTI:		return "KTI";
 	      case PCI_VENDOR_ID_ADAPTEC:	return "Adaptec";
+	      case PCI_VENDOR_ID_ADAPTEC2:	return "Adaptec";
 	      case PCI_VENDOR_ID_ATRONICS:	return "Atronics";
 	      case PCI_VENDOR_ID_TIGERJET:	return "TigerJet";
 	      case PCI_VENDOR_ID_ARK:		return "ARK Logic";
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/53c7xx.c linux/drivers/scsi/53c7xx.c
--- v2.1.124/linux/drivers/scsi/53c7xx.c	Mon Aug  3 12:45:46 1998
+++ linux/drivers/scsi/53c7xx.c	Mon Oct  5 13:43:31 1998
@@ -325,6 +325,7 @@
 static int disable (struct Scsi_Host *host);
 static int NCR53c7xx_run_tests (struct Scsi_Host *host);
 static void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
+static void NCR53c7x0_intfly (struct Scsi_Host *host);
 static int ncr_halt (struct Scsi_Host *host);
 static void intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd 
     *cmd);
@@ -847,7 +848,6 @@
     unsigned char revision;
     struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
 	host->hostdata[0];
-    struct Scsi_Host *search;
     /* 
      * There are some things which we need to know about in order to provide
      * a semblance of support.  Print 'em if they aren't what we expect, 
@@ -1072,26 +1072,13 @@
 
     NCR53c7x0_driver_init (host);
 
-    /*
-     * Set up an interrupt handler if we aren't already sharing an IRQ
-     * with another board.
-     */
-
-    for (search = first_host; search && !(search->hostt == the_template &&
-	search->irq == host->irq && search != host); search=search->next);
-
-    if (!search) {
-	if (request_irq(host->irq, NCR53c7x0_intr, 0, "53c7xx", NCR53c7x0_intr))
-	{
-	    printk("scsi%d : IRQ%d not free, detaching\n",
+    if (request_irq(host->irq, NCR53c7x0_intr, 0, "53c7xx", host))
+    {
+	printk("scsi%d : IRQ%d not free, detaching\n",
 		host->host_no, host->irq);
-	    scsi_unregister (host);
-	    return -1;
-	} 
-    } else {
-	printk("scsi%d : using interrupt handler previously installed for scsi%d\n",
-	    host->host_no, search->host_no);
-    }
+	scsi_unregister (host);
+	return -1;
+    } 
 
     if ((hostdata->run_tests && hostdata->run_tests(host) == -1) ||
         (hostdata->options & OPTION_DEBUG_TESTS_ONLY)) {
@@ -2509,18 +2496,7 @@
 	    abnormal_finished (cmd, DID_ERROR << 16);
 	return SPECIFIC_INT_NOTHING;
     case A_int_norm_emulateintfly:
-	/* I'm not sure this is the right ! thing to do, but it works
-	 * with the A4000T when copyback is disabled, and also the
-	 * WarpEngine with copyback enabled, so it looks as though
-	 * it does work to some extent.
-	 *
-	 * RGH:  I don't really like it - You get an interrupt which
-	 * calls NCR53c7x0_intr(), which calls this function (via
-	 * intr_dma()), which calls NCR53c7x0_intr().....
-	 * Anyway let's see how it goes for now.
-	 */
-	hostdata->emulated_intfly = 1;
-	NCR53c7x0_intr(host->irq, NULL, NULL);
+	NCR53c7x0_intfly(host);
 	return SPECIFIC_INT_NOTHING;
     case A_int_test_1:
     case A_int_test_2:
@@ -4141,288 +4117,230 @@
 }
 #endif
 
+
 /*
- * Function : static void NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs)
+ * Function : static void NCR53c7x0_intfly (struct Scsi_Host *host)
  *
- * Purpose : handle NCR53c7x0 interrupts for all NCR devices sharing
- *	the same IRQ line.  
+ * Purpose : Scan command queue for specified host, looking for completed
+ *           commands.
  * 
- * Inputs : Since we're using the SA_INTERRUPT interrupt handler
- *	semantics, irq indicates the interrupt which invoked 
- *	this handler.  
+ * Inputs : Scsi_Host pointer.
  *
- * On the 710 we simualte an INTFLY with a script interrupt, and the
- * script interrupt handler will call back to this function.
+ * 	This is called from the interrupt handler, when a simulated INTFLY
+ * 	interrupt occurs.
  */
 
-static void 
-NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs) {
+static void
+NCR53c7x0_intfly (struct Scsi_Host *host)
+{
     NCR53c7x0_local_declare();
-    struct Scsi_Host *host;			/* Host we are looking at */
-    unsigned char istat; 			/* Values of interrupt regs */
     struct NCR53c7x0_hostdata *hostdata;	/* host->hostdata[0] */
     struct NCR53c7x0_cmd *cmd,			/* command which halted */
 	**cmd_prev_ptr;
-    u32 *dsa;					/* DSA */
-    int done = 1;				/* Indicates when handler 
-						   should terminate */
-    int interrupted = 0;			/* This HA generated 
-						   an interrupt */
-    int have_intfly;				/* Don't print warning 
-						   messages when we stack
-						   INTFLYs */
     unsigned long flags;				
+    char search_found = 0;			/* Got at least one ? */
 
-#ifdef NCR_DEBUG
-    char buf[80];				/* Debugging sprintf buffer */
-    size_t buflen;				/* Length of same */
-#endif
-
-#if defined(CONFIG_AMIGA)
-    custom.intena = IF_PORTS;
-#endif
-
-    do {
-	done = 1;
-	for (host = first_host; host; host = host->next) 
-	    if (host->hostt == the_template
-#if defined(MVME16x_INTFLY)
-			/* We have two different interrupts pointing
-			 * at this routine, so remove this check */
-#else
-				&& host->irq == irq
-#endif
-							) {
-    	    NCR53c7x0_local_setup(host);
+    hostdata = (struct NCR53c7x0_hostdata *) host->hostdata[0];
+    NCR53c7x0_local_setup(host);
 
-	    hostdata = (struct NCR53c7x0_hostdata *) host->hostdata[0];
-	    hostdata->dsp_changed = 0;
-	    interrupted = 0;
-	    have_intfly = 0;
-
-	    do {
-		hostdata->dstat_valid = 0;
-		interrupted = 0;
-		/*
-		 * Only read istat once, since reading it again will unstack
-		 * interrupts?
-		 */
-		istat = NCR53c7x0_read8(hostdata->istat);
-
-		if ((hostdata->options & OPTION_INTFLY) && 
-#ifdef MVME16x_INTFLY
-                    /* the bit is set which indicates an on-the-fly int */
-                        (*(volatile unsigned long *)0xfff40068 & 0x8000))
-#else
-		    (hostdata->emulated_intfly != 0))
-#endif
-		{
-		    char search_found = 0;	/* Got at least one ? */
-		    done = 0;
-		    interrupted = 1;
+    if (hostdata->options & OPTION_DEBUG_INTR)
+    printk ("scsi%d : INTFLY\n", host->host_no); 
 
-#ifdef MVME16x_INTFLY
-                    /* clear the INTFLY bit */
-                    *(volatile unsigned long *)0xfff40074 = 0x8000;
-#endif
+    /*
+    * Traverse our list of running commands, and look
+    * for those with valid (non-0xff ff) status and message
+    * bytes encoded in the result which signify command
+    * completion.
+    */
 
-		    if (hostdata->options & OPTION_DEBUG_INTR)
-			printk ("scsi%d : INTFLY\n", host->host_no); 
+    save_flags(flags);
+    cli();
+restart:
+    for (cmd_prev_ptr = (struct NCR53c7x0_cmd **)&(hostdata->running_list),
+	cmd = (struct NCR53c7x0_cmd *) hostdata->running_list; cmd ;
+	cmd_prev_ptr = (struct NCR53c7x0_cmd **) &(cmd->next), 
+    	cmd = (struct NCR53c7x0_cmd *) cmd->next)
+    {
+	Scsi_Cmnd *tmp;
 
-		    /*
-		     * Traverse our list of running commands, and look
-		     * for those with valid (non-0xff ff) status and message
-		     * bytes encoded in the result which signify command
-		     * completion.
-		     */
+	if (!cmd) {
+	    printk("scsi%d : very weird.\n", host->host_no);
+	    break;
+	}
 
+	if (!(tmp = cmd->cmd)) {
+	    printk("scsi%d : weird.  NCR53c7x0_cmd has no Scsi_Cmnd\n",
+		    host->host_no);
+	    continue;
+	}
+	/* Copy the result over now; may not be complete,
+	 * but subsequent tests may as well be done on
+	 * cached memory.
+	 */
+	tmp->result = cmd->result;
 
-		    save_flags(flags);
-		    cli();
-restart:
-		    for (cmd_prev_ptr = (struct NCR53c7x0_cmd **) 
-			 &(hostdata->running_list), cmd = 
-			 (struct NCR53c7x0_cmd *) hostdata->running_list; cmd ;
-			 cmd_prev_ptr = (struct NCR53c7x0_cmd **) &(cmd->next), 
-    	    	    	 cmd = (struct NCR53c7x0_cmd *) cmd->next) {
-			Scsi_Cmnd *tmp;
-
-			if (!cmd) {
-			    printk("scsi%d : very weird.\n", host->host_no);
-			    break;
-			}
-
-			if (!(tmp = cmd->cmd)) {
-			    printk("scsi%d : weird.  NCR53c7x0_cmd has no Scsi_Cmnd\n",
-				host->host_no);
-				continue;
-			}
-                        /* Copy the result over now; may not be complete,
-			 * but subsequent tests may as well be done on
-                         * cached memory.
-                         */
-                        tmp->result = cmd->result;
-#if 0
-			printk ("scsi%d : looking at result of 0x%x\n",
-			    host->host_no, cmd->cmd->result);
-#endif
-		
-			if (((tmp->result & 0xff) == 0xff) ||
+	if (((tmp->result & 0xff) == 0xff) ||
 			    ((tmp->result & 0xff00) == 0xff00))
-			    continue;
+	    continue;
 
-			search_found = 1;
+	search_found = 1;
 
-			if (cmd->bounce.len)
-			    memcpy ((void *)cmd->bounce.addr,
+	if (cmd->bounce.len)
+	    memcpy ((void *)cmd->bounce.addr,
 				(void *)cmd->bounce.buf, cmd->bounce.len);
 
-			/* Important - remove from list _before_ done is called */
-			if (cmd_prev_ptr)
-			    *cmd_prev_ptr = (struct NCR53c7x0_cmd *) cmd->next;
-
-			--hostdata->busy[tmp->target][tmp->lun];
-    	    	    	cmd->next = hostdata->free;
-    	    	    	hostdata->free = cmd;
-
-    	    	    	tmp->host_scribble = NULL;
-
-			if (hostdata->options & OPTION_DEBUG_INTR) {
-			    printk ("scsi%d : command complete : pid %lu, id %d,lun %d result 0x%x ", 
-				host->host_no, tmp->pid, tmp->target, tmp->lun, tmp->result);
-			    print_command (tmp->cmnd);
-			}
+	/* Important - remove from list _before_ done is called */
+	if (cmd_prev_ptr)
+	    *cmd_prev_ptr = (struct NCR53c7x0_cmd *) cmd->next;
 
-#if 0
-			hostdata->options &= ~OPTION_DEBUG_INTR;
-#endif
-			tmp->scsi_done(tmp);
-			goto restart;
+	--hostdata->busy[tmp->target][tmp->lun];
+	cmd->next = hostdata->free;
+	hostdata->free = cmd;
 
-		    }
-		    restore_flags(flags);
-		   
-		    if (!search_found && !have_intfly)  {
-			printk ("scsi%d : WARNING : INTFLY with no completed commands.\n",
-			    host->host_no);
-		    } else if (!have_intfly)  {
-			have_intfly = 1; 
-    		    	run_process_issue_queue();
-		    }
-		}
+	tmp->host_scribble = NULL;
 
-		if (hostdata->emulated_intfly)
-		{
-			hostdata->emulated_intfly = 0;
-			return;
-		}
+	if (hostdata->options & OPTION_DEBUG_INTR) {
+	    printk ("scsi%d : command complete : pid %lu, id %d,lun %d result 0x%x ", 
+		  host->host_no, tmp->pid, tmp->target, tmp->lun, tmp->result);
+	    print_command (tmp->cmnd);
+	}
 
-		if (istat & (ISTAT_SIP|ISTAT_DIP)) {
-		    done = 0;
-		    interrupted = 1;
-    	    	    hostdata->state = STATE_HALTED;
+	tmp->scsi_done(tmp);
+	goto restart;
+    }
+    restore_flags(flags);
 
-		    if (NCR53c7x0_read8 (SSTAT2_REG) & SSTAT2_FF_MASK) 
-			printk ("scsi%d : SCSI FIFO not empty\n", 
+    if (!search_found)  {
+	printk ("scsi%d : WARNING : INTFLY with no completed commands.\n",
 			    host->host_no);
+    } else {
+	run_process_issue_queue();
+    }
+    return;
+}
 
-		    /*
-		     * NCR53c700 and NCR53c700-66 change the current SCSI
-		     * process, hostdata->ncrcurrent, in the Linux driver so
-		     * cmd = hostdata->ncrcurrent.
-		     *
-		     * With other chips, we must look through the commands
-		     * executing and find the command structure which 
-		     * corresponds to the DSA register.
-		     */
-
-		    if (hostdata->options & OPTION_700) {
-			cmd = (struct NCR53c7x0_cmd *) hostdata->ncrcurrent;
-		    } else {
-			dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
-			for (cmd = (struct NCR53c7x0_cmd *) 
-			    hostdata->running_list; cmd &&
-    	    	    	    (dsa + (hostdata->dsa_start / sizeof(u32))) != 
-    	    	    	    	cmd->dsa;
-			    cmd = (struct NCR53c7x0_cmd *)(cmd->next));
-		    }
-		    if (hostdata->options & OPTION_DEBUG_INTR) {
-			if (cmd) {
-			    printk("scsi%d : interrupt for pid %lu, id %d, lun %d ", 
-				host->host_no, cmd->cmd->pid, (int) cmd->cmd->target,
-				(int) cmd->cmd->lun);
-			    print_command (cmd->cmd->cmnd);
-			} else {
-			    printk("scsi%d : no active command\n", host->host_no);
-			}
-		    }
+/*
+ * Function : static void NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs)
+ *
+ * Purpose : handle NCR53c7x0 interrupts for all NCR devices sharing
+ *	the same IRQ line.  
+ * 
+ * Inputs : Since we're using the SA_INTERRUPT interrupt handler
+ *	semantics, irq indicates the interrupt which invoked 
+ *	this handler.  
+ *
+ * On the 710 we simualte an INTFLY with a script interrupt, and the
+ * script interrupt handler will call back to this function.
+ */
 
-		    if (istat & ISTAT_SIP) {
-			if (hostdata->options & OPTION_DEBUG_INTR) 
-			    printk ("scsi%d : ISTAT_SIP\n", host->host_no);
-			intr_scsi (host, cmd);
-		    }
-		
-		    if (istat & ISTAT_DIP) {
-			if (hostdata->options & OPTION_DEBUG_INTR) 
-			    printk ("scsi%d : ISTAT_DIP\n", host->host_no);
-			intr_dma (host, cmd);
-		    }
+static void 
+NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs) {
+    NCR53c7x0_local_declare();
+    struct Scsi_Host *host;			/* Host we are looking at */
+    unsigned char istat; 			/* Values of interrupt regs */
+    struct NCR53c7x0_hostdata *hostdata;	/* host->hostdata[0] */
+    struct NCR53c7x0_cmd *cmd;			/* command which halted */
+    u32 *dsa;					/* DSA */
 
-		    if (!hostdata->dstat_valid) {
-			hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
-			hostdata->dstat_valid = 1;
-		    }
+#ifdef NCR_DEBUG
+    char buf[80];				/* Debugging sprintf buffer */
+    size_t buflen;				/* Length of same */
+#endif
 
-		    if (!(hostdata->dstat & DSTAT_DFE)) {
-		      printk ("scsi%d : DMA FIFO not empty\n", host->host_no);
-		      /* Really need to check this out for 710 RGH */
-                      NCR53c7x0_write8 (CTEST8_REG, CTEST8_10_CLF);
-                      while (NCR53c7x0_read8 (CTEST8_REG) & CTEST8_10_CLF);
-		      hostdata->dstat |= DSTAT_DFE;
-		    }
-		}
-	    } while (interrupted);
+    host     = (struct Scsi_Host *)dev_id;
+    hostdata = (struct NCR53c7x0_hostdata *) host->hostdata[0];
+    NCR53c7x0_local_setup(host);
 
+    /*
+     * Only read istat once per loop, since reading it again will unstack
+     * interrupts
+     */
 
+    while ((istat = NCR53c7x0_read8(hostdata->istat)) & (ISTAT_SIP|ISTAT_DIP)) {
+	hostdata->dsp_changed = 0;
+	hostdata->dstat_valid = 0;
+    	hostdata->state = STATE_HALTED;
 
-	    if (hostdata->intrs != -1)
-		hostdata->intrs++;
-#if 0
-	    if (hostdata->intrs > 40) {
-		printk("scsi%d : too many interrupts, halting", host->host_no);
-		disable(host);
+	if (NCR53c7x0_read8 (SSTAT2_REG) & SSTAT2_FF_MASK) 
+	    printk ("scsi%d : SCSI FIFO not empty\n", host->host_no);
+
+	/*
+	 * NCR53c700 and NCR53c700-66 change the current SCSI
+	 * process, hostdata->ncrcurrent, in the Linux driver so
+	 * cmd = hostdata->ncrcurrent.
+	 *
+	 * With other chips, we must look through the commands
+	 * executing and find the command structure which 
+	 * corresponds to the DSA register.
+	 */
+
+	if (hostdata->options & OPTION_700) {
+	    cmd = (struct NCR53c7x0_cmd *) hostdata->ncrcurrent;
+	} else {
+	    dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
+	    for (cmd = (struct NCR53c7x0_cmd *) hostdata->running_list;
+		cmd && (dsa + (hostdata->dsa_start / sizeof(u32))) != cmd->dsa;
+		    cmd = (struct NCR53c7x0_cmd *)(cmd->next))
+		;
+	}
+	if (hostdata->options & OPTION_DEBUG_INTR) {
+	    if (cmd) {
+		printk("scsi%d : interrupt for pid %lu, id %d, lun %d ", 
+		    host->host_no, cmd->cmd->pid, (int) cmd->cmd->target,
+		    (int) cmd->cmd->lun);
+		print_command (cmd->cmd->cmnd);
+	    } else {
+		printk("scsi%d : no active command\n", host->host_no);
 	    }
-#endif
+	}
+	
+	if (istat & ISTAT_SIP) {
+	    if (hostdata->options & OPTION_DEBUG_INTR) 
+		printk ("scsi%d : ISTAT_SIP\n", host->host_no);
+	    intr_scsi (host, cmd);
+	}
+	
+	if (istat & ISTAT_DIP) {
+	    if (hostdata->options & OPTION_DEBUG_INTR) 
+		printk ("scsi%d : ISTAT_DIP\n", host->host_no);
+	    intr_dma (host, cmd);
+	}
+	
+	if (!hostdata->dstat_valid) {
+	    hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+	    hostdata->dstat_valid = 1;
+	}
+	
+	if (!(hostdata->dstat & DSTAT_DFE)) {
+	    printk ("scsi%d : DMA FIFO not empty\n", host->host_no);
+	    /* Really need to check this out for 710 RGH */
+	    NCR53c7x0_write8 (CTEST8_REG, CTEST8_10_CLF);
+	    while (NCR53c7x0_read8 (CTEST8_REG) & CTEST8_10_CLF)
+		;
+	    hostdata->dstat |= DSTAT_DFE;
+	}
 
-	    if (!hostdata->idle && hostdata->state == STATE_HALTED) {
-		if (!hostdata->dsp_changed) {
-		    hostdata->dsp = (u32 *) 
-			bus_to_virt(NCR53c7x0_read32(DSP_REG));
-		}
-			
+	if (!hostdata->idle && hostdata->state == STATE_HALTED) {
+	    if (!hostdata->dsp_changed)
+		hostdata->dsp = (u32 *)bus_to_virt(NCR53c7x0_read32(DSP_REG));
 #if 0
-		printk("scsi%d : new dsp is 0x%lx (virt 0x%p)\n",
-		    host->host_no,  virt_to_bus(hostdata->dsp), hostdata->dsp);
+	    printk("scsi%d : new dsp is 0x%lx (virt 0x%p)\n",
+		host->host_no,  virt_to_bus(hostdata->dsp), hostdata->dsp);
 #endif
 		
-		hostdata->state = STATE_RUNNING;
-		NCR53c7x0_write32 (DSP_REG, virt_to_bus(hostdata->dsp));
-		if (hostdata->options & OPTION_DEBUG_TRACE) {
+	    hostdata->state = STATE_RUNNING;
+	    NCR53c7x0_write32 (DSP_REG, virt_to_bus(hostdata->dsp));
+	    if (hostdata->options & OPTION_DEBUG_TRACE) {
 #ifdef CYCLIC_TRACE
-		    log_insn (hostdata->dsp);
+		log_insn (hostdata->dsp);
 #else
-	    	    print_insn (host, hostdata->dsp, "t ", 1);
+	    	print_insn (host, hostdata->dsp, "t ", 1);
 #endif
-		    NCR53c7x0_write8 (DCNTL_REG, hostdata->saved_dcntl |
-				DCNTL_SSM | DCNTL_STD);
-		}
+		NCR53c7x0_write8 (DCNTL_REG,
+			hostdata->saved_dcntl | DCNTL_SSM | DCNTL_STD);
 	    }
 	}
-    } while (!done);
-#ifdef CONFIG_AMIGA
-	custom.intena = IF_SETCLR | IF_PORTS;
-#endif
+    }
 }
 
 
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx
--- v2.1.124/linux/drivers/scsi/ChangeLog.ncr53c8xx	Tue Jul 21 00:15:31 1998
+++ linux/drivers/scsi/ChangeLog.ncr53c8xx	Mon Oct  5 13:40:23 1998
@@ -1,4 +1,26 @@
-Sar Jul 18 13:00 1998 Gerard Roudier (groudier@club-internet.fr)
+Sun Oct  4 14:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.0i
+	- Cosmetic changes for sparc (but not for the driver) that needs  
+	  __irq_itoa() to be used for printed IRQ value to be understandable.
+	- Some problems with the driver that didn't occur using driver 2.5f 
+	  were due to a SCSI selection problem triggered by a clearly 
+	  documented feature that in fact seems not to work: (53C8XX chips  
+	  are claimed by the manuals to be able to execute SCSI scripts just 
+	  after abitration while the SCSI core is performing SCSI selection).
+	  This optimization is broken and has been removed.
+	- Some broken scsi devices are confused when a negotiation is started 
+	  on a LUN that does not correspond to a real device. According to 
+	  SCSI specs, this is a device firmware bug. This has been worked 
+	  around by only starting negotiation if the LUN has previously be 
+	  used for at least 1 successful SCSI command.
+	- The 'last message sent' printed out on M_REJECT message reception 
+	  was read from the SFBR i/o register after the previous message had 
+	  been sent. 
+	  This was not correct and affects all previous driver versions and 
+	  the original FreeBSD one as well. The SCSI scripts has been fixed 
+	  so that it now provides the right information to the C code.
+
+Sat Jul 18 13:00 1998 Gerard Roudier (groudier@club-internet.fr)
 	* revision 3.0g
 	- Preliminary fixes for Big Endian (sent by Eddie C. Dost).
 	  Big Endian architectures should work again with the driver.
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.1.124/linux/drivers/scsi/Makefile	Thu Sep 17 17:53:36 1998
+++ linux/drivers/scsi/Makefile	Mon Oct  5 13:43:31 1998
@@ -147,6 +147,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_BLZ603EPLUS_SCSI),y)
+L_OBJS += amiga7xx.o 53c7xx.o
+else
+  ifeq ($(CONFIG_BLZ603EPLUS_SCSI),m)
+  M_OBJS += amiga7xx.o 53c7xx.o
+  endif
+endif
+
 ifeq ($(CONFIG_WARPENGINE_SCSI),y)
 L_OBJS += amiga7xx.o 53c7xx.o
 else
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/README.aic7xxx linux/drivers/scsi/README.aic7xxx
--- v2.1.124/linux/drivers/scsi/README.aic7xxx	Thu Aug  6 14:06:32 1998
+++ linux/drivers/scsi/README.aic7xxx	Thu Oct  8 08:07:33 1998
@@ -21,15 +21,24 @@
     AHA-2940W              
     AHA-2940U              
     AHA-2940UW
-    AHA-2940AU             
+    AHA-2940AU 
+    AHA-2940U2W
+    AHA-2940U2
+    AHA-2940U2B
+    AHA-2940U2BOEM
     AHA-2944D              
     AHA-2944WD
     AHA-2944UD
     AHA-2944UWD
+    AHA-2950U2
+    AHA-2950U2W
+    AHA-2950U2B
     AHA-3940
     AHA-3940U
     AHA-3940W
     AHA-3940UW
+    AHA-3940U2W
+    AHA-3950U2B
     AHA-3985
     AHA-3985U
     AHA-3985W
@@ -42,13 +51,14 @@
     AIC-786x
     AIC-787x
     AIC-788x
-    AIC-7895
+    AIC-789x
 
     Bus Types
     ----------------------------
     W - Wide SCSI, SCSI-3, 16bit bus, 68pin connector, will also support
         SCSI-1/SCSI-2 50pin devices, transfer rates up to 20MB/s.
     U - Ultra SCSI, transfer rates up to 40MB/s.
+    U2- Ultra 2 SCSI, transfer rates up to 80MB/s.
     D - Differential SCSI.
     T - Twin Channel SCSI. Up to 14 SCSI devices.
 
@@ -72,7 +82,7 @@
       (Original Linux FTP/patch maintainer)
     Jess Johnson    jester@frenzy.com
       (AIC7xxx FAQ author)
-    Doug Ledford    dledford@dialnet.net
+    Doug Ledford    dledford@redhat.com
       (Current Linux aic7xxx-5.x.x Driver/Patch/FTP/FAQ maintainer)
     
     Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original
@@ -104,38 +114,41 @@
         Some SCSI devices need the initial reset that this option disables
 	in order to work.  If you have problems at bootup, please make sure
 	you aren't using this option.
-    "aic7xxx=reverse_scan" - Have the driver register the SCSI cards in the
-        reverse of the normal order.  This may help those people who have more
-        than one PCI Adaptec controller force the correct controller to be
-        scsi0 under linux so that their boot hard drive is also sda under
-        linux
+	
+    "aic7xxx=reverse_scan" - Certain PCI motherboards scan for devices at
+        bootup by scanning from the highest numbered PCI device to the
+	lowest numbered PCI device, others do just the opposite and scan
+	from lowest to highest numbered PCI device.  There is no reliable
+	way to autodetect this ordering.  So, we default to the most common
+	order, which is lowest to highest.  Then, in case your motherboard
+	scans from highest to lowest, we have this option.  If your BIOS
+	finds the drives on controller A before controller B but the linux
+	kernel finds your drives on controller B before A, then you should
+	use this option.
+	
     "aic7xxx=extended" - Force the driver to detect extended drive translation
         on your controller.  This helps those people who have cards without
         a SEEPROM make sure that linux and all other operating systems think
         the same way about your hard drives.
+	
     "aic7xxx=irq_trigger:x" - Replace x with either 0 or 1 to force the kernel
         to use the correct IRQ type for your card.  This only applies to EISA
         based controllers.  On these controllers, 0 is for Edge triggered
         interrupts, and 1 is for Level triggered interrupts.  If you aren't
         sure or don't know which IRQ trigger type your EISA card uses, then
         let the kernel autodetect the trigger type.
+	
     "aic7xxx=verbose" - This option can be used in one of two ways.  If you
-        simply specify aic7xxx=verbose, then the kernel will automatically pick
-        the default set of verbose messages for you to see.  Alternatively, you
-        can specify the command as "aic7xxx=verbose:0xXXXX" where the X entries
-        are replaced with hexadecimal digits.  This option is a bit field type
-        option.  For a full listing of the available options, search for the
-        #define VERBOSE_xxxxxx lines in the aic7xxx.c file.  If you want verbose
-        messages, then it is recommended that you simply use the aic7xxx=verbose
-        variant of this command.
-    "aic7xxx=7895_irq_hack:x" - This option enables some work around code to
-        fix a bug in the Tyan Thunder II motherboard BIOS.  The BIOS
-        incorrectly sets the IRQs on the two channels of the 7895 to two
-        different values even though the motherboard hardware doesn't support
-        this mode of operation.  The valid values for x are: 0 to force
-        both channels to use the IRQ assigned to Channel A, 1 to force both
-        channels to use the IRQ assigned to Channel B, and -1 will disable
-        this horrible abomination of a hack.  The default is disabled (-1).
+        simply specify aic7xxx=verbose, then the kernel will automatically
+	pick the default set of verbose messages for you to see.
+	Alternatively, you can specify the command as 
+	"aic7xxx=verbose:0xXXXX" where the X entries are replaced with
+	hexadecimal digits.  This option is a bit field type option.  For
+	a full listing of the available options, search for the 
+	#define VERBOSE_xxxxxx lines in the aic7xxx.c file.  If you want
+	verbose messages, then it is recommended that you simply use the
+	aic7xxx=verbose variant of this command.
+	
     "aic7xxx=pci_parity:x" - This option controls whether or not the driver
         enables PCI parity error checking on the PCI bus.  By default, this
         checking is disabled.  To enable the checks, simply specify pci_parity
@@ -147,6 +160,145 @@
         NOTE: In order to get Even PCI parity checking, you must use the
         version of the option that does not include the : and a number at
         the end (unless you want to enter exactly 2^32 - 1 as the number).
+	
+    "aic7xxx=no_probe" - This option will disable the probing for any VLB
+        based 2842 controllers and any EISA based controllers.  This is
+	needed on certain newer motherboards where the normal EISA I/O ranges
+	have been claimed by other PCI devices.  Probing on those machines
+	will often result in the machine crashing or spontaneously rebooting
+	during startup.  Examples of machines that need this are the
+	Dell PowerEdge 6300 machines.
+	
+    "aic7xxx=panic_on_abort" - This option is for debugging and will cause
+        the driver to panic the linux kernel and freeze the system the first
+	time the drivers abort or reset routines are called.  This is most
+	helpful when some problem causes infinite reset loops that scroll too
+	fast to see.  By using this option, you can write down what the errors
+	actually are and send that information to me so it can be fixed.
+	
+    "aic7xxx=dump_card" - This option will print out the *entire* set of
+        configuration registers on the card during the init sequence.  This
+	is a debugging aid used to see exactly what state the card is in
+	when we finally finish our initialization routines.  If you don't
+	have documentation on the chipsets, this will do you absolutely
+	no good unless you are simply trying to write all the information
+	down in order to send it to me.
+	
+    "aic7xxx=dump_sequencer" - This is the same as the above options except
+        that instead of dumping the register contents on the card, this
+	option dumps the contents of the sequencer program RAM.  This gives
+	the ability to verify that the instructions downloaded to the
+	card's sequencer are indeed what they are suppossed to be.  Again,
+	unless you have documentation to tell you how to interpret these
+	numbers, then it is totally useless.
+	
+    "aic7xxx=override_term:0xffffffff" - This option is used to force the
+    	termination on your SCSI controllers to a particular setting.  This
+	is a bit mask variable that applies for up to 8 aic7xxx SCSI channels.
+	Each channel gets 4 bits, divided as follows:
+	bit   3   2   1   0
+	      |   |   |   Enable/Disable Single Ended Low Byte Termination
+	      |   |   En/Disable Single Ended High Byte Termination
+	      |   En/Disable Low Byte LVD Termination
+	      En/Disable High Byte LVD Termination
+
+	The upper 2 bits that deal with LVD termination only apply to Ultra2
+	controllers.  Futhermore, due to the current Ultra2 controller
+	designs, these bits are tied together such that setting either bit
+	enables both low and high byte LVD termination.  It is not possible
+	to only set high or low byte LVD termination in this manner.  This is
+	an artifact of the BIOS definition on Ultra2 controllers.  For other
+	controllers, the only important bits are the two lowest bits.  Setting
+	the higher bits on non-Ultra2 controllers has no effect.  A few
+	examples of how to use this option:
+
+	Enable low and high byte termination on a non-ultra2 controller that
+	is the first aic7xxx controller (the correct bits are 0011), 
+	aic7xxx=override_term:0x3
+
+	Enable all termination on the third aic7xxx controller, high byte
+	termination on the second aic7xxx controller, and low and high byte
+	SE termination on the first aic7xxx controller 
+	(bits are 1111 0010 0011), 
+	aic7xxx=override_term:0xf23
+	
+	No attempt has been made to make this option non-cryptic.  It really
+	shouldn't be used except in dire circumstances, and if that happens,
+	I'm probably going to be telling you what to set this to anyway :)
+
+    "aic7xxx=stpwlev:0xffffffff" - This option is used to control the STPWLEV
+        bit in the DEVCONFIG PCI register.  Currently, this is one of the
+	very few registers that we have absolutely *no* way of detecting
+	what the variable should be.  It depends entirely on how the chipset
+	and external terminators were coupled by the card/motherboard maker.
+	Further, a chip reset (at power up) always sets this bit to 0.  If
+	there is no BIOS to run on the chipset/card (such as with a 2910C
+	or a motherboard controller with the BIOS totally disabled) then
+	the variable may not get set properly.  Of course, if the proper
+	setting was 0, then that's what it would be after the reset, but if
+	the proper setting is actually 1.....you get the picture.  Now, since
+	we can't detect this at all, I've added this option to force the
+	setting.  If you have a BIOS on your controller then you should never
+	need to use this option.  However, if you are having lots of SCSI
+	reset problems and can't seem to get them knocked out, this may help.
+
+	Here's a test to know for certain if you need this option.  Make
+	a boot floppy that you can use to boot your computer up and that
+	will detect the aic7xxx controller.  Next, power down your computer.
+	While it's down, unplug all SCSI cables from your Adaptec SCSI
+	controller.  Boot the system back up to the Adaptec EZ-SCSI BIOS
+	and then make sure that termination is enabled on your adapter (if
+	you have an Adaptec BIOS of course).  Next, boot up the floppy you
+	made and wait for it to detect the aic7xxx controller.  If the kernel
+	finds the controller fine, says scsi : x hosts and then tries to
+	detect your devices like normal, up to the point where it fails to
+	mount your root file system and panics, then you're fine.  If, on
+	the other hand, the system goes into an infinite reset loop, then
+	you need to use this option and/or the previous option to force the
+	proper termination settings on your controller.   If this happens,
+	then you next need to figure out what your settings should be.
+
+	To find the correct settings, power your machine back down, connect
+	back up the SCSI cables, and boot back into your machine like normal.
+	However, boot with the aic7xxx=verbose:0x39 option.  Record the
+	initial DEVCONFIG values for each of your aic7xxx controllers as
+	they are listed, and also record what the machine is detecting as
+	the proper termination on your controllers.  NOTE: the order in
+	which the initial DEVCONFIG values are printed out is not gauranteed
+	to be the same order as the SCSI controllers are registered.  The
+	above option and this option both work on the order of the SCSI
+	controllers as they are registered, so make sure you match the right
+	DEVCONFIG values with the right controllers if you have more than
+	one aic7xxx controller.
+
+	Once you have the detected termination settings and the initial
+	DEVCONFIG values for each controller, then figure out what the
+	termination on each of the controllers *should* be.  Hopefully, that
+	part is correct, but it could possibly be wrong if there is
+	bogus cable detection logic on your controller or something similar.
+	If all the controllers have the correct termination settings, then
+	don't set the aic7xxx=override_term variable at all, leave it alone.
+	Next, on any controllers that go into an infinite reset loop when
+	you unplug all the SCSI cables, get the starting DEVCONFIG value.
+	If the initial DEVCONFIG value is divisible by 2, then the correct
+	setting for that controller is 0.  If it's an odd number, then
+	the correct setting for that controller is 1.  For any other
+	controllers that didn't have an infinite reset problem, then reverse
+	the above options.  If DEVCONFIG was even, then the correct setting
+	is 1, if not then the correct setting is 0.
+
+	Now that you know what the correct setting was for each controller,
+	we need to encode that into the aic7xxx=stpwlev:0x... variable.
+	This variable is a bit field encoded variable.  Bit 0 is for the first
+	aic7xxx controller, bit 1 for the next, etc.  Put all these bits
+	together and you get a number.  For example, if the third aic7xxx
+	needed a 1, but the second and first both needed a 0, then the bits
+	would be 100 in binary.  This then translates to 0x04.  You would
+	therefore set aic7xxx=stpwlev:0x04.  This is fairly standard binary
+	to hexadecimal conversions here.  If you aren't up to speed on the
+	binary->hex conversion then send an email to the aic7xxx mailing
+	list and someone can help you out.
+
     "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to enable
         tagged queueing on specific devices.  As of driver version 5.0.6, we
         now globally enable tagged queueing by default, but we also disable
@@ -214,8 +366,8 @@
     
     aic7xxx=verbose,extended,irq_trigger:1
     
-    The only requirement is that individual options be separated by a comma on
-    the command line.
+    The only requirement is that individual options be separated by a comma or
+    a period on the command line.
         
   Module Loading command options
   ------------------------------
@@ -263,11 +415,35 @@
     Thanks to Michael Neuffer for for his upper-level SCSI help, and
     Matthew Jacob for statistics support.
 
+  Debugging the driver
+  ------------------------------
+    Should you have problems with this driver, and would like some help in
+    getting them solved, there are a couple debugging items built into
+    the driver to facilitate getting the needed information from the system.
+    In general, I need a complete description of the problem, with as many
+    logs as possible concerning what happens.  To help with this, there is
+    a command option aic7xxx=panic_on_abort.  This option, when set, forces
+    the driver to panic the kernel on the first SCSI abort issued by the
+    mid level SCSI code.  If your system is going to reset loops and you
+    can't read the screen, then this is what you need.  Not only will it
+    stop the system, but it also prints out a large amount of state
+    information in the process.  Second, if you specify the option
+    "aic7xxx=verbose:0x1ffff", the system will print out *SOOOO* much
+    information as it runs that you won't be able to see anything.
+    However, this can actually be very usefull if your machine simply
+    locks up when trying to boot, since it will pin-point what was last
+    happening (in regards to the aic7xxx driver) immediately prior to
+    the lockup.  This is really only usefull if your machine simply can
+    not boot up successfully.  If you can get your machine to run, then
+    this will produce far too much information.
+
   FTP sites
   ------------------------------
-    ftp://ftp.dialnet.net/pub/linux/aic7xxx/
+    ftp://ftp.redhat.com/pub/aic/
       - Primary site for Doug Ledford developed driver releases
-      - US Linux mirror of Teleport site
+    ftp://ftp.dialnet.net/pub/linux/aic7xxx
+      - Temporary mirror of the redhat.com ftp site while people
+        get used to the new address
     ftp://ftp.pcnet.com/users/eischen/Linux/
       - Dan Eischen's driver distribution area
     ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c
--- v2.1.124/linux/drivers/scsi/aha1542.c	Wed Sep  9 14:51:08 1998
+++ linux/drivers/scsi/aha1542.c	Wed Oct  7 15:52:55 1998
@@ -16,6 +16,8 @@
  *  Modified by Mike McLagan <mike.mclagan@linux.org>
  *        Recognise extended mode on AHA1542CP, different bit than 1542CF
  *        1-Jan-97
+ *  Modified by Bjorn L. Thordarson and Einar Thor Einarsson
+ *        Recognize that DMA0 is valid DMA channel -- 13-Jul-98
  */
 
 #include <linux/module.h>
@@ -98,13 +100,6 @@
  *		    Factory default is 5 MB/s.
  */
 
-
-/* The DMA-Controller.  We need to fool with this because we want to 
-   be able to use the aha1542 without having to have the bios enabled */
-#define DMA_MODE_REG	0xd6
-#define DMA_MASK_REG	0xd4
-#define	CASCADE		0xc0
-
 #define BIOS_TRANSLATION_1632 0  /* Used by some old 1542A boards */
 #define BIOS_TRANSLATION_6432 1 /* Default case these days */
 #define BIOS_TRANSLATION_25563 2 /* Big disk case */
@@ -767,8 +762,8 @@
     *dma_chan = 5;
     break;
   case 0x01:
-    printk("DMA priority 0 not available for Adaptec driver\n");
-    return -1;
+    *dma_chan = 0;
+    break;
   case 0:
     /* This means that the adapter, although Adaptec 1542 compatible, doesn't use a DMA channel.
        Currently only aware of the BusLogic BT-445S VL-Bus adapter which needs this. */
@@ -1038,9 +1033,9 @@
 				    goto unregister;
 			    }
 			    
-			    if (dma_chan >= 5) {
-				    outb((dma_chan - 4) | CASCADE, DMA_MODE_REG);
-				    outb(dma_chan - 4, DMA_MASK_REG);
+			    if (dma_chan == 0 || dma_chan >= 5) {
+				    set_dma_mode(dma_chan, DMA_MODE_CASCADE);
+				    enable_dma(dma_chan);
 			    }
 		    }
 		    aha_host[irq_level - 9] = shpnt;
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx/aic7xxx.reg linux/drivers/scsi/aic7xxx/aic7xxx.reg
--- v2.1.124/linux/drivers/scsi/aic7xxx/aic7xxx.reg	Tue Apr 14 14:29:21 1998
+++ linux/drivers/scsi/aic7xxx/aic7xxx.reg	Thu Oct  8 08:07:33 1998
@@ -1,7 +1,7 @@
 /*
  * Aic7xxx register and scratch ram definitions.
  *
- * Copyright (c) 1994-1997 Justin Gibbs.
+ * Copyright (c) 1994-1998 Justin Gibbs.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -161,6 +161,7 @@
 	access_mode RW
 	bit	WIDEXFER	0x80		/* Wide transfer control */
 	mask	SXFR		0x70		/* Sync transfer rate */
+	mask	SXFR_ULTRA2	0x7f		/* Sync transfer rate */
 	mask	SOFS		0x0f		/* Sync offset */
 }
 
@@ -174,6 +175,13 @@
 	access_mode RW
 	mask	TID		0xf0		/* Target ID mask */
 	mask	OID		0x0f		/* Our ID mask */
+	/*
+	 * SCSI Maximum Offset (p. 4-61 aic7890/91 Data Book)
+	 * The aic7890/91 allow an offset of up to 127 transfers in both wide
+	 * and narrow mode.
+	 */
+	alias	SCSIOFFSET
+	mask	SOFS_ULTRA2	0x7f		/* Sync offset U2 chips */
 }
 
 /*
@@ -227,14 +235,15 @@
 register SSTAT0	{
 	address			0x00b
 	access_mode RO
-	bit	TARGET		0x80		/* Board acting as target */
-	bit	SELDO		0x40		/* Selection Done */
-	bit	SELDI		0x20		/* Board has been selected */
-	bit	SELINGO		0x10		/* Selection In Progress */
-	bit	SWRAP		0x08		/* 24bit counter wrap */
-	bit	SDONE		0x04		/* STCNT = 0x000000 */
-	bit	SPIORDY		0x02		/* SCSI PIO Ready */
-	bit	DMADONE		0x01		/* DMA transfer completed */
+	bit	TARGET		0x80	/* Board acting as target */
+	bit	SELDO		0x40	/* Selection Done */
+	bit	SELDI		0x20	/* Board has been selected */
+	bit	SELINGO		0x10	/* Selection In Progress */
+	bit	SWRAP		0x08	/* 24bit counter wrap */
+	bit	IOERR		0x08	/* LVD Tranceiver mode changed */
+	bit	SDONE		0x04	/* STCNT = 0x000000 */
+	bit	SPIORDY		0x02	/* SCSI PIO Ready */
+	bit	DMADONE		0x01	/* DMA transfer completed */
 }
 
 /*
@@ -276,6 +285,7 @@
 	address			0x00d
 	access_mode RO
 	bit	OVERRUN		0x80
+	bit	EXP_ACTIVE	0x10	/* SCSI Expander Active */
 	mask	SFCNT		0x1f
 }
 
@@ -290,14 +300,13 @@
 }
 
 /*
- * SCSI Test Control (p. 3-27)
+ * SCSI ID for the aic7890/91 chips
  */
-register SCSITEST {
+register SCSIID_ULTRA2 {
 	address			0x00f
 	access_mode RW
-	bit	RQAKCNT		0x04
-	bit	CNTRTEST	0x02
-	bit	CMODE		0x01
+	mask	TID		0xf0		/* Target ID mask */
+	mask	OID		0x0f		/* Our ID mask */
 }
 
 /*
@@ -312,6 +321,7 @@
 	bit	ENSELDI		0x20
 	bit	ENSELINGO	0x10
 	bit	ENSWRAP		0x08
+	bit	ENIOERR		0x08	/* LVD Tranceiver mode changes */
 	bit	ENSDONE		0x04
 	bit	ENSPIORDY	0x02
 	bit	ENDMADONE	0x01
@@ -424,7 +434,10 @@
 	bit	DIAGLEDON	0x40	/* Aic78X0 only */
 	bit	AUTOFLUSHDIS	0x20
 	bit	SELBUSB		0x08
+	bit	ENAB40		0x08	/* LVD transceiver active */
+	bit	ENAB20		0x04	/* SE/HVD transceiver active */
 	bit	SELWIDE		0x02
+	bit	XCVR		0x01	/* External transceiver active */
 }
 
 /*
@@ -547,6 +560,19 @@
 	bit	ENABLE		0x01
 }
 
+register DSCOMMAND0 {
+	address			0x084
+	access_mode RW
+	bit	CACHETHEN	0x80
+	bit	DPARCKEN	0x40
+	bit	MPARCKEN	0x20
+	bit	EXTREQLCK	0x10
+	bit	INTSCBRAMSEL	0x08
+	bit	RAMPS		0x04
+	bit	USCBSIZE32	0x02
+	bit	CIOPARCKEN	0x01
+}
+
 /*
  * On the aic78X0 chips, Board Control is replaced by the DSCommand
  * register (p. 4-64)
@@ -676,6 +702,7 @@
 register ERROR {
 	address			0x092
 	access_mode RO
+	bit	CIOPARERR	0x80	/* Ultra2 only */
 	bit	PCIERRSTAT	0x40	/* PCI only */
 	bit	MPARERR		0x20	/* PCI only */
 	bit	DPARERR		0x10	/* PCI only */
@@ -701,6 +728,7 @@
 register DFCNTRL {
 	address			0x093
 	access_mode RW
+	bit	PRELOADEN	0x80	/* aic7890 only */
 	bit	WIDEODD		0x40
 	bit	SCSIEN		0x20
 	bit	SDMAEN		0x10
@@ -715,6 +743,7 @@
 register DFSTATUS {
 	address			0x094
 	access_mode RO
+	bit	PRELOAD_AVAIL	0x80
 	bit	DWORDEMP	0x20
 	bit	MREQPEND	0x10
 	bit	HDONE		0x08
@@ -777,6 +806,14 @@
 }
 
 /*
+ * Special Function
+ */
+register SFUNCT {
+	address			0x09f
+	access_mode RW
+}
+
+/*
  * SCB Definition (p. 5-4)
  */
 scb {
@@ -865,6 +902,109 @@
 
 register DSPCISTATUS {
 	address			0x086
+	mask	DFTHRSH_100	0xc0
+}
+
+register CCHADDR {
+	address			0x0E0
+	size 8
+}
+
+register CCHCNT {
+	address			0x0E8
+}
+
+register CCSGRAM {
+	address			0x0E9
+}
+
+register CCSGADDR {
+	address			0x0EA
+}
+
+register CCSGCTL {
+	address			0x0EB
+	bit	CCSGDONE	0x80
+	bit	CCSGEN		0x08
+	bit	FLAG		0x02
+	bit	CCSGRESET	0x01
+}
+
+register CCSCBCNT {
+	address			0xEF
+}
+
+register CCSCBCTL {
+	address			0x0EE
+	bit	CCSCBDONE	0x80
+	bit	ARRDONE		0x40	/* SCB Array prefetch done */
+	bit	CCARREN		0x10
+	bit	CCSCBEN		0x08
+	bit	CCSCBDIR	0x04
+	bit	CCSCBRESET	0x01
+}
+
+register CCSCBADDR {
+	address			0x0ED
+}
+
+register CCSCBRAM {
+	address			0xEC
+}
+
+register CCSCBPTR {
+	address			0x0F1
+}
+
+register HNSCB_QOFF {
+	address			0x0F4
+}
+
+register SNSCB_QOFF {
+	address			0x0F6
+}
+
+register SDSCB_QOFF {
+	address			0x0F8
+}
+
+register QOFF_CTLSTA {
+	address			0x0FA
+	bit	SCB_AVAIL	0x40
+	bit	SNSCB_ROLLOVER	0x20
+	bit	SDSCB_ROLLOVER	0x10
+	mask	SCB_QSIZE	0x07
+	mask	SCB_QSIZE_256	0x06
+}
+
+register DFF_THRSH {
+	address			0x0FB
+	mask	WR_DFTHRSH	0x70
+	mask	RD_DFTHRSH	0x07
+	mask	RD_DFTHRSH_MIN	0x00
+	mask	RD_DFTHRSH_25	0x01
+	mask	RD_DFTHRSH_50	0x02
+	mask	RD_DFTHRSH_63	0x03
+	mask	RD_DFTHRSH_75	0x04
+	mask	RD_DFTHRSH_85	0x05
+	mask	RD_DFTHRSH_90	0x06
+	mask	RD_DFTHRSH_MAX	0x07
+	mask	WR_DFTHRSH_MIN	0x00
+	mask	WR_DFTHRSH_25	0x10
+	mask	WR_DFTHRSH_50	0x20
+	mask	WR_DFTHRSH_63	0x30
+	mask	WR_DFTHRSH_75	0x40
+	mask	WR_DFTHRSH_85	0x50
+	mask	WR_DFTHRSH_90	0x60
+	mask	WR_DFTHRSH_MAX	0x70
+}
+
+register SG_CACHEPTR {
+	access_mode RW
+	address			0x0fc
+	mask	SG_USER_DATA	0xfc
+	bit	LAST_SEG	0x02
+	bit	LAST_SEG_DONE	0x01
 }
 
 register BRDCTL	{
@@ -877,6 +1017,12 @@
 	bit	BRDRW		0x04
 	bit	BRDCTL1		0x02
 	bit	BRDCTL0		0x01
+	/* 7890 Definitions */
+	bit	BRDDAT4		0x10
+	bit	BRDDAT3		0x08
+	bit	BRDDAT2		0x04
+	bit	BRDRW_ULTRA2	0x02
+	bit	BRDSTB_ULTRA2	0x01
 }
 
 /*
@@ -935,7 +1081,7 @@
 	/*
 	 * 1 byte per target starting at this address for configuration values
 	 */
-	TARG_SCRATCH {
+	TARG_SCSIRATE {
 		size		16
 	}
 	/*
@@ -960,6 +1106,7 @@
 	/* Parameters for DMA Logic */
 	DMAPARAMS {
 		size		1
+		bit	PRELOADEN	0x80
 		bit	WIDEODD		0x40
 		bit	SCSIEN		0x20
 		bit	SDMAEN		0x10
@@ -1078,12 +1225,27 @@
 		mask	MSGOUT_PHASEMIS	0x10
 		alias	RETURN_1
 	}
+	ARG_2 {
+		size		1
+		alias	RETURN_2
+	}
+
 	/*
 	 * Snapshot of MSG_OUT taken after each message is sent.
 	 */
 	LAST_MSG {
 		size		1
 	}
+
+	/*
+	 * Number of times we have filled the CCSGRAM with prefetched
+	 * SG elements.
+	 */
+	PREFETCH_CNT {
+		size		1
+	}
+
+
 	/*
 	 * These are reserved registers in the card's scratch ram.  Some of
 	 * the values are specified in the AHA2742 technical reference manual
@@ -1108,22 +1270,35 @@
 		mask	BIOSDISABLED		0x30	
 		bit	CHANNEL_B_PRIMARY	0x08
 	}
+	/*
+	 * Per target SCSI offset values for Ultra2 controllers.
+	 */
+	TARG_OFFSET {
+		address		0x070
+		size		16
+	}
 }
 
 const SCB_LIST_NULL	0xff
 
+const CCSGADDR_MAX	0x80
+const CCSGRAM_MAXSEGS	16
+
 /* Offsets into the SCBID array where different data is stored */
 const UNTAGGEDSCB_OFFSET	0
 const QOUTFIFO_OFFSET		1
 const QINFIFO_OFFSET		2
 
 /* WDTR Message values */
-const BUS_8_BIT		0x00
+const BUS_8_BIT			0x00
 const BUS_16_BIT		0x01
 const BUS_32_BIT		0x02
+
+/* Offset maximums */
 const MAX_OFFSET_8BIT		0x0f
-const MAX_OFFSET_16BIT	0x08
-const HOST_MSG		0xFF
+const MAX_OFFSET_16BIT		0x08
+const MAX_OFFSET_ULTRA2		0x7f
+const HOST_MSG			0xff
 
 /* Target mode command processing constants */
 const CMD_GROUP_CODE_SHIFT	0x05
@@ -1135,11 +1310,7 @@
 /*
  * Downloaded (kernel inserted) constants
  */
-/*
- * Mask of bits to test against when looking at the Queue Count
- * registers.  Works around a bug on aic7850 chips. 
- */
-const QCNTMASK		download
+
 /*
  * Number of command descriptors in the command descriptor array.
  */
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx/aic7xxx.seq linux/drivers/scsi/aic7xxx/aic7xxx.seq
--- v2.1.124/linux/drivers/scsi/aic7xxx/aic7xxx.seq	Tue Apr 14 14:29:21 1998
+++ linux/drivers/scsi/aic7xxx/aic7xxx.seq	Thu Oct  8 08:07:33 1998
@@ -1,7 +1,7 @@
 /*
  * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
  *
- * Copyright (c) 1994-1997 Justin Gibbs.
+ * Copyright (c) 1994-1998 Justin Gibbs.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -14,7 +14,7 @@
  *    derived from this software without specific prior written permission.
  *
  * Where this Software is combined with software released under the terms of 
- * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * the GNU Public License (GPL) and the terms of the GPL would require the 
  * combined work to also be released under the terms of the GPL, the terms
  * and conditions of this License will apply in addition to those of the
  * GPL with the exception of any terms or conditions of this License that
@@ -32,7 +32,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	$Id: aic7xxx.seq,v 1.74 1997/06/27 19:38:42 gibbs Exp $
+ *	$Id: aic7xxx.seq,v 1.77 1998/06/28 02:58:57 gibbs Exp $
  */
 
 #include "aic7xxx.reg"
@@ -59,38 +59,55 @@
 reset:
 	clr	SCSISIGO;		/* De-assert BSY */
 	/* Always allow reselection */
-.if ( TARGET_MODE )
-	mvi	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP;
-.else
-	mvi	SCSISEQ, ENRSELI|ENAUTOATNP;
-.endif
+	if ((p->flags & AHC_TARGETMODE) != 0) {
+		mvi	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP;
+	} else {
+		mvi	SCSISEQ, ENRSELI|ENAUTOATNP;
+	}
+
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		/* Ensure that no DMA operations are in progress */
+		clr	CCSGCTL;
+		clr	CCSCBCTL;
+	}
+
 	call	clear_target_state;
 	and	SXFRCTL0, ~SPIOEN;
 poll_for_work:
-	mov	A, QINPOS;
+	if ((p->features & AHC_QUEUE_REGS) == 0) {
+		mov	A, QINPOS;
+	}
 poll_for_work_loop:
-	and	SEQCTL, ~PAUSEDIS;
-	test	SSTAT0, SELDO|SELDI	jnz selection;
-	test	SCSISEQ, ENSELO	jnz poll_for_work;
-.if ( TWIN_CHANNEL )
-	/*
-	 * Twin channel devices cannot handle things like SELTO
-	 * interrupts on the "background" channel.  So, if we
-	 * are selecting, keep polling the current channel util
-	 * either a selection or reselection occurs.
-	 */
-	xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
+	if ((p->features & AHC_QUEUE_REGS) == 0) {
+		and	SEQCTL, ~PAUSEDIS;
+	}
 	test	SSTAT0, SELDO|SELDI	jnz selection;
 	test	SCSISEQ, ENSELO	jnz poll_for_work;
-	xor	SBLKCTL,SELBUSB;	/* Toggle back */
-.endif
+	if ((p->features & AHC_TWIN) != 0) {
+		/*
+		 * Twin channel devices cannot handle things like SELTO
+		 * interrupts on the "background" channel.  So, if we
+		 * are selecting, keep polling the current channel util
+		 * either a selection or reselection occurs.
+		 */
+		xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
+		test	SSTAT0, SELDO|SELDI	jnz selection;
+		test	SCSISEQ, ENSELO	jnz poll_for_work;
+		xor	SBLKCTL,SELBUSB;	/* Toggle back */
+	}
 	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
 test_queue:
 	/* Has the driver posted any work for us? */
-	or	SEQCTL, PAUSEDIS;
-	cmp	KERNEL_QINPOS, A je poll_for_work_loop;
-	inc	QINPOS;
-	and	SEQCTL, ~PAUSEDIS;
+	if ((p->features & AHC_QUEUE_REGS) != 0) {
+		test	QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
+		mov	NONE, SNSCB_QOFF;
+		inc	QINPOS;
+	} else {
+		or	SEQCTL, PAUSEDIS;
+		cmp	KERNEL_QINPOS, A je poll_for_work_loop;
+		inc	QINPOS;
+		and	SEQCTL, ~PAUSEDIS;
+	}
 
 /*
  * We have at least one queued SCB now and we don't have any 
@@ -98,26 +115,24 @@
  * any SCBs available for use, pull the tag from the QINFIFO
  * and get to work on it.
  */
-.if ( SCB_PAGING )
-	mov	ALLZEROS	call	get_free_or_disc_scb;
-.endif
+	if ((p->flags & AHC_PAGESCBS) != 0) {
+		mov	ALLZEROS	call	get_free_or_disc_scb;
+	}
+
 dequeue_scb:
 	add	A, -1, QINPOS;
-	mvi	QINFIFO_OFFSET call set_SCBID_host_addr_and_cnt;
-	mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+	mvi	QINFIFO_OFFSET call fetch_byte;
 
-	call	dma_finish;
-	mov	SINDEX, DFDAT;
-.if !( SCB_PAGING )
-	/* In the non-paging case, the SCBID == hardware SCB index */
-	mov	SCBPTR, SINDEX;
-.endif
+	if ((p->flags & AHC_PAGESCBS) == 0) {
+		/* In the non-paging case, the SCBID == hardware SCB index */
+		mov	SCBPTR, RETURN_2;
+	}
 dma_queued_scb:
 /*
  * DMA the SCB from host ram into the current SCB location.
  */
 	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
-	call	dma_scb;
+	mov	RETURN_2	 call dma_scb;
 
 start_scb:
 	/*
@@ -135,16 +150,22 @@
 	jmp	poll_for_work;
 
 start_selection:
-.if ( TWIN_CHANNEL )
-	and	SINDEX,~SELBUSB,SBLKCTL;/* Clear the channel select bit */
-	and	A,SELBUSB,SCB_TCL;	/* Get new channel bit */
-	or	SINDEX,A;
-	mov	SBLKCTL,SINDEX;		/* select channel */
-.endif
+	if ((p->features & AHC_TWIN) != 0) {
+		and	SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
+		and	A,SELBUSB,SCB_TCL;	/* Get new channel bit */
+		or	SINDEX,A;
+		mov	SBLKCTL,SINDEX;		/* select channel */
+	}
 initialize_scsiid:
-	and	A, TID, SCB_TCL;	/* Get target ID */
-	and	SCSIID, OID;		/* Clear old target */
-	or	SCSIID, A;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		and	A, TID, SCB_TCL;	/* Get target ID */
+		and	SCSIID_ULTRA2, OID;	/* Clear old target */
+		or	SCSIID_ULTRA2, A;
+	} else {
+		and	A, TID, SCB_TCL;	/* Get target ID */
+		and	SCSIID, OID;		/* Clear old target */
+		or	SCSIID, A;
+	}
 	mvi	SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret;
 
 /*
@@ -155,143 +176,154 @@
 initialize_channel:
 	or	A, CLRSTCNT|CLRCHN, SINDEX;
 	or	SXFRCTL0, A;
-.if ( ULTRA )
+	if ((p->features & AHC_ULTRA) != 0) {
 ultra:
-	mvi	SINDEX, ULTRA_ENB+1;
-	test	SAVED_TCL, 0x80		jnz ultra_2;	/* Target ID > 7 */
-	dec	SINDEX;
+		mvi	SINDEX, ULTRA_ENB+1;
+		test	SAVED_TCL, 0x80		jnz ultra_2; /* Target ID > 7 */
+		dec	SINDEX;
 ultra_2:
-	mov     FUNCTION1,SAVED_TCL;
-	mov     A,FUNCTION1;
-	test	SINDIR, A	jz ndx_dtr;
-	or	SXFRCTL0, FAST20;
-.endif
- 
+		mov     FUNCTION1,SAVED_TCL;
+		mov     A,FUNCTION1;
+		test	SINDIR, A	jz ndx_dtr;
+		or	SXFRCTL0, FAST20;
+	} 
 /*
  * Initialize SCSIRATE with the appropriate value for this target.
  * The SCSIRATE settings for each target are stored in an array
- * based at TARG_SCRATCH.
+ * based at TARG_SCSIRATE.
  */
 ndx_dtr:
 	shr	A,4,SAVED_TCL;
-	test	SBLKCTL,SELBUSB	jz ndx_dtr_2;
-	or	SAVED_TCL, SELBUSB; /* Add the channel bit while we're here */
-	or	A,0x08;			/* Channel B entries add 8 */
+	if ((p->features & AHC_TWIN) != 0) {
+		test	SBLKCTL,SELBUSB	jz ndx_dtr_2;
+		or	SAVED_TCL, SELBUSB; 
+		or	A,0x08;			/* Channel B entries add 8 */
 ndx_dtr_2:
-	add	SINDEX,TARG_SCRATCH,A;
+	}
+
+	if ((p->features & AHC_ULTRA2) != 0) {
+		add	SINDEX, TARG_OFFSET, A;
+		mov	SCSIOFFSET, SINDIR;
+	}
+
+	add	SINDEX,TARG_SCSIRATE,A;
 	mov	SCSIRATE,SINDIR ret;
 
 
 selection:
 	test	SSTAT0,SELDO	jnz select_out;
 select_in:
-.if ( TARGET_MODE )
-	test	SSTAT0, TARGET	jz initiator_reselect;
-	/*
-	 * We've just been selected.  Assert BSY and
-	 * setup the phase for receiving the messages
-	 * from the target.
-	 */
-	mvi	SCSISIGO, P_MESGOUT|BSYO;
-	mvi	CLRSINT0, CLRSELDO;
-
-	/*
-	 * If ATN isn't asserted, go directly to bus free.
-	 */
-	test	SCSISIGI, ATNI	jz	target_busfree;
-
-	/*
-	 * Setup the DMA for sending the identify and
-	 * command information.
-	 */
-	mov	A, TMODE_CMDADDR_NEXT;
-	mvi	TMODE_CMDADDR call set_32byte_haddr_and_clrcnt;
-	mvi	DFCNTRL, FIFORESET;
+	if ((p->flags & AHC_TARGETMODE) != 0) {
+		test	SSTAT0, TARGET	jz initiator_reselect;
+		/*
+		 * We've just been selected.  Assert BSY and
+		 * setup the phase for receiving the messages
+		 * from the target.
+		 */
+		mvi	SCSISIGO, P_MESGOUT|BSYO;
+		mvi	CLRSINT0, CLRSELDO;
+
+		/*
+		 * If ATN isn't asserted, go directly to bus free.
+		 */
+		test	SCSISIGI, ATNI	jz	target_busfree;
+
+		/*
+		 * Setup the DMA for sending the identify and
+		 * command information.
+		 */
+		mov	A, TMODE_CMDADDR_NEXT;
+		mvi	DINDEX, HADDR;
+		mvi	TMODE_CMDADDR call set_32byte_addr;
+		mvi	DFCNTRL, FIFORESET;
 
-	clr	SINDEX;
-	/* Watch ATN closely now */
+		clr	SINDEX;
+		/* Watch ATN closely now */
 message_loop:
-	or	SXFRCTL0, SPIOEN;
-	test	SSTAT0, SPIORDY jz .;
-	and	SXFRCTL0, ~SPIOEN;
-	mov	DINDEX, SCSIDATL;
-	mov	DFDAT, DINDEX;
-	inc	SINDEX;
-
-	/* Message Testing... */
-	test	DINDEX, MSG_IDENTIFYFLAG jz . + 2;
-	mov	ARG_1, DINDEX;
-
-	test	SCSISIGI, ATNI	jnz	message_loop;
-	add	A, -4, SINDEX;
-	jc	target_cmdphase;
-	mvi	DFDAT, SCB_LIST_NULL;	/* Terminate the message list */
+		or	SXFRCTL0, SPIOEN;
+		test	SSTAT0, SPIORDY jz .;
+		and	SXFRCTL0, ~SPIOEN;
+		mov	DINDEX, SCSIDATL;
+		mov	DFDAT, DINDEX;
+		inc	SINDEX;
+
+		/* Message Testing... */
+		test	DINDEX, MSG_IDENTIFYFLAG jz . + 2;
+		mov	ARG_1, DINDEX;
+
+		test	SCSISIGI, ATNI	jnz	message_loop;
+		add	A, -4, SINDEX;
+		jc	target_cmdphase;
+		mvi	DFDAT, SCB_LIST_NULL;	/* Terminate the message list */
 
 target_cmdphase:
-	add	HCNT[0], 1, A;
-	mvi	SCSISIGO, P_COMMAND|BSYO;
-	or	SXFRCTL0, SPIOEN;
-	test	SSTAT0, SPIORDY jz .;
-	mov	A, SCSIDATL;
-	mov	DFDAT, A;	/* Store for host */
-
-	/*
-	 * Determine the number of bytes to read
-	 * based on the command group code.  Count is
-	 * one less than the total since we've already
-	 * fetched the first byte.
-	 */
-	clr	SINDEX;
-	shr	A, CMD_GROUP_CODE_SHIFT;
-	add	SEQADDR0, A;
-
-	add	SINDEX, CMD_GROUP0_BYTE_DELTA;
-	nop;	/* Group 1 and 2 are the same */
-	add	SINDEX, CMD_GROUP2_BYTE_DELTA;
-	nop;	/* Group 3 is reserved */
-	add	SINDEX, CMD_GROUP4_BYTE_DELTA;
-	add	SINDEX, CMD_GROUP5_BYTE_DELTA;
+		add	HCNT[0], 1, A;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		mvi	SCSISIGO, P_COMMAND|BSYO;
+		or	SXFRCTL0, SPIOEN;
+		test	SSTAT0, SPIORDY jz .;
+		mov	A, SCSIDATL;
+		mov	DFDAT, A;	/* Store for host */
+
+		/*
+		 * Determine the number of bytes to read
+		 * based on the command group code.  Count is
+		 * one less than the total since we've already
+		 * fetched the first byte.
+		 */
+		clr	SINDEX;
+		shr	A, CMD_GROUP_CODE_SHIFT;
+		add	SEQADDR0, A;
+
+		add	SINDEX, CMD_GROUP0_BYTE_DELTA;
+		nop;	/* Group 1 and 2 are the same */
+		add	SINDEX, CMD_GROUP2_BYTE_DELTA;
+		nop;	/* Group 3 is reserved */
+		add	SINDEX, CMD_GROUP4_BYTE_DELTA;
+		add	SINDEX, CMD_GROUP5_BYTE_DELTA;
 		/* Group 6 and 7 are not handled yet */
 
-	mov	A, SINDEX;
-	add	HCNT[0], A;
+		mov	A, SINDEX;
+		add	HCNT[0], A;
 
 command_loop:
-	test	SSTAT0, SPIORDY jz .;
-	cmp	SINDEX, 1 jne . + 2;
-	and	SXFRCTL0, ~SPIOEN;	/* Last Byte */
-	mov	DFDAT, SCSIDATL;
-	dec	SINDEX;
-	test	SINDEX, 0xFF jnz command_loop;
+		test	SSTAT0, SPIORDY jz .;
+		cmp	SINDEX, 1 jne . + 2;
+		and	SXFRCTL0, ~SPIOEN;	/* Last Byte */
+		mov	DFDAT, SCSIDATL;
+		dec	SINDEX;
+		test	SINDEX, 0xFF jnz command_loop;
 
-	or	DFCNTRL, HDMAEN|FIFOFLUSH;
+		or	DFCNTRL, HDMAEN|FIFOFLUSH;
 	
-	call	dma_finish;
+		call	dma_finish;
 
-	test	ARG_1, MSG_IDENTIFY_DISCFLAG jz selectin_post;
+		test	ARG_1, MSG_IDENTIFY_DISCFLAG jz selectin_post;
 
-	mvi	SCSISIGO, P_MESGIN|BSYO;
+		mvi	SCSISIGO, P_MESGIN|BSYO;
 
-	or	SXFRCTL0, SPIOEN;
+		or	SXFRCTL0, SPIOEN;
 
-	mvi	MSG_DISCONNECT call target_outb;
+		mvi	MSG_DISCONNECT call target_outb;
 	
 selectin_post:
-	inc	TMODE_CMDADDR_NEXT;
-	cmp	TMODE_CMDADDR_NEXT, TMODE_NUMCMDS jne . + 2;
-	clr	TMODE_CMDADDR_NEXT;
-	mvi	QOUTFIFO, SCB_LIST_NULL;
-	mvi	INTSTAT,CMDCMPLT;
+		inc	TMODE_CMDADDR_NEXT;
+		cmp	TMODE_CMDADDR_NEXT, TMODE_NUMCMDS jne . + 2;
+		clr	TMODE_CMDADDR_NEXT;
+		mvi	QOUTFIFO, SCB_LIST_NULL;
+		mvi	INTSTAT,CMDCMPLT;
 
-	test	ARG_1, MSG_IDENTIFY_DISCFLAG jnz target_busfree;
+		test	ARG_1, MSG_IDENTIFY_DISCFLAG jnz target_busfree;
 
-	/* Busy loop on something then go to data or status phase */
+		/* Busy loop on something then go to data or status phase */
 
 target_busfree:
-	clr	SCSISIGO;
-	jmp	poll_for_work;
+		clr	SCSISIGO;
+		jmp	poll_for_work;
+
+	}
 
-.endif /* TARGET_MODE */
 /*
  * Reselection has been initiated by a target. Make a note that we've been
  * reselected, but haven't seen an IDENTIFY message from the target yet.
@@ -396,26 +428,41 @@
 	jmp	data_phase_loop;
 
 p_data:
-	mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		mvi	DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
+	} else {
+		mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+	}
 	test	LASTPHASE, IOI jnz . + 2;
 	or	DMAPARAMS, DIRECTION;
 	call	assert;			/*
 					 * Ensure entering a data
 					 * phase is okay - seen identify, etc.
 					 */
-
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	CCSGADDR, CCSGADDR_MAX;
+	}
 	test	SEQ_FLAGS, DPHASE	jnz data_phase_reinit;
 
+	/* We have seen a data phase */
+	or	SEQ_FLAGS, DPHASE;
+
 	/*
 	 * Initialize the DMA address and counter from the SCB.
 	 * Also set SG_COUNT and SG_NEXT in memory since we cannot
 	 * modify the values in the SCB itself until we see a
 	 * save data pointers message.
 	 */
-	mvi	DINDEX, HADDR;
-	mvi	SCB_DATAPTR	call bcopy_7;
-
-	call	set_stcnt_from_hcnt;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov	HADDR, SCB_DATAPTR, 7;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCB_DATAPTR	call bcopy_7;
+	}
+
+	if ((p->features & AHC_ULTRA2) == 0) {
+		call	set_stcnt_from_hcnt;
+	}
 
 	mov	SG_COUNT,SCB_SGCOUNT;
 
@@ -432,19 +479,38 @@
  * had an overrun.
  */
 	or	SXFRCTL1,BITBUCKET;
-	mvi	HCNT[0], 0xff;
-	mvi	HCNT[1], 0xff;
-	mvi	HCNT[2], 0xff;
-	call	set_stcnt_from_hcnt;
 	and	DMAPARAMS, ~(HDMAEN|SDMAEN);
-
+	if ((p->features & AHC_ULTRA2) != 0) {
+		bmov	HCNT, ALLONES, 3;
+	} else {
+		mvi	STCNT[0], 0xFF;
+		mvi	STCNT[1], 0xFF;
+		mvi	STCNT[2], 0xFF;
+	}
 data_phase_inbounds:
-/* If we are the last SG block, ensure wideodd is off. */
+/* If we are the last SG block, tell the hardware. */
 	cmp	SG_COUNT,0x01 jne data_phase_wideodd;
-	and	DMAPARAMS, ~WIDEODD;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		or	SG_CACHEPTR, LAST_SEG;
+	} else {
+		and	DMAPARAMS, ~WIDEODD;
+	}
 data_phase_wideodd:
-	mov	DMAPARAMS  call dma;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		mov	SINDEX, ALLONES;
+		mov	DFCNTRL, DMAPARAMS;
+		test	SSTAT0, SDONE jnz .;/* Wait for preload to complete */
+data_phase_dma_loop:
+		test	SSTAT0,	SDONE jnz data_phase_dma_done;
+		test	SSTAT1,PHASEMIS	jz data_phase_dma_loop;	/* ie. underrun */
+data_phase_dma_phasemis:
+		test	SSTAT0,SDONE	jnz . + 2;
+		mov	SINDEX,ALLZEROS;	/* Remeber the phasemiss */
+	} else {
+		mov	DMAPARAMS  call dma;
+	}
 
+data_phase_dma_done:
 /* Go tell the host about any overruns */
 	test	SXFRCTL1,BITBUCKET jnz data_phase_overrun;
 
@@ -458,11 +524,6 @@
 	dec	SG_COUNT;	/* one less segment to go */
 
 	test	SG_COUNT, 0xff	jz data_phase_finish; /* Are we done? */
-
-	clr	A;			/* add sizeof(struct scatter) */
-	add	SG_NEXT[0],SG_SIZEOF;
-	adc	SG_NEXT[1],A;
-
 /*
  * Load a struct scatter and set up the data address and length.
  * If the working value of the SG count is nonzero, then
@@ -471,33 +532,71 @@
  * This, like all DMA's, assumes little-endian host data storage.
  */
 sg_load:
-	mvi	DINDEX, HADDR;
-	mvi	SG_NEXT	call bcopy_4;
-
-	mvi	HCNT[0],SG_SIZEOF;
-	clr	HCNT[1];
-	clr	HCNT[2];
-
-	or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
-
-	call	dma_finish;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		/*
+		 * Do we have any prefetch left???
+		 */
+		cmp	CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail;
+
+		/*
+		 * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
+		 */
+		add	A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
+		mvi	A, CCSGADDR_MAX;
+		jc	. + 2;
+		shl	A, 3, SG_COUNT;
+		mov	CCHCNT, A;
+		bmov	CCHADDR, SG_NEXT, 4;
+		mvi	CCSGCTL, CCSGEN|CCSGRESET;
+		test	CCSGCTL, CCSGDONE jz .;
+		and	CCSGCTL, ~CCSGEN;
+		test	CCSGCTL, CCSGEN jnz .;
+		mvi	CCSGCTL, CCSGRESET;
+prefetched_segs_avail:
+		bmov 	HADDR, CCSGRAM, 8;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SG_NEXT	call bcopy_4;
+
+		mvi	HCNT[0],SG_SIZEOF;
+		clr	HCNT[1];
+		clr	HCNT[2];
+
+		or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+
+		call	dma_finish;
+
+		/*
+		 * Copy data from FIFO into SCB data pointer and data count.
+		 * This assumes that the SG segments are of the form:
+		 * struct ahc_dma_seg {
+		 *	u_int32_t	addr;	four bytes, little-endian order
+		 *	u_int32_t	len;	four bytes, little endian order
+		 * };
+		 */
+		mvi	HADDR	call dfdat_in_7;
+	}
+
+	if ((p->features & AHC_ULTRA2) == 0) {
+		/* Load STCNT as well.  It is a mirror of HCNT */
+		call	set_stcnt_from_hcnt;
+	}
 
-/*
- * Copy data from FIFO into SCB data pointer and data count.  This assumes
- * that the SG segments are of the form:
- *
- * struct ahc_dma_seg {
- *	u_int32_t	addr;		four bytes, little-endian order
- *	u_int32_t	len;		four bytes, little endian order
- * };
- */
-	mvi	HADDR	call dfdat_in_7;
+/* Advance the SG pointer */
+	clr	A;			/* add sizeof(struct scatter) */
+	add	SG_NEXT[0],SG_SIZEOF;
+	adc	SG_NEXT[1],A;
 
-/* Load STCNT as well.  It is a mirror of HCNT */
-	call	set_stcnt_from_hcnt;
 	test	SSTAT1,PHASEMIS	jz data_phase_loop;
+	/* Ensure the last seg is visable at the shaddow layer */
+	if ((p->features & AHC_ULTRA2) != 0) {
+		or	DFCNTRL, PRELOADEN;
+	}
 
 data_phase_finish:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		call	ultra2_dmafinish;
+	}
 /*
  * After a DMA finishes, save the SG and STCNT residuals back into the SCB
  * We use STCNT instead of HCNT, since it's a reflection of how many bytes 
@@ -508,12 +607,17 @@
 	mov	SCB_RESID_DCNT[2],STCNT[2];
 	mov	SCB_RESID_SGCNT, SG_COUNT;
 
-	/* We have seen a data phase */
-	or	SEQ_FLAGS, DPHASE;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+	}
 
 	jmp	ITloop;
 
 data_phase_overrun:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		call	ultra2_dmafinish;
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+	}
 /*
  * Turn off BITBUCKET mode and notify the host
  */
@@ -521,6 +625,19 @@
 	mvi	INTSTAT,DATA_OVERRUN;
 	jmp	ITloop;
 
+ultra2_dmafinish:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		test	DFCNTRL, DIRECTION jnz ultra2_dmahalt;
+		and	DFCNTRL, ~SCSIEN;
+		test	DFCNTRL, SCSIEN jnz .;
+		or	DFCNTRL, FIFOFLUSH;
+		test	DFSTATUS, FIFOEMP jz . - 1;
+ultra2_dmahalt:
+		and     DFCNTRL, ~(SCSIEN|HDMAEN);
+		test	DFCNTRL, HDMAEN jnz .;
+		ret;
+	}
+
 /*
  * Command phase.  Set up the DMA registers and let 'er rip.
  */
@@ -530,14 +647,26 @@
 /*
  * Load HADDR and HCNT.
  */
-	mvi	DINDEX, HADDR;
-	mvi	SCB_CMDPTR	call bcopy_5;
-	clr	HCNT[1];
-	clr	HCNT[2];
-
-	call	set_stcnt_from_hcnt;
-
-	mvi	(SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		or	SG_CACHEPTR, LAST_SEG;
+	}
+
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov	HADDR, SCB_CMDPTR, 5;
+		bmov	HCNT[1], ALLZEROS, 2;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCB_CMDPTR	call bcopy_5;
+		clr	HCNT[1];
+		clr	HCNT[2];
+	}
+
+	if ((p->features & AHC_ULTRA2) == 0) {
+		call	set_stcnt_from_hcnt;
+		mvi	(SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
+	} else {
+		mvi	(PRELOADEN|SCSIEN|HDMAEN|DIRECTION) call dma;
+	}
 	jmp	ITloop;
 
 /*
@@ -562,7 +691,6 @@
  * on an SCB that might not be for the current nexus. (For example, a
  * BDR message in responce to a bad reselection would leave us pointed to
  * an SCB that doesn't have anything to do with the current target).
-
  * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
  * bus device reset).
  *
@@ -574,11 +702,11 @@
 	mov	SINDEX, MSG_OUT;
 	cmp	SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
 p_mesgout_identify:
-.if ( WIDE )
-	and	SINDEX,0xf,SCB_TCL;	/* lun */
-.else
-	and	SINDEX,0x7,SCB_TCL;	/* lun */
-.endif
+	if ((p->features & AHC_WIDE) != 0) {
+		and	SINDEX,0xf,SCB_TCL;	/* lun */
+	} else {
+		and	SINDEX,0x7,SCB_TCL;	/* lun */
+	}
 	and	A,DISCENB,SCB_CONTROL;	/* mask off disconnect privledge */
 	or	SINDEX,A;		/* or in disconnect privledge */
 	or	SINDEX,MSG_IDENTIFYFLAG;
@@ -606,6 +734,7 @@
 p_mesgout_from_host:
 	cmp	SINDEX, HOST_MSG	jne p_mesgout_onebyte;
 	mvi     INTSTAT,AWAITING_MSG;
+	nop;
 	/*
 	 * Did the host detect a phase change?
 	 */
@@ -692,6 +821,7 @@
 check_status:
 	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Just a residual? */
 	mvi	INTSTAT,BAD_STATUS;			/* let driver know */
+	nop;
 	cmp	RETURN_1, SEND_SENSE	jne complete;
 	/* This SCB becomes the next to execute as it will retrieve sense */
 	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
@@ -710,21 +840,21 @@
 	/* If we are untagged, clear our address up in host ram */
 	test	SCB_CONTROL, TAG_ENB jnz complete_post;
 	mov	A, SAVED_TCL;
-	mvi	UNTAGGEDSCB_OFFSET call set_SCBID_host_addr_and_cnt;
-	mvi	DFCNTRL, FIFORESET;
-	mvi	DFDAT, SCB_LIST_NULL;
-	or	DFCNTRL, HDMAEN|FIFOFLUSH;
-	call	dma_finish;
+	mvi	UNTAGGEDSCB_OFFSET call post_byte_setup;
+	mvi	SCB_LIST_NULL call post_byte;
 
 complete_post:
 	/* Post the SCB and issue an interrupt */
-	mov	A, QOUTPOS;
-	mvi	QOUTFIFO_OFFSET call set_SCBID_host_addr_and_cnt;
-	mvi	DFCNTRL, FIFORESET;
-	mov	DFDAT, SCB_TAG;
-	or	DFCNTRL, HDMAEN|FIFOFLUSH;
-	call	dma_finish;
-	inc 	QOUTPOS;
+	if ((p->features & AHC_QUEUE_REGS) != 0) {
+		mov	A, SDSCB_QOFF;
+	} else {
+		mov	A, QOUTPOS;
+	}
+	mvi	QOUTFIFO_OFFSET call post_byte_setup;
+	mov	SCB_TAG call post_byte;
+	if ((p->features & AHC_QUEUE_REGS) == 0) {
+		inc 	QOUTPOS;
+	}
 	mvi	INTSTAT,CMDCMPLT;
 
 add_to_free_list:
@@ -796,18 +926,19 @@
  * clearing the "disconnected" bit so we don't "find" it by accident later.
  */
 mesgin_identify:
-.if ( WIDE )
-	and	A,0x0f;			/* lun in lower four bits */
-.else
-	and	A,0x07;			/* lun in lower three bits */
-.endif
+	
+	if ((p->features & AHC_WIDE) != 0) {
+		and	A,0x0f;		/* lun in lower four bits */
+	} else {
+		and	A,0x07;		/* lun in lower three bits */
+	}
 	or      SAVED_TCL,A;		/* SAVED_TCL should be complete now */
 
 	call	get_untagged_SCBID;
 	cmp	ARG_1, SCB_LIST_NULL	je snoop_tag;
-.if ( SCB_PAGING )
-	test	SEQ_FLAGS, SCBPTR_VALID	jz use_retrieveSCB;
-.endif
+	if ((p->flags & AHC_PAGESCBS) != 0) {
+		test	SEQ_FLAGS, SCBPTR_VALID	jz use_retrieveSCB;
+	}
 	/*
 	 * If the SCB was found in the disconnected list (as is
 	 * always the case in non-paging scenarios), SCBPTR is already
@@ -833,19 +964,19 @@
 get_tag:
 	mvi	ARG_1	call inb_next;	/* tag value */
 
-.if ! ( SCB_PAGING )
+	if ((p->flags & AHC_PAGESCBS) == 0) {
 index_by_tag:
-	mov	SCBPTR,ARG_1;
-	test	SCB_CONTROL,TAG_ENB	jz  not_found;
-	mov	SCBPTR	call rem_scb_from_disc_list;
-.else
-/*
- * Ensure that the SCB the tag points to is for an SCB transaction
- * to the reconnecting target.
- */
+		mov	SCBPTR,ARG_1;
+		test	SCB_CONTROL,TAG_ENB	jz  not_found;
+		mov	SCBPTR	call rem_scb_from_disc_list;
+	} else {
+		/*
+		 * Ensure that the SCB the tag points to is for
+		 * an SCB transaction to the reconnecting target.
+		 */
 use_retrieveSCB:
-	call	retrieveSCB;
-.endif
+		call	retrieveSCB;
+	}
 setup_SCB:
 	mov	A, SAVED_TCL;
 	cmp	SCB_TCL, A	jne not_found_cleanup_scb;
@@ -924,16 +1055,16 @@
 inb_last:
 	mov	NONE,SCSIDATL ret;		/*dummy read from latch to ACK*/
 
-.if ( TARGET_MODE )
-/*
- * Send a byte to an initiator in Automatic PIO mode.
- * SPIOEN must be on prior to calling this routine.
- */
+if ((p->flags & AHC_TARGETMODE) != 0) {
+	/*
+	 * Send a byte to an initiator in Automatic PIO mode.
+	 * SPIOEN must be on prior to calling this routine.
+	 */
 target_outb:
 	mov	SCSIDATL, SINDEX;
 	test	SSTAT0, SPIORDY	jz .;
 	ret;
-.endif
+}
 	
 mesgin_phasemis:
 /*
@@ -980,7 +1111,17 @@
 dma_dmadone:
 	and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
 dma_halt:
-	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN)	jnz dma_halt; 
+	/*
+	 * Some revisions of the aic7880 have a problem where, if the
+	 * data fifo is full, but the PCI input latch is not empty, 
+	 * HDMAEN cannot be cleared.  The fix used here is to attempt
+	 * to drain the data fifo until there is space for the input
+	 * latch to drain and HDMAEN de-asserts.
+	 */
+	if ((p->features & AHC_ULTRA2) == 0) {
+		mov	NONE, DFDAT;
+	}
+	test	DFCNTRL, HDMAEN jnz dma_halt;
 return:
 	ret;
 
@@ -1079,18 +1220,67 @@
 	mov	ARG_1, SCB_TAG	ret;
 	mvi	ARG_1, SCB_LIST_NULL ret;
 
-set_SCBID_host_addr_and_cnt:
-	mov	DINDEX, SINDEX;
-	mvi	SCBID_ADDR call	set_1byte_haddr_and_clrcnt;
-	mvi	HCNT[0], 1 ret;
+/*
+ * Fetch a byte from host memory given an index of (A + (256 * SINDEX))
+ * and a base address of SCBID_ADDR.  The byte is returned in RETURN_2.
+ */
+fetch_byte:
+	mov	ARG_2, SINDEX;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	DINDEX, CCHADDR;
+		mvi	SCBID_ADDR call set_1byte_addr;
+		mvi	CCHCNT, 1;
+		mvi	CCSGCTL, CCSGEN|CCSGRESET;
+		test	CCSGCTL, CCSGDONE jz .;
+		mvi	CCSGCTL, CCSGRESET;
+		bmov	RETURN_2, CCSGRAM, 1 ret;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCBID_ADDR call set_1byte_addr;
+		mvi	HCNT[0], 1;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+		call	dma_finish;
+		mov	RETURN_2, DFDAT ret;
+	}
+
+/*
+ * Prepare the hardware to post a byte to host memory given an
+ * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR.
+ */
+post_byte_setup:
+	mov	ARG_2, SINDEX;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	DINDEX, CCHADDR;
+		mvi	SCBID_ADDR call	set_1byte_addr;
+		mvi	CCHCNT, 1;
+		mvi	CCSCBCTL, CCSCBRESET ret;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCBID_ADDR call	set_1byte_addr;
+		mvi	HCNT[0], 1;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		mvi	DFCNTRL, FIFORESET ret;
+	}
+
+post_byte:
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov	CCSCBRAM, SINDEX, 1;
+		or	CCSCBCTL, CCSCBEN|CCSCBRESET;
+		test	CCSCBCTL, CCSCBDONE jz .;
+		clr	CCSCBCTL ret;
+	} else {
+		mov	DFDAT, SINDEX;
+		or	DFCNTRL, HDMAEN|FIFOFLUSH;
+		jmp	dma_finish;
+	}
 
 get_SCBID_from_host:
 	mov	A, SAVED_TCL;
-	mvi	UNTAGGEDSCB_OFFSET call set_SCBID_host_addr_and_cnt;
-	mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
-
-	call	dma_finish;
-	mov	ARG_1, DFDAT ret;
+	mvi	UNTAGGEDSCB_OFFSET call fetch_byte;
+	mov	RETURN_1,  RETURN_2 ret;
 
 phase_lock:     
 	test	SSTAT1, REQINIT jz phase_lock;
@@ -1116,65 +1306,102 @@
 	mov	DINDIR, SINDIR ret;
 
 /*
- * Setup haddr and count assuming that A is an
- * index into an array of 32byte objects.
+ * Setup addr assuming that A is an index into
+ * an array of 32byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
  */
-set_32byte_haddr_and_clrcnt:
-	shr	DINDEX, 3, A;
+set_32byte_addr:
+	shr	ARG_2, 3, A;
 	shl	A, 5;
-set_1byte_haddr_and_clrcnt:	/* DINDEX must be 0 upon call */
-	add	HADDR[0], A, SINDIR;
-	mov	A, DINDEX;
-	adc	HADDR[1], A, SINDIR;
+/*
+ * Setup addr assuming that A + (ARG_1 * 256) is an
+ * index into an array of 1byte objects, SINDEX contains
+ * the base address of that array, and DINDEX contains
+ * the base address of the location to store the computed
+ * address.
+ */
+set_1byte_addr:
+	add	DINDIR, A, SINDIR;
+	mov	A, ARG_2;
+	adc	DINDIR, A, SINDIR;
 	clr	A;
-	adc	HADDR[2], A, SINDIR;
-	adc	HADDR[3], A, SINDIR;
-	/* Clear Count */
-	clr	HCNT[1];
-	clr	HCNT[2] ret;
+	adc	DINDIR, A, SINDIR;
+	adc	DINDIR, A, SINDIR ret;
 
+/*
+ * Either post or fetch and SCB from host memory based on the
+ * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
+ */
 dma_scb:
-	/*
-	 * SCB index is in SINDEX.  Determine the physical address in
-	 * the host where this SCB is located and load HADDR with it.
-	 */
 	mov	A, SINDEX;
-	mvi	HSCB_ADDR call set_32byte_haddr_and_clrcnt;
-	mvi	HCNT[0], 28;
-	mov	DFCNTRL, DMAPARAMS;
-	test	DMAPARAMS, DIRECTION	jnz dma_scb_fromhost;
-	/* Fill it with the SCB data */
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	DINDEX, CCHADDR;
+		mvi	HSCB_ADDR call set_32byte_addr;
+		mov	CCSCBPTR, SCBPTR;
+		mvi	CCHCNT, 32;
+		test	DMAPARAMS, DIRECTION jz dma_scb_tohost;
+		mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
+		cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
+		jmp	dma_scb_finish;
+dma_scb_tohost:
+		if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+			mvi	CCSCBCTL, CCSCBRESET;
+			bmov	CCSCBRAM, SCB_CONTROL, 32;
+			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
+			test	CCSCBCTL, CCSCBDONE jz .;
+		} else {
+			mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
+			cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
+		}
+dma_scb_finish:
+		clr	CCSCBCTL;
+		test	CCSCBCTL, CCARREN|CCSCBEN jnz .;
+		ret;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	HSCB_ADDR call set_32byte_addr;
+		mvi	HCNT[0], 32;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		mov	DFCNTRL, DMAPARAMS;
+		test	DMAPARAMS, DIRECTION	jnz dma_scb_fromhost;
+		/* Fill it with the SCB data */
 copy_scb_tofifo:
-	mvi	SINDEX, SCB_CONTROL;
-	add	A, 28, SINDEX;
+		mvi	SINDEX, SCB_CONTROL;
+		add	A, 32, SINDEX;
 copy_scb_tofifo_loop:
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	cmp	SINDEX, A jne copy_scb_tofifo_loop;
-	or	DFCNTRL, HDMAEN|FIFOFLUSH;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		cmp	SINDEX, A jne copy_scb_tofifo_loop;
+		or	DFCNTRL, HDMAEN|FIFOFLUSH;
 dma_scb_fromhost:
-	call	dma_finish;
-	/* If we were putting the SCB, we are done */
-	test	DMAPARAMS, DIRECTION	jz	return;
-	mvi	SCB_CONTROL  call dfdat_in_7;
-	call	dfdat_in_7_continued;
-	call	dfdat_in_7_continued;
-	jmp	dfdat_in_7_continued;
+		call	dma_finish;
+		/* If we were putting the SCB, we are done */
+		test	DMAPARAMS, DIRECTION	jz	return;
+		mvi	SCB_CONTROL  call dfdat_in_7;
+		call	dfdat_in_7_continued;
+		call	dfdat_in_7_continued;
+		jmp	dfdat_in_7_continued;
 dfdat_in_7:
-	mov     DINDEX,SINDEX;
+		mov     DINDEX,SINDEX;
 dfdat_in_7_continued:
-	mov	DINDIR,DFDAT;
-	mov	DINDIR,DFDAT;
-	mov	DINDIR,DFDAT;
-	mov	DINDIR,DFDAT;
-	mov	DINDIR,DFDAT;
-	mov	DINDIR,DFDAT;
-	mov	DINDIR,DFDAT ret;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT;
+		mov	DINDIR,DFDAT ret;
+	}
+
 
 /*
  * Wait for DMA from host memory to data FIFO to complete, then disable
@@ -1188,13 +1415,13 @@
 	ret;
 
 add_scb_to_free_list:
-.if ( SCB_PAGING )
-	mov	SCB_NEXT, FREE_SCBH;
-	mov	FREE_SCBH, SCBPTR;
-.endif
+	if ((p->flags & AHC_PAGESCBS) != 0) {
+		mov	SCB_NEXT, FREE_SCBH;
+		mov	FREE_SCBH, SCBPTR;
+	}
 	mvi	SCB_TAG, SCB_LIST_NULL ret;
 
-.if ( SCB_PAGING )
+if ((p->flags & AHC_PAGESCBS) != 0) {
 get_free_or_disc_scb:
 	cmp	FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
 	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
@@ -1211,7 +1438,7 @@
 dequeue_free_scb:
 	mov	SCBPTR, FREE_SCBH;
 	mov	FREE_SCBH, SCB_NEXT ret;
-.endif
+}
 
 add_scb_to_disc_list:
 /*
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx/bsd_q.h linux/drivers/scsi/aic7xxx/bsd_q.h
--- v2.1.124/linux/drivers/scsi/aic7xxx/bsd_q.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/bsd_q.h	Thu Oct  8 08:07:33 1998
@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  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, this list of conditions and the following disclaimer.
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS 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.
+ *
+ *	@(#)queue.h	8.5 (Berkeley) 8/20/94
+ * $Id: queue.h,v 1.21 1998/05/12 03:55:25 gibbs Exp $
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define	_SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * slingly-linked tail queues, lists, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may only be traversed in the forward direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ *			SLIST	LIST	STAILQ	TAILQ	CIRCLEQ
+ * _HEAD		+	+	+	+	+
+ * _ENTRY		+	+	+	+	+
+ * _INIT		+	+	+	+	+
+ * _EMPTY		+	+	+	+	+
+ * _FIRST		+	+	-	+	+
+ * _NEXT		+	+	-	+	+
+ * _PREV		-	-	-	+	+
+ * _LAST		-	-	-	+	+
+ * _FOREACH		+	+	-	+	-
+ * _INSERT_HEAD		+	+	+	+	+
+ * _INSERT_BEFORE	-	+	-	+	+
+ * _INSERT_AFTER	+	+	+	+	+
+ * _INSERT_TAIL		-	-	+	+	+
+ * _REMOVE_HEAD		+	-	+	-	-
+ * _REMOVE		+	+	+	+	+
+ *
+ */
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type)						\
+struct name {								\
+	struct type *slh_first;	/* first element */			\
+}
+ 
+#define SLIST_ENTRY(type)						\
+struct {								\
+	struct type *sle_next;	/* next element */			\
+}
+ 
+/*
+ * Singly-linked List functions.
+ */
+#define	SLIST_EMPTY(head)	((head)->slh_first == NULL)
+
+#define	SLIST_FIRST(head)	((head)->slh_first)
+
+#define SLIST_FOREACH(var, head, field)					\
+	for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
+
+#define SLIST_INIT(head) {						\
+	(head)->slh_first = NULL;					\
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do  {			\
+	(elm)->field.sle_next = (slistelm)->field.sle_next;		\
+	(slistelm)->field.sle_next = (elm);				\
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.sle_next = (head)->slh_first;			\
+	(head)->slh_first = (elm);					\
+} while (0)
+
+#define SLIST_NEXT(elm, field)	((elm)->field.sle_next)
+
+#define SLIST_REMOVE_HEAD(head, field) do {				\
+	(head)->slh_first = (head)->slh_first->field.sle_next;		\
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do {			\
+	if ((head)->slh_first == (elm)) {				\
+		SLIST_REMOVE_HEAD((head), field);			\
+	}								\
+	else {								\
+		struct type *curelm = (head)->slh_first;		\
+		while( curelm->field.sle_next != (elm) )		\
+			curelm = curelm->field.sle_next;		\
+		curelm->field.sle_next =				\
+		    curelm->field.sle_next->field.sle_next;		\
+	}								\
+} while (0)
+
+/*
+ * Singly-linked Tail queue definitions.
+ */
+#define STAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *stqh_first;/* first element */			\
+	struct type **stqh_last;/* addr of last next element */		\
+}
+
+#define STAILQ_ENTRY(type)						\
+struct {								\
+	struct type *stqe_next;	/* next element */			\
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define	STAILQ_INIT(head) do {						\
+	(head)->stqh_first = NULL;					\
+	(head)->stqh_last = &(head)->stqh_first;			\
+} while (0)
+
+#define STAILQ_FIRST(head)	((head)->stqh_first)
+#define STAILQ_LAST(head)	(*(head)->stqh_last)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.stqe_next = (head)->stqh_first) == NULL)	\
+		(head)->stqh_last = &(elm)->field.stqe_next;		\
+	(head)->stqh_first = (elm);					\
+} while (0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.stqe_next = NULL;					\
+	*(head)->stqh_last = (elm);					\
+	(head)->stqh_last = &(elm)->field.stqe_next;			\
+} while (0)
+
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do {		\
+	if (((elm)->field.stqe_next = (tqelm)->field.stqe_next) == NULL)\
+		(head)->stqh_last = &(elm)->field.stqe_next;		\
+	(tqelm)->field.stqe_next = (elm);				\
+} while (0)
+
+#define STAILQ_NEXT(elm, field)	((elm)->field.stqe_next)
+
+#define STAILQ_REMOVE_HEAD(head, field) do {				\
+	if (((head)->stqh_first =					\
+	     (head)->stqh_first->field.stqe_next) == NULL)		\
+		(head)->stqh_last = &(head)->stqh_first;		\
+} while (0)
+
+#define STAILQ_REMOVE(head, elm, type, field) do {			\
+	if ((head)->stqh_first == (elm)) {				\
+		STAILQ_REMOVE_HEAD(head, field);			\
+	}								\
+	else {								\
+		struct type *curelm = (head)->stqh_first;		\
+		while( curelm->field.stqe_next != (elm) )		\
+			curelm = curelm->field.stqe_next;		\
+		if((curelm->field.stqe_next =				\
+		    curelm->field.stqe_next->field.stqe_next) == NULL)	\
+			(head)->stqh_last = &(curelm)->field.stqe_next;	\
+	}								\
+} while (0)
+
+/*
+ * List definitions.
+ */
+
+#define LIST_ENTRY(type)						\
+struct {								\
+	struct type *le_next;	/* next element */			\
+	struct type **le_prev;	/* address of previous next element */	\
+}
+
+/*
+ * List functions.
+ */
+
+#define	LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#define LIST_FIRST(head)	((head)->lh_first)
+
+#define LIST_FOREACH(var, head, field)					\
+	for((var) = (head)->lh_first; (var); (var) = (var)->field.le_next)
+
+#define	LIST_INIT(head) do {						\
+	(head)->lh_first = NULL;					\
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do {			\
+	if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)	\
+		(listelm)->field.le_next->field.le_prev =		\
+		    &(elm)->field.le_next;				\
+	(listelm)->field.le_next = (elm);				\
+	(elm)->field.le_prev = &(listelm)->field.le_next;		\
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.le_prev = (listelm)->field.le_prev;		\
+	(elm)->field.le_next = (listelm);				\
+	*(listelm)->field.le_prev = (elm);				\
+	(listelm)->field.le_prev = &(elm)->field.le_next;		\
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do {				\
+	if (((elm)->field.le_next = (head)->lh_first) != NULL)		\
+		(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+	(head)->lh_first = (elm);					\
+	(elm)->field.le_prev = &(head)->lh_first;			\
+} while (0)
+
+#define LIST_NEXT(elm, field)	((elm)->field.le_next)
+
+#define LIST_REMOVE(elm, field) do {					\
+	if ((elm)->field.le_next != NULL)				\
+		(elm)->field.le_next->field.le_prev = 			\
+		    (elm)->field.le_prev;				\
+	*(elm)->field.le_prev = (elm)->field.le_next;			\
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *tqh_first;	/* first element */			\
+	struct type **tqh_last;	/* addr of last next element */		\
+}
+
+#define TAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+}
+
+/*
+ * Tail queue functions.
+ */
+#define	TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FOREACH(var, head, field)					\
+	for (var = TAILQ_FIRST(head); var; var = TAILQ_NEXT(var, field))
+
+#define	TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define	TAILQ_LAST(head, headname) \
+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define	TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_PREV(elm, headname, field) \
+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define	TAILQ_INIT(head) do {						\
+	(head)->tqh_first = NULL;					\
+	(head)->tqh_last = &(head)->tqh_first;				\
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
+		(head)->tqh_first->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(head)->tqh_first = (elm);					\
+	(elm)->field.tqe_prev = &(head)->tqh_first;			\
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.tqe_next = NULL;					\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &(elm)->field.tqe_next;			\
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+		(elm)->field.tqe_next->field.tqe_prev = 		\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(listelm)->field.tqe_next = (elm);				\
+	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
+	(elm)->field.tqe_next = (listelm);				\
+	*(listelm)->field.tqe_prev = (elm);				\
+	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do {				\
+	if (((elm)->field.tqe_next) != NULL)				\
+		(elm)->field.tqe_next->field.tqe_prev = 		\
+		    (elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *cqh_first;		/* first element */		\
+	struct type *cqh_last;		/* last element */		\
+}
+
+#define CIRCLEQ_ENTRY(type)						\
+struct {								\
+	struct type *cqe_next;		/* next element */		\
+	struct type *cqe_prev;		/* previous element */		\
+}
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
+
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+
+#define CIRCLEQ_FOREACH(var, head, field)				\
+	for((var) = (head)->cqh_first;					\
+	    (var) != (void *)(head);					\
+	    (var) = (var)->field.cqe_next)
+
+#define	CIRCLEQ_INIT(head) do {						\
+	(head)->cqh_first = (void *)(head);				\
+	(head)->cqh_last = (void *)(head);				\
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	(elm)->field.cqe_next = (listelm)->field.cqe_next;		\
+	(elm)->field.cqe_prev = (listelm);				\
+	if ((listelm)->field.cqe_next == (void *)(head))		\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(listelm)->field.cqe_next->field.cqe_prev = (elm);	\
+	(listelm)->field.cqe_next = (elm);				\
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {		\
+	(elm)->field.cqe_next = (listelm);				\
+	(elm)->field.cqe_prev = (listelm)->field.cqe_prev;		\
+	if ((listelm)->field.cqe_prev == (void *)(head))		\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(listelm)->field.cqe_prev->field.cqe_next = (elm);	\
+	(listelm)->field.cqe_prev = (elm);				\
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.cqe_next = (head)->cqh_first;			\
+	(elm)->field.cqe_prev = (void *)(head);				\
+	if ((head)->cqh_last == (void *)(head))				\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(head)->cqh_first->field.cqe_prev = (elm);		\
+	(head)->cqh_first = (elm);					\
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.cqe_next = (void *)(head);				\
+	(elm)->field.cqe_prev = (head)->cqh_last;			\
+	if ((head)->cqh_first == (void *)(head))			\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(head)->cqh_last->field.cqe_next = (elm);		\
+	(head)->cqh_last = (elm);					\
+} while (0)
+
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+
+#define CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)
+
+#define CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev)
+
+#define	CIRCLEQ_REMOVE(head, elm, field) do {				\
+	if ((elm)->field.cqe_next == (void *)(head))			\
+		(head)->cqh_last = (elm)->field.cqe_prev;		\
+	else								\
+		(elm)->field.cqe_next->field.cqe_prev =			\
+		    (elm)->field.cqe_prev;				\
+	if ((elm)->field.cqe_prev == (void *)(head))			\
+		(head)->cqh_first = (elm)->field.cqe_next;		\
+	else								\
+		(elm)->field.cqe_prev->field.cqe_next =			\
+		    (elm)->field.cqe_next;				\
+} while (0)
+
+#ifdef KERNEL
+
+/*
+ * XXX insque() and remque() are an old way of handling certain queues.
+ * They bogusly assumes that all queue heads look alike.
+ */
+
+struct quehead {
+	struct quehead *qh_link;
+	struct quehead *qh_rlink;
+};
+
+#ifdef	__GNUC__
+
+static __inline void
+insque(void *a, void *b)
+{
+	struct quehead *element = a, *head = b;
+
+	element->qh_link = head->qh_link;
+	element->qh_rlink = head;
+	head->qh_link = element;
+	element->qh_link->qh_rlink = element;
+}
+
+static __inline void
+remque(void *a)
+{
+	struct quehead *element = a;
+
+	element->qh_link->qh_rlink = element->qh_rlink;
+	element->qh_rlink->qh_link = element->qh_link;
+	element->qh_rlink = 0;
+}
+
+#else /* !__GNUC__ */
+
+void	insque __P((void *a, void *b));
+void	remque __P((void *a));
+
+#endif /* __GNUC__ */
+
+#endif /* KERNEL */
+
+#endif /* !_SYS_QUEUE_H_ */
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx/sequencer.h linux/drivers/scsi/aic7xxx/sequencer.h
--- v2.1.124/linux/drivers/scsi/aic7xxx/sequencer.h	Tue Apr 14 14:29:21 1998
+++ linux/drivers/scsi/aic7xxx/sequencer.h	Thu Oct  8 08:07:33 1998
@@ -2,7 +2,7 @@
  * Instruction formats for the sequencer program downloaded to
  * Aic7xxx SCSI host adapters
  *
- * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 1997, 1998 Justin T. Gibbs.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -37,42 +37,44 @@
  */
 
 struct ins_format1 {
-	unsigned char immediate;
-	unsigned char source;
-	unsigned char destination;
-	unsigned char opcode_ret;
-#define DOWNLOAD_CONST_IMMEDIATE 0x80
+	unsigned int	immediate	: 8,
+			source		: 9,
+			destination	: 9,
+			ret		: 1,
+			opcode		: 4,
+			parity		: 1;
 };
 
 struct ins_format2 {
-	unsigned char shift_control;
-	unsigned char source;
-	unsigned char destination;
-	unsigned char opcode_ret;
-#define RETURN_BIT 0x01
+	unsigned int	shift_control	: 8,
+			source		: 9,
+			destination	: 9,
+			ret		: 1,
+			opcode		: 4,
+			parity		: 1;
 };
 
 struct ins_format3 {
-	unsigned char immediate;
-	unsigned char source;
-	unsigned char address;
-	unsigned char opcode_addr;
-#define ADDR_HIGH_BIT 0x01
+	unsigned int	immediate	: 8,
+			source		: 9,
+			address		: 10,
+			opcode		: 4,
+			parity		: 1;
 };
 
-#ifndef __KERNEL__
-struct instruction {
-	union {
+union ins_formats {
 		struct ins_format1 format1;
 		struct ins_format2 format2;
 		struct ins_format3 format3;
 		unsigned char	   bytes[4];
-	} format;
-	u_int	srcline;
+		unsigned int	   integer;
+};
+struct instruction {
+	union	ins_formats format;
+	unsigned int	srcline;
 	struct symbol *patch_label;
 	STAILQ_ENTRY(instruction) links;
 };
-#endif
 
 #define	AIC_OP_OR	0x0
 #define	AIC_OP_AND	0x1
@@ -80,6 +82,7 @@
 #define	AIC_OP_ADD	0x3
 #define	AIC_OP_ADC	0x4
 #define	AIC_OP_ROL	0x5
+#define	AIC_OP_BMOV	0x6
 
 #define	AIC_OP_JMP	0x8
 #define AIC_OP_JC	0x9
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.1.124/linux/drivers/scsi/aic7xxx.c	Mon Sep 28 10:51:34 1998
+++ linux/drivers/scsi/aic7xxx.c	Thu Oct  8 08:07:34 1998
@@ -98,7 +98,7 @@
 
 /*+M**************************************************************************
  *
- * Further driver modifications made by Doug Ledford <dledford@dialnet.net>
+ * Further driver modifications made by Doug Ledford <dledford@redhat.com>
  *
  * Copyright (c) 1997-1998 Doug Ledford
  *
@@ -163,6 +163,147 @@
  *
  *_M*************************************************************************/
 
+/*
+ * The next three defines are user configurable.  These should be the only
+ * defines a user might need to get in here and change.  There are other
+ * defines buried deeper in the code, but those really shouldn't need touched
+ * under normal conditions.
+ */
+
+/*
+ * AIC7XXX_FAKE_NEGOTIATION_CMDS
+ *   We now have two distinctly different methods of device negotiation
+ *   in this code.  The two methods are selected by either defining or not
+ *   defining this option.  The difference is as follows:
+ *
+ *   With AIC7XXX_FAKE_NEGOTIATION_CMDS not set (commented out)
+ *     When the driver is in need of issuing a negotiation command for any
+ *     given device, it will add the negotiation message on to part of a
+ *     regular SCSI command for the device.  In the process, if the device
+ *     is configured for and using tagged queueing, then the code will
+ *     also issue that single command as a non-tagged command, attach the
+ *     negotiation message to that one command, and use a temporary
+ *     queue depth of one to keep the untagged and tagged commands from
+ *     overlapping.
+ *       Pros: This doesn't use any extra SCB structures, it's simple, it
+ *         works most of the time (if not all of the time now), and
+ *         since we get the device capability info frmo the INQUIRY data
+ *         now, shouldn't cause any problems.
+ *       Cons: When we need to send a negotiation command to a device, we
+ *         must use a command that is being sent to LUN 0 of the device.
+ *         If we try sending one to high LUN numbers, then some devices
+ *         get noticeably upset.  Since we have to wait for a command with
+ *         LUN == 0 to come along, we may not be able to renegotiate when
+ *         we want if the user is actually using say LUN 1 of a CD Changer
+ *         instead of using LUN 0 for an extended period of time.
+ *
+ *   With AIC7XXX_FAKE_NEGOTIATION_CMDS defined
+ *     When we need to negotiate with a device, instead of attaching our
+ *     negotiation message to an existing command, we insert our own
+ *     fictional Scsi_Cmnd into the chain that has the negotiation message
+ *     attached to it.  We send this one command as untagged regardless
+ *     of the device type, and we fiddle with the queue depth the same as
+ *     we would with the option unset to avoid overlapping commands.  The
+ *     primary difference between this and the unset option is that the
+ *     negotiation message is no longer attached to a specific command,
+ *     instead it is its own command and is merely triggered by a
+ *     combination of both A) We need to negotiate and B) The mid level
+ *     SCSI code has sent us a command.  We still don't do any negotiation
+ *     unless there is a valid SCSI command to be processed.
+ *       Pros: This fixes the problem above in the Cons section.  Since we
+ *         issue our own fake command, we can set the LUN to 0 regardless
+ *         of what the LUN is in the real command.  It also means that if
+ *         the device get's nasty over negotiation issues, it won't be
+ *         showing up on a regular command, so we won't get any SENSE buffer
+ *         data or STATUS_BYTE returns to the mid level code that are caused
+ *         by snits in the negotiation code.
+ *       Cons: We add more code, and more complexity.  This means more ways
+ *         in which things could break.  It means a larger driver.  It means
+ *         more resource consumption for the fake commands.  However, the
+ *         biggest problem is this.  Take a system where there is a CD-ROM
+ *         on the SCSI bus.  Someone has a CD in the CD-ROM and is using it.
+ *         For some reason the SCSI bus gets reset.  We don't touch the
+ *         CD-ROM again for quite a period of time (so we don't renegotiate
+ *         after the reset until we do touch the CD-ROM again).  In the
+ *         time while we aren't using the CD-ROM, the current disc is
+ *         removed and a new one put in.  When we go to check that disc, we
+ *         will first have to renegotiate.  In so doing, we issue our fake
+ *         SCSI command, which happens to be TEST_UNIT_READY.  The CD-ROM
+ *         negotiates with us, then responds to our fake command with a
+ *         CHECK_CONDITION status.  We REQUEST_SENSE from the CD-ROM, it
+ *         then sends the SENSE data to our fake command to tell it that
+ *         it has been through a disc change.  There, now we've cleared out
+ *         the SENSE data along with our negotiation command, and when the
+ *         real command executes, it won't pick up that the CD was changed.
+ *         That's the biggest Con to this approach.  In the future, I could
+ *         probably code around this problem though, so this option is still
+ *         viable.
+ *
+ *  So, which command style should you use?  I would appreciate it if people
+ *  could try out both types.  I want to know about any cases where one
+ *  method works and the other doesn't.  If one method works on significantly
+ *  more systems than another, then it will become the default.  If the second
+ *  option turns out to work best, then I'll find a way to work around that
+ *  big con I listed.
+ *
+ *  -- July 7, 02:33
+ *    OK...I just added some code that should make the Con listed for the
+ *    fake commands a non issue now.  However, it needs testing.  For now,
+ *    I'm going to make the default to use the fake commands, we'll see how
+ *    it goes.
+ */
+ 
+#define AIC7XXX_FAKE_NEGOTIATION_CMDS
+
+/*
+ * AIC7XXX_STRICT_PCI_SETUP
+ *   Should we assume the PCI config options on our controllers are set with
+ *   sane and proper values, or should we be anal about our PCI config
+ *   registers and force them to what we want?  The main advantage to
+ *   defining this option is on non-Intel hardware where the BIOS may not
+ *   have been run to set things up, or if you have one of the BIOSless
+ *   Adaptec controllers, such as a 2910, that don't get set up by the
+ *   BIOS.  However, keep in mind that we really do set the most important
+ *   items in the driver regardless of this setting, this only controls some
+ *   of the more esoteric PCI options on these cards.  In that sense, I
+ *   would default to leaving this off.  However, if people wish to try
+ *   things both ways, that would also help me to know if there are some
+ *   machines where it works one way but not another.
+ *
+ *   -- July 7, 17:09
+ *     OK...I need this on my machine for testing, so the default is to
+ *     leave it defined.
+ *
+ *   -- July 7, 18:49
+ *     I needed it for testing, but it didn't make any difference, so back
+ *     off she goes.
+ *
+ *   -- July 16, 23:04
+ *     I turned it back on to try and compensate for the 2.1.x PCI code
+ *     which no longer relies solely on the BIOS and now tries to set
+ *     things itself.
+ */
+
+#define AIC7XXX_STRICT_PCI_SETUP
+
+/*
+ * AIC7XXX_VERBOSE_DEBUGGING
+ *   This option enables a lot of extra printk();s in the code, surrounded
+ *   by if (aic7xxx_verbose ...) statements.  Executing all of those if
+ *   statements and the extra checks can get to where it actually does have
+ *   an impact on CPU usage and such, as well as code size.  Disabling this
+ *   define will keep some of those from becoming part of the code.
+ *
+ *   NOTE:  Currently, this option has no real effect, I will be adding the
+ *   various #ifdef's in the code later when I've decided a section is
+ *   complete and no longer needs debugging.  OK...a lot of things are now
+ *   surrounded by this define, so turning this off does have an impact.
+ */
+ 
+/*
+ * #define AIC7XXX_VERBOSE_DEBUGGING
+ */
+ 
 #ifdef MODULE
 #include <linux/module.h>
 #endif
@@ -187,10 +328,10 @@
 #include "hosts.h"
 #include "aic7xxx.h"
 
+#include "aic7xxx/bsd_q.h"
 #include "aic7xxx/sequencer.h"
 #include "aic7xxx/scsi_message.h"
 #include "aic7xxx_reg.h"
-#include "aic7xxx_seq.h"
 
 #include <linux/stat.h>
 #include <linux/malloc.h>        /* for kmalloc() */
@@ -209,7 +350,7 @@
     0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "5.0.20"
+#define AIC7XXX_C_VERSION  "5.1.0"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
@@ -242,17 +383,15 @@
 #  include <linux/bios32.h>
 #endif
 
-#if defined(__powerpc__) || defined(__i386__)
-#  define MMAPIO
-#endif
-
 #if defined(__powerpc__)
+#  define MMAPIO
 #  ifdef mb
 #    undef mb
 #  endif
 #  define mb() \
      __asm__ __volatile__("eieio" ::: "memory")
 #elif defined(__i386__)
+#  define MMAPIO
 #  ifdef mb
 #    undef mb
 #  endif
@@ -330,7 +469,7 @@
  *       tagged queue value array is always active now.  I've added
  *       a setup option to set this particular array and I'm hoping
  *       insmod will be smart enough to set it properly as well.  It's
- *       by use of this array that a person can disable tagged queueing.
+ *       by use of this array that a person can enable tagged queueing.
  *       The DEFAULT_TAG_COMMANDS define has been changed to disable
  *       tagged queueing by default, so if your devices can handle tagged
  *       queueing you will need to add a line to their lilo.conf file like:
@@ -396,6 +535,9 @@
  */
 
 /*
+ * NOTE: The below structure is for reference only, the actual structure
+ *       to modify in order to change things is located around line
+ *       number 1305
 adapter_tag_info_t aic7xxx_tag_info[] =
 {
   {DEFAULT_TAG_COMMANDS},
@@ -429,7 +571,11 @@
   "Adaptec AHA-394X Ultra SCSI host adapter",           /* AIC_7882 */
   "Adaptec AHA-398X Ultra SCSI host adapter",           /* AIC_7883 */
   "Adaptec AHA-2944 Ultra SCSI host adapter",           /* AIC_7884 */
-  "Adaptec AIC-7895 Ultra SCSI host adapter"            /* AIC_7895 */
+  "Adaptec AIC-7895 Ultra SCSI host adapter",           /* AIC_7895 */
+  "Adaptec AIC-7890/1 Ultra2 SCSI host adapter",        /* AIC_7890 */
+  "Adaptec AHA-294X Ultra2 SCSI host adapter",          /* AIC_7890 */
+  "Adaptec AIC-7896/7 Ultra2 SCSI host adapter",        /* AIC_7896 */
+  "Adaptec AHA-394X Ultra2 SCSI host adapter"           /* AIC_7897 */
 };
 
 /*
@@ -467,10 +613,10 @@
 /*
  * Standard EISA Host ID regs  (Offset from slot base)
  */
-#define HID0                0x80   /* 0,1: msb of ID2, 2-7: ID1      */
-#define HID1                0x81   /* 0-4: ID3, 5-7: LSB ID2         */
-#define HID2                0x82   /* product                        */
-#define HID3                0x83   /* firmware revision              */
+#define AHC_HID0              0x80   /* 0,1: msb of ID2, 2-7: ID1      */
+#define AHC_HID1              0x81   /* 0-4: ID3, 5-7: LSB ID2         */
+#define AHC_HID2              0x82   /* product                        */
+#define AHC_HID3              0x83   /* firmware revision              */
 
 /*
  * AIC-7770 I/O range to reserve for a card
@@ -494,11 +640,13 @@
 #define                LATTIME                0x0000FF00ul
 
 #define        DEVCONFIG                0x40
-#define                SCBSIZE32        0x00010400ul        /* aic789X only */
+#define                SCBSIZE32        0x00010000ul        /* aic789X only */
 #define                MPORTMODE        0x00000400ul        /* aic7870 only */
 #define                RAMPSM           0x00000200ul        /* aic7870 only */
+#define                RAMPSM_ULTRA2    0x00000004
 #define                VOLSENSE         0x00000100ul
 #define                SCBRAMSEL        0x00000080ul
+#define                SCBRAMSEL_ULTRA2 0x00000008
 #define                MRDCEN           0x00000040ul
 #define                EXTSCBTIME       0x00000020ul        /* aic7870 only */
 #define                EXTSCBPEN        0x00000010ul        /* aic7870 only */
@@ -507,6 +655,8 @@
 #define                STPWLEVEL        0x00000002ul
 #define                DIFACTNEGEN      0x00000001ul        /* aic7870 only */
 
+#define        SCAMCTL                  0x1a                /* Ultra2 only  */
+#define        CCSCBBADDR               0xf0                /* aic7895/6/7  */
 
 /*
  * Define the different types of SEEPROMs on aic7xxx adapters
@@ -569,7 +719,9 @@
 #define CF284XSTERM     0x0020  /* SCSI low byte termination (284x cards) */
 #define CFRESETB        0x0040  /* reset SCSI bus at boot */
 #define CFBPRIMARY      0x0100  /* Channel B primary on 7895 chipsets */
-/* UNUSED                0xFE80 */
+#define CFSEAUTOTERM    0x0400  /* aic7890 Perform SE Auto Term */
+#define CFLVDSTERM      0x0800  /* aic7890 LVD Termination */
+/* UNUSED                0xF280 */
   unsigned short adapter_control;        /* word 17 */
 
 /*
@@ -682,7 +834,6 @@
 
 typedef enum {
         SCB_FREE                = 0x0000,
-        SCB_WDTR_16BIT          = 0x0001,
         SCB_WAITINGQ            = 0x0002,
         SCB_ACTIVE              = 0x0004,
         SCB_SENSE               = 0x0008,
@@ -691,11 +842,12 @@
         SCB_RESET               = 0x0040,
         SCB_RECOVERY_SCB        = 0x0080,
         SCB_WAS_BUSY            = 0x0100,
+        SCB_MSGOUT_SENT         = 0x0200,
         SCB_MSGOUT_SDTR         = 0x0400,
         SCB_MSGOUT_WDTR         = 0x0800,
-        SCB_MSGOUT_WDTR_8BIT    = 0x0800,
-        SCB_MSGOUT_WDTR_16BIT   = 0x0801,
-        SCB_MSGOUT_BITS         = SCB_MSGOUT_SDTR | SCB_MSGOUT_WDTR_16BIT,
+        SCB_MSGOUT_BITS         = SCB_MSGOUT_SENT | 
+                                  SCB_MSGOUT_SDTR |
+                                  SCB_MSGOUT_WDTR,
         SCB_QUEUED_ABORT        = 0x1000,
         SCB_QUEUED_FOR_DONE     = 0x2000
 } scb_flag_type;
@@ -711,7 +863,9 @@
         AHC_EXTEND_TRANS_A        = 0x00000100,
         AHC_EXTEND_TRANS_B        = 0x00000200,
         AHC_TERM_ENB_A            = 0x00000400,
+        AHC_TERM_ENB_SE_LOW       = 0x00000400,
         AHC_TERM_ENB_B            = 0x00000800,
+        AHC_TERM_ENB_SE_HIGH      = 0x00000800,
         AHC_HANDLING_REQINITS     = 0x00001000,
         AHC_TARGETMODE            = 0x00002000,
         AHC_NEWEEPROM_FMT         = 0x00004000,
@@ -726,45 +880,62 @@
         AHC_B_SCANNED             = 0x00200000,
         AHC_MULTI_CHANNEL         = 0x00400000,
         AHC_BIOS_ENABLED          = 0x00800000,
-        AHC_ABORT_PENDING         = 0x02000000,
-        AHC_RESET_PENDING         = 0x04000000,
+        AHC_SEEPROM_FOUND         = 0x01000000,
+        AHC_TERM_ENB_LVD          = 0x02000000,
+        AHC_ABORT_PENDING         = 0x04000000,
+        AHC_RESET_PENDING         = 0x08000000,
 #define AHC_IN_ISR_BIT              28
         AHC_IN_ISR                = 0x10000000,
         AHC_IN_ABORT              = 0x20000000,
-        AHC_IN_RESET              = 0x40000000
+        AHC_IN_RESET              = 0x40000000,
+        AHC_EXTERNAL_SRAM         = 0x80000000
 } ahc_flag_type;
 
 typedef enum {
-        AHC_NONE                  = 0x00000000,
-        AHC_ULTRA                 = 0x00000001,
-        AHC_WIDE                  = 0x00000002,
-        AHC_TWIN                  = 0x00000008,
-        AHC_AIC7770               = 0x00000010,
-        AHC_AIC7850               = 0x00000020,
-        AHC_AIC7860               = 0x00000021,
-        AHC_AIC7870               = 0x00000040,
-        AHC_AIC7880               = 0x00000041,
-        AHC_AIC7895               = 0x00000081,
-        AHC_AIC78x0               = 0x000000E0,
-        AHC_274                   = 0x00000110,
-        AHC_284                   = 0x00000210,
-        AHC_294AU                 = 0x00000421,
-        AHC_294                   = 0x00000440,
-        AHC_294U                  = 0x00000441,
-        AHC_394                   = 0x00000840,
-        AHC_394U                  = 0x00000841,
-        AHC_394AU                 = 0x00000881,
-        AHC_398                   = 0x00001040,
-        AHC_398U                  = 0x00001041,
-        AHC_39x                   = 0x00001880
-} ahc_type;
+  AHC_NONE             = 0x0000,
+  AHC_CHIPID_MASK      = 0x00ff,
+  AHC_AIC7770          = 0x0001,
+  AHC_AIC7850          = 0x0002,
+  AHC_AIC7860          = 0x0003,
+  AHC_AIC7870          = 0x0004,
+  AHC_AIC7880          = 0x0005,
+  AHC_AIC7890          = 0x0006,
+  AHC_AIC7895          = 0x0007,
+  AHC_AIC7896          = 0x0008,
+  AHC_VL               = 0x0100,
+  AHC_EISA             = 0x0200,
+  AHC_PCI              = 0x0400,
+} ahc_chip;
+
+typedef enum {
+  AHC_FENONE           = 0x0000,
+  AHC_ULTRA            = 0x0001,
+  AHC_ULTRA2           = 0x0002,
+  AHC_WIDE             = 0x0004,
+  AHC_TWIN             = 0x0008,
+  AHC_MORE_SRAM        = 0x0010,
+  AHC_CMD_CHAN         = 0x0020,
+  AHC_QUEUE_REGS       = 0x0040,
+  AHC_SG_PRELOAD       = 0x0080,
+  AHC_SPIOCAP          = 0x0100,
+  AHC_AIC7770_FE       = AHC_FENONE,
+  AHC_AIC7850_FE       = AHC_SPIOCAP,
+  AHC_AIC7860_FE       = AHC_ULTRA|AHC_SPIOCAP,
+  AHC_AIC7870_FE       = AHC_FENONE,
+  AHC_AIC7880_FE       = AHC_ULTRA,
+  AHC_AIC7890_FE       = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2|
+                         AHC_QUEUE_REGS|AHC_SG_PRELOAD,
+  AHC_AIC7895_FE       = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA,
+  AHC_AIC7896_FE       = AHC_AIC7890_FE,
+} ahc_feature;
 
 struct aic7xxx_scb {
         struct aic7xxx_hwscb  *hscb;          /* corresponding hardware scb */
         Scsi_Cmnd             *cmd;              /* Scsi_Cmnd for this scb */
         struct aic7xxx_scb    *q_next;        /* next scb in queue */
-        scb_flag_type          flags;         /* current state of scb */
+        volatile scb_flag_type flags;         /* current state of scb */
         struct hw_scatterlist *sg_list;       /* SG list in adapter format */
+        void                  *kmalloc_ptr;
         unsigned char          tag_action;
         unsigned char          sg_count;
         unsigned char          sense_cmd[6];  /*
@@ -792,28 +963,50 @@
   { ILLHADDR,  "Illegal Host Access" },
   { ILLSADDR,  "Illegal Sequencer Address referenced" },
   { ILLOPCODE, "Illegal Opcode in sequencer program" },
-  { SQPARERR,  "Sequencer Ram Parity Error" }
+  { SQPARERR,  "Sequencer Ram Parity Error" },
+  { DPARERR,   "Data-Path Ram Parity Error" },
+  { MPARERR,   "Scratch Ram/SCB Array Ram Parity Error" },
+  { PCIERRSTAT,"PCI Error detected" },
+  { CIOPARERR, "CIOBUS Parity Error" }
 };
 
 static unsigned char
 generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
 
 typedef struct {
-  struct aic7xxx_hwscb *hscbs;
   scb_queue_type free_scbs;        /*
                                     * SCBs assigned to free slot on
                                     * card (no paging required)
                                     */
+  struct aic7xxx_scb   *scb_array[AIC7XXX_MAXSCB];
+  struct aic7xxx_hwscb *hscbs;
   unsigned char  numscbs;          /* current number of scbs */
   unsigned char  maxhscbs;         /* hardware scbs */
   unsigned char  maxscbs;          /* max scbs including pageable scbs */
-  struct aic7xxx_scb   *scb_array[AIC7XXX_MAXSCB];
+  void          *hscb_kmalloc_ptr;
 } scb_data_type;
 
+struct target_cmd {
+  unsigned char mesg_bytes[4];
+  unsigned char command[28];
+};
+
+#define AHC_TRANS_CUR    0x0001
+#define AHC_TRANS_ACTIVE 0x0002
+#define AHC_TRANS_GOAL   0x0004
+#define AHC_TRANS_USER   0x0008
+#define AHC_TRANS_QUITE  0x0010
 typedef struct {
-  unsigned char period;
-  unsigned char offset;
-} syncinfo_type;
+  unsigned char cur_width;
+  unsigned char goal_width;
+  unsigned char cur_period;
+  unsigned char goal_period;
+  unsigned char cur_offset;
+  unsigned char goal_offset;
+  unsigned char user_width;
+  unsigned char user_period;
+  unsigned char user_offset;
+} transinfo_type;
 
 /*
  * Define a structure used for each host adapter.  Note, in order to avoid
@@ -841,15 +1034,18 @@
   spinlock_t               spin_lock;
 #endif
   volatile unsigned char   cpu_lock_count[NR_CPUS];
-  ahc_type                 type;             /* card type */
+  ahc_chip                 chip;             /* chip type */
+  ahc_feature              features;         /* chip features */
   unsigned long            last_reset;
   unsigned long            isr_count;        /* Interrupt count */
   unsigned long            spurious_int;
+  struct target_cmd       *targetcmds;
+  unsigned int             num_targetcmds;
   unsigned short           discenable;       /* Targets allowed to disconnect */
   unsigned short           tagenable;        /* Targets using tagged I/O */
   unsigned short           orderedtag;       /* Ordered Q tags allowed */
   volatile unsigned char   activescbs;       /* active scbs */
-  unsigned char            max_activescbs;
+  volatile unsigned char   max_activescbs;
   unsigned char            unpause;          /* unpause value for HCNTRL */
   unsigned char            pause;            /* pause value for HCNTRL */
   volatile unsigned char   qoutfifonext;
@@ -870,7 +1066,7 @@
 #define  DEVICE_SCANNED                 0x80
   volatile unsigned char   dev_flags[MAX_TARGETS];
   volatile unsigned char   dev_active_cmds[MAX_TARGETS];
-  unsigned char            dev_temp_queue_depth[MAX_TARGETS];
+  volatile unsigned char   dev_temp_queue_depth[MAX_TARGETS];
   unsigned char            dev_commands_sent[MAX_TARGETS];
 
   /*
@@ -917,7 +1113,7 @@
 #define MSG_TYPE_INITIATOR_MSGIN   0x02
   unsigned char            msg_len;          /* Length of message */
   unsigned char            msg_index;        /* Index into msg_buf array */
-  syncinfo_type            syncinfo[MAX_TARGETS];
+  transinfo_type           transinfo[MAX_TARGETS];
   volatile scb_queue_type  waiting_scbs;     /*
                                               * SCBs waiting for space in
                                               * the QINFIFO.
@@ -945,10 +1141,10 @@
   volatile unsigned char   qoutfifo[256];
   volatile unsigned char   qinfifo[256];
   unsigned int             irq;              /* IRQ for this adapter */
-  unsigned short           needsdtr;
-  unsigned short           sdtr_pending;
-  unsigned short           needwdtr;
-  unsigned short           wdtr_pending;
+  volatile unsigned short  needsdtr;
+  volatile unsigned short  sdtr_pending;
+  volatile unsigned short  needwdtr;
+  volatile unsigned short  wdtr_pending;
   int                      instance;         /* aic7xxx instance number */
   int                      scsi_id;          /* host adapter SCSI ID */
   int                      scsi_id_b;        /* channel B for twin adapters */
@@ -965,8 +1161,10 @@
 #endif
   unsigned char            pci_bus;
   unsigned char            pci_device_fn;
+  struct seeprom_config    sc;
+  unsigned short           sc_type;
+  unsigned short           sc_size;
 
-#ifdef AIC7XXX_PROC_STATS
   /*
    * Statistics Kept:
    *
@@ -980,18 +1178,21 @@
    *
    * NOTE: Enabling this feature is likely to cause a noticeable performance
    * decrease as the accesses into the stats structures blows apart multiple
-   * cache lines and is CPU time consuming.
+   * cache lines and is CPU time consuming.  We keep the xfer count always
+   * for use by the aic7xxx_proc.c code, but only do the bins if the
+   * proc stats code is enabled.
    */
   struct aic7xxx_xferstats {
     long xfers;                              /* total xfer count */
     long w_total;                            /* total writes */
     long w_total512;                         /* 512 byte blocks written */
-    long w_bins[10];                         /* binned write */
     long r_total;                            /* total reads */
     long r_total512;                         /* 512 byte blocks read */
+#ifdef AIC7XXX_PROC_STATS
+    long w_bins[10];                         /* binned write */
     long r_bins[10];                         /* binned reads */
-  } stats[MAX_TARGETS][MAX_LUNS];            /* [(channel << 3)|target][lun] */
 #endif /* AIC7XXX_PROC_STATS */
+  } stats[MAX_TARGETS][MAX_LUNS];            /* [(channel << 3)|target][lun] */
 };
 
 /*
@@ -999,29 +1200,33 @@
  * Provides a mapping of transfer periods in ns/4 to the proper value to
  * stick in the SCSIRATE reg to use that transfer rate.
  */
-static struct {
-  short period;
+#define AHC_SYNCRATE_ULTRA2 0
+#define AHC_SYNCRATE_ULTRA  2
+#define AHC_SYNCRATE_FAST   5
+static struct aic7xxx_syncrate {
   /* Rates in Ultra mode have bit 8 of sxfr set */
 #define                ULTRA_SXFR 0x100
-  short rate;
-  const char *english;
+  int sxfr_ultra2;
+  int sxfr;
+  unsigned char period;
+  const char *rate[2];
 } aic7xxx_syncrates[] = {
-  { 12,  0x100,  "20.0"  },
-  { 15,  0x110,  "16.0"  },
-  { 18,  0x120,  "13.4"  },
-  { 25,  0x000,  "10.0"  },
-  { 31,  0x010,   "8.0"  },
-  { 37,  0x020,   "6.67" },
-  { 43,  0x030,   "5.7"  },
-  { 50,  0x040,   "5.0"  },
-  { 56,  0x050,   "4.4"  },
-  { 62,  0x060,   "4.0"  },
-  { 68,  0x070,   "3.6"  }
+  { 0x13,  0x000,  10,  {"40.0", "80.0"} },
+  { 0x14,  0x000,  11,  {"33.0", "66.6"} },
+  { 0x15,  0x100,  12,  {"20.0", "40.0"} },
+  { 0x16,  0x110,  15,  {"16.0", "32.0"} },
+  { 0x17,  0x120,  18,  {"13.4", "26.8"} },
+  { 0x18,  0x000,  25,  {"10.0", "20.0"} },
+  { 0x19,  0x010,  31,  {"8.0",  "16.0"} },
+  { 0x1a,  0x020,  37,  {"6.67", "13.3"} },
+  { 0x1b,  0x030,  43,  {"5.7",  "11.4"} },
+  { 0x10,  0x040,  50,  {"5.0",  "10.0"} },
+  { 0x00,  0x050,  56,  {"4.4",  "8.8" } },
+  { 0x00,  0x060,  62,  {"4.0",  "8.0" } },
+  { 0x00,  0x070,  68,  {"3.6",  "7.2" } },
+  { 0x00,  0x000,  0,   {NULL, NULL}   },
 };
 
-static int num_aic7xxx_syncrates =
-    sizeof(aic7xxx_syncrates) / sizeof(aic7xxx_syncrates[0]);
-
 #define CTL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 3) & 0x1),  \
                         (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
                         ((scb->hscb)->target_channel_lun & 0x07)
@@ -1041,58 +1246,170 @@
 
 /*
  * XXX - these options apply unilaterally to _all_ 274x/284x/294x
- *       cards in the system.  This should be fixed.
+ *       cards in the system.  This should be fixed.  Exceptions to this
+ *       rule are noted in the comments.
  */
-static int aic7xxx_7895_irq_hack = -1;       /* This enables a hack to fix
-                                              * IRQ settings on buggy 7895
-                                              * MB controller setups
-                                              *  -1 == Disable this hack
-                                              *   0 == Use the Channel A IRQ
-                                              *   1 == Use the Channel B IRQ
-                                              */
-static unsigned int aic7xxx_extended = 0;    /* extended translation on? */
-static unsigned int aic7xxx_no_reset = 0;    /* no resetting of SCSI bus */
-static int aic7xxx_irq_trigger = -1;         /*
-                                              * -1 use board setting
-                                              *  0 use edge triggered
-                                              *  1 use level triggered
-                                              */
-static int aic7xxx_reverse_scan = 0;         /*
-                                              * Set this to anything but 0
-                                              * to make the probe code
-                                              * reverse the order of PCI
-                                              * devices
-                                              */
-static int aic7xxx_override_term = 0;        /*
-                                              * Set this to non-0 to make the
-                                              * driver override any BIOS
-                                              * configured termination
-                                              * settings based upon the
-                                              * results of the cable detect
-                                              * logic.  This only applies
-                                              * to cards that have cable
-                                              * detection logic and a SEEPROM
-                                              */
-static int aic7xxx_panic_on_abort = 0;       /*
-                                              * Set this to non-0 in order
-                                              * to force the driver to panic
-                                              * the kernel and print out
-                                              * debugging info on an abort
-                                              * or reset call into the
-                                              * driver.
-                                              */
-static int aic7xxx_pci_parity = 0;           /*
-                                              * Set this to:
-                                              *   0 - Shut off PCI parity check
-                                              *  -1 - Normal parity check
-                                              *  anything else - reverse pci
-                                              *    pci parity checking
-                                              */
+
+
+/*
+ * Skip the scsi bus reset.  Non 0 make us skip the reset at startup.  This
+ * has no effect on any later resets that might occur due to things like
+ * SCSI bus timeouts.
+ */
+static unsigned int aic7xxx_no_reset = 0;
+/*
+ * Certain PCI motherboards will scan PCI devices from highest to lowest,
+ * others scan from lowest to highest, and they tend to do all kinds of
+ * strange things when they come into contact with PCI bridge chips.  The
+ * net result of all this is that the PCI card that is actually used to boot
+ * the machine is very hard to detect.  Most motherboards go from lowest
+ * PCI slot number to highest, and the first SCSI controller found is the
+ * one you boot from.  The only exceptions to this are when a controller
+ * has its BIOS disabled.  So, we by default sort all of our SCSI controllers
+ * from lowest PCI slot number to highest PCI slot number.  We also force
+ * all controllers with their BIOS disabled to the end of the list.  This
+ * works on *almost* all computers.  Where it doesn't work, we have this
+ * option.  Setting this option to non-0 will reverse the order of the sort
+ * to highest first, then lowest, but will still leave cards with their BIOS
+ * disabled at the very end.  That should fix everyone up unless there are
+ * really strange cirumstances.
+ */
+static int aic7xxx_reverse_scan = 0;
+/*
+ * This setting enables a hack to fix the IRQ settings on buggy 7895
+ * MB controller setups:
+ *    -1 == Disable this hack
+ *     0 == Use the Channel A IRQ for both channels
+ *     1 == Use the Channel B IRQ for both channels
+ */
+static unsigned int aic7xxx_extended = 0;
+/*
+ * The IRQ trigger method used on EISA controllers. Does not effect PCI cards.
+ *   -1 = Use detected settings.
+ *    0 = Force Edge triggered mode.
+ *    1 = Force Level triggered mode.
+ */
+static int aic7xxx_irq_trigger = -1;
+/*
+ * This variable is used to override the termination settings on a controller.
+ * This should not be used under normal conditions.  However, in the case
+ * that a controller does not have a readable SEEPROM (so that we can't
+ * read the SEEPROM settings directly) and that a controller has a buggered
+ * version of the cable detection logic, this can be used to force the 
+ * correct termination.  It is preferable to use the manual termination
+ * settings in the BIOS if possible, but some motherboard controllers store
+ * those settings in a format we can't read.  In other cases, auto term
+ * should also work, but the chipset was put together with no auto term
+ * logic (common on motherboard controllers).  In those cases, we have
+ * 32 bits here to work with.  That's good for 8 controllers/channels.  The
+ * bits are organized as 4 bits per channel, with scsi0 getting the lowest
+ * 4 bits in the int.  A 1 in a bit position indicates the termination setting
+ * that corresponds to that bit should be enabled, a 0 is disabled.
+ * It looks something like this:
+ *
+ *    0x0f =  1111-Single Ended Low Byte Termination on/off
+ *            ||\-Single Ended High Byte Termination on/off
+ *            |\-LVD Low Byte Termination on/off
+ *            \-LVD High Byte Termination on/off
+ *
+ * For non-Ultra2 controllers, the upper 2 bits are not important.  So, to
+ * enable both high byte and low byte termination on scsi0, I would need to
+ * make sure that the override_term variable was set to 0x03 (bits 0011).
+ * To make sure that all termination is enabled on an Ultra2 controller at
+ * scsi2 and only high byte termination on scsi1 and high and low byte
+ * termination on scsi0, I would set override_term=0xf23 (bits 1111 0010 0011)
+ *
+ * For the most part, users should never have to use this, that's why I
+ * left it fairly cryptic instead of easy to understand.  If you need it,
+ * most likely someone will be telling you what your's needs to be set to.
+ */
+static int aic7xxx_override_term = -1;
+/*
+ * Certain motherboard chipset controllers tend to screw
+ * up the polarity of the term enable output pin.  Use this variable
+ * to force the correct polarity for your system.  This is a bitfield variable
+ * similar to the previous one, but this one has one bit per channel instead
+ * of four.
+ *    0 = Force the setting to active low.
+ *    1 = Force setting to active high.
+ * Most Adaptec cards are active high, several motherboards are active low.
+ * To force a 2940 card at SCSI 0 to active high and a motherboard 7895
+ * controller at scsi1 and scsi2 to active low, and a 2910 card at scsi3
+ * to active high, you would need to set stpwlev=0x9 (bits 1001).
+ *
+ * People shouldn't need to use this, but if you are experiencing lots of
+ * SCSI timeout problems, this may help.  There is one sure way to test what
+ * this option needs to be.  Using a boot floppy to boot the system, configure
+ * your system to enable all SCSI termination (in the Adaptec SCSI BIOS) and
+ * if needed then also pass a value to override_term to make sure that the
+ * driver is enabling SCSI termination, then set this variable to either 0
+ * or 1.  When the driver boots, make sure there are *NO* SCSI cables
+ * connected to your controller.  If it finds and inits the controller
+ * without problem, then the setting you passed to stpwlev was correct.  If
+ * the driver goes into a reset loop and hangs the system, then you need the
+ * other setting for this variable.  If neither setting lets the machine
+ * boot then you have definite termination problems that may not be fixable.
+ */
+static int aic7xxx_stpwlev = -1;
+/*
+ * Set this to non-0 in order to force the driver to panic the kernel
+ * and print out debugging info on a SCSI abort or reset cycle.
+ */
+static int aic7xxx_panic_on_abort = 0;
+/*
+ * PCI bus parity checking of the Adaptec controllers.  This is somewhat
+ * dubious at best.  To my knowledge, this option has never actually
+ * solved a PCI parity problem, but on certain machines with broken PCI
+ * chipset configurations, it can generate tons of false error messages.
+ * It's included in the driver for completeness.
+ *   0 = Shut off PCI parity check
+ *  -1 = Normal polarity pci parity checking
+ *   1 = reverse polarity pci parity checking
+ *
+ * NOTE: you can't actually pass -1 on the lilo prompt.  So, to set this
+ * variable to -1 you would actually want to simply pass the variable
+ * name without a number.  That will invert the 0 which will result in
+ * -1.
+ */
+static int aic7xxx_pci_parity = 0;
+/*
+ * Set this to any non-0 value to cause us to dump the contents of all
+ * the card's registers in a hex dump format tailored to each model of
+ * controller.
+ * 
+ * NOTE: THE CONTROLLER IS LEFT IN AN UNUSEABLE STATE BY THIS OPTION.
+ *       YOU CANNOT BOOT UP WITH THIS OPTION, IT IS FOR DEBUGGING PURPOSES
+ *       ONLY
+ */
+static int aic7xxx_dump_card = 0;
+/*
+ * Set this to a non-0 value to make us dump out the 32 bit instruction
+ * registers on the card after completing the sequencer download.  This
+ * allows the actual sequencer download to be verified.  It is possible
+ * to use this option and still boot up and run your system.  This is
+ * only intended for debugging purposes.
+ */
+static int aic7xxx_dump_sequencer = 0;
+/*
+ * Certain newer motherboards have put new PCI based devices into the
+ * IO spaces that used to typically be occupied by VLB or EISA cards.
+ * This overlap can cause these newer motherboards to lock up when scanned
+ * for older EISA and VLB devices.  Setting this option to non-0 will
+ * cause the driver to skip scanning for any VLB or EISA controllers and
+ * only support the PCI controllers.  NOTE: this means that if the kernel
+ * os compiled with PCI support disabled, then setting this to non-0
+ * would result in never finding any devices :)
+ */
+static int aic7xxx_no_probe = 0;
+
 /*
  * So that insmod can find the variable and make it point to something
  */
 #ifdef MODULE
 static char * aic7xxx = NULL;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,18)
+MODULE_PARM(aic7xxx, "s");
+#endif
 
 /*
  * Just in case someone uses commas to separate items on the insmod
@@ -1138,9 +1455,9 @@
 #define VERBOSE_SCSIINT        0x0004
 #define VERBOSE_PROBE          0x0008
 #define VERBOSE_PROBE2         0x0010
-#define VERBOSE_QUEUE          0x0020
+#define VERBOSE_NEGOTIATION2   0x0020
 #define VERBOSE_MINOR_ERROR    0x0040
-#define VERBOSE_QUEUE_FULL     0x0080
+#define VERBOSE_TRACING        0x0080
 #define VERBOSE_ABORT          0x0f00
 #define VERBOSE_ABORT_MID      0x0100
 #define VERBOSE_ABORT_FIND     0x0200
@@ -1157,49 +1474,95 @@
 
 /****************************************************************************
  *
- * These functions are not used yet, but when we do memory mapped
- * IO, we'll use them then.
+ * We're going to start putting in function declarations so that order of
+ * functions is no longer important.  As needed, they are added here.
+ *
+ ***************************************************************************/
+
+static void aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd);
+static void aic7xxx_print_card(struct aic7xxx_host *p);
+static void aic7xxx_print_scratch_ram(struct aic7xxx_host *p);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+static void aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded);
+static void aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer);
+#endif
+
+/****************************************************************************
+ *
+ * These functions are now used.  They happen to be wrapped in useless
+ * inb/outb port read/writes around the real reads and writes because it
+ * seems that certain very fast CPUs have a problem dealing with us when
+ * going at full speed.
  *
  ***************************************************************************/
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+static inline void
+mdelay(int milliseconds)
+{
+  int i;
+
+  for(i=0; i<milliseconds; i++)
+    udelay(1000);
+}
+#endif
 
 static inline unsigned char
 aic_inb(struct aic7xxx_host *p, long port)
 {
+#ifdef MMAPIO
   unsigned char x;
   if(p->maddr)
+  {
     x = p->maddr[port];
+  }
   else
+  {
     x = inb(p->base + port);
+  }
   mb();
   return(x);
+#else
+  return(inb(p->base + port));
+#endif
 }
 
 static inline void
 aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
 {
+#ifdef MMAPIO
   if(p->maddr)
+  {
     p->maddr[port] = val;
+  }
   else
+  {
     outb(val, p->base + port);
+  }
   mb();
+#else
+  outb(val, p->base + port);
+#endif
 }
 
-static inline void
+static void
 aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size)
 {
+#ifdef MMAPIO
   if(p->maddr)
   {
     int i;
-
     for (i=0; i < size; i++)
     {
       p->maddr[port] = valp[i];
     }
+    mb();
   }
   else
     outsb(p->base + port, valp, size);
-  mb();
+#else
+  outsb(p->base + port, valp, size);
+#endif
 }
 
 /*+F*************************************************************************
@@ -1227,10 +1590,13 @@
     { "irq_trigger", &aic7xxx_irq_trigger },
     { "verbose",     &aic7xxx_verbose },
     { "reverse_scan",&aic7xxx_reverse_scan },
-    { "7895_irq_hack", &aic7xxx_7895_irq_hack },
     { "override_term", &aic7xxx_override_term },
+    { "stpwlev", &aic7xxx_stpwlev },
+    { "no_probe", &aic7xxx_no_probe },
     { "panic_on_abort", &aic7xxx_panic_on_abort },
     { "pci_parity", &aic7xxx_pci_parity },
+    { "dump_card", &aic7xxx_dump_card },
+    { "dump_sequencer", &aic7xxx_dump_sequencer },
     { "tag_info",    NULL }
   };
 
@@ -1327,7 +1693,7 @@
         }
         else if (!strncmp(p, "verbose", n))
         {
-          *(options[i].flag) = 0xff69;
+          *(options[i].flag) = 0xff09;
         }
         else
         {
@@ -1387,54 +1753,64 @@
 static inline void
 restart_sequencer(struct aic7xxx_host *p)
 {
-  /* Set the sequencer address to 0. */
-  aic_outb(p, 0, SEQADDR0);
-  aic_outb(p, 0, SEQADDR1);
-
-  /*
-   * Reset and unpause the sequencer.  The reset is suppose to
-   * start the sequencer running, so we immediately do a pause_sequencer
-   * since some of our code expects the sequencer paused after a restart
-   */
-  aic_outb(p, SEQRESET | FASTMODE, SEQCTL);
-  pause_sequencer(p);
+  aic_outb(p, FASTMODE|SEQRESET, SEQCTL);
 }
 
+/*
+ * We include the aic7xxx_seq.c file here so that the other defines have
+ * already been made, and so that it comes before the code that actually
+ * downloads the instructions (since we don't typically use function
+ * prototype, our code has to be ordered that way, it's a left-over from
+ * the original driver days.....I should fix it some time DL).
+ */
+#include "aic7xxx_seq.c"
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_next_patch
+ *   aic7xxx_check_patch
  *
  * Description:
- *   Find the next patch to download.
+ *   See if the next patch to download should be downloaded.
  *-F*************************************************************************/
-static struct sequencer_patch *
-aic7xxx_next_patch(struct sequencer_patch *cur_patch, int options, int instrptr)
+static int
+aic7xxx_check_patch(struct aic7xxx_host *p,
+  struct sequencer_patch **start_patch, int start_instr, int *skip_addr)
 {
-  while (cur_patch != NULL)
+  struct sequencer_patch *cur_patch;
+  struct sequencer_patch *last_patch;
+  int num_patches;
+
+  num_patches = sizeof(sequencer_patches)/sizeof(struct sequencer_patch);
+  last_patch = &sequencer_patches[num_patches];
+  cur_patch = *start_patch;
+
+  while ((cur_patch < last_patch) && (start_instr == cur_patch->begin))
   {
-    if ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE))
-      || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE))
-      || (instrptr >= cur_patch->end))
+    if (cur_patch->patch_func(p) == 0)
     {
       /*
-       * Either we want to keep this section of code, or we have consumed
-       * this patch.  Skip to the next patch.
+       * Start rejecting code.
        */
-      cur_patch++;
-      if (cur_patch->options == 0)
-      {
-        /* Out of patches. */
-        cur_patch = NULL;
-      }
+      *skip_addr = start_instr + cur_patch->skip_instr;
+      cur_patch += cur_patch->skip_patch;
     }
     else
     {
-      /* Found an OK patch. */
-      break;
+      /*
+       * Found an OK patch.  Advance the patch pointer to the next patch
+       * and wait for our instruction pointer to get here.
+       */
+      cur_patch++;
     }
   }
-  return (cur_patch);
+
+  *start_patch = cur_patch;
+  if (start_instr < *skip_addr)
+    /*
+     * Still skipping
+     */
+    return (0);
+  return(1);
 }
 
 
@@ -1446,15 +1822,21 @@
  *   Find the next patch to download.
  *-F*************************************************************************/
 static void
-aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr)
+aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr,
+  unsigned char *dconsts)
 {
+  union ins_formats instr;
+  struct ins_format1 *fmt1_ins;
+  struct ins_format3 *fmt3_ins;
   unsigned char opcode;
-  struct ins_format3 instr;
-  unsigned char dconsts[4] = { 0, 0, 0, 0 };
 
-  instr = *(struct ins_format3 *) &seqprog[instrptr * 4];
+  instr = *(union ins_formats*) &seqprog[instrptr * 4];
+
+  fmt1_ins = &instr.format1;
+  fmt3_ins = NULL;
+
   /* Pull the opcode */
-  opcode = (instr.opcode_addr & ~DOWNLOAD_CONST_IMMEDIATE) >> 1;
+  opcode = instr.format1.opcode;
   switch (opcode)
   {
     case AIC_OP_JMP:
@@ -1466,44 +1848,86 @@
     case AIC_OP_JE:
     case AIC_OP_JZ:
     {
+      struct sequencer_patch *cur_patch;
       int address_offset;
       unsigned int address;
-      struct sequencer_patch *patch;
+      int skip_addr;
       int i;
 
+      fmt3_ins = &instr.format3;
       address_offset = 0;
-      address = instr.address;
-      address |= (instr.opcode_addr & ADDR_HIGH_BIT) << 8;
-      for (i = 0; i < NUMBER(sequencer_patches); i++)
+      address = fmt3_ins->address;
+      cur_patch = sequencer_patches;
+      skip_addr = 0;
+
+      for (i = 0; i < address;)
       {
-        patch = &sequencer_patches[i];
-        if ((((patch->options & options) == 0) && (patch->negative == FALSE)) ||
-            (((patch->options & options) != 0) && (patch->negative == TRUE)))
+        aic7xxx_check_patch(p, &cur_patch, i, &skip_addr);
+        if (skip_addr > i)
         {
-          if (address >= patch->end)
-          {
-            address_offset += patch->end - patch->begin;
-          }
+          int end_addr;
+
+          end_addr = MIN(address, skip_addr);
+          address_offset += end_addr - i;
+          i = skip_addr;
+        }
+        else
+        {
+          i++;
         }
       }
       address -= address_offset;
-      instr.address = address & 0xFF;
-      instr.opcode_addr &= ~ADDR_HIGH_BIT;
-      instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT;
+      fmt3_ins->address = address;
+      /* Fall Through to the next code section */
     }
-       /*  Fall through  */
     case AIC_OP_OR:
     case AIC_OP_AND:
     case AIC_OP_XOR:
     case AIC_OP_ADD:
     case AIC_OP_ADC:
-      if (instr.opcode_addr & DOWNLOAD_CONST_IMMEDIATE)
+    case AIC_OP_BMOV:
+      if (fmt1_ins->parity != 0)
       {
-        instr.immediate = dconsts[instr.immediate];
+        fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
       }
-      instr.opcode_addr &= ~DOWNLOAD_CONST_IMMEDIATE;
+      fmt1_ins->parity = 0;
+      /* Fall Through to the next code section */
     case AIC_OP_ROL:
-      aic_outsb(p, SEQRAM, &instr.immediate, 4);
+      if ((p->features & AHC_ULTRA2) != 0)
+      {
+        int i, count;
+
+        /* Calculate odd parity for the instruction */
+        for ( i=0, count=0; i < 31; i++)
+        {
+          unsigned int mask;
+
+          mask = 0x01 << i;
+          if ((instr.integer & mask) != 0)
+            count++;
+        }
+        if (!(count & 0x01))
+          instr.format1.parity = 1;
+      }
+      else
+      {
+        if (fmt3_ins != NULL)
+        {
+          instr.integer =  fmt3_ins->immediate |
+                          (fmt3_ins->source << 8) |
+                          (fmt3_ins->address << 16) |
+                          (fmt3_ins->opcode << 25);
+        }
+        else
+        {
+          instr.integer =  fmt1_ins->immediate |
+                          (fmt1_ins->source << 8) |
+                          (fmt1_ins->destination << 16) |
+                          (fmt1_ins->ret << 24) |
+                          (fmt1_ins->opcode << 25);
+        }
+      }
+      aic_outsb(p, SEQRAM, instr.bytes, 4);
       break;
 
     default:
@@ -1523,104 +1947,100 @@
 static void
 aic7xxx_loadseq(struct aic7xxx_host *p)
 {
-  int options;
   struct sequencer_patch *cur_patch;
   int i;
   int downloaded;
+  int skip_addr;
+  unsigned char download_consts[4] = {0, 0, 0, 0};
 
   if (aic7xxx_verbose & VERBOSE_PROBE)
   {
     printk(KERN_INFO "(scsi%d) Downloading sequencer code...", p->host_no);
   }
-  if (aic7xxx_verbose & VERBOSE_PROBE2)
-    printk("\n");
-  options = 1;  /* Code for all options. */
+  download_consts[TMODE_NUMCMDS] = p->num_targetcmds;
+  cur_patch = &sequencer_patches[0];
   downloaded = 0;
-  if (p->type & AHC_ULTRA)
-  {
-    options |= ULTRA;
-    if (aic7xxx_verbose & VERBOSE_PROBE2)
-      printk(KERN_INFO "(scsi%d)   Will download code for option ULTRA\n",
-        p->host_no);
-  }
-  if (p->type & AHC_TWIN)
-  {
-    options |= TWIN_CHANNEL;
-    if (aic7xxx_verbose & VERBOSE_PROBE2)
-      printk(KERN_INFO "(scsi%d)   Will download code for option "
-        "TWIN_CHANNEL\n", p->host_no);
-  }
-  if (p->type & AHC_WIDE)
-  {
-    options |= WIDE;
-    if (aic7xxx_verbose & VERBOSE_PROBE2)
-      printk(KERN_INFO "(scsi%d)   Will download code for option WIDE\n",
-        p->host_no);
-  }
-  /* if (p->scb_data->maxscbs > p->scb_data->maxhscbs)  this should always
-                                                        be true, don't test,
-                                                        just do.            */
-  {
-    options |= SCB_PAGING;
-    if (aic7xxx_verbose & VERBOSE_PROBE2)
-      printk(KERN_INFO "(scsi%d)   Will download code for option SCB_PAGING\n",
-        p->host_no);
-  }
-  /* We don't actually support target mode yet, so leave this out 
-  if (p->flags & AHC_TARGETMODE)
-    options |= TARGET_MODE; */
-
-  if ( (options & ~(ULTRA|TWIN_CHANNEL|WIDE|SCB_PAGING|0x01)) )
-  {
-    printk(KERN_INFO "(scsi%d) Unknown bits set in the options field, "
-      "correcting.\n", p->host_no);
-    options &= ULTRA|TWIN_CHANNEL|WIDE|SCB_PAGING|0x01;
-  }
-
+  skip_addr = 0;
 
-  cur_patch = sequencer_patches;
-  aic_outb(p, PERRORDIS | LOADRAM, SEQCTL);
+  aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL);
   aic_outb(p, 0, SEQADDR0);
   aic_outb(p, 0, SEQADDR1);
 
   for (i = 0; i < sizeof(seqprog) / 4;  i++)
   {
-    cur_patch = aic7xxx_next_patch(cur_patch, options, i);
-    if (cur_patch && (cur_patch->begin <= i) && (cur_patch->end > i))
+    if (aic7xxx_check_patch(p, &cur_patch, i, &skip_addr) == 0)
     {
       /* Skip this instruction for this configuration. */
       continue;
     }
-    aic7xxx_download_instr(p, options, i);
+    aic7xxx_download_instr(p, i, &download_consts[0]);
     downloaded++;
   }
 
-  aic_outb(p, FASTMODE, SEQCTL);
+  aic_outb(p, FASTMODE|SEQRESET, SEQCTL);
+  if (aic7xxx_verbose & VERBOSE_PROBE)
+  {
+    printk(" %d instructions downloaded\n", downloaded);
+  }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+  if (aic7xxx_dump_sequencer)
+    aic7xxx_print_sequencer(p, downloaded);
+#endif
+}
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_print_sequencer
+ *
+ * Description:
+ *   Print the contents of the sequencer memory to the screen.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded)
+{
+  int i, k, temp;
+  
+  aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL);
   aic_outb(p, 0, SEQADDR0);
   aic_outb(p, 0, SEQADDR1);
 
-  if (aic7xxx_verbose & VERBOSE_PROBE2)
-    printk(KERN_INFO "(scsi%d) Download complete,", p->host_no);
-
-  if (aic7xxx_verbose & VERBOSE_PROBE)
+  k = 0;
+  for (i=0; i < downloaded; i++)
   {
-    printk(" %d instructions downloaded\n", downloaded);
+    if ( k == 0 )
+      printk("%03x: ", i);
+    temp = aic_inb(p, SEQRAM);
+    temp |= (aic_inb(p, SEQRAM) << 8);
+    temp |= (aic_inb(p, SEQRAM) << 16);
+    temp |= (aic_inb(p, SEQRAM) << 24);
+    printk("%08x", temp);
+    if ( ++k == 8 )
+    {
+      printk("\n");
+      k = 0;
+    }
+    else
+      printk(" ");
   }
+  aic_outb(p, FASTMODE|SEQRESET, SEQCTL);
+  printk("\n");
 }
+#endif
 
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_delay
  *
  * Description:
- *   Delay for specified amount of time.  We use udelay because the timer
+ *   Delay for specified amount of time.  We use mdelay because the timer
  *   interrupt is not guaranteed to be enabled.  This will cause an
  *   infinite loop since jiffies (clock ticks) is not updated.
  *-F*************************************************************************/
 static void
 aic7xxx_delay(int seconds)
 {
-  mdelay(seconds*1000);
+  mdelay(seconds * 1000);
 }
 
 /*+F*************************************************************************
@@ -1652,170 +2072,345 @@
   return(bp);
 }
 
-
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_scsirate
+ *   aic7xxx_find_syncrate
  *
  * Description:
  *   Look up the valid period to SCSIRATE conversion in our table
  *-F*************************************************************************/
-static unsigned char
-aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
-    unsigned char *period, unsigned char *offset, int target, int channel,
-    int set)
+static struct aic7xxx_syncrate *
+aic7xxx_find_syncrate(struct aic7xxx_host *p, unsigned int *period,
+  unsigned int maxsync)
 {
-  int i = num_aic7xxx_syncrates;
-  unsigned char response_period;
-  unsigned char tindex;
-  unsigned short target_mask;
-  unsigned char lun;
-
-  tindex = target | (channel << 3);
-  target_mask = 0x01 << tindex;
-  lun = aic_inb(p, SCB_TCL) & 0x07;
-
-  response_period = *period;
+  struct aic7xxx_syncrate *syncrate;
 
-  /*
-   * If the offset is 0, then the device is requesting asynchronous
-   * transfers.
-   */
-  if ((*period != 0) && (*offset != 0))
+  syncrate = &aic7xxx_syncrates[maxsync];
+  while ( (syncrate->rate[0] != NULL) &&
+         (!(p->features & AHC_ULTRA2) || syncrate->sxfr_ultra2) )
   {
-    for (i = 0; i < num_aic7xxx_syncrates; i++)
+    if ( *period <= syncrate->period )
     {
-      if (*period <= aic7xxx_syncrates[i].period)
+      /*
+       * When responding to a target that requests sync, the requested rate
+       * may fall between two rates that we can output, but still be a rate
+       * that we can receive.  Because of this, we want to respond with the
+       * same rate that it sent to us even if the persiod we use to send
+       * data to it is lower.  Only lower the response period if we must.
+       */
+      if(syncrate == &aic7xxx_syncrates[maxsync])
       {
-        /*
-         * Watch out for Ultra speeds when ultra is not enabled and
-         * vice-versa.
-         */
-        if (!(p->type & AHC_ULTRA) &&
-            (aic7xxx_syncrates[i].rate & ULTRA_SXFR))
-        {
-          /*
-           * This should only happen if the drive is the first to negotiate
-           * and chooses a high rate.   We'll just move down the table until
-           * we hit a non ultra speed.
-           */
-          continue;
-        }
-        *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F);
-        *period = aic7xxx_syncrates[i].period;
-
-        /*
-         * When responding to a target that requests
-         * sync, that rate may fall between two rates
-         * that we can output, but still be a rate
-         * that we can receive.  Because of this,
-         * we may want to respond to the target with
-         * the same rate that it sent to us even
-         * if the period we use to send data to it
-         * is lower.  Only lower the response period
-         * if we must.
-         */
-        if ((i == 0) ||
-           ((aic7xxx_syncrates[i-1].rate & ULTRA_SXFR) != 0
-             && (p->type & AHC_ULTRA) == 0))
-        {
-          response_period = *period;
-        }
-
-        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-             (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
-        {
-          printk(INFO_LEAD "Synchronous at %sMHz, "
-                 "offset %d.\n", p->host_no, channel, target, lun,
-                 aic7xxx_syncrates[i].english, *offset);
-          p->dev_flags[tindex] &= ~ DEVICE_PRINT_SDTR;
-        }
-        break;
+        *period = syncrate->period;
       }
+      break;
     }
+    syncrate++;
   }
-
-  if (i >= num_aic7xxx_syncrates)
+  if ( (*period == 0) || (syncrate->rate[0] == NULL) ||
+       ((p->features & AHC_ULTRA2) && (syncrate->sxfr_ultra2 == 0)) )
   {
     /*
-     * Use asynchronous transfers.
+     * Use async transfers for this target
      */
-    *scsirate = 0;
     *period = 0;
-    *offset = 0;
-    response_period = 0;
-    if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-         (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
-    {
-      printk(INFO_LEAD "Using asynchronous transfers.\n",
-             p->host_no, channel, target, lun);
-      p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR;
-    }
+    syncrate = NULL;
   }
+  return (syncrate);
+}
 
-  /*
-   * Ensure Ultra mode is set properly for this target.
-   */
-  if ( (*scsirate != 0) && 
-       (aic7xxx_syncrates[i].rate & ULTRA_SXFR) )
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_find_period
+ *
+ * Description:
+ *   Look up the valid SCSIRATE to period conversion in our table
+ *-F*************************************************************************/
+static unsigned int
+aic7xxx_find_period(struct aic7xxx_host *p, unsigned int scsirate,
+  unsigned int maxsync)
+{
+  struct aic7xxx_syncrate *syncrate;
+
+  if ((p->features & AHC_ULTRA2) != 0)
   {
-    p->ultraenb |= target_mask;
+    scsirate &= SXFR_ULTRA2;
   }
   else
   {
-    p->ultraenb &= ~target_mask;
+    scsirate &= SXFR;
   }
-  if (set)
-  {
-    unsigned char sxfrctl0;
 
-    sxfrctl0 = aic_inb(p, SXFRCTL0);
-    sxfrctl0 &= ~FAST20;
-    if (p->ultraenb & target_mask)
+  syncrate = &aic7xxx_syncrates[maxsync];
+  while (syncrate->rate[0] != NULL)
+  {
+    if ((p->features & AHC_ULTRA2) != 0)
     {
-      sxfrctl0 |= FAST20;
+      if (syncrate->sxfr_ultra2 == 0)
+        break;
+      else if (scsirate == syncrate->sxfr_ultra2)
+        return (syncrate->period);
+    }
+    else if (scsirate == (syncrate->sxfr & ~ULTRA_SXFR))
+    {
+      return (syncrate->period);
     }
-    aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB);
-    aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1);
-    aic_outb(p, sxfrctl0, SXFRCTL0);
+    syncrate++;
   }
-  return(response_period);
+  return (0); /* async */
 }
 
 /*+F*************************************************************************
  * Function:
- *   scbq_init
+ *   aic7xxx_validate_offset
  *
  * Description:
- *   SCB queue initialization.
- *
+ *   Set a valid offset value for a particular card in use and transfer
+ *   settings in use.
  *-F*************************************************************************/
-static inline void
-scbq_init(volatile scb_queue_type *queue)
+static void
+aic7xxx_validate_offset(struct aic7xxx_host *p,
+  struct aic7xxx_syncrate *syncrate, unsigned int *offset, int wide)
 {
-  queue->head = NULL;
-  queue->tail = NULL;
+  unsigned int maxoffset;
+
+  /* Limit offset to what the card (and device) can do */
+  if (syncrate == NULL)
+  {
+    maxoffset = 0;
+  }
+  else if (p->features & AHC_ULTRA2)
+  {
+    maxoffset = MAX_OFFSET_ULTRA2;
+  }
+  else
+  {
+    if (wide)
+      maxoffset = MAX_OFFSET_16BIT;
+    else
+      maxoffset = MAX_OFFSET_8BIT;
+  }
+  *offset = MIN(*offset, maxoffset);
 }
 
 /*+F*************************************************************************
  * Function:
- *   scbq_insert_head
+ *   aic7xxx_set_syncrate
  *
  * Description:
- *   Add an SCB to the head of the list.
- *
+ *   Set the actual syncrate down in the card and in our host structs
  *-F*************************************************************************/
-static inline void
-scbq_insert_head(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
+static void
+aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate,
+    int target, int channel, unsigned int period, unsigned int offset,
+    unsigned int type)
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
-  unsigned long cpu_flags;
-#endif
+  unsigned char tindex;
+  unsigned short target_mask;
+  unsigned char lun;
+  unsigned int old_period, old_offset;
 
-  DRIVER_LOCK
-  scb->q_next = queue->head;
-  queue->head = scb;
-  if (queue->tail == NULL)       /* If list was empty, update tail. */
+  tindex = target | (channel << 3);
+  target_mask = 0x01 << tindex;
+  lun = aic_inb(p, SCB_TCL) & 0x07;
+
+  if (syncrate == NULL)
+  {
+    period = 0;
+    offset = 0;
+  }
+
+  old_period = p->transinfo[tindex].cur_period;
+  old_offset = p->transinfo[tindex].cur_offset;
+
+  
+  if (type & AHC_TRANS_CUR)
+  {
+    unsigned int scsirate;
+
+    scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+    if (p->features & AHC_ULTRA2)
+    {
+      scsirate &= ~SXFR_ULTRA2;
+      if (syncrate != NULL)
+      {
+        scsirate |= syncrate->sxfr_ultra2;
+      }
+      if (type & AHC_TRANS_ACTIVE)
+      {
+        aic_outb(p, offset, SCSIOFFSET);
+      }
+      aic_outb(p, offset, TARG_OFFSET + tindex);
+    }
+    else /* Not an Ultra2 controller */
+    {
+      scsirate &= ~(SXFR|SOFS);
+      p->ultraenb &= ~target_mask;
+      if (syncrate != NULL)
+      {
+        if (syncrate->sxfr & ULTRA_SXFR)
+        {
+          p->ultraenb |= target_mask;
+        }
+        scsirate |= (syncrate->sxfr & SXFR);
+        scsirate |= (offset & SOFS);
+      }
+      if (type & AHC_TRANS_ACTIVE)
+      {
+        unsigned char sxfrctl0;
+
+        sxfrctl0 = aic_inb(p, SXFRCTL0);
+        sxfrctl0 &= ~FAST20;
+        if (p->ultraenb & target_mask)
+          sxfrctl0 |= FAST20;
+        aic_outb(p, sxfrctl0, SXFRCTL0);
+      }
+      aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB);
+      aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1 );
+    }
+    if (type & AHC_TRANS_ACTIVE)
+    {
+      aic_outb(p, scsirate, SCSIRATE);
+    }
+    aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
+    p->transinfo[tindex].cur_period = period;
+    p->transinfo[tindex].cur_offset = offset;
+    if ( !(type & AHC_TRANS_QUITE) &&
+         (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+         (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
+    {
+      if (offset)
+      {
+        int rate_mod = (scsirate & WIDEXFER) ? 1 : 0;
+      
+        printk(INFO_LEAD "Synchronous at %s Mbyte/sec, "
+               "offset %d.\n", p->host_no, channel, target, lun,
+               syncrate->rate[rate_mod], offset);
+      }
+      else
+      {
+        printk(INFO_LEAD "Using asynchronous transfers.\n",
+               p->host_no, channel, target, lun);
+      }
+      p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR;
+    }
+  }
+
+  if (type & AHC_TRANS_GOAL)
+  {
+    p->transinfo[tindex].goal_period = period;
+    p->transinfo[tindex].goal_offset = offset;
+  }
+
+  if (type & AHC_TRANS_USER)
+  {
+    p->transinfo[tindex].user_period = period;
+    p->transinfo[tindex].user_offset = offset;
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_set_width
+ *
+ * Description:
+ *   Set the actual width down in the card and in our host structs
+ *-F*************************************************************************/
+static void
+aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel, int lun,
+    unsigned int width, unsigned int type)
+{
+  unsigned char tindex;
+  unsigned short target_mask;
+  unsigned int old_width, new_offset;
+
+  tindex = target | (channel << 3);
+  target_mask = 1 << tindex;
+  
+  old_width = p->transinfo[tindex].cur_width;
+
+  if (p->features & AHC_ULTRA2)
+    new_offset = MAX_OFFSET_ULTRA2;
+  else if (width == MSG_EXT_WDTR_BUS_16_BIT)
+    new_offset = MAX_OFFSET_16BIT;
+  else
+    new_offset = MAX_OFFSET_8BIT;
+  
+  if (type & AHC_TRANS_CUR) 
+  {
+    unsigned char scsirate;
+
+    scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+
+    scsirate &= ~WIDEXFER;
+    if (width == MSG_EXT_WDTR_BUS_16_BIT)
+      scsirate |= WIDEXFER;
+
+    aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
+
+    if (type & AHC_TRANS_ACTIVE)
+      aic_outb(p, scsirate, SCSIRATE);
+
+    p->transinfo[tindex].cur_width = width;
+
+    if ((aic7xxx_verbose & VERBOSE_NEGOTIATION2) && 
+        (p->dev_flags[tindex] & DEVICE_PRINT_WDTR))
+    {
+      printk(INFO_LEAD "Using %s transfers\n", p->host_no, channel, target,
+        lun, (scsirate & WIDEXFER) ? "Wide(16bit)" : "Narrow(8bit)" );
+      p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
+    }
+  }
+
+  if (type & AHC_TRANS_GOAL)
+    p->transinfo[tindex].goal_width = width;
+  if (type & AHC_TRANS_USER)
+    p->transinfo[tindex].user_width = width;
+
+  /*
+   * Having just set the width, the SDTR should come next, and we need a valid
+   * offset for the SDTR.  So, we make sure we put a valid one in here now as
+   * the goal_offset.
+   */
+  if (p->transinfo[tindex].goal_offset)
+    p->transinfo[tindex].goal_offset = new_offset;
+
+}
+      
+/*+F*************************************************************************
+ * Function:
+ *   scbq_init
+ *
+ * Description:
+ *   SCB queue initialization.
+ *
+ *-F*************************************************************************/
+static void
+scbq_init(volatile scb_queue_type *queue)
+{
+  queue->head = NULL;
+  queue->tail = NULL;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   scbq_insert_head
+ *
+ * Description:
+ *   Add an SCB to the head of the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_insert_head(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned long cpu_flags;
+#endif
+
+  DRIVER_LOCK
+  scb->q_next = queue->head;
+  queue->head = scb;
+  if (queue->tail == NULL)       /* If list was empty, update tail. */
     queue->tail = queue->head;
   DRIVER_UNLOCK
 }
@@ -1828,7 +2423,7 @@
  *   Remove an SCB from the head of the list.
  *
  *-F*************************************************************************/
-static __inline struct aic7xxx_scb *
+static inline struct aic7xxx_scb *
 scbq_remove_head(volatile scb_queue_type *queue)
 {
   struct aic7xxx_scb * scbp;
@@ -2021,7 +2616,7 @@
  * Description:
  *   Set the specified target busy.
  *-F*************************************************************************/
-static __inline void
+static inline void
 aic7xxx_busy_target(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
   p->untagged_scbs[scb->hscb->target_channel_lun] = scb->hscb->tag;
@@ -2035,7 +2630,7 @@
  *   Returns the index of the busy target, and optionally sets the
  *   target inactive.
  *-F*************************************************************************/
-static __inline unsigned char
+static inline unsigned char
 aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char tcl,
     int unbusy)
 {
@@ -2091,47 +2686,47 @@
  * Description:
  *   Get an SCB from the free list or by allocating a new one.
  *-F*************************************************************************/
-static struct aic7xxx_scb *
-aic7xxx_allocate_scb(struct aic7xxx_host *p, int force_alloc)
+static int
+aic7xxx_allocate_scb(struct aic7xxx_host *p)
 {
   struct aic7xxx_scb   *scbp = NULL;
   int scb_size = sizeof(struct aic7xxx_scb) +
                  sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG;
   int i;
+  int step = PAGE_SIZE / 1024;
   unsigned long scb_count = 0;
   struct hw_scatterlist *hsgp;
   struct aic7xxx_scb *scb_ap;
+  unsigned long temp;
 
 
-  if (force_alloc == FALSE)
-  {
-    scbp = scbq_remove_head(&p->scb_data->free_scbs);
-    if (scbp != NULL)
-      return(scbp);
-  }
-  /*
-   * Either there wasn't an SCB or this is a strictly allocation call
-   */
-
   if (p->scb_data->numscbs < p->scb_data->maxscbs)
   {
-
     /*
-     * Optimize for 30 scbs at a time, but allow a final allocation of
-     * fewer than 30 scbs.  Except on 64 bit platforms, we optimize for
-     * 29 SCBs at a time because a pointer is 4 bytes larger and we don't
-     * want to overrun this suppossedly 32K allocation to 64K and waste
-     * tons of space.
+     * Calculate the optimal number of SCBs to allocate.
+     *
+     * NOTE: This formula works because the sizeof(sg_array) is always
+     * 1024.  Therefore, scb_size * i would always be > PAGE_SIZE *
+     * (i/step).  The (i-1) allows the left hand side of the equation
+     * to grow into the right hand side to a point of near perfect
+     * efficiency since scb_size * (i -1) is growing slightly faster
+     * than the right hand side.  If the number of SG array elements
+     * is changed, this function may not be near so efficient any more.
      */
-    if( sizeof(void *) == sizeof(int) )
-      scb_count = MIN(30, p->scb_data->maxscbs - p->scb_data->numscbs);
-    else
-      scb_count = MIN(29, p->scb_data->maxscbs - p->scb_data->numscbs);
-    
+    for ( i=step;; i *= 2 )
+    {
+      if ( (scb_size * (i-1)) >= ( (PAGE_SIZE * (i/step)) - 64 ) )
+      {
+        i /= 2;
+        break;
+      }
+    }
+    scb_count = MIN( (i-1), p->scb_data->maxscbs - p->scb_data->numscbs);
     scb_ap = (struct aic7xxx_scb *)kmalloc(scb_size * scb_count, GFP_ATOMIC);
     if (scb_ap != NULL)
     {
-      if (aic7xxx_verbose & VERBOSE_QUEUE)
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
       {
         if (p->scb_data->numscbs == 0)
           printk(INFO_LEAD "Allocating initial %ld SCB structures.\n",
@@ -2140,8 +2735,12 @@
           printk(INFO_LEAD "Allocating %ld additional SCB structures.\n",
             p->host_no, -1, -1, -1, scb_count);
       }
+#endif
       memset(scb_ap, 0, scb_count * scb_size);
-      hsgp = (struct hw_scatterlist *) &scb_ap[scb_count];
+      temp = (unsigned long) &scb_ap[scb_count];
+      temp += 1023;
+      temp &= ~1023;
+      hsgp = (struct hw_scatterlist *)temp;
       for (i=0; i < scb_count; i++)
       {
         scbp = &scb_ap[i];
@@ -2155,20 +2754,14 @@
         p->scb_data->scb_array[p->scb_data->numscbs++] = scbp;
         scbq_insert_head(&p->scb_data->free_scbs, scbp);
       }
+      scbp->kmalloc_ptr = scb_ap;
     }
     else
     {
-      return(NULL);
+      return(0);
     }
   }
-  if (force_alloc == TRUE)
-  {
-    return((struct aic7xxx_scb *)scb_count);
-  }
-  else
-  {
-    return(scbq_remove_head(&p->scb_data->free_scbs));
-  }
+  return(scb_count);
 }
 
 /*+F*************************************************************************
@@ -2180,7 +2773,7 @@
  *   to queue completed commands, then call scsi_done() on them when
  *   we're finished.  This function queues the completed commands.
  *-F*************************************************************************/
-static inline void
+static void
 aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
 {
   cmd->host_scribble = (char *)p->completeq.head;
@@ -2194,7 +2787,7 @@
  * Description:
  *   Process the completed command queue.
  *-F*************************************************************************/
-static inline void
+static void
 aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
 {
   Scsi_Cmnd *cmd;
@@ -2267,17 +2860,15 @@
   }
   if (scb->flags & SCB_RESET)
   {
-      cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | 
-        (cmd->result & 0xffff);
+      cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff);
   }
   else if (scb->flags & SCB_ABORT)
   {
-      cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | 
-        (cmd->result & 0xffff);
+      cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff);
   }
-  if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
+  else if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
   {
-    if(cmd->cmnd[0] == INQUIRY)
+    if ( (cmd->cmnd[0] == INQUIRY) && (cmd->result == DID_OK) )
     {
       char *buffer;
       
@@ -2295,35 +2886,65 @@
 #define WIDE_INQUIRY_BITS 0x60
 #define SYNC_INQUIRY_BITS 0x10
       if ( (buffer[7] & WIDE_INQUIRY_BITS) &&
-           (p->needwdtr_copy & (1<<tindex)) &&
-           (p->type & AHC_WIDE) )
+           (p->features & AHC_WIDE) )
       {
         p->needwdtr |= (1<<tindex);
         p->needwdtr_copy |= (1<<tindex);
-        p->syncinfo[tindex].offset = MAX_OFFSET_16BIT;
+        if ( (p->flags & AHC_SEEPROM_FOUND) &&
+             (p->transinfo[tindex].user_width != MSG_EXT_WDTR_BUS_16_BIT) )
+          p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_8_BIT;
+        else
+          p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_16_BIT;
       }
       else
       {
         p->needwdtr &= ~(1<<tindex);
         p->needwdtr_copy &= ~(1<<tindex);
-        p->syncinfo[tindex].offset = MAX_OFFSET_8BIT;
+        pause_sequencer(p);
+        aic7xxx_set_width(p, cmd->target, cmd->channel, cmd->lun,
+                          MSG_EXT_WDTR_BUS_8_BIT, (AHC_TRANS_ACTIVE |
+                                                   AHC_TRANS_GOAL |
+                                                   AHC_TRANS_CUR) );
+        unpause_sequencer(p, FALSE);
       }
       if (buffer[7] & SYNC_INQUIRY_BITS)
       {
         p->needsdtr |= (1<<tindex);
         p->needsdtr_copy |= (1<<tindex);
+
+        if (p->flags & AHC_SEEPROM_FOUND)
+          p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period;
+        else if (p->features & AHC_ULTRA2)
+          p->transinfo[tindex].goal_period =
+            aic7xxx_syncrates[AHC_SYNCRATE_ULTRA2].period;
+        else if (p->features & AHC_ULTRA)
+          p->transinfo[tindex].goal_period =
+            aic7xxx_syncrates[AHC_SYNCRATE_ULTRA].period;
+        else
+          p->transinfo[tindex].goal_period =
+            aic7xxx_syncrates[AHC_SYNCRATE_FAST].period;
+
+        if (p->features & AHC_ULTRA2)
+          p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+        else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT)
+          p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+        else
+          p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
       }
       else
       {
         p->needsdtr &= ~(1<<tindex);
         p->needsdtr_copy &= ~(1<<tindex);
+        p->transinfo[tindex].goal_period = 0;
+        p->transinfo[tindex].goal_offset = 0;
       }
       p->dev_flags[tindex] |= DEVICE_SCANNED;
+      p->dev_flags[tindex] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR;
 #undef WIDE_INQUIRY_BITS
 #undef SYNC_INQUIRY_BITS
     }
   }
-  if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
+  else if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
   {
     unsigned short mask;
     int message_error = FALSE;
@@ -2346,7 +2967,7 @@
       p->wdtr_pending &= ~mask;
       if (message_error)
       {
-        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
              (p->dev_flags[tindex] & DEVICE_PRINT_WDTR) )
         {
           printk(INFO_LEAD "Device failed to complete Wide Negotiation "
@@ -2366,7 +2987,7 @@
       p->sdtr_pending &= ~mask;
       if (message_error)
       {
-        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
              (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
         {
           printk(INFO_LEAD "Device failed to complete Sync Negotiation "
@@ -2402,12 +3023,17 @@
       {
         scbq_insert_tail(&p->waiting_scbs, scbp);
       }
-    }
-    if ( (queue_depth > p->dev_active_cmds[tindex]) && scbp )
-    {
-      scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
-      if (scbp)
-        scbq_insert_tail(&p->waiting_scbs, scbp);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
+        printk(INFO_LEAD "Moving SCB from delayed to waiting queue.\n",
+               p->host_no, CTL_OF_SCB(scbp));
+#endif
+      if (queue_depth > p->dev_active_cmds[tindex])
+      {
+        scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
+        if (scbp)
+          scbq_insert_tail(&p->waiting_scbs, scbp);
+      }
     }
   }
   if ( !(scb->tag_action) && (p->tagenable & (1<<tindex)) )
@@ -2427,7 +3053,6 @@
     aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun, TRUE);
   }
 
-#ifdef AIC7XXX_PROC_STATS
   {
     int actual;
 
@@ -2445,11 +3070,17 @@
     if ((actual >= 512) && (((cmd->result >> 16) & 0xf) == DID_OK))
     {
       struct aic7xxx_xferstats *sp;
+#ifdef AIC7XXX_PROC_STATS
       long *ptr;
       int x;
+#endif /* AIC7XXX_PROC_STATS */
 
       sp = &p->stats[TARGET_INDEX(cmd)][cmd->lun & 0x7];
       sp->xfers++;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if ( (sp->xfers > 16) && (aic7xxx_verbose > 0xffff) )
+        aic7xxx_verbose &= 0xffff;
+#endif
 
       /*
        * For block devices, cmd->request.cmd is always == either READ or
@@ -2462,14 +3093,19 @@
       {
         sp->w_total++;
         sp->w_total512 += (actual >> 9);
+#ifdef AIC7XXX_PROC_STATS
         ptr = sp->w_bins;
+#endif /* AIC7XXX_PROC_STATS */
       }
       else
       {
         sp->r_total++;
         sp->r_total512 += (actual >> 9);
+#ifdef AIC7XXX_PROC_STATS
         ptr = sp->r_bins;
+#endif /* AIC7XXX_PROC_STATS */
       }
+#ifdef AIC7XXX_PROC_STATS
       for (x = 9; x <= 17; x++)
       {
         if (actual < (1 << x))
@@ -2482,10 +3118,9 @@
       {
         ptr[x - 9]++;
       }
+#endif /* AIC7XXX_PROC_STATS */
     }
   }
-#endif /* AIC7XXX_PROC_STATS */
-
   aic7xxx_free_scb(p, scb);
   aic7xxx_queue_cmd_complete(p, cmd);
 
@@ -2667,7 +3302,10 @@
   {
     p->qinfifo[qinpos++] = SCB_LIST_NULL;
   }
-  aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+  if (p->features & AHC_QUEUE_REGS)
+    aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+  else
+    aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
 
   return (found);
 }
@@ -2740,7 +3378,7 @@
       {
         case 0:
                  min_target = 0;
-                 max_target = (p->type & AHC_WIDE) ? 15 : 7;
+                 max_target = (p->features & AHC_WIDE) ? 15 : 7;
                  break;
         case 1:
                  min_target = 8;
@@ -2749,7 +3387,7 @@
         case ALL_CHANNELS:
         default:
                  min_target = 0;
-                 max_target = (p->type & (AHC_TWIN|AHC_WIDE)) ? 15 : 7;
+                 max_target = (p->features & (AHC_TWIN|AHC_WIDE)) ? 15 : 7;
                  break;
       }
     }
@@ -2812,7 +3450,7 @@
       j = 0; 
       prev_scbp = NULL; 
       scbp = p->delayed_scbs[i].head;
-      while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) )
+      while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) )
       {
         prev_scbp = scbp;
         scbp = scbp->q_next;
@@ -2837,7 +3475,7 @@
           prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
         }
       }
-      if ( j > p->scb_data->numscbs )
+      if ( j > (p->scb_data->maxscbs + 1) )
       {
         if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
           printk(WARN_LEAD "Yikes!! There's a loop in the "
@@ -2871,7 +3509,7 @@
     j = 0; 
     prev_scbp = NULL; 
     scbp = p->waiting_scbs.head;
-    while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) )
+    while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) )
     {
       prev_scbp = scbp;
       scbp = scbp->q_next;
@@ -2896,7 +3534,7 @@
         prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
       }
     }
-    if ( j > p->scb_data->numscbs )
+    if ( j > (p->scb_data->maxscbs + 1) )
     {
       if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
         printk(WARN_LEAD "Yikes!! There's a loop in the "
@@ -2918,7 +3556,7 @@
     next = aic_inb(p, WAITING_SCBH);  /* Start at head of list. */
     prev = SCB_LIST_NULL;
     j = 0;
-    while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) )
+    while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
     {
       aic_outb(p, next, SCBPTR);
       scb_index = aic_inb(p, SCB_TAG);
@@ -2966,7 +3604,7 @@
         }
       }
     }
-    if ( j > p->scb_data->maxhscbs )
+    if ( j > (p->scb_data->maxscbs + 1) )
     {
       printk(WARN_LEAD "Yikes!!  There is a loop in the waiting for "
         "selection list!\n", p->host_no, channel, target, lun);
@@ -2981,13 +3619,14 @@
   if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
     printk(INFO_LEAD "Cleaning disconnected scbs "
       "list.\n", p->host_no, channel, target, lun);
+  if (p->features & AHC_PAGESCBS)
   {
     unsigned char next, prev, scb_index;
 
     next = aic_inb(p, DISCONNECTED_SCBH);
     prev = SCB_LIST_NULL;
     j = 0;
-    while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) )
+    while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
     {
       aic_outb(p, next, SCBPTR);
       scb_index = aic_inb(p, SCB_TAG);
@@ -3020,7 +3659,7 @@
         }
       }
     }
-    if ( j > p->scb_data->maxhscbs )
+    if ( j > (p->scb_data->maxscbs + 1) )
     {
       printk(WARN_LEAD "Yikes!!  There is a loop in the disconnected list!\n",
         p->host_no, channel, target, lun);
@@ -3032,15 +3671,23 @@
    * Walk the free list making sure no entries on the free list have
    * a valid SCB_TAG value or SCB_CONTROL byte.
    */
+  if (p->features & AHC_PAGESCBS)
   {
     unsigned char next;
 
     j = 0;
     next = aic_inb(p, FREE_SCBH);
-    while ( (next != SCB_LIST_NULL) && (j++ < p->scb_data->maxhscbs) )
+    if ( (next >= p->scb_data->maxhscbs) && (next != SCB_LIST_NULL) )
+    {
+      printk(WARN_LEAD "Bogus FREE_SCBH!.\n", p->host_no, channel,
+        target, lun);
+      init_lists = TRUE;
+      next = SCB_LIST_NULL;
+    }
+    while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
     {
       aic_outb(p, next, SCBPTR);
-      if ( aic_inb(p, SCB_TAG) < p->scb_data->numscbs )
+      if (aic_inb(p, SCB_TAG) < p->scb_data->numscbs)
       {
         printk(WARN_LEAD "Free list inconsistency!.\n", p->host_no, channel,
           target, lun);
@@ -3054,7 +3701,7 @@
         next = aic_inb(p, SCB_NEXT);
       }
     }
-    if ( j > p->scb_data->maxhscbs )
+    if ( j > (p->scb_data->maxscbs + 1) )
     {
       printk(WARN_LEAD "Yikes!!  There is a loop in the free list!\n",
         p->host_no, channel, target, lun);
@@ -3072,7 +3719,7 @@
     aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
     aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
   }
-  for (i = p->scb_data->maxhscbs; i >= 0; --i)
+  for (i = p->scb_data->maxhscbs - 1; i >= 0; i--)
   {
     unsigned char scbid;
 
@@ -3148,7 +3795,7 @@
   aic_outb(p, CLRSELDO | CLRSELDI | CLRSELINGO, CLRSINT0);
   aic_outb(p, CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
        CLRPHASECHG | CLRREQINIT, CLRSINT1);
-  aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT, CLRINT);
+  aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT | CLRPARERR, CLRINT);
 }
 
 /*+F*************************************************************************
@@ -3161,21 +3808,26 @@
 static void
 aic7xxx_reset_current_bus(struct aic7xxx_host *p)
 {
-  unsigned char scsiseq;
 
   /* Disable reset interrupts. */
   aic_outb(p, aic_inb(p, SIMODE1) & ~ENSCSIRST, SIMODE1);
 
+  /* Turn off the bus' current operations, after all, we shouldn't have any
+   * valid commands left to cause a RSELI and SELO once we've tossed the
+   * bus away with this reset, so we might as well shut down the sequencer
+   * until the bus is restarted as oppossed to saving the current settings
+   * and restoring them (which makes no sense to me). */
+
   /* Turn on the bus reset. */
-  scsiseq = aic_inb(p, SCSISEQ);
-  aic_outb(p, scsiseq | SCSIRSTO, SCSISEQ);
+  aic_outb(p, aic_inb(p, SCSISEQ) | SCSIRSTO, SCSISEQ);
+  while ( (aic_inb(p, SCSISEQ) & SCSIRSTO) == 0)
+    mdelay(5);
 
-  mdelay(5);
+  mdelay(10);
 
   /* Turn off the bus reset. */
-  aic_outb(p, scsiseq & ~SCSIRSTO, SCSISEQ);
-
-  mdelay(2);
+  aic_outb(p, 0, SCSISEQ);
+  mdelay(5);
 
   aic7xxx_clear_intstat(p);
   /* Re-enable reset interrupts. */
@@ -3193,7 +3845,7 @@
 static void
 aic7xxx_reset_channel(struct aic7xxx_host *p, int channel, int initiate_reset)
 {
-  unsigned long offset, offset_max;
+  unsigned long offset_min, offset_max;
   unsigned char sblkctl;
   int cur_channel;
 
@@ -3206,48 +3858,52 @@
   {
     p->needsdtr |= (p->needsdtr_copy & 0xFF00);
     p->sdtr_pending &= 0x00FF;
-    offset = TARG_SCRATCH + 8;
-    offset_max = TARG_SCRATCH + 16;
+    offset_min = 8;
+    offset_max = 16;
   }
   else
   {
-    if (p->type & AHC_WIDE)
+    if (p->features & AHC_WIDE)
     {
       p->needsdtr = p->needsdtr_copy;
       p->needwdtr = p->needwdtr_copy;
       p->sdtr_pending = 0x0;
       p->wdtr_pending = 0x0;
-      offset = TARG_SCRATCH;
-      offset_max = TARG_SCRATCH + 16;
+      offset_min = 0;
+      offset_max = 16;
     }
     else
     {
       /* Channel A */
       p->needsdtr |= (p->needsdtr_copy & 0x00FF);
       p->sdtr_pending &= 0xFF00;
-      offset = TARG_SCRATCH;
-      offset_max = TARG_SCRATCH + 8;
+      offset_min = 0;
+      offset_max = 8;
     }
   }
 
-  while (offset < offset_max)
+  while (offset_min < offset_max)
   {
     /*
      * Revert to async/narrow transfers until we renegotiate.
      */
-    u_char targ_scratch;
-
-    targ_scratch = aic_inb(p, offset);
-    targ_scratch &= SXFR;
-    aic_outb(p, targ_scratch, offset++);
+    aic_outb(p, 0, TARG_SCSIRATE + offset_min);
+    if (p->features & AHC_ULTRA2)
+    {
+      aic_outb(p, 0, TARG_OFFSET + offset_min);
+    }
+    offset_min++;
   }
 
   /*
    * Reset the bus and unpause/restart the controller
    */
   sblkctl = aic_inb(p, SBLKCTL);
-  cur_channel = (sblkctl & SELBUSB) >> 3;
-  if (cur_channel != channel)
+  if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+    cur_channel = (sblkctl & SELBUSB) >> 3;
+  else
+    cur_channel = 0;
+  if ( (cur_channel != channel) && (p->features & AHC_TWIN) )
   {
     /*
      * Case 1: Command for another bus is active
@@ -3264,7 +3920,7 @@
     {
       aic7xxx_reset_current_bus(p);
     }
-    aic_outb(p, 0, SCSISEQ);
+    aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
     aic7xxx_clear_intstat(p);
     aic_outb(p, sblkctl, SBLKCTL);
   }
@@ -3285,7 +3941,7 @@
     {
       aic7xxx_reset_current_bus(p);
     }
-    aic_outb(p, 0, SCSISEQ);
+    aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
     aic7xxx_clear_intstat(p);
   }
   if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
@@ -3296,7 +3952,13 @@
    */
   aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
 
-  if ( !(p->type & AHC_TWIN) )
+  /*
+   * Convince Mid Level SCSI code to leave us be for a little bit...
+   */
+  p->last_reset = jiffies;
+  p->host->last_reset = (jiffies + (HZ * AIC7XXX_RESET_DELAY));
+
+  if ( !(p->features & AHC_TWIN) )
   {
     restart_sequencer(p);
   }
@@ -3312,7 +3974,7 @@
  *   Scan the awaiting_scbs queue downloading and starting as many
  *   scbs as we can.
  *-F*************************************************************************/
-static inline void
+static void
 aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
 {
   struct aic7xxx_scb *scb;
@@ -3337,23 +3999,38 @@
     tindex = TARGET_INDEX(scb->cmd);
     if ( !scb->tag_action && (p->tagenable & (1<<tindex)) )
     {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
+        printk(INFO_LEAD "Reducing Queue depth for untagged command.\n",
+               p->host_no, CTL_OF_SCB(scb));
+#endif
       p->dev_temp_queue_depth[tindex] = 1;
     }
     if ( (p->dev_active_cmds[tindex] >=
           p->dev_temp_queue_depth[tindex]) ||
          (p->dev_last_reset[tindex] >= (jiffies - (4 * HZ))) )
     {
-        scbq_insert_tail(&p->delayed_scbs[tindex], scb);
-        if ( !(p->dev_timer[tindex].expires) &&
-             !(p->dev_active_cmds[tindex]) )
-        {
-          p->dev_timer[tindex].expires = p->dev_last_reset[tindex] + (4 * HZ);
-          add_timer(&p->dev_timer[tindex]);
-        }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
+        printk(INFO_LEAD "Moving SCB to Delayed Queue.\n",
+               p->host_no, CTL_OF_SCB(scb));
+#endif
+      scbq_insert_tail(&p->delayed_scbs[tindex], scb);
+      if ( !(p->dev_timer[tindex].expires) &&
+           !(p->dev_active_cmds[tindex]) )
+      {
+        p->dev_timer[tindex].expires = p->dev_last_reset[tindex] + (4 * HZ);
+        add_timer(&p->dev_timer[tindex]);
+      }
     }
     else
     {
         scb->flags &= ~SCB_WAITINGQ;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+        if (aic7xxx_verbose > 0xffff)
+          printk(INFO_LEAD "Sending command %d/0x%x to QINFIFO\n", p->host_no,
+                 CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
+#endif
         p->dev_active_cmds[tindex]++;
         p->activescbs++;
         if ( !(scb->tag_action) )
@@ -3366,62 +4043,142 @@
   }
   if (sent)
   {
-    pause_sequencer(p);
-    aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
-    unpause_sequencer(p, FALSE);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+    if (aic7xxx_verbose > 0xffff)
+    {
+      printk(INFO_LEAD "Sending commands to QINFIFO\n", p->host_no,
+             -1, -1, -1);
+      if ( (p->isr_count < 16) && (aic7xxx_panic_on_abort) &&
+           (p->flags & AHC_PAGESCBS) )
+        aic7xxx_check_scbs(p, "While sending commands to QINFIFO");
+    }
+#endif
+    if (p->features & AHC_QUEUE_REGS)
+      aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+    else
+    {
+      pause_sequencer(p);
+      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+      unpause_sequencer(p, FALSE);
+    }
     if (p->activescbs > p->max_activescbs)
       p->max_activescbs = p->activescbs;
   }
   DRIVER_UNLOCK
 }
 
+#ifdef CONFIG_PCI
+
+#define  DPE 0x80
+#define  SSE 0x40
+#define  RMA 0x20
+#define  RTA 0x10
+#define  STA 0x08
+#define  DPR 0x01
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_timer
+ *   aic7xxx_pci_intr
  *
  * Description:
- *   Take expired extries off of delayed queues and place on waiting queue
- *   then run waiting queue to start commands.
- ***************************************************************************/
+ *   Check the scsi card for PCI errors and clear the interrupt
+ *
+ *   NOTE: If you don't have this function and a 2940 card encounters
+ *         a PCI error condition, the machine will end up locked as the
+ *         interrupt handler gets slammed with non-stop PCI error interrupts
+ *-F*************************************************************************/
 static void
-aic7xxx_timer(struct aic7xxx_host *p)
+aic7xxx_pci_intr(struct aic7xxx_host *p)
 {
-  int i, j;
-  unsigned long cpu_flags = 0;
-  struct aic7xxx_scb *scb;
+  unsigned char status1;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)   
-  DRIVER_LOCK
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+  pci_read_config_byte(p->pdev, PCI_STATUS + 1, &status1);
 #else
-  spin_lock_irqsave(&io_request_lock, cpu_flags);
+  pcibios_read_config_byte(p->pci_bus, p->pci_device_fn,
+                           PCI_STATUS + 1, &status1);
 #endif
-  for(i=0; i<MAX_TARGETS; i++)
-  {
-    if ( (p->dev_timer[i].expires) && 
-          (p->dev_timer[i].expires <= jiffies) )
-    {
-      p->dev_timer[i].expires = 0;
-      if ( (p->dev_timer[i].prev != NULL) ||
-           (p->dev_timer[i].next != NULL) )
-      {
-        del_timer(&p->dev_timer[i]);
-      }
-      p->dev_temp_queue_depth[i] =  p->dev_max_queue_depth[i];
-      j = 0;
-      while ( ((scb = scbq_remove_head(&p->delayed_scbs[i])) != NULL) &&
-              (j++ < p->scb_data->numscbs) )
-      {
-        scbq_insert_tail(&p->waiting_scbs, scb);
-      }
-      if (j == p->scb_data->numscbs)
-      {
-        printk(INFO_LEAD "timer: Yikes, loop in delayed_scbs list.\n",
-          p->host_no, 0, i, -1);
-        scbq_init(&p->delayed_scbs[i]);
-        scbq_init(&p->waiting_scbs);
-        /*
-         * Well, things are screwed now, wait for a reset to clean the junk
+
+  if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Data Parity Error during PCI address or PCI write"
+      "phase.\n", p->host_no, -1, -1, -1);
+  if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Signal System Error Detected\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Data Parity Error has been reported via PCI pin "
+      "PERR#\n", p->host_no, -1, -1, -1);
+  
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+  pci_write_config_byte(p->pdev, PCI_STATUS + 1, status1);
+#else
+  pcibios_write_config_byte(p->pci_bus, p->pci_device_fn,
+                            PCI_STATUS + 1, status1);
+#endif
+  if (status1 & (DPR|RMA|RTA))
+    aic_outb(p,  CLRPARERR, CLRINT);
+
+  if ( (aic7xxx_panic_on_abort) && (p->spurious_int > 500) )
+    aic7xxx_panic_abort(p, NULL);
+
+}
+#endif /* CONFIG_PCI */
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_timer
+ *
+ * Description:
+ *   Take expired extries off of delayed queues and place on waiting queue
+ *   then run waiting queue to start commands.
+ ***************************************************************************/
+static void
+aic7xxx_timer(struct aic7xxx_host *p)
+{
+  int i, j;
+  unsigned long cpu_flags = 0;
+  struct aic7xxx_scb *scb;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)   
+  DRIVER_LOCK
+#else
+  spin_lock_irqsave(&io_request_lock, cpu_flags);
+#endif
+  for(i=0; i<MAX_TARGETS; i++)
+  {
+    if ( (p->dev_timer[i].expires) && 
+          (p->dev_timer[i].expires <= jiffies) )
+    {
+      p->dev_timer[i].expires = 0;
+      if ( (p->dev_timer[i].prev != NULL) ||
+           (p->dev_timer[i].next != NULL) )
+      {
+        del_timer(&p->dev_timer[i]);
+      }
+      p->dev_temp_queue_depth[i] =  p->dev_max_queue_depth[i];
+      j = 0;
+      while ( ((scb = scbq_remove_head(&p->delayed_scbs[i])) != NULL) &&
+              (j++ < p->scb_data->numscbs) )
+      {
+        scbq_insert_tail(&p->waiting_scbs, scb);
+      }
+      if (j == p->scb_data->numscbs)
+      {
+        printk(INFO_LEAD "timer: Yikes, loop in delayed_scbs list.\n",
+          p->host_no, 0, i, -1);
+        scbq_init(&p->delayed_scbs[i]);
+        scbq_init(&p->waiting_scbs);
+        /*
+         * Well, things are screwed now, wait for a reset to clean the junk
          * out.
          */
       }
@@ -3545,12 +4302,11 @@
 aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, int channel)
 {
   unsigned short targ_mask;
-  unsigned char  targ_scratch;
-  int scratch_offset = target;
+  unsigned char tindex = target;
 
-  scratch_offset += channel << 3;
+  tindex |= ((channel & 0x01) << 3);
 
-  targ_mask = (0x01 << scratch_offset);
+  targ_mask = (0x01 << tindex);
   /*
    * Go back to async/narrow transfers and renegotiate.
    */
@@ -3558,9 +4314,9 @@
   p->needwdtr |= (p->needwdtr_copy & targ_mask);
   p->sdtr_pending &= ~targ_mask;
   p->wdtr_pending &= ~targ_mask;
-  targ_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset);
-  targ_scratch &= SXFR;
-  aic_outb(p, targ_scratch, TARG_SCRATCH + scratch_offset);
+  aic_outb(p, 0, TARG_SCSIRATE + tindex);
+  if (p->features & AHC_ULTRA2)
+    aic_outb(p, 0, TARG_OFFSET + tindex);
   aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
   if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
     printk(INFO_LEAD "Bus Device Reset delivered.\n", p->host_no, channel,
@@ -3580,20 +4336,30 @@
 {
   struct aic7xxx_scb *scb;
   unsigned short target_mask;
-  unsigned char target, scratch_offset, lun;
+  unsigned char target, lun, tindex;
   unsigned char queue_flag = FALSE;
   char channel;
 
   target = ((aic_inb(p, SAVED_TCL) >> 4) & 0x0f);
-  channel = (aic_inb(p, SBLKCTL) >> 3) & 0x01;
-  scratch_offset = target + (channel << 3);
+  if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+    channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+  else
+    channel = 0;
+  tindex = target + (channel << 3);
   lun = aic_inb(p, SAVED_TCL) & 0x07;
-  target_mask = (0x01 << scratch_offset);
+  target_mask = (0x01 << tindex);
 
+  /*
+   * Go ahead and clear the SEQINT now, that avoids any interrupt race
+   * conditions later on in case we enable some other interrupt.
+   */
+  aic_outb(p, CLRSEQINT, CLRINT);
   switch (intstat & SEQINT_MASK)
   {
     case NO_MATCH:
       {
+        aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
+                 SCSISEQ);
         printk(WARN_LEAD "No active SCB for reconnecting target - Issuing "
                "BUS DEVICE RESET.\n", p->host_no, channel, target, lun);
         printk(WARN_LEAD "      SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
@@ -3654,12 +4420,11 @@
         p->msg_len = 0;
         p->msg_index = 0;
 
-       /*
-        * We have to clear the SEQINT *BEFORE* we set the REQINIT handler
-        * active or else VLB and edge triggered EISA cards could loose the
-        * first REQINIT and cause a bus hang/reset cycle.
-        */
-        aic_outb(p, CLRSEQINT, CLRINT);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+        if (aic7xxx_verbose > 0xffff)
+          printk(INFO_LEAD "Enabling REQINITs for MSG_IN\n", p->host_no,
+                 channel, target, lun);
+#endif
 
        /*      
         * To actually receive the message, simply turn on
@@ -3682,13 +4447,11 @@
          * or WDTR message for this target. If we did, this is a
          * signal that the target is refusing negotiation.
          */
-        unsigned char targ_scratch;
         unsigned char scb_index;
         unsigned char last_msg;
 
         scb_index = aic_inb(p, SCB_TAG);
         scb = p->scb_data->scb_array[scb_index];
-        targ_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset);
         last_msg = aic_inb(p, LAST_MSG);
 
         if ( (last_msg == MSG_IDENTIFYFLAG) &&
@@ -3696,7 +4459,7 @@
             !(scb->flags & SCB_MSGOUT_BITS) )
         {
           if ((scb->tag_action == MSG_ORDERED_Q_TAG) &&
-              (p->dev_flags[scratch_offset] & DEVICE_TAGGED_SUCCESS))
+              (p->dev_flags[tindex] & DEVICE_TAGGED_SUCCESS))
           {
             /*
              * OK...the device seems able to accept tagged commands, but
@@ -3718,7 +4481,7 @@
             aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
           }
           else if ( (scb->tag_action == MSG_SIMPLE_Q_TAG) &&
-                   !(p->dev_flags[scratch_offset] & DEVICE_TAGGED_SUCCESS) )
+                   !(p->dev_flags[tindex] & DEVICE_TAGGED_SUCCESS) )
           {
             unsigned char i, reset = 0;
             struct aic7xxx_scb *scbp;
@@ -3733,8 +4496,8 @@
              */
             p->tagenable &= ~target_mask;
             p->orderedtag &= ~target_mask;
-            p->dev_max_queue_depth[scratch_offset] =
-               p->dev_temp_queue_depth[scratch_offset] = 1;
+            p->dev_max_queue_depth[tindex] =
+               p->dev_temp_queue_depth[tindex] = 1;
             /*
              * We set this command up as a bus device reset.  However, we have
              * to clear the tag type as it's causing us problems.  We shouldnt
@@ -3783,40 +4546,34 @@
           /*
            * note 8bit xfers and clear flag
            */
-          targ_scratch &= 0x7F;
           p->needwdtr &= ~target_mask;
           p->needwdtr_copy &= ~target_mask;
           p->wdtr_pending &= ~target_mask;
-          if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-               (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
+            (AHC_TRANS_ACTIVE|AHC_TRANS_GOAL|AHC_TRANS_CUR));
+          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+                               AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
+          if ( (p->needsdtr_copy & target_mask) &&
+               !(p->sdtr_pending & target_mask) )
           {
-            printk(INFO_LEAD "Refusing WIDE negotiation; using 8 bit "
-              "transfers.\n", p->host_no, CTL_OF_SCB(scb));
-            p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
-          }
-          scb->flags &= ~SCB_MSGOUT_WDTR_16BIT;
-          p->syncinfo[scratch_offset].offset = MAX_OFFSET_8BIT;
-          if (p->needsdtr_copy & target_mask)
-            p->needsdtr |= target_mask;
+            p->sdtr_pending |= target_mask;
+            scb->flags |= SCB_MSGOUT_SDTR;
+            aic_outb(p, HOST_MSG, MSG_OUT);
+            aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+          }
         }
         else if (scb->flags & SCB_MSGOUT_SDTR)
         {
          /*
           * note asynch xfers and clear flag
           */
-          targ_scratch &= 0xF0;
           p->needsdtr &= ~target_mask;
           p->needsdtr_copy &= ~target_mask;
           p->sdtr_pending &= ~target_mask;
-          p->syncinfo[scratch_offset].period = 0;
-          p->syncinfo[scratch_offset].offset = 0;
-          if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-               (p->dev_flags[scratch_offset] & DEVICE_PRINT_SDTR) )
-          {
-            printk(INFO_LEAD "Refusing synchronous negotiation; using "
-              "asynchronous transfers.\n", p->host_no, CTL_OF_SCB(scb));
-            p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_SDTR;
-          }
+          scb->flags &= ~SCB_MSGOUT_SDTR;
+          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+            (AHC_TRANS_CUR|AHC_TRANS_ACTIVE|AHC_TRANS_GOAL));
         }
         else if (aic7xxx_verbose & VERBOSE_SEQINT)
         {
@@ -3826,8 +4583,6 @@
           printk(INFO_LEAD "Received MESSAGE_REJECT for unknown cause.  "
             "Ignoring.\n", p->host_no, channel, target, lun);
         }
-        aic_outb(p, targ_scratch, TARG_SCRATCH + scratch_offset);
-        aic_outb(p, targ_scratch, SCSIRATE);
       }
       break;
 
@@ -3842,11 +4597,15 @@
          * the sequencer running in the common case of command completes
          * without error.  The sequencer will have DMA'd the SCB back
          * up to us, so we can reference the drivers SCB array.
+         *
+         * Set the default return value to 0 indicating not to send
+         * sense.  The sense code will change this if needed and this
+         * reduces code duplication.
          */
+        aic_outb(p, 0, RETURN_1);
         scb_index = aic_inb(p, SCB_TAG);
         if (scb_index > p->scb_data->numscbs)
         {
-          aic_outb(p, 0, RETURN_1);
           printk(WARN_LEAD "Invalid SCB during SEQINT 0x%02x, SCB_TAG %d.\n",
             p->host_no, channel, target, lun, intstat, scb_index);
           break;
@@ -3854,12 +4613,6 @@
         scb = p->scb_data->scb_array[scb_index];
         hscb = scb->hscb;
 
-        /*
-         * Set the default return value to 0 indicating not to send
-         * sense.  The sense code will change this if needed and this
-         * reduces code duplication.
-         */
-        aic_outb(p, 0, RETURN_1);
         if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
         {
           printk(WARN_LEAD "Invalid SCB during SEQINT 0x%x, scb %d, flags 0x%x,"
@@ -3928,7 +4681,6 @@
 
                 scb->sg_count = hscb->SG_segment_count = 1;
                 scb->sg_length = sizeof(cmd->sense_buffer);
-                scb->flags &= ~SCB_MSGOUT_BITS;
                 scb->tag_action = 0;
                 /*
                  * This problem could be caused if the target has lost power
@@ -3936,12 +4688,67 @@
                  * so if needed, we'll re-negotiate while doing the sense cmd.
                  * However, if this SCB already was attempting to negotiate,
                  * then we assume this isn't the problem and skip this part.
-                 *
-                 * 1998/04/23 - We also don't want to set the flag if the
-                 *   original command was a TEST_UNIT_READY since that
-                 *   implies a SEND_SENSE anyway.
                  */
-                if (scb->cmd->cmnd[0] != TEST_UNIT_READY)
+#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
+                if ( (scb->cmd->cmnd[0] != TEST_UNIT_READY) &&
+                     (p->dev_flags[tindex] & DEVICE_SCANNED) &&
+                     !(p->wdtr_pending & target_mask) && 
+                     !(p->sdtr_pending & target_mask) )
+                {
+                  p->needwdtr |= (p->needwdtr_copy & target_mask);
+                  p->needsdtr |= (p->needsdtr_copy & target_mask);
+                }
+                else if ( (scb->cmd == p->dev_wdtr_cmnd[tindex]) ||
+                          (scb->cmd == p->dev_sdtr_cmnd[tindex]) )
+                {
+                  /*
+                   * This is already a negotiation command, so we must have
+                   * already done either WDTR or SDTR (or maybe both).  So
+                   * we simply check sdtr_pending and needsdtr to see if we
+                   * should throw out SDTR on this command.
+                   *
+                   * Note: Don't check the needsdtr_copy here, instead just
+                   * check to see if WDTR wiped out our SDTR and set needsdtr.
+                   * Even if WDTR did wipe out SDTR and set needsdtr, if
+                   * parse_msg() then turned around and started our SDTR
+                   * in back to back fasion, then conclusion of that should
+                   * have negated any needsdtr setting.  That's why we only
+                   * check needsdtr and sdtr_pending.
+                   */
+                  scb->flags &= ~SCB_MSGOUT_BITS;
+                  if ( (scb->cmd == p->dev_wdtr_cmnd[tindex]) &&
+                       !(p->sdtr_pending & target_mask) &&
+                       (p->needsdtr & target_mask) )
+                  {
+                    p->sdtr_pending |= target_mask;
+                    hscb->control |= MK_MESSAGE;
+                    scb->flags |= SCB_MSGOUT_SDTR;
+                  }
+
+                  /*
+                   * This is the important part though.  We are getting sense
+                   * info back from this device.  It's going into a fake
+                   * command.  We need to put that into the real command
+                   * instead so that the mid level SCSI code can act upon it.
+                   * So, when we set up these fake commands, the next pointer
+                   * is used to point to the real command.  Use that to change
+                   * the address of our sense_buffer[] to the real command.
+                   * However, don't do this if the real command is also a
+                   * TEST_UNIT_READY as it will most likely pull down its own
+                   * SENSE information anyway.
+                   */
+                  if (cmd->next->cmnd[0] != TEST_UNIT_READY)
+                  {
+                    scb->sg_list[0].address = 
+                      cpu_to_le32(VIRT_TO_BUS(&cmd->next->sense_buffer[0]));
+                    hscb->data_pointer = scb->sg_list[0].address;
+                  }
+                }
+#else
+                if (  (scb->cmd->cmnd[0] != TEST_UNIT_READY) &&
+                     !(scb->flags & SCB_MSGOUT_BITS) && 
+                      (scb->cmd->lun == 0) &&
+                      (p->dev_flags[TARGET_INDEX(scb->cmd)] & DEVICE_SCANNED) )
                 {
                   if ( (p->needwdtr_copy & target_mask) &&
                       !(p->wdtr_pending & target_mask) &&
@@ -3950,7 +4757,7 @@
                     p->needwdtr |= target_mask;
                     p->wdtr_pending |= target_mask;
                     hscb->control |= MK_MESSAGE;
-                    scb->flags |= SCB_MSGOUT_WDTR_16BIT;
+                    scb->flags |= SCB_MSGOUT_WDTR;
                   }
                   if ( p->needsdtr_copy & target_mask )
                   {
@@ -3964,12 +4771,26 @@
                     }
                   }
                 }
-
+                else
+                  scb->flags &= ~SCB_MSGOUT_BITS;
+#endif /* AIC7XXX_FAKE_NEGOTIATION_CMDS */
                 scb->flags |= SCB_SENSE;
                 /*
                  * Ensure the target is busy since this will be an
                  * an untagged request.
                  */
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+                if (aic7xxx_verbose > 0xffff)
+                {
+                  if (scb->flags & SCB_MSGOUT_BITS)
+                    printk(INFO_LEAD "Requesting SENSE with %s\n", p->host_no,
+                           CTL_OF_SCB(scb), (scb->flags & SCB_MSGOUT_SDTR) ?
+                           "SDTR" : "WDTR");
+                  else
+                    printk(INFO_LEAD "Requesting SENSE, no MSG\n", p->host_no,
+                           CTL_OF_SCB(scb));
+                }
+#endif
                 aic7xxx_busy_target(p, scb);
                 aic_outb(p, SEND_SENSE, RETURN_1);
                 aic7xxx_error(cmd) = DID_OK;
@@ -3994,7 +4815,7 @@
                */
               aic7xxx_search_qinfifo(p, target, channel, lun,
                 SCB_LIST_NULL, 0, TRUE,
-                &p->delayed_scbs[scratch_offset]);
+                &p->delayed_scbs[tindex]);
               next_scbp = p->waiting_scbs.head;
               while ( next_scbp != NULL )
               {
@@ -4004,7 +4825,7 @@
                      SCB_LIST_NULL) )
                 {
                   scbq_remove(&p->waiting_scbs, prev_scbp);
-                  scbq_insert_tail(&p->delayed_scbs[scratch_offset],
+                  scbq_insert_tail(&p->delayed_scbs[tindex],
                     prev_scbp);
                 }
               }
@@ -4024,15 +4845,15 @@
                   {
                     if (next_scbp->flags & SCB_WAITINGQ)
                     {
-                      p->dev_active_cmds[scratch_offset]++;
+                      p->dev_active_cmds[tindex]++;
                       p->activescbs--;
-                      scbq_remove(&p->delayed_scbs[scratch_offset], next_scbp);
+                      scbq_remove(&p->delayed_scbs[tindex], next_scbp);
                       scbq_remove(&p->waiting_scbs, next_scbp);
                     }
-                    scbq_insert_head(&p->delayed_scbs[scratch_offset],
+                    scbq_insert_head(&p->delayed_scbs[tindex],
                       next_scbp);
                     next_scbp->flags |= SCB_WAITINGQ;
-                    p->dev_active_cmds[scratch_offset]--;
+                    p->dev_active_cmds[tindex]--;
                     p->activescbs--;
                     next_hscb = aic_inb(p, SCB_NEXT);
                     aic_outb(p, 0, SCB_CONTROL);
@@ -4065,65 +4886,67 @@
               aic_outb(p, active_hscb, SCBPTR);
               if (scb->flags & SCB_WAITINGQ)
               {
-                scbq_remove(&p->delayed_scbs[scratch_offset], scb);
+                scbq_remove(&p->delayed_scbs[tindex], scb);
                 scbq_remove(&p->waiting_scbs, scb);
-                p->dev_active_cmds[scratch_offset]++;
+                p->dev_active_cmds[tindex]++;
                 p->activescbs++;
               }
-              scbq_insert_head(&p->delayed_scbs[scratch_offset], scb);
-              p->dev_active_cmds[scratch_offset]--;
+              scbq_insert_head(&p->delayed_scbs[tindex], scb);
+              p->dev_active_cmds[tindex]--;
               p->activescbs--;
               scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY;
                   
-              if (p->dev_timer[scratch_offset].expires == 0) 
+              if (p->dev_timer[tindex].expires == 0) 
               {
-                if ( p->dev_active_cmds[scratch_offset] )
+                if ( p->dev_active_cmds[tindex] )
                 {
-                  p->dev_timer[scratch_offset].expires = jiffies + (HZ * 2);
-                  add_timer(&p->dev_timer[scratch_offset]);
+                  p->dev_timer[tindex].expires = jiffies + (HZ * 2);
+                  add_timer(&p->dev_timer[tindex]);
                 }
                 else
                 {
-                  p->dev_timer[scratch_offset].expires = jiffies + (HZ / 2);
-                  add_timer(&p->dev_timer[scratch_offset]);
+                  p->dev_timer[tindex].expires = jiffies + (HZ / 2);
+                  add_timer(&p->dev_timer[tindex]);
                 }
               }
-              if (aic7xxx_verbose & VERBOSE_QUEUE_FULL)
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+              if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
               {
                 if (queue_flag)
                   printk(INFO_LEAD "Queue full received; queue depth %d, "
                     "active %d\n", p->host_no, CTL_OF_SCB(scb),
-                    p->dev_max_queue_depth[scratch_offset],
-                    p->dev_active_cmds[scratch_offset]);
+                    p->dev_max_queue_depth[tindex],
+                    p->dev_active_cmds[tindex]);
                 else
                   printk(INFO_LEAD "Target busy\n", p->host_no, CTL_OF_SCB(scb));
 
               }
+#endif
               if (queue_flag)
               {
-                p->dev_temp_queue_depth[scratch_offset] = 
-                  p->dev_active_cmds[scratch_offset];
-                if ( p->dev_last_queue_full[scratch_offset] !=
-                     p->dev_active_cmds[scratch_offset] )
+                p->dev_temp_queue_depth[tindex] = 
+                  p->dev_active_cmds[tindex];
+                if ( p->dev_last_queue_full[tindex] !=
+                     p->dev_active_cmds[tindex] )
                 {
-                  p->dev_last_queue_full[scratch_offset] = 
-                      p->dev_active_cmds[scratch_offset];
-                  p->dev_last_queue_full_count[scratch_offset] = 0;
+                  p->dev_last_queue_full[tindex] = 
+                      p->dev_active_cmds[tindex];
+                  p->dev_last_queue_full_count[tindex] = 0;
                 }
                 else
                 {
-                  p->dev_last_queue_full_count[scratch_offset]++;
+                  p->dev_last_queue_full_count[tindex]++;
                 }
-                if ( (p->dev_last_queue_full_count[scratch_offset] > 14) &&
-                     (p->dev_active_cmds[scratch_offset] > 4) )
+                if ( (p->dev_last_queue_full_count[tindex] > 14) &&
+                     (p->dev_active_cmds[tindex] > 4) )
                 {
-                  if (aic7xxx_verbose & VERBOSE_NEGOTIATION)
+                  if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
                     printk(INFO_LEAD "Queue depth reduced to %d\n", p->host_no,
-                      CTL_OF_SCB(scb), p->dev_active_cmds[scratch_offset]);
-                  p->dev_max_queue_depth[scratch_offset] = 
-                      p->dev_active_cmds[scratch_offset];
-                  p->dev_last_queue_full[scratch_offset] = 0;
-                  p->dev_last_queue_full_count[scratch_offset] = 0;
+                      CTL_OF_SCB(scb), p->dev_active_cmds[tindex]);
+                  p->dev_max_queue_depth[tindex] = 
+                      p->dev_active_cmds[tindex];
+                  p->dev_last_queue_full[tindex] = 0;
+                  p->dev_last_queue_full_count[tindex] = 0;
                 }
               }
               break;
@@ -4157,6 +4980,15 @@
          * this target.
          */
 
+        if ( !(scb->flags & SCB_DEVICE_RESET) &&
+              (aic_inb(p, MSG_OUT) == MSG_IDENTIFYFLAG) &&
+              (scb->hscb->control & TAG_ENB) )
+        {
+          p->msg_buf[p->msg_index++] = scb->tag_action;
+          p->msg_buf[p->msg_index++] = scb->hscb->tag;
+          p->msg_len += 2;
+        }
+
         if (scb->flags & SCB_DEVICE_RESET)
         {
           p->msg_buf[p->msg_index++] = MSG_BUS_DEV_RESET;
@@ -4169,12 +5001,6 @@
         {
           if (scb->tag_action)
           {
-            if (msg_out == MSG_IDENTIFYFLAG)
-            {
-              p->msg_buf[p->msg_index++] = scb->tag_action;
-              p->msg_buf[p->msg_index++] = scb->hscb->tag;
-	      p->msg_len += 2;
-	    }
             p->msg_buf[p->msg_index++] = MSG_ABORT_TAG;
           }
           else
@@ -4188,23 +5014,67 @@
         }
         else if (scb->flags & SCB_MSGOUT_WDTR)
         {
-          aic7xxx_construct_wdtr(p, (scb->flags & SCB_WDTR_16BIT));
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+          if (aic7xxx_verbose > 0xffff)
+            printk(INFO_LEAD "Sending WDTR message.\n", p->host_no,
+                   CTL_OF_SCB(scb));
+#endif
+          aic7xxx_construct_wdtr(p,
+            p->transinfo[TARGET_INDEX(scb->cmd)].goal_width);
         }
         else if (scb->flags & SCB_MSGOUT_SDTR)
         {
-          unsigned char period, offset;
-
+          unsigned int max_sync, period;
+          /*
+           * We need to set an accurate goal_offset instead of
+           * the ridiculously high one we default to.  We should
+           * now know if we are wide.  Plus, the WDTR code will 
+           * set our goal_offset for us as well.
+           */
+          if (p->transinfo[tindex].goal_offset)
+          {
+            if (p->features & AHC_ULTRA2)
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+            else if (p->transinfo[tindex].cur_width == MSG_EXT_WDTR_BUS_16_BIT)
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+            else
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+          }
           /*
-           * Pull the user defined setting from scratch RAM.
+           * Now that the device is selected, use the bits in SBLKCTL and
+           * SSTAT2 to determine the max sync rate for this device.
            */
-          period = p->syncinfo[scratch_offset].period;
-          offset = p->syncinfo[scratch_offset].offset;
-          if ( (p->needsdtr_copy & target_mask) == 0)
+          if (p->features & AHC_ULTRA2)
+          {
+            if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+                !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
+            {
+              max_sync = AHC_SYNCRATE_ULTRA2;
+            }
+            else
+            {
+              max_sync = AHC_SYNCRATE_ULTRA;
+            }
+          }
+          else if (p->features & AHC_ULTRA)
           {
-            period = 0;
-            offset = 0;
+            max_sync = AHC_SYNCRATE_ULTRA;
           }
-          aic7xxx_construct_sdtr(p, period, offset);
+          else
+          {
+            max_sync = AHC_SYNCRATE_FAST;
+          }
+          period = p->transinfo[tindex].goal_period;
+          aic7xxx_find_syncrate(p, &period, max_sync);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+          if (aic7xxx_verbose > 0xffff)
+            printk(INFO_LEAD "Sending SDTR %d/%d message.\n", p->host_no,
+                   CTL_OF_SCB(scb),
+                   p->transinfo[tindex].goal_period,
+                   p->transinfo[tindex].goal_offset);
+#endif
+          aic7xxx_construct_sdtr(p, period,
+            p->transinfo[tindex].goal_offset);
         }
         else 
         {
@@ -4224,7 +5094,7 @@
          * have this problem since they continually interrupt the kernel
          * until we take care of the situation.
          */
-        aic_outb(p, CLRSEQINT, CLRINT);
+        scb->flags |= SCB_MSGOUT_SENT;
         p->msg_index = 0;
         p->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
         p->flags |= AHC_HANDLING_REQINITS;
@@ -4273,6 +5143,7 @@
       }
       break;
 
+#if AIC7XXX_NOT_YET 
     case TRACEPOINT:
       {
         printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no, channel,
@@ -4287,7 +5158,6 @@
       }
       break;
 
-#if AIC7XXX_NOT_YET 
     /* XXX Fill these in later */
     case MSG_BUFFER_BUSY:
       printk("aic7xxx: Message buffer busy.\n");
@@ -4307,7 +5177,6 @@
   /*
    * Clear the sequencer interrupt and unpause the sequencer.
    */
-  aic_outb(p, CLRSEQINT, CLRINT);
   unpause_sequencer(p, /* unpause always */ TRUE);
 }
 
@@ -4322,14 +5191,18 @@
 static int
 aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  int reject, done;
-  unsigned char target_scratch, scratch_offset;
+  int reject, reply, done;
+  unsigned char target_scsirate, tindex;
   unsigned short target_mask;
+  unsigned char target, channel, lun;
 
-  reject = done = FALSE;
-  scratch_offset = TARGET_INDEX(scb->cmd);
-  target_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset);
-  target_mask = (0x01 << scratch_offset);
+  target = scb->cmd->target;
+  channel = scb->cmd->channel;
+  lun = scb->cmd->lun;
+  reply = reject = done = FALSE;
+  tindex = TARGET_INDEX(scb->cmd);
+  target_scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+  target_mask = (0x01 << tindex);
 
   /*
    * Parse as much of the message as is availible,
@@ -4355,9 +5228,10 @@
     {
       case MSG_EXT_SDTR:
       {
-        unsigned char period, response_period, offset;
-        unsigned char max_offset, saved_offset, rate;
-
+        unsigned int period, offset;
+        unsigned char maxsync, saved_offset;
+        struct aic7xxx_syncrate *syncrate;
+        
         if (p->msg_buf[1] != MSG_EXT_SDTR_LEN)
         {
           reject = TRUE;
@@ -4370,51 +5244,114 @@
         }
 
         period = p->msg_buf[3];
-        saved_offset = p->msg_buf[4];
+        saved_offset = offset = p->msg_buf[4];
 
-        if (target_scratch & WIDEXFER)
+        if (p->features & AHC_ULTRA2)
+        {
+          if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+               !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
+          {
+            maxsync = AHC_SYNCRATE_ULTRA2;
+          }
+          else
+          {
+            maxsync = AHC_SYNCRATE_ULTRA;
+          }
+        }
+        else if (p->features & AHC_ULTRA)
         {
-          max_offset = MAX_OFFSET_16BIT;
+          maxsync = AHC_SYNCRATE_ULTRA;
         }
         else
         {
-          max_offset = MAX_OFFSET_8BIT;
+          maxsync = AHC_SYNCRATE_FAST;
         }
-        offset = MIN(saved_offset, max_offset);
-        response_period = aic7xxx_scsirate(p, &rate, &period,
-          &offset, scb->cmd->target, scb->cmd->channel, /* set */ TRUE);
-        /* Preserve the WideXfer flag */
-        target_scratch = rate | (target_scratch & WIDEXFER);
-
-        /*
-         * Update the TARGET_SCRATCH, the SCSIRATE, and our syncinfo
-         * areas.
-         */
-        aic_outb(p, target_scratch, TARG_SCRATCH + scratch_offset);
-        aic_outb(p, target_scratch, SCSIRATE);
-        p->syncinfo[scratch_offset].period = response_period;
-        p->syncinfo[scratch_offset].offset = offset;
+  
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+        if (aic7xxx_verbose > 0xffff)
+        {
+          printk(INFO_LEAD "Finished receipt of SDTR, parsing %d/%d\n",
+                 p->host_no, CTL_OF_SCB(scb), period, offset);
+          syncrate = aic7xxx_find_syncrate(p, &period, maxsync);
+          printk(INFO_LEAD "After find_syncrate() %d/%d\n",
+                 p->host_no, CTL_OF_SCB(scb), period, offset);
+          aic7xxx_validate_offset(p, syncrate, &offset,
+                                  target_scsirate & WIDEXFER);
+          printk(INFO_LEAD "After validate_offset() %d/%d\n",
+                 p->host_no, CTL_OF_SCB(scb), period, offset);
+          aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+                               offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+          printk(INFO_LEAD "Final values of Period/Offset as set: %d/%d\n",
+                 p->host_no, CTL_OF_SCB(scb), period, offset);
+        }
+        else
+        {
+          syncrate = aic7xxx_find_syncrate(p, &period, maxsync);
+          aic7xxx_validate_offset(p, syncrate, &offset,
+                                  target_scsirate & WIDEXFER);
+          aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+                               offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+        }
+#else
+        syncrate = aic7xxx_find_syncrate(p, &period, maxsync);
+        aic7xxx_validate_offset(p, syncrate, &offset,
+                                target_scsirate & WIDEXFER);
+        aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+                             offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+#endif
 
+        if (offset == 0)
+        {
+          /*
+           * Uhh ohh, things fell through to async....update the goal
+           * items and the needsdtr_copy to reflect this...
+           */
+          aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+                 offset, AHC_TRANS_GOAL|AHC_TRANS_QUITE);
+          p->needsdtr_copy &= ~target_mask;
+        }
         /*
          * Did we start this, if not, or if we went to low and had to
          * go async, then send an SDTR back to the target
          */
         p->needsdtr &= ~target_mask;
-        if (scb->flags & SCB_MSGOUT_SDTR)
+        p->sdtr_pending &= ~target_mask;
+        if ( ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) ==
+              (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) &&
+             (offset == saved_offset) )
         {
-          if (saved_offset != offset)
-          {
-            p->needsdtr_copy &= ~target_mask;
-            reject = TRUE; 
-          }
-          scb->flags &= ~SCB_MSGOUT_SDTR;
-          p->sdtr_pending &= ~target_mask;
+          scb->flags &= ~SCB_MSGOUT_BITS;
         }
         else
         {
+          /*
+           * Send a reply SDTR back.  Even if we sent the first one, it
+           * is valid to send another one out immediately to re-negotiate
+           * things, and a few devices don't like getting rejects after
+           * we already sent them one SDTR.  Just send an SDTR for async
+           * this time if need be (or for the correct params if we didn't
+           * start all of this).  If this is a Reject Reply type message,
+           * then we've put the async settings into the goal area for
+           * future reference (when we get the AWAITING_MSG interrupt).
+           * If this is a case where we are responding to the target's
+           * initiated SDTR, then leave our own goal and user values in
+           * place (unless the device hasn't been scanned yet, in which
+           * case, put the user values into the goal values so we don't
+           * send out an Async message).
+           */
+          if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) )
+          {
+            p->transinfo[tindex].goal_width =
+              p->transinfo[tindex].user_width;
+            p->transinfo[tindex].goal_period =
+              p->transinfo[tindex].user_period;
+            p->transinfo[tindex].goal_offset =
+              p->transinfo[tindex].user_offset;
+            p->needwdtr_copy |= target_mask;
+            p->needsdtr_copy |= target_mask;
+          }
           scb->flags &= ~SCB_MSGOUT_BITS;
           scb->flags |= SCB_MSGOUT_SDTR;
-          p->sdtr_pending |= target_mask;
           aic_outb(p, HOST_MSG, MSG_OUT);
           aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
         }
@@ -4437,112 +5374,111 @@
         }
 
         bus_width = p->msg_buf[3];
-        p->needwdtr &= ~target_mask;
-        if (scb->flags & SCB_MSGOUT_WDTR)
+        if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR)) ==
+             (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR) )
         {
           switch(bus_width)
           {
             default:
             {
               reject = TRUE;
-              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-                   (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
+              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+                   ((p->dev_flags[tindex] & DEVICE_PRINT_WDTR) ||
+                    (aic7xxx_verbose > 0xffff)) )
               {
                 printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
                   p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
-                p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
+                p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
               }
             } /* We fall through on purpose */
             case MSG_EXT_WDTR_BUS_8_BIT:
             {
               bus_width = MSG_EXT_WDTR_BUS_8_BIT;
               p->needwdtr_copy &= ~target_mask;
-              target_scratch &= 0x7f;
-              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-                   (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
-              {
-                printk(INFO_LEAD "Using narrow (8 bit) transfers.\n",
-                  p->host_no, CTL_OF_SCB(scb));
-                p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
-              }
               break;
             }
             case MSG_EXT_WDTR_BUS_16_BIT:
             {
-              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-                   (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
-              {
-                printk(INFO_LEAD "Using wide (16 bit) transfers.\n",
-                  p->host_no, CTL_OF_SCB(scb));
-                p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
-              }
-              target_scratch |= WIDEXFER;
               break;
             }
           }
-          scb->flags &= ~SCB_MSGOUT_WDTR_16BIT;
+          scb->flags &= ~SCB_MSGOUT_BITS;
           p->wdtr_pending &= ~target_mask;
-          /*
-           * By virtue of the SCSI spec, a WDTR message negates any existing
-           * SDTR negotiations.  So, even if needsdtr isn't marked for this
-           * device, we still have to do a new SDTR message if the device
-           * supports SDTR at all.  Therefore, we check needsdtr_copy instead
-           * of needstr.
-           */
-          if ( (p->needsdtr_copy & target_mask) &&
-              !(p->sdtr_pending & target_mask))
-          {
-            p->needsdtr |= target_mask;
-          }
+          p->needwdtr &= ~target_mask;
         }
         else
         {
           scb->flags &= ~SCB_MSGOUT_BITS;
+          scb->flags |= SCB_MSGOUT_WDTR;
+          reply = TRUE;
+          if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) )
+          {
+            /* 
+             * Well, we now know the WDTR and SYNC caps of this device since
+             * it contacted us first, mark it as such and copy the user stuff
+             * over to the goal stuff.
+             */
+            p->transinfo[tindex].goal_width =
+              p->transinfo[tindex].user_width;
+            p->transinfo[tindex].goal_period =
+              p->transinfo[tindex].user_period;
+            p->transinfo[tindex].goal_offset =
+              p->transinfo[tindex].user_offset;
+            p->needwdtr_copy |= target_mask;
+            p->needsdtr_copy |= target_mask;
+          }
           switch(bus_width)
           {
             default:
             {
-              if (p->type & AHC_WIDE)
+              if ( (p->features & AHC_WIDE) &&
+                   (p->transinfo[tindex].goal_width ==
+                    MSG_EXT_WDTR_BUS_16_BIT) )
               {
                 bus_width = MSG_EXT_WDTR_BUS_16_BIT;
-                p->needwdtr_copy |= target_mask;
-                scb->flags |= SCB_MSGOUT_WDTR_16BIT;
-                target_scratch |= WIDEXFER;
-                if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-                     (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
-                {
-                  printk(INFO_LEAD "Using wide (16 bit) transfers.\n",
-                    p->host_no, CTL_OF_SCB(scb));
-                  p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
-                }
                 break;
               }
             } /* Fall through if we aren't a wide card */
             case MSG_EXT_WDTR_BUS_8_BIT:
             {
-              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
               p->needwdtr_copy &= ~target_mask;
-              scb->flags |= SCB_MSGOUT_WDTR_8BIT;
-              target_scratch &= 0x7f;
-              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-                   (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
-              {
-                printk(INFO_LEAD "Using narrow (8 bit) transfers.\n",
-                  p->host_no, CTL_OF_SCB(scb));
-                p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
-              }
+              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
               break;
             }
           }
+          p->needwdtr &= ~target_mask;
+          p->wdtr_pending &= ~target_mask;
           aic_outb(p, HOST_MSG, MSG_OUT);
           aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
-          p->wdtr_pending |= target_mask;
         }
-        aic_outb(p, target_scratch, SCSIRATE);
-        aic_outb(p, target_scratch, TARG_SCRATCH + scratch_offset);
-        p->syncinfo[scratch_offset].offset =
-          (bus_width == MSG_EXT_WDTR_BUS_8_BIT) ? 
-          MAX_OFFSET_8BIT : MAX_OFFSET_16BIT;
+        aic7xxx_set_width(p, target, channel, lun, bus_width,
+                          AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+        
+        /*
+         * By virtue of the SCSI spec, a WDTR message negates any existing
+         * SDTR negotiations.  So, even if needsdtr isn't marked for this
+         * device, we still have to do a new SDTR message if the device
+         * supports SDTR at all.  Therefore, we check needsdtr_copy instead
+         * of needstr.
+         */
+        aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+                             AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
+        if ( (p->needsdtr_copy & target_mask) &&
+            !(p->sdtr_pending & target_mask))
+        {
+          p->needsdtr |= target_mask;
+          if ( !reject && !reply )
+          {
+            scb->flags &= ~SCB_MSGOUT_WDTR;
+            if (p->transinfo[tindex].goal_period)
+            {
+              p->sdtr_pending |= target_mask;
+              scb->flags |= SCB_MSGOUT_SDTR;
+              aic_outb(p, HOST_MSG, MSG_OUT);
+              aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+            }
+          }
+        }
         done = TRUE;
         break;
       }
@@ -4558,10 +5494,12 @@
   {
     aic_outb(p, MSG_MESSAGE_REJECT, MSG_OUT);
     aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+    done = TRUE;
   }
   return(done);
 }
 
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_handle_reqinit
@@ -4605,10 +5543,20 @@
           {
             aic_outb(p, p->msg_buf[p->msg_index], SINDEX);
             aic_outb(p, 0, RETURN_1);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+            if (aic7xxx_verbose > 0xffff)
+              printk(INFO_LEAD "Completed sending of REQINIT message.\n",
+                     p->host_no, CTL_OF_SCB(scb));
+#endif
           }
           else
           {
             aic_outb(p, MSGOUT_PHASEMIS, RETURN_1);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+            if (aic7xxx_verbose > 0xffff)
+              printk(INFO_LEAD "PHASEMIS while sending REQINIT message.\n",
+                     p->host_no, CTL_OF_SCB(scb));
+#endif
           }
           unpause_sequencer(p, TRUE);
         }
@@ -4642,12 +5590,23 @@
         }
         if (phasemis || done)
         {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+          if (aic7xxx_verbose > 0xffff)
+          {
+            if (phasemis)
+              printk(INFO_LEAD "PHASEMIS while receiving REQINIT message.\n",
+                     p->host_no, CTL_OF_SCB(scb));
+            else
+              printk(INFO_LEAD "Completed receipt of REQINIT message.\n",
+                     p->host_no, CTL_OF_SCB(scb));
+          }
+#endif
           /* Time to end our message session */
           p->msg_len = 0;
           p->msg_type = MSG_TYPE_NONE;
-          p->flags &= ~AHC_HANDLING_REQINITS;
           aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
           aic_outb(p, CLRSCSIINT, CLRINT);
+          p->flags &= ~AHC_HANDLING_REQINITS;
           unpause_sequencer(p, TRUE);
         }
         break;
@@ -4691,30 +5650,14 @@
   }
 
 
-  if ( (p->flags & AHC_HANDLING_REQINITS) && (status & REQINIT) )
-  {
-    if (scb)
-    {
-      aic7xxx_handle_reqinit(p, scb);
-    }
-    else
-    {
-      p->flags &= ~AHC_HANDLING_REQINITS;
-      aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
-      aic_outb(p, CLRREQINIT, CLRSINT1);
-      aic_outb(p, CLRSCSIINT, CLRINT);
-      p->msg_type = MSG_TYPE_NONE;
-      p->msg_index = 0;
-      p->msg_len = 0;
-    }
-    return;
-  }
-
   if ((status & SCSIRSTI) != 0)
   {
     int channel;
 
-    channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+    if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+      channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+    else
+      channel = 0;
 
     if (aic7xxx_verbose & VERBOSE_RESET)
       printk(WARN_LEAD "Someone else reset the channel!!\n",
@@ -4737,10 +5680,16 @@
     unsigned char lastphase = aic_inb(p, LASTPHASE);
     unsigned char saved_tcl = aic_inb(p, SAVED_TCL);
     unsigned char target = (saved_tcl >> 4) & 0x0F;
-    int channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+    int channel;
     int printerror = TRUE;
 
-    aic_outb(p, 0, SCSISEQ);
+    if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+      channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+    else
+      channel = 0;
+
+    aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
+             SCSISEQ);
     if (lastphase == P_MESGOUT)
     {
       unsigned char message;
@@ -4782,20 +5731,16 @@
         aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag);
         aic7xxx_run_done_queue(p, FALSE);
       }
-      else
-      {  /* Since we don't really know what happened here, we'll wait */
-         /* for the commands to timeout and get aborted if need be    */
-        aic7xxx_add_curscb_to_free_list(p);
-      }
       printk(INFO_LEAD "Unexpected busfree, LASTPHASE = 0x%x, "
              "SEQADDR = 0x%x\n", p->host_no, channel, target, -1, lastphase,
              (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
       scb = NULL;
     }
+    aic_outb(p, MSG_NOOP, MSG_OUT);
     aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
       SIMODE1);
     p->flags &= ~AHC_HANDLING_REQINITS;
-    aic_outb(p, CLRBUSFREE | CLRREQINIT, CLRSINT1);
+    aic_outb(p, CLRBUSFREE, CLRSINT1);
     aic_outb(p, CLRSCSIINT, CLRINT);
     restart_sequencer(p);
     unpause_sequencer(p, TRUE);
@@ -4807,6 +5752,24 @@
     Scsi_Cmnd *cmd;
 
     scbptr = aic_inb(p, WAITING_SCBH);
+    if (scbptr > p->scb_data->maxhscbs)
+    {
+      /*
+       * I'm still trying to track down exactly how this happens, but until
+       * I find it, this code will make sure we aren't passing bogus values
+       * into the SCBPTR register, even if that register will just wrap
+       * things around, we still don't like having out of range variables.
+       *
+       * NOTE: Don't check the aic7xxx_verbose variable, I want this message
+       * to always be displayed.
+       */
+      printk(INFO_LEAD "Invalid WAITING_SCBH value %d, improvising.\n",
+             p->host_no, -1, -1, -1, scbptr);
+      if (p->scb_data->maxhscbs > 4)
+        scbptr &= (p->scb_data->maxhscbs - 1);
+      else
+        scbptr &= 0x03;
+    }
     aic_outb(p, scbptr, SCBPTR);
     scb_index = aic_inb(p, SCB_TAG);
 
@@ -4827,6 +5790,8 @@
              "SSTAT1 = 0x%x\n", aic_inb(p, SCSISEQ),
              aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
              aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
+      if (aic7xxx_panic_on_abort)
+        aic7xxx_panic_abort(p, NULL);
     }
     else
     {
@@ -4863,6 +5828,10 @@
        * What we need to do then is to let the command timeout again so
        * we get a reset since this abort just failed.
        */
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
+        printk(INFO_LEAD "Selection Timeout.\n", p->host_no, CTL_OF_SCB(scb));
+#endif
       if (p->flags & SCB_QUEUED_ABORT)
       {
         cmd->result = 0;
@@ -4875,9 +5844,9 @@
      * are allowed to reselect in.
      */
     aic_outb(p, 0, SCSISEQ);
-    aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+    aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1);
     p->flags &= ~AHC_HANDLING_REQINITS;
-    aic_outb(p, CLRSELTIMEO | CLRBUSFREE | CLRREQINIT, CLRSINT1);
+    aic_outb(p, CLRSELTIMEO | CLRBUSFREE, CLRSINT1);
     aic_outb(p, CLRSCSIINT, CLRINT);
     restart_sequencer(p);
     unpause_sequencer(p, TRUE);
@@ -4942,7 +5911,7 @@
      * A parity error has occurred during a data
      * transfer phase. Flag it and continue.
      */
-    printk(WARN_LEAD "Parity error during phase %s.\n",
+    printk(WARN_LEAD "Parity error during %s phase.\n",
            p->host_no, CTL_OF_SCB(scb), phase);
 
     /*
@@ -4960,6 +5929,17 @@
     aic_outb(p, CLRSCSIINT, CLRINT);
     unpause_sequencer(p, /* unpause_always */ TRUE);
   }
+  else if ( (status & REQINIT) &&
+            (p->flags & AHC_HANDLING_REQINITS) )
+  {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+    if (aic7xxx_verbose > 0xffff)
+      printk(INFO_LEAD "Handling REQINIT, SSTAT1=0x%x.\n", p->host_no,
+             CTL_OF_SCB(scb), aic_inb(p, SSTAT1));
+#endif
+    aic7xxx_handle_reqinit(p, scb);
+    return;
+  }
   else
   {
     /*
@@ -4980,66 +5960,151 @@
   }
 }
 
-#ifdef CONFIG_PCI
-
-#define  DPE 0x80
-#define  SSE 0x40
-#define  RMA 0x20
-#define  RTA 0x10
-#define  STA 0x08
-#define  DPR 0x01
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_pci_intr
- *
- * Description:
- *   Check the scsi card for PCI errors and clear the interrupt
- *
- *   NOTE: If you don't have this function and a 2940 card encounters
- *         a PCI error condition, the machine will end up locked as the
- *         interrupt handler gets slammed with non-stop PCI error interrupts
- *-F*************************************************************************/
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
 static void
-aic7xxx_pci_intr(struct aic7xxx_host *p)
+aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer)
 {
-  unsigned char status1;
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
-  pci_read_config_byte(p->pdev, PCI_STATUS, &status1);
-#else
-  pcibios_read_config_byte(p->pci_bus, p->pci_device_fn,
-                                            PCI_STATUS, &status1);
-#endif
+  unsigned char saved_scbptr, free_scbh, dis_scbh, wait_scbh, temp;
+  int i, bogus, lost;
+  static unsigned char scb_status[AIC7XXX_MAXSCB];
+
+#define SCB_NO_LIST 0
+#define SCB_FREE_LIST 1
+#define SCB_WAITING_LIST 2
+#define SCB_DISCONNECTED_LIST 4
+#define SCB_CURRENTLY_ACTIVE 8
+
+  /*
+   * Note, these checks will fail on a regular basis once the machine moves
+   * beyond the bus scan phase.  The problem is race conditions concerning
+   * the scbs and where they are linked in.  When you have 30 or so commands
+   * outstanding on the bus, and run this twice with every interrupt, the
+   * chances get pretty good that you'll catch the sequencer with an SCB
+   * only partially linked in.  Therefore, once we pass the scan phase
+   * of the bus, we really should disable this function.
+   */
+  bogus = FALSE;
+  memset(&scb_status[0], 0, sizeof(scb_status));
+  pause_sequencer(p);
+  saved_scbptr = aic_inb(p, SCBPTR);
+  if (saved_scbptr >= p->scb_data->maxhscbs)
+  {
+    printk("Bogus SCBPTR %d\n", saved_scbptr);
+    bogus = TRUE;
+  }
+  scb_status[saved_scbptr] = SCB_CURRENTLY_ACTIVE;
+  free_scbh = aic_inb(p, FREE_SCBH);
+  if ( (free_scbh != SCB_LIST_NULL) &&
+       (free_scbh >= p->scb_data->maxhscbs) )
+  {
+    printk("Bogus FREE_SCBH %d\n", free_scbh);
+    bogus = TRUE;
+  }
+  else
+  {
+    temp = free_scbh;
+    while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+    {
+      if(scb_status[temp] & 0x07)
+      {
+        printk("HSCB %d on multiple lists, status 0x%02x", temp,
+               scb_status[temp] | SCB_FREE_LIST);
+        bogus = TRUE;
+      }
+      scb_status[temp] |= SCB_FREE_LIST;
+      aic_outb(p, temp, SCBPTR);
+      temp = aic_inb(p, SCB_NEXT);
+    }
+  }
 
-  if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Data Parity Error during PCI address or PCI write"
-      "phase.\n", p->host_no, -1, -1, -1);
-  if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Signal System Error Detected\n", p->host_no,
-      -1, -1, -1);
-  if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no,
-      -1, -1, -1);
-  if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no,
-      -1, -1, -1);
-  if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no,
-      -1, -1, -1);
-  if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Data Parity Error has been reported via PCI pin "
-      "PERR#\n", p->host_no, -1, -1, -1);
+  dis_scbh = aic_inb(p, DISCONNECTED_SCBH);
+  if ( (dis_scbh != SCB_LIST_NULL) &&
+       (dis_scbh >= p->scb_data->maxhscbs) )
+  {
+    printk("Bogus DISCONNECTED_SCBH %d\n", dis_scbh);
+    bogus = TRUE;
+  }
+  else
+  {
+    temp = dis_scbh;
+    while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+    {
+      if(scb_status[temp] & 0x07)
+      {
+        printk("HSCB %d on multiple lists, status 0x%02x", temp,
+               scb_status[temp] | SCB_DISCONNECTED_LIST);
+        bogus = TRUE;
+      }
+      scb_status[temp] |= SCB_DISCONNECTED_LIST;
+      aic_outb(p, temp, SCBPTR);
+      temp = aic_inb(p, SCB_NEXT);
+    }
+  }
   
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
-  pci_write_config_byte(p->pdev, PCI_STATUS, status1);
-#else
-  pcibios_write_config_byte(p->pci_bus, p->pci_device_fn,
-                                            PCI_STATUS, status1);
-#endif
-  if (status1 & (DPR|RMA|RTA))
-    aic_outb(p,  CLRPARERR, CLRINT);
+  wait_scbh = aic_inb(p, WAITING_SCBH);
+  if ( (wait_scbh != SCB_LIST_NULL) &&
+       (wait_scbh >= p->scb_data->maxhscbs) )
+  {
+    printk("Bogus WAITING_SCBH %d\n", wait_scbh);
+    bogus = TRUE;
+  }
+  else
+  {
+    temp = wait_scbh;
+    while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+    {
+      if(scb_status[temp] & 0x07)
+      {
+        printk("HSCB %d on multiple lists, status 0x%02x", temp,
+               scb_status[temp] | SCB_WAITING_LIST);
+        bogus = TRUE;
+      }
+      scb_status[temp] |= SCB_WAITING_LIST;
+      aic_outb(p, temp, SCBPTR);
+      temp = aic_inb(p, SCB_NEXT);
+    }
+  }
 
+  lost=0;
+  for(i=0; i < p->scb_data->maxhscbs; i++)
+  {
+    aic_outb(p, i, SCBPTR);
+    temp = aic_inb(p, SCB_NEXT);
+    if ( ((temp != SCB_LIST_NULL) &&
+          (temp >= p->scb_data->maxhscbs)) )
+    {
+      printk("HSCB %d bad, SCB_NEXT invalid(%d).\n", i, temp);
+      bogus = TRUE;
+    }
+    if ( temp == i )
+    {
+      printk("HSCB %d bad, SCB_NEXT points to self.\n", i);
+      bogus = TRUE;
+    }
+    temp = aic_inb(p, SCB_PREV);
+    if ((temp != SCB_LIST_NULL) &&
+        (temp >= p->scb_data->maxhscbs))
+    {
+      printk("HSCB %d bad, SCB_PREV invalid(%d).\n", i, temp);
+      bogus = TRUE;
+    }
+    if (scb_status[i] == 0)
+      lost++;
+    if (lost > 1)
+    {
+      printk("Too many lost scbs.\n");
+      bogus=TRUE;
+    }
+  }
+  aic_outb(p, saved_scbptr, SCBPTR);
+  unpause_sequencer(p, FALSE);
+  if (bogus)
+  {
+    printk("Bogus parameters found in card SCB array structures.\n");
+    printk("%s\n", buffer);
+    aic7xxx_panic_abort(p, NULL);
+  }
+  return;
 }
 #endif
 
@@ -5066,7 +6131,8 @@
   if (!((intstat = aic_inb(p, INTSTAT)) & INT_PEND))
   {
 #ifdef CONFIG_PCI
-    if ((p->type & AHC_AIC78x0) && (p->spurious_int > 500))
+    if ( (p->chip & AHC_PCI) && (p->spurious_int > 500) &&
+        !(p->flags & AHC_HANDLING_REQINITS) )
     {
       if ( aic_inb(p, ERROR) & PCIERRSTAT )
       {
@@ -5074,7 +6140,7 @@
       }
       p->spurious_int = 0;
     }
-    else
+    else if ( !(p->flags & AHC_HANDLING_REQINITS) )
     {
       p->spurious_int++;
     }
@@ -5089,6 +6155,12 @@
    */
   p->isr_count++;
 
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+  if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) &&
+       (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) )
+    aic7xxx_check_scbs(p, "Bogus settings at start of interrupt.");
+#endif
+
   /*
    * Handle all the interrupt sources - especially for SCSI
    * interrupts, we won't get a second chance at them.
@@ -5099,6 +6171,11 @@
     Scsi_Cmnd *cmd;
     unsigned char scb_index;
 
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+    if(aic7xxx_verbose > 0xffff)
+      printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1);
+#endif
+    
     /*
      * Clear interrupt status before running the completion loop.
      * This eliminates a race condition whereby a command could
@@ -5152,6 +6229,14 @@
           SCB_QUEUED_ABORT);
         unpause_sequencer(p, FALSE);
       }
+      else if (scb->flags & SCB_ABORT)
+      {
+        /*
+         * We started to abort this, but it completed on us, let it
+         * through as successful
+         */
+        scb->flags &= ~(SCB_ABORT|SCB_RESET);
+      }
       switch (status_byte(scb->hscb->target_status))
       {
         case QUEUE_FULL:
@@ -5192,16 +6277,33 @@
         printk(KERN_ERR "  %s\n", hard_error[i].errmesg);
       }
     }
-    printk(KERN_ERR "(scsi%d)   LINE=%d\n", p->host_no,
+    printk(KERN_ERR "(scsi%d)   SEQADDR=0x%x\n", p->host_no,
       (((aic_inb(p, SEQADDR1) << 8) & 0x100) | aic_inb(p, SEQADDR0)));
-    aic7xxx_reset_channel(p, 0, TRUE);
-    if ( p->type & AHC_TWIN )
+    if (aic7xxx_panic_on_abort)
+      aic7xxx_panic_abort(p, NULL);
+    if (errno & PCIERRSTAT)
+      aic7xxx_pci_intr(p);
+    if (errno & (SQPARERR | ILLOPCODE | ILLSADDR))
+    {
+      sti();
+      panic("aic7xxx: unrecoverable BRKADRINT.\n");
+    }
+    if (errno & ILLHADDR)
     {
-      aic7xxx_reset_channel(p, 1, TRUE);
-      restart_sequencer(p);
+      printk(KERN_ERR "(scsi%d) BUG! Driver accessed chip without first "
+             "pausing controller!\n", p->host_no);
     }
-    aic7xxx_run_done_queue(p, FALSE);
-    aic_outb(p, CLRBRKADRINT, CLRINT);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+    if (errno & DPARERR)
+    {
+      if (aic_inb(p, DMAPARAMS) & DIRECTION)
+        printk("(scsi%d) while DMAing SCB from host to card.\n", p->host_no);
+      else
+        printk("(scsi%d) while DMAing SCB from card to host.\n", p->host_no);
+    }
+#endif
+    aic_outb(p, CLRPARERR | CLRBRKADRINT, CLRINT);
+    unpause_sequencer(p, FALSE);
   }
 
   if (intstat & SEQINT)
@@ -5213,6 +6315,13 @@
   {
     aic7xxx_handle_scsiint(p, intstat);
   }
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+  if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) &&
+       (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) )
+    aic7xxx_check_scbs(p, "Bogus settings at end of interrupt.");
+#endif
+
 }
 
 /*+F*************************************************************************
@@ -5229,49 +6338,34 @@
 {
   unsigned long cpu_flags;
   struct aic7xxx_host *p;
-  static unsigned int re_entry_counter = 0;
   
   p = (struct aic7xxx_host *)dev_id;
   if(!p)
     return;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,95)
+  spin_lock_irqsave(&io_request_lock, cpu_flags);
   if(test_and_set_bit(AHC_IN_ISR_BIT, &p->flags))
   {
-    if(re_entry_counter++ > 100000UL)
-    {
-      /*
-       * Hmmm...we seem to be looping here.  This usually means that our
-       * interrupt routine got killed by a NULL pointer deref.  Panic.
-       */
-      sti();
-      panic("aic7xxx: The interrupt routine appears to have seg faulted.\n");
-    }
     return;
   }
-  re_entry_counter = 0;
-  spin_lock_irqsave(&io_request_lock, cpu_flags);
-  aic7xxx_isr(irq, dev_id, regs);
+  do
+  {
+    aic7xxx_isr(irq, dev_id, regs);
+  } while ( (aic_inb(p, INTSTAT) & INT_PEND) );
   aic7xxx_done_cmds_complete(p);
   aic7xxx_run_waiting_queues(p);
-  spin_unlock_irqrestore(&io_request_lock, cpu_flags);
   clear_bit(AHC_IN_ISR_BIT, &p->flags);
+  spin_unlock_irqrestore(&io_request_lock, cpu_flags);
 #else
   if(set_bit(AHC_IN_ISR_BIT, (int *)&p->flags))
   {
-    if(re_entry_counter++ > 100000UL)
-    {
-      /*
-       * Hmmm...we seem to be looping here.  This usually means that our
-       * interrupt routine got killed by a NULL pointer deref.  Panic.
-       */
-      sti();
-      panic("aic7xxx: The interrupt routine appears to have seg faulted.\n");
-    }
     return;
   }
-  re_entry_counter = 0;
   DRIVER_LOCK
-  aic7xxx_isr(irq, dev_id, regs);
+  do
+  {
+    aic7xxx_isr(irq, dev_id, regs);
+  } while ( (aic_inb(p, INTSTAT) & INT_PEND) );
   DRIVER_UNLOCK
   aic7xxx_done_cmds_complete(p);
   aic7xxx_run_waiting_queues(p);
@@ -5324,7 +6418,7 @@
  
     if (!(p->discenable & target_mask))
     {
-      if (aic7xxx_verbose & VERBOSE_QUEUE)
+      if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
         printk(INFO_LEAD "Disconnection disabled, unable to "
              "enable tagged queueing.\n",
              p->host_no, device->channel, device->id, device->lun);
@@ -5364,7 +6458,7 @@
       }
       if ((device->tagged_queue == 0) && tag_enabled)
       {
-        if (aic7xxx_verbose & VERBOSE_QUEUE)
+        if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
         {
               printk(INFO_LEAD "Enabled tagged queuing, queue depth %d.\n",
                 p->host_no, device->channel, device->id,
@@ -5418,7 +6512,7 @@
      * to allocate some when memory is more or less exhausted and we need
      * the SCB in order to perform a swap operation (possible deadlock)
      */
-    if ( aic7xxx_allocate_scb(p, TRUE) == NULL )
+    if ( aic7xxx_allocate_scb(p) == 0 )
       return;
   }
 }
@@ -5451,7 +6545,7 @@
  *   an unused function.
  *-F*************************************************************************/
 #if defined(__i386__) || defined(__alpha__)
-static ahc_type
+static int
 aic7xxx_probe(int slot, int base, ahc_flag_type *flags)
 {
   int i;
@@ -5460,13 +6554,17 @@
   static struct {
     int n;
     unsigned char signature[sizeof(buf)];
-    ahc_type type;
+    ahc_chip type;
     int bios_disabled;
   } AIC7xxx[] = {
-    { 4, { 0x04, 0x90, 0x77, 0x71 }, AHC_274, FALSE }, /* host adapter 274x */
-    { 4, { 0x04, 0x90, 0x77, 0x70 }, AHC_AIC7770, FALSE },  /* mb 7770  */
-    { 4, { 0x04, 0x90, 0x77, 0x56 }, AHC_284, FALSE }, /* 284x BIOS enabled */
-    { 4, { 0x04, 0x90, 0x77, 0x57 }, AHC_284, TRUE }   /* 284x BIOS disabled */
+    { 4, { 0x04, 0x90, 0x77, 0x70 },
+      AHC_AIC7770|AHC_EISA, FALSE },  /* mb 7770  */
+    { 4, { 0x04, 0x90, 0x77, 0x71 },
+      AHC_AIC7770|AHC_EISA, FALSE }, /* host adapter 274x */
+    { 4, { 0x04, 0x90, 0x77, 0x56 },
+      AHC_AIC7770|AHC_VL, FALSE }, /* 284x BIOS enabled */
+    { 4, { 0x04, 0x90, 0x77, 0x57 },
+      AHC_AIC7770|AHC_VL, TRUE }   /* 284x BIOS disabled */
   };
 
   /*
@@ -5496,7 +6594,7 @@
         {
           *flags |= AHC_BIOS_ENABLED;
         }
-        return (AIC7xxx[i].type);
+        return (i);
       }
 
       printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
@@ -5504,7 +6602,7 @@
     }
   }
 
-  return (AHC_NONE);
+  return (-1);
 }
 #endif /* (__i386__) || (__alpha__) */
 
@@ -5661,7 +6759,7 @@
  * Description:
  *   Acquires access to the memory port on PCI controllers.
  *-F*************************************************************************/
-static inline int
+static int
 acquire_seeprom(struct aic7xxx_host *p)
 {
   int wait;
@@ -5695,7 +6793,7 @@
  * Description:
  *   Releases access to the memory port on PCI controllers.
  *-F*************************************************************************/
-static inline void
+static void
 release_seeprom(struct aic7xxx_host *p)
 {
   aic_outb(p, 0, SEECTL);
@@ -5895,19 +6993,38 @@
  * Description:
  *   Writes a value to the BRDCTL register.
  *-F*************************************************************************/
-static inline void
+static void
 write_brdctl(struct aic7xxx_host *p, unsigned char value)
 {
   unsigned char brdctl;
 
-  brdctl = BRDCS | BRDSTB;
+  if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
+  {
+    brdctl = BRDSTB;
+    if (p->flags & AHC_CHNLB)
+      brdctl |= BRDCS;
+  }
+  else if (p->features & AHC_ULTRA2)
+    brdctl = 0;
+  else
+    brdctl = BRDSTB | BRDCS;
   aic_outb(p, brdctl, BRDCTL);
+  udelay(1);
   brdctl |= value;
   aic_outb(p, brdctl, BRDCTL);
-  brdctl &= ~BRDSTB;
+  udelay(1);
+  if (p->features & AHC_ULTRA2)
+    brdctl |= BRDSTB_ULTRA2;
+  else
+    brdctl &= ~BRDSTB;
   aic_outb(p, brdctl, BRDCTL);
-  brdctl &= ~BRDCS;
+  udelay(1);
+  if (p->features & AHC_ULTRA2)
+    brdctl = 0;
+  else
+    brdctl &= ~BRDCS;
   aic_outb(p, brdctl, BRDCTL);
+  udelay(1);
 }
 
 /*+F*************************************************************************
@@ -5917,11 +7034,27 @@
  * Description:
  *   Reads the BRDCTL register.
  *-F*************************************************************************/
-static inline unsigned char
+static unsigned char
 read_brdctl(struct aic7xxx_host *p)
 {
-  aic_outb(p, BRDRW | BRDCS, BRDCTL);
-  return (aic_inb(p, BRDCTL));
+  unsigned char brdctl, value;
+
+  if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
+  {
+    brdctl = BRDRW;
+    if (p->flags & AHC_CHNLB)
+      brdctl |= BRDCS;
+  }
+  else if (p->features & AHC_ULTRA2)
+    brdctl = BRDRW_ULTRA2;
+  else
+    brdctl = BRDRW | BRDCS;
+  aic_outb(p, brdctl, BRDCTL);
+  udelay(1);
+  value = aic_inb(p, BRDCTL);
+  aic_outb(p, 0, BRDCTL);
+  udelay(1);
+  return (value);
 }
 
 /*+F*************************************************************************
@@ -5938,11 +7071,14 @@
   unsigned char brdctl;
 
   aic_outb(p, BRDRW | BRDCS, BRDCTL);
+  udelay(1);
   aic_outb(p, 0, BRDCTL);
+  udelay(1);
   brdctl = aic_inb(p, BRDCTL);
+  udelay(1);
   *int_50 = !(brdctl & BRDDAT5);
   *ext_present = !(brdctl & BRDDAT6);
-  *eeprom = ( aic_inb(p, SPIOCAP) & EEPROM ) != 0;
+  *eeprom = (aic_inb(p, SPIOCAP) & EEPROM);
 }
 
 /*+F*************************************************************************
@@ -5985,12 +7121,37 @@
   brdctl = read_brdctl(p);
 
   *ext_present = !(brdctl & BRDDAT6);
-  *eeprom = (brdctl & BRDDAT7);
+  *eeprom = !(brdctl & BRDDAT7);
 
   /*
    * We're done, the calling function will release the SEEPROM for us
    */
-  
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic787x_ultra2_term_detect
+ *
+ * Description:
+ *   Detect the termination settings present on ultra2 class controllers
+ *
+ * NOTE: This functions assumes the SEEPROM will have already been aquired
+ *       prior to invocation of this function.
+ *-F*************************************************************************/
+static void
+aic7xxx_ultra2_term_detect(struct aic7xxx_host *p, int *enableSE_low,
+                           int *enableSE_high, int *enableLVD_low,
+                           int *enableLVD_high, int *eprom_present)
+{
+  unsigned char brdctl;
+
+  brdctl = read_brdctl(p);
+
+  *eprom_present  = (brdctl & BRDDAT7);
+  *enableSE_high  = (brdctl & BRDDAT6);
+  *enableSE_low   = (brdctl & BRDDAT5);
+  *enableLVD_high = (brdctl & BRDDAT4);
+  *enableLVD_low  = (brdctl & BRDDAT3);
 }
 
 /*+F*************************************************************************
@@ -6002,146 +7163,192 @@
  *   SEEPROMs available.
  *-F*************************************************************************/
 static void
-configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1,
-    unsigned short adapter_control, unsigned char max_targ)
+configure_termination(struct aic7xxx_host *p)
 {
-  int internal50_present;
+  int internal50_present = 0;
   int internal68_present = 0;
   int external_present = 0;
-  int eprom_present;
-  int high_on;
-  int low_on;
+  int eprom_present = 0;
+  int enableSE_low = 0;
+  int enableSE_high = 0;
+  int enableLVD_low = 0;
+  int enableLVD_high = 0;
+  unsigned char brddat = 0;
+  unsigned char max_target = 0;
+  unsigned char sxfrctl1 = aic_inb(p, SXFRCTL1);
 
   if (acquire_seeprom(p))
   {
-    if (adapter_control & CFAUTOTERM)
-    {
-      printk(KERN_INFO "aic7xxx: Warning - detected auto-termination on "
-                       "controller:\n");
-      printk(KERN_INFO "aic7xxx: <%s> at ", board_names[p->board_name_index]);
-      switch(p->type & 0x1ff1)
-      {
-        case AHC_AIC7770:
-        case AHC_274:
-          printk("EISA slot %d\n", p->pci_device_fn);
-          break;
-        case AHC_284:
-          printk("VLB slot %d\n", p->pci_device_fn);
-          break;
-        default:
-          printk("PCI %d/%d\n", PCI_SLOT(p->pci_device_fn),
-            PCI_FUNC(p->pci_device_fn));
-          break;
+    if (p->features & (AHC_WIDE|AHC_TWIN))
+      max_target = 16;
+    else
+      max_target = 8;
+    aic_outb(p, SEEMS | SEECS, SEECTL);
+    sxfrctl1 &= ~STPWEN;
+    if ( (p->adapter_control & CFAUTOTERM) ||
+         (p->features & AHC_ULTRA2) )
+    {
+      if ( (p->adapter_control & CFAUTOTERM) && !(p->features & AHC_ULTRA2) )
+      {
+        printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n",
+               p->host_no);
+        printk(KERN_INFO "(scsi%d) Please verify driver detected settings are "
+          "correct.\n", p->host_no);
+        printk(KERN_INFO "(scsi%d) If not, then please properly set the device "
+          "termination\n", p->host_no);
+        printk(KERN_INFO "(scsi%d) in the Adaptec SCSI BIOS by hitting CTRL-A "
+          "when prompted\n", p->host_no);
+        printk(KERN_INFO "(scsi%d) during machine bootup.\n", p->host_no);
       }
-      printk(KERN_INFO "aic7xxx: Please verify driver detected settings are "
-        "correct.\n");
-      printk(KERN_INFO "aic7xxx: If not, then please properly set the device "
-        "termination\n");
-      printk(KERN_INFO "aic7xxx: in the Adaptec SCSI BIOS by hitting CTRL-A "
-        "when prompted\n");
-      printk(KERN_INFO "aic7xxx: during machine bootup.\n");
       /* Configure auto termination. */
-      aic_outb(p, SEECS | SEEMS, SEECTL);
 
-      if ( (p->type & AHC_AIC7860) == AHC_AIC7860 )
+      if (p->features & AHC_ULTRA2)
       {
-        aic785x_cable_detect(p, &internal50_present, &external_present,
-          &eprom_present);
+        if (aic7xxx_override_term == -1)
+          aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high,
+                                     &enableLVD_low, &enableLVD_high,
+                                     &eprom_present);
+        if (!(p->adapter_control & CFSEAUTOTERM))
+        {
+          enableSE_low = (p->adapter_control & CFSTERM);
+          enableSE_high = (p->adapter_control & CFWSTERM);
+        }
+        if (!(p->adapter_control & CFAUTOTERM))
+        {
+          enableLVD_low = enableLVD_high = (p->adapter_control & CFLVDSTERM);
+        }
+        internal50_present = 0;
+        internal68_present = 1;
+        external_present = 1;
       }
-      else
+      else if ( (p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870 )
       {
         aic787x_cable_detect(p, &internal50_present, &internal68_present,
           &external_present, &eprom_present);
       }
-      if (max_targ > 8)
-      {
-        printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Int-68 %s, "
-               "Ext-68 %s)\n",
-               internal50_present ? "YES" : "NO",
-               internal68_present ? "YES" : "NO",
-               external_present ? "YES" : "NO");
-      }
       else
       {
-        printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n",
-               internal50_present ? "YES" : "NO",
-               external_present ? "YES" : "NO");
+        aic785x_cable_detect(p, &internal50_present, &external_present,
+          &eprom_present);
+      }
+      
+      if (max_target <= 8)
         internal68_present = 0;
+
+      if ( !(p->features & AHC_ULTRA2) )
+      {
+        if (max_target > 8)
+        {
+          printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, "
+                 "Ext-68 %s)\n", p->host_no,
+                 internal50_present ? "YES" : "NO",
+                 internal68_present ? "YES" : "NO",
+                 external_present ? "YES" : "NO");
+        }
+        else
+        {
+          printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Ext-50 %s)\n",
+                 p->host_no,
+                 internal50_present ? "YES" : "NO",
+                 external_present ? "YES" : "NO");
+        }
       }
       if (aic7xxx_verbose & VERBOSE_PROBE2)
-        printk(KERN_INFO "aic7xxx: EEPROM %s present.\n",
+        printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no,
              eprom_present ? "is" : "is not");
 
       /*
        * Now set the termination based on what we found.  BRDDAT6
        * controls wide termination enable.
+       * Flash Enable = BRDDAT7
+       * SE High Term Enable = BRDDAT6
+       * SE Low Term Enable = BRDDAT5 (7890)
+       * LVD High Term Enable = BRDDAT4 (7890)
        */
-      high_on = FALSE;
-      low_on = FALSE;
-      if ((max_targ > 8) &&
-          ((external_present == 0) || (internal68_present == 0)))
+      if ( !(p->features & AHC_ULTRA2) &&
+           (internal50_present && internal68_present && external_present) )
       {
-        high_on = TRUE;
+        printk(KERN_INFO "(scsi%d) Illegal cable configuration!!  Only two\n",
+               p->host_no);
+        printk(KERN_INFO "(scsi%d) connectors on the SCSI controller may be "
+               "in use at a time!\n", p->host_no);
+        /*
+         * Force termination (low and high byte) on.  This is safer than
+         * leaving it completely off, especially since this message comes
+         * most often from motherboard controllers that don't even have 3
+         * connectors, but instead are failing the cable detection.
+         */
+        internal50_present = external_present = 0;
+        enableSE_high = enableSE_low = 1;
       }
 
-      if ( ( (internal50_present ? 1 : 0) +
-             (internal68_present ? 1 : 0) +
-             (external_present   ? 1 : 0) ) <= 1)
+      if ((max_target > 8) &&
+          ((external_present == 0) || (internal68_present == 0) ||
+           (enableSE_high != 0)))
       {
-        low_on = TRUE;
+        brddat |= BRDDAT6;
+        p->flags |= AHC_TERM_ENB_SE_HIGH;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n",
+                 p->host_no);
       }
-          
-      if (internal50_present && internal68_present && external_present)
+
+      if ( (((internal50_present ? 1 : 0) +
+             (internal68_present ? 1 : 0) +
+             (external_present   ? 1 : 0)) <= 1) ||
+           (enableSE_low != 0) )
       {
-        printk(KERN_INFO "aic7xxx: Illegal cable configuration!!  Only two\n");
-        printk(KERN_INFO "aic7xxx: connectors on the SCSI controller may be "
-               "in use at a time!\n");
+        if (p->features & AHC_ULTRA2)
+          brddat |= BRDDAT5;
+        else
+          sxfrctl1 |= STPWEN;
+        p->flags |= AHC_TERM_ENB_SE_LOW;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n",
+                 p->host_no);
       }
 
-      if (high_on == TRUE)
-        write_brdctl(p, BRDDAT6);
-      else
-        write_brdctl(p, 0);
-
-      if (low_on == TRUE)
-        *sxfrctl1 |= STPWEN;
-
-      if (max_targ > 8)
+      if (enableLVD_low != 0)
       {
-        printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
-              low_on ? "ON" : "OFF", high_on ? "ON" : "OFF");
+        sxfrctl1 |= STPWEN;
+        p->flags |= AHC_TERM_ENB_LVD;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) LVD Low byte termination Enabled\n",
+                 p->host_no);
       }
-      else
+          
+      if (enableLVD_high != 0)
       {
-        printk(KERN_INFO "aic7xxx: Termination %s\n",
-          low_on ? "Enabled" : "Disabled");
+        brddat |= BRDDAT4;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) LVD High byte termination Enabled\n",
+                 p->host_no);
       }
     }
     else
     {
-      if (adapter_control & CFSTERM)
-        *sxfrctl1 |= STPWEN;
-
-      aic_outb(p, SEEMS | SEECS, SEECTL);
-      /*
-       * Configure high byte termination.
-       */
-      if (adapter_control & CFWSTERM)
-      {
-        write_brdctl(p, BRDDAT6);
-      }
-      else
+      if (p->adapter_control & CFSTERM)
       {
-        write_brdctl(p, 0);
+        if (p->features & AHC_ULTRA2)
+          brddat |= BRDDAT5;
+        else
+          sxfrctl1 |= STPWEN;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n",
+                 p->host_no);
       }
-      if (aic7xxx_verbose & VERBOSE_PROBE2)
+
+      if (p->adapter_control & CFWSTERM)
       {
-        printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
-               (adapter_control & CFSTERM) ? "ON" : "OFF",
-               (adapter_control & CFWSTERM) ? "ON" : "OFF");
+        brddat |= BRDDAT6;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n",
+                 p->host_no);
       }
     }
+    write_brdctl(p, brddat);
     release_seeprom(p);
+    aic_outb(p, sxfrctl1, SXFRCTL1);
   }
 }
 
@@ -6183,7 +7390,12 @@
       aic_outb(p, i, SCBPTR);
       aic_outb(p, 0, SCB_CONTROL);   /* Clear the control byte. */
       aic_outb(p, i + 1, SCB_NEXT);  /* Set the next pointer. */
+      aic_outb(p, i - 1, SCB_PREV);  /* Set the prev pointer. */
       aic_outb(p, SCB_LIST_NULL, SCB_TAG);  /* Make the tag invalid. */
+      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS);  /* no busy untagged */
+      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+1);/* targets active yet */
+      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+2);
+      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+3);
     }
 
     /* Make sure the last SCB terminates the free list. */
@@ -6195,6 +7407,11 @@
     aic_outb(p, 0, SCB_CONTROL);
 
     p->scb_data->maxhscbs = i;
+    /*
+     * Use direct indexing instead for speed
+     */
+    if ( i == AIC7XXX_MAXSCB )
+      p->flags &= ~AHC_PAGESCBS;
   }
 
 }
@@ -6213,9 +7430,7 @@
   int i, result;
   int max_targets;
   int found = 1;
-  unsigned char target_settings;
-  unsigned char term, scsi_conf, sxfrctl1;
-  unsigned short ultraenable = 0;
+  unsigned char term, scsi_conf;
   struct Scsi_Host *host;
 
   /*
@@ -6236,11 +7451,11 @@
   host->n_io_port = 0xFF;
   host->base = (unsigned char *) p->mbase;
   host->irq = p->irq;
-  if (p->type & AHC_WIDE)
+  if (p->features & AHC_WIDE)
   {
     host->max_id = 16;
   }
-  if (p->type & AHC_TWIN)
+  if (p->features & AHC_TWIN)
   {
     host->max_channel = 1;
   }
@@ -6248,6 +7463,7 @@
   p->host = host;
   p->last_reset = 0;
   p->host_no = host->host_no;
+  host->unique_id = p->instance;
   p->isr_count = 0;
   p->next = NULL;
   p->completeq.head = NULL;
@@ -6270,7 +7486,7 @@
   for (i = 0; i < MAX_TARGETS; i++)
   {
     p->dev_commands_sent[i] = 0;
-    p->dev_flags[i] = DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR;
+    p->dev_flags[i] = 0;
     p->dev_active_cmds[i] = 0;
     p->dev_last_reset[i] = 0;
     p->dev_last_queue_full[i] = 0;
@@ -6283,19 +7499,16 @@
     p->dev_timer[i].expires = 0;
     p->dev_timer[i].data = (unsigned long)p;
     p->dev_timer[i].function = (void *)aic7xxx_timer;
-    p->syncinfo[i].period = 0;
-    p->syncinfo[i].offset = 0;
   }
 
   printk(KERN_INFO "(scsi%d) <%s> found at ", p->host_no,
     board_names[p->board_name_index]);
-  switch(p->type & 0x1ff1)
+  switch(p->chip)
   {
-    case AHC_AIC7770:
-    case AHC_274:
+    case (AHC_AIC7770|AHC_EISA):
       printk("EISA slot %d\n", p->pci_device_fn);
       break;
-    case AHC_284:
+    case (AHC_AIC7770|AHC_VL):
       printk("VLB slot %d\n", p->pci_device_fn);
       break;
     default:
@@ -6303,7 +7516,7 @@
         PCI_FUNC(p->pci_device_fn));
       break;
   }
-  if (p->type & AHC_TWIN)
+  if (p->features & AHC_TWIN)
   {
     printk(KERN_INFO "(scsi%d) Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
            p->host_no, p->scsi_id, p->scsi_id_b);
@@ -6314,7 +7527,7 @@
 
     channel = "";
 
-    if ((p->type & AHC_39x) != 0)
+    if ((p->flags & AHC_MULTI_CHANNEL) != 0)
     {
       channel = " A";
 
@@ -6323,7 +7536,7 @@
         channel = (p->flags & AHC_CHNLB) ? " B" : " C";
       }
     }
-    if (p->type & AHC_WIDE)
+    if (p->features & AHC_WIDE)
     {
       printk(KERN_INFO "(scsi%d) Wide ", p->host_no);
     }
@@ -6340,129 +7553,159 @@
    */
   detect_maxscb(p);
   printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs);
-  printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n",
-    p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis",
-    p->base, p->irq);
-  printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at 0x%lx\n",
-    p->host_no, p->mbase, (unsigned long)p->maddr);
-
-
+  if (aic7xxx_verbose & VERBOSE_PROBE2)
+  {
+    printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n",
+      p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis",
+      p->base, p->irq);
+    printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at 0x%lx\n",
+      p->host_no, p->mbase, (unsigned long)p->maddr);
+  }
 
   /*
-   * Register IRQ with the kernel.  Only allow sharing IRQs with
-   * PCI devices.
+   * Now that we know our instance number, we can set the flags we need to
+   * force termination if need be.
    */
-  if ((p->type & AHC_AIC7770) == AHC_AIC7770)
-  {
-    result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p));
-  }
-  else
+  if (aic7xxx_stpwlev != -1)
   {
-    result = (request_irq(p->irq, do_aic7xxx_isr, SA_SHIRQ,
-              "aic7xxx", p));
-    if (result < 0)
+    /*
+     * This option only applies to PCI controllers.
+     */
+    if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI)
     {
-      result = (request_irq(p->irq, do_aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
-              "aic7xxx", p));
+      unsigned char devconfig;
+
+#if LINUX_KERNEL_VERSION > KERNEL_VERSION(2,1,92)
+      pci_read_config_byte(p->pdev, DEVCONFIG, &devconfig);
+#else
+      pcibios_read_config_byte(p->pci_bus, p->pci_device_fn,
+                               DEVCONFIG, &devconfig);
+#endif
+      if ( (aic7xxx_stpwlev >> p->instance) & 0x01 )
+      {
+        devconfig |= 0x02;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk("(scsi%d) Force setting STPWLEV bit\n", p->host_no);
+      }
+      else
+      {
+        devconfig &= ~0x02;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk("(scsi%d) Force clearing STPWLEV bit\n", p->host_no);
+      }
+#if LINUX_KERNEL_VERSION > KERNEL_VERSION(2,1,92)
+      pci_write_config_byte(p->pdev, DEVCONFIG, devconfig);
+#else
+      pcibios_write_config_byte(p->pci_bus, p->pci_device_fn,
+                                DEVCONFIG, devconfig);
+#endif
     }
   }
-  if (result < 0)
-  {
-    printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring.\n",
-           p->host_no, p->irq);
-    return (0);
-  }
 
   /*
-   * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
+   * That took care of devconfig and stpwlev, now for the actual termination
+   * settings.
    */
-  if (p->type & AHC_TWIN)
+  if (aic7xxx_override_term != -1)
   {
     /*
-     * The controller is gated to channel B after a chip reset; set
-     * bus B values first.
+     * Again, this only applies to PCI controllers.  We don't have problems
+     * with the termination on 274x controllers to the best of my knowledge.
      */
-    term = ((p->flags & AHC_TERM_ENB_B) != 0) ? STPWEN : 0;
-    aic_outb(p, p->scsi_id_b, SCSIID);
-    scsi_conf = aic_inb(p, SCSICONF + 1);
-    sxfrctl1 = aic_inb(p, SXFRCTL1);
-    aic_outb(p, (scsi_conf & (ENSPCHK | STIMESEL)) | term | 
-         ENSTIMER | ACTNEGEN, SXFRCTL1);
-    aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
-    if (p->type & AHC_ULTRA)
+    if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI)
     {
-      aic_outb(p, DFON | SPIOEN | FAST20, SXFRCTL0);
-    }
-    else
-    {
-      aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+      unsigned char term_override;
+
+      term_override = ( (aic7xxx_override_term >> (p->instance * 4)) & 0x0f);
+      p->adapter_control &= 
+        ~(CFSTERM|CFWSTERM|CFLVDSTERM|CFAUTOTERM|CFSEAUTOTERM);
+      if ( (p->features & AHC_ULTRA2) && (term_override & 0x0c) )
+      {
+        p->adapter_control |= CFLVDSTERM;
+      }
+      if (term_override & 0x02)
+      {
+        p->adapter_control |= CFWSTERM;
+      }
+      if (term_override & 0x01)
+      {
+        p->adapter_control |= CFSTERM;
+      }
     }
-    if ( (p->type & AHC_AIC7770) == AHC_AIC7770 )
+  }
+
+  if ( (p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1) )
+  {
+    if (p->features & AHC_SPIOCAP)
     {
-      scsi_conf &= ~0x07;
-      scsi_conf |= p->scsi_id_b;
-      aic_outb(p, scsi_conf | (term) ? TERM_ENB : 0, SCSICONF + 1);
+      if ( aic_inb(p, SPIOCAP) & SSPIOCPS )
+      /*
+       * Update the settings in sxfrctl1 to match the termination
+       * settings.
+       */
+        configure_termination(p);
     }
-    if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) )
+    else if ((p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870)
     {
-      /* Reset SCSI bus B. */
-      if (aic7xxx_verbose & VERBOSE_PROBE)
-        printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no);
-
-      aic7xxx_reset_current_bus(p);
+      configure_termination(p);
     }
-
-    /* Select channel A */
-    aic_outb(p, SELNARROW, SBLKCTL);
   }
 
-  term = ((p->flags & AHC_TERM_ENB_A) != 0) ? STPWEN : 0;
-  aic_outb(p, p->scsi_id, SCSIID);
-  scsi_conf = aic_inb(p, SCSICONF);
-  sxfrctl1 = aic_inb(p, SXFRCTL1);
-  aic_outb(p, (scsi_conf & (ENSPCHK | STIMESEL)) | term | 
-       ENSTIMER | ACTNEGEN, SXFRCTL1);
-  aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
-  if (p->type & AHC_ULTRA)
-  {
-    aic_outb(p, DFON | SPIOEN | FAST20, SXFRCTL0);
-  }
-  else
-  {
-    aic_outb(p, DFON | SPIOEN, SXFRCTL0);
-  }
-  if ( (p->type & AHC_AIC7770) == AHC_AIC7770 )
+  /*
+   * Load the sequencer program, then re-enable the board -
+   * resetting the AIC-7770 disables it, leaving the lights
+   * on with nobody home.
+   */
+  aic7xxx_loadseq(p);
+
+  if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
   {
-    scsi_conf &= ~0x07;
-    scsi_conf |= p->scsi_id;
-    aic_outb(p, scsi_conf | (term) ? TERM_ENB : 0, SCSICONF);
+    aic_outb(p, ENABLE, BCTL);  /* Enable the boards BUS drivers. */
   }
+  aic_outb(p, aic_inb(p, SBLKCTL) & ~AUTOFLUSHDIS, SBLKCTL);
 
+  /*
+   * Clear out any possible pending interrupts.
+   */
+  aic7xxx_clear_intstat(p);
 
-  if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) )
+  /*
+   * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
+   */
+  if (p->features & AHC_TWIN)
   {
-    /* Reset SCSI bus A. */
-    if (aic7xxx_verbose & VERBOSE_PROBE)
-    {  /* In case we are a 3940, 3985, or 7895, print the right channel */
-      char *channel = "";
-      if (p->flags & AHC_MULTI_CHANNEL)
-      {
-        channel = " A";
-        if (p->flags & (AHC_CHNLB|AHC_CHNLC))
-          channel = (p->flags & AHC_CHNLB) ? " B" : " C";
-      }
-      printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel);
-    }
+    /* Select channel B */
+    aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL);
 
-    aic7xxx_reset_current_bus(p);
+    term = ((p->flags & AHC_TERM_ENB_B) != 0) ? STPWEN : 0;
+    aic_outb(p, p->scsi_id_b, SCSIID);
+    scsi_conf = aic_inb(p, SCSICONF + 1);
+    aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+    aic_outb(p, (scsi_conf & ENSPCHK) | term | 
+         ENSTIMER | ACTNEGEN, SXFRCTL1);
+    aic_outb(p, 0, SIMODE0);
+    aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
+    aic_outb(p, 0, SCSIRATE);
 
-    /*
-     * Delay for the reset delay.
-     */
-    if (!reset_delay)
-      aic7xxx_delay(AIC7XXX_RESET_DELAY);
+    /* Select channel A */
+    aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL);
   }
 
+  term = ((p->flags & AHC_TERM_ENB_SE_LOW) != 0) ? STPWEN : 0;
+  if (p->features & AHC_ULTRA2)
+    aic_outb(p, p->scsi_id, SCSIID_ULTRA2);
+  else
+    aic_outb(p, p->scsi_id, SCSIID);
+  scsi_conf = aic_inb(p, SCSICONF);
+  aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+  aic_outb(p, (scsi_conf & ENSPCHK) | term | 
+       ENSTIMER | ACTNEGEN, SXFRCTL1);
+  aic_outb(p, 0, SIMODE0);
+  aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
+  aic_outb(p, 0, SCSIRATE);
+  if ( p->features & AHC_ULTRA2)
+    aic_outb(p, 0, SCSIOFFSET);
+
   /*
    * Look at the information that board initialization or the board
    * BIOS has left us. In the lower four bits of each target's
@@ -6471,14 +7714,7 @@
    * BIOS has decided to disable synchronous negotiation to that
    * target so we don't activate the needsdtr flag.
    */
-  p->needsdtr_copy = 0x0;
-  p->sdtr_pending = 0x0;
-  p->needwdtr_copy = 0x0;
-  p->wdtr_pending = 0x0;
-  p->tagenable = 0x0;
-  p->ultraenb = 0x0;
-  p->discenable = 0xffff;
-  if ((p->type & (AHC_TWIN|AHC_WIDE)) == 0)
+  if ((p->features & (AHC_TWIN|AHC_WIDE)) == 0)
   {
     max_targets = 8;
   }
@@ -6487,110 +7723,31 @@
     max_targets = 16;
   }
 
-  /*
-   * Grab the disconnection disable table and invert it for our needs
-   */
-  if (p->flags & AHC_USEDEFAULTS)
-  {
-    printk(KERN_INFO "(scsi%d) Host adapter BIOS disabled. Using default SCSI "
-           "device parameters.\n", p->host_no);
-    if (p->type & AHC_ULTRA)
-      p->ultraenb = 0xffff;
-    p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B;
-  }
-  else
+  if (!(aic7xxx_no_reset))
   {
-    p->discenable = ~((aic_inb(p, DISC_DSB + 1) << 8) |
-        aic_inb(p, DISC_DSB));
-    if (p->type & AHC_ULTRA)
-      p->ultraenb = (aic_inb(p, ULTRA_ENB + 1) << 8) | 
-          aic_inb(p, ULTRA_ENB);
-  }
-
-  for (i = 0; i < max_targets; i++)
-  {
-    if (p->flags & AHC_USEDEFAULTS)
-    {
-      target_settings = 0;  /* 10 or 20 MHz depending on Ultra enable */
-      p->needsdtr_copy |= (0x01 << i);
-      p->needwdtr_copy |= (0x01 << i);
-      if (p->type & AHC_ULTRA)
-        ultraenable |= (0x01 << i);
-    }
-    else
-    {
-      target_settings = aic_inb(p, TARG_SCRATCH + i);
-      if (target_settings & 0x0F)
-      {
-        p->needsdtr_copy |= (0x01 << i);
-        /*
-         * Default to asynchronous transfers (0 offset)
-         */
-        target_settings &= 0xF0;
-      }
-      if (target_settings & 0x80)
-      {
-        p->needwdtr_copy |= (0x01 << i);
-        /*
-         * Clear the wide flag. When wide negotiation is successful,
-         * we'll enable it.
-         */
-        target_settings &= 0x7F;
-      }
-    }
-
     /*
-     * If we reset the bus, then clear the transfer ssettings, else leave
+     * If we reset the bus, then clear the transfer settings, else leave
      * them be
      */
-    if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) )
-      aic_outb(p, target_settings, TARG_SCRATCH + i);
-    if (p->needsdtr_copy & (0x01 << i))
+    for (i = 0; i < max_targets; i++)
     {
-      short sxfr, j;
-
-      sxfr = target_settings & SXFR;
-      if ((p->ultraenb & (1 << i)) != 0)
+      aic_outb(p, 0, TARG_SCSIRATE + i);
+      if (p->features & AHC_ULTRA2)
       {
-        /* Want an ultra speed in the table */
-        sxfr |= 0x100;
+        aic_outb(p, 0, TARG_OFFSET + i);
       }
-      for (j = 0; j < NUMBER(aic7xxx_syncrates); j++)
-      {
-        if (sxfr == aic7xxx_syncrates[j].rate)
-          break;
-      }
-      p->syncinfo[i].period = aic7xxx_syncrates[j].period;
-      p->syncinfo[i].offset =
-       (p->type & AHC_WIDE) ? MAX_OFFSET_16BIT : MAX_OFFSET_8BIT;
+      p->transinfo[i].cur_offset = 0;
+      p->transinfo[i].cur_period = 0;
+      p->transinfo[i].cur_width = MSG_EXT_WDTR_BUS_8_BIT;
     }
-    else
-    {
-      p->syncinfo[i].period = 0;
-      p->syncinfo[i].offset = 0;
-    }
-  }
 
-  /*
-   * If we are not wide, forget WDTR. This makes the driver
-   * work on some cards that don't leave these fields cleared
-   * when BIOS is not installed.
-   */
-  if ( !(p->type & AHC_WIDE))
-  {
-    p->needwdtr_copy = 0;
-  }
-  p->needsdtr = p->needsdtr_copy;
-  p->needwdtr = p->needwdtr_copy;
-
-  /*
-   * If we reset the bus, then clear the transfer ssettings, else leave
-   * them be
-   */
-  if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) )
-  {
+    /*
+     * If we reset the bus, then clear the transfer settings, else leave
+     * them be.
+     */
     aic_outb(p, 0, ULTRA_ENB);
     aic_outb(p, 0, ULTRA_ENB + 1);
+    p->ultraenb = 0;
   }
 
   /*
@@ -6600,21 +7757,35 @@
    * routine should only allocate contiguous memory, but note that
    * this could be a problem if kmalloc() is changed.
    */
-  if (p->scb_data->hscbs == NULL)
   {
     size_t array_size;
     unsigned int hscb_physaddr;
+    unsigned long temp;
 
     array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb);
-    p->scb_data->hscbs = kmalloc(array_size, GFP_ATOMIC);
+    if (p->scb_data->hscbs == NULL)
+    {
+      /*
+       * A little padding so we can align thing the way we want
+       */
+      p->scb_data->hscbs = kmalloc(array_size + 0x1f, GFP_ATOMIC);
+    }
     if (p->scb_data->hscbs == NULL)
     {
       printk("(scsi%d) Unable to allocate hardware SCB array; "
              "failing detection.\n", p->host_no);
-      release_region(p->base, MAXREG - MINREG);
-      free_irq(p->irq, p);
+      p->irq = 0;
       return(0);
     }
+    /*
+     * Save the actual kmalloc buffer pointer off, then align our
+     * buffer to a 32 byte boundary
+     */
+    p->scb_data->hscb_kmalloc_ptr = p->scb_data->hscbs;
+    temp = (unsigned long)p->scb_data->hscbs;
+    temp += 0x1f;
+    temp &= ~0x1f;
+    p->scb_data->hscbs = (struct aic7xxx_hwscb *)temp;
     /* At least the control byte of each SCB needs to be 0. */
     memset(p->scb_data->hscbs, 0, array_size);
 
@@ -6631,12 +7802,19 @@
     aic_outb(p, (hscb_physaddr >> 8) & 0xFF, SCBID_ADDR + 1);
     aic_outb(p, (hscb_physaddr >> 16) & 0xFF, SCBID_ADDR + 2);
     aic_outb(p, (hscb_physaddr >> 24) & 0xFF, SCBID_ADDR + 3);
+  }
 
-    /* The Q-FIFOs we just set up are all empty */
-    aic_outb(p, 0, QINPOS);
-    aic_outb(p, 0, KERNEL_QINPOS);
-    aic_outb(p, 0, QOUTPOS);
-
+  /* The Q-FIFOs we just set up are all empty */
+  aic_outb(p, 0, QINPOS);
+  aic_outb(p, 0, KERNEL_QINPOS);
+  aic_outb(p, 0, QOUTPOS);
+
+  if(p->features & AHC_QUEUE_REGS)
+  {
+    aic_outb(p, SCB_QSIZE_256, QOFF_CTLSTA);
+    aic_outb(p, 0, SDSCB_QOFF);
+    aic_outb(p, 0, SNSCB_QOFF);
+    aic_outb(p, 0, HNSCB_QOFF);
   }
 
   /*
@@ -6649,20 +7827,19 @@
    * Message out buffer starts empty
    */
   aic_outb(p, MSG_NOOP, MSG_OUT);
+  aic_outb(p, MSG_NOOP, LAST_MSG);
 
   /*
-   * Load the sequencer program, then re-enable the board -
-   * resetting the AIC-7770 disables it, leaving the lights
-   * on with nobody home. On the PCI bus you *may* be home,
-   * but then your mailing address is dynamically assigned
-   * so no one can find you anyway :-)
-   */
-  aic7xxx_loadseq(p);
-
-  if ( (p->type & AHC_AIC7770) == AHC_AIC7770 )
-  {
-    aic_outb(p, ENABLE, BCTL);  /* Enable the boards BUS drivers. */
-  }
+   * Set all the other asundry items that haven't been set yet.
+   * This includes just dumping init values to a lot of registers simply
+   * to make sure they've been touched and are ready for use parity wise
+   * speaking.
+   */
+  aic_outb(p, 0, TMODE_CMDADDR);
+  aic_outb(p, 0, TMODE_CMDADDR + 1);
+  aic_outb(p, 0, TMODE_CMDADDR + 2);
+  aic_outb(p, 0, TMODE_CMDADDR + 3);
+  aic_outb(p, 0, TMODE_CMDADDR_NEXT);
 
   /*
    * Link us into the list of valid hosts
@@ -6671,10 +7848,104 @@
   first_aic7xxx = p;
 
   /*
-   * Unpause the sequencer before returning and enable
-   * interrupts - we shouldn't get any until the first
-   * command is sent to us by the high-level SCSI code.
+   * Clear out any possible pending interrupts, again.
+   */
+  aic7xxx_clear_intstat(p);
+
+  /*
+   * Allocate the first set of scbs for this controller.  This is to stream-
+   * line code elsewhere in the driver.  If we have to check for the existence
+   * of scbs in certain code sections, it slows things down.  However, as
+   * soon as we register the IRQ for this card, we could get an interrupt that
+   * includes possibly the SCSI_RSTI interrupt.  If we catch that interrupt
+   * then we are likely to segfault if we don't have at least one chunk of
+   * SCBs allocated or add checks all through the reset code to make sure
+   * that the SCBs have been allocated which is an invalid running condition
+   * and therefore I think it's preferable to simply pre-allocate the first
+   * chunk of SCBs.
    */
+  aic7xxx_allocate_scb(p);
+
+  if ( !(aic7xxx_no_reset) )
+  {
+    if (p->features & AHC_TWIN)
+    {
+      if (aic7xxx_verbose & VERBOSE_PROBE2)
+        printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no);
+      aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL);
+      aic7xxx_reset_current_bus(p);
+      aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL);
+    }
+    /* Reset SCSI bus A. */
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
+    {  /* In case we are a 3940, 3985, or 7895, print the right channel */
+      char *channel = "";
+      if (p->flags & AHC_MULTI_CHANNEL)
+      {
+        channel = " A";
+        if (p->flags & (AHC_CHNLB|AHC_CHNLC))
+          channel = (p->flags & AHC_CHNLB) ? " B" : " C";
+      }
+      printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel);
+    }
+    
+    /*
+     * Some of the new Ultra2 chipsets need a longer delay after a chip
+     * reset than just the init setup creates, so we have to delay here
+     * before we go into a reset in order to make the chips happy.
+     */
+    if (p->features & AHC_ULTRA2)
+      mdelay(250);
+    aic7xxx_reset_current_bus(p);
+
+    /*
+     * Delay for the reset delay.
+     */
+    if (!reset_delay)
+      aic7xxx_delay(AIC7XXX_RESET_DELAY);
+  }
+  else
+  {
+    if (!reset_delay)
+    {
+      printk(KERN_INFO "(scsi%d) Not resetting SCSI bus.  Note: Don't use "
+             "the no_reset\n", p->host_no);
+      printk(KERN_INFO "(scsi%d) option unless you have a verifiable need "
+             "for it.\n", p->host_no);
+      printk(KERN_INFO "(scsi%d) The no_reset option is known to break some "
+             "systems,\n", p->host_no);
+      printk(KERN_INFO "(scsi%d) and is not supported by the driver author\n",
+             p->host_no);
+      aic7xxx_delay(AIC7XXX_RESET_DELAY);
+    }
+  }
+  
+  /*
+   * Register IRQ with the kernel.  Only allow sharing IRQs with
+   * PCI devices.
+   */
+  if (!(p->chip & AHC_PCI))
+  {
+    result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p));
+  }
+  else
+  {
+    result = (request_irq(p->irq, do_aic7xxx_isr, SA_SHIRQ,
+              "aic7xxx", p));
+    if (result < 0)
+    {
+      result = (request_irq(p->irq, do_aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
+              "aic7xxx", p));
+    }
+  }
+  if (result < 0)
+  {
+    printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring "
+           "controller.\n", p->host_no, p->irq);
+    p->irq = 0;
+    return (0);
+  }
+
   unpause_sequencer(p, /* unpause_always */ TRUE);
 
   return (found);
@@ -6691,12 +7962,9 @@
 int
 aic7xxx_chip_reset(struct aic7xxx_host *p)
 {
-  unsigned char hcntrl;
+  unsigned char sblkctl;
   int wait;
 
-  /* Retain the IRQ type across the chip reset. */
-  hcntrl = (aic_inb(p, HCNTRL) & IRQMS) | INTEN;
-
   /*
    * For some 274x boards, we must clear the CHIPRST bit and pause
    * the sequencer. For some reason, this makes the driver work.
@@ -6707,29 +7975,26 @@
    * In the future, we may call this function as a last resort for
    * error handling.  Let's be nice and not do any unecessary delays.
    */
-  wait = 1000;  /* 1 second (1000 * 1000 usec) */
-  while ((wait > 0) && ((aic_inb(p, HCNTRL) & CHIPRSTACK) == 0))
+  wait = 1000;  /* 1 second (1000 * 1 msec) */
+  while (--wait && !(aic_inb(p, HCNTRL) & CHIPRSTACK))
   {
-    mdelay(1);  /* 1 msec = 1000 usec */
-    wait = wait - 1;
-  }
-
-  if ((aic_inb(p, HCNTRL) & CHIPRSTACK) == 0)
-  {
-    printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
+    mdelay(1);  /* 1 msec */
   }
 
-  aic_outb(p, hcntrl | PAUSE, HCNTRL);
+  pause_sequencer(p);
 
-  switch( aic_inb(p, SBLKCTL) & 0x0a )
+  sblkctl = aic_inb(p, SBLKCTL) & (SELBUSB|SELWIDE);
+  if (p->chip & AHC_PCI)
+    sblkctl &= ~SELBUSB;
+  switch( sblkctl )
   {
     case 0:  /* normal narrow card */
       break;
     case 2:  /* Wide card */
-      p->type |= AHC_WIDE;
+      p->features |= AHC_WIDE;
       break;
     case 8:  /* Twin card */
-      p->type |= AHC_TWIN;
+      p->features |= AHC_TWIN;
       p->flags |= AHC_MULTI_CHANNEL;
       break;
     default: /* hmmm...we don't know what this is */
@@ -6753,6 +8018,7 @@
 {
   struct aic7xxx_host *p = NULL;
   struct Scsi_Host *host;
+  int i;
 
   /*
    * Allocate a storage area by registering us with the mid-level
@@ -6764,6 +8030,7 @@
   {
     p = (struct aic7xxx_host *) host->hostdata;
     memset(p, 0, sizeof(struct aic7xxx_host));
+    *p = *temp;
     p->host = host;
 
     p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC);
@@ -6778,35 +8045,20 @@
        * For some reason we don't have enough memory.  Free the
        * allocated memory for the aic7xxx_host struct, and return NULL.
        */
+      release_region(p->base, MAXREG - MINREG);
       scsi_unregister(host);
-      p = NULL;
+      return(NULL);
     }
-    if (p != NULL)
-    {
-      p->host_no = host->host_no;
-      p->base = temp->base;
-      p->mbase = temp->mbase;
-      p->maddr = temp->maddr;
-      p->flags = temp->flags;
-      p->type = temp->type;
-      p->unpause = temp->unpause;
-      p->pause = temp->pause;
-      p->pdev = temp->pdev;
-      p->pci_bus = temp->pci_bus;
-      p->pci_device_fn = temp->pci_device_fn;
-      p->bios_address = temp->bios_address;
-      p->irq = temp->irq;
-      p->scsi_id = temp->scsi_id;
-      p->scsi_id_b = temp->scsi_id_b;
-      p->discenable = temp->discenable;
-      p->ultraenb = temp->ultraenb;
-      p->tagenable = 0;
-      p->orderedtag = 0;
-      p->board_name_index = temp->board_name_index;
-      p->adapter_control = temp->adapter_control;
-      p->bios_control = temp->bios_control;
-      DRIVER_LOCK_INIT
+    p->host_no = host->host_no;
+    p->tagenable = 0;
+    p->orderedtag = 0;
+    for (i=0; i<MAX_TARGETS; i++)
+    {
+      p->transinfo[i].goal_period = 0;
+      p->transinfo[i].goal_offset = 0;
+      p->transinfo[i].goal_width = MSG_EXT_WDTR_BUS_8_BIT;
     }
+    DRIVER_LOCK_INIT
   }
   return (p);
 }
@@ -6822,28 +8074,35 @@
 static void
 aic7xxx_free(struct aic7xxx_host *p)
 {
-  int i, jump;
+  int i;
 
   /*
    * Free the allocated hardware SCB space.
    */
-  if (p->scb_data->hscbs != NULL)
+  if (p->scb_data != NULL)
   {
-    kfree(p->scb_data->hscbs);
-  }
-  /*
-   * Free the driver SCBs.  These were allocated on an as-need
-   * basis. However, we allocated them 30 at a time up until the
-   * very last allocation (if there was one).  So, we need to free
-   * every 30th pointer to free the array (this also frees the
-   * SG_array structs as well).
-   *
-   * Note, on 64 bit machines we allocate 29 at a time instead.
-   */
-  jump = (sizeof(int) == sizeof(void *)) ? 30 : 29;
-  for (i = 0; i < p->scb_data->numscbs; i += jump)
-  {
-    kfree(p->scb_data->scb_array[i]);
+    if (p->scb_data->hscbs != NULL)
+    {
+      kfree(p->scb_data->hscb_kmalloc_ptr);
+      p->scb_data->hscbs = p->scb_data->hscb_kmalloc_ptr = NULL;
+    }
+    /*
+     * Free the driver SCBs.  These were allocated on an as-need
+     * basis.  We allocated these in groups depending on how many
+     * we could fit into a given amount of RAM.  The tail SCB for
+     * these allocations has a pointer to the alloced area.
+     */
+    for (i = 0; i < p->scb_data->numscbs; i++)
+    {
+      if (p->scb_data->scb_array[i]->kmalloc_ptr != NULL)
+        kfree(p->scb_data->scb_array[i]->kmalloc_ptr);
+      p->scb_data->scb_array[i] = NULL;
+    }
+  
+    /*
+     * Free the SCB data area.
+     */
+    kfree(p->scb_data);
   }
 
   /*
@@ -6858,10 +8117,6 @@
       kfree(p->dev_sdtr_cmnd[i]);
   }
 
-  /*
-   * Free the SCB data area.
-   */
-  kfree(p->scb_data);
 }
 
 /*+F*************************************************************************
@@ -6872,12 +8127,12 @@
  *   Load the seeprom and configure adapter and target settings.
  *   Returns 1 if the load was successful and 0 otherwise.
  *-F*************************************************************************/
-static int
-load_seeprom (struct aic7xxx_host *p, unsigned char *sxfrctl1)
+static void
+aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
 {
   int have_seeprom = 0;
-  int i, max_targets;
-  unsigned char target_settings, scsi_conf;
+  int i, max_targets, mask;
+  unsigned char scsirate, scsi_conf;
   unsigned short scarray[128];
   struct seeprom_config *sc = (struct seeprom_config *) scarray;
 
@@ -6885,42 +8140,31 @@
   {
     printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
   }
-  switch (p->type & 0x00001ff1)
+  switch (p->chip)
   {
-    case AHC_AIC7770:  /* None of these adapters have seeproms. */
-    case AHC_274:
+    case (AHC_AIC7770|AHC_EISA):  /* None of these adapters have seeproms. */
+      if (aic_inb(p, SCSICONF) & TERM_ENB)
+        p->flags |= AHC_TERM_ENB_A;
+      if ( (p->features & AHC_TWIN) && (aic_inb(p, SCSICONF + 1) & TERM_ENB) )
+        p->flags |= AHC_TERM_ENB_B;
       break;
 
-    case AHC_284:
+    case (AHC_AIC7770|AHC_VL):
       have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
       break;
 
-    case AHC_AIC7850:  /* The 2910B is a 7850 with a seeprom. */
-    case AHC_294AU:
-    case AHC_AIC7870:  /* For these controllers we try the three possible */
-    case AHC_AIC7895:  /* SEEPROM read types.  If none works, then we are */
-    case AHC_294:      /* SOL.  This should catch any SEEPROM variety     */
-    case AHC_394:      /* Adaptec or some motherboard manufacturer might  */
-    case AHC_294U:     /* throw at us, and since we perform a checksum    */
-    case AHC_394U:     /* during the read, we should get bogus seeprom    */
-    case AHC_AIC7860:  /* reads. */
-    case AHC_AIC7880:
-    case AHC_398:
-    case AHC_398U:
+    default:
       have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
-                                  scarray, sizeof(*sc)/2, C46);
-      if (!have_seeprom)
-        have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
-                                  scarray, sizeof(scarray)/2, C46);
-      if (!have_seeprom)
-        have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
-                                  scarray, sizeof(*sc)/2, C56_66);
+                                  scarray, p->sc_size, p->sc_type);
       if (!have_seeprom)
-        have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
-                                  scarray, sizeof(scarray)/2, C56_66);
-      break;
-
-    default:
+      {
+        if(p->sc_type == C46)
+          have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                      scarray, p->sc_size, C56_66);
+        else
+          have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                      scarray, p->sc_size, C46);
+      }
       break;
   }
 
@@ -6928,10 +8172,31 @@
   {
     if (aic7xxx_verbose & VERBOSE_PROBE2)
     {
-      printk("\naic7xxx: No SEEPROM available; using defaults.\n");
+      printk("\naic7xxx: No SEEPROM available.\n");
+    }
+    p->flags |= AHC_NEWEEPROM_FMT;
+    if (aic_inb(p, SCSISEQ) == 0)
+    {
+      p->flags |= AHC_USEDEFAULTS;
+      p->flags &= ~AHC_BIOS_ENABLED;
+      p->scsi_id = p->scsi_id_b = 7;
+      *sxfrctl1 |= STPWEN;
+      if (aic7xxx_verbose & VERBOSE_PROBE2)
+      {
+        printk("aic7xxx: Using default values.\n");
+      }
+    }
+    else if (aic7xxx_verbose & VERBOSE_PROBE2)
+    {
+      printk("aic7xxx: Using leftover BIOS values.\n");
     }
-    p->flags |= AHC_USEDEFAULTS;
-    p->flags &= ~AHC_BIOS_ENABLED;
+    if ( *sxfrctl1 & STPWEN )
+    {
+      p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
+      sc->adapter_control &= ~CFAUTOTERM;
+      sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM;
+    }
+    p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B;
   }
   else
   {
@@ -6941,15 +8206,25 @@
     }
 
     /*
+     * Note things in our flags
+     */
+    p->flags |= AHC_SEEPROM_FOUND;
+
+    /*
      * Update the settings in sxfrctl1 to match the termination settings.
      */
     *sxfrctl1 = 0;
 
     /*
+     * Get our SCSI ID from the SEEPROM setting...
+     */
+    p->scsi_id = (sc->brtime_id & CFSCSIID);
+
+    /*
      * First process the settings that are different between the VLB
      * and PCI adapter seeproms.
      */
-    if (p->type & AHC_284)
+    if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7770)
     {
       /* VLB adapter seeproms */
       if (sc->bios_control & CF284XEXTEND)
@@ -6958,42 +8233,42 @@
       if (sc->adapter_control & CF284XSTERM)
       {
         *sxfrctl1 |= STPWEN;
-        p->flags |= AHC_TERM_ENB_A;
+        p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
       }
-      /*
-       * The 284x SEEPROM doesn't have a max targets field.  We
-       * set it to 16 to make sure we take care of the 284x-wide
-       * adapters.  For narrow adapters, going through the extra
-       * 8 target entries will not cause any harm since they will
-       * will not be used.
-       *
-       * XXX - We should probably break out the bus detection
-       *       from the register function so we can use it here
-       *       to tell us how many targets there really are.
-       */
-      max_targets = 16;
     }
     else
     {
       /* PCI adapter seeproms */
       if (sc->bios_control & CFEXTEND)
         p->flags |= AHC_EXTEND_TRANS_A;
+      if (sc->bios_control & CFBIOSEN)
+        p->flags |= AHC_BIOS_ENABLED;
+      else
+        p->flags &= ~AHC_BIOS_ENABLED;
 
       if (sc->adapter_control & CFSTERM)
       {
         *sxfrctl1 |= STPWEN;
-        p->flags |= AHC_TERM_ENB_A;
+        p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
       }
-
-      /* Limit to 16 targets just in case. */
-      max_targets = MIN(sc->max_targets & CFMAXTARG, 16);
     }
+    p->sc = *sc;
+  }
 
-    p->discenable = 0;
+  p->discenable = 0;
+    
+  /*
+   * Limit to 16 targets just in case.  The 2842 for one is known to
+   * blow the max_targets setting, future cards might also.
+   */
+  max_targets = MIN(sc->max_targets & CFMAXTARG,
+                   ((p->features & (AHC_TWIN | AHC_WIDE)) ? 16 : 8));
 
+  if (have_seeprom)
+  {
     for (i = 0; i < max_targets; i++)
     {
-      if( (p->type & AHC_ULTRA) &&
+      if( (p->features & AHC_ULTRA) &&
          !(sc->adapter_control & CFULTRAEN) &&
           (sc->device_flags[i] & CFSYNCHISULTRA) )
       {
@@ -7001,114 +8276,203 @@
         break;
       }
     }
+  }
 
-    for (i = 0; i < max_targets; i++)
+  for (i = 0; i < max_targets; i++)
+  {
+    mask = (0x01 << i);
+    if (!have_seeprom)
+    {
+      if(aic_inb(p, SCSISEQ) != 0)
+      {
+        /*
+         * OK...the BIOS set things up and left behind the settings we need.
+         * Just make our sc->device_flags[i] entry match what the card has
+         * set for this device.
+         */
+        p->discenable = 
+          ~(aic_inb(p, DISC_DSB) | (aic_inb(p, DISC_DSB + 1) << 8) );
+        p->ultraenb =
+          (aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8) );
+        sc->device_flags[i] = (p->discenable & mask) ? CFDISC : 0;
+        if (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER)
+          sc->device_flags[i] |= CFWIDEB;
+        if (p->features & AHC_ULTRA2)
+        {
+          if (aic_inb(p, TARG_OFFSET + i))
+          {
+            sc->device_flags[i] |= CFSYNCH;
+            sc->device_flags[i] |= (aic_inb(p, TARG_SCSIRATE + i) & 0x07);
+            if ( (aic_inb(p, TARG_SCSIRATE + i) & 0x18) == 0x18 )
+              sc->device_flags[i] |= CFSYNCHISULTRA;
+          }
+        }
+        else
+        {
+          if (aic_inb(p, TARG_SCSIRATE + i) & ~WIDEXFER)
+          {
+            sc->device_flags[i] |= CFSYNCH;
+            if (p->features & AHC_ULTRA)
+              sc->device_flags[i] |= ((p->ultraenb & mask) ?
+                                      CFSYNCHISULTRA : 0);
+          }
+        }
+      }
+      else
+      {
+        /*
+         * Assume the BIOS has NOT been run on this card and nothing between
+         * the card and the devices is configured yet.
+         */
+        sc->device_flags[i] = CFDISC;
+        if (p->features & AHC_WIDE)
+          sc->device_flags[i] |= CFWIDEB;
+        if (p->features & AHC_ULTRA2)
+          sc->device_flags[i] |= 3;
+        else if (p->features & AHC_ULTRA)
+          sc->device_flags[i] |= CFSYNCHISULTRA;
+        sc->device_flags[i] |= CFSYNCH;
+        aic_outb(p, 0, TARG_SCSIRATE + i);
+        if (p->features & AHC_ULTRA2)
+          aic_outb(p, 0, TARG_OFFSET + i);
+      }
+    }
+    if (sc->device_flags[i] & CFDISC)
+    {
+      p->discenable |= mask;
+    }
+    if (p->flags & AHC_NEWEEPROM_FMT)
     {
-      target_settings = (sc->device_flags[i] & CFXFER) << 4;
-      if (sc->device_flags[i] & CFSYNCH)
-      {
-        target_settings |= SOFS;
-      }
-      if (sc->device_flags[i] & CFWIDEB)
+      if (sc->device_flags[i] & CFSYNCHISULTRA)
       {
-        target_settings |= WIDEXFER;
+        p->ultraenb |= mask;
       }
-      if (sc->device_flags[i] & CFDISC)
+    }
+    else if (sc->adapter_control & CFULTRAEN)
+    {
+      p->ultraenb |= mask;
+    }
+    if ( (sc->device_flags[i] & CFSYNCH) == 0)
+    {
+      sc->device_flags[i] &= ~CFXFER;
+      p->ultraenb &= ~mask;
+      p->transinfo[i].user_offset = 0;
+      p->transinfo[i].user_period = 0;
+      p->transinfo[i].cur_offset = 0;
+      p->transinfo[i].cur_period = 0;
+      p->needsdtr_copy &= ~mask;
+    }
+    else
+    {
+      if (p->features & AHC_ULTRA2)
       {
-        p->discenable |= (0x01 << i);
+        p->transinfo[i].user_offset = MAX_OFFSET_ULTRA2;
+        p->transinfo[i].cur_offset = aic_inb(p, TARG_OFFSET + i);
+        scsirate = (sc->device_flags[i] & CFXFER) |
+                   ((p->ultraenb & mask) ? 0x18 : 0x10);
+        p->transinfo[i].user_period = aic7xxx_find_period(p, scsirate,
+                                       AHC_SYNCRATE_ULTRA2);
+        p->transinfo[i].cur_period = aic7xxx_find_period(p,
+                                       aic_inb(p, TARG_SCSIRATE + i),
+                                       AHC_SYNCRATE_ULTRA2);
       }
-      if (p->flags & AHC_NEWEEPROM_FMT)
+      else
       {
-        if (sc->device_flags[i] & CFSYNCHISULTRA)
+        scsirate = (sc->device_flags[i] & CFXFER) << 4;
+        if (sc->device_flags[i] & CFWIDEB)
+          p->transinfo[i].user_offset = MAX_OFFSET_16BIT;
+        else
+          p->transinfo[i].user_offset = MAX_OFFSET_8BIT;
+        if (p->features & AHC_ULTRA)
         {
-          p->ultraenb |= (0x01 << i);
+          short ultraenb;
+          ultraenb = aic_inb(p, ULTRA_ENB) |
+            (aic_inb(p, ULTRA_ENB + 1) << 8);
+          p->transinfo[i].user_period = aic7xxx_find_period(p,
+                                          scsirate,
+                                          (p->ultraenb & mask) ?
+                                          AHC_SYNCRATE_ULTRA :
+                                          AHC_SYNCRATE_FAST);
+          p->transinfo[i].cur_period = aic7xxx_find_period(p,
+                                         aic_inb(p, TARG_SCSIRATE + i),
+                                         (ultraenb & mask) ? 
+                                         AHC_SYNCRATE_ULTRA :
+                                         AHC_SYNCRATE_FAST);
         }
+        else
+          p->transinfo[i].user_period = aic7xxx_find_period(p,
+                                          scsirate, AHC_SYNCRATE_FAST);
       }
-      else if (sc->adapter_control & CFULTRAEN)
-      {
-        p->ultraenb |= (0x01 << i);
-      }
-      if ( ((target_settings & 0x70) == 0x40) &&
-           (p->ultraenb & (0x01 << i)) )
-      {
-        target_settings &= ~0x70;
-        p->ultraenb &= ~(0x01 << i);
-      }
-      /*
-       * Don't output these settings if we aren't resetting the bus, instead,
-       * leave the devices current settings in place
-       */
-      if (!(aic7xxx_no_reset))
-        aic_outb(p, target_settings, TARG_SCRATCH + i);
+      p->needsdtr_copy |= mask;
     }
-    aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB);
-    aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1);
-    aic_outb(p,  (p->ultraenb & 0xFF), ULTRA_ENB);
-    aic_outb(p,  ((p->ultraenb >> 8) & 0xFF), ULTRA_ENB + 1);
-
-    p->scsi_id = sc->brtime_id & CFSCSIID;
-    p->adapter_control = sc->adapter_control;
-    p->bios_control = sc->bios_control;
-
-    if (p->bios_control & CFBIOSEN)
+    if ( (sc->device_flags[i] & CFWIDEB) && (p->features & AHC_WIDE) )
     {
-      p->flags &= ~AHC_USEDEFAULTS;
-      p->flags |= AHC_BIOS_ENABLED;
+      p->transinfo[i].user_width = MSG_EXT_WDTR_BUS_16_BIT;
+      p->needwdtr_copy |= mask;
     }
     else
     {
-      p->flags &= ~AHC_BIOS_ENABLED;
-      p->flags |= AHC_USEDEFAULTS;
+      p->transinfo[i].user_width = MSG_EXT_WDTR_BUS_8_BIT;
+      p->needwdtr_copy &= ~mask;
     }
+    p->transinfo[i].cur_width =
+      (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER) ?
+      MSG_EXT_WDTR_BUS_16_BIT : MSG_EXT_WDTR_BUS_8_BIT;
+  }
+  aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB);
+  aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1);
+  p->needwdtr = p->needwdtr_copy;
+  p->needsdtr = p->needsdtr_copy;
+  p->wdtr_pending = p->sdtr_pending = 0;
+
+  /*
+   * We set the p->ultraenb from the SEEPROM to begin with, but now we make
+   * it match what is already down in the card.  If we are doing a reset
+   * on the card then this will get put back to a default state anyway.
+   * This allows us to not have to pre-emptively negotiate when using the
+   * no_reset option.
+   */
+  if (p->features & AHC_ULTRA)
+    p->ultraenb = aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8);
+
+  
+  scsi_conf = (p->scsi_id & HSCSIID);
+
+  if(have_seeprom)
+  {
+    p->adapter_control = sc->adapter_control;
+    p->bios_control = sc->bios_control;
 
-    if ((p->type & 0x1ff1) == AHC_AIC7895)
+    switch (p->chip & AHC_CHIPID_MASK)
     {
-      if (p->adapter_control & CFBPRIMARY)
-        p->flags |= AHC_CHANNEL_B_PRIMARY;
+      case AHC_AIC7895:
+      case AHC_AIC7896:
+        if (p->adapter_control & CFBPRIMARY)
+          p->flags |= AHC_CHANNEL_B_PRIMARY;
+      default:
+        break;
     }
 
-    scsi_conf = (p->scsi_id & 0x7);
     if (sc->adapter_control & CFSPARITY)
       scsi_conf |= ENSPCHK;
-    /*
-     * The 7850 controllers with a seeprom, do not honor the CFRESETB
-     * flag in the seeprom.  Assume that we want to reset the SCSI bus.
-     */
-    if (sc->adapter_control & CFRESETB)
-      scsi_conf |= RESET_SCSI;
-    /*
-     * We may be a 2842, if so, preserve the TERM_ENB bit in scsi conf
-     */
-    if ( (p->flags & AHC_TERM_ENB_A) && 
-        ((p->type & AHC_AIC7770) == AHC_AIC7770) )
-      scsi_conf |= TERM_ENB;
-    /*
-     * If this is an Ultra card, is Ultra mode enabled?  If not, disable
-     * it in the host struct as well
-     */
-    if ( (p->type & AHC_ULTRA) && 
-        !(sc->adapter_control & CFULTRAEN) &&
-        !(p->flags & AHC_NEWEEPROM_FMT) )
-      p->type &= ~AHC_ULTRA;
+  }
+  else
+  {
+    scsi_conf |= ENSPCHK | RESET_SCSI;
+  }
 
+  /*
+   * Only set the SCSICONF and SCSICONF + 1 registers if we are a PCI card.
+   * The 2842 and 2742 cards already have these registers set and we don't
+   * want to muck with them since we don't set all the bits they do.
+   */
+  if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI )
+  {
     /* Set the host ID */
     aic_outb(p, scsi_conf, SCSICONF);
     /* In case we are a wide card */
     aic_outb(p, p->scsi_id, SCSICONF + 1);
-
-    if ((p->type & AHC_AIC7860) == AHC_AIC7860)
-    {
-      if ( aic_inb(p, SPIOCAP) & SSPIOCPS )
-      /*
-       * Update the settings in sxfrctl1 to match the termination
-       * settings.
-       */
-        configure_termination(p, sxfrctl1, sc->adapter_control, max_targets);
-    }
-    else if (have_seeprom && ((p->type & AHC_AIC7770) != AHC_AIC7770))
-      configure_termination(p, sxfrctl1, sc->adapter_control, max_targets);
   }
-  return (have_seeprom);
 }
 
 /*+F*************************************************************************
@@ -7131,7 +8495,7 @@
   struct aic7xxx_host *list_p = NULL;
   int found = 0;
   ahc_flag_type flags = 0;
-  ahc_type type;
+  int type;
   unsigned char sxfrctl1;
 #if defined(__i386__) || defined(__alpha__)
   unsigned char hcntrl, hostconf;
@@ -7165,7 +8529,7 @@
    * EISA/VL-bus card signature probe.
    */
   slot = MINSLOT;
-  while (slot <= MAXSLOT)
+  while ( (slot <= MAXSLOT) && !(aic7xxx_no_probe) )
   {
     base = SLOTBASE(slot) + MINREG;
 
@@ -7179,27 +8543,11 @@
       continue; /* back to the beginning of the for loop */
     }
     flags = 0;
-    type = aic7xxx_probe(slot, base + HID0, &flags);
-    switch (type)
+    type = aic7xxx_probe(slot, base + AHC_HID0, &flags);
+    if (type == -1)
     {
-      case AHC_AIC7770:
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk("aic7xxx: <%s> at EISA %d\n",
-               board_names[2], slot);
-        break;
-      case AHC_274:
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk("aic7xxx: <%s> at EISA %d\n",
-               board_names[3], slot);
-        break;
-      case AHC_284:
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk("aic7xxx: <%s> at VLB %d\n",
-               board_names[4], slot);
-        break;
-      default:
-        slot++;
-        continue; /* back to the beginning of the while loop */
+      slot++;
+      continue;
     }
     temp_p = kmalloc(sizeof(struct aic7xxx_host), GFP_ATOMIC);
     if (temp_p == NULL)
@@ -7222,8 +8570,6 @@
     temp_p->unpause = hcntrl | INTEN;
     temp_p->pause = hcntrl | PAUSE | INTEN;
     temp_p->base = base;
-    temp_p->type = type;
-    temp_p->flags = flags | AHC_PAGESCBS;
     temp_p->mbase = 0;
     temp_p->maddr = 0;
     temp_p->pci_bus = 0;
@@ -7234,6 +8580,8 @@
       temp_p->irq = 0;
     else
       temp_p->irq = aic_inb(temp_p, INTDEF) & 0x0F;
+    temp_p->flags |= AHC_PAGESCBS;
+
     switch (temp_p->irq)
     {
       case 9:
@@ -7256,6 +8604,7 @@
      * We are commited now, everything has been checked and this card
      * has been found, now we just set it up
      */
+
     /*
      * Insert our new struct into the list at the end
      */
@@ -7277,12 +8626,18 @@
         temp_p->flags |= AHC_EXTEND_TRANS_B;
     }
 
-    switch (temp_p->type & 0x1ff1)
+    switch (type)
     {
-      case AHC_AIC7770:
+      case 0:
         temp_p->board_name_index = 2;
-      case AHC_274:
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk("aic7xxx: <%s> at EISA %d\n",
+               board_names[2], slot);
+        /* FALLTHROUGH */
+      case 1:
       {
+        temp_p->chip = AHC_AIC7770 | AHC_EISA;
+        temp_p->features |= AHC_AIC7770_FE;
         temp_p->bios_control = aic_inb(temp_p, HA_274_BIOSCTRL);
 
         /*
@@ -7291,7 +8646,12 @@
          * the mid-level SCSI code which channel is primary.
          */
         if (temp_p->board_name_index == 0)
+        {
           temp_p->board_name_index = 3;
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk("aic7xxx: <%s> at EISA %d\n",
+                 board_names[3], slot);
+        }
         if (temp_p->bios_control & CHANNEL_B_PRIMARY)
         {
           temp_p->flags |= AHC_CHANNEL_B_PRIMARY;
@@ -7299,7 +8659,6 @@
 
         if ((temp_p->bios_control & BIOSMODE) == BIOSDISABLED)
         {
-          temp_p->flags |= AHC_USEDEFAULTS;
           temp_p->flags &= ~AHC_BIOS_ENABLED;
         }
         else
@@ -7308,79 +8667,46 @@
           temp_p->flags |= AHC_BIOS_ENABLED;
           if ( (temp_p->bios_control & 0x20) == 0 )
           {
-            switch(temp_p->bios_control & 0x07)
-            {
-              case 0x0:
-                temp_p->bios_address = 0xcc000;
-                break;
-              case 0x1:
-                temp_p->bios_address = 0xd0000;
-                break;
-              case 0x2:
-                temp_p->bios_address = 0xd4000;
-                break;
-              case 0x3:
-                temp_p->bios_address = 0xd8000;
-                break;
-              case 0x4:
-                temp_p->bios_address = 0xdc000;
-                break;
-              case 0x5:
-                temp_p->bios_address = 0xe0000;
-                break;
-              case 0x6:
-                temp_p->bios_address = 0xe4000;
-                break;
-              case 0x7:
-                temp_p->bios_address = 0xe8000;
-                break;
-              default:
-                break; /* can't get here */
-            }
+            temp_p->bios_address = 0xcc000;
+            temp_p->bios_address += (0x4000 * (temp_p->bios_control & 0x07));
           }
           else
           {
-            switch(temp_p->bios_control & 0x06)
-            {
-              case 0x0:
-                temp_p->bios_address = 0xd0000;
-                break;
-              case 0x2:
-                temp_p->bios_address = 0xd8000;
-                break;
-              case 0x4:
-                temp_p->bios_address = 0xe0000;
-                break;
-              case 0x6:
-                temp_p->bios_address = 0xe8000;
-                break;
-              default:
-                break; /* can't get here */
-            }
+            temp_p->bios_address = 0xd0000;
+            temp_p->bios_address += (0x8000 * (temp_p->bios_control & 0x06));
           }
         }
         temp_p->adapter_control = aic_inb(temp_p, SCSICONF) << 8;
         temp_p->adapter_control |= aic_inb(temp_p, SCSICONF + 1);
-        if (temp_p->flags & AHC_USEDEFAULTS)
+        if (temp_p->features & AHC_WIDE)
         {
-          temp_p->scsi_id = temp_p->scsi_id_b = 7;
-          temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B;
+          temp_p->scsi_id = temp_p->adapter_control & HWSCSIID;
+          temp_p->scsi_id_b = temp_p->scsi_id;
         }
         else
         {
-          if ( ((temp_p->adapter_control >> 8) & TERM_ENB) != 0 )
-            temp_p->flags |= AHC_TERM_ENB_A;
-          if ( (temp_p->adapter_control & TERM_ENB) != 0 )
-            temp_p->flags |= AHC_TERM_ENB_B;
           temp_p->scsi_id = (temp_p->adapter_control >> 8) & HSCSIID;
           temp_p->scsi_id_b = temp_p->adapter_control & HSCSIID;
         }
+        aic7xxx_load_seeprom(temp_p, &sxfrctl1);
         break;
       }
 
-      case AHC_284:
-        load_seeprom(temp_p, &sxfrctl1);
+      case 2:
+      case 3:
+        temp_p->chip = AHC_AIC7770 | AHC_VL;
+        temp_p->features |= AHC_AIC7770_FE;
+        if (type == 2)
+          temp_p->flags |= AHC_BIOS_ENABLED;
+        else
+          temp_p->flags &= ~AHC_BIOS_ENABLED;
+        if (aic_inb(temp_p, SCSICONF) & TERM_ENB)
+          sxfrctl1 = STPWEN;
+        aic7xxx_load_seeprom(temp_p, &sxfrctl1);
         temp_p->board_name_index = 4;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk("aic7xxx: <%s> at VLB %d\n",
+               board_names[2], slot);
         switch( aic_inb(temp_p, STATUS_2840) & BIOS_SEL )
         {
           case 0x00:
@@ -7439,42 +8765,84 @@
     {
       unsigned short      vendor_id;
       unsigned short      device_id;
-      ahc_type            type;
+      ahc_chip            chip;
       ahc_flag_type       flags;
+      ahc_feature         features;
       int                 board_name_index;
-    } const aic7xxx_pci_devices[] = {
+      unsigned short      seeprom_size;
+      unsigned short      seeprom_type;
+    } const aic_pdevs[] = {
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7810, AHC_NONE,
-       AHC_FNONE,                                            1 },
+       AHC_FNONE, AHC_FENONE,                                1,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AHC_AIC7850,
-       AHC_PAGESCBS | AHC_USEDEFAULTS,                       5 },
+       AHC_PAGESCBS, AHC_AIC7850_FE,                         5,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850,
-       AHC_PAGESCBS | AHC_USEDEFAULTS,                       6 },
+       AHC_PAGESCBS, AHC_AIC7850_FE,                         6,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,  7 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_294AU,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,  8 },
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       8,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AHC_AIC7870,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                      9 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_294,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     10 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_394,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     11 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_398,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     12 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_294,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     13 },
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE,      9,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_AIC7870,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE,     10,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_AIC7870,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7870_FE,                                      11,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_AIC7870,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7870_FE,                                      12,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_AIC7870,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE,     13,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AHC_AIC7880,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     14 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_294U,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     15 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_394U,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     16 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_398U,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     17 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_294U,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     18 },
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     14,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     15,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7880_FE,                                      16,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7880_FE,                                      17,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, 19 }
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7895_FE,                                      19,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      20,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      21,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7896_FE,                                      22,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7896_FE,                                      23,
+       32, C56_66 },
     };
 
     unsigned short command;
@@ -7482,7 +8850,6 @@
 #ifdef MMAPIO
     unsigned long page_offset, base;
 #endif
-    struct aic7xxx_host *first_7895 = NULL;
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
     struct pci_dev *pdev = NULL;
 #else
@@ -7491,17 +8858,17 @@
     unsigned char pci_bus, pci_devfn, pci_irq;
 #endif
 
-    for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++)
+    for (i = 0; i < NUMBER(aic_pdevs); i++)
     {
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
       pdev = NULL;
-      while ((pdev = pci_find_device(aic7xxx_pci_devices[i].vendor_id,
-                                     aic7xxx_pci_devices[i].device_id,
+      while ((pdev = pci_find_device(aic_pdevs[i].vendor_id,
+                                     aic_pdevs[i].device_id,
                                      pdev)))
 #else
       index = 0;
-      while (!(pcibios_find_device(aic7xxx_pci_devices[i].vendor_id,
-                                   aic7xxx_pci_devices[i].device_id,
+      while (!(pcibios_find_device(aic_pdevs[i].vendor_id,
+                                   aic_pdevs[i].device_id,
                                    index++, &pci_bus, &pci_devfn)) )
 #endif
       {
@@ -7518,9 +8885,12 @@
                                     GFP_ATOMIC)) != NULL )
         {
           memset(temp_p, 0, sizeof(struct aic7xxx_host));
-          temp_p->type = aic7xxx_pci_devices[i].type;
-          temp_p->flags = aic7xxx_pci_devices[i].flags;
-          temp_p->board_name_index = aic7xxx_pci_devices[i].board_name_index;
+          temp_p->chip = aic_pdevs[i].chip | AHC_PCI;
+          temp_p->flags = aic_pdevs[i].flags;
+          temp_p->features = aic_pdevs[i].features;
+          temp_p->board_name_index = aic_pdevs[i].board_name_index;
+          temp_p->sc_size = aic_pdevs[i].seeprom_size;
+          temp_p->sc_type = aic_pdevs[i].seeprom_type;
 
           /*
            * Read sundry information from PCI BIOS.
@@ -7532,19 +8902,29 @@
           temp_p->pci_device_fn = pdev->devfn;
           temp_p->base = pdev->base_address[0];
           temp_p->mbase = pdev->base_address[1];
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk("aic7xxx: <%s> at PCI %d/%d\n", 
+              board_names[aic_pdevs[i].board_name_index],
+              PCI_SLOT(temp_p->pdev->devfn),
+              PCI_FUNC(temp_p->pdev->devfn));
           pci_read_config_word(pdev, PCI_COMMAND, &command);
           if (aic7xxx_verbose & VERBOSE_PROBE2)
           {
             printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n",
               (int)command);
           }
+#ifdef AIC7XXX_STRICT_PCI_SETUP
           command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
             PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER |
             PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#else
+          command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#endif
           if (aic7xxx_pci_parity == 0)
             command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
           pci_write_config_word(pdev, PCI_COMMAND, command);
-          pci_read_config_dword(pdev, PCI_COMMAND, &devconfig);
+#ifdef AIC7XXX_STRICT_PCI_SETUP
+          pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
           if (aic7xxx_verbose & VERBOSE_PROBE2)
           {
             printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig);
@@ -7558,15 +8938,16 @@
           {
             devconfig |= 0x00000008;
           }
-          pci_write_config_dword(pdev, PCI_COMMAND, devconfig);
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-            printk("aic7xxx: <%s> at PCI %d/%d\n", 
-              board_names[aic7xxx_pci_devices[i].board_name_index],
-              PCI_SLOT(temp_p->pdev->devfn),
-              PCI_FUNC(temp_p->pdev->devfn));
-#else
+          pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+#endif /* AIC7XXX_STRICT_PCI_SETUP */
+#else  /* LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) */
           temp_p->pci_bus = pci_bus;
           temp_p->pci_device_fn = pci_devfn;
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk("aic7xxx: <%s> at PCI %d/%d\n", 
+              board_names[aic_pdevs[i].board_name_index],
+              PCI_SLOT(temp_p->pci_device_fn),
+              PCI_FUNC(temp_p->pci_device_fn));
           pcibios_read_config_byte(pci_bus, pci_devfn, PCI_INTERRUPT_LINE,
             &pci_irq);
           temp_p->irq = pci_irq;
@@ -7582,12 +8963,17 @@
             printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n",
               (int)command);
           }
+#ifdef AIC7XXX_STRICT_PCI_SETUP
           command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
             PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER |
             PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#else
+          command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#endif
           if (aic7xxx_pci_parity == 0)
             command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
           pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND, command);
+#ifdef AIC7XXX_STRICT_PCI_SETUP
           pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, &devconfig);
           if (aic7xxx_verbose & VERBOSE_PROBE2)
           {
@@ -7603,12 +8989,8 @@
             devconfig |= 0x00000008;
           }
           pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, devconfig);
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-            printk("aic7xxx: <%s> at PCI %d/%d\n", 
-              board_names[aic7xxx_pci_devices[i].board_name_index],
-              PCI_SLOT(temp_p->pci_device_fn),
-              PCI_FUNC(temp_p->pci_device_fn));
-#endif
+#endif /* AIC7XXX_STRICT_PCI_SETUP */
+#endif /* LINUIX_VERSION_CODE > KERNEL_VERSION(2,1,92) */
 
           /*
            * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so
@@ -7616,49 +8998,24 @@
            */
           temp_p->base &= PCI_BASE_ADDRESS_IO_MASK;
           temp_p->mbase &= PCI_BASE_ADDRESS_MEM_MASK;
-          temp_p->unpause = (aic_inb(temp_p, HCNTRL) & IRQMS) | INTEN;
+          temp_p->unpause = INTEN;
           temp_p->pause = temp_p->unpause | PAUSE;
 
 #ifdef MMAPIO
-          if((temp_p->type & AHC_AIC7850) != AHC_AIC7850)
-          {
-            base = temp_p->mbase & PAGE_MASK;
-            page_offset = temp_p->mbase - base;
-            /*
-             * replace the next line with this one if you are using 2.1.x:
-             * temp_p->maddr = ioremap(base, page_offset + 256);
-             */
+          base = temp_p->mbase & PAGE_MASK;
+          page_offset = temp_p->mbase - base;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-            temp_p->maddr = ioremap(base, page_offset + 256);
+          temp_p->maddr = ioremap_nocache(base, page_offset + 256);
 #else
-            temp_p->maddr = vremap(base, page_offset + 256);
+          temp_p->maddr = vremap(base, page_offset + 256);
 #endif
-            if(temp_p->maddr)
-            {
-              temp_p->maddr += page_offset;
-            }
-          }
-          else
+          if(temp_p->maddr)
           {
-#ifdef __i386__
-            /*
-             * Resort to PIO mode on these controllers and Intel hardware.
-             * For other hardware we need to either disable these controllers
-             * or do without MMAPed IO.  However, for PPC, we can't do
-             * MMAPed IO (according to what I've heard) so we may be forced
-             * to just fail detection on those cards.
-             */
-            temp_p->maddr = NULL;
-#else
-            kfree(temp_p);
-            temp_p = NULL;
-            continue;
-#endif /* __i386__ */
+            temp_p->maddr += page_offset;
           }
 #endif
 
-          aic_outb(temp_p, temp_p->pause, HCNTRL);
-          while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ;
+          pause_sequencer(temp_p);
 
           /*
            * Clear out any pending PCI error status messages.  Also set
@@ -7675,7 +9032,10 @@
           /*
            * Remember how the card was setup in case there is no seeprom.
            */
-          temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID;
+          if (temp_p->features & AHC_ULTRA2)
+            temp_p->scsi_id = aic_inb(temp_p, SCSIID_ULTRA2) & OID;
+          else
+            temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID;
           /*
            * Get current termination setting
            */
@@ -7688,11 +9048,17 @@
             continue;
           }
 
-          switch (temp_p->type & 0x1ff1)
+          /*
+           * Doing a switch based upon i is really gross, but since Justin
+           * changed around the chip ID stuff, we can't use that any more.
+           * Since we don't scan the devices the same way as FreeBSD, we end
+           * up doing this gross hack in order to avoid totally splitting
+           * away from Justin's init code in ahc_pci.c
+           */
+          switch (i)
           {
-            case AHC_394:   /* 3940 */
-            case AHC_394U:  /* 3940-Ultra */
-              temp_p->flags |= AHC_MULTI_CHANNEL;
+            case 7:   /* 3940 */
+            case 12:  /* 3940-Ultra */
               switch(PCI_SLOT(temp_p->pci_device_fn))
               {
                 case 5:
@@ -7703,9 +9069,8 @@
               }
               break;
 
-            case AHC_398:   /* 3985 */
-            case AHC_398U:  /* 3985-Ultra */
-              temp_p->flags |= AHC_MULTI_CHANNEL;
+            case 8:   /* 3985 */
+            case 13:  /* 3985-Ultra */
               switch(PCI_SLOT(temp_p->pci_device_fn))
               {
                 case 8:
@@ -7719,54 +9084,44 @@
               }
               break;
 
-            case AHC_AIC7895:
-              temp_p->flags |= AHC_MULTI_CHANNEL;
+            case 15:
+            case 18:
+            case 19:
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               if (PCI_FUNC(temp_p->pdev->devfn) != 0)
               {
                 temp_p->flags |= AHC_CHNLB;
               }
-              pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
-              devconfig = le32_to_cpu(devconfig);
-              devconfig |= SCBSIZE32;
-              devconfig = cpu_to_le32(devconfig);
-              pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+              /*
+               * The 7895 is the only chipset that sets the SCBSIZE32 param
+               * in the DEVCONFIG register.  The Ultra2 chipsets use
+               * the DSCOMMAND0 register instead.
+               */
+              if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
+              {
+                pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+                devconfig |= SCBSIZE32;
+                pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+              }
 #else
               if (PCI_FUNC(temp_p->pci_device_fn) != 0)
               {
                 temp_p->flags |= AHC_CHNLB;
               }
-              pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG,
-                &devconfig);
-              devconfig |= SCBSIZE32;
-              pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG,
-                devconfig);
-#endif
-              if (aic7xxx_7895_irq_hack != -1)
+              /*
+               * The 7895 is the only chipset that sets the SCBSIZE32 param
+               * in the DEVCONFIG register.  The Ultra2 chipsets use
+               * the DSCOMMAND0 register instead.
+               */
+              if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
               {
-                if (first_7895 == NULL)
-                {
-                  printk(KERN_INFO "aic7xxx: Using 7895_irq_hack.  Please "
-                    "upgrade your motherboard BIOS\n");
-                  first_7895 = temp_p;
-                }
-                else if (aic7xxx_7895_irq_hack == 0)
-                {
-                  if (temp_p->flags & AHC_CHNLB)
-                    temp_p->irq = first_7895->irq;
-                  else
-                    first_7895->irq = temp_p->irq;
-                  first_7895 = NULL;
-                }
-                else
-                {
-                  if ( !(temp_p->flags & AHC_CHNLB) )
-                    temp_p->irq = first_7895->irq;
-                  else
-                    first_7895->irq = temp_p->irq;
-                  first_7895 = NULL;
-                }
+                pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+                  &devconfig);
+                devconfig |= SCBSIZE32;
+                pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+                  devconfig);
               }
+#endif
               break;
             default:
               break;
@@ -7778,21 +9133,74 @@
            * on 394x and 398x cards we'll end up reading the wrong settings
            * for channels B and C
            */
-          if ( !(load_seeprom(temp_p, &sxfrctl1)) )
+          switch (temp_p->chip & AHC_CHIPID_MASK)
           {
-            temp_p->flags |= AHC_USEDEFAULTS;
-            if (sxfrctl1 & STPWEN)
-              temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B;
+            case AHC_AIC7890:
+            case AHC_AIC7896:
+              aic_outb(temp_p, 0, SCAMCTL);
+              /*
+               * We used to set DPARCKEN in this register, but after talking
+               * to a tech from Adaptec, I found out they don't use that
+               * particular bit in their own register settings, and when you
+               * combine that with the fact that I determined that we were
+               * seeing Data-Path Parity Errors on things we shouldn't see
+               * them on, I think there is a bug in the silicon and the way
+               * to work around it is to disable this particular check.  Also
+               * This bug only showed up on certain commands, so it seems to
+               * be pattern related or some such.  The commands we would
+               * typically send as a linux TEST_UNIT_READY or INQUIRY command
+               * could cause it to be triggered, while regular commands that
+               * actually made reasonable use of the SG array capabilities
+               * seemed not to cause the problem.
+               */
+              /*
+              aic_outb(temp_p, aic_inb(temp_p, DSCOMMAND0) |
+                               CACHETHEN | DPARCKEN | MPARCKEN |
+                               USCBSIZE32 | CIOPARCKEN,
+                               DSCOMMAND0);
+               */
+              aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
+                                CACHETHEN | MPARCKEN | USCBSIZE32 |
+                                CIOPARCKEN) & ~DPARCKEN, DSCOMMAND0);
+              /* FALLTHROUGH */
+            default:
+              /*
+               * We attempt to read a SEEPROM on *everything*.  If we fail,
+               * then we fail, but this covers things like 2910c cards that
+               * now have SEEPROMs with their 7856 chipset that we would
+               * otherwise ignore.  They still don't have a BIOS, but they
+               * have a SEEPROM that the SCSISelect utility on the Adaptec
+               * diskettes can configure.
+               */
+              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+              break;
+            case AHC_AIC7850:
+            case AHC_AIC7860:
+              /*
+               * Set the DSCOMMAND0 register on these cards different from
+               * on the 789x cards.  Also, read the SEEPROM as well.
+               */
+              aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
+                                CACHETHEN | MPARCKEN) & ~DPARCKEN,
+                       DSCOMMAND0);
+              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+              break;
           }
+          
 
           /*
            * and then we need another switch based on the type in order to
            * make sure the channel B primary flag is set properly on 7895
-           * controllers....Arrrgggghhh!!!
+           * controllers....Arrrgggghhh!!!  We also have to catch the fact
+           * that when you disable the BIOS on the 7895 on the Intel DK440LX
+           * motherboard, and possibly others, it only sets the BIOS disabled
+           * bit on the A channel...I think I'm starting to lean towards
+           * going postal....
            */
-          switch(temp_p->type & 0x1ff1)
+          switch(temp_p->chip & AHC_CHIPID_MASK)
           {
             case AHC_AIC7895:
+            case AHC_AIC7896:
               current_p = list_p;
               while(current_p != NULL)
               {
@@ -7801,11 +9209,21 @@
                       PCI_SLOT(temp_p->pci_device_fn)) )
                 {
                   if ( PCI_FUNC(current_p->pci_device_fn) == 0 )
+                  {
                     temp_p->flags |= 
                       (current_p->flags & AHC_CHANNEL_B_PRIMARY);
+                    temp_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS);
+                    temp_p->flags |=
+                      (current_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS));
+                  }
                   else
+                  {
                     current_p->flags |=
                       (temp_p->flags & AHC_CHANNEL_B_PRIMARY);
+                    current_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS);
+                    current_p->flags |=
+                      (temp_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS));
+                  }
                 }
                 current_p = current_p->next;
               }
@@ -7815,6 +9233,56 @@
           }
 
           /*
+           * We do another switch based on i so that we can exclude all
+           * 3895 devices from the next option since the 3895 cards use
+           * shared external SCB RAM while all other cards have dedicated
+           * external SCB RAM per channel.  Also exclude the 7850 and
+           * 7860 based stuff since they can have garbage in the bit
+           * that indicates external RAM and get some of this stuff
+           * wrong as a result.
+           */
+          switch(temp_p->chip & AHC_CHIPID_MASK)
+          {
+            default:
+              break;
+            case AHC_AIC7895:
+            case AHC_AIC7896:
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+              pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+#else
+              pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+                                        &devconfig);
+#endif
+              if (temp_p->features & AHC_ULTRA2)
+              {
+                if (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2)
+                {
+                  aic_outb(temp_p,
+                           aic_inb(temp_p, DSCOMMAND0) & ~SCBRAMSEL_ULTRA2,
+                           DSCOMMAND0);
+                  temp_p->flags |= AHC_EXTERNAL_SRAM;
+                  devconfig |= EXTSCBPEN;
+                }
+              }
+              else if (devconfig & RAMPSM)
+              {
+                devconfig &= ~SCBRAMSEL;
+                devconfig |= EXTSCBPEN;
+                temp_p->flags |= AHC_EXTERNAL_SRAM;
+              }
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+              pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+#else
+              pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+                                         devconfig);
+#endif
+              if ( (temp_p->flags & AHC_EXTERNAL_SRAM) &&
+                   (temp_p->flags & AHC_CHNLB) )
+                aic_outb(temp_p, 1, CCSCBBADDR);
+              break;
+          }
+
+          /*
            * Take the LED out of diagnostic mode
            */
           aic_outb(temp_p, 
@@ -7823,75 +9291,21 @@
 
           /*
            * We don't know where this is set in the SEEPROM or by the
-           * BIOS, so we default to 100%.
+           * BIOS, so we default to 100%.  On Ultra2 controllers, use 75%
+           * instead.
            */
-          aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS);
-
-          if (temp_p->flags & AHC_USEDEFAULTS)
+          if (temp_p->features & AHC_ULTRA2)
           {
-            int j;
-            unsigned char k;
-            /*
-             * Default setup; should only be used if the adapter does
-             * not have a SEEPROM.
-             */
-            /*
-             * Check the target scratch area to see if someone set us
-             * up already.  We are previously set up if the scratch
-             * area contains something other than all zeroes and ones.
-             */
-            for (j = TARG_SCRATCH; j < 0x60; j++)
-            {
-              k = aic_inb(temp_p, j);
-              /* Check for all zeros and ones.  Break out if we pass */
-              if( (k != 0x00) && (k != 0xff) )
-                break;
-            }
-            /* If j makes it to 0x60, then all entries are either 0x00 or
-             * 0xff.  We would then assume we have *not* been initialized
-             * and drop through here.  OTOH, if even one entry is inited,
-             * then as long as we appear to have a valid SCSI ID, we'll use
-             * the leftover BIOS values.
-             */
-            if ((j != 0x60) && (temp_p->scsi_id != 0))
-            {
-              temp_p->flags &= ~AHC_USEDEFAULTS;
-              if (aic7xxx_verbose & VERBOSE_PROBE2)
-              {
-                printk(KERN_INFO "aic7xxx: Using leftover BIOS values.\n");
-              }
-            }
-            else
-            {
-              /*
-               * Assume only one connector and always turn on
-               * termination.
-               */
-              temp_p->flags &= ~AHC_BIOS_ENABLED;
-              temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B;
-              sxfrctl1 = STPWEN;
-              temp_p->scsi_id = 7;
-            }
-            aic_outb(temp_p, (temp_p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI,
-                 SCSICONF);
-            /* In case we are a wide card. */
-            aic_outb(temp_p, temp_p->scsi_id, SCSICONF + 1);
+            aic_outb(temp_p, RD_DFTHRSH_75 | WR_DFTHRSH_75, DFF_THRSH);
           }
-          else /* not using defaults */
+          else
           {
-            if (sxfrctl1 & STPWEN)
-              temp_p->flags |= AHC_TERM_ENB_A;
+            aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS);
           }
 
           if (aic7xxx_extended)
             temp_p->flags |= AHC_EXTEND_TRANS_A;
 
-          /*
-           * Put our termination setting into sxfrctl1 now that the
-           * generic initialization is complete.
-           */
-          sxfrctl1 |= aic_inb(temp_p, SXFRCTL1);
-          aic_outb(temp_p, sxfrctl1, SXFRCTL1);
           if ( list_p == NULL )
           {
             list_p = current_p = temp_p;
@@ -7909,7 +9323,7 @@
         else /* Well, we found one, but we couldn't get any memory */
         {
           printk("aic7xxx: Found <%s>\n", 
-            board_names[aic7xxx_pci_devices[i].board_name_index]);
+            board_names[aic_pdevs[i].board_name_index]);
           printk(KERN_INFO "aic7xxx: Unable to allocate device memory, "
             "skipping.\n");
         }
@@ -7930,87 +9344,74 @@
    */
 
   {
-    struct aic7xxx_host *vlb_enab, *vlb_disab, *pci;
+    struct aic7xxx_host *sort_list[4] = { NULL, NULL, NULL, NULL };
+    struct aic7xxx_host *vlb, *pci;
     struct aic7xxx_host *prev_p;
     struct aic7xxx_host *p;
     unsigned char left;
 
-    prev_p = vlb_enab = vlb_disab = pci = NULL;
+    prev_p = vlb = pci = NULL;
 
     temp_p = list_p;
     while (temp_p != NULL)
     {
-      switch(temp_p->type)
+      switch(temp_p->chip & ~AHC_CHIPID_MASK)
       {
-        case AHC_AIC7770:
-        case AHC_274:
-        case AHC_284:
-          if (temp_p->flags & AHC_BIOS_ENABLED)
+        case AHC_EISA:
+        case AHC_VL:
+        {
+          p = temp_p;
+          if (p->flags & AHC_BIOS_ENABLED)
+            vlb = sort_list[0];
+          else
+            vlb = sort_list[2];
+
+          if (vlb == NULL)
           {
-            if (vlb_enab == NULL)
-            {
-              vlb_enab = temp_p;
-              temp_p = temp_p->next;
-              vlb_enab->next = NULL;
-            }
-            else
-            {
-              current_p = vlb_enab;
-              prev_p = NULL;
-              while ( (current_p != NULL) &&
-                      (current_p->bios_address < temp_p->bios_address))
-              {
-                prev_p = current_p;
-                current_p = current_p->next;
-              }
-              if (prev_p != NULL)
-              {
-                prev_p->next = temp_p;
-                temp_p = temp_p->next;
-                prev_p->next->next = current_p;
-              }
-              else
-              {
-                vlb_enab = temp_p;
-                temp_p = temp_p->next;
-                vlb_enab->next = current_p;
-              }
-            }
+            vlb = temp_p;
+            temp_p = temp_p->next;
+            vlb->next = NULL;
           }
           else
           {
-            if (vlb_disab == NULL)
+            current_p = vlb;
+            prev_p = NULL;
+            while ( (current_p != NULL) &&
+                    (current_p->bios_address < temp_p->bios_address))
+            {
+              prev_p = current_p;
+              current_p = current_p->next;
+            }
+            if (prev_p != NULL)
             {
-              vlb_disab = temp_p;
+              prev_p->next = temp_p;
               temp_p = temp_p->next;
-              vlb_disab->next = NULL;
+              prev_p->next->next = current_p;
             }
             else
             {
-              current_p = vlb_disab;
-              prev_p = NULL;
-              while ( (current_p != NULL) &&
-                      (current_p->base < temp_p->base))
-              {
-                prev_p = current_p;
-                current_p = current_p->next;
-              }
-              if (prev_p != NULL)
-              {
-                prev_p->next = temp_p;
-                temp_p = temp_p->next;
-                prev_p->next->next = current_p;
-              }
-              else
-              {
-                vlb_disab = temp_p;
-                temp_p = temp_p->next;
-                vlb_disab->next = current_p;
-              }
+              vlb = temp_p;
+              temp_p = temp_p->next;
+              vlb->next = current_p;
             }
           }
+          
+          if (p->flags & AHC_BIOS_ENABLED)
+            sort_list[0] = vlb;
+          else
+            sort_list[2] = vlb;
+          
           break;
+        }
         default:  /* All PCI controllers fall through to default */
+        {
+
+          p = temp_p;
+          if (p->flags & AHC_BIOS_ENABLED) 
+            pci = sort_list[1];
+          else
+            pci = sort_list[3];
+
           if (pci == NULL)
           {
             pci = temp_p;
@@ -8085,75 +9486,60 @@
               pci->next = current_p;
             }
           }
+
+          if (p->flags & AHC_BIOS_ENABLED)
+            sort_list[1] = pci;
+          else
+            sort_list[3] = pci;
+
           break;
+        }
       }  /* End of switch(temp_p->type) */
     } /* End of while (temp_p != NULL) */
     /*
      * At this point, the cards have been broken into 4 sorted lists, now
      * we run through the lists in order and register each controller
      */
-    left = found;
-    temp_p = vlb_enab;
-    while(temp_p != NULL)
-    {
-      template->name = board_names[temp_p->board_name_index];
-      p = aic7xxx_alloc(template, temp_p);
-      if (p != NULL)
-      {
-        p->instance = found - left;
-        if (aic7xxx_register(template, p, (--left)) == 0)
-        {
-          found--;
-          aic7xxx_free(p);
-          scsi_unregister(p->host);
-        }
-      }
-      current_p = temp_p;
-      temp_p = (struct aic7xxx_host *)temp_p->next;
-      kfree(current_p);
-    }
-    temp_p = pci;
-    while(temp_p != NULL)
-    {
-      template->name = board_names[temp_p->board_name_index];
-      p = aic7xxx_alloc(template, temp_p);
-      if (p != NULL)
-      {
-        p->instance = found - left;
-        if (aic7xxx_register(template, p, (--left)) == 0)
-        {
-          found--;
-          aic7xxx_free(p);
-          scsi_unregister(p->host);
-        }
-      }
-      current_p = temp_p;
-      temp_p = (struct aic7xxx_host *)temp_p->next;
-      kfree(current_p);
-    }
-    temp_p = vlb_disab;
-    while(temp_p != NULL)
     {
-      template->name = board_names[temp_p->board_name_index];
-      p = aic7xxx_alloc(template, temp_p);
-      if (p != NULL)
+      int i;
+      
+      left = found;
+      for (i=0; i<NUMBER(sort_list); i++)
       {
-        p->instance = found - left;
-        if (aic7xxx_register(template, p, (--left)) == 0)
+        temp_p = sort_list[i];
+        while(temp_p != NULL)
         {
-          found--;
-          aic7xxx_free(p);
-          scsi_unregister(p->host);
+          template->name = board_names[temp_p->board_name_index];
+          p = aic7xxx_alloc(template, temp_p);
+          if (p != NULL)
+          {
+            p->instance = found - left;
+            if (aic7xxx_register(template, p, (--left)) == 0)
+            {
+              found--;
+              aic7xxx_release(p->host);
+              scsi_unregister(p->host);
+            }
+            else if (aic7xxx_dump_card)
+            {
+              pause_sequencer(p);
+              aic7xxx_print_card(p);
+              aic7xxx_print_scratch_ram(p);
+              unpause_sequencer(p, TRUE);
+            }
+          }
+          current_p = temp_p;
+          temp_p = (struct aic7xxx_host *)temp_p->next;
+          kfree(current_p);
         }
       }
-      current_p = temp_p;
-      temp_p = (struct aic7xxx_host *)temp_p->next;
-      kfree(current_p);
     }
   }
   return (found);
 }
 
+#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_negotiation_complete
@@ -8165,7 +9551,7 @@
 static void
 aic7xxx_negotiation_complete(Scsi_Cmnd *cmd)
 {
-  memset(&cmd->sense_buffer[0], 0, sizeof(cmd->sense_buffer));
+  return;
 }
 
 /*+F*************************************************************************
@@ -8205,10 +9591,19 @@
       cmd->underflow = 0;
       cmd->cmd_len = 6;
     }
+    /*
+     * Before sending this thing out, we also amke the cmd->next pointer
+     * point to the real command so we can stuff any possible SENSE data
+     * intp the real command instead of this fake command.  This has to be
+     * done each time the command is built, not just the first time, hence
+     * it's outside of the above if()...
+     */
+    p->dev_wdtr_cmnd[tindex]->next = old_cmd;
     aic7xxx_queue(p->dev_wdtr_cmnd[tindex], 
                   aic7xxx_negotiation_complete);
   }
-  else if ( (p->needsdtr & (1<<tindex)) && !(p->sdtr_pending & (1<<tindex)) )
+  else if ( (p->needsdtr & (1<<tindex)) && !(p->sdtr_pending & (1<<tindex)) &&
+            !(p->wdtr_pending & (1<<tindex)) )
   {
     if(p->dev_sdtr_cmnd[tindex] == NULL)
     {
@@ -8232,11 +9627,45 @@
       cmd->underflow = 0;
       cmd->cmd_len = 6;
     }
+    /*
+     * Before sending this thing out, we also amke the cmd->next pointer
+     * point to the real command so we can stuff any possible SENSE data
+     * intp the real command instead of this fake command.  This has to be
+     * done each time the command is built, not just the first time, hence
+     * it's outside of the above if()...
+     */
+    p->dev_sdtr_cmnd[tindex]->next = old_cmd;
     aic7xxx_queue(p->dev_sdtr_cmnd[tindex], 
                   aic7xxx_negotiation_complete);
   }
 }
 
+#endif
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_print_scb
+ *
+ * Description:
+ *   Dump the byte codes for an about to be sent SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  int i;
+  unsigned char *x;  
+
+  x = (unsigned char *)&scb->hscb->control;
+
+  for(i=0; i<32; i++)
+  {
+    printk("%02x ", x[i]);
+  }
+  printk("\n");
+}
+#endif
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_buildscb
@@ -8290,12 +9719,13 @@
   }
   if (p->dev_flags[TARGET_INDEX(cmd)] & DEVICE_SCANNED)
   {
+#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
     if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) )
     {
       if (cmd == p->dev_wdtr_cmnd[TARGET_INDEX(cmd)])
       {
         p->wdtr_pending |= mask;
-        scb->flags |= SCB_MSGOUT_WDTR_16BIT;
+        scb->flags |= SCB_MSGOUT_WDTR;
         hscb->control &= DISCENB;
         hscb->control |= MK_MESSAGE;
         scb->tag_action = 0;
@@ -8305,7 +9735,8 @@
         aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd));
       }
     }
-    if ( (p->needsdtr & mask) && !(p->sdtr_pending & mask) )
+    else if ( (p->needsdtr & mask) && !(p->sdtr_pending & mask) &&
+              !(p->wdtr_pending & mask) )
     {
       if (cmd == p->dev_sdtr_cmnd[TARGET_INDEX(cmd)])
       {
@@ -8320,6 +9751,36 @@
         aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd));
       }
     }
+#else
+    if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) &&
+         !(p->sdtr_pending & mask) && (cmd->lun == 0) )
+    {
+      p->wdtr_pending |= mask;
+      scb->flags |= SCB_MSGOUT_WDTR;
+      hscb->control &= DISCENB;
+      hscb->control |= MK_MESSAGE;
+      scb->tag_action = 0;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
+        printk(INFO_LEAD "Building WDTR command.\n", p->host_no,
+               CTL_OF_CMD(cmd));
+#endif
+    }
+    else if ( (p->needsdtr & mask) && !(p->wdtr_pending & mask) &&
+              !(p->sdtr_pending & mask) && (cmd->lun == 0) )
+    {
+      p->sdtr_pending |= mask;
+      scb->flags |= SCB_MSGOUT_SDTR;
+      hscb->control &= DISCENB;
+      hscb->control |= MK_MESSAGE;
+      scb->tag_action = 0;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
+        printk(INFO_LEAD "Building SDTR command.\n", p->host_no,
+               CTL_OF_CMD(cmd));
+#endif
+    }
+#endif
   }
   hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
         ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
@@ -8352,19 +9813,26 @@
 
     sg = (struct scatterlist *)cmd->request_buffer;
     scb->sg_length = 0;
+    /*
+     * Copy the segments into the SG array.  NOTE!!! - We used to
+     * have the first entry both in the data_pointer area and the first
+     * SG element.  That has changed somewhat.  We still have the first
+     * entry in both places, but now we download the address of
+     * scb->sg_list[1] instead of 0 to the sg pointer in the hscb.
+     */
     for (i = 0; i < cmd->use_sg; i++)
     {
       scb->sg_list[i].address = cpu_to_le32(VIRT_TO_BUS(sg[i].address));
       scb->sg_list[i].length = cpu_to_le32(sg[i].length);
       scb->sg_length += sg[i].length;
     }
-    hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(scb->sg_list));
-    hscb->SG_segment_count = cmd->use_sg;
-    scb->sg_count = cmd->use_sg;
-
     /* Copy the first SG into the data pointer area. */
     hscb->data_pointer = scb->sg_list[0].address;
     hscb->data_count = scb->sg_list[0].length;
+    scb->sg_count = cmd->use_sg;
+    hscb->SG_segment_count = cmd->use_sg;
+    hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[1]));
+
   }
   else
   {
@@ -8389,6 +9857,12 @@
       hscb->data_pointer = 0;
     }
   }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+  if((cmd->cmnd[0] == TEST_UNIT_READY) && (aic7xxx_verbose & VERBOSE_PROBE2))
+  {
+    aic7xxx_print_scb(p, scb);
+  }
+#endif
 }
 
 /*+F*************************************************************************
@@ -8403,7 +9877,9 @@
 {
   struct aic7xxx_host *p;
   struct aic7xxx_scb *scb;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
   int tindex = TARGET_INDEX(cmd);
+#endif
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
   unsigned long cpu_flags = 0;
 #endif
@@ -8412,18 +9888,22 @@
   /*
    * Check to see if channel was scanned.
    */
+  
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
   if (!(p->flags & AHC_A_SCANNED) && (cmd->channel == 0))
   {
-    printk(INFO_LEAD "Scanning channel for devices.\n",
-      p->host_no, 0, -1, -1);
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
+      printk(INFO_LEAD "Scanning channel for devices.\n",
+        p->host_no, 0, -1, -1);
     p->flags |= AHC_A_SCANNED;
   }
   else
   {
     if (!(p->flags & AHC_B_SCANNED) && (cmd->channel == 1))
     {
-      printk(INFO_LEAD "Scanning channel for devices.\n",
-        p->host_no, 1, -1, -1);
+      if (aic7xxx_verbose & VERBOSE_PROBE2)
+        printk(INFO_LEAD "Scanning channel for devices.\n",
+          p->host_no, 1, -1, -1);
       p->flags |= AHC_B_SCANNED;
     }
   }
@@ -8437,12 +9917,25 @@
     if ( p->dev_active_cmds[tindex] > 220 )
       p->dev_active_cmds[tindex] = 0;
   }
-  DRIVER_LOCK
-  scb = aic7xxx_allocate_scb(p, FALSE);
-  DRIVER_UNLOCK
+#endif
+
+  scb = scbq_remove_head(&p->scb_data->free_scbs);
+  if (scb == NULL)
+  {
+    DRIVER_LOCK
+    aic7xxx_allocate_scb(p);
+    DRIVER_UNLOCK
+    scb = scbq_remove_head(&p->scb_data->free_scbs);
+  }
   if (scb == NULL)
   {
-    panic("(scsi%d) aic7xxx_queue:Couldn't get a free SCB.\n", p->host_no);
+    printk(WARN_LEAD "Couldn't get a free SCB.\n", p->host_no,
+           CTL_OF_CMD(cmd));
+    cmd->result = (DID_BUS_BUSY << 16);
+    DRIVER_LOCK
+    aic7xxx_queue_cmd_complete(p, cmd);
+    DRIVER_UNLOCK
+    return 0;
   }
   else
   {
@@ -8461,23 +9954,16 @@
      * the SCB to the sequencer and watch the fun begin.
      */
     cmd->scsi_done = fn;
+    cmd->result = DID_OK;
+    memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
     aic7xxx_error(cmd) = DID_OK;
     aic7xxx_status(cmd) = 0;
-    cmd->result = 0;
     cmd->host_scribble = NULL;
-    memset(&cmd->sense_buffer[0], 0, sizeof(cmd->sense_buffer));
 
     scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
 
     DRIVER_LOCK
-    if (p->delayed_scbs[tindex].head != NULL) 
-    {
-        scbq_insert_tail(&p->delayed_scbs[tindex], scb);
-    }
-    else
-    {
-        scbq_insert_tail(&p->waiting_scbs, scb);
-    }
+    scbq_insert_tail(&p->waiting_scbs, scb);
     if ( (p->flags & (AHC_IN_ISR | AHC_IN_ABORT | AHC_IN_RESET)) == 0)
     {
       aic7xxx_run_waiting_queues(p);
@@ -8665,7 +10151,10 @@
       printk(INFO_LEAD "Queueing device reset "
            "command.\n", p->host_no, CTL_OF_SCB(scb));
     p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
-    aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+    if (p->features & AHC_QUEUE_REGS)
+      aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+    else
+      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
     scb->flags |= SCB_QUEUED_ABORT;
     result = SCSI_RESET_PENDING;
   }
@@ -8686,30 +10175,142 @@
  *   Abort the current SCSI command(s).
  *-F*************************************************************************/
 void
-aic7xxx_panic_abort(struct aic7xxx_host *p)
+aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
 {
-  int i;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+  int i, mask, found, need_tag;
+  struct aic7xxx_scb *scb;
+  unsigned char qinpos, hscbp;
+
+  found = FALSE;
+#endif
 
-  printk("aic7xxx driver version %s\n", AIC7XXX_C_VERSION);
+  printk("aic7xxx driver version %s/%s\n", AIC7XXX_C_VERSION,
+         UTS_RELEASE);
   printk("Controller type:\n    %s\n", board_names[p->board_name_index]);
+  printk("p->flags=0x%x, p->chip=0x%x, p->features=0x%x, "
+         "sequencer %s paused\n",
+     p->flags, p->chip, p->features,
+    (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" );
+  pause_sequencer(p);
+  disable_irq(p->irq);
+  aic7xxx_print_card(p);
+  aic7xxx_print_scratch_ram(p);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
   for(i=0; i<MAX_TARGETS; i++)
   {
     if(p->dev_flags[i] & DEVICE_PRESENT)
     {
-      printk(INFO_LEAD "dev_flags=0x%x, WDTR:%s, SDTR:%s, q_depth=%d:%d\n",
+      mask = (0x01 << i);
+      printk(INFO_LEAD "dev_flags=0x%x, WDTR:%c/%c/%c, SDTR:%c/%c/%c,"
+             " q_depth=%d:%d:%d\n",
         p->host_no, 0, i, 0, p->dev_flags[i],
-        (p->needwdtr_copy & (1 << i)) ? "Yes" : "No",
-        (p->needsdtr_copy & (1 << i)) ? "Yes" : "No",
+        (p->wdtr_pending & mask) ? 'Y' : 'N',
+        (p->needwdtr & mask) ? 'Y' : 'N',
+        (p->needwdtr_copy & mask) ? 'Y' : 'N',
+        (p->sdtr_pending & mask) ? 'Y' : 'N',
+        (p->needsdtr & mask) ? 'Y' : 'N',
+        (p->needsdtr_copy & mask) ? 'Y' : 'N',
+        p->dev_active_cmds[i],
         p->dev_max_queue_depth[i], p->dev_mid_level_queue_depth[i]);
+      printk(INFO_LEAD "targ_scsirate=0x%x", p->host_no, 0, i, 0,
+             aic_inb(p, TARG_SCSIRATE + i));
+      if (p->features & AHC_ULTRA2)
+        printk(", targ_offset=%d", aic_inb(p, TARG_OFFSET + i));
+      printk("\n");
     }
   }
-  printk("SIMODE0=0x%x, SIMODE1=0x%x, SSTAT0=0x%x, SSTAT1=0x%x, INTSTAT=0x%x\n",
-    aic_inb(p, SIMODE0), aic_inb(p, SIMODE1), aic_inb(p, SSTAT0),
-    aic_inb(p, SSTAT1), aic_inb(p, INTSTAT) );
-  printk("p->flags=0x%x, p->type=0x%x, sequencer %s paused\n",
-     p->flags, p->type,
-    (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" );
-  panic("Stopping to debug\n");
+  /*
+   * Search for this command and see if we can't track it down, it's the
+   * one causing the timeout.  Print out this command first, then all other
+   * active commands afterwords.
+   */
+  need_tag = -1;
+  if ( cmd )
+  {
+    scb = p->scb_data->scb_array[aic7xxx_position(cmd)];
+    if ( (scb->flags & SCB_ACTIVE) && (scb->cmd == cmd) )
+    {
+      printk("Timed out command is scb #%d:\n", scb->hscb->tag);
+      printk("Tag%d: flags=0x%x, control=0x%x, TCL=0x%x, %s\n", scb->hscb->tag,
+             scb->flags, scb->hscb->control, scb->hscb->target_channel_lun,
+             (scb->flags & SCB_WAITINGQ) ? "WAITINGQ" : "Sent" );
+      need_tag = scb->hscb->tag;
+      if (scb->flags & SCB_WAITINGQ) found=TRUE;
+    }
+  }
+  printk("QINFIFO: (TAG) ");
+  qinpos = aic_inb(p, QINPOS);
+  while ( qinpos != p->qinfifonext )
+  {
+    if (p->qinfifo[qinpos] == need_tag)
+      found=TRUE;
+    printk("%d ", p->qinfifo[qinpos++]);
+  }  
+  printk("\n");
+  printk("Current SCB: (SCBPTR/TAG/CONTROL) %d/%d/0x%x\n", aic_inb(p, SCBPTR),
+         aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL) );
+  if (aic_inb(p, SCB_TAG) == need_tag)  found=TRUE;
+  printk("WAITING_SCBS: (SCBPTR/TAG/CONTROL) %d->",
+         hscbp = aic_inb(p, WAITING_SCBH));
+  while (hscbp != SCB_LIST_NULL)
+  {
+    aic_outb(p, hscbp, SCBPTR);
+    printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL));
+    hscbp = aic_inb(p, SCB_NEXT);
+    if (aic_inb(p, SCB_TAG) == need_tag)  found=TRUE;
+  }
+  printk("\n");
+  printk("DISCONNECTED_SCBS: (SCBPTR/TAG/CONTROL) %d->",
+         hscbp = aic_inb(p, DISCONNECTED_SCBH));
+  while (hscbp != SCB_LIST_NULL)
+  {
+    aic_outb(p, hscbp, SCBPTR);
+    printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL));
+    hscbp = aic_inb(p, SCB_NEXT);
+    if (aic_inb(p, SCB_TAG) == need_tag)  found=TRUE;
+  }
+  printk("\n");
+  printk("FREE_SCBS: (SCBPTR/TAG/CONTROL) %d->",
+         hscbp = aic_inb(p, FREE_SCBH));
+  while (hscbp != SCB_LIST_NULL)
+  {
+    aic_outb(p, hscbp, SCBPTR);
+    printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL));
+    hscbp = aic_inb(p, SCB_NEXT);
+  }
+  printk("\n");
+
+  if (found == FALSE)
+  {
+    /*
+     * We haven't found the offending SCB yet, and it should be around
+     * somewhere, so go look for it in the cards SCBs.
+     */
+    printk("SCBPTR CONTROL TAG PREV NEXT\n");
+    for(i=0; i<p->scb_data->maxhscbs; i++)
+    {
+      aic_outb(p, i, SCBPTR);
+      printk("   %3d      %02x  %02x   %02x   %02x\n", i,
+             aic_inb(p, SCB_CONTROL), aic_inb(p, SCB_TAG),
+             aic_inb(p, SCB_PREV), aic_inb(p, SCB_NEXT));
+    }
+  }
+  
+
+  for (i=0; i < p->scb_data->numscbs; i++)
+  {
+    scb = p->scb_data->scb_array[i];
+    if ( (scb->flags & SCB_ACTIVE) && (scb->cmd != cmd) )
+    {
+      printk("Tag%d: flags=0x%x, control=0x%x, TCL=0x%x, %s\n", scb->hscb->tag,
+             scb->flags, scb->hscb->control, scb->hscb->target_channel_lun,
+             (scb->flags & SCB_WAITINGQ) ? "WAITINGQ" : "Sent" );
+    }
+  }
+#endif
+  sti();
+  for(;;) barrier();
 }
 
 /*+F*************************************************************************
@@ -8743,7 +10344,7 @@
    * code.
    */
   if (aic7xxx_panic_on_abort)
-    aic7xxx_panic_abort(p);
+    aic7xxx_panic_abort(p, cmd);
 
   DRIVER_LOCK
 
@@ -8798,7 +10399,7 @@
           p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
         else
           cmd_prev->host_scribble = cmd_next->host_scribble;
-        cmd_next->done(cmd_next);
+        cmd_next->scsi_done(cmd_next);
         unpause_sequencer(p, FALSE);
         DRIVER_UNLOCK
         return(SCSI_ABORT_NOT_RUNNING); /* It's already back as a successful
@@ -8822,7 +10423,12 @@
  *     command held by the scb pointer and is a valid abort request.
  *   Now, we just have to figure out what to do from here.  Current plan is:
  *     if we have already been here on this command, escalate to a reset
- *     if scb is on waiting list or QINFIFO, send it back as aborted
+ *     if scb is on waiting list or QINFIFO, send it back as aborted, but
+ *       we also need to be aware of the possibility that we could be using
+ *       a faked negotiation command that is holding this command up,  if
+ *       so we need to take care of that command instead, which means we
+ *       would then treat this one like it was sitting around disconnected
+ *       instead.
  *     if scb is on WAITING_SCB list in sequencer, free scb and send back
  *     if scb is disconnected and not completed, abort with abort message
  *     if scb is currently running, then it may be causing the bus to hang
@@ -8902,18 +10508,55 @@
 
   if ((found == 0) && (scb->flags & SCB_WAITINGQ))
   {
-      int tindex = TARGET_INDEX(cmd);
-     
-      if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) 
-        printk(INFO_LEAD "SCB found on waiting list and "
-            "aborted.\n", p->host_no, CTL_OF_SCB(scb));
-      scbq_remove(&p->waiting_scbs, scb);
-      scbq_remove(&p->delayed_scbs[tindex], scb);
-      p->dev_active_cmds[tindex]++;
-      p->activescbs++;
-      scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
-      scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
+    int tindex = TARGET_INDEX(cmd);
+#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
+    unsigned short mask;
+
+    mask = (1 << tindex);
+
+    if (p->wdtr_pending & mask)
+    {
+      if (p->dev_wdtr_cmnd[tindex]->next != cmd)
+        found = 1;
+      else
+        found = 0;
+    }
+    else if (p->sdtr_pending & mask)
+    {
+      if (p->dev_sdtr_cmnd[tindex]->next != cmd)
+        found = 1;
+      else
+        found = 0;
+    }
+    else
+    {
       found = 1;
+    }
+    if (found == 0)
+    {
+      /*
+       * OK..this means the command we are currently getting an abort
+       * for has an outstanding negotiation command in front of it.
+       * We don't really have a way to tie back into the negotiation
+       * commands, so we just send this back as pending, then it
+       * will get reset in 2 seconds.
+       */
+      unpause_sequencer(p, TRUE);
+      scb->flags |= SCB_ABORT;
+      DRIVER_UNLOCK
+      return(SCSI_ABORT_PENDING);
+    }
+#endif
+    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) 
+      printk(INFO_LEAD "SCB found on waiting list and "
+          "aborted.\n", p->host_no, CTL_OF_SCB(scb));
+    scbq_remove(&p->waiting_scbs, scb);
+    scbq_remove(&p->delayed_scbs[tindex], scb);
+    p->dev_active_cmds[tindex]++;
+    p->activescbs++;
+    scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
+    scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
+    found = 1;
   }
 
 /*
@@ -9006,7 +10649,10 @@
       printk(INFO_LEAD "SCB disconnected.  Queueing Abort"
         " SCB.\n", p->host_no, CTL_OF_SCB(scb));
     p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
-    aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+    if (p->features & AHC_QUEUE_REGS)
+      aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+    else
+      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
   }
   if (found)
   {
@@ -9083,7 +10729,7 @@
    * code.
    */
   if (aic7xxx_panic_on_abort)
-    aic7xxx_panic_abort(p);
+    aic7xxx_panic_abort(p, cmd);
 
   DRIVER_LOCK
 
@@ -9203,7 +10849,7 @@
         "other action has failed.\n", p->host_no, CTL_OF_CMD(cmd));
     action = BUS_RESET;
   }
-  if ( (action & BUS_RESET) && !(p->type & AHC_TWIN) )
+  if ( (action & BUS_RESET) && !(p->features & AHC_TWIN) )
   {
     action = HOST_RESET;
   }
@@ -9270,16 +10916,11 @@
       p->reset_start = jiffies;
       p->flags |= AHC_IN_RESET;
       aic7xxx_reset_channel(p, cmd->channel, TRUE);
-      if ( (p->type & AHC_TWIN) && (action & HOST_RESET) )
+      if ( (p->features & AHC_TWIN) && (action & HOST_RESET) )
       {
         aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE);
         restart_sequencer(p);
       }
-      if (scb == NULL)
-      {
-        cmd->result = DID_RESET << 16;
-        cmd->done(cmd);
-      }
       p->last_reset = jiffies;
       if (action != HOST_RESET)
         result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
@@ -9391,6 +11032,207 @@
   aic7xxx_free(p);
   return(0);
 }
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_print_card
+ *
+ * Description:
+ *   Print out all of the control registers on the card
+ *
+ *   NOTE: This function is not yet safe for use on the VLB and EISA
+ *   controllers, so it isn't used on those controllers at all.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_card(struct aic7xxx_host *p)
+{
+  int i, j, k, chip;
+  static struct register_ranges {
+    int num_ranges;
+    int range_val[32];
+  } cards_ds[] = {
+    { 0, {0,} }, /* none */
+    {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1f, 0x1f, 0x60, 0x60, /*7771*/
+          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9b, 0x9f} },
+    { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7850*/
+          0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+    { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7860*/
+          0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+    {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1c, 0x1f, 0x60, 0x60, /*7870*/
+          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+    {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1a, 0x1c, 0x1f, 0x60, 0x60, /*7880*/
+          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+    {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7890*/
+          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
+          0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
+          0xfe, 0xff} },
+    {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1b, 0x1f, 0x60, 0x60, /*7895*/
+          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a,
+          0x9f, 0x9f, 0xe0, 0xf1} },
+    {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7896*/
+          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
+          0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
+          0xfe, 0xff} },
+  };
+#ifdef CONFIG_PCI
+  static struct register_ranges cards_ns[] = {
+    { 0, {0,} }, /* none */
+    { 0, {0,} }, /* 7771 */
+    { 7, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x28, 0x2b, 0x30, 0x33,
+          0x3c, 0x41, 0x43, 0x47} },
+    { 7, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x28, 0x2b, 0x30, 0x33,
+          0x3c, 0x41, 0x43, 0x47} },
+    { 5, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x33, 0x3c, 0x41} },
+    { 5, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47} },
+    { 5, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3} },
+    { 6, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47,
+          0xdc, 0xe3} },
+    { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3,
+          0xff, 0xff} }
+  };
+#endif
+  chip = p->chip & AHC_CHIPID_MASK;
+  /*
+   * Let's run through the PCI space first....
+   */
+  printk("%s at ",
+         board_names[p->board_name_index]);
+  switch(p->chip & ~AHC_CHIPID_MASK)
+  {
+    case AHC_VL:
+      printk("VLB Slot %d.\n", p->pci_device_fn);
+      break;
+    case AHC_EISA:
+      printk("EISA Slot %d.\n", p->pci_device_fn);
+      break;
+    case AHC_PCI:
+    default:
+      printk("PCI %d/%d.\n", PCI_SLOT(p->pci_device_fn),
+             PCI_FUNC(p->pci_device_fn));
+      break;
+  }
+
+#ifdef CONFIG_PCI
+  {
+    unsigned char temp;
+    
+    printk("PCI Dump:\n");
+    k=0;
+    for(i=0; i<cards_ns[chip].num_ranges; i++)
+    {
+      for(j  = cards_ns[chip].range_val[ i * 2 ];
+          j <= cards_ns[chip].range_val[ i * 2 + 1 ] ;
+          j++)
+      {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+        pci_read_config_byte(p->pdev, j, &temp);
+#else
+        pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, j, &temp);
+#endif
+        printk("%02x:%02x ", j, temp);
+        if(++k == 13)
+        {
+          printk("\n");
+          k = 0;
+        }
+      }
+    }
+  }
+  if(k != 0)
+    printk("\n");
+#endif /* CONFIG_PCI */
+
+  /*
+   * Now the registers on the card....
+   */
+  printk("Card Dump:\n");
+  k = 0;
+  for(i=0; i<cards_ds[chip].num_ranges; i++)
+  {
+    for(j  = cards_ds[chip].range_val[ i * 2 ];
+        j <= cards_ds[chip].range_val[ i * 2 + 1 ] ;
+        j++)
+    {
+      printk("%02x:%02x ", j, aic_inb(p, j));
+      if(++k == 13)
+      {
+        printk("\n");
+        k=0;
+      }
+    }
+  }
+  if(k != 0)
+    printk("\n");
+  if (p->flags & AHC_SEEPROM_FOUND)
+  {
+    unsigned short *sc1;
+    sc1 = (unsigned short *)&p->sc;
+    
+    printk("SEEPROM dump.\n");
+    for(i=1; i<=32; i++)
+    {
+      printk("0x%04x", sc1[i-1]);
+      if ( (i % 8) == 0 )
+        printk("\n");
+      else
+        printk("  ");
+    }
+  }
+
+  /*
+   * If this was an Ultra2 controller, then we just hosed the card in terms
+   * of the QUEUE REGS.  This function is only called at init time or by
+   * the panic_abort function, so it's safe to assume a generic init time
+   * setting here
+   */
+
+  if(p->features & AHC_QUEUE_REGS)
+  {
+    aic_outb(p, 0, SDSCB_QOFF);
+    aic_outb(p, 0, SNSCB_QOFF);
+    aic_outb(p, 0, HNSCB_QOFF);
+  }
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_print_scratch_ram
+ *
+ * Description:
+ *   Print out the scratch RAM values on the card.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_scratch_ram(struct aic7xxx_host *p)
+{
+  int i, k;
+
+  k = 0;
+  printk("Scratch RAM:\n");
+  for(i = SRAM_BASE; i < SEQCTL; i++)
+  {
+    printk("%02x:%02x ", i, aic_inb(p, i));
+    if(++k == 13)
+    {
+      printk("\n");
+      k=0;
+    }
+  }
+  if (p->features & AHC_MORE_SRAM)
+  {
+    for(i = TARG_OFFSET; i < 0x80; i++)
+    {
+      printk("%02x:%02x ", i, aic_inb(p, i));
+      if(++k == 13)
+      {
+        printk("\n");
+        k=0;
+      }
+    }
+  }
+  printk("\n");
+}
+
 
 #include "aic7xxx_proc.c"
 
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c
--- v2.1.124/linux/drivers/scsi/aic7xxx_proc.c	Sun Jun  7 11:16:34 1998
+++ linux/drivers/scsi/aic7xxx_proc.c	Thu Oct  8 08:07:34 1998
@@ -85,10 +85,8 @@
   struct aic7xxx_host *p;
   int    size = 0;
   unsigned char i;
-#ifdef AIC7XXX_PROC_STATS
   struct aic7xxx_xferstats *sp;
   unsigned char target, lun;
-#endif
 
   HBAptr = NULL;
 
@@ -130,16 +128,18 @@
    */
 
   size = 4096;
-#ifdef AIC7XXX_PROC_STATS
   for (target = 0; target < MAX_TARGETS; target++)
   {
     for (lun = 0; lun < MAX_LUNS; lun++)
     {
       if (p->stats[target][lun].xfers != 0)
+#ifdef AIC7XXX_PROC_STATS
         size += 512;
+#else
+        size += 256;
+#endif
     }
   }
-#endif
   if (aic7xxx_buffer_size != size)
   {
     if (aic7xxx_buffer != NULL) 
@@ -195,9 +195,11 @@
       if (p->flags & (AHC_CHNLB|AHC_CHNLC))
         channel = (p->flags & AHC_CHNLB) ? " Channel B" : " Channel C";
     }
-    if (p->type & AHC_WIDE)
+    if (p->features & AHC_WIDE)
       wide = "Wide ";
-    if (p->type & AHC_ULTRA)
+    if (p->features & AHC_ULTRA2)
+      ultra = "Ultra2-LVD/SE ";
+    else if (p->features & AHC_ULTRA)
       ultra = "Ultra ";
     size += sprintf(BLS, "                           %s%sController%s\n",
       ultra, wide, channel);
@@ -210,25 +212,26 @@
   {
     size += sprintf(BLS, "    PCI MMAPed I/O Base: 0x%lx\n", p->mbase);
   }
-  if( !(p->type & AHC_AIC78x0) )
+  if( (p->chip & (AHC_VL | AHC_EISA)) )
   {
     size += sprintf(BLS, "    BIOS Memory Address: 0x%08x\n", p->bios_address);
-    size += sprintf(BLS, "                         %s\n",
-            (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled");
-  }
-  else
-  {
-    size += sprintf(BLS, "      Adaptec SCSI BIOS: %s\n",
-            (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled");
   }
+  size += sprintf(BLS, " Adapter SEEPROM Config: %s\n",
+          (p->flags & AHC_SEEPROM_FOUND) ? "SEEPROM found and used." :
+         ((p->flags & AHC_USEDEFAULTS) ? "SEEPROM not found, using defaults." :
+           "SEEPROM not found, using leftover BIOS values.") );
+  size += sprintf(BLS, "      Adaptec SCSI BIOS: %s\n",
+          (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled");
   size += sprintf(BLS, "                    IRQ: %d\n", HBAptr->irq);
   size += sprintf(BLS, "                   SCBs: Active %d, Max Active %d,\n",
             p->activescbs, p->max_activescbs);
   size += sprintf(BLS, "                         Allocated %d, HW %d, "
             "Page %d\n", p->scb_data->numscbs, p->scb_data->maxhscbs,
             p->scb_data->maxscbs);
+  if (p->flags & AHC_EXTERNAL_SRAM)
+    size += sprintf(BLS, "                         Using External SCB SRAM\n");
   size += sprintf(BLS, "             Interrupts: %ld", p->isr_count);
-  if (p->type & AHC_AIC7770)
+  if (p->chip & AHC_EISA)
   {
     size += sprintf(BLS, " %s\n",
         (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
@@ -244,7 +247,7 @@
   size += sprintf(BLS, "   Extended Translation: %sabled\n",
       (p->flags & AHC_EXTEND_TRANS_A) ? "En" : "Dis");
   size += sprintf(BLS, "Disconnect Enable Flags: 0x%04x\n", p->discenable);
-  if (p->type & AHC_ULTRA)
+  if (p->features & (AHC_ULTRA | AHC_ULTRA2))
   {
     size += sprintf(BLS, "     Ultra Enable Flags: 0x%04x\n", p->ultraenb);
   }
@@ -268,7 +271,6 @@
     size += sprintf(BLS, "%d,", p->dev_max_queue_depth[i]);
   size += sprintf(BLS, "%d}\n", p->dev_max_queue_depth[i]);
 
-#ifdef AIC7XXX_PROC_STATS
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Statistics:\n");
   for (target = 0; target < MAX_TARGETS; target++)
@@ -280,7 +282,7 @@
       {
         continue;
       }
-      if (p->type & AHC_TWIN)
+      if (p->features & AHC_TWIN)
       {
         size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
             p->host_no, (target >> 3), (target & 0x7), lun);
@@ -290,10 +292,50 @@
         size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
             p->host_no, 0, target, lun);
       }
-      size += sprintf(BLS, "nxfers %ld (%ld read;%ld written)\n",
+      size += sprintf(BLS, "  Device using %s/%s\n",
+            (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ?
+            "Wide" : "Narrow",
+            (p->transinfo[target].cur_offset != 0) ?
+            "Sync transfers at" : "Async transfers." );
+      if (p->transinfo[target].cur_offset != 0)
+      {
+        struct aic7xxx_syncrate *sync_rate;
+        int period = p->transinfo[target].cur_period;
+        int rate = (p->transinfo[target].cur_width ==
+                    MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
+
+        sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2);
+        if (sync_rate != NULL)
+        {
+          size += sprintf(BLS, "  %s MByte/sec, offset %d\n",
+                          sync_rate->rate[rate],
+                          p->transinfo[target].cur_offset );
+        }
+        else
+        {
+          size += sprintf(BLS, "  3.3 MByte/sec, offset %d\n",
+                          p->transinfo[target].cur_offset );
+        }
+      }
+      size += sprintf(BLS, "    Device Negotiation Settings\n");
+      size += sprintf(BLS, "        Period Offset Bus Width\n");
+      size += sprintf(BLS, "User       %03d    %03d        %d\n",
+                      p->transinfo[target].user_period,
+                      p->transinfo[target].user_offset,
+                      p->transinfo[target].user_width);
+      size += sprintf(BLS, "Goal       %03d    %03d        %d\n",
+                      p->transinfo[target].goal_period,
+                      p->transinfo[target].goal_offset,
+                      p->transinfo[target].goal_width);
+      size += sprintf(BLS, "Current    %03d    %03d        %d\n",
+                      p->transinfo[target].cur_period,
+                      p->transinfo[target].cur_offset,
+                      p->transinfo[target].cur_width);
+      size += sprintf(BLS, "    Total transfers %ld (%ld read;%ld written)\n",
           sp->xfers, sp->r_total, sp->w_total);
-      size += sprintf(BLS, "blks(512) rd=%ld; blks(512) wr=%ld\n",
+      size += sprintf(BLS, "      blks(512) rd=%ld; blks(512) wr=%ld\n",
           sp->r_total512, sp->w_total512);
+#ifdef AIC7XXX_PROC_STATS
       size += sprintf(BLS, "%s\n", HDRB);
       size += sprintf(BLS, " Reads:");
       for (i = 0; i < NUMBER(sp->r_bins); i++)
@@ -306,10 +348,10 @@
       {
         size += sprintf(BLS, "%6ld ", sp->w_bins[i]);
       }
+#endif /* AIC7XXX_PROC_STATS */
       size += sprintf(BLS, "\n\n");
     }
   }
-#endif /* AIC7XXX_PROC_STATS */
 
   if (size >= aic7xxx_buffer_size)
   {
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx_reg.h linux/drivers/scsi/aic7xxx_reg.h
--- v2.1.124/linux/drivers/scsi/aic7xxx_reg.h	Tue Apr 14 14:29:21 1998
+++ linux/drivers/scsi/aic7xxx_reg.h	Thu Oct  8 08:07:34 1998
@@ -49,11 +49,13 @@
 
 #define	SCSIRATE        		0x04
 #define		WIDEXFER        	0x80
+#define		SXFR_ULTRA2     	0x7f
 #define		SXFR            	0x70
 #define		SOFS            	0x0f
 
 #define	SCSIID          		0x05
-#define		OID             	0x0f
+#define	SCSIOFFSET      		0x05
+#define		SOFS_ULTRA2     	0x7f
 
 #define	SCSIDATL        		0x06
 
@@ -73,6 +75,7 @@
 #define		SELDO           	0x40
 #define		SELDI           	0x20
 #define		SELINGO         	0x10
+#define		IOERR           	0x08
 #define		SWRAP           	0x08
 #define		SDONE           	0x04
 #define		SPIORDY         	0x02
@@ -100,20 +103,20 @@
 #define	SSTAT2          		0x0d
 #define		OVERRUN         	0x80
 #define		SFCNT           	0x1f
+#define		EXP_ACTIVE      	0x10
 
 #define	SSTAT3          		0x0e
 #define		SCSICNT         	0xf0
 #define		OFFCNT          	0x0f
 
-#define	SCSITEST        		0x0f
-#define		RQAKCNT         	0x04
-#define		CNTRTEST        	0x02
-#define		CMODE           	0x01
+#define	SCSIID_ULTRA2   		0x0f
+#define		OID             	0x0f
 
 #define	SIMODE0         		0x10
 #define		ENSELDO         	0x40
 #define		ENSELDI         	0x20
 #define		ENSELINGO       	0x10
+#define		ENIOERR         	0x08
 #define		ENSWRAP         	0x08
 #define		ENSDONE         	0x04
 #define		ENSPIORDY       	0x02
@@ -161,10 +164,15 @@
 #define		BRDDAT7         	0x80
 #define		BRDDAT6         	0x40
 #define		BRDDAT5         	0x20
+#define		BRDDAT4         	0x10
 #define		BRDSTB          	0x10
 #define		BRDCS           	0x08
+#define		BRDDAT3         	0x08
+#define		BRDDAT2         	0x04
 #define		BRDRW           	0x04
+#define		BRDRW_ULTRA2    	0x02
 #define		BRDCTL1         	0x02
+#define		BRDSTB_ULTRA2   	0x01
 #define		BRDCTL0         	0x01
 
 #define	SEECTL          		0x1e
@@ -181,11 +189,14 @@
 #define		DIAGLEDEN       	0x80
 #define		DIAGLEDON       	0x40
 #define		AUTOFLUSHDIS    	0x20
+#define		ENAB40          	0x08
+#define		ENAB20          	0x04
 #define		SELWIDE         	0x02
+#define		XCVR            	0x01
 
 #define	SRAM_BASE       		0x20
 
-#define	TARG_SCRATCH    		0x20
+#define	TARG_SCSIRATE   		0x20
 
 #define	ULTRA_ENB       		0x30
 
@@ -194,6 +205,7 @@
 #define	MSG_OUT         		0x34
 
 #define	DMAPARAMS       		0x35
+#define		PRELOADEN       	0x80
 #define		WIDEODD         	0x40
 #define		SCSIEN          	0x20
 #define		SDMAENACK       	0x10
@@ -258,7 +270,12 @@
 #define		SEND_REJ        	0x20
 #define		MSGOUT_PHASEMIS 	0x10
 
-#define	LAST_MSG        		0x52
+#define	ARG_2           		0x52
+#define	RETURN_2        		0x52
+
+#define	LAST_MSG        		0x53
+
+#define	PREFETCH_CNT    		0x54
 
 #define	SCSICONF        		0x5a
 #define		TERM_ENB        	0x80
@@ -314,10 +331,18 @@
 
 #define	STACK           		0x6f
 
+#define	TARG_OFFSET     		0x70
+
 #define	BCTL            		0x84
 #define		ACE             	0x08
 #define		ENABLE          	0x01
 
+#define	DSCOMMAND0      		0x84
+#define		INTSCBRAMSEL    	0x08
+#define		RAMPS           	0x04
+#define		USCBSIZE32      	0x02
+#define		CIOPARCKEN      	0x01
+
 #define	DSCOMMAND       		0x84
 #define		CACHETHEN       	0x80
 #define		DPARCKEN        	0x40
@@ -329,12 +354,12 @@
 #define		BON             	0x0f
 
 #define	BUSSPD          		0x86
-#define		DFTHRSH_100     	0xc0
 #define		DFTHRSH         	0xc0
 #define		STBOFF          	0x38
 #define		STBON           	0x07
 
 #define	DSPCISTATUS     		0x86
+#define		DFTHRSH_100     	0xc0
 
 #define	HCNTRL          		0x87
 #define		POWRDN          	0x40
@@ -381,6 +406,7 @@
 #define		CLRSEQINT       	0x01
 
 #define	ERROR           		0x92
+#define		CIOPARERR       	0x80
 #define		PCIERRSTAT      	0x40
 #define		MPARERR         	0x20
 #define		DPARERR         	0x10
@@ -392,6 +418,7 @@
 #define	DFCNTRL         		0x93
 
 #define	DFSTATUS        		0x94
+#define		PRELOAD_AVAIL   	0x80
 #define		DWORDEMP        	0x20
 #define		MREQPEND        	0x10
 #define		HDONE           	0x08
@@ -413,6 +440,8 @@
 
 #define	QOUTCNT         		0x9e
 
+#define	SFUNCT          		0x9f
+
 #define	SCB_CONTROL     		0xa0
 #define		MK_MESSAGE      	0x80
 #define		DISCENB         	0x40
@@ -464,25 +493,95 @@
 #define		ADSEL           	0x1e
 #define		DI_2840         	0x01
 
+#define	CCHADDR         		0xe0
+
+#define	CCHCNT          		0xe8
+
+#define	CCSGRAM         		0xe9
+
+#define	CCSGADDR        		0xea
+
+#define	CCSGCTL         		0xeb
+#define		CCSGDONE        	0x80
+#define		CCSGEN          	0x08
+#define		FLAG            	0x02
+#define		CCSGRESET       	0x01
+
+#define	CCSCBRAM        		0xec
+
+#define	CCSCBADDR       		0xed
+
+#define	CCSCBCTL        		0xee
+#define		CCSCBDONE       	0x80
+#define		ARRDONE         	0x40
+#define		CCARREN         	0x10
+#define		CCSCBEN         	0x08
+#define		CCSCBDIR        	0x04
+#define		CCSCBRESET      	0x01
+
+#define	CCSCBCNT        		0xef
+
+#define	CCSCBPTR        		0xf1
+
+#define	HNSCB_QOFF      		0xf4
+
+#define	SNSCB_QOFF      		0xf6
+
+#define	SDSCB_QOFF      		0xf8
+
+#define	QOFF_CTLSTA     		0xfa
+#define		SCB_AVAIL       	0x40
+#define		SNSCB_ROLLOVER  	0x20
+#define		SDSCB_ROLLOVER  	0x10
+#define		SCB_QSIZE       	0x07
+#define		SCB_QSIZE_256   	0x06
+
+#define	DFF_THRSH       		0xfb
+#define		WR_DFTHRSH      	0x70
+#define		WR_DFTHRSH_MAX  	0x70
+#define		WR_DFTHRSH_90   	0x60
+#define		WR_DFTHRSH_85   	0x50
+#define		WR_DFTHRSH_75   	0x40
+#define		WR_DFTHRSH_63   	0x30
+#define		WR_DFTHRSH_50   	0x20
+#define		WR_DFTHRSH_25   	0x10
+#define		RD_DFTHRSH_MAX  	0x07
+#define		RD_DFTHRSH      	0x07
+#define		RD_DFTHRSH_90   	0x06
+#define		RD_DFTHRSH_85   	0x05
+#define		RD_DFTHRSH_75   	0x04
+#define		RD_DFTHRSH_63   	0x03
+#define		RD_DFTHRSH_50   	0x02
+#define		RD_DFTHRSH_25   	0x01
+#define		WR_DFTHRSH_MIN  	0x00
+#define		RD_DFTHRSH_MIN  	0x00
+
+#define	SG_CACHEPTR     		0xfc
+#define		SG_USER_DATA    	0xfc
+#define		LAST_SEG        	0x02
+#define		LAST_SEG_DONE   	0x01
+
 
-#define	CMD_GROUP_CODE_SHIFT	0x05
-#define	BUS_8_BIT	0x00
-#define	QOUTFIFO_OFFSET	0x01
 #define	CMD_GROUP2_BYTE_DELTA	0xfa
 #define	MAX_OFFSET_8BIT	0x0f
 #define	BUS_16_BIT	0x01
 #define	QINFIFO_OFFSET	0x02
 #define	CMD_GROUP5_BYTE_DELTA	0x0b
+#define	CMD_GROUP_CODE_SHIFT	0x05
+#define	MAX_OFFSET_ULTRA2	0x7f
 #define	MAX_OFFSET_16BIT	0x08
+#define	BUS_8_BIT	0x00
+#define	QOUTFIFO_OFFSET	0x01
 #define	UNTAGGEDSCB_OFFSET	0x00
+#define	CCSGRAM_MAXSEGS	0x10
 #define	SCB_LIST_NULL	0xff
 #define	SG_SIZEOF	0x08
 #define	CMD_GROUP4_BYTE_DELTA	0x04
 #define	CMD_GROUP0_BYTE_DELTA	0xfc
 #define	HOST_MSG	0xff
 #define	BUS_32_BIT	0x02
+#define	CCSGADDR_MAX	0x80
 
 
 /* Downloaded Constant Definitions */
-#define	TMODE_NUMCMDS	0x01
-#define	QCNTMASK	0x00
+#define	TMODE_NUMCMDS	0x00
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx_seq.c linux/drivers/scsi/aic7xxx_seq.c
--- v2.1.124/linux/drivers/scsi/aic7xxx_seq.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx_seq.c	Thu Oct  8 08:07:34 1998
@@ -0,0 +1,769 @@
+/*
+  * DO NOT EDIT - This file is automatically generated.
+  */
+static unsigned char seqprog[] = {
+	0xff, 0x6a, 0x06, 0x08,
+	0x32, 0x6a, 0x00, 0x00,
+	0x12, 0x6a, 0x00, 0x00,
+	0xff, 0x6a, 0xd6, 0x09,
+	0xff, 0x6a, 0xdc, 0x09,
+	0x00, 0x65, 0x38, 0x59,
+	0xf7, 0x01, 0x02, 0x08,
+	0xff, 0x4e, 0xc8, 0x08,
+	0xbf, 0x60, 0xc0, 0x08,
+	0x60, 0x0b, 0x7c, 0x68,
+	0x40, 0x00, 0x0e, 0x68,
+	0x08, 0x1f, 0x3e, 0x10,
+	0x60, 0x0b, 0x7c, 0x68,
+	0x40, 0x00, 0x0e, 0x68,
+	0x08, 0x1f, 0x3e, 0x10,
+	0xff, 0x3e, 0x3e, 0x60,
+	0x40, 0xfa, 0x10, 0x78,
+	0xff, 0xf6, 0xd4, 0x08,
+	0x01, 0x4e, 0x9c, 0x18,
+	0x40, 0x60, 0xc0, 0x00,
+	0x00, 0x4d, 0x10, 0x70,
+	0x01, 0x4e, 0x9c, 0x18,
+	0xbf, 0x60, 0xc0, 0x08,
+	0x00, 0x6a, 0x72, 0x5c,
+	0xff, 0x4e, 0xc8, 0x18,
+	0x02, 0x6a, 0x88, 0x5b,
+	0xff, 0x52, 0x20, 0x09,
+	0x0d, 0x6a, 0x6a, 0x00,
+	0x00, 0x52, 0xfe, 0x5b,
+	0xff, 0x3e, 0x74, 0x09,
+	0xff, 0x90, 0x7c, 0x08,
+	0xff, 0x3e, 0x20, 0x09,
+	0x00, 0x65, 0x44, 0x58,
+	0x00, 0x65, 0x0e, 0x40,
+	0xf7, 0x1f, 0xca, 0x08,
+	0x08, 0xa1, 0xc8, 0x08,
+	0x00, 0x65, 0xca, 0x00,
+	0xff, 0x65, 0x3e, 0x08,
+	0xf0, 0xa1, 0xc8, 0x08,
+	0x0f, 0x0f, 0x1e, 0x08,
+	0x00, 0x0f, 0x1e, 0x00,
+	0xf0, 0xa1, 0xc8, 0x08,
+	0x0f, 0x05, 0x0a, 0x08,
+	0x00, 0x05, 0x0a, 0x00,
+	0x5a, 0x6a, 0x00, 0x04,
+	0x12, 0x65, 0xc8, 0x00,
+	0x00, 0x01, 0x02, 0x00,
+	0x31, 0x6a, 0xca, 0x00,
+	0x80, 0x37, 0x64, 0x68,
+	0xff, 0x65, 0xca, 0x18,
+	0xff, 0x37, 0xdc, 0x08,
+	0xff, 0x6e, 0xc8, 0x08,
+	0x00, 0x6c, 0x6c, 0x78,
+	0x20, 0x01, 0x02, 0x00,
+	0x4c, 0x37, 0xc8, 0x28,
+	0x08, 0x1f, 0x74, 0x78,
+	0x08, 0x37, 0x6e, 0x00,
+	0x08, 0x64, 0xc8, 0x00,
+	0x70, 0x64, 0xca, 0x18,
+	0xff, 0x6c, 0x0a, 0x08,
+	0x20, 0x64, 0xca, 0x18,
+	0xff, 0x6c, 0x08, 0x0c,
+	0x40, 0x0b, 0x04, 0x69,
+	0x80, 0x0b, 0xf6, 0x78,
+	0xa4, 0x6a, 0x06, 0x00,
+	0x40, 0x6a, 0x16, 0x00,
+	0x10, 0x03, 0xf2, 0x78,
+	0xff, 0x50, 0xc8, 0x08,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x49, 0x6a, 0xee, 0x5b,
+	0x01, 0x6a, 0x26, 0x01,
+	0xff, 0x6a, 0xca, 0x08,
+	0x08, 0x01, 0x02, 0x00,
+	0x02, 0x0b, 0x92, 0x78,
+	0xf7, 0x01, 0x02, 0x08,
+	0xff, 0x06, 0xcc, 0x08,
+	0xff, 0x66, 0x32, 0x09,
+	0x01, 0x65, 0xca, 0x18,
+	0x80, 0x66, 0xa0, 0x78,
+	0xff, 0x66, 0xa2, 0x08,
+	0x10, 0x03, 0x90, 0x68,
+	0xfc, 0x65, 0xc8, 0x18,
+	0x00, 0x65, 0xa8, 0x48,
+	0xff, 0x6a, 0x32, 0x01,
+	0x01, 0x64, 0x18, 0x19,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0x84, 0x6a, 0x06, 0x00,
+	0x08, 0x01, 0x02, 0x00,
+	0x02, 0x0b, 0xb2, 0x78,
+	0xff, 0x06, 0xc8, 0x08,
+	0xff, 0x64, 0x32, 0x09,
+	0xff, 0x6a, 0xca, 0x08,
+	0x5b, 0x64, 0xc8, 0x28,
+	0x00, 0x62, 0xc4, 0x18,
+	0xfc, 0x65, 0xca, 0x18,
+	0xff, 0x6a, 0xd4, 0x08,
+	0xfa, 0x65, 0xca, 0x18,
+	0xff, 0x6a, 0xd4, 0x08,
+	0x04, 0x65, 0xca, 0x18,
+	0x0b, 0x65, 0xca, 0x18,
+	0xff, 0x65, 0xc8, 0x08,
+	0x00, 0x8c, 0x18, 0x19,
+	0x02, 0x0b, 0xce, 0x78,
+	0x01, 0x65, 0xd4, 0x60,
+	0xf7, 0x01, 0x02, 0x08,
+	0xff, 0x06, 0x32, 0x09,
+	0xff, 0x65, 0xca, 0x18,
+	0xff, 0x65, 0xce, 0x68,
+	0x0a, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x64, 0x5c,
+	0x40, 0x51, 0xe6, 0x78,
+	0xe4, 0x6a, 0x06, 0x00,
+	0x08, 0x01, 0x02, 0x00,
+	0x04, 0x6a, 0x18, 0x5b,
+	0x01, 0x50, 0xa0, 0x18,
+	0x00, 0x50, 0xec, 0xe0,
+	0xff, 0x6a, 0xa0, 0x08,
+	0xff, 0x6a, 0x3a, 0x01,
+	0x02, 0x6a, 0x22, 0x01,
+	0x40, 0x51, 0xf2, 0x68,
+	0xff, 0x6a, 0x06, 0x08,
+	0x00, 0x65, 0x0e, 0x40,
+	0x20, 0x6a, 0x16, 0x00,
+	0xf0, 0x19, 0x6e, 0x08,
+	0x08, 0x6a, 0x18, 0x00,
+	0x08, 0x11, 0x22, 0x00,
+	0x08, 0x6a, 0x5a, 0x58,
+	0x08, 0x6a, 0x68, 0x00,
+	0x00, 0x65, 0x18, 0x41,
+	0x12, 0x6a, 0x00, 0x00,
+	0x40, 0x6a, 0x16, 0x00,
+	0xff, 0x3e, 0x20, 0x09,
+	0xff, 0xba, 0x7c, 0x08,
+	0xff, 0xa1, 0x6e, 0x08,
+	0x08, 0x6a, 0x18, 0x00,
+	0x08, 0x11, 0x22, 0x00,
+	0x08, 0x6a, 0x5a, 0x58,
+	0x80, 0x6a, 0x68, 0x00,
+	0x80, 0x36, 0x6c, 0x00,
+	0x00, 0x65, 0xd2, 0x5b,
+	0xff, 0x3d, 0xc8, 0x08,
+	0xbf, 0x64, 0x48, 0x79,
+	0x80, 0x64, 0xf0, 0x71,
+	0xa0, 0x64, 0x0e, 0x72,
+	0xc0, 0x64, 0x08, 0x72,
+	0xe0, 0x64, 0x52, 0x72,
+	0x01, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x18, 0x41,
+	0xf7, 0x11, 0x22, 0x08,
+	0x00, 0x65, 0x38, 0x59,
+	0xff, 0x06, 0xd4, 0x08,
+	0xf7, 0x01, 0x02, 0x08,
+	0x09, 0x0c, 0x32, 0x79,
+	0x08, 0x0c, 0x0e, 0x68,
+	0x01, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0x26, 0x09,
+	0xff, 0x6a, 0x08, 0x08,
+	0xdf, 0x01, 0x02, 0x08,
+	0x01, 0x6a, 0x7a, 0x00,
+	0x03, 0x36, 0x6c, 0x0c,
+	0x08, 0x6a, 0xcc, 0x00,
+	0xa9, 0x6a, 0xe8, 0x5b,
+	0x00, 0x65, 0x66, 0x41,
+	0xa8, 0x6a, 0x6a, 0x00,
+	0x79, 0x6a, 0x6a, 0x00,
+	0x40, 0x3d, 0x50, 0x69,
+	0x04, 0x35, 0x6a, 0x00,
+	0x00, 0x65, 0x3a, 0x5b,
+	0x80, 0x6a, 0xd4, 0x01,
+	0x10, 0x36, 0x42, 0x69,
+	0x10, 0x36, 0x6c, 0x00,
+	0x07, 0xac, 0x10, 0x31,
+	0x88, 0x6a, 0xcc, 0x00,
+	0xac, 0x6a, 0xe0, 0x5b,
+	0x00, 0x65, 0xda, 0x5b,
+	0xff, 0xa3, 0x70, 0x08,
+	0x39, 0x6a, 0xcc, 0x00,
+	0xa4, 0x6a, 0xe6, 0x5b,
+	0xff, 0x38, 0x74, 0x69,
+	0x80, 0x02, 0x04, 0x00,
+	0xe7, 0x35, 0x6a, 0x08,
+	0x03, 0x69, 0x18, 0x31,
+	0xff, 0x6a, 0x10, 0x00,
+	0xff, 0x6a, 0x12, 0x00,
+	0xff, 0x6a, 0x14, 0x00,
+	0x01, 0x38, 0x7a, 0x61,
+	0x02, 0xfc, 0xf8, 0x01,
+	0xbf, 0x35, 0x6a, 0x08,
+	0xff, 0x69, 0xca, 0x08,
+	0xff, 0x35, 0x26, 0x09,
+	0x04, 0x0b, 0x7e, 0x69,
+	0x04, 0x0b, 0x8a, 0x69,
+	0x10, 0x0c, 0x80, 0x79,
+	0x04, 0x0b, 0x88, 0x69,
+	0xff, 0x6a, 0xca, 0x08,
+	0x00, 0x35, 0x22, 0x5b,
+	0x80, 0x02, 0xd6, 0x69,
+	0xff, 0x65, 0xc8, 0x79,
+	0xff, 0x38, 0x70, 0x18,
+	0xff, 0x38, 0xc8, 0x79,
+	0x80, 0xea, 0xaa, 0x61,
+	0xef, 0x38, 0xc8, 0x18,
+	0x80, 0x6a, 0xc8, 0x00,
+	0x00, 0x65, 0x9c, 0x49,
+	0x33, 0x38, 0xc8, 0x28,
+	0xff, 0x64, 0xd0, 0x09,
+	0x04, 0x39, 0xc0, 0x31,
+	0x09, 0x6a, 0xd6, 0x01,
+	0x80, 0xeb, 0xa2, 0x79,
+	0xf7, 0xeb, 0xd6, 0x09,
+	0x08, 0xeb, 0xa6, 0x69,
+	0x01, 0x6a, 0xd6, 0x01,
+	0x08, 0xe9, 0x10, 0x31,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x39, 0x6a, 0xe6, 0x5b,
+	0x08, 0x6a, 0x18, 0x01,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0x0d, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x64, 0x5c,
+	0x88, 0x6a, 0x54, 0x5c,
+	0x00, 0x65, 0xda, 0x5b,
+	0xff, 0x6a, 0xc8, 0x08,
+	0x08, 0x39, 0x72, 0x18,
+	0x00, 0x3a, 0x74, 0x20,
+	0x10, 0x0c, 0x66, 0x79,
+	0x80, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0xe0, 0x59,
+	0xff, 0x08, 0x52, 0x09,
+	0xff, 0x09, 0x54, 0x09,
+	0xff, 0x0a, 0x56, 0x09,
+	0xff, 0x38, 0x50, 0x09,
+	0x12, 0x01, 0x02, 0x00,
+	0x00, 0x65, 0x18, 0x41,
+	0x00, 0x65, 0xe0, 0x59,
+	0x12, 0x01, 0x02, 0x00,
+	0x7f, 0x02, 0x04, 0x08,
+	0xe1, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x18, 0x41,
+	0x04, 0x93, 0xea, 0x69,
+	0xdf, 0x93, 0x26, 0x09,
+	0x20, 0x93, 0xe4, 0x69,
+	0x02, 0x93, 0x26, 0x01,
+	0x01, 0x94, 0xe6, 0x79,
+	0xd7, 0x93, 0x26, 0x09,
+	0x08, 0x93, 0xec, 0x69,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x00, 0x65, 0x3a, 0x5b,
+	0x02, 0xfc, 0xf8, 0x01,
+	0x05, 0xb4, 0x10, 0x31,
+	0x02, 0x6a, 0x1a, 0x31,
+	0x88, 0x6a, 0xcc, 0x00,
+	0xb4, 0x6a, 0xe4, 0x5b,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0x00, 0x65, 0xda, 0x5b,
+	0x3d, 0x6a, 0x22, 0x5b,
+	0xac, 0x6a, 0x22, 0x5b,
+	0x00, 0x65, 0x18, 0x41,
+	0x00, 0x65, 0x3a, 0x5b,
+	0xff, 0x06, 0x44, 0x09,
+	0x00, 0x65, 0x18, 0x41,
+	0xff, 0x34, 0xca, 0x08,
+	0x80, 0x65, 0x32, 0x62,
+	0x0f, 0xa1, 0xca, 0x08,
+	0x07, 0xa1, 0xca, 0x08,
+	0x40, 0xa0, 0xc8, 0x08,
+	0x00, 0x65, 0xca, 0x00,
+	0x80, 0x65, 0xca, 0x00,
+	0x80, 0xa0, 0x22, 0x7a,
+	0xff, 0x65, 0x0c, 0x08,
+	0x00, 0x65, 0x34, 0x42,
+	0x20, 0xa0, 0x3a, 0x7a,
+	0xff, 0x65, 0x0c, 0x08,
+	0x00, 0x65, 0xd2, 0x5b,
+	0xa0, 0x3d, 0x46, 0x62,
+	0x23, 0xa0, 0x0c, 0x08,
+	0x00, 0x65, 0xd2, 0x5b,
+	0xa0, 0x3d, 0x46, 0x62,
+	0x00, 0xb9, 0x3a, 0x42,
+	0xff, 0x65, 0x3a, 0x62,
+	0xa1, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0xd4, 0x08,
+	0x10, 0x51, 0x46, 0x72,
+	0x40, 0x6a, 0x18, 0x00,
+	0xff, 0x65, 0x0c, 0x08,
+	0x00, 0x65, 0xd2, 0x5b,
+	0xa0, 0x3d, 0x46, 0x62,
+	0x10, 0x3d, 0x06, 0x00,
+	0x00, 0x65, 0x0e, 0x42,
+	0x40, 0x6a, 0x18, 0x00,
+	0xff, 0x34, 0xa6, 0x08,
+	0x80, 0x34, 0x4e, 0x62,
+	0x7f, 0xa0, 0x40, 0x09,
+	0x08, 0x6a, 0x68, 0x00,
+	0x00, 0x65, 0x18, 0x41,
+	0x64, 0x6a, 0x12, 0x5b,
+	0x80, 0x64, 0xbe, 0x6a,
+	0x04, 0x64, 0xa4, 0x72,
+	0x02, 0x64, 0xaa, 0x72,
+	0x00, 0x6a, 0x6c, 0x72,
+	0x03, 0x64, 0xba, 0x72,
+	0x01, 0x64, 0xa0, 0x72,
+	0x07, 0x64, 0x00, 0x73,
+	0x08, 0x64, 0x68, 0x72,
+	0x11, 0x6a, 0x22, 0x01,
+	0x07, 0x6a, 0x04, 0x5b,
+	0xff, 0x06, 0xd4, 0x08,
+	0x00, 0x65, 0x18, 0x41,
+	0xff, 0xa8, 0x70, 0x6a,
+	0xff, 0xa2, 0x88, 0x7a,
+	0x01, 0x6a, 0x6a, 0x00,
+	0x00, 0xb9, 0xfe, 0x5b,
+	0xff, 0xa2, 0x88, 0x7a,
+	0x71, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0xd4, 0x08,
+	0x40, 0x51, 0x88, 0x62,
+	0x0d, 0x6a, 0x6a, 0x00,
+	0x00, 0xb9, 0xfe, 0x5b,
+	0xff, 0x3e, 0x74, 0x09,
+	0xff, 0x90, 0x7c, 0x08,
+	0x00, 0x65, 0x44, 0x58,
+	0x00, 0x65, 0x2a, 0x41,
+	0x20, 0xa0, 0x90, 0x6a,
+	0xff, 0x37, 0xc8, 0x08,
+	0x00, 0x6a, 0xa8, 0x5b,
+	0xff, 0x6a, 0xbe, 0x5b,
+	0xff, 0xf8, 0xc8, 0x08,
+	0xff, 0x4f, 0xc8, 0x08,
+	0x01, 0x6a, 0xa8, 0x5b,
+	0x00, 0xb9, 0xbe, 0x5b,
+	0x01, 0x4f, 0x9e, 0x18,
+	0x02, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x6c, 0x5c,
+	0x00, 0x65, 0x2a, 0x41,
+	0x41, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x18, 0x41,
+	0x04, 0xa0, 0x40, 0x01,
+	0x00, 0x65, 0x84, 0x5c,
+	0x00, 0x65, 0x2a, 0x41,
+	0x10, 0x36, 0x68, 0x7a,
+	0xff, 0x38, 0x46, 0x09,
+	0xa4, 0x6a, 0xcc, 0x00,
+	0x39, 0x6a, 0xe6, 0x5b,
+	0xac, 0x6a, 0xcc, 0x00,
+	0x14, 0x6a, 0xe6, 0x5b,
+	0xa9, 0x6a, 0xe8, 0x5b,
+	0x00, 0x65, 0x68, 0x42,
+	0xef, 0x36, 0x6c, 0x08,
+	0x00, 0x65, 0x68, 0x42,
+	0x0f, 0x64, 0xc8, 0x08,
+	0x07, 0x64, 0xc8, 0x08,
+	0x00, 0x37, 0x6e, 0x00,
+	0x00, 0x65, 0x78, 0x5b,
+	0xff, 0x51, 0xce, 0x72,
+	0x20, 0x36, 0xde, 0x7a,
+	0x00, 0x90, 0x5c, 0x5b,
+	0x00, 0x65, 0xe0, 0x42,
+	0xff, 0x06, 0xd4, 0x08,
+	0x00, 0x65, 0xd2, 0x5b,
+	0xe0, 0x3d, 0xfa, 0x62,
+	0x20, 0x12, 0xfa, 0x62,
+	0x51, 0x6a, 0x08, 0x5b,
+	0xff, 0x51, 0x20, 0x09,
+	0x20, 0xa0, 0xfa, 0x7a,
+	0x00, 0x90, 0x5c, 0x5b,
+	0x00, 0x65, 0x56, 0x5b,
+	0xff, 0x37, 0xc8, 0x08,
+	0x00, 0xa1, 0xf2, 0x62,
+	0x04, 0xa0, 0xf2, 0x7a,
+	0xfb, 0xa0, 0x40, 0x09,
+	0x80, 0x36, 0x6c, 0x00,
+	0x80, 0xa0, 0x68, 0x7a,
+	0x7f, 0xa0, 0x40, 0x09,
+	0xff, 0x6a, 0x04, 0x5b,
+	0x00, 0x65, 0x68, 0x42,
+	0x04, 0xa0, 0xf8, 0x7a,
+	0x00, 0x65, 0x84, 0x5c,
+	0x00, 0x65, 0xfa, 0x42,
+	0x00, 0x65, 0x6c, 0x5c,
+	0x31, 0x6a, 0x22, 0x01,
+	0x0c, 0x6a, 0x04, 0x5b,
+	0x00, 0x65, 0x68, 0x42,
+	0x61, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x68, 0x42,
+	0x10, 0x3d, 0x06, 0x00,
+	0xff, 0x65, 0x68, 0x0c,
+	0xff, 0x06, 0xd4, 0x08,
+	0x01, 0x0c, 0x0a, 0x7b,
+	0x04, 0x0c, 0x0a, 0x6b,
+	0xe0, 0x03, 0x7a, 0x08,
+	0xe0, 0x3d, 0x1e, 0x63,
+	0xff, 0x65, 0xcc, 0x08,
+	0xff, 0x12, 0xda, 0x0c,
+	0xff, 0x06, 0xd4, 0x0c,
+	0xff, 0x65, 0x0c, 0x08,
+	0x02, 0x0b, 0x1a, 0x7b,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0xd1, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x18, 0x41,
+	0xff, 0x65, 0x26, 0x09,
+	0x01, 0x0b, 0x32, 0x6b,
+	0x10, 0x0c, 0x24, 0x7b,
+	0x04, 0x0b, 0x2c, 0x6b,
+	0xff, 0x6a, 0xca, 0x08,
+	0x04, 0x93, 0x30, 0x6b,
+	0x01, 0x94, 0x2e, 0x7b,
+	0x10, 0x94, 0x30, 0x6b,
+	0xc7, 0x93, 0x26, 0x09,
+	0xff, 0x99, 0xd4, 0x08,
+	0x08, 0x93, 0x34, 0x6b,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x80, 0x36, 0x38, 0x6b,
+	0x21, 0x6a, 0x22, 0x05,
+	0xff, 0x65, 0x20, 0x09,
+	0xff, 0x51, 0x46, 0x63,
+	0xff, 0x37, 0xc8, 0x08,
+	0xa1, 0x6a, 0x50, 0x43,
+	0xff, 0x51, 0xc8, 0x08,
+	0xb9, 0x6a, 0x50, 0x43,
+	0xff, 0xba, 0x54, 0x73,
+	0xff, 0xba, 0x20, 0x09,
+	0xff, 0x65, 0xca, 0x18,
+	0x00, 0x6c, 0x4a, 0x63,
+	0xff, 0x90, 0xca, 0x0c,
+	0xff, 0x6a, 0xca, 0x04,
+	0x20, 0x36, 0x72, 0x7b,
+	0x00, 0x90, 0x3e, 0x5b,
+	0xff, 0x65, 0x72, 0x73,
+	0xff, 0xba, 0x66, 0x73,
+	0xff, 0xbb, 0xcc, 0x08,
+	0xff, 0xba, 0x20, 0x09,
+	0xff, 0x66, 0x76, 0x09,
+	0xff, 0x65, 0x20, 0x09,
+	0xff, 0xbb, 0x70, 0x73,
+	0xff, 0xba, 0xcc, 0x08,
+	0xff, 0xbb, 0x20, 0x09,
+	0xff, 0x66, 0x74, 0x09,
+	0xff, 0x65, 0x20, 0x0d,
+	0xff, 0xba, 0x7e, 0x0c,
+	0x00, 0x6a, 0x72, 0x5c,
+	0x0d, 0x6a, 0x6a, 0x00,
+	0x00, 0x51, 0xfe, 0x43,
+	0xff, 0x3f, 0xcc, 0x73,
+	0xff, 0x6a, 0xa2, 0x00,
+	0x00, 0x3f, 0x3e, 0x5b,
+	0xff, 0x65, 0xcc, 0x73,
+	0x20, 0x36, 0x6c, 0x00,
+	0x20, 0xa0, 0x86, 0x6b,
+	0xff, 0xb9, 0xa2, 0x0c,
+	0xff, 0x6a, 0xa2, 0x04,
+	0xff, 0x65, 0xa4, 0x08,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x45, 0x6a, 0xf2, 0x5b,
+	0x01, 0x6a, 0xd0, 0x01,
+	0x09, 0x6a, 0xd6, 0x01,
+	0x80, 0xeb, 0x92, 0x7b,
+	0x01, 0x6a, 0xd6, 0x01,
+	0x01, 0xe9, 0xa4, 0x34,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x45, 0x6a, 0xf2, 0x5b,
+	0x01, 0x6a, 0x18, 0x01,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0x0d, 0x6a, 0x26, 0x01,
+	0x00, 0x65, 0x64, 0x5c,
+	0xff, 0x99, 0xa4, 0x0c,
+	0xff, 0x65, 0xa4, 0x08,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x45, 0x6a, 0xf2, 0x5b,
+	0x01, 0x6a, 0xd0, 0x01,
+	0x01, 0x6a, 0xdc, 0x05,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x45, 0x6a, 0xf2, 0x5b,
+	0x01, 0x6a, 0x18, 0x01,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0x01, 0x6a, 0x26, 0x05,
+	0x01, 0x65, 0xd8, 0x31,
+	0x09, 0xee, 0xdc, 0x01,
+	0x80, 0xee, 0xc2, 0x7b,
+	0xff, 0x6a, 0xdc, 0x0d,
+	0xff, 0x65, 0x32, 0x09,
+	0x0a, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x64, 0x44,
+	0xff, 0x37, 0xc8, 0x08,
+	0x00, 0x6a, 0x88, 0x5b,
+	0xff, 0x52, 0xa2, 0x0c,
+	0x01, 0x0c, 0xd2, 0x7b,
+	0x04, 0x0c, 0xd2, 0x6b,
+	0xe0, 0x03, 0x7a, 0x08,
+	0xff, 0x3d, 0x06, 0x0c,
+	0xff, 0x8c, 0x10, 0x08,
+	0xff, 0x8d, 0x12, 0x08,
+	0xff, 0x8e, 0x14, 0x0c,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x0c,
+	0x3d, 0x64, 0xa4, 0x28,
+	0x55, 0x64, 0xc8, 0x28,
+	0x00, 0x6c, 0xda, 0x18,
+	0xff, 0x52, 0xc8, 0x08,
+	0x00, 0x6c, 0xda, 0x20,
+	0xff, 0x6a, 0xc8, 0x08,
+	0x00, 0x6c, 0xda, 0x20,
+	0x00, 0x6c, 0xda, 0x24,
+	0xff, 0x65, 0xc8, 0x08,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x41, 0x6a, 0xee, 0x5b,
+	0xff, 0x90, 0xe2, 0x09,
+	0x20, 0x6a, 0xd0, 0x01,
+	0x04, 0x35, 0x10, 0x7c,
+	0x1d, 0x6a, 0xdc, 0x01,
+	0xdc, 0xee, 0x0c, 0x64,
+	0x00, 0x65, 0x1c, 0x44,
+	0x01, 0x6a, 0xdc, 0x01,
+	0x20, 0xa0, 0xd8, 0x31,
+	0x09, 0xee, 0xdc, 0x01,
+	0x80, 0xee, 0x16, 0x7c,
+	0x19, 0x6a, 0xdc, 0x01,
+	0xd8, 0xee, 0x1a, 0x64,
+	0xff, 0x6a, 0xdc, 0x09,
+	0x18, 0xee, 0x1e, 0x6c,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x41, 0x6a, 0xee, 0x5b,
+	0x20, 0x6a, 0x18, 0x01,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0xff, 0x35, 0x26, 0x09,
+	0x04, 0x35, 0x48, 0x6c,
+	0xa0, 0x6a, 0xca, 0x00,
+	0x20, 0x65, 0xc8, 0x18,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0x00, 0x65, 0x34, 0x64,
+	0x0a, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x64, 0x5c,
+	0x04, 0x35, 0x38, 0x7b,
+	0xa0, 0x6a, 0x54, 0x5c,
+	0x00, 0x65, 0x56, 0x5c,
+	0x00, 0x65, 0x56, 0x5c,
+	0x00, 0x65, 0x56, 0x44,
+	0xff, 0x65, 0xcc, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x0c,
+	0x08, 0x94, 0x64, 0x7c,
+	0xf7, 0x93, 0x26, 0x09,
+	0x08, 0x93, 0x68, 0x6c,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0xff, 0x40, 0x74, 0x09,
+	0xff, 0x90, 0x80, 0x08,
+	0xff, 0x6a, 0x72, 0x05,
+	0xff, 0x40, 0x80, 0x64,
+	0xff, 0x3f, 0x78, 0x64,
+	0xff, 0x6a, 0xca, 0x04,
+	0xff, 0x3f, 0x20, 0x09,
+	0x01, 0x6a, 0x6a, 0x00,
+	0x00, 0xb9, 0xfe, 0x5b,
+	0x00, 0x90, 0x5c, 0x43,
+	0xff, 0x40, 0x20, 0x09,
+	0xff, 0xba, 0x80, 0x0c,
+	0xff, 0x6a, 0x76, 0x01,
+	0xff, 0x3f, 0x74, 0x09,
+	0xff, 0x90, 0x7e, 0x08,
+	0xff, 0xba, 0x38, 0x73,
+	0xff, 0xba, 0x20, 0x09,
+	0xff, 0x3f, 0x76, 0x09,
+	0xff, 0x3f, 0x20, 0x0d,
+};
+
+static int aic7xxx_patch12_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch12_func(struct aic7xxx_host *p)
+{
+	return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895);
+}
+
+static int aic7xxx_patch11_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch11_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_WIDE) != 0);
+}
+
+static int aic7xxx_patch10_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch10_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_ULTRA2) == 0);
+}
+
+static int aic7xxx_patch9_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch9_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_ULTRA) != 0);
+}
+
+static int aic7xxx_patch8_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch8_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_ULTRA2) != 0);
+}
+
+static int aic7xxx_patch7_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch7_func(struct aic7xxx_host *p)
+{
+	return ((p->flags & AHC_PAGESCBS) == 0);
+}
+
+static int aic7xxx_patch6_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch6_func(struct aic7xxx_host *p)
+{
+	return ((p->flags & AHC_PAGESCBS) != 0);
+}
+
+static int aic7xxx_patch5_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch5_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_QUEUE_REGS) != 0);
+}
+
+static int aic7xxx_patch4_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch4_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_TWIN) != 0);
+}
+
+static int aic7xxx_patch3_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch3_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_QUEUE_REGS) == 0);
+}
+
+static int aic7xxx_patch2_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch2_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_CMD_CHAN) != 0);
+}
+
+static int aic7xxx_patch1_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch1_func(struct aic7xxx_host *p)
+{
+	return ((p->flags & AHC_TARGETMODE) != 0);
+}
+
+static int aic7xxx_patch0_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch0_func(struct aic7xxx_host *p)
+{
+	return (0);
+}
+
+struct sequencer_patch {
+	int		(*patch_func)(struct aic7xxx_host *);
+	unsigned int	begin	   :10,
+			skip_instr :10,
+			skip_patch :12;
+} sequencer_patches[] = {
+	{ aic7xxx_patch1_func, 1, 1, 2 },
+	{ aic7xxx_patch0_func, 2, 1, 1 },
+	{ aic7xxx_patch2_func, 3, 2, 1 },
+	{ aic7xxx_patch3_func, 7, 1, 1 },
+	{ aic7xxx_patch3_func, 8, 1, 1 },
+	{ aic7xxx_patch4_func, 11, 4, 1 },
+	{ aic7xxx_patch5_func, 16, 3, 2 },
+	{ aic7xxx_patch0_func, 19, 4, 1 },
+	{ aic7xxx_patch6_func, 23, 1, 1 },
+	{ aic7xxx_patch7_func, 26, 1, 1 },
+	{ aic7xxx_patch4_func, 34, 4, 1 },
+	{ aic7xxx_patch8_func, 38, 3, 2 },
+	{ aic7xxx_patch0_func, 41, 3, 1 },
+	{ aic7xxx_patch9_func, 47, 7, 1 },
+	{ aic7xxx_patch4_func, 55, 3, 1 },
+	{ aic7xxx_patch8_func, 58, 2, 1 },
+	{ aic7xxx_patch1_func, 63, 60, 1 },
+	{ aic7xxx_patch8_func, 164, 1, 2 },
+	{ aic7xxx_patch0_func, 165, 1, 1 },
+	{ aic7xxx_patch2_func, 169, 1, 1 },
+	{ aic7xxx_patch2_func, 172, 1, 2 },
+	{ aic7xxx_patch0_func, 173, 2, 1 },
+	{ aic7xxx_patch10_func, 175, 1, 1 },
+	{ aic7xxx_patch8_func, 182, 1, 2 },
+	{ aic7xxx_patch0_func, 183, 3, 1 },
+	{ aic7xxx_patch8_func, 187, 1, 2 },
+	{ aic7xxx_patch0_func, 188, 1, 1 },
+	{ aic7xxx_patch8_func, 189, 7, 2 },
+	{ aic7xxx_patch0_func, 196, 1, 1 },
+	{ aic7xxx_patch2_func, 201, 13, 2 },
+	{ aic7xxx_patch0_func, 214, 8, 1 },
+	{ aic7xxx_patch10_func, 222, 1, 1 },
+	{ aic7xxx_patch8_func, 227, 1, 1 },
+	{ aic7xxx_patch8_func, 228, 1, 1 },
+	{ aic7xxx_patch8_func, 233, 1, 1 },
+	{ aic7xxx_patch8_func, 235, 2, 1 },
+	{ aic7xxx_patch8_func, 240, 8, 1 },
+	{ aic7xxx_patch8_func, 249, 1, 1 },
+	{ aic7xxx_patch2_func, 250, 2, 2 },
+	{ aic7xxx_patch0_func, 252, 4, 1 },
+	{ aic7xxx_patch10_func, 256, 2, 2 },
+	{ aic7xxx_patch0_func, 258, 1, 1 },
+	{ aic7xxx_patch11_func, 265, 1, 2 },
+	{ aic7xxx_patch0_func, 266, 1, 1 },
+	{ aic7xxx_patch5_func, 328, 1, 2 },
+	{ aic7xxx_patch0_func, 329, 1, 1 },
+	{ aic7xxx_patch3_func, 332, 1, 1 },
+	{ aic7xxx_patch11_func, 351, 1, 2 },
+	{ aic7xxx_patch0_func, 352, 1, 1 },
+	{ aic7xxx_patch6_func, 356, 1, 1 },
+	{ aic7xxx_patch7_func, 364, 3, 2 },
+	{ aic7xxx_patch0_func, 367, 1, 1 },
+	{ aic7xxx_patch1_func, 396, 3, 1 },
+	{ aic7xxx_patch10_func, 410, 1, 1 },
+	{ aic7xxx_patch2_func, 453, 7, 2 },
+	{ aic7xxx_patch0_func, 460, 8, 1 },
+	{ aic7xxx_patch2_func, 469, 4, 2 },
+	{ aic7xxx_patch0_func, 473, 6, 1 },
+	{ aic7xxx_patch2_func, 479, 4, 2 },
+	{ aic7xxx_patch0_func, 483, 3, 1 },
+	{ aic7xxx_patch2_func, 512, 17, 4 },
+	{ aic7xxx_patch12_func, 520, 4, 2 },
+	{ aic7xxx_patch0_func, 524, 2, 1 },
+	{ aic7xxx_patch0_func, 529, 33, 1 },
+	{ aic7xxx_patch6_func, 566, 2, 1 },
+	{ aic7xxx_patch6_func, 569, 9, 1 },
+
+};
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/aic7xxx_seq.h linux/drivers/scsi/aic7xxx_seq.h
--- v2.1.124/linux/drivers/scsi/aic7xxx_seq.h	Sun Jun  7 11:16:34 1998
+++ linux/drivers/scsi/aic7xxx_seq.h	Wed Dec 31 16:00:00 1969
@@ -1,524 +0,0 @@
-/*
-  * DO NOT EDIT - This file is automatically generated.
-  */
-static unsigned char seqprog[] = {
-	0xff, 0x6a, 0x03, 0x02,
-	0x32, 0x6a, 0x00, 0x00,
-	0x12, 0x6a, 0x00, 0x00,
-	0x00, 0x65, 0x92, 0x16,
-	0xf7, 0x01, 0x01, 0x02,
-	0xff, 0x4e, 0x64, 0x02,
-	0xbf, 0x60, 0x60, 0x02,
-	0x60, 0x0b, 0x37, 0x1a,
-	0x40, 0x00, 0x05, 0x1a,
-	0x08, 0x1f, 0x1f, 0x04,
-	0x60, 0x0b, 0x37, 0x1a,
-	0x40, 0x00, 0x05, 0x1a,
-	0x08, 0x1f, 0x1f, 0x04,
-	0xff, 0x3e, 0x1d, 0x18,
-	0x40, 0x60, 0x60, 0x00,
-	0x00, 0x4d, 0x06, 0x1c,
-	0x01, 0x4e, 0x4e, 0x06,
-	0xbf, 0x60, 0x60, 0x02,
-	0x00, 0x6a, 0xd8, 0x17,
-	0xff, 0x4e, 0x64, 0x06,
-	0x02, 0x6a, 0x93, 0x17,
-	0x0d, 0x6a, 0x93, 0x00,
-	0x00, 0x65, 0xd1, 0x17,
-	0xff, 0x99, 0x65, 0x02,
-	0xff, 0x65, 0x90, 0x02,
-	0x0d, 0x6a, 0x35, 0x00,
-	0x00, 0x65, 0xb3, 0x17,
-	0xff, 0x3e, 0xba, 0x02,
-	0xff, 0x90, 0x3e, 0x02,
-	0xff, 0x3e, 0x90, 0x02,
-	0x00, 0x65, 0x20, 0x16,
-	0x00, 0x65, 0x05, 0x10,
-	0xf7, 0x1f, 0x65, 0x02,
-	0x08, 0xa1, 0x64, 0x02,
-	0x00, 0x65, 0x65, 0x00,
-	0xff, 0x65, 0x1f, 0x02,
-	0xf0, 0xa1, 0x64, 0x02,
-	0x0f, 0x05, 0x05, 0x02,
-	0x00, 0x05, 0x05, 0x00,
-	0x5a, 0x6a, 0x00, 0x01,
-	0x12, 0x65, 0x64, 0x00,
-	0x00, 0x01, 0x01, 0x00,
-	0x31, 0x6a, 0x65, 0x00,
-	0x80, 0x37, 0x2d, 0x1a,
-	0xff, 0x65, 0x65, 0x06,
-	0xff, 0x37, 0x6e, 0x02,
-	0xff, 0x6e, 0x64, 0x02,
-	0x00, 0x6c, 0x31, 0x1e,
-	0x20, 0x01, 0x01, 0x00,
-	0x4c, 0x37, 0x64, 0x0a,
-	0x08, 0x1f, 0x35, 0x1e,
-	0x08, 0x37, 0x37, 0x00,
-	0x08, 0x64, 0x64, 0x00,
-	0x20, 0x64, 0x65, 0x06,
-	0xff, 0x6c, 0x04, 0x03,
-	0x40, 0x0b, 0x78, 0x1a,
-	0x80, 0x0b, 0x71, 0x1e,
-	0xa4, 0x6a, 0x03, 0x00,
-	0x40, 0x6a, 0x0b, 0x00,
-	0x10, 0x03, 0x6f, 0x1e,
-	0xff, 0x50, 0x64, 0x02,
-	0x49, 0x6a, 0xa9, 0x17,
-	0x01, 0x6a, 0x93, 0x00,
-	0xff, 0x6a, 0x65, 0x02,
-	0x08, 0x01, 0x01, 0x00,
-	0x02, 0x0b, 0x41, 0x1e,
-	0xf7, 0x01, 0x01, 0x02,
-	0xff, 0x06, 0x66, 0x02,
-	0xff, 0x66, 0x99, 0x02,
-	0x01, 0x65, 0x65, 0x06,
-	0x80, 0x66, 0x48, 0x1e,
-	0xff, 0x66, 0x51, 0x02,
-	0x10, 0x03, 0x40, 0x1a,
-	0xfc, 0x65, 0x64, 0x06,
-	0x00, 0x65, 0x4c, 0x12,
-	0xff, 0x6a, 0x99, 0x00,
-	0x01, 0x64, 0x8c, 0x06,
-	0x84, 0x6a, 0x03, 0x00,
-	0x08, 0x01, 0x01, 0x00,
-	0x02, 0x0b, 0x4f, 0x1e,
-	0xff, 0x06, 0x64, 0x02,
-	0xff, 0x64, 0x99, 0x02,
-	0xff, 0x6a, 0x65, 0x02,
-	0x5b, 0x64, 0x64, 0x0a,
-	0x00, 0x62, 0x62, 0x06,
-	0xfc, 0x65, 0x65, 0x06,
-	0xff, 0x6a, 0x6a, 0x02,
-	0xfa, 0x65, 0x65, 0x06,
-	0xff, 0x6a, 0x6a, 0x02,
-	0x04, 0x65, 0x65, 0x06,
-	0x0b, 0x65, 0x65, 0x06,
-	0xff, 0x65, 0x64, 0x02,
-	0x00, 0x8c, 0x8c, 0x06,
-	0x02, 0x0b, 0x5d, 0x1e,
-	0x01, 0x65, 0x60, 0x18,
-	0xf7, 0x01, 0x01, 0x02,
-	0xff, 0x06, 0x99, 0x02,
-	0xff, 0x65, 0x65, 0x06,
-	0xff, 0x65, 0x5d, 0x1a,
-	0x0a, 0x93, 0x93, 0x00,
-	0x00, 0x65, 0xd1, 0x17,
-	0x40, 0x51, 0x69, 0x1e,
-	0xe4, 0x6a, 0x03, 0x00,
-	0x08, 0x01, 0x01, 0x00,
-	0x04, 0x6a, 0x5c, 0x17,
-	0x01, 0x50, 0x50, 0x06,
-	0x01, 0x50, 0x6c, 0x98,
-	0xff, 0x6a, 0x50, 0x02,
-	0xff, 0x6a, 0x9d, 0x00,
-	0x02, 0x6a, 0x91, 0x00,
-	0x40, 0x51, 0x6f, 0x1a,
-	0xff, 0x6a, 0x03, 0x02,
-	0x00, 0x65, 0x05, 0x10,
-	0x20, 0x6a, 0x0b, 0x00,
-	0xf0, 0x19, 0x37, 0x02,
-	0x08, 0x6a, 0x0c, 0x00,
-	0x08, 0x11, 0x11, 0x00,
-	0x08, 0x6a, 0x28, 0x16,
-	0x08, 0x6a, 0x34, 0x00,
-	0x00, 0x65, 0x82, 0x10,
-	0x12, 0x6a, 0x00, 0x00,
-	0x40, 0x6a, 0x0b, 0x00,
-	0xff, 0x3e, 0x90, 0x02,
-	0xff, 0xba, 0x3e, 0x02,
-	0xff, 0xa1, 0x37, 0x02,
-	0x08, 0x6a, 0x0c, 0x00,
-	0x08, 0x11, 0x11, 0x00,
-	0x08, 0x6a, 0x28, 0x16,
-	0x80, 0x6a, 0x34, 0x00,
-	0x80, 0x36, 0x36, 0x00,
-	0x00, 0x65, 0x9b, 0x17,
-	0xff, 0x3d, 0x64, 0x02,
-	0xbf, 0x64, 0x9a, 0x1e,
-	0x80, 0x64, 0xc9, 0x1c,
-	0xa0, 0x64, 0xd4, 0x1c,
-	0xc0, 0x64, 0xd1, 0x1c,
-	0xe0, 0x64, 0xf5, 0x1c,
-	0x01, 0x6a, 0x91, 0x00,
-	0x00, 0x65, 0x82, 0x10,
-	0xf7, 0x11, 0x11, 0x02,
-	0x00, 0x65, 0x92, 0x16,
-	0xff, 0x06, 0x6a, 0x02,
-	0xf7, 0x01, 0x01, 0x02,
-	0x09, 0x0c, 0x8f, 0x1e,
-	0x08, 0x0c, 0x05, 0x1a,
-	0x01, 0x6a, 0x91, 0x00,
-	0xff, 0x6a, 0x93, 0x02,
-	0xff, 0x6a, 0x04, 0x02,
-	0xdf, 0x01, 0x01, 0x02,
-	0x01, 0x6a, 0x3d, 0x00,
-	0x03, 0x36, 0x36, 0x03,
-	0x08, 0x6a, 0x66, 0x00,
-	0xa9, 0x6a, 0xa6, 0x17,
-	0x00, 0x65, 0xa5, 0x10,
-	0x79, 0x6a, 0x35, 0x00,
-	0x40, 0x3d, 0x9d, 0x1a,
-	0x04, 0x35, 0x35, 0x00,
-	0x00, 0x65, 0x6c, 0x17,
-	0x10, 0x36, 0x97, 0x1a,
-	0x88, 0x6a, 0x66, 0x00,
-	0xac, 0x6a, 0xa2, 0x17,
-	0x00, 0x65, 0x9f, 0x17,
-	0xff, 0xa3, 0x38, 0x02,
-	0x39, 0x6a, 0x66, 0x00,
-	0xa4, 0x6a, 0xa5, 0x17,
-	0xff, 0x38, 0xac, 0x1a,
-	0x80, 0x02, 0x02, 0x00,
-	0xff, 0x6a, 0x8c, 0x00,
-	0xff, 0x6a, 0x8d, 0x00,
-	0xff, 0x6a, 0x8e, 0x00,
-	0x00, 0x65, 0x9f, 0x17,
-	0xe7, 0x35, 0x35, 0x02,
-	0x01, 0x38, 0xae, 0x18,
-	0xbf, 0x35, 0x35, 0x02,
-	0x00, 0x35, 0x61, 0x17,
-	0x80, 0x02, 0xc6, 0x1a,
-	0xff, 0x65, 0xc0, 0x1e,
-	0xff, 0x38, 0x38, 0x06,
-	0xff, 0x38, 0xc0, 0x1e,
-	0xff, 0x6a, 0x64, 0x02,
-	0x08, 0x39, 0x39, 0x06,
-	0x00, 0x3a, 0x3a, 0x08,
-	0x88, 0x6a, 0x66, 0x00,
-	0x39, 0x6a, 0xa5, 0x17,
-	0x08, 0x6a, 0x8c, 0x00,
-	0xff, 0x6a, 0x8d, 0x02,
-	0xff, 0x6a, 0x8e, 0x02,
-	0x0d, 0x93, 0x93, 0x00,
-	0x00, 0x65, 0xd1, 0x17,
-	0x88, 0x6a, 0xc9, 0x17,
-	0x00, 0x65, 0x9f, 0x17,
-	0x10, 0x0c, 0xa5, 0x1e,
-	0xff, 0x08, 0xa9, 0x02,
-	0xff, 0x09, 0xaa, 0x02,
-	0xff, 0x0a, 0xab, 0x02,
-	0xff, 0x38, 0xa8, 0x02,
-	0x10, 0x36, 0x36, 0x00,
-	0x00, 0x65, 0x82, 0x10,
-	0x7f, 0x02, 0x02, 0x02,
-	0xe1, 0x6a, 0x91, 0x00,
-	0x00, 0x65, 0x82, 0x10,
-	0x00, 0x65, 0x6c, 0x17,
-	0x88, 0x6a, 0x66, 0x00,
-	0xb4, 0x6a, 0xa4, 0x17,
-	0xff, 0x6a, 0x8d, 0x02,
-	0xff, 0x6a, 0x8e, 0x02,
-	0x00, 0x65, 0x9f, 0x17,
-	0x3d, 0x6a, 0x61, 0x17,
-	0x00, 0x65, 0x82, 0x10,
-	0x00, 0x65, 0x6c, 0x17,
-	0xff, 0x06, 0xa2, 0x02,
-	0x00, 0x65, 0x82, 0x10,
-	0xff, 0x34, 0x65, 0x02,
-	0x80, 0x65, 0xe6, 0x18,
-	0x0f, 0xa1, 0x65, 0x02,
-	0x07, 0xa1, 0x65, 0x02,
-	0x40, 0xa0, 0x64, 0x02,
-	0x00, 0x65, 0x65, 0x00,
-	0x80, 0x65, 0x65, 0x00,
-	0x80, 0xa0, 0xde, 0x1e,
-	0xff, 0x65, 0x06, 0x02,
-	0x00, 0x65, 0xe7, 0x10,
-	0x20, 0xa0, 0xe9, 0x1e,
-	0xff, 0x65, 0x06, 0x02,
-	0x00, 0x65, 0x9b, 0x17,
-	0xa0, 0x3d, 0xef, 0x18,
-	0x23, 0xa0, 0x06, 0x02,
-	0x00, 0x65, 0x9b, 0x17,
-	0xa0, 0x3d, 0xef, 0x18,
-	0x00, 0xb9, 0xe9, 0x10,
-	0xff, 0x65, 0xe9, 0x18,
-	0xa1, 0x6a, 0x91, 0x00,
-	0x10, 0x51, 0xef, 0x1c,
-	0x40, 0x6a, 0x0c, 0x00,
-	0xff, 0x65, 0x06, 0x02,
-	0x00, 0x65, 0x9b, 0x17,
-	0xa0, 0x3d, 0xef, 0x18,
-	0x10, 0x3d, 0x03, 0x00,
-	0x00, 0x65, 0xd4, 0x10,
-	0x40, 0x6a, 0x0c, 0x00,
-	0xff, 0x34, 0x52, 0x02,
-	0x80, 0x34, 0xf3, 0x18,
-	0x7f, 0xa0, 0xa0, 0x02,
-	0x08, 0x6a, 0x34, 0x00,
-	0x00, 0x65, 0x82, 0x10,
-	0x64, 0x6a, 0x59, 0x17,
-	0x80, 0x64, 0x2f, 0x1b,
-	0x04, 0x64, 0x22, 0x1d,
-	0x02, 0x64, 0x25, 0x1d,
-	0x00, 0x6a, 0x02, 0x1d,
-	0x03, 0x64, 0x2d, 0x1d,
-	0x01, 0x64, 0x20, 0x1d,
-	0x07, 0x64, 0x50, 0x1d,
-	0x08, 0x64, 0x00, 0x1d,
-	0x11, 0x6a, 0x91, 0x00,
-	0x07, 0x6a, 0x52, 0x17,
-	0xff, 0x06, 0x6a, 0x02,
-	0x00, 0x65, 0x82, 0x10,
-	0xff, 0xa8, 0x04, 0x1b,
-	0xff, 0xa2, 0x0f, 0x1f,
-	0x01, 0x6a, 0x35, 0x00,
-	0x00, 0xb9, 0xb3, 0x17,
-	0xff, 0xa2, 0x0f, 0x1f,
-	0x71, 0x6a, 0x91, 0x00,
-	0x40, 0x51, 0x0f, 0x19,
-	0x0d, 0x6a, 0x35, 0x00,
-	0x00, 0xb9, 0xb3, 0x17,
-	0xff, 0x3e, 0xba, 0x02,
-	0xff, 0x90, 0x3e, 0x02,
-	0x00, 0x65, 0x20, 0x16,
-	0x00, 0x65, 0x8b, 0x10,
-	0x20, 0xa0, 0x16, 0x1b,
-	0xff, 0x37, 0x64, 0x02,
-	0x00, 0x6a, 0x93, 0x17,
-	0x01, 0x6a, 0x93, 0x00,
-	0xff, 0x6a, 0x99, 0x00,
-	0x0a, 0x93, 0x93, 0x00,
-	0x00, 0x65, 0xd1, 0x17,
-	0xff, 0x4f, 0x64, 0x02,
-	0x01, 0x6a, 0x93, 0x17,
-	0x01, 0x6a, 0x93, 0x00,
-	0xff, 0xb9, 0x99, 0x02,
-	0x0a, 0x93, 0x93, 0x00,
-	0x00, 0x65, 0xd1, 0x17,
-	0x01, 0x4f, 0x4f, 0x06,
-	0x02, 0x6a, 0x91, 0x00,
-	0x00, 0x65, 0xd5, 0x17,
-	0x00, 0x65, 0x8b, 0x10,
-	0x41, 0x6a, 0x91, 0x00,
-	0x00, 0x65, 0x82, 0x10,
-	0x04, 0xa0, 0xa0, 0x00,
-	0x00, 0x65, 0xe1, 0x17,
-	0x00, 0x65, 0x8b, 0x10,
-	0x10, 0x36, 0x00, 0x1f,
-	0xff, 0x38, 0xa3, 0x02,
-	0xa4, 0x6a, 0x66, 0x00,
-	0x39, 0x6a, 0xa5, 0x17,
-	0xac, 0x6a, 0x66, 0x00,
-	0x14, 0x6a, 0xa5, 0x17,
-	0xa9, 0x6a, 0xa6, 0x17,
-	0x00, 0x65, 0x00, 0x11,
-	0xef, 0x36, 0x36, 0x02,
-	0x00, 0x65, 0x00, 0x11,
-	0x0f, 0x64, 0x64, 0x02,
-	0x07, 0x64, 0x64, 0x02,
-	0x00, 0x37, 0x37, 0x00,
-	0x00, 0x65, 0x8b, 0x17,
-	0xff, 0x51, 0x37, 0x1d,
-	0x20, 0x36, 0x3f, 0x1f,
-	0x00, 0x90, 0x7d, 0x17,
-	0x00, 0x65, 0x40, 0x11,
-	0xff, 0x06, 0x6a, 0x02,
-	0x00, 0x65, 0x9b, 0x17,
-	0xe0, 0x3d, 0x4d, 0x19,
-	0x20, 0x12, 0x4d, 0x19,
-	0x51, 0x6a, 0x54, 0x17,
-	0xff, 0x51, 0x90, 0x02,
-	0x20, 0xa0, 0x4d, 0x1f,
-	0x00, 0x90, 0x7d, 0x17,
-	0x00, 0x65, 0x7a, 0x17,
-	0xff, 0x37, 0x64, 0x02,
-	0x00, 0xa1, 0x49, 0x19,
-	0x04, 0xa0, 0x49, 0x1f,
-	0xfb, 0xa0, 0xa0, 0x02,
-	0x80, 0x36, 0x36, 0x00,
-	0x80, 0xa0, 0x00, 0x1f,
-	0x7f, 0xa0, 0xa0, 0x02,
-	0xff, 0x6a, 0x52, 0x17,
-	0x00, 0x65, 0x00, 0x11,
-	0x04, 0xa0, 0x4c, 0x1f,
-	0x00, 0x65, 0xe1, 0x17,
-	0x00, 0x65, 0x4d, 0x11,
-	0x00, 0x65, 0xd5, 0x17,
-	0x31, 0x6a, 0x91, 0x00,
-	0x0c, 0x6a, 0x52, 0x17,
-	0x00, 0x65, 0x00, 0x11,
-	0x61, 0x6a, 0x91, 0x00,
-	0x00, 0x65, 0x00, 0x11,
-	0x10, 0x3d, 0x03, 0x00,
-	0xff, 0x65, 0x34, 0x03,
-	0xff, 0x06, 0x6a, 0x02,
-	0x01, 0x0c, 0x55, 0x1f,
-	0x04, 0x0c, 0x55, 0x1b,
-	0xe0, 0x03, 0x3d, 0x02,
-	0xe0, 0x3d, 0x5f, 0x19,
-	0xff, 0x65, 0x66, 0x02,
-	0xff, 0x12, 0x6d, 0x03,
-	0xff, 0x06, 0x6a, 0x03,
-	0xff, 0x65, 0x06, 0x02,
-	0x02, 0x0b, 0x5d, 0x1f,
-	0xff, 0x6a, 0x6a, 0x03,
-	0xd1, 0x6a, 0x91, 0x00,
-	0x00, 0x65, 0x82, 0x10,
-	0xff, 0x65, 0x93, 0x02,
-	0x01, 0x0b, 0x69, 0x1b,
-	0x10, 0x0c, 0x62, 0x1f,
-	0x04, 0x0b, 0x66, 0x1b,
-	0xff, 0x6a, 0x65, 0x02,
-	0x04, 0x93, 0x68, 0x1b,
-	0x01, 0x94, 0x67, 0x1f,
-	0x10, 0x94, 0x68, 0x1b,
-	0xc7, 0x93, 0x93, 0x02,
-	0x38, 0x93, 0x6a, 0x1b,
-	0xff, 0x6a, 0x6a, 0x03,
-	0x80, 0x36, 0x6b, 0x1b,
-	0x21, 0x6a, 0x91, 0x01,
-	0xff, 0x65, 0x90, 0x02,
-	0xff, 0x51, 0x72, 0x19,
-	0xff, 0x37, 0x64, 0x02,
-	0xa1, 0x6a, 0x77, 0x11,
-	0xff, 0x51, 0x64, 0x02,
-	0xb9, 0x6a, 0x77, 0x11,
-	0xff, 0xba, 0x79, 0x1d,
-	0xff, 0xba, 0x90, 0x02,
-	0xff, 0x65, 0x65, 0x06,
-	0x00, 0x6c, 0x74, 0x19,
-	0xff, 0x90, 0x65, 0x03,
-	0xff, 0x6a, 0x65, 0x01,
-	0x20, 0x36, 0x88, 0x1f,
-	0x00, 0x90, 0x6e, 0x17,
-	0xff, 0x65, 0x88, 0x1d,
-	0xff, 0xba, 0x82, 0x1d,
-	0xff, 0xbb, 0x66, 0x02,
-	0xff, 0xba, 0x90, 0x02,
-	0xff, 0x66, 0xbb, 0x02,
-	0xff, 0x65, 0x90, 0x02,
-	0xff, 0xbb, 0x87, 0x1d,
-	0xff, 0xba, 0x66, 0x02,
-	0xff, 0xbb, 0x90, 0x02,
-	0xff, 0x66, 0xba, 0x02,
-	0xff, 0x65, 0x90, 0x03,
-	0xff, 0xba, 0x3f, 0x03,
-	0x00, 0x6a, 0xd8, 0x17,
-	0x0d, 0x6a, 0x35, 0x00,
-	0x00, 0x51, 0xb3, 0x11,
-	0xff, 0x3f, 0x96, 0x1d,
-	0xff, 0x6a, 0x51, 0x00,
-	0x00, 0x3f, 0x6e, 0x17,
-	0xff, 0x65, 0x96, 0x1d,
-	0x20, 0x36, 0x36, 0x00,
-	0x20, 0xa0, 0x92, 0x1b,
-	0xff, 0xb9, 0x51, 0x03,
-	0xff, 0x6a, 0x51, 0x01,
-	0xff, 0x65, 0x66, 0x02,
-	0x45, 0x6a, 0xab, 0x17,
-	0x01, 0x6a, 0x8c, 0x01,
-	0xff, 0x37, 0x64, 0x02,
-	0x00, 0x6a, 0x93, 0x17,
-	0x0d, 0x6a, 0x93, 0x00,
-	0x00, 0x65, 0xd1, 0x17,
-	0xff, 0x99, 0x51, 0x03,
-	0x01, 0x0c, 0x9b, 0x1f,
-	0x04, 0x0c, 0x9b, 0x1b,
-	0xe0, 0x03, 0x3d, 0x02,
-	0xff, 0x3d, 0x03, 0x03,
-	0xff, 0x8c, 0x08, 0x02,
-	0xff, 0x8d, 0x09, 0x02,
-	0xff, 0x8e, 0x0a, 0x03,
-	0xff, 0x6c, 0x6d, 0x02,
-	0xff, 0x6c, 0x6d, 0x02,
-	0xff, 0x6c, 0x6d, 0x02,
-	0xff, 0x6c, 0x6d, 0x02,
-	0xff, 0x6c, 0x6d, 0x02,
-	0xff, 0x6c, 0x6d, 0x02,
-	0xff, 0x6c, 0x6d, 0x03,
-	0x3d, 0x64, 0x66, 0x0a,
-	0x55, 0x64, 0x64, 0x0a,
-	0x00, 0x6c, 0x88, 0x06,
-	0xff, 0x66, 0x64, 0x02,
-	0x00, 0x6c, 0x89, 0x08,
-	0xff, 0x6a, 0x64, 0x02,
-	0x00, 0x6c, 0x8a, 0x08,
-	0x00, 0x6c, 0x8b, 0x08,
-	0xff, 0x6a, 0x8d, 0x02,
-	0xff, 0x6a, 0x8e, 0x03,
-	0xff, 0x65, 0x64, 0x02,
-	0x41, 0x6a, 0xa9, 0x17,
-	0x1c, 0x6a, 0x8c, 0x00,
-	0xff, 0x35, 0x93, 0x02,
-	0x04, 0x35, 0xc3, 0x1b,
-	0xa0, 0x6a, 0x65, 0x00,
-	0x1c, 0x65, 0x64, 0x06,
-	0xff, 0x6c, 0x99, 0x02,
-	0xff, 0x6c, 0x99, 0x02,
-	0xff, 0x6c, 0x99, 0x02,
-	0xff, 0x6c, 0x99, 0x02,
-	0xff, 0x6c, 0x99, 0x02,
-	0xff, 0x6c, 0x99, 0x02,
-	0xff, 0x6c, 0x99, 0x02,
-	0x00, 0x65, 0xba, 0x19,
-	0x0a, 0x93, 0x93, 0x00,
-	0x00, 0x65, 0xd1, 0x17,
-	0x04, 0x35, 0x6b, 0x1f,
-	0xa0, 0x6a, 0xc9, 0x17,
-	0x00, 0x65, 0xca, 0x17,
-	0x00, 0x65, 0xca, 0x17,
-	0x00, 0x65, 0xca, 0x11,
-	0xff, 0x65, 0x66, 0x02,
-	0xff, 0x99, 0x6d, 0x02,
-	0xff, 0x99, 0x6d, 0x02,
-	0xff, 0x99, 0x6d, 0x02,
-	0xff, 0x99, 0x6d, 0x02,
-	0xff, 0x99, 0x6d, 0x02,
-	0xff, 0x99, 0x6d, 0x02,
-	0xff, 0x99, 0x6d, 0x03,
-	0x08, 0x94, 0xd1, 0x1f,
-	0xf7, 0x93, 0x93, 0x02,
-	0x08, 0x93, 0xd3, 0x1b,
-	0xff, 0x6a, 0x6a, 0x03,
-	0xff, 0x40, 0xba, 0x02,
-	0xff, 0x90, 0x40, 0x02,
-	0xff, 0x6a, 0xb9, 0x01,
-	0xff, 0x40, 0xdf, 0x19,
-	0xff, 0x3f, 0xdb, 0x19,
-	0xff, 0x6a, 0x65, 0x01,
-	0xff, 0x3f, 0x90, 0x02,
-	0x01, 0x6a, 0x35, 0x00,
-	0x00, 0xb9, 0xb3, 0x17,
-	0x00, 0x90, 0x7d, 0x11,
-	0xff, 0x40, 0x90, 0x02,
-	0xff, 0xba, 0x40, 0x03,
-	0xff, 0x6a, 0xbb, 0x00,
-	0xff, 0x3f, 0xba, 0x02,
-	0xff, 0x90, 0x3f, 0x02,
-	0xff, 0xba, 0x6b, 0x1d,
-	0xff, 0xba, 0x90, 0x02,
-	0xff, 0x3f, 0xbb, 0x02,
-	0xff, 0x3f, 0x90, 0x03,
-};
-#define	WIDE            	0x20
-#define	ULTRA           	0x10
-#define	SCB_PAGING      	0x8
-#define	TWIN_CHANNEL    	0x4
-#define	TARGET_MODE     	0x2
-struct sequencer_patch {
-	int	options;
-	int	negative;
-	int	begin;
-	int	end;
-} sequencer_patches[] = {
-	{ 0x00000002, 0, 0x001, 0x002 },
-	{ 0x00000002, 1, 0x002, 0x003 },
-	{ 0x00000004, 0, 0x009, 0x00d },
-	{ 0x00000008, 0, 0x012, 0x013 },
-	{ 0x00000008, 1, 0x018, 0x019 },
-	{ 0x00000004, 0, 0x020, 0x024 },
-	{ 0x00000010, 0, 0x02a, 0x031 },
-	{ 0x00000002, 0, 0x038, 0x071 },
-	{ 0x00000020, 0, 0x0d6, 0x0d7 },
-	{ 0x00000020, 1, 0x0d7, 0x0d8 },
-	{ 0x00000020, 0, 0x12f, 0x130 },
-	{ 0x00000020, 1, 0x130, 0x131 },
-	{ 0x00000008, 0, 0x134, 0x135 },
-	{ 0x00000008, 1, 0x13c, 0x13f },
-	{ 0x00000008, 0, 0x13f, 0x140 },
-	{ 0x00000002, 0, 0x15c, 0x15f },
-	{ 0x00000008, 0, 0x1d5, 0x1d7 },
-	{ 0x00000008, 0, 0x1d8, 0x1e1 },
-	{ 0x00000000, 0, 0x000, 0x000 }
-};
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/amiga7xx.c linux/drivers/scsi/amiga7xx.c
--- v2.1.124/linux/drivers/scsi/amiga7xx.c	Mon Aug  3 12:45:46 1998
+++ linux/drivers/scsi/amiga7xx.c	Mon Oct  5 13:43:31 1998
@@ -50,6 +50,28 @@
 
     tpnt->proc_dir = &proc_scsi_amiga7xx;
 
+#ifdef CONFIG_BLZ603EPLUS_SCSI
+    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);
+
+	zorro_config_board(key, 0);
+	num++;
+    }
+#endif
+
 #ifdef CONFIG_WARPENGINE_SCSI
     if ((key = zorro_find(ZORRO_PROD_MACROSYSTEMS_WARP_ENGINE_40xx, 0, 0)))
     {
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/blz1230.c linux/drivers/scsi/blz1230.c
--- v2.1.124/linux/drivers/scsi/blz1230.c	Tue Jun 23 10:01:23 1998
+++ linux/drivers/scsi/blz1230.c	Mon Oct  5 13:43:31 1998
@@ -131,7 +131,7 @@
 
 		/* Set the command buffer */
 		esp->esp_command = (volatile unsigned char*) cmd_buffer;
-		esp->esp_command_dvma = VTOP((unsigned long) cmd_buffer);
+		esp->esp_command_dvma = virt_to_bus((unsigned long) cmd_buffer);
 
 		esp->irq = IRQ_AMIGA_PORTS;
 		request_irq(IRQ_AMIGA_PORTS, esp_intr, 0, 
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/blz2060.c linux/drivers/scsi/blz2060.c
--- v2.1.124/linux/drivers/scsi/blz2060.c	Tue Jun 23 10:01:23 1998
+++ linux/drivers/scsi/blz2060.c	Mon Oct  5 13:43:31 1998
@@ -107,7 +107,7 @@
 		
 		/* Set the command buffer */
 		esp->esp_command = (volatile unsigned char*) cmd_buffer;
-		esp->esp_command_dvma = VTOP((unsigned long) cmd_buffer);
+		esp->esp_command_dvma = virt_to_bus((unsigned long) cmd_buffer);
 
 		esp->irq = IRQ_AMIGA_PORTS;
 		request_irq(IRQ_AMIGA_PORTS, esp_intr, 0, 
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/cyberstorm.c linux/drivers/scsi/cyberstorm.c
--- v2.1.124/linux/drivers/scsi/cyberstorm.c	Tue Jun 23 10:01:23 1998
+++ linux/drivers/scsi/cyberstorm.c	Mon Oct  5 13:43:31 1998
@@ -127,7 +127,7 @@
 		
 		/* Set the command buffer */
 		esp->esp_command = (volatile unsigned char*) cmd_buffer;
-		esp->esp_command_dvma = VTOP((unsigned long) cmd_buffer);
+		esp->esp_command_dvma = virt_to_bus((unsigned long) cmd_buffer);
 
 		esp->irq = IRQ_AMIGA_PORTS;
 		request_irq(IRQ_AMIGA_PORTS, esp_intr, 0, 
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/cyberstormII.c linux/drivers/scsi/cyberstormII.c
--- v2.1.124/linux/drivers/scsi/cyberstormII.c	Tue Jun 23 10:01:23 1998
+++ linux/drivers/scsi/cyberstormII.c	Mon Oct  5 13:43:31 1998
@@ -118,7 +118,7 @@
 		
 		/* Set the command buffer */
 		esp->esp_command = (volatile unsigned char*) cmd_buffer;
-		esp->esp_command_dvma = VTOP((unsigned long) cmd_buffer);
+		esp->esp_command_dvma = virt_to_bus((unsigned long) cmd_buffer);
 
 		esp->irq = IRQ_AMIGA_PORTS;
 		request_irq(IRQ_AMIGA_PORTS, esp_intr, 0, 
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/fastlane.c linux/drivers/scsi/fastlane.c
--- v2.1.124/linux/drivers/scsi/fastlane.c	Mon Aug  3 12:45:46 1998
+++ linux/drivers/scsi/fastlane.c	Mon Oct  5 13:43:31 1998
@@ -163,7 +163,7 @@
 		
 		/* Set the command buffer */
 		esp->esp_command = (volatile unsigned char*) cmd_buffer;
-		esp->esp_command_dvma = VTOP((unsigned long) cmd_buffer);
+		esp->esp_command_dvma = virt_to_bus((unsigned long) cmd_buffer);
 
 		esp->irq = IRQ_AMIGA_PORTS;
 		request_irq(IRQ_AMIGA_PORTS, esp_intr, 0, 
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v2.1.124/linux/drivers/scsi/hosts.c	Thu Sep 17 17:53:36 1998
+++ linux/drivers/scsi/hosts.c	Mon Oct  5 13:47:23 1998
@@ -41,7 +41,15 @@
 
 #include "hosts.h"
 
-#if defined(CONFIG_A4000T_SCSI) || defined(CONFIG_WARPENGINE_SCSI) || defined(CONFIG_A4091_SCSI) || defined (CONFIG_GVP_TURBO_SCSI)
+#if defined(CONFIG_A4000T_SCSI) || \
+    defined(CONFIG_WARPENGINE_SCSI) || \
+    defined(CONFIG_A4091_SCSI) || \
+    defined (CONFIG_GVP_TURBO_SCSI) || \
+    defined (CONFIG_BLZ603E)
+#define AMIGA7XXCONFIG
+#endif
+
+#ifdef AMIGA7XXCONFIG
 #include "amiga7xx.h"
 #endif
 
@@ -317,7 +325,7 @@
 static Scsi_Host_Template builtin_scsi_hosts[] =
 {
 #ifdef CONFIG_AMIGA
-#if defined(CONFIG_WARPENGINE_SCSI) || defined(CONFIG_A4000T_SCSI) || defined(CONFIG_A4091_SCSI) || defined (CONFIG_GVP_TURBO_SCSI)
+#ifdef AMIGA7XXCONFIG
 	AMIGA7XX_SCSI,
 #endif
 #ifdef CONFIG_A3000_SCSI
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- v2.1.124/linux/drivers/scsi/ncr53c8xx.c	Wed Sep  9 14:51:08 1998
+++ linux/drivers/scsi/ncr53c8xx.c	Mon Oct  5 13:40:23 1998
@@ -73,7 +73,7 @@
 */
 
 /*
-**	16 July 1998, version 3.0g
+**	October 4 1998, version 3.0i
 **
 **	Supported SCSI-II features:
 **	    Synchronous negotiation
@@ -584,6 +584,7 @@
 #endif
 
 #ifdef __sparc__
+#include <asm/irq.h>
 #define remap_pci_mem(base, size)	((vm_offset_t) __va(base))
 #define unmap_pci_mem(vaddr, size)
 #define pcivtophys(p)			((p) & pci_dvma_mask)
@@ -2011,9 +2012,9 @@
 	ncrcmd	start		[  5];
 	ncrcmd  startpos	[  1];
 	ncrcmd	select		[  6];
-	ncrcmd	select2		[  7];
+	ncrcmd	select2		[  9];
 	ncrcmd	loadpos		[  4];
-	ncrcmd	send_ident	[  7];
+	ncrcmd	send_ident	[  9];
 	ncrcmd	prepare		[  6];
 	ncrcmd	prepare2	[  7];
 #ifdef SCSI_NCR_PROFILE_SUPPORT
@@ -2340,6 +2341,15 @@
 	*/
 
 	/*
+	**	The M_REJECT problem seems to be due to a selection 
+	**	timing problem.
+	**	Wait immediately for the selection to complete. 
+	**	(2.5x behaves so)
+	*/
+	SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+		0,
+
+	/*
 	**	Next time use the next slot.
 	*/
 	SCR_COPY (4),
@@ -2384,8 +2394,10 @@
 		offsetof (struct dsb, smsg),
 	SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
 		PADDRH (resend_ident),
+	SCR_LOAD_REG (scratcha, 0x80),
+		0,
 	SCR_COPY (1),
-		RADDR (sfbr),
+		RADDR (scratcha),
 		NADDR (lastmsg),
 }/*-------------------------< PREPARE >----------------------*/,{
 	/*
@@ -2818,7 +2830,7 @@
 	SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
 		NADDR (msgout),
 	SCR_COPY (1),
-		RADDR (sfbr),
+		NADDR (msgout),
 		NADDR (lastmsg),
 	/*
 	**	If it was no ABORT message ...
@@ -3343,7 +3355,7 @@
 	SCR_MOVE_ABS (4) ^ SCR_MSG_OUT,
 		NADDR (msgout),
 	SCR_COPY (1),
-		RADDR (sfbr),
+		NADDR (msgout),
 		NADDR (lastmsg),
 	SCR_JUMP,
 		PADDR (msg_out_done),
@@ -3399,7 +3411,7 @@
 	SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
 		NADDR (msgout),
 	SCR_COPY (1),
-		RADDR (sfbr),
+		NADDR (msgout),
 		NADDR (lastmsg),
 	SCR_JUMP,
 		PADDR (msg_out_done),
@@ -3504,7 +3516,7 @@
 	SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
 		NADDR (msgout),
 	SCR_COPY (1),
-		RADDR (sfbr),
+		NADDR (msgout),
 		NADDR (lastmsg),
 	SCR_CLR (SCR_ACK|SCR_ATN),
 		0,
@@ -4492,9 +4504,9 @@
 	int i;
 
 #ifdef __sparc__
-printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=0x%x\n",
+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, device->slot.irq);
+	device->slot.io_port, __irq_itoa(device->slot.irq));
 #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,
@@ -4696,15 +4708,25 @@
 #ifdef	SCSI_NCR_SHARE_IRQ
 #define	NCR_SA_INTERRUPT_FLAGS (SA_INTERRUPT | SA_SHIRQ)
 	if (bootverbose > 1)
+#ifdef __sparc__
+		printk(KERN_INFO "%s: requesting shared irq %s (dev_id=0x%lx)\n",
+		        ncr_name(np), __irq_itoa(device->slot.irq), (u_long) np);
+#else
 		printk(KERN_INFO "%s: requesting shared irq %d (dev_id=0x%lx)\n",
 		        ncr_name(np), device->slot.irq, (u_long) np);
+#endif
 #else
 #define	NCR_SA_INTERRUPT_FLAGS SA_INTERRUPT
 #endif
 	if (request_irq(device->slot.irq, ncr53c8xx_intr,
 			NCR_SA_INTERRUPT_FLAGS, "ncr53c8xx", np)) {
+#ifdef __sparc__
+		printk(KERN_ERR "%s: request irq %s failure\n",
+			ncr_name(np), __irq_itoa(device->slot.irq));
+#else
 		printk(KERN_ERR "%s: request irq %d failure\n",
 			ncr_name(np), device->slot.irq);
+#endif
 		goto attach_error;
 	}
 	np->irq = device->slot.irq;
@@ -4794,7 +4816,8 @@
 	if (np->irq) {
 #ifdef DEBUG_NCR53C8XX
 #ifdef __sparc__
-	printk(KERN_INFO "%s: freeing irq 0x%x\n", ncr_name(np), np->irq);
+	printk(KERN_INFO "%s: freeing irq %s\n", ncr_name(np),
+	       __irq_itoa(np->irq));
 #else
 	printk(KERN_INFO "%s: freeing irq %d\n", ncr_name(np), np->irq);
 #endif
@@ -4951,7 +4974,7 @@
 
 	nego = 0;
 
-	if ((!tp->widedone || !tp->period) && !tp->nego_cp && tp->inq_done) {
+	if ((!tp->widedone || !tp->period) && !tp->nego_cp && tp->inq_done && lp) {
 
 		/*
 		**	negotiate wide transfers ?
@@ -5610,8 +5633,12 @@
 */
 
 #ifdef DEBUG_NCR53C8XX
+#ifdef __sparc__
+	printk("%s: freeing irq %s\n", ncr_name(np), __irq_itoa(np->irq));
+#else
 	printk("%s: freeing irq %d\n", ncr_name(np), np->irq);
 #endif
+#endif
 	free_irq(np->irq, np);
 
 	/*
@@ -5866,7 +5893,8 @@
 		if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) {
 			u_char * p = (u_char*) & cmd->sense_buffer;
 			int i;
-			printk ("\n%s: sense data:", ncr_name (np));
+			PRINT_ADDR(cmd);
+			printk ("sense data:");
 			for (i=0; i<14; i++) printk (" %x", *p++);
 			printk (".\n");
 		}
@@ -9792,7 +9820,7 @@
 	}
 
 	if ((chip->features & FE_CLSE) && !cache_line_size) {
-		cache_line_size = 16;
+		cache_line_size = CACHE_LINE_SIZE;
 		if (initverbose >= 2)
 			printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fixup)\n", cache_line_size);
 		pcibios_write_config_byte(bus, device_fn,
@@ -10715,7 +10743,11 @@
 	copy_info(&info, "revision id 0x%x\n",	np->revision_id);
 
 	copy_info(&info, "  IO port address 0x%lx, ", (u_long) np->port);
+#ifdef __sparc__
+	copy_info(&info, "IRQ number %s\n", __irq_itoa(np->irq));
+#else
 	copy_info(&info, "IRQ number %d\n", (int) np->irq);
+#endif
 
 #ifndef NCR_IOMAPPED
 	if (np->reg)
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h
--- v2.1.124/linux/drivers/scsi/ncr53c8xx.h	Fri Jul 31 17:07:58 1998
+++ linux/drivers/scsi/ncr53c8xx.h	Thu Oct  8 21:32:26 1998
@@ -45,7 +45,7 @@
 /*
 **	Name and revision of the driver
 */
-#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 3.0g"
+#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 3.0i"
 
 /*
 **	Check supported Linux versions
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.1.124/linux/drivers/scsi/scsi.c	Thu Sep 17 17:53:36 1998
+++ linux/drivers/scsi/scsi.c	Thu Oct  8 08:07:34 1998
@@ -1476,6 +1476,7 @@
 
     SCpnt->internal_timeout = NORMAL_TIMEOUT;
     SCpnt->abort_reason = 0;
+    SCpnt->result = 0;
     internal_cmnd (SCpnt);
 
     SCSI_LOG_MLQUEUE(3,printk ("Leaving scsi_do_cmd()\n"));
@@ -1704,6 +1705,8 @@
   SCpnt->request_bufflen = SCpnt->bufflen;
   SCpnt->use_sg = SCpnt->old_use_sg;
   SCpnt->cmd_len = SCpnt->old_cmd_len;
+  SCpnt->result = 0;
+  memset ((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer);
   return internal_cmnd (SCpnt);
 }
 
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/scsi_obsolete.c linux/drivers/scsi/scsi_obsolete.c
--- v2.1.124/linux/drivers/scsi/scsi_obsolete.c	Wed Jul  1 19:38:55 1998
+++ linux/drivers/scsi/scsi_obsolete.c	Thu Oct  8 08:07:34 1998
@@ -219,6 +219,8 @@
 
     memcpy ((void *) SCpnt->cmnd , (void *) generic_sense,
 	    sizeof(generic_sense));
+    memset ((void *) SCpnt->sense_buffer, 0,
+            sizeof(SCpnt->sense_buffer));
 
     SCpnt->cmnd[1] = SCpnt->lun << 5;
     SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer);
@@ -227,6 +229,7 @@
     SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer);
     SCpnt->use_sg = 0;
     SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
+    SCpnt->result = 0;
     internal_cmnd (SCpnt);
 }
 
@@ -375,7 +378,8 @@
 		       SCpnt->host->host_no, SCpnt->channel, SCpnt->target,
 		       SCpnt->lun);
 		scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
-		return;
+		status = REDO;
+                break;
 	    }
 	    else
 	    {
@@ -491,12 +495,8 @@
 		printk("scsi%d, channel %d : RESERVATION CONFLICT performing"
 		       " reset.\n", SCpnt->host->host_no, SCpnt->channel);
 		scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
-		return;
-#if 0
-		exit = DRIVER_SOFT | SUGGEST_ABORT;
-		status = MAYREDO;
+		status = REDO;
 		break;
-#endif
 	    default:
 		printk ("Internal error %s %d \n"
 			"status byte = %d \n", __FILE__,
@@ -634,10 +634,13 @@
 	    memcpy ((void *) SCpnt->cmnd,
 		    (void*) SCpnt->data_cmnd,
 		    sizeof(SCpnt->data_cmnd));
+            memset ((void *) SCpnt->sense_buffer, 0,
+                    sizeof(SCpnt->sense_buffer));
 	    SCpnt->request_buffer = SCpnt->buffer;
 	    SCpnt->request_bufflen = SCpnt->bufflen;
 	    SCpnt->use_sg = SCpnt->old_use_sg;
 	    SCpnt->cmd_len = SCpnt->old_cmd_len;
+            SCpnt->result = 0;
 	    internal_cmnd (SCpnt);
 	}
 	break;
diff -u --recursive --new-file v2.1.124/linux/drivers/scsi/scsicam.c linux/drivers/scsi/scsicam.c
--- v2.1.124/linux/drivers/scsi/scsicam.c	Sun Dec 21 17:04:49 1997
+++ linux/drivers/scsi/scsicam.c	Tue Oct  6 08:56:36 1998
@@ -114,6 +114,8 @@
     	end_head = largest->end_head;
     	end_sector = largest->end_sector & 0x3f;
 
+        if( end_head + 1 == 0 || end_sector == 0 ) return -1;
+
 #ifdef DEBUG
 	printk ("scsicam_bios_param : end at h = %d, c = %d, s = %d\n",
 	    end_head, end_cyl, end_sector);
diff -u --recursive --new-file v2.1.124/linux/drivers/video/amifb.c linux/drivers/video/amifb.c
--- v2.1.124/linux/drivers/video/amifb.c	Mon Oct  5 13:13:41 1998
+++ linux/drivers/video/amifb.c	Mon Oct  5 12:24:34 1998
@@ -61,6 +61,7 @@
 #include <asm/amigaints.h>
 #include <asm/setup.h>
 
+#include <video/fbcon.h>
 #include <video/fbcon-afb.h>
 #include <video/fbcon-ilbm.h>
 #include <video/fbcon-mfb.h>
diff -u --recursive --new-file v2.1.124/linux/drivers/video/atafb.c linux/drivers/video/atafb.c
--- v2.1.124/linux/drivers/video/atafb.c	Mon Oct  5 13:13:41 1998
+++ linux/drivers/video/atafb.c	Mon Oct  5 13:25:46 1998
@@ -1636,9 +1636,9 @@
 	 * Even with hicolor r/g/b=5/6/5 bit!
 	 */
 	col = f030_col[regno];
-	*red = (col >> 26) & 0x3f;
-	*green = (col >> 18) & 0x3f;
-	*blue = (col >> 2) & 0x3f;
+	*red = (col >> 16) & 0xff00;
+	*green = (col >> 8) & 0xff00;
+	*blue = (col << 8) & 0xff00;
 	*transp = 0;
 	return 0;
 }
@@ -1650,14 +1650,18 @@
 {
 	if (regno > 255)
 		return 1;
-	f030_col[regno] = (red << 26) | (green << 18) | (blue << 2);
+	f030_col[regno] = (((red & 0xfc00) << 16) |
+			   ((green & 0xfc00) << 8) |
+			   ((blue & 0xfc00) >> 8));
 	if (regno < 16) {
 		shifter_tt.color_reg[regno] =
-			(((red & 0xe) >> 1) | ((red & 1) << 3) << 8) |
-			(((green & 0xe) >> 1) | ((green & 1) << 3) << 4) |
-			((blue & 0xe) >> 1) | ((blue & 1) << 3);
+			(((red & 0xe000) >> 13) | ((red & 0x1000) >> 12) << 8) |
+			(((green & 0xe000) >> 13) | ((green & 0x1000) >> 12) << 4) |
+			((blue & 0xe000) >> 13) | ((blue & 0x1000) >> 12);
 #ifdef FBCON_HAS_CFB16
-		fbcon_cfb16_cmap[regno] = (red << 11) | (green << 5) | blue;
+		fbcon_cfb16_cmap[regno] = ((red & 0xf800) |
+					   ((green & 0xfc00) >> 5) |
+					   ((blue & 0xf800) >> 11));
 #endif
 	}
 	return 0;
diff -u --recursive --new-file v2.1.124/linux/drivers/video/fbcon.c linux/drivers/video/fbcon.c
--- v2.1.124/linux/drivers/video/fbcon.c	Mon Oct  5 13:13:41 1998
+++ linux/drivers/video/fbcon.c	Wed Oct  7 09:22:08 1998
@@ -1251,6 +1251,7 @@
 	    for (j = 0; j < fontheight(p); j++)
 		*data++ = *fontdata++;
 	    memset(data, 0, 32-j);
+	    data += 32 - j;
 	}
     }
 #ifndef CONFIG_FBCON_FONTWIDTH8_ONLY
@@ -1262,6 +1263,7 @@
 		fontdata += sizeof(u16);
 	    }
 	    memset(data, 0, 2*(32-j));
+	    data += 2 * (32 - j);
 	}
     } else if (op->width <= 24) {
 	for (i = 0; i < op->charcount; i++) {
@@ -1272,6 +1274,7 @@
 		fontdata += sizeof(u32);
 	    }
 	    memset(data, 0, 3*(32-j));
+	    data += 3 * (32 - j);
 	}
     } else {
 	for (i = 0; i < op->charcount; i++) {
@@ -1283,6 +1286,7 @@
 		fontdata += sizeof(u32);
 	    }
 	    memset(data, 0, 4*(32-j));
+	    data += 4 * (32 - j);
 	}
     }
 #endif
diff -u --recursive --new-file v2.1.124/linux/drivers/video/mdacon.c linux/drivers/video/mdacon.c
--- v2.1.124/linux/drivers/video/mdacon.c	Mon Oct  5 13:13:42 1998
+++ linux/drivers/video/mdacon.c	Mon Oct  5 12:28:08 1998
@@ -19,7 +19,6 @@
  *  more details.
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/fs.h>
diff -u --recursive --new-file v2.1.124/linux/drivers/video/offb.c linux/drivers/video/offb.c
--- v2.1.124/linux/drivers/video/offb.c	Mon Oct  5 13:13:42 1998
+++ linux/drivers/video/offb.c	Mon Oct  5 12:21:13 1998
@@ -35,6 +35,8 @@
 
 #include <video/fbcon.h>
 #include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb32.h>
 #include <video/macmodes.h>
 
 
@@ -48,6 +50,14 @@
     struct { u_char red, green, blue, pad; } palette[256];
     volatile unsigned char *cmap_adr;
     volatile unsigned char *cmap_data;
+    union {
+#ifdef FBCON_HAS_CFB16
+	u16 cfb16[16];
+#endif
+#ifdef FBCON_HAS_CFB32
+	u32 cfb32[16];
+#endif
+    } fbcon_cmap;
 };
 
 #ifdef __powerpc__
@@ -205,7 +215,7 @@
 	oldbpp = display->var.bits_per_pixel;
 	display->var = *var;
     }
-    if (oldbpp != var->bits_per_pixel) {
+    if ((oldbpp != var->bits_per_pixel) || (display->cmap.len == 0)) {
 	if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
 	    return err;
 	do_install_cmap(con, info);
@@ -476,7 +486,7 @@
 
     printk(KERN_INFO "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n",
 	   width, height, name, address, depth, pitch);
-    if (depth != 8) {
+    if (depth != 8 && depth != 16 && depth != 32) {
 	printk("%s: can't use depth = %d\n", full_name, depth);
 	return;
     }
@@ -509,16 +519,51 @@
 	info->cmap_data = info->cmap_adr + 1;
     }
 
-    fix->visual = info->cmap_adr ? FB_VISUAL_PSEUDOCOLOR :
-	FB_VISUAL_STATIC_PSEUDOCOLOR;
+    if (depth == 8)
+	fix->visual = info->cmap_adr ? FB_VISUAL_PSEUDOCOLOR
+				     : FB_VISUAL_STATIC_PSEUDOCOLOR;
+    else
+	fix->visual = FB_VISUAL_TRUECOLOR;
 
     var->xoffset = var->yoffset = 0;
-    var->bits_per_pixel = 8;
+    var->bits_per_pixel = depth;
+    switch (depth) {
+	case 8:
+	    var->bits_per_pixel = 8;
+	    var->red.offset = 0;
+	    var->red.length = 8;
+	    var->green.offset = 0;
+	    var->green.length = 8;
+	    var->blue.offset = 0;
+	    var->blue.length = 8;
+	    var->transp.offset = 0;
+	    var->transp.length = 0;
+	    break;
+	case 16:	/* RGB 555 */
+	    var->bits_per_pixel = 16;
+	    var->red.offset = 10;
+	    var->red.length = 5;
+	    var->green.offset = 5;
+	    var->green.length = 5;
+	    var->blue.offset = 0;
+	    var->blue.length = 5;
+	    var->transp.offset = 0;
+	    var->transp.length = 0;
+	    break;
+	case 32:	/* RGB 888 */
+	    var->bits_per_pixel = 32;
+	    var->red.offset = 16;
+	    var->red.length = 8;
+	    var->green.offset = 8;
+	    var->green.length = 8;
+	    var->blue.offset = 0;
+	    var->blue.length = 8;
+	    var->transp.offset = 24;
+	    var->transp.length = 8;
+	    break;
+    }
+    var->red.msb_right = var->green.msb_right = var->blue.msb_right = var->transp.msb_right = 0;
     var->grayscale = 0;
-    var->red.offset = var->green.offset = var->blue.offset = 0;
-    var->red.length = var->green.length = var->blue.length = 8;
-    var->red.msb_right = var->green.msb_right = var->blue.msb_right = 0;
-    var->transp.offset = var->transp.length = var->transp.msb_right = 0;
     var->nonstd = 0;
     var->activate = 0;
     var->height = var->width = -1;
@@ -545,11 +590,37 @@
     disp->line_length = fix->line_length;
     disp->can_soft_blank = info->cmap_adr ? 1 : 0;
     disp->inverse = 0;
+    switch (depth) {
 #ifdef FBCON_HAS_CFB8
-    disp->dispsw = &fbcon_cfb8;
-#else
-    disp->dispsw = &fbcon_dummy;
+        case 8:
+            disp->dispsw = &fbcon_cfb8;
+            break;
 #endif
+#ifdef FBCON_HAS_CFB16
+        case 16:
+            disp->dispsw = &fbcon_cfb16;
+            disp->dispsw_data = info->fbcon_cmap.cfb16;
+            for (i = 0; i < 16; i++)
+		info->fbcon_cmap.cfb16[i] =
+		    (((default_blu[i] >> 3) & 0x1f) << 10) |
+		    (((default_grn[i] >> 3) & 0x1f) << 5) |
+		    ((default_red[i] >> 3) & 0x1f);
+            break;
+#endif
+#ifdef FBCON_HAS_CFB32
+        case 32:
+            disp->dispsw = &fbcon_cfb32;
+            disp->dispsw_data = info->fbcon_cmap.cfb32;
+            for (i = 0; i < 16; i++)
+		info->fbcon_cmap.cfb32[i] = (default_blu[i] << 16) |
+					    (default_grn[i] << 8) |
+					    default_red[i];
+            break;
+#endif
+        default:
+            disp->dispsw = &fbcon_dummy;
+    }
+
     disp->scrollmode = SCROLL_YREDRAW;
 
     strcpy(info->info.modename, "OFfb ");
@@ -584,7 +655,7 @@
     if (!console_fb_info) {
 	display_info.height = var->yres;
 	display_info.width = var->xres;
-	display_info.depth = 8;
+	display_info.depth = depth;
 	display_info.pitch = fix->line_length;
 	display_info.mode = 0;
 	strncpy(display_info.name, name, sizeof(display_info.name));
@@ -696,9 +767,11 @@
 			 u_int transp, struct fb_info *info)
 {
     struct fb_info_offb *info2 = (struct fb_info_offb *)info;
-
+    int i;
+	
     if (!info2->cmap_adr || regno > 255)
 	return 1;
+
     red >>= 8;
     green >>= 8;
     blue >>= 8;
@@ -706,6 +779,7 @@
     info2->palette[regno].red = red;
     info2->palette[regno].green = green;
     info2->palette[regno].blue = blue;
+
     *info2->cmap_adr = regno;
     mach_eieio();
     *info2->cmap_data = red;
@@ -714,6 +788,23 @@
     mach_eieio();
     *info2->cmap_data = blue;
     mach_eieio();
+
+    if (regno < 16)
+	switch (info2->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB16
+	    case 16:
+		info2->fbcon_cmap.cfb16[regno] = (regno << 10) | (regno << 5) |
+						 regno;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	    case 32:
+		i = (regno << 8) | regno;
+		info2->fbcon_cmap.cfb32[regno] = (i << 16) | i;
+		break;
+#endif
+       }
+
     return 0;
 }
 
@@ -752,14 +843,14 @@
     switch (mode->depth) {
 	case 8:
 	case 0:		/* default */
-	    cmode = 0;	/* CMODE_8 */
+	    cmode = CMODE_8;
 	    break;
 	case 16:
-	    cmode = 1;	/* CMODE_16 */
+	    cmode = CMODE_16;
 	    break;
 	case 24:
 	case 32:
-	    cmode = 2;	/* CMODE_32 */
+	    cmode = CMODE_32;
 	    break;
 	default:
 	    return -EINVAL;
diff -u --recursive --new-file v2.1.124/linux/drivers/video/vesafb.c linux/drivers/video/vesafb.c
--- v2.1.124/linux/drivers/video/vesafb.c	Mon Oct  5 13:13:42 1998
+++ linux/drivers/video/vesafb.c	Thu Oct  8 14:10:28 1998
@@ -50,6 +50,7 @@
 int  video_bpp;
 int  video_width;
 int  video_height;
+int  video_height_virtual;
 int  video_type = FB_TYPE_PACKED_PIXELS;
 int  video_visual;
 int  video_linelength;
@@ -249,9 +250,38 @@
 }
 
 static int vesafb_set_var(struct fb_var_screeninfo *var, int con,
-			 struct fb_info *info)
+			  struct fb_info *info)
 {
-	memcpy(var, &vesafb_defined, sizeof(struct fb_var_screeninfo));
+
+	if (var->xres           != vesafb_defined.xres           ||
+	    var->yres           != vesafb_defined.yres           ||
+	    var->xres_virtual   != vesafb_defined.xres_virtual   ||
+	    var->yres_virtual   >  video_height_virtual          ||
+	    var->yres_virtual   <  video_height                  ||
+	    var->xoffset                                         ||
+	    var->bits_per_pixel != vesafb_defined.bits_per_pixel ||
+	    var->nonstd)
+		return -EINVAL;
+
+	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST)
+		return 0;
+
+	if (ypan || ywrap) {
+		if (vesafb_defined.yres_virtual != var->yres_virtual) {
+			vesafb_defined.yres_virtual = var->yres_virtual;
+			if (con != -1) {
+				fb_display[con].var = vesafb_defined;
+				info->changevar(con);
+			}
+		}
+
+		if (var->yoffset != vesafb_defined.yoffset)
+			return vesafb_pan_display(var,con,info);
+		return 0;
+	}
+
+	if (var->yoffset)
+		return -EINVAL;
 	return 0;
 }
 
@@ -550,6 +580,16 @@
 		vesafb_defined.yres_virtual = video_height;
 		ypan = ywrap = 0;
 	}
+	video_height_virtual = vesafb_defined.yres_virtual;
+
+	/* some dummy values for timing to make fbset happy */
+	vesafb_defined.pixclock     = 10000000 / video_width * 1000 / video_height;
+	vesafb_defined.left_margin  = (video_width / 8) & 0xf8;
+	vesafb_defined.right_margin = 32;
+	vesafb_defined.upper_margin = 16;
+	vesafb_defined.lower_margin = 4;
+	vesafb_defined.hsync_len    = (video_width / 8) & 0xf8;
+	vesafb_defined.vsync_len    = 4;
 
 	if (video_bpp > 8) {
 		vesafb_defined.red.offset    = screen_info.red_pos;
diff -u --recursive --new-file v2.1.124/linux/drivers/zorro/zorro.c linux/drivers/zorro/zorro.c
--- v2.1.124/linux/drivers/zorro/zorro.c	Mon Aug  3 12:45:47 1998
+++ linux/drivers/zorro/zorro.c	Mon Oct  5 13:48:05 1998
@@ -10,7 +10,7 @@
  *    for more details.
  */
 
-
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -185,8 +185,7 @@
 
 __initfunc(void zorro_init(void))
 {
-    u_int i, j;
-    u32 disabled_z2mem = 0;
+    u_int i;
 
     if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(ZORRO)) {
 	printk("Zorro: No Zorro bus detected\n");
@@ -205,25 +204,10 @@
 
     /* Unmark all used Zorro II memory */
     for (i = 0; i < m68k_num_memory; i++)
-	if (m68k_memory[i].addr < 16*1024*1024) {
-	    if (AMIGAHW_PRESENT(ZORRO3)) {
-		/* don't use Zorro II RAM as system memory on Zorro III */
-		/* capable machines */
-		if (i == 0) {
-		    /* don't cut off the branch we're sitting on */
-		    printk("Warning: kernel runs in Zorro II memory\n");
-		} else {
-		    disabled_z2mem += m68k_memory[i].size;
-		    m68k_num_memory--;
-		    for (j = i; j < m68k_num_memory; j++)
-			m68k_memory[j] = m68k_memory[j+1];
-		    i--;
-		    continue;
-		}
-	    }
+	if (m68k_memory[i].addr < 16*1024*1024)
 	    mark_region(m68k_memory[i].addr, m68k_memory[i].size, 0);
-	}
-    if (disabled_z2mem)
-	printk("%dK of Zorro II memory will not be used as system memory\n",
-	       disabled_z2mem>>10);
+
+#ifdef CONFIG_PROC_FS
+    zorro_proc_init();
+#endif
 }
diff -u --recursive --new-file v2.1.124/linux/fs/ext2/truncate.c linux/fs/ext2/truncate.c
--- v2.1.124/linux/fs/ext2/truncate.c	Wed Jun 24 22:54:08 1998
+++ linux/fs/ext2/truncate.c	Mon Oct  5 10:04:34 1998
@@ -62,8 +62,6 @@
 #define TINDIRECT_BLOCK(inode,offset) \
 	(INDIRECT_BLOCK(inode,offset) / (addr_per_block*addr_per_block))
 
-static u32 le32_zero = cpu_to_le32(0);
-
 /*
  * Truncate has the most races in the whole filesystem: coding it is
  * a pain in the a**. Especially as I don't do any locking...
@@ -128,18 +126,16 @@
 	} while (retry);
 
 	for (i = 0; i < addr_per_block; i++)
-		if (le32_to_cpu(*(ind++)))
+		if (*(ind++))
 			goto in_use;
 
 	if (bh->b_count == 1) {
 		int tmp;
-		if (ind_bh) {
+		if (ind_bh)
 			tmp = le32_to_cpu(*p);
-			*p = le32_zero;
-		} else {
+		else
 			tmp = *p;
-			*p = 0;
-		}
+		*p = 0;
 		inode->i_blocks -= (inode->i_sb->s_blocksize / 512);
 		mark_inode_dirty(inode);
 		/*
@@ -232,13 +228,11 @@
 		ext2_error(inode->i_sb, "trunc_indirect",
 			"Read failure, inode=%ld, block=%d",
 			inode->i_ino, tmp);
-		if (dind_bh) {
-			*p = le32_zero;
+		*p = 0;
+		if (dind_bh)
 			mark_buffer_dirty(dind_bh, 1);
-		} else {
-			*p = 0;
+		else
 			mark_inode_dirty(inode);
-		}
 		return 0;
 	}
 
@@ -268,7 +262,7 @@
 			}
 		}
 
-		*ind = le32_zero;
+		*ind = 0;
 		inode->i_blocks -= blocks;
 		mark_inode_dirty(inode);
 		bforget(bh);
@@ -316,13 +310,11 @@
 		ext2_error(inode->i_sb, "trunc_dindirect",
 			"Read failure, inode=%ld, block=%d",
 			inode->i_ino, tmp);
-		if (tind_bh) {
-			*p = le32_zero;
+		*p = 0;
+		if (tind_bh)
 			mark_buffer_dirty(tind_bh, 1);
-		} else {
-			*p = 0;
+		else
 			mark_inode_dirty(inode);
-		}
 		return 0;
 	}
 
diff -u --recursive --new-file v2.1.124/linux/fs/hfs/ChangeLog linux/fs/hfs/ChangeLog
--- v2.1.124/linux/fs/hfs/ChangeLog	Thu Sep 17 17:53:37 1998
+++ linux/fs/hfs/ChangeLog	Wed Oct  7 15:52:55 1998
@@ -123,7 +123,7 @@
 	* dir.c
 	changed mark_inodes_deleted to dget/d_delete/dput the dentry
 	instead of just dropping it. the bytes available should now
-	be updated updated properly upon deletion.
+	be updated properly upon deletion.
 
 Wed Dec 10 00:01:25 1997  a sun  <asun@zoology.washington.edu>
 
@@ -629,7 +629,7 @@
 
 	* file.c, file_cap.c, hfs_fs.h:
 	Rename fix_perms() to hfs_file_fix_mode() and
-	move it from from file_cap.c to file.c.
+	move it from file_cap.c to file.c.
 
 	* README.sgml, super.c:
 	Make the default for the names mount option vary with the fork option.
diff -u --recursive --new-file v2.1.124/linux/fs/hfs/HFS.txt linux/fs/hfs/HFS.txt
--- v2.1.124/linux/fs/hfs/HFS.txt	Sun Jan  4 10:40:17 1998
+++ linux/fs/hfs/HFS.txt	Wed Oct  7 15:52:55 1998
@@ -273,7 +273,7 @@
         header files.  Their format is described briefly in the section
         ``A Guide to Special File Formats'' in this document.  They are
         documented in detail in ``AppleSingle/AppleDouble Formats:
-        Developer's Note (9/94)'', available from from Apple's Developer
+        Developer's Note (9/94)'', available from Apple's Developer
         Services Page <http://devworld.apple.com>.
 
         Note that the naming convention for the header file can cause
diff -u --recursive --new-file v2.1.124/linux/fs/hpfs/hpfs_fs.c linux/fs/hpfs/hpfs_fs.c
--- v2.1.124/linux/fs/hpfs/hpfs_fs.c	Sat Sep  5 16:46:41 1998
+++ linux/fs/hpfs/hpfs_fs.c	Mon Oct  5 10:04:34 1998
@@ -340,7 +340,7 @@
 	struct hpfs_boot_block *bootblock;
 	struct hpfs_super_block *superblock;
 	struct hpfs_spare_block *spareblock;
-	struct hpfs_dirent *de;
+	struct hpfs_dirent *de = NULL;
 	struct buffer_head *bh0, *bh1, *bh2;
 	struct quad_buffer_head qbh;
 	dnode_secno root_dno;
diff -u --recursive --new-file v2.1.124/linux/fs/locks.c linux/fs/locks.c
--- v2.1.124/linux/fs/locks.c	Tue Mar 10 10:03:34 1998
+++ linux/fs/locks.c	Wed Oct  7 15:52:55 1998
@@ -192,7 +192,7 @@
 /* Insert waiter into blocker's block list.
  * We use a circular list so that processes can be easily woken up in
  * the order they blocked. The documentation doesn't require this but
- * it seems seems like the reasonable thing to do.
+ * it seems like the reasonable thing to do.
  */
 static void locks_insert_block(struct file_lock *blocker, 
 			       struct file_lock *waiter)
diff -u --recursive --new-file v2.1.124/linux/fs/namei.c linux/fs/namei.c
--- v2.1.124/linux/fs/namei.c	Mon Sep 28 10:51:35 1998
+++ linux/fs/namei.c	Mon Oct  5 10:04:34 1998
@@ -29,6 +29,8 @@
 #include <asm/semaphore.h>
 #include <asm/spinlock.h>
 #include <asm/namei.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
 
 /* This can be removed after the beta phase. */
 #define CACHE_SUPERVISE	/* debug the correctness of dcache entries */
diff -u --recursive --new-file v2.1.124/linux/fs/ncpfs/ncplib_kernel.c linux/fs/ncpfs/ncplib_kernel.c
--- v2.1.124/linux/fs/ncpfs/ncplib_kernel.c	Wed Apr  1 20:11:53 1998
+++ linux/fs/ncpfs/ncplib_kernel.c	Mon Oct  5 10:04:35 1998
@@ -300,6 +300,7 @@
 	return result;
 }
 
+#ifdef CONFIG_NCPFS_NFS_NS
 static int
 ncp_obtain_DOS_dir_base(struct ncp_server *server,
 		__u8 volnum, __u32 dirent,
@@ -323,6 +324,7 @@
 	ncp_unlock_server(server);
 	return result;
 }
+#endif /* CONFIG_NCPFS_NFS_NS */
 
 static inline int
 ncp_get_known_namespace(struct ncp_server *server, __u8 volume)
diff -u --recursive --new-file v2.1.124/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.1.124/linux/fs/proc/array.c	Thu Sep 17 17:53:38 1998
+++ linux/fs/proc/array.c	Mon Oct  5 10:04:35 1998
@@ -563,6 +563,29 @@
 			fp = *(unsigned long *) (fp - 12);
 		} while (count ++ < 16);
 	}
+#elif defined (__sparc__)
+	{
+		unsigned long pc, fp, bias = 0;
+		unsigned long task_base = (unsigned long) p;
+		struct reg_window *rw;
+		int count = 0;
+
+#ifdef __sparc_v9__
+		bias = STACK_BIAS;
+#endif
+	    	fp = p->tss.ksp + bias;
+		do {
+			/* Bogus frame pointer? */
+			if (fp < (task_base + sizeof(struct task_struct)) ||
+			    fp >= (task_base + (2 * PAGE_SIZE)))
+				break;
+			rw = (struct reg_window *) fp;
+			pc = rw->ins[7];
+			if (pc < first_sched || pc >= last_sched)
+				return pc;
+			fp = rw->ins[6] + bias;
+		} while (++count < 16);
+	}
 #endif
 	return 0;
 }
diff -u --recursive --new-file v2.1.124/linux/fs/super.c linux/fs/super.c
--- v2.1.124/linux/fs/super.c	Mon Sep 28 10:51:35 1998
+++ linux/fs/super.c	Mon Oct  5 10:04:35 1998
@@ -54,10 +54,6 @@
  */
 static struct semaphore mount_sem = MUTEX;
 
-#ifdef CONFIG_BSD_PROCESS_ACCT
-extern void acct_auto_close(kdev_t);
-#endif
-
 extern void wait_for_keypress(void);
 extern struct file_operations * get_blkfops(unsigned int major);
 
diff -u --recursive --new-file v2.1.124/linux/include/asm-i386/dma.h linux/include/asm-i386/dma.h
--- v2.1.124/linux/include/asm-i386/dma.h	Fri Jul 31 17:06:09 1998
+++ linux/include/asm-i386/dma.h	Thu Oct  8 21:32:14 1998
@@ -9,6 +9,8 @@
 #define _ASM_DMA_H
 
 #include <asm/io.h>		/* need byte IO */
+#include <asm/spinlock.h>	/* And spinlocks */
+#include <linux/delay.h>
 
 
 #ifdef HAVE_REALLY_SLOW_DMA_CONTROLLER
@@ -129,6 +131,21 @@
 #define DMA_MODE_WRITE	0x48	/* memory to I/O, no autoinit, increment, single mode */
 #define DMA_MODE_CASCADE 0xC0   /* pass thru DREQ->HRQ, DACK<-HLDA only */
 
+
+extern spinlock_t  dma_spin_lock;
+
+static __inline__ unsigned long claim_dma_lock(void)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&dma_spin_lock, flags);
+	return flags;
+}
+
+static __inline__ void release_dma_lock(unsigned long flags)
+{
+	spin_unlock_irqrestore(&dma_spin_lock, flags);
+}
+
 /* enable/disable a specific DMA channel */
 static __inline__ void enable_dma(unsigned int dmanr)
 {
@@ -144,6 +161,8 @@
 		dma_outb(dmanr | 4,  DMA1_MASK_REG);
 	else
 		dma_outb((dmanr & 3) | 4,  DMA2_MASK_REG);
+	/* I hate voodoo programming but .. */
+	udelay(20);
 }
 
 /* Clear the 'DMA Pointer Flip Flop'.
@@ -151,7 +170,7 @@
  * Use this once to initialize the FF to a known state.
  * After that, keep track of it. :-)
  * --- In order to do that, the DMA routines below should ---
- * --- only be used while interrupts are disabled! ---
+ * --- only be used while holding the DMA lock ! ---
  */
 static __inline__ void clear_dma_ff(unsigned int dmanr)
 {
diff -u --recursive --new-file v2.1.124/linux/include/asm-i386/unistd.h linux/include/asm-i386/unistd.h
--- v2.1.124/linux/include/asm-i386/unistd.h	Wed Sep  9 14:51:12 1998
+++ linux/include/asm-i386/unistd.h	Mon Oct  5 12:25:03 1998
@@ -192,8 +192,8 @@
 #define __NR_capset		185
 #define __NR_sigaltstack	186
 #define __NR_sendfile		187
-#define __NR_streams1		188	/* some people actually want it */
-#define __NR_streams2		189	/* some people actually want it */
+#define __NR_getpmsg		188	/* some people actually want streams */
+#define __NR_putpmsg		189	/* some people actually want streams */
 
 /* user-visible error numbers are in the range -1 - -122: see <asm-i386/errno.h> */
 
diff -u --recursive --new-file v2.1.124/linux/include/asm-m68k/linux_logo.h linux/include/asm-m68k/linux_logo.h
--- v2.1.124/linux/include/asm-m68k/linux_logo.h	Mon Oct  5 13:13:43 1998
+++ linux/include/asm-m68k/linux_logo.h	Mon Oct  5 13:54:39 1998
@@ -882,7 +882,7 @@
 
 #else
 
-#define LINUX_LOGO_COLORS 221
+#define LINUX_LOGO_COLORS 214
 
 #endif
 
diff -u --recursive --new-file v2.1.124/linux/include/asm-m68k/mac_psc.h linux/include/asm-m68k/mac_psc.h
--- v2.1.124/linux/include/asm-m68k/mac_psc.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-m68k/mac_psc.h	Mon Oct  5 13:54:39 1998
@@ -0,0 +1,73 @@
+/*
+ *	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).
+ */
+ 
+#define PSCBASE		0x50F31000
+
+/*
+ *	The IER/IFR registers work like the VIA, except that it has 4
+ *	of them each on different interrupt levels.
+ */
+ 
+#define pIFR3		0x130
+#define pIFR4		0x140
+#define pIFR5		0x150
+#define pIFR6		0x160
+
+#define pIER3		0x134
+#define pIER4		0x144
+#define pIER5		0x154
+#define pIER6		0x164
+
+/*
+ *	Ethernet Control Registers
+ */
+ 
+#define PSC_ENETRD_CTL  0xc10
+#define PSC_ENETWR_CTL  0xc20
+
+/*
+ *	Receive DMA channel (add +0x10 for 2nd channel)
+ */
+ 
+#define PSC_ENETRD_ADDR 0x1020 
+#define PSC_ENETRD_LEN  0x1024
+#define PSC_ENETRD_CMD  0x1028
+
+/*
+ *	Transmit DMA channel (add +0x10 for 2nd channel)
+ */
+ 
+#define PSC_ENETWR_ADDR 0x1040
+#define PSC_ENETWR_LEN  0x1044
+#define PSC_ENETWR_CMD  0x1048
+
+/*
+ *	Access functions
+ */
+ 
+extern volatile unsigned char *psc;
+
+extern inline void psc_write_word(int offset, u16 data)
+{
+	*((volatile u16 *)(psc+offset)) = data;
+}
+
+extern inline void psc_write_long(int offset, u32 data)
+{
+	*((volatile u32 *)(psc+offset)) = data;
+}
+
+extern inline u16 psc_read_word(int offset)
+{
+	return *((volatile u16 *)(psc+offset));
+}
+
+extern inline u32 psc_read_long(int offset)
+{
+	return *((volatile u32 *)(psc+offset));
+}
+
diff -u --recursive --new-file v2.1.124/linux/include/asm-m68k/machdep.h linux/include/asm-m68k/machdep.h
--- v2.1.124/linux/include/asm-m68k/machdep.h	Mon Aug  3 12:45:47 1998
+++ linux/include/asm-m68k/machdep.h	Mon Oct  5 13:54:39 1998
@@ -37,6 +37,7 @@
 extern void (*mach_floppy_setup)(char *, int *);
 extern void (*mach_floppy_eject)(void);
 extern void (*mach_heartbeat) (int);
+extern void (*mach_l2_flush) (int);
 extern int mach_sysrq_key;
 extern int mach_sysrq_shift_state;
 extern int mach_sysrq_shift_mask;
diff -u --recursive --new-file v2.1.124/linux/include/asm-m68k/macintosh.h linux/include/asm-m68k/macintosh.h
--- v2.1.124/linux/include/asm-m68k/macintosh.h	Tue Jun 23 10:01:28 1998
+++ linux/include/asm-m68k/macintosh.h	Mon Oct  5 13:54:39 1998
@@ -80,6 +80,7 @@
 
 #define MAC_ETHER_NONE		0	
 #define MAC_ETHER_SONIC		1
+#define MAC_ETHER_MACE		2
 
 #define MAC_NO_NUBUS		0
 #define MAC_NUBUS		1
@@ -99,48 +100,48 @@
 #define MAC_MODEL_Q900		20
 #define MAC_MODEL_PB170		21
 #define MAC_MODEL_Q700		22
-#define MAC_MODEL_CLII		23
+#define MAC_MODEL_CLII		23	/* aka: P200 */
 #define MAC_MODEL_PB140		25
-#define MAC_MODEL_Q950		26
+#define MAC_MODEL_Q950		26	/* aka: WGS95 */
 #define MAC_MODEL_LCIII		27	/* aka: P450 */
 #define MAC_MODEL_PB210		29
 #define MAC_MODEL_C650		30
 #define MAC_MODEL_PB230		32
 #define MAC_MODEL_PB180		33
 #define MAC_MODEL_PB160		34
-#define MAC_MODEL_Q800		35
+#define MAC_MODEL_Q800		35	/* aka: WGS80 */
 #define MAC_MODEL_Q650		36 
 #define MAC_MODEL_LCII		37	/* aka: P400/405/410/430 */
 #define MAC_MODEL_PB250		38
 #define MAC_MODEL_IIVI		44
-#define MAC_MODEL_P600		45
+#define MAC_MODEL_P600		45	/* aka: P600CD */
 #define MAC_MODEL_IIVX		48
-#define MAC_MODEL_CCL		49
+#define MAC_MODEL_CCL		49	/* aka: P250 */
 #define MAC_MODEL_PB165C	50
-#define MAC_MODEL_C610		52
+#define MAC_MODEL_C610		52	/* aka: WGS60 */
 #define MAC_MODEL_Q610		53
-#define MAC_MODEL_PB145		54
+#define MAC_MODEL_PB145		54	/* aka: PB145B */
 #define MAC_MODEL_P520		56	/* aka: LC520 */
 #define MAC_MODEL_C660		60
-#define MAC_MODEL_P460		62	/* aka: LCIII+, P466/7 */
+#define MAC_MODEL_P460		62	/* aka: LCIII+, P466/P467 */
 #define MAC_MODEL_PB180C	71
-#define MAC_MODEL_PB520		72
+#define MAC_MODEL_PB520		72	/* aka: PB520C, PB540, PB540C, PB550C */
 #define MAC_MODEL_PB270C	77
 #define MAC_MODEL_Q840		78
-#define MAC_MODEL_P550		80
-#define MAC_MODEL_CCLII		83
+#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
 #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/580, P577/578/508 */
+#define MAC_MODEL_P575		92	/* aka: LC575, P577/P578 */
 #define MAC_MODEL_Q605		94
-#define MAC_MODEL_Q630		98	/* aka: LC630, P630/631/636/640 */
-#define MAC_MODEL_P588		99	/* aka: ?? */
+#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 --recursive --new-file v2.1.124/linux/include/asm-m68k/stat.h linux/include/asm-m68k/stat.h
--- v2.1.124/linux/include/asm-m68k/stat.h	Tue Jun 23 10:01:28 1998
+++ linux/include/asm-m68k/stat.h	Mon Oct  5 12:33:40 1998
@@ -38,42 +38,4 @@
 	unsigned long  __unused5;
 };
 
-#if 0
-typedef struct {
-	unsigned int	major;
-	unsigned int	minor;
-} __new_dev_t;
-
-struct stat64 {
-	__new_dev_t	st_dev;
-	__u64		st_ino;
-	unsigned int	st_mode;
-	unsigned int	st_nlink;
-	unsigned int	st_uid;
-	unsigned int	st_gid;
-	__new_dev_t	st_rdev;
-	__s64		st_size;
-	__u64		st_blocks;
-	unsigned long	st_atime;
-	unsigned long	__unused1;
-	unsigned long	st_mtime;
-	unsigned long	__unused2;
-	unsigned long	st_ctime;
-	unsigned long	__unused3;
-	unsigned long	st_blksize;
-	unsigned long	__unused4;
-};
-#endif
-
-#define __XSTAT_VER_1		1
-#define __XSTAT_VER_2		2
-#define __XSTAT_VER_MASK	0xff
-
-#define __XSTAT_VER_XSTAT	0x000
-#define __XSTAT_VER_LXSTAT	0x100
-#define __XSTAT_VER_FXSTAT	0x200
-#define __XSTAT_VER_TYPEMASK	0xff00
-
-#define __XMKNOD_VER_1		1
-
 #endif /* _M68K_STAT_H */
diff -u --recursive --new-file v2.1.124/linux/include/asm-m68k/unistd.h linux/include/asm-m68k/unistd.h
--- v2.1.124/linux/include/asm-m68k/unistd.h	Sat Sep  5 16:46:41 1998
+++ linux/include/asm-m68k/unistd.h	Mon Oct  5 13:54:39 1998
@@ -5,7 +5,6 @@
  * This file contains the system call numbers.
  */
 
-#define __NR_setup		  0	/* used only by init, to get system going */
 #define __NR_exit		  1
 #define __NR_fork		  2
 #define __NR_read		  3
@@ -192,8 +191,8 @@
 #define __NR_capset		185
 #define __NR_sigaltstack	186
 #define __NR_sendfile		187
-#define __NR_streams1		188	/* some people actually want it */
-#define __NR_streams2		189	/* some people actually want it */
+#define __NR_getpmsg		188	/* some people actually want streams */
+#define __NR_putpmsg		189	/* some people actually want streams */
 
 /* user-visible error numbers are in the range -1 - -122: see
    <asm-m68k/errno.h> */
@@ -311,7 +310,6 @@
 #define __NR__exit __NR_exit
 static inline _syscall0(int,idle)
 static inline _syscall0(int,pause)
-static inline _syscall1(int,setup,int,magic)
 static inline _syscall0(int,sync)
 static inline _syscall0(pid_t,setsid)
 static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
diff -u --recursive --new-file v2.1.124/linux/include/asm-m68k/xstat.h linux/include/asm-m68k/xstat.h
--- v2.1.124/linux/include/asm-m68k/xstat.h	Fri May  8 23:14:55 1998
+++ linux/include/asm-m68k/xstat.h	Wed Dec 31 16:00:00 1969
@@ -1,35 +0,0 @@
-/* $Id: xstat.h,v 1.1 1998/02/06 12:51:30 jj Exp $
- * xstat.h: sys_xstat/xmknod architecture dependent stuff.
- *
- * Copyright (C) 1998  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
- */
- 
-extern __inline__ int cp_xstat(struct inode *inode, struct stat64 *s, unsigned long blocks, int blksize)
-{
-	struct stat64 tmp;
-	
-	memset (&tmp, 0, sizeof(tmp));
-	tmp.st_dev.major = MAJOR(inode->i_dev);
-	tmp.st_dev.minor = MINOR(inode->i_dev);
-	tmp.st_ino = inode->i_ino;
-	tmp.st_mode = inode->i_mode;
-	tmp.st_nlink = inode->i_nlink;
-	tmp.st_uid = inode->i_uid;
-	tmp.st_gid = inode->i_gid;
-	tmp.st_rdev.major = MAJOR(inode->i_rdev);
-	tmp.st_rdev.minor = MINOR(inode->i_rdev);
-	tmp.st_size = inode->i_size;
-	tmp.st_blksize = blksize;
-	tmp.st_blocks = blocks;
-	tmp.st_atime = inode->i_atime;
-	tmp.st_mtime = inode->i_mtime;
-	tmp.st_ctime = inode->i_ctime;
-	return copy_to_user(s,&tmp,sizeof(tmp));
-}
-
-extern __inline__ int get_user_new_dev_t(kdev_t *kdev, __new_dev_t *udev) {
-	__new_dev_t ndev;
-	if (copy_from_user (&ndev, udev, sizeof(__new_dev_t))) return -EFAULT;
-	*kdev = MKDEV(ndev.major, ndev.minor);
-	return 0;
-}
diff -u --recursive --new-file v2.1.124/linux/include/asm-ppc/io.h linux/include/asm-ppc/io.h
--- v2.1.124/linux/include/asm-ppc/io.h	Mon Oct  5 13:13:43 1998
+++ linux/include/asm-ppc/io.h	Mon Oct  5 13:01:02 1998
@@ -205,27 +205,9 @@
  */
 extern inline void eieio(void)
 {
-	__asm__ __volatile__ ("eieio" : : : "memory" );
+	__asm__ __volatile__ ("eieio" : : : "memory");
 }
 
-
-/*
- * the constraint handling of gcc-2.7 is broken, fall back to
- * the old optimizer unfriendly eieio()
- */
-
-#if defined(__GNUC__) && __GNUC__ == 2 && __GNUC_MINOR__ <= 7
-#define __memory_clobber_pre \
-	do { } while(0)
-#define __memory_clobber_post \
-	eieio()
-#else
-#define __memory_clobber_pre \
-	__asm__ __volatile__ (" \t#%0,%1" : "=m" (*addr) : "0" (*addr) )
-#define __memory_clobber_post \
-	__asm__ __volatile__ ("eieio \t#%0,%1" : "=m" (*addr) : "0" (*addr) )
-#endif
-
 /*
  * 8, 16 and 32 bit, big and little endian I/O operations, with barrier.
  */
@@ -233,26 +215,21 @@
 {
 	int ret;
 
-	__memory_clobber_pre;
-	ret = *addr;
-	__memory_clobber_post;
+	__asm__ __volatile__("lbz%U1%X1 %0,%1; eieio" : "=r" (ret) : "m" (*addr));
 	return ret;
 }
 
 extern inline void out_8(volatile unsigned char *addr, int val)
 {
-	__memory_clobber_pre;
-	*addr = val;
-	__memory_clobber_post;
+	__asm__ __volatile__("stb%U0%X0 %1,%0; eieio" : "=m" (*addr) : "r" (val));
 }
 
 extern inline int in_le16(volatile unsigned short *addr)
 {
 	int ret;
 
-	__memory_clobber_pre;
-	ret = ld_le16(addr);
-	__memory_clobber_post;
+	__asm__ __volatile__("lhbrx %0,0,%1; eieio" : "=r" (ret) :
+			      "r" (addr), "m" (*addr));
 	return ret;
 }
 
@@ -260,58 +237,47 @@
 {
 	int ret;
 
-	__memory_clobber_pre;
-	ret = *addr;
-	__memory_clobber_post;
+	__asm__ __volatile__("lhz%U1%X1 %0,%1; eieio" : "=r" (ret) : "m" (*addr));
 	return ret;
 }
 
 extern inline void out_le16(volatile unsigned short *addr, int val)
 {
-	__memory_clobber_pre;
-	st_le16(addr, val);
-	__memory_clobber_post;
+	__asm__ __volatile__("sthbrx %1,0,%2; eieio" : "=m" (*addr) :
+			      "r" (val), "r" (addr));
 }
 
 extern inline void out_be16(volatile unsigned short *addr, int val)
 {
-	__memory_clobber_pre;
-	*addr = val;
-	__memory_clobber_post;
+	__asm__ __volatile__("sth%U0%X0 %1,%0; eieio" : "=m" (*addr) : "r" (val));
 }
 
 extern inline unsigned in_le32(volatile unsigned *addr)
 {
 	unsigned ret;
 
-	__memory_clobber_pre;
-	ret = ld_le32(addr);
-	__memory_clobber_post;
+	__asm__ __volatile__("lwbrx %0,0,%1; eieio" : "=r" (ret) :
+			     "r" (addr), "m" (*addr));
 	return ret;
 }
 
-extern inline int in_be32(volatile unsigned *addr)
+extern inline unsigned in_be32(volatile unsigned *addr)
 {
-	int ret;
+	unsigned ret;
 
-	__memory_clobber_pre;
-	ret = *addr;
-	__memory_clobber_post;
+	__asm__ __volatile__("lwz%U1%X1 %0,%1; eieio" : "=r" (ret) : "m" (*addr));
 	return ret;
 }
 
 extern inline void out_le32(volatile unsigned *addr, int val)
 {
-	__memory_clobber_pre;
-	st_le32(addr, val);
-	__memory_clobber_post;
+	__asm__ __volatile__("stwbrx %1,0,%2; eieio" : "=m" (*addr) :
+			     "r" (val), "r" (addr));
 }
 
 extern inline void out_be32(volatile unsigned *addr, int val)
 {
-	__memory_clobber_pre;
-	*addr = val;
-	__memory_clobber_post;
+	__asm__ __volatile__("stw%U0%X0 %1,%0; eieio" : "=m" (*addr) : "r" (val));
 }
 
 #endif
diff -u --recursive --new-file v2.1.124/linux/include/asm-ppc/unistd.h linux/include/asm-ppc/unistd.h
--- v2.1.124/linux/include/asm-ppc/unistd.h	Mon Oct  5 13:13:43 1998
+++ linux/include/asm-ppc/unistd.h	Mon Oct  5 12:25:03 1998
@@ -192,8 +192,8 @@
 #define __NR_capset		184
 #define __NR_sigaltstack	185
 #define __NR_sendfile		186
-#define __NR_streams1		187	/* some people actually want it */
-#define __NR_streams2		188	/* some people actually want it */
+#define __NR_getpmsg		187	/* some people actually want streams */
+#define __NR_putpmsg		188	/* some people actually want streams */
 
 
 #define __NR(n)	#n
diff -u --recursive --new-file v2.1.124/linux/include/linux/apm_bios.h linux/include/linux/apm_bios.h
--- v2.1.124/linux/include/linux/apm_bios.h	Mon Feb 23 18:12:11 1998
+++ linux/include/linux/apm_bios.h	Wed Oct  7 18:52:09 1998
@@ -3,7 +3,7 @@
 
 /*
  * Include file for the interface to an APM BIOS
- * Copyright 1994, 1995 Stephen Rothwell (Stephen.Rothwell@pd.necisa.oz.au)
+ * Copyright 1994-1998 Stephen Rothwell (Stephen.Rothwell@canb.auug.org.au)
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -14,8 +14,6 @@
  * 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.
- *
- * $Id: apm_bios.h,v 0.9 1995/03/09 13:50:05 sfr Exp $
  */
 
 typedef unsigned short	apm_event_t;
@@ -83,7 +81,7 @@
 extern int		apm_register_callback(int (*callback)(apm_event_t));
 extern void		apm_unregister_callback(int (*callback)(apm_event_t));
 
-extern int		apm_set_power_state(ushort state);
+extern void		apm_power_off(void);
 extern int		apm_display_blank(void);
 extern int		apm_display_unblank(void);
 
diff -u --recursive --new-file v2.1.124/linux/include/linux/cyclades.h linux/include/linux/cyclades.h
--- v2.1.124/linux/include/linux/cyclades.h	Sat Sep  5 16:46:41 1998
+++ linux/include/linux/cyclades.h	Thu Oct  8 16:39:33 1998
@@ -757,9 +757,9 @@
 /* Custom Registers */
 
 #define	CyPLX_VER	(0x3400)
-#define	PLX_9050	0x11
-#define	PLX_9060	0x12
-#define	PLX_9080	0x13
+#define	PLX_9050	0x0b
+#define	PLX_9060	0x0c
+#define	PLX_9080	0x0d
 
 /***************************************************************************/
 
diff -u --recursive --new-file v2.1.124/linux/include/linux/errqueue.h linux/include/linux/errqueue.h
--- v2.1.124/linux/include/linux/errqueue.h	Mon Oct  5 13:13:44 1998
+++ linux/include/linux/errqueue.h	Mon Oct  5 12:28:08 1998
@@ -1,6 +1,8 @@
 #ifndef _LINUX_ERRQUEUE_H
 #define _LINUX_ERRQUEUE_H 1
 
+#include <linux/config.h>
+
 struct sock_extended_err
 {
 	__u32	ee_errno;	
diff -u --recursive --new-file v2.1.124/linux/include/linux/hippidevice.h linux/include/linux/hippidevice.h
--- v2.1.124/linux/include/linux/hippidevice.h	Fri Jul 31 17:07:27 1998
+++ linux/include/linux/hippidevice.h	Thu Oct  8 21:31:19 1998
@@ -49,6 +49,7 @@
 extern int hippi_header_parse(struct sk_buff *skb, unsigned char *haddr);
 
 extern void hippi_net_init(void);
+void hippi_setup(struct device *dev);
 
 extern struct device *init_hippi_dev(struct device *, int);
 #endif
diff -u --recursive --new-file v2.1.124/linux/include/linux/if_pppvar.h linux/include/linux/if_pppvar.h
--- v2.1.124/linux/include/linux/if_pppvar.h	Thu Jul 16 18:09:29 1998
+++ linux/include/linux/if_pppvar.h	Mon Oct  5 09:56:12 1998
@@ -42,7 +42,7 @@
  */
 
 /*
- *  ==FILEVERSION 980704==
+ *  ==FILEVERSION 981004==
  *
  *  NOTE TO MAINTAINERS:
  *   If you modify this file at all, please set the above date.
@@ -80,6 +80,7 @@
 	struct slcompress *slcomp;	/* for TCP header compression	*/
 	struct sk_buff_head xmt_q;	/* frames to send from pppd	*/
 	struct sk_buff_head rcv_q;	/* frames for pppd to read	*/
+	unsigned long	xmit_busy;	/* bit 0 set when xmitter busy  */
 
 	/* Information specific to using ppp on async serial lines. */
 	struct tty_struct *tty;		/* ptr to TTY structure	*/
diff -u --recursive --new-file v2.1.124/linux/include/linux/joystick.h linux/include/linux/joystick.h
--- v2.1.124/linux/include/linux/joystick.h	Thu May 14 19:47:43 1998
+++ linux/include/linux/joystick.h	Thu Oct  8 21:33:08 1998
@@ -2,18 +2,54 @@
 #define _LINUX_JOYSTICK_H
 
 /*
- * /usr/include/linux/joystick.h  Version 1.0.9
+ * /usr/include/linux/joystick.h  Version 1.2
  *
  * Copyright (C) 1996-1998 Vojtech Pavlik
  */
 
+/*
+ * 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@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
 #include <asm/types.h>
+#include <linux/module.h>
 
 /*
  * Version
  */
 
-#define JS_VERSION		0x00010008L
+#define JS_VERSION		0x01020b
+
+/*
+ * Types and constants for reading from /dev/js
+ */
+
+#define JS_EVENT_BUTTON		0x01	/* button pressed/released */
+#define JS_EVENT_AXIS		0x02	/* joystick moved */
+#define JS_EVENT_INIT		0x80	/* initial state of device */
+
+struct js_event {
+	__u32 time;	/* event timestamp in miliseconds */
+	__s16 value;	/* value */
+	__u8 type;	/* event type */
+	__u8 number;	/* axis/button number */
+};
 
 /*
  * IOCTL commands for joystick driver
@@ -23,9 +59,10 @@
 
 #define JSIOCGAXES		_IOR('j', 0x11, __u8)			/* get number of axes */
 #define JSIOCGBUTTONS		_IOR('j', 0x12, __u8)			/* get number of buttons */
+#define JSIOCGNAME(len)		_IOC(_IOC_READ, 'j', 0x13, len)         /* get identifier string */
 
-#define JSIOCSCORR		_IOW('j', 0x21, struct js_corr[4])	/* set correction values */
-#define JSIOCGCORR		_IOR('j', 0x22, struct js_corr[4])	/* get correction values */
+#define JSIOCSCORR		_IOW('j', 0x21, struct js_corr)		/* set correction values */
+#define JSIOCGCORR		_IOR('j', 0x22, struct js_corr)		/* get correction values */
 
 /*
  * Types and constants for get/set correction
@@ -36,45 +73,222 @@
 
 struct js_corr {
 	__s32 coef[8];
-	__u16 prec;
+	__s16 prec;
 	__u16 type;
 };
 
 /*
- * Types and constants for reading from /dev/js
+ * v0.x compatibility definitions
  */
 
-#define JS_EVENT_BUTTON		0x01	/* button pressed/released */
-#define JS_EVENT_AXIS		0x02	/* joystick moved */
-#define JS_EVENT_INIT		0x80	/* initial state of device at open time */
+#define JS_RETURN		sizeof(struct JS_DATA_TYPE)
+#define JS_TRUE			1
+#define JS_FALSE		0
+#define JS_X_0			0x01
+#define JS_Y_0			0x02
+#define JS_X_1			0x04
+#define JS_Y_1			0x08
+#define JS_MAX			2
+
+#define JS_DEF_TIMEOUT		0x1300
+#define JS_DEF_CORR		0
+#define JS_DEF_TIMELIMIT	10L
+
+#define JS_SET_CAL		1
+#define JS_GET_CAL		2
+#define JS_SET_TIMEOUT		3
+#define JS_GET_TIMEOUT		4
+#define JS_SET_TIMELIMIT	5
+#define JS_GET_TIMELIMIT	6
+#define JS_GET_ALL		7
+#define JS_SET_ALL		8
 
-struct js_event {
-	__u32 time;	/* event timestamp in miliseconds since open */
-	__s16 value;	/* value */
-	__u8  type;	/* type of event, see above */
-	__u8  number;	/* axis/button number */
+struct JS_DATA_TYPE {
+	int buttons;
+	int x;
+	int y;
+};
+
+struct JS_DATA_SAVE_TYPE {
+	int JS_TIMEOUT;
+	int BUSY;
+	long JS_EXPIRETIME;
+	long JS_TIMELIMIT;
+	struct JS_DATA_TYPE JS_SAVE;
+	struct JS_DATA_TYPE JS_CORR;
 };
 
 /*
- * Backward (version 0.x) compatibility definitions
+ * Internal definitions
  */
 
-#define JS_RETURN 	sizeof(struct JS_DATA_TYPE)
-#define JS_TRUE 	1
-#define JS_FALSE 	0
-#define JS_X_0		0x01	/* bit mask for x-axis js0 */
-#define JS_Y_0		0x02	/* bit mask for y-axis js0 */
-#define JS_X_1		0x04	/* bit mask for x-axis js1 */
-#define JS_Y_1		0x08	/* bit mask for y-axis js1 */
-#define JS_MAX 		2	/* max number of joysticks */
+#ifdef __KERNEL__
 
-struct JS_DATA_TYPE {
-	int buttons;	/* immediate button state */
-	int x;		/* immediate x axis value */
-	int y;		/* immediate y axis value */
+#define JS_BUFF_SIZE		64		/* output buffer size */
+
+#include <linux/version.h>
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+#ifndef LINUX_VERSION_CODE
+#error "You need to use at least 2.0 Linux kernel."
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0)
+#error "You need to use at least 2.0 Linux kernel."
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+#define JS_HAS_RDTSC (current_cpu_data.x86_capability & 0x10)
+#include <linux/init.h>
+#else
+#ifdef MODULE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,0,35)
+#define JS_HAS_RDTSC (x86_capability & 0x10)
+#else
+#define JS_HAS_RDTSC 0
+#endif
+#else
+#define JS_HAS_RDTSC (x86_capability & 0x10)
+#endif
+#define __initdata
+#define __init
+#define __cli cli
+#define __save_flags(flags) save_flags(flags)
+#define __restore_flags(flags)	restore_flags(flags)
+#define spin_lock_irqsave(x, flags) do { cli(); save_flags(flags); } while (0)
+#define spin_unlock_irqrestore(x, flags) restore_flags(flags)
+#define spin_lock_init(x) do { } while (0)
+typedef struct { int something; } spinlock_t;
+#define SPIN_LOCK_UNLOCKED { 0 }
+#define MODULE_AUTHOR(x)
+#define MODULE_PARM(x,y)
+#define MODULE_SUPPORTED_DEVICE(x)
+#define signal_pending(x) (((x)->signal) & ~((x)->blocked))
+#endif
+
+/*
+ * Parport stuff
+ */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+#define USE_PARPORT
+#endif
+
+#ifdef USE_PARPORT
+#include <linux/parport.h>
+#define JS_PAR_STATUS(y)	parport_read_status(y->port)
+#define JS_PAR_DATA_IN(y)	parport_read_data(y->port)
+#define JS_PAR_DATA_OUT(x,y)	parport_write_data(y->port, x)
+#define JS_PAR_CTRL_OUT(x,y)	parport_write_control(y->port, x)
+#else
+#define JS_PAR_STATUS(y)	inb(y+1)
+#define JS_PAR_DATA_IN(y)	inb(y)
+#define JS_PAR_DATA_OUT(x,y)	outb(x,y)
+#define JS_PAR_CTRL_OUT(x,y)	outb(x,y+2)
+#endif
+
+#define JS_PAR_STATUS_INVERT	(0x80)
+
+/*
+ * Internal types
+ */
+
+struct js_dev;
+
+typedef int (*js_read_func)(void *info, int **axes, int **buttons);
+typedef unsigned int (*js_time_func)(void);
+typedef int (*js_delta_func)(unsigned int x, unsigned int y);
+typedef int (*js_ops_func)(struct js_dev *dev);
+
+struct js_data {
+	int *axes;
+	int *buttons;
+};
+
+struct js_dev {
+	struct js_dev *next;
+	struct js_list *list;
+	struct js_port *port;
+	struct wait_queue *wait;
+	struct js_data cur;
+	struct js_data new;
+	struct js_corr *corr;
+	struct js_event buff[JS_BUFF_SIZE];
+	js_ops_func open;
+	js_ops_func close;
+	int ahead;
+	int bhead;
+	int tail;
+	int num_axes;
+	int num_buttons;
+	char *name;
+};
+
+struct js_list {
+	struct js_list *next;
+	struct js_dev *dev;
+	int tail;
+	int startup;
 };
 
+struct js_port {
+	struct js_port *next;
+	struct js_port *prev;
+	js_read_func read;
+	struct js_dev **devs;
+	int **axes;
+	int **buttons;
+	struct js_corr **corr;
+	void *info;
+	int ndevs;
+};
+
+/*
+ * Sub-module interface
+ */
+
+extern unsigned int js_time_speed;
+extern js_time_func js_get_time;
+extern js_delta_func js_delta;
+
+extern unsigned int js_time_speed_a;
+extern js_time_func js_get_time_a;
+extern js_delta_func js_delta_a;
+
+extern struct js_port *js_register_port(struct js_port *port, void *info,
+	int devs, int infos, js_read_func read);
+extern struct js_port *js_unregister_port(struct js_port *port);
+
+extern int js_register_device(struct js_port *port, int number, int axes,
+	int buttons, char *name, js_ops_func open, js_ops_func close);
+extern void js_unregister_device(struct js_dev *dev);
+
+/*
+ * Kernel interface
+ */
 
 extern int js_init(void);
+extern int js_am_init(void);
+extern int js_an_init(void);
+extern int js_as_init(void);
+extern int js_console_init(void);
+extern int js_db9_init(void);
+extern int js_gr_init(void);
+extern int js_l4_init(void);
+extern int js_lt_init(void);
+extern int js_sw_init(void);
+extern int js_tm_init(void);
+
+extern void js_am_setup(char *str, int *ints);
+extern void js_an_setup(char *str, int *ints);
+extern void js_as_setup(char *str, int *ints);
+extern void js_console_setup(char *str, int *ints);
+extern void js_db9_setup(char *str, int *ints);
+extern void js_l4_setup(char *str, int *ints);
+
+#endif /* __KERNEL__ */
 
 #endif /* _LINUX_JOYSTICK_H */
diff -u --recursive --new-file v2.1.124/linux/include/linux/linkage.h linux/include/linux/linkage.h
--- v2.1.124/linux/include/linux/linkage.h	Tue Jan 20 16:45:03 1998
+++ linux/include/linux/linkage.h	Tue Oct  6 08:10:35 1998
@@ -36,7 +36,7 @@
 #ifdef __ASSEMBLY__
 
 #define ALIGN __ALIGN
-#define ALIGN_STRING __ALIGN_STRING
+#define ALIGN_STR __ALIGN_STR
 
 #define ENTRY(name) \
   .globl SYMBOL_NAME(name); \
diff -u --recursive --new-file v2.1.124/linux/include/linux/openpic.h linux/include/linux/openpic.h
--- v2.1.124/linux/include/linux/openpic.h	Thu Aug  6 14:06:34 1998
+++ linux/include/linux/openpic.h	Mon Oct  5 13:00:11 1998
@@ -327,7 +327,7 @@
      */
 
 /* Global Operations */
-extern void openpic_init(void);
+extern void openpic_init(int);
 extern void openpic_reset(void);
 extern void openpic_enable_8259_pass_through(void);
 extern void openpic_disable_8259_pass_through(void);
diff -u --recursive --new-file v2.1.124/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.1.124/linux/include/linux/pci.h	Mon Oct  5 13:13:45 1998
+++ linux/include/linux/pci.h	Thu Oct  8 21:31:15 1998
@@ -1037,6 +1037,13 @@
 #define PCI_DEVICE_ID_ADAPTEC_7884	0x8478
 #define PCI_DEVICE_ID_ADAPTEC_1030	0x8b78
 
+#define PCI_VENDOR_ID_ADAPTEC2  0x9005
+#define PCI_DEVICE_ID_ADAPTEC2_2940U2  0x0010
+#define PCI_DEVICE_ID_ADAPTEC2_7890  0x001f
+#define PCI_DEVICE_ID_ADAPTEC2_3940U2  0x0050
+#define PCI_DEVICE_ID_ADAPTEC2_7896  0x005f
+
+
 #define PCI_VENDOR_ID_ATRONICS		0x907f
 #define PCI_DEVICE_ID_ATRONICS_2015	0x2015
 
diff -u --recursive --new-file v2.1.124/linux/include/linux/rose.h linux/include/linux/rose.h
--- v2.1.124/linux/include/linux/rose.h	Thu Dec  4 13:12:39 1997
+++ linux/include/linux/rose.h	Wed Oct  7 15:52:55 1998
@@ -9,6 +9,8 @@
 
 #define ROSE_MTU	251
 
+#define ROSE_MAX_DIGIS 6
+
 #define	ROSE_DEFER	1
 #define ROSE_T1		2
 #define	ROSE_T2		3
@@ -24,6 +26,7 @@
 #define	SIOCRSACCEPT		(SIOCPROTOPRIVATE+3)
 #define	SIOCRSCLRRT		(SIOCPROTOPRIVATE+4)
 #define	SIOCRSGL2CALL		(SIOCPROTOPRIVATE+5)
+#define	SIOCRSGFACILITIES	(SIOCPROTOPRIVATE+6)
 
 #define	ROSE_DTE_ORIGINATED	0x00
 #define	ROSE_NUMBER_BUSY	0x01
@@ -48,6 +51,14 @@
 	ax25_address	srose_digi;
 };
 
+struct full_sockaddr_rose {
+	sa_family_t	srose_family;
+	rose_address	srose_addr;
+	ax25_address	srose_call;
+	unsigned int	srose_ndigis;
+	ax25_address	srose_digis[ROSE_MAX_DIGIS];
+};
+
 struct rose_route_struct {
 	rose_address	address;
 	unsigned short	mask;
@@ -60,6 +71,17 @@
 struct rose_cause_struct {
 	unsigned char	cause;
 	unsigned char	diagnostic;
+};
+
+struct rose_facilities_struct {
+	rose_address	source_addr,   dest_addr;
+	ax25_address	source_call,   dest_call;
+	unsigned char	source_ndigis, dest_ndigis;
+	ax25_address	source_digis[ROSE_MAX_DIGIS];
+	ax25_address	dest_digis[ROSE_MAX_DIGIS];
+	unsigned int	rand;
+	rose_address	fail_addr;
+	ax25_address	fail_call;
 };
 
 #endif
diff -u --recursive --new-file v2.1.124/linux/include/linux/serial167.h linux/include/linux/serial167.h
--- v2.1.124/linux/include/linux/serial167.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/serial167.h	Mon Oct  5 14:12:12 1998
@@ -0,0 +1,175 @@
+/*
+ * serial167.h
+ *
+ * Richard Hirst [richard@sleepie.demon.co.uk]
+ *
+ * Based on cyclades.h
+ */
+
+struct cyclades_monitor {
+        unsigned long           int_count;
+        unsigned long           char_count;
+        unsigned long           char_max;
+        unsigned long           char_last;
+};
+
+/*
+ * 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
+ */
+
+struct cyclades_port {
+	int                     magic;
+	int                     type;
+	int			card;
+	int			line;
+	int			flags; 		/* defined in tty.h */
+	struct tty_struct 	*tty;
+	int			read_status_mask;
+	int			timeout;
+	int			xmit_fifo_size;
+	int                     cor1,cor2,cor3,cor4,cor5,cor6,cor7;
+	int                     tbpr,tco,rbpr,rco;
+	int			ignore_status_mask;
+	int			close_delay;
+	int			IER; 	/* Interrupt Enable Register */
+	int			event;
+	unsigned long		last_active;
+	int			count;	/* # of fd on device */
+	int                     x_char; /* to be pushed out ASAP */
+	int                     x_break;
+	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;
+        int                     default_threshold;
+        int                     default_timeout;
+	struct tq_struct	tqueue;
+	struct termios		normal_termios;
+	struct termios		callout_termios;
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
+        struct cyclades_monitor mon;
+};
+
+#define CYCLADES_MAGIC  0x4359
+
+#define CYGETMON                0x435901
+#define CYGETTHRESH             0x435902
+#define CYSETTHRESH             0x435903
+#define CYGETDEFTHRESH          0x435904
+#define CYSETDEFTHRESH          0x435905
+#define CYGETTIMEOUT            0x435906
+#define CYSETTIMEOUT            0x435907
+#define CYGETDEFTIMEOUT         0x435908
+#define CYSETDEFTIMEOUT         0x435909
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at cy interrupt time.
+ */
+#define Cy_EVENT_READ_PROCESS	0
+#define Cy_EVENT_WRITE_WAKEUP	1
+#define Cy_EVENT_HANGUP		2
+#define Cy_EVENT_BREAK		3
+#define Cy_EVENT_OPEN_WAKEUP	4
+
+
+
+#define CyMaxChipsPerCard 1
+
+/**** cd2401 registers ****/
+
+#define CyGFRCR         (0x81)
+#define CyCCR		(0x13)
+#define      CyCLR_CHAN		(0x40)
+#define      CyINIT_CHAN	(0x20)
+#define      CyCHIP_RESET	(0x10)
+#define      CyENB_XMTR		(0x08)
+#define      CyDIS_XMTR		(0x04)
+#define      CyENB_RCVR		(0x02)
+#define      CyDIS_RCVR		(0x01)
+#define CyCAR		(0xee)
+#define CyIER		(0x11)
+#define      CyMdmCh		(0x80)
+#define      CyRxExc		(0x20)
+#define      CyRxData		(0x08)
+#define      CyTxMpty		(0x02)
+#define      CyTxRdy		(0x01)
+#define CyLICR		(0x26)
+#define CyRISR		(0x89)
+#define      CyTIMEOUT		(0x80)
+#define      CySPECHAR		(0x70)
+#define      CyOVERRUN		(0x08)
+#define      CyPARITY		(0x04)
+#define      CyFRAME		(0x02)
+#define      CyBREAK		(0x01)
+#define CyREOIR		(0x84)
+#define CyTEOIR		(0x85)
+#define CyMEOIR		(0x86)
+#define      CyNOTRANS		(0x08)
+#define CyRFOC		(0x30)
+#define CyRDR		(0xf8)
+#define CyTDR		(0xf8)
+#define CyMISR		(0x8b)
+#define CyRISR		(0x89)
+#define CyTISR		(0x8a)
+#define CyMSVR1		(0xde)
+#define CyMSVR2		(0xdf)
+#define      CyDSR		(0x80)
+#define      CyDCD		(0x40)
+#define      CyCTS		(0x20)
+#define      CyDTR		(0x02)
+#define      CyRTS		(0x01)
+#define CyRTPRL		(0x25)
+#define CyRTPRH		(0x24)
+#define CyCOR1		(0x10)
+#define      CyPARITY_NONE	(0x00)
+#define      CyPARITY_E		(0x40)
+#define      CyPARITY_O		(0xC0)
+#define      Cy_5_BITS		(0x04)
+#define      Cy_6_BITS		(0x05)
+#define      Cy_7_BITS		(0x06)
+#define      Cy_8_BITS		(0x07)
+#define CyCOR2		(0x17)
+#define      CyETC		(0x20)
+#define      CyCtsAE		(0x02)
+#define CyCOR3		(0x16)
+#define      Cy_1_STOP		(0x02)
+#define      Cy_2_STOP		(0x04)
+#define CyCOR4		(0x15)
+#define      CyREC_FIFO		(0x0F)  /* Receive FIFO threshold */
+#define CyCOR5		(0x14)
+#define CyCOR6		(0x18)
+#define CyCOR7		(0x07)
+#define CyRBPR		(0xcb)
+#define CyRCOR		(0xc8)
+#define CyTBPR		(0xc3)
+#define CyTCOR		(0xc0)
+#define CySCHR1		(0x1f)
+#define CySCHR2 	(0x1e)
+#define CyTPR		(0xda)
+#define CyPILR1		(0xe3)
+#define CyPILR2		(0xe0)
+#define CyPILR3		(0xe1)
+#define CyCMR		(0x1b)
+#define      CyASYNC		(0x02)
+#define CyLICR          (0x26)
+#define CyLIVR          (0x09)
+#define CySCRL		(0x23)
+#define CySCRH		(0x22)
+#define CyTFTC		(0x80)
+
+
+/* max number of chars in the FIFO */
+
+#define CyMAX_CHAR_FIFO	12
+
+/***************************************************************************/
diff -u --recursive --new-file v2.1.124/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v2.1.124/linux/include/linux/skbuff.h	Sat Sep  5 16:46:41 1998
+++ linux/include/linux/skbuff.h	Thu Oct  8 21:34:29 1998
@@ -106,6 +106,12 @@
 	__u32		shapestamp;		/* Stamp for shaper    */
 	__u16		shapepend;		/* Pending */
 #endif
+
+#if defined(CONFIG_HIPPI)
+	union{
+		__u32	ifield;
+	} private;
+#endif
 };
 
 /* These are just the default values. This is run time configurable.
diff -u --recursive --new-file v2.1.124/linux/include/linux/x25.h linux/include/linux/x25.h
--- v2.1.124/linux/include/linux/x25.h	Mon Jul  7 08:19:59 1997
+++ linux/include/linux/x25.h	Mon Oct  5 10:04:35 1998
@@ -5,8 +5,6 @@
 #ifndef	X25_KERNEL_H
 #define	X25_KERNEL_H
 
-#define PF_X25			AF_X25
-
 #define	SIOCX25GSUBSCRIP	(SIOCPROTOPRIVATE + 0)
 #define	SIOCX25SSUBSCRIP	(SIOCPROTOPRIVATE + 1)
 #define	SIOCX25GFACILITIES	(SIOCPROTOPRIVATE + 2)
diff -u --recursive --new-file v2.1.124/linux/include/linux/zorro.h linux/include/linux/zorro.h
--- v2.1.124/linux/include/linux/zorro.h	Mon Aug  3 12:45:48 1998
+++ linux/include/linux/zorro.h	Mon Oct  5 13:39:20 1998
@@ -399,6 +399,7 @@
 #define  ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_REG		ZORRO_ID(VILLAGE_TRONIC, 0x17, 0)
 #define  ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3		ZORRO_ID(VILLAGE_TRONIC, 0x18, 0)
 #define  ZORRO_PROD_VILLAGE_TRONIC_ARIADNE			ZORRO_ID(VILLAGE_TRONIC, 0xC9, 0)
+#define  ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2			ZORRO_ID(VILLAGE_TRONIC, 0xCA, 0)
 
 #define ZORRO_MANUF_UTILITIES_UNLIMITED				0x087B
 #define  ZORRO_PROD_UTILITIES_UNLIMITED_EMPLANT_DELUXE		ZORRO_ID(UTILITIES_UNLIMITED, 0x15, 0)
diff -u --recursive --new-file v2.1.124/linux/include/net/rose.h linux/include/net/rose.h
--- v2.1.124/linux/include/net/rose.h	Tue Apr 14 14:29:26 1998
+++ linux/include/net/rose.h	Wed Oct  7 15:52:55 1998
@@ -56,7 +56,7 @@
 #define	ROSE_DEFAULT_ROUTING		1		/* Default routing flag */
 #define	ROSE_DEFAULT_FAIL_TIMEOUT	(120 * HZ)	/* Time until link considered usable */
 #define	ROSE_DEFAULT_MAXVC		50		/* Maximum number of VCs per neighbour */
-#define	ROSE_DEFAULT_WINDOW_SIZE	3		/* Default window size */
+#define	ROSE_DEFAULT_WINDOW_SIZE	7		/* Default window size */
 
 #define ROSE_MODULUS 			8
 #define	ROSE_MAX_PACKET_SIZE		251		/* Maximum packet size */
@@ -72,6 +72,9 @@
 #define	FAC_NATIONAL_FLAGS		0x3F
 #define	FAC_NATIONAL_DEST_DIGI		0xE9
 #define	FAC_NATIONAL_SRC_DIGI		0xEB
+#define	FAC_NATIONAL_FAIL_CALL		0xED
+#define	FAC_NATIONAL_FAIL_ADD		0xEE
+#define	FAC_NATIONAL_DIGIS			0xEF
 
 #define	FAC_CCITT_DEST_NSAP		0xC9
 #define	FAC_CCITT_SRC_NSAP		0xCB
@@ -111,19 +114,12 @@
 	unsigned int		rand;
 };
 
-struct rose_facilities {
-	rose_address		source_addr,   dest_addr;
-	ax25_address		source_call,   dest_call;
-	unsigned char		source_ndigis, dest_ndigis;
-	ax25_address		source_digi,   dest_digi;
-	unsigned int		rand;
-};
-
 typedef struct {
 	rose_address		source_addr,   dest_addr;
 	ax25_address		source_call,   dest_call;
 	unsigned char		source_ndigis, dest_ndigis;
-	ax25_address		source_digi,   dest_digi;
+	ax25_address		source_digis[ROSE_MAX_DIGIS];
+	ax25_address		dest_digis[ROSE_MAX_DIGIS];
 	struct rose_neigh	*neighbour;
 	struct device		*device;
 	unsigned int		lci, rand;
@@ -131,7 +127,12 @@
 	unsigned char		cause, diagnostic;
 	unsigned short		vs, vr, va, vl;
 	unsigned long		t1, t2, t3, hb, idle;
+#ifdef M_BIT
+	unsigned short		fraglen;
+	struct sk_buff_head	frag_queue;
+#endif
 	struct sk_buff_head	ack_queue;
+	struct rose_facilities_struct facilities;
 	struct timer_list	timer;
 	struct timer_list	idletimer;
 	struct sock		*sk;		/* Backlink to socket */
@@ -218,7 +219,7 @@
 extern int  rose_validate_nr(struct sock *, unsigned short);
 extern void rose_write_internal(struct sock *, int);
 extern int  rose_decode(struct sk_buff *, int *, int *, int *, int *, int *);
-extern int  rose_parse_facilities(struct sk_buff *, struct rose_facilities *);
+extern int  rose_parse_facilities(unsigned char *, struct rose_facilities_struct *);
 extern int  rose_create_facilities(unsigned char *, rose_cb *);
 extern void rose_disconnect(struct sock *, int, int, int);
 
diff -u --recursive --new-file v2.1.124/linux/init/main.c linux/init/main.c
--- v2.1.124/linux/init/main.c	Mon Oct  5 13:13:47 1998
+++ linux/init/main.c	Thu Oct  8 17:25:16 1998
@@ -48,6 +48,10 @@
 #include <linux/dio.h>
 #endif
 
+#ifdef CONFIG_ZORRO
+#include <linux/zorro.h>
+#endif
+
 #ifdef CONFIG_MTRR
 #  include <asm/mtrr.h>
 #endif
@@ -106,8 +110,26 @@
 #ifdef CONFIG_PRINTER
 extern void lp_setup(char *str, int *ints);
 #endif
-#ifdef CONFIG_JOYSTICK
-extern void js_setup(char *str, int *ints);
+#ifdef CONFIG_JOY_AMIGA
+extern void js_am_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_JOY_ANALOG
+extern void js_an_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_JOY_ASSASIN
+extern void js_as_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_JOY_CONSOLE
+extern void js_console_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_JOY_DB9
+extern void js_db9_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_JOY_TURBOGRAFX
+extern void js_tg_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_JOY_LIGHTNING
+extern void js_l4_setup(char *str, int *ints);
 #endif
 extern void eth_setup(char *str, int *ints);
 #ifdef CONFIG_ARCNET_COM20020
@@ -579,13 +601,37 @@
 #ifdef CONFIG_PRINTER
         { "lp=", lp_setup },
 #endif
-#ifdef CONFIG_JOYSTICK
-	{ "js=", js_setup },
+#ifdef CONFIG_JOY_AMIGA
+	{ "js_am=", js_am_setup },
+#endif
+#ifdef CONFIG_JOY_ANALOG
+	{ "js_an=", js_an_setup },
+#endif
+#ifdef CONFIG_JOY_ASSASIN
+	{ "js_as=", js_as_setup },
+#endif
+#ifdef CONFIG_JOY_CONSOLE
+	{ "js_console=", js_console_setup },
+	{ "js_console2=", js_console_setup },
+	{ "js_console3=", js_console_setup },
+#endif
+#ifdef CONFIG_JOY_DB9
+	{ "js_db9=", js_db9_setup },
+	{ "js_db9_2=", js_db9_setup },
+	{ "js_db9_3=", js_db9_setup },
+#endif
+#ifdef CONFIG_JOY_TURBOGRAFX
+	{ "js_tg=", js_tg_setup },
+	{ "js_tg_2=", js_tg_setup },
+	{ "js_tg_3=", js_tg_setup },
 #endif
 #ifdef CONFIG_SCSI
 	{ "max_scsi_luns=", scsi_luns_setup },
 	{ "scsi_logging=", scsi_logging_setup },
 #endif
+#ifdef CONFIG_JOY_LIGHTNING
+	{ "js_l4=", js_l4_setup },
+#endif
 #ifdef CONFIG_SCSI_ADVANSYS
 	{ "advansys=", advansys_setup },
 #endif
@@ -1174,9 +1220,6 @@
 #ifdef CONFIG_SYSCTL
 	sysctl_init();
 #endif
-#ifdef CONFIG_DIO
-	dio_init();
-#endif
 
 	/*
 	 * Ok, at this point all CPU's should be initialized, so
@@ -1196,6 +1239,12 @@
 #endif
 #ifdef CONFIG_ARCH_ACORN
 	ecard_init();
+#endif
+#ifdef CONFIG_ZORRO
+	zorro_init();
+#endif
+#ifdef CONFIG_DIO
+	dio_init();
 #endif
 
 	/* Networking initialization needs a process context */ 
diff -u --recursive --new-file v2.1.124/linux/ipc/shm.c linux/ipc/shm.c
--- v2.1.124/linux/ipc/shm.c	Wed Aug 26 11:37:45 1998
+++ linux/ipc/shm.c	Tue Oct  6 14:08:44 1998
@@ -18,6 +18,7 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/init.h>
+#include <linux/vmalloc.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -101,7 +102,7 @@
 		return -ENOMEM;
 	}
 
-	shp->shm_pages = (ulong *) kmalloc (numpages*sizeof(ulong),GFP_KERNEL);
+	shp->shm_pages = (ulong *) vmalloc (numpages*sizeof(ulong));
 	if (!shp->shm_pages) {
 		shm_segs[id] = (struct shmid_ds *) IPC_UNUSED;
 		wake_up (&shm_lock);
@@ -204,7 +205,7 @@
 			shm_swp--;
 		}
 	}
-	kfree(shp->shm_pages);
+	vfree(shp->shm_pages);
 	shm_tot -= numpages;
 	kfree(shp);
 	return;
diff -u --recursive --new-file v2.1.124/linux/kernel/dma.c linux/kernel/dma.c
--- v2.1.124/linux/kernel/dma.c	Tue Aug  1 00:02:47 1995
+++ linux/kernel/dma.c	Wed Oct  7 15:54:44 1998
@@ -32,6 +32,8 @@
  */
 
 
+spinlock_t dma_spin_lock = SPIN_LOCK_UNLOCKED;
+
 
 /* Channel n is busy iff dma_chan_busy[n].lock != 0.
  * DMA0 used to be reserved for DRAM refresh, but apparently not any more...
diff -u --recursive --new-file v2.1.124/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.1.124/linux/mm/vmscan.c	Wed Sep  9 14:51:13 1998
+++ linux/mm/vmscan.c	Tue Oct  6 16:10:25 1998
@@ -511,9 +511,10 @@
 int kswapd(void *unused)
 {
 	struct wait_queue wait = { current, NULL };
+
 	current->session = 1;
 	current->pgrp = 1;
-	sprintf(current->comm, "kswapd");
+	strcpy(current->comm, "kswapd");
 	sigfillset(&current->blocked);
 	
 	/*
diff -u --recursive --new-file v2.1.124/linux/net/802/hippi.c linux/net/802/hippi.c
--- v2.1.124/linux/net/802/hippi.c	Thu May 14 19:47:44 1998
+++ linux/net/802/hippi.c	Wed Oct  7 11:13:49 1998
@@ -34,6 +34,7 @@
 #include <linux/errno.h>
 #include <net/arp.h>
 #include <net/sock.h>
+#include <asm/uaccess.h>
 #include <asm/checksum.h>
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -80,16 +81,8 @@
 	hip->le.dest_addr_type	= 2;	/* 12 bit SC address */
 	hip->le.src_addr_type	= 2;	/* 12 bit SC address */
 
-#if 1
-	if (saddr)
-	{
-		printk("existing saddr - this should not happen, configure ARP please!\n");
-		memcpy(hip->le.src_switch_addr, saddr + 3, 3);
-	}else
-		memcpy(hip->le.src_switch_addr, dev->dev_addr + 3, 3);
-
+	memcpy(hip->le.src_switch_addr, dev->dev_addr + 3, 3);
 	memset(&hip->le.reserved, 0, 16);
-#endif
 
 	hip->snap.dsap		= HIPPI_EXTENDED_SAP;
 	hip->snap.ssap		= HIPPI_EXTENDED_SAP;
@@ -103,7 +96,7 @@
 	{
 		memcpy(hip->le.dest_switch_addr, daddr + 3, 3);
 		memcpy(&skb->private.ifield, daddr + 2, 4);
-		return(HIPPI_HLEN);
+		return HIPPI_HLEN;
 	}
 	return -HIPPI_HLEN;
 }
diff -u --recursive --new-file v2.1.124/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.1.124/linux/net/ipv4/arp.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv4/arp.c	Wed Oct  7 11:13:49 1998
@@ -431,7 +431,7 @@
 	 *	No arp on this interface.
 	 */
 	
-	if (dev->flags&IFF_NOARP)
+	if (dev->flags&(IFF_NOARP|IFF_NODYNARP))
 		return;
 
 	/*
@@ -547,7 +547,7 @@
  */  
 	if (in_dev == NULL ||
 	    arp->ar_hln != dev->addr_len    || 
-	    dev->flags & IFF_NOARP          ||
+	    dev->flags & (IFF_NOARP|IFF_NODYNARP) ||
 	    skb->pkt_type == PACKET_OTHERHOST ||
 	    skb->pkt_type == PACKET_LOOPBACK ||
 	    arp->ar_pln != 4)
diff -u --recursive --new-file v2.1.124/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v2.1.124/linux/net/ipv4/ip_fw.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ip_fw.c	Wed Oct  7 19:09:56 1998
@@ -1325,7 +1325,7 @@
 
 	case IP_FW_MASQ_TIMEOUTS: {
 #ifdef CONFIG_IP_MASQUERADE
-		return ip_fw_masq_timeouts(m, len);
+		ret = ip_fw_masq_timeouts(m, len);
 #else
 		ret = EINVAL;
 #endif
diff -u --recursive --new-file v2.1.124/linux/net/ipv4/ip_masq_cuseeme.c linux/net/ipv4/ip_masq_cuseeme.c
--- v2.1.124/linux/net/ipv4/ip_masq_cuseeme.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ip_masq_cuseeme.c	Mon Oct  5 12:28:08 1998
@@ -39,6 +39,7 @@
  *	
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <asm/system.h>
 #include <linux/types.h>
diff -u --recursive --new-file v2.1.124/linux/net/ipv4/ip_masq_ftp.c linux/net/ipv4/ip_masq_ftp.c
--- v2.1.124/linux/net/ipv4/ip_masq_ftp.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ip_masq_ftp.c	Mon Oct  5 12:28:09 1998
@@ -37,6 +37,7 @@
  *	
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <asm/system.h>
 #include <linux/types.h>
diff -u --recursive --new-file v2.1.124/linux/net/ipv4/ip_masq_irc.c linux/net/ipv4/ip_masq_irc.c
--- v2.1.124/linux/net/ipv4/ip_masq_irc.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ip_masq_irc.c	Mon Oct  5 12:28:09 1998
@@ -40,6 +40,7 @@
  *	
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/types.h>
diff -u --recursive --new-file v2.1.124/linux/net/ipv4/ip_masq_raudio.c linux/net/ipv4/ip_masq_raudio.c
--- v2.1.124/linux/net/ipv4/ip_masq_raudio.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ip_masq_raudio.c	Mon Oct  5 12:28:09 1998
@@ -62,6 +62,7 @@
  *	
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <asm/system.h>
 #include <linux/types.h>
diff -u --recursive --new-file v2.1.124/linux/net/ipv4/ip_masq_vdolive.c linux/net/ipv4/ip_masq_vdolive.c
--- v2.1.124/linux/net/ipv4/ip_masq_vdolive.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ip_masq_vdolive.c	Mon Oct  5 12:28:09 1998
@@ -22,6 +22,7 @@
  *	
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.124/linux/net/ipv4/ip_nat_dumb.c linux/net/ipv4/ip_nat_dumb.c
--- v2.1.124/linux/net/ipv4/ip_nat_dumb.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ip_nat_dumb.c	Mon Oct  5 12:28:09 1998
@@ -24,6 +24,7 @@
  * NOTE:	It is just working model of real NAT.
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/mm.h>
 #include <linux/sched.h>
diff -u --recursive --new-file v2.1.124/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.124/linux/net/netsyms.c	Mon Oct  5 13:13:49 1998
+++ linux/net/netsyms.c	Wed Oct  7 15:54:44 1998
@@ -62,7 +62,8 @@
 	defined(CONFIG_EL2)	||	defined(CONFIG_NE2000)		|| \
 	defined(CONFIG_E2100)	||	defined(CONFIG_HPLAN_PLUS)	|| \
 	defined(CONFIG_HPLAN)	||	defined(CONFIG_AC3200)		|| \
-	defined(CONFIG_ES3210)
+	defined(CONFIG_ES3210)	||	defined(CONFIG_ULTRA32)		|| \
+	defined(CONFIG_LNE390)	||	defined(CONFIG_NE3210)
 #include "../drivers/net/8390.h"
 #endif
 
@@ -367,7 +368,8 @@
 	defined(CONFIG_EL2)	||	defined(CONFIG_NE2000)		|| \
 	defined(CONFIG_E2100)	||	defined(CONFIG_HPLAN_PLUS)	|| \
 	defined(CONFIG_HPLAN)	||	defined(CONFIG_AC3200)		|| \
-	defined(CONFIG_ES3210)
+	defined(CONFIG_ES3210)	||	defined(CONFIG_ULTRA32)		|| \
+	defined(CONFIG_LNE390)	||	defined(CONFIG_NE3210)
 /* If 8390 NIC support is built in, we will need these. */
 EXPORT_SYMBOL(ei_open);
 EXPORT_SYMBOL(ei_close);
diff -u --recursive --new-file v2.1.124/linux/net/rose/af_rose.c linux/net/rose/af_rose.c
--- v2.1.124/linux/net/rose/af_rose.c	Sat Sep  5 16:46:42 1998
+++ linux/net/rose/af_rose.c	Wed Oct  7 15:52:55 1998
@@ -57,7 +57,7 @@
 #include <linux/if_arp.h>
 #include <linux/init.h>
 
-int rose_ndevs = 6;
+int rose_ndevs = 10;
 
 int sysctl_rose_restart_request_timeout = ROSE_DEFAULT_T0;
 int sysctl_rose_call_request_timeout    = ROSE_DEFAULT_T1;
@@ -524,7 +524,7 @@
 		sk->protinfo.rose->dest_ndigis = 0;
 		memset(&sk->protinfo.rose->dest_addr, '\0', ROSE_ADDR_LEN);
 		memset(&sk->protinfo.rose->dest_call, '\0', AX25_ADDR_LEN);
-		memset(&sk->protinfo.rose->dest_digi, '\0', AX25_ADDR_LEN);
+		memset(sk->protinfo.rose->dest_digis, '\0', AX25_ADDR_LEN*ROSE_MAX_DIGIS);
 		sk->max_ack_backlog = backlog;
 		sk->state           = TCP_LISTEN;
 		return 0;
@@ -549,6 +549,10 @@
 	sock_init_data(sock, sk);
 	
 	skb_queue_head_init(&rose->ack_queue);
+#ifdef M_BIT
+	skb_queue_head_init(&rose->frag_queue);
+	rose->fraglen    = 0;
+#endif
 
 	sock->ops    = &rose_proto_ops;
 	sk->protocol = protocol;
@@ -583,6 +587,10 @@
 	sock_init_data(NULL, sk);
 
 	skb_queue_head_init(&rose->ack_queue);
+#ifdef M_BIT
+	skb_queue_head_init(&rose->frag_queue);
+	rose->fraglen  = 0;
+#endif
 
 	sk->type     = osk->type;
 	sk->socket   = osk->socket;
@@ -662,16 +670,23 @@
 	struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr;
 	struct device *dev;
 	ax25_address *user, *source;
+	int n;
 
 	if (sk->zapped == 0)
 		return -EINVAL;
 
-	if (addr_len != sizeof(struct sockaddr_rose))
+	if (addr_len != sizeof(struct sockaddr_rose) && addr_len != sizeof(struct full_sockaddr_rose))
 		return -EINVAL;
 
 	if (addr->srose_family != AF_ROSE)
 		return -EINVAL;
 
+	if (addr_len == sizeof(struct sockaddr_rose) && addr->srose_ndigis > 1)
+		return -EINVAL;
+
+	if (addr->srose_ndigis > ROSE_MAX_DIGIS)
+		return -EINVAL;
+
 	if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) {
 		SOCK_DEBUG(sk, "ROSE: bind failed: invalid address\n");
 		return -EADDRNOTAVAIL;
@@ -685,13 +700,19 @@
 		user = source;
 	}
 
-	sk->protinfo.rose->source_addr = addr->srose_addr;
-	sk->protinfo.rose->source_call = *user;
-	sk->protinfo.rose->device      = dev;
-
-	if (addr->srose_ndigis == 1) {
-		sk->protinfo.rose->source_ndigis = 1;
-		sk->protinfo.rose->source_digi   = addr->srose_digi;
+	sk->protinfo.rose->source_addr   = addr->srose_addr;
+	sk->protinfo.rose->source_call   = *user;
+	sk->protinfo.rose->device        = dev;
+	sk->protinfo.rose->source_ndigis = addr->srose_ndigis;
+
+	if (addr_len == sizeof(struct full_sockaddr_rose)) {
+		struct full_sockaddr_rose *full_addr = (struct full_sockaddr_rose *)uaddr;
+		for (n = 0 ; n < addr->srose_ndigis ; n++)
+			sk->protinfo.rose->source_digis[n] = full_addr->srose_digis[n];
+	} else {
+		if (sk->protinfo.rose->source_ndigis == 1) {
+			sk->protinfo.rose->source_digis[0] = addr->srose_digi;
+		}
 	}
 
 	rose_insert_socket(sk);
@@ -708,6 +729,7 @@
 	unsigned char cause, diagnostic;
 	ax25_address *user;
 	struct device *dev;
+	int n;
 
 	if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
 		sock->state = SS_CONNECTED;
@@ -725,12 +747,22 @@
 	sk->state   = TCP_CLOSE;
 	sock->state = SS_UNCONNECTED;
 
-	if (addr_len != sizeof(struct sockaddr_rose))
+	if (addr_len != sizeof(struct sockaddr_rose) && addr_len != sizeof(struct full_sockaddr_rose))
 		return -EINVAL;
 
 	if (addr->srose_family != AF_ROSE)
 		return -EINVAL;
 
+	if (addr_len == sizeof(struct sockaddr_rose) && addr->srose_ndigis > 1)
+		return -EINVAL;
+
+	if (addr->srose_ndigis > ROSE_MAX_DIGIS)
+		return -EINVAL;
+
+	/* Source + Destination digis should not exceed ROSE_MAX_DIGIS */
+	if ((sk->protinfo.rose->source_ndigis + addr->srose_ndigis) > ROSE_MAX_DIGIS)
+		return -EINVAL;
+
 	if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, &diagnostic)) == NULL)
 		return -ENETUNREACH;
 
@@ -753,13 +785,19 @@
 		rose_insert_socket(sk);		/* Finish the bind */
 	}
 
-	sk->protinfo.rose->dest_addr = addr->srose_addr;
-	sk->protinfo.rose->dest_call = addr->srose_call;
-	sk->protinfo.rose->rand      = ((int)sk->protinfo.rose & 0xFFFF) + sk->protinfo.rose->lci;
-
-	if (addr->srose_ndigis == 1) {
-		sk->protinfo.rose->dest_ndigis = 1;
-		sk->protinfo.rose->dest_digi   = addr->srose_digi;
+	sk->protinfo.rose->dest_addr   = addr->srose_addr;
+	sk->protinfo.rose->dest_call   = addr->srose_call;
+	sk->protinfo.rose->rand        = ((int)sk->protinfo.rose & 0xFFFF) + sk->protinfo.rose->lci;
+	sk->protinfo.rose->dest_ndigis = addr->srose_ndigis;
+
+	if (addr_len == sizeof(struct full_sockaddr_rose)) {
+		struct full_sockaddr_rose *full_addr = (struct full_sockaddr_rose *)uaddr;
+		for (n = 0 ; n < addr->srose_ndigis ; n++)
+			sk->protinfo.rose->dest_digis[n] = full_addr->srose_digis[n];
+	} else {
+		if (sk->protinfo.rose->dest_ndigis == 1) {
+			sk->protinfo.rose->dest_digis[0] = addr->srose_digi;
+		}
 	}
 
 	/* Move to connecting socket, start sending Connect Requests */
@@ -863,6 +901,7 @@
 {
 	struct sockaddr_rose *srose = (struct sockaddr_rose *)uaddr;
 	struct sock *sk = sock->sk;
+	int n;
 
 	if (peer != 0) {
 		if (sk->state != TCP_ESTABLISHED)
@@ -871,21 +910,37 @@
 		srose->srose_ndigis = 0;
 		srose->srose_addr   = sk->protinfo.rose->dest_addr;
 		srose->srose_call   = sk->protinfo.rose->dest_call;
-		if (sk->protinfo.rose->dest_ndigis == 1) {
-			srose->srose_ndigis = 1;
-			srose->srose_digi   = sk->protinfo.rose->dest_digi;
+		srose->srose_ndigis = sk->protinfo.rose->dest_ndigis;
+		if (*uaddr_len >= sizeof(struct full_sockaddr_rose)) {
+			struct full_sockaddr_rose *full_srose = (struct full_sockaddr_rose *)uaddr;
+			for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++)
+				full_srose->srose_digis[n] = sk->protinfo.rose->dest_digis[n];
+			*uaddr_len = sizeof(struct full_sockaddr_rose);
+		} else {
+			if (sk->protinfo.rose->dest_ndigis >= 1) {
+				srose->srose_ndigis = 1;
+				srose->srose_digi = sk->protinfo.rose->dest_digis[0];
+			}
+			*uaddr_len = sizeof(struct sockaddr_rose);
 		}
-		*uaddr_len = sizeof(struct sockaddr_rose);
 	} else {
 		srose->srose_family = AF_ROSE;
 		srose->srose_ndigis = 0;
 		srose->srose_addr   = sk->protinfo.rose->source_addr;
 		srose->srose_call   = sk->protinfo.rose->source_call;
-		if (sk->protinfo.rose->source_ndigis == 1) {
-			srose->srose_ndigis = 1;
-			srose->srose_digi   = sk->protinfo.rose->source_digi;
+		srose->srose_ndigis = sk->protinfo.rose->source_ndigis;
+		if (*uaddr_len >= sizeof(struct full_sockaddr_rose)) {
+			struct full_sockaddr_rose *full_srose = (struct full_sockaddr_rose *)uaddr;
+			for (n = 0 ; n < sk->protinfo.rose->source_ndigis ; n++)
+				full_srose->srose_digis[n] = sk->protinfo.rose->source_digis[n];
+			*uaddr_len = sizeof(struct full_sockaddr_rose);
+		} else {
+			if (sk->protinfo.rose->source_ndigis >= 1) {
+				srose->srose_ndigis = 1;
+				srose->srose_digi = sk->protinfo.rose->source_digis[sk->protinfo.rose->source_ndigis-1];
+			}
+			*uaddr_len = sizeof(struct sockaddr_rose);
 		}
-		*uaddr_len = sizeof(struct sockaddr_rose);
 	}
 
 	return 0;
@@ -895,14 +950,19 @@
 {
 	struct sock *sk;
 	struct sock *make;
-	struct rose_facilities facilities;
+	struct rose_facilities_struct facilities;
+	int n, len;
 
 	skb->sk = NULL;		/* Initially we don't know who it's for */
 
 	/*
 	 *	skb->data points to the rose frame start
 	 */
-	if (!rose_parse_facilities(skb, &facilities)) {
+	memset(&facilities, 0x00, sizeof(struct rose_facilities_struct));
+	
+	len  = (((skb->data[3] >> 4) & 0x0F) + 1) / 2;
+	len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2;
+	if (!rose_parse_facilities(skb->data + len + 4, &facilities)) {
 		rose_transmit_clear_request(neigh, lci, ROSE_INVALID_FACILITY, 76);
 		return 0;
 	}
@@ -924,13 +984,16 @@
 	make->protinfo.rose->dest_addr     = facilities.dest_addr;
 	make->protinfo.rose->dest_call     = facilities.dest_call;
 	make->protinfo.rose->dest_ndigis   = facilities.dest_ndigis;
-	make->protinfo.rose->dest_digi     = facilities.dest_digi;
+	for (n = 0 ; n < facilities.dest_ndigis ; n++)
+		make->protinfo.rose->dest_digis[n] = facilities.dest_digis[n];
 	make->protinfo.rose->source_addr   = facilities.source_addr;
 	make->protinfo.rose->source_call   = facilities.source_call;
 	make->protinfo.rose->source_ndigis = facilities.source_ndigis;
-	make->protinfo.rose->source_digi   = facilities.source_digi;
+	for (n = 0 ; n < facilities.source_ndigis ; n++)
+		make->protinfo.rose->source_digis[n]= facilities.source_digis[n];
 	make->protinfo.rose->neighbour     = neigh;
 	make->protinfo.rose->device        = dev;
+	make->protinfo.rose->facilities    = facilities;
 
 	make->protinfo.rose->neighbour->use++;
 
@@ -968,10 +1031,10 @@
 	struct sock *sk = sock->sk;
 	struct sockaddr_rose *usrose = (struct sockaddr_rose *)msg->msg_name;
 	int err;
-	struct sockaddr_rose srose;
+	struct full_sockaddr_rose srose;
 	struct sk_buff *skb;
 	unsigned char *asmptr;
-	int size, qbit = 0;
+	int n, size, qbit = 0;
 
 	if (msg->msg_flags & ~MSG_DONTWAIT)
 		return -EINVAL;
@@ -988,15 +1051,19 @@
 		return -ENETUNREACH;
 
 	if (usrose != NULL) {
-		if (msg->msg_namelen < sizeof(srose))
+		if (msg->msg_namelen != sizeof(struct sockaddr_rose) && msg->msg_namelen != sizeof(struct full_sockaddr_rose))
 			return -EINVAL;
-		srose = *usrose;
+		memset(&srose, 0, sizeof(struct full_sockaddr_rose));
+		memcpy(&srose, usrose, msg->msg_namelen);
 		if (rosecmp(&sk->protinfo.rose->dest_addr, &srose.srose_addr) != 0 ||
 		    ax25cmp(&sk->protinfo.rose->dest_call, &srose.srose_call) != 0)
 			return -EISCONN;
-		if (srose.srose_ndigis == 1 && sk->protinfo.rose->dest_ndigis == 1) {
-			if (ax25cmp(&sk->protinfo.rose->dest_digi, &srose.srose_digi) != 0)
-				return -EISCONN;
+		if (srose.srose_ndigis != sk->protinfo.rose->dest_ndigis)
+			return -EISCONN;
+		if (srose.srose_ndigis == sk->protinfo.rose->dest_ndigis) {
+			for (n = 0 ; n < srose.srose_ndigis ; n++)
+				if (ax25cmp(&sk->protinfo.rose->dest_digis[n], &srose.srose_digis[n]) != 0)
+					return -EISCONN;
 		}
 		if (srose.srose_family != AF_ROSE)
 			return -EINVAL;
@@ -1007,12 +1074,9 @@
 		srose.srose_family = AF_ROSE;
 		srose.srose_addr   = sk->protinfo.rose->dest_addr;
 		srose.srose_call   = sk->protinfo.rose->dest_call;
-		srose.srose_ndigis = 0;
-
-		if (sk->protinfo.rose->dest_ndigis == 1) {
-			srose.srose_ndigis = 1;
-			srose.srose_digi   = sk->protinfo.rose->dest_digi;
-		}
+		srose.srose_ndigis = sk->protinfo.rose->dest_ndigis;
+		for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++)
+			srose.srose_digis[n] = sk->protinfo.rose->dest_digis[n];
 	}
 
 	SOCK_DEBUG(sk, "ROSE: sendto: Addresses built.\n");
@@ -1068,7 +1132,54 @@
 		return -ENOTCONN;
 	}
 
+#ifdef M_BIT
+#define ROSE_PACLEN (256-ROSE_MIN_LEN)
+	if (skb->len - ROSE_MIN_LEN > ROSE_PACLEN) {
+		unsigned char header[ROSE_MIN_LEN];
+		struct sk_buff *skbn;
+		int frontlen;
+		int lg;
+		
+		/* Save a copy of the Header */
+		memcpy(header, skb->data, ROSE_MIN_LEN);
+		skb_pull(skb, ROSE_MIN_LEN);
+
+		frontlen = skb_headroom(skb);
+
+		while (skb->len > 0) {
+			if ((skbn = sock_alloc_send_skb(sk, frontlen + ROSE_PACLEN, 0, 0, &err)) == NULL)
+				return err;
+
+			skbn->sk   = sk;
+			skbn->free = 1;
+			skbn->arp  = 1;
+
+			skb_reserve(skbn, frontlen);
+
+			lg = (ROSE_PACLEN > skb->len) ? skb->len : ROSE_PACLEN;
+
+			/* Copy the user data */
+			memcpy(skb_put(skbn, lg), skb->data, lg);
+			skb_pull(skb, lg);
+
+			/* Duplicate the Header */
+			skb_push(skbn, ROSE_MIN_LEN);
+			memcpy(skbn->data, header, ROSE_MIN_LEN);
+
+			if (skb->len > 0)
+				skbn->data[2] |= M_BIT;
+		
+			skb_queue_tail(&sk->write_queue, skbn); /* Throw it on the queue */
+		}
+		
+		skb->free = 1;
+		kfree_skb(skb, FREE_WRITE);
+	} else {
+		skb_queue_tail(&sk->write_queue, skb);		/* Throw it on the queue */
+	}
+#else
 	skb_queue_tail(&sk->write_queue, skb);	/* Shove it onto the queue */
+#endif
 
 	rose_kick(sk);
 
@@ -1084,7 +1195,7 @@
 	int copied, qbit;
 	unsigned char *asmptr;
 	struct sk_buff *skb;
-	int er;
+	int n, er;
 
 	/*
 	 * This works for seqpacket too. The receiver has ordered the queue for
@@ -1120,16 +1231,21 @@
 		srose->srose_family = AF_ROSE;
 		srose->srose_addr   = sk->protinfo.rose->dest_addr;
 		srose->srose_call   = sk->protinfo.rose->dest_call;
-		srose->srose_ndigis = 0;
-
-		if (sk->protinfo.rose->dest_ndigis == 1) {
-			srose->srose_ndigis = 1;
-			srose->srose_digi   = sk->protinfo.rose->dest_digi;
+		srose->srose_ndigis = sk->protinfo.rose->dest_ndigis;
+		if (msg->msg_namelen >= sizeof(struct full_sockaddr_rose)) {
+			struct full_sockaddr_rose *full_srose = (struct full_sockaddr_rose *)msg->msg_name;
+			for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++)
+				full_srose->srose_digis[n] = sk->protinfo.rose->dest_digis[n];
+			msg->msg_namelen = sizeof(struct full_sockaddr_rose);
+		} else {
+			if (sk->protinfo.rose->dest_ndigis >= 1) {
+				srose->srose_ndigis = 1;
+				srose->srose_digi = sk->protinfo.rose->dest_digis[0];
+			}
+			msg->msg_namelen = sizeof(struct sockaddr_rose);
 		}
 	}
 
-	msg->msg_namelen = sizeof(struct sockaddr_rose);
-
 	skb_free_datagram(sk, skb);
 
 	return copied;
@@ -1259,7 +1375,7 @@
 
 	cli();
 
-	len += sprintf(buffer, "dest_addr  dest_call src_addr   src_call  dev   lci st vs vr va   t  t1  t2  t3  hb    idle Snd-Q Rcv-Q inode\n");
+	len += sprintf(buffer, "dest_addr  dest_call src_addr   src_call  dev   lci neigh st vs vr va   t  t1  t2  t3  hb    idle Snd-Q Rcv-Q inode\n");
 
 	for (s = rose_list; s != NULL; s = s->next) {
 		if ((dev = s->protinfo.rose->device) == NULL)
@@ -1276,11 +1392,12 @@
 		else
 			callsign = ax2asc(&s->protinfo.rose->source_call);
 
-		len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X  %d  %d  %d  %d %3lu %3lu %3lu %3lu %3lu %3lu/%03lu %5d %5d %ld\n",
+		len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X %05d  %d  %d  %d  %d %3lu %3lu %3lu %3lu %3lu %3lu/%03lu %5d %5d %ld\n",
 			rose2asc(&s->protinfo.rose->source_addr),
 			callsign,
 			devname, 
 			s->protinfo.rose->lci & 0x0FFF,
+			(s->protinfo.rose->neighbour) ? s->protinfo.rose->neighbour->number : 0,
 			s->protinfo.rose->state,
 			s->protinfo.rose->vs,
 			s->protinfo.rose->vr,
@@ -1399,7 +1516,7 @@
 
 	sock_register(&rose_family_ops);
 	register_netdevice_notifier(&rose_dev_notifier);
-	printk(KERN_INFO "G4KLX ROSE for Linux. Version 0.3 for AX25.037 Linux 2.1\n");
+	printk(KERN_INFO "F6FBB/G4KLX ROSE for Linux. Version 0.62 for AX25.037 Linux 2.1\n");
 
 	ax25_protocol_register(AX25_P_ROSE, rose_route_frame);
 	ax25_linkfail_register(rose_link_failed);
diff -u --recursive --new-file v2.1.124/linux/net/rose/rose_loopback.c linux/net/rose/rose_loopback.c
--- v2.1.124/linux/net/rose/rose_loopback.c	Tue Apr 14 14:29:27 1998
+++ linux/net/rose/rose_loopback.c	Wed Oct  7 15:52:55 1998
@@ -84,7 +84,7 @@
 		lci_i     = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF);
 		frametype = skb->data[2];
 		dest      = (rose_address *)(skb->data + 4);
-		lci_o     = sysctl_rose_maximum_vcs - lci_i + 1;
+		lci_o     = 0xFFF - lci_i;
 
 		skb->h.raw = skb->data;
 
diff -u --recursive --new-file v2.1.124/linux/net/rose/rose_route.c linux/net/rose/rose_route.c
--- v2.1.124/linux/net/rose/rose_route.c	Tue Apr 14 14:29:27 1998
+++ linux/net/rose/rose_route.c	Wed Oct  7 15:52:55 1998
@@ -65,7 +65,7 @@
  */
 static int rose_add_node(struct rose_route_struct *rose_route, struct device *dev)
 {
-	struct rose_node  *rose_node;
+	struct rose_node  *rose_node, *rose_tmpn, *rose_tmpp;
 	struct rose_neigh *rose_neigh;
 	unsigned long flags;
 	int i;
@@ -122,7 +122,27 @@
 		restore_flags(flags);
 	}
 
+	/*
+	 * This is a new node to be inserted into the list. Find where it needs
+	 * to be inserted into the list, and insert it. We want to be sure
+	 * to order the list in descending order of mask size to ensure that
+	 * later when we are searching this list the first match will be the
+	 * best match.
+	 */
 	if (rose_node == NULL) {
+		rose_tmpn = rose_node_list;
+		rose_tmpp = NULL;
+
+		while (rose_tmpn != NULL) {
+			if (rose_tmpn->mask > rose_route->mask) {
+				rose_tmpp = rose_tmpn;
+				rose_tmpn = rose_tmpn->next;
+			} else {
+				break;
+			}
+		}
+
+		/* create new node */
 		if ((rose_node = kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL)
 			return -ENOMEM;
 
@@ -133,8 +153,25 @@
 		rose_node->neighbour[0] = rose_neigh;
 
 		save_flags(flags); cli();
-		rose_node->next = rose_node_list;
-		rose_node_list  = rose_node;
+
+		if (rose_tmpn == NULL) {
+			if (rose_tmpp == NULL) {	/* Empty list */
+				rose_node_list  = rose_node;
+				rose_node->next = NULL;
+			} else {
+				rose_tmpp->next = rose_node;
+				rose_node->next = NULL;
+			}
+		} else {
+			if (rose_tmpp == NULL) {	/* 1st node */
+				rose_node->next = rose_node_list;
+				rose_node_list  = rose_node;
+			} else {
+				rose_tmpp->next = rose_node;
+				rose_node->next = rose_tmpn;
+			}
+		}
+
 		restore_flags(flags);
 
 		rose_neigh->count++;
@@ -328,6 +365,11 @@
 	rose_loopback_neigh->number    = rose_neigh_no++;
 	rose_loopback_neigh->restarted = 1;
 
+	skb_queue_head_init(&rose_loopback_neigh->queue);
+
+	init_timer(&rose_loopback_neigh->ftimer);
+	init_timer(&rose_loopback_neigh->t0timer);
+
 	save_flags(flags); cli();
 	rose_loopback_neigh->next = rose_neigh_list;
 	rose_neigh_list           = rose_loopback_neigh;
@@ -349,7 +391,7 @@
 			break;
 
 	if (rose_node != NULL) return 0;
-
+	
 	if ((rose_node = kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL)
 		return -ENOMEM;
 
@@ -359,6 +401,7 @@
 	rose_node->loopback     = 1;
 	rose_node->neighbour[0] = rose_loopback_neigh;
 
+	/* Insert at the head of list. Address is always mask=10 */
 	save_flags(flags); cli();
 	rose_node->next = rose_node_list;
 	rose_node_list  = rose_node;
@@ -450,6 +493,7 @@
 /*
  *	Clear all nodes and neighbours out, except for neighbours with
  *	active connections going through them.
+ *  Do not clear loopback neighbour and nodes.
  */
 static int rose_clear_routes(void)
 {
@@ -459,18 +503,18 @@
 	while (rose_node != NULL) {
 		t         = rose_node;
 		rose_node = rose_node->next;
-
-		rose_remove_node(t);
+		if (!t->loopback)
+			rose_remove_node(t);
 	}
 
 	while (rose_neigh != NULL) {
 		s          = rose_neigh;
 		rose_neigh = rose_neigh->next;
 
-		s->count = 0;
-
-		if (s->use == 0)
+		if (s->use == 0 && !s->loopback) {
+			s->count = 0;
 			rose_remove_neigh(s);
+		}
 	}
 
 	return 0;
@@ -539,29 +583,21 @@
 struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, unsigned char *diagnostic)
 {
 	struct rose_node *node;
-	struct rose_neigh *neigh;
 	int failed = 0;
-	int mask   = 0;
 	int i;
 
-	for (neigh = NULL, node = rose_node_list; node != NULL; node = node->next) {
+	for (node = rose_node_list; node != NULL; node = node->next) {
 		if (rosecmpm(addr, &node->address, node->mask) == 0) {
-			if (node->mask > mask) {
-				mask = node->mask;
-
-				for (i = 0; i < node->count; i++) {
-					if (!rose_ftimer_running(node->neighbour[i]))
-						neigh = node->neighbour[i];
-					else
-						failed = 1;
-				}
+			for (i = 0; i < node->count; i++) {
+				if (!rose_ftimer_running(node->neighbour[i])) {
+					return node->neighbour[i]; }
+				else
+					failed = 1;
 			}
+			break;
 		}
 	}
 
-	if (neigh != NULL)
-		return neigh;
-
 	if (failed) {
 		*cause      = ROSE_OUT_OF_ORDER;
 		*diagnostic = 0;
@@ -697,7 +733,7 @@
 {
 	struct rose_neigh *rose_neigh, *new_neigh;
 	struct rose_route *rose_route;
-	struct rose_facilities facilities;
+	struct rose_facilities_struct facilities;
 	rose_address *src_addr, *dest_addr;
 	struct sock *sk;
 	unsigned short frametype;
@@ -705,6 +741,7 @@
 	unsigned char cause, diagnostic;
 	struct device *dev;
 	unsigned long flags;
+	int len;
 
 	if (call_in_firewall(PF_ROSE, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
 		return 0;
@@ -718,8 +755,10 @@
 		if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->ax25_dev->dev == rose_neigh->dev)
 			break;
 
-	if (rose_neigh == NULL)
+	if (rose_neigh == NULL) {
+		printk("rose_route : unknown neighbour or device %s\n", ax2asc(&ax25->dest_addr));
 		return 0;
+	}
 
 	/*
 	 *	Obviously the link is working, halt the ftimer.
@@ -739,8 +778,26 @@
 	 *	Find an existing socket.
 	 */
 	if ((sk = rose_find_socket(lci, rose_neigh)) != NULL) {
-		skb->h.raw = skb->data;
-		return rose_process_rx_frame(sk, skb);
+		if (frametype == ROSE_CALL_REQUEST) {
+			/* Remove an existing unused socket */
+			rose_clear_queues(sk);
+			sk->protinfo.rose->cause      = ROSE_NETWORK_CONGESTION;
+			sk->protinfo.rose->diagnostic = 0;
+			sk->protinfo.rose->neighbour->use--;
+			sk->protinfo.rose->neighbour = NULL;
+			sk->protinfo.rose->lci   = 0;
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = 0;
+			sk->shutdown            |= SEND_SHUTDOWN;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+		}
+		else {
+			skb->h.raw = skb->data;
+			return rose_process_rx_frame(sk, skb);
+		}
 	}
 
 	/*
@@ -760,7 +817,11 @@
 	 */
 	for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) {
 		if (rose_route->lci1 == lci && rose_route->neigh1 == rose_neigh) {
-			if (rose_route->neigh2 != NULL) {
+			if (frametype == ROSE_CALL_REQUEST) {
+				/* F6FBB - Remove an existing unused route */
+				rose_remove_route(rose_route);
+				break;
+			} else if (rose_route->neigh2 != NULL) {
 				skb->data[0] &= 0xF0;
 				skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F;
 				skb->data[1]  = (rose_route->lci2 >> 0) & 0xFF;
@@ -775,7 +836,11 @@
 			}
 		}
 		if (rose_route->lci2 == lci && rose_route->neigh2 == rose_neigh) {
-			if (rose_route->neigh1 != NULL) {
+			if (frametype == ROSE_CALL_REQUEST) {
+				/* F6FBB - Remove an existing unused route */
+				rose_remove_route(rose_route);
+				break;
+			} else if (rose_route->neigh1 != NULL) {
 				skb->data[0] &= 0xF0;
 				skb->data[0] |= (rose_route->lci1 >> 8) & 0x0F;
 				skb->data[1]  = (rose_route->lci1 >> 0) & 0xFF;
@@ -799,7 +864,12 @@
 	if (frametype != ROSE_CALL_REQUEST)	/* XXX */
 		return 0;
 
-	if (!rose_parse_facilities(skb, &facilities)) {
+	len  = (((skb->data[3] >> 4) & 0x0F) + 1) / 2;
+	len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2;
+
+	memset(&facilities, 0x00, sizeof(struct rose_facilities_struct));
+	
+	if (!rose_parse_facilities(skb->data + len + 4, &facilities)) {
 		rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76);
 		return 0;
 	}
@@ -873,11 +943,11 @@
 	len += sprintf(buffer, "address    mask n neigh neigh neigh\n");
 
 	for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) {
-		if (rose_node->loopback) {
+		/* if (rose_node->loopback) {
 			len += sprintf(buffer + len, "%-10s %04d 1 loopback\n",
 				rose2asc(&rose_node->address),
 				rose_node->mask);
-		} else {
+		} else { */
 			len += sprintf(buffer + len, "%-10s %04d %d",
 				rose2asc(&rose_node->address),
 				rose_node->mask,
@@ -888,7 +958,7 @@
 					rose_node->neighbour[i]->number);
 
 			len += sprintf(buffer + len, "\n");
-		}
+		/* } */
 
 		pos = begin + len;
 
@@ -925,10 +995,10 @@
 	len += sprintf(buffer, "addr  callsign  dev  count use mode restart  t0  tf digipeaters\n");
 
 	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) {
-		if (!rose_neigh->loopback) {
+		/* if (!rose_neigh->loopback) { */
 			len += sprintf(buffer + len, "%05d %-9s %-4s   %3d %3d  %3s     %3s %3lu %3lu",
 				rose_neigh->number,
-				ax2asc(&rose_neigh->callsign),
+				(rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(&rose_neigh->callsign),
 				rose_neigh->dev ? rose_neigh->dev->name : "???",
 				rose_neigh->count,
 				rose_neigh->use,
@@ -953,7 +1023,7 @@
 
 			if (pos > offset + length)
 				break;
-		}
+		/* } */
 	}
 
 	sti();
diff -u --recursive --new-file v2.1.124/linux/net/rose/rose_subr.c linux/net/rose/rose_subr.c
--- v2.1.124/linux/net/rose/rose_subr.c	Thu Feb 12 20:56:15 1998
+++ linux/net/rose/rose_subr.c	Wed Oct  7 15:52:55 1998
@@ -251,9 +251,11 @@
 	return ROSE_ILLEGAL;
 }
 
-static int rose_parse_national(unsigned char *p, struct rose_facilities *facilities, int len)
+static int rose_parse_national(unsigned char *p, struct rose_facilities_struct *facilities, int len)
 {
-	unsigned char l, n = 0;
+	unsigned char *pt;
+	unsigned char l, lg, n = 0;
+	int fac_national_digis_received = 0;
 
 	do {
 		switch (*p & 0xC0) {
@@ -280,12 +282,33 @@
 			case 0xC0:
 				l = p[1];
 				if (*p == FAC_NATIONAL_DEST_DIGI) {
-					memcpy(&facilities->source_digi, p + 2, AX25_ADDR_LEN);
-					facilities->source_ndigis = 1;
+					if (!fac_national_digis_received) {
+						memcpy(&facilities->source_digis[0], p + 2, AX25_ADDR_LEN);
+						facilities->source_ndigis = 1;
+					}
+				}
+				else if (*p == FAC_NATIONAL_SRC_DIGI) {
+					if (!fac_national_digis_received) {
+						memcpy(&facilities->dest_digis[0], p + 2, AX25_ADDR_LEN);
+						facilities->dest_ndigis = 1;
+					}
+				}
+				else if (*p == FAC_NATIONAL_FAIL_CALL) {
+					memcpy(&facilities->fail_call, p + 2, AX25_ADDR_LEN);
+				}
+				else if (*p == FAC_NATIONAL_FAIL_ADD) {
+					memcpy(&facilities->fail_addr, p + 3, ROSE_ADDR_LEN);
 				}
-				if (*p == FAC_NATIONAL_SRC_DIGI) {
-					memcpy(&facilities->dest_digi,   p + 2, AX25_ADDR_LEN);
-					facilities->dest_ndigis = 1;
+				else if (*p == FAC_NATIONAL_DIGIS) {
+					fac_national_digis_received = 1;
+					facilities->source_ndigis = 0;
+					facilities->dest_ndigis   = 0;
+					for (pt = p + 2, lg = 0 ; lg < l ; pt += AX25_ADDR_LEN, lg += AX25_ADDR_LEN) {
+						if (pt[6] & AX25_HBIT)
+							memcpy(&facilities->dest_digis[facilities->dest_ndigis++], pt, AX25_ADDR_LEN);
+						else
+							memcpy(&facilities->source_digis[facilities->source_ndigis++], pt, AX25_ADDR_LEN);
+					}
 				}
 				p   += l + 2;
 				n   += l + 2;
@@ -297,7 +320,7 @@
 	return n;
 }
 
-static int rose_parse_ccitt(unsigned char *p, struct rose_facilities *facilities, int len)
+static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *facilities, int len)
 {
 	unsigned char l, n = 0;
 	char callsign[11];
@@ -346,17 +369,9 @@
 	return n;
 }
 
-int rose_parse_facilities(struct sk_buff *skb, struct rose_facilities *facilities)
+int rose_parse_facilities(unsigned char *p, struct rose_facilities_struct *facilities)
 {
 	int facilities_len, len;
-	unsigned char *p;
-
-	memset(facilities, 0x00, sizeof(struct rose_facilities));
-
-	len  = (((skb->data[3] >> 4) & 0x0F) + 1) / 2;
-	len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2;
-
-	p = skb->data + len + 4;
 
 	facilities_len = *p++;
 
@@ -388,6 +403,7 @@
 					break;
 			}
 		}
+		else break;	/* Error in facilities format */
 	}
 
 	return 1;
@@ -397,7 +413,7 @@
 {
 	unsigned char *p = buffer + 1;
 	char *callsign;
-	int len;
+	int len, nb;
 
 	/* National Facilities */
 	if (rose->rand != 0 || rose->source_ndigis == 1 || rose->dest_ndigis == 1) {
@@ -410,17 +426,40 @@
 			*p++ = (rose->rand >> 0) & 0xFF;
 		}
 
-		if (rose->source_ndigis == 1) {
+		/* Sent before older facilities */
+		if ((rose->source_ndigis > 0) || (rose->dest_ndigis > 0)) {
+			int maxdigi = 0;
+			*p++ = FAC_NATIONAL_DIGIS;
+			*p++ = AX25_ADDR_LEN * (rose->source_ndigis + rose->dest_ndigis);
+			for (nb = 0 ; nb < rose->source_ndigis ; nb++) {
+				if (++maxdigi >= ROSE_MAX_DIGIS)
+					break;
+				memcpy(p, &rose->source_digis[nb], AX25_ADDR_LEN);
+				p[6] |= AX25_HBIT;
+				p += AX25_ADDR_LEN;
+			}
+			for (nb = 0 ; nb < rose->dest_ndigis ; nb++) {
+				if (++maxdigi >= ROSE_MAX_DIGIS)
+					break;
+				memcpy(p, &rose->dest_digis[nb], AX25_ADDR_LEN);
+				p[6] &= ~AX25_HBIT;
+				p += AX25_ADDR_LEN;
+			}
+		}
+
+		/* For compatibility */
+		if (rose->source_ndigis > 0) {
 			*p++ = FAC_NATIONAL_SRC_DIGI;
 			*p++ = AX25_ADDR_LEN;
-			memcpy(p, &rose->source_digi, AX25_ADDR_LEN);
+			memcpy(p, &rose->source_digis[0], AX25_ADDR_LEN);
 			p   += AX25_ADDR_LEN;
 		}
 
-		if (rose->dest_ndigis == 1) {
+		/* For compatibility */
+		if (rose->dest_ndigis > 0) {
 			*p++ = FAC_NATIONAL_DEST_DIGI;
 			*p++ = AX25_ADDR_LEN;
-			memcpy(p, &rose->dest_digi, AX25_ADDR_LEN);
+			memcpy(p, &rose->dest_digis[0], AX25_ADDR_LEN);
 			p   += AX25_ADDR_LEN;
 		}
 	}
diff -u --recursive --new-file v2.1.124/linux/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c
--- v2.1.124/linux/net/sunrpc/clnt.c	Mon Oct  5 13:13:49 1998
+++ linux/net/sunrpc/clnt.c	Tue Oct  6 09:39:44 1998
@@ -383,7 +383,7 @@
 		xprt_reserve(task);
 		goto out;
 	} else if (task->tk_status == -ETIMEDOUT) {
-		printk("RPC: task timed out\n");
+		dprintk("RPC: task timed out\n");
 		task->tk_action = call_timeout;
 		goto out;
 	} else {
diff -u --recursive --new-file v2.1.124/linux/scripts/lxdialog/dialog.h linux/scripts/lxdialog/dialog.h
--- v2.1.124/linux/scripts/lxdialog/dialog.h	Thu Apr 18 04:38:54 1996
+++ linux/scripts/lxdialog/dialog.h	Tue Oct  6 09:39:44 1998
@@ -28,6 +28,24 @@
 
 #include CURSES_LOC
 
+/*
+ * Colors in ncurses 1.9.9e do not work properly since foreground and
+ * background colors are OR'd rather than separately masked.  This version
+ * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible
+ * with standard curses.  The simplest fix (to make this work with standard
+ * curses) uses the wbkgdset() function, not used in the original hack.
+ * Turn it off if we're building with 1.9.9e, since it just confuses things.
+ */
+#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE)
+#define OLD_NCURSES 1
+#undef  wbkgdset
+#define wbkgdset(w,p) /*nothing*/
+#else
+#define OLD_NCURSES 0
+#endif
+
+#define TR(params) _tracef params
+
 #define ESC 27
 #define TAB 9
 #define MAX_LEN 2048
diff -u --recursive --new-file v2.1.124/linux/scripts/lxdialog/lxdialog.c linux/scripts/lxdialog/lxdialog.c
--- v2.1.124/linux/scripts/lxdialog/lxdialog.c	Tue Dec 23 20:25:10 1997
+++ linux/scripts/lxdialog/lxdialog.c	Tue Oct  6 09:39:44 1998
@@ -63,6 +63,9 @@
     (void) setlocale (LC_ALL, "");
 #endif
 
+#ifdef TRACE
+    trace(TRACE_CALLS|TRACE_UPDATE);
+#endif
     if (argc < 2) {
 	Usage (argv[0]);
 	exit (-1);
diff -u --recursive --new-file v2.1.124/linux/scripts/lxdialog/menubox.c linux/scripts/lxdialog/menubox.c
--- v2.1.124/linux/scripts/lxdialog/menubox.c	Tue Jul 21 00:15:33 1998
+++ linux/scripts/lxdialog/menubox.c	Tue Oct  6 09:39:44 1998
@@ -70,8 +70,12 @@
     /* Clear 'residue' of last item */
     wattrset (win, menubox_attr);
     wmove (win, choice, 0);
+#if OLD_NCURSES
     for (i = 0; i < menu_width; i++)
 	waddch (win, ' ');
+#else
+    wclrtoeol(win);
+#endif
     wattrset (win, selected ? item_selected_attr : item_attr);
     mvwaddstr (win, choice, item_x, menu_item);
     if (hotkey) {
@@ -173,6 +177,7 @@
     for (i = 0; i < width - 2; i++)
 	waddch (dialog, ACS_HLINE);
     wattrset (dialog, dialog_attr);
+    wbkgdset (dialog, dialog_attr & A_COLOR);
     waddch (dialog, ACS_RTEE);
 
     if (title != NULL) {
diff -u --recursive --new-file v2.1.124/linux/scripts/lxdialog/textbox.c linux/scripts/lxdialog/textbox.c
--- v2.1.124/linux/scripts/lxdialog/textbox.c	Tue Feb  6 23:47:28 1996
+++ linux/scripts/lxdialog/textbox.c	Tue Oct  6 09:39:44 1998
@@ -90,6 +90,8 @@
 
     /* Create window for text region, used for scrolling text */
     text = subwin (dialog, height - 4, width - 2, y + 1, x + 1);
+    wattrset (text, dialog_attr);
+    wbkgdset (text, dialog_attr & A_COLOR);
 
     keypad (text, TRUE);
 
@@ -101,6 +103,7 @@
     for (i = 0; i < width - 2; i++)
 	waddch (dialog, ACS_HLINE);
     wattrset (dialog, dialog_attr);
+    wbkgdset (dialog, dialog_attr & A_COLOR);
     waddch (dialog, ACS_RTEE);
 
     if (title != NULL) {
@@ -459,8 +462,12 @@
 
     getyx (win, y, x);
     /* Clear 'residue' of previous line */
+#if OLD_NCURSES
     for (i = 0; i < width - x; i++)
 	waddch (win, ' ');
+#else
+    wclrtoeol(win);
+#endif
 }
 
 /*
@@ -530,6 +537,7 @@
 	exit (-1);
     }
     wattrset (win, position_indicator_attr);
+    wbkgdset (win, position_indicator_attr & A_COLOR);
     percent = !file_size ?
 	100 : ((fpos - bytes_read + page - buf) * 100) / file_size;
     wmove (win, height - 3, width - 9);
