diff -u --recursive --new-file v2.1.90/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.90/linux/Documentation/Configure.help	Tue Mar 17 22:18:13 1998
+++ linux/Documentation/Configure.help	Sun Mar 22 21:56:21 1998
@@ -26,35 +26,36 @@
 # compile it and much more is contained in the Kernel-HOWTO, available
 # via ftp (user: anonymous) from sunsite.unc.edu in the directory
 # /pub/Linux/docs/HOWTO. Before you start compiling, make sure that
-# you have the necessary versions of all programs; they are listed
-# in Documentation/Changes.
+# you have the necessary versions of all programs and libraries
+# required to compile and run this kernel; they are listed in the file
+# Documentation/Changes. Make sure to read the toplevel kernel README 
+# file as well.
 #
-# Format of this file: description<nl>variable<nl>helptext<nl><nl>.
-# If the question being documented is of type "choice", we list only
-# the first occurring config variable. The help texts must not contain
+# Format of this file: description<nl>variable<nl>helptext<nl><nl>. If
+# the question being documented is of type "choice", we list only the
+# first occurring config variable. The help texts must not contain
 # empty lines. Order of the help texts does not matter, however, no
 # variable should be documented twice: if it is, only the first
 # occurrence will be used by Configure. It is not absolutely necessary
 # that the one-line descriptions of the variables used here are
-# exactly the same as the ones in the corresponding Config.in
-# scripts. The lines in a help text should be indented two
-# positions. Lines starting with `#' are ignored. To be nice to
-# menuconfig, limit your lines to 70 characters. Use emacs' kfill.el
-# to edit this file or you lose.
+# exactly the same as the ones in the corresponding Config.in scripts.
+# The lines in a help text should be indented two positions. Lines
+# starting with `#' are ignored. To be nice to menuconfig, limit your
+# lines to 70 characters. Use emacs' kfill.el to edit and ispell.el to
+# spell check this file or you lose.
 #
 # If you add a help text to this file, please try to be as gentle as
 # possible. Don't use unexplained acronyms and generally write for the
 # hypothetical ignorant but intelligent user who has just bought a PC,
 # removed Windows, installed Linux and is now recompiling the kernel
-# for the first time. Tell them what to do if they're
-# unsure. Technical information should go in a README in the
-# Documentation directory. Mention all the relevant READMEs and HOWTOs
-# in the help text.
+# for the first time. Tell them what to do if they're unsure. Technical 
+# information should go in a README in the Documentation directory.
+# Mention all the relevant READMEs and HOWTOs in the help text.
 #
 # All this was shamelessly stolen from several different sources. Many
-# thanks to all the contributors.  Feel free to use these help texts
-# in your own kernel configuration tools. The texts are copyrighted
-# (c) 1995-1997 by Axel Boldt and others and governed by the GNU
+# thanks to all the contributors. Feel free to use these help texts in
+# your own kernel configuration tools. The texts are copyrighted (c)
+# 1995-1998 by Axel Boldt and many others and are governed by the GNU
 # Public License.
 
 Prompt for development and/or incomplete code/drivers
@@ -132,7 +133,7 @@
   Saying Y here will allow you to use a portion of your RAM memory as
   a block device, so that you can make filesystems on it, read and
   write to it and do all the other things that you can do with normal
-  block devices (such as harddrives).  It is usually used to load and
+  block devices (such as hard drives).  It is usually used to load and
   store a copy of a minimal root file system off of a floppy into RAM
   during the initial install of Linux.  Note that the kernel command
   line option "ramdisk=XX" is now obsolete.  For details, read
@@ -155,7 +156,7 @@
   Saying Y here will allow you to mount a file as a file system.  This
   is useful if you want to check an ISO9660 file system before burning
   the CD, or want to use floppy images without first writing them to
-  floppy.  This option also allows one to mount a filesystem with
+  floppy.  This option also allows you to mount a filesystem with
   encryption.  To use these features, you need a recent version of
   mount (available via ftp (user: anonymous) from
   ftp.win.tue.nl/pub/linux/util/).  Note that this loop device has
@@ -182,35 +183,38 @@
   This 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 combination of up to eight IDE disk/cdrom/tape/floppy
-  drives.  Useful information about large (>540MB) IDE disks,
-  soundcard IDE ports, module support, and other topics, is 
-  contained in Documentation/ide.txt.  If you have one or more IDE
-  drives, say Y here.  If your system has no IDE drives, or if memory
+  drives. Useful information about large (>540MB) IDE disks, sound
+  card IDE ports, module support, and other topics, is contained in
+  Documentation/ide.txt. For detailed information about hard drives,
+  consult the Disk-HOWTO, available via ftp (user: anonymous) from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you have one or more IDE
+  drives, say Y here. If your system has no IDE drives, or if memory
   requirements are really tight, you could say N here, and select the
-  "Old harddisk driver" instead to save about 13kB of memory in the
-  kernel.  To fine-tune IDE drive/interface parameters for improved
+  "Old hard disk driver" instead to save about 13kB of memory in the
+  kernel. To fine-tune IDE drive/interface parameters for improved
   performance, look for the hdparm package at
   sunsite.unc.edu:/pub/Linux/kernel/patches/diskdrives/
 
-Old harddisk (MFM/RLL/IDE) driver
+Old hard disk (MFM/RLL/IDE) driver
 CONFIG_BLK_DEV_HD_ONLY
-  There are two drivers for MFM/RLL/IDE disks.  Most people use the
+  There are two drivers for MFM/RLL/IDE disks. Most people use the
   newer enhanced driver, but this old one is still around for two
-  reasons.  Some older systems have strange timing problems and seem
-  to work only with the old driver (which itself does not work with
-  some newer systems).  The other reason is that the old driver is
-  smaller, since it lacks the enhanced functionality of the new one.
-  This makes it a good choice for systems with very tight memory
-  restrictions, or for systems with only older MFM/RLL/ESDI drives.
-  Choosing the old driver can save 13kB or so of kernel memory.  If
-  you are unsure, then just choose the Enhanced IDE/MFM/RLL driver
-  instead of this one.
+  reasons. Some older systems have strange timing problems and seem to
+  work only with the old driver (which itself does not work with some
+  newer systems). The other reason is that the old driver is smaller,
+  since it lacks the enhanced functionality of the new one. This makes
+  it a good choice for systems with very tight memory restrictions, or
+  for systems with only older MFM/RLL/ESDI drives. Choosing the old
+  driver can save 13kB or so of kernel memory. If you are unsure, then
+  just choose the Enhanced IDE/MFM/RLL driver instead of this one. For
+  more detailed information, read the Disk-HOWTO, available via ftp
+  (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO.
 
 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
   the new enhanced driver by itself.  This option however installs the
-  old harddisk driver to control the primary IDE/disk interface in the
+  old hard disk driver to control the primary IDE/disk interface in the
   system, leaving the new enhanced IDE driver take care of only the
   2nd/3rd/4th IDE interfaces. Doing this will prevent you from having
   an IDE/ATAPI CDROM or tape drive connected to the primary IDE
@@ -222,9 +226,9 @@
 
 Include IDE/ATA-2 DISK support
 CONFIG_BLK_DEV_IDEDISK
-  This will include enhanced support for MFM/RLL/IDE harddisks. If you
+  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 harddisk driver instead, say Y.  If you want to compile this
+  old hard disk driver instead, say Y.  If you want to compile this
   driver as a module ( = code which can be inserted in and removed
   from the running kernel whenever you want), say M here and read
   Documentation/modules.txt. The module will be called ide-disk.o. Do
@@ -240,11 +244,11 @@
   NEC-260, Mitsumi FX400, Sony 55E, and just about all non-SCSI
   double(2X), quad(4X), and six(6X) speed drives.  At boot time, the
   CDROM drive will be identified along with other IDE devices, as
-  "hdb" or "hdc", or something similar.  If this is your only CDROM
-  drive, you can say N to all other CDROM options, but be sure to say
-  Y to "ISO9660 cdrom filesystem support".  Read the CDROM-HOWTO,
-  available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO and the file
+  "hdb" or "hdc", or something similar (check the boot messages with
+  dmesg).  If this is your only CDROM drive, you can say N to all
+  other CDROM options, but be sure to say Y to "ISO9660 cdrom
+  filesystem support".  Read the CDROM-HOWTO, available via ftp (user:
+  anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO and the file
   Documentation/cdrom/ide-cd.  Note that older versions of lilo (the
   linux boot loader) cannot properly deal with IDE/ATAPI CDROMs, so
   install lilo-16 or higher, available from
@@ -260,11 +264,12 @@
   to the SCSI protocol.  At boot time, the tape drive will be
   identified along with other IDE devices, as "hdb" or "hdc", or
   something similar, and will be mapped to a character device such as
-  "ht0".  Be sure to consult the drivers/block/ide-tape.c and
-  Documentation/ide.txt files for usage information.  If you want to
-  compile the driver as a module ( = code which can be inserted in and
-  removed from the running kernel whenever you want), say M here and
-  read Documentation/modules.txt. The module will be called ide-tape.o.
+  "ht0" (check the boot messages with dmesg).  Be sure to consult the
+  drivers/block/ide-tape.c and Documentation/ide.txt files for usage
+  information.  If you want to compile the driver as a module ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want), say M here and read
+  Documentation/modules.txt. The module will be called ide-tape.o.
 
 Include IDE/ATAPI FLOPPY support
 CONFIG_BLK_DEV_IDEFLOPPY
@@ -274,11 +279,11 @@
   and the ATAPI ZIP (ATAPI PD-CD/CDR drives are not supported by this
   driver; support for PD-CD/CDR drives is available through the SCSI
   emulation). At boot time, the FLOPPY drive will be identified along
-  with other IDE devices, as "hdb" or "hdc", or something similar. If
-  you want to compile the driver as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want),
-  say M here and read Documentation/modules.txt.  The module will be
-  called ide-floppy.o.
+  with other IDE devices, as "hdb" or "hdc", or something similar
+  (check the boot messages with dmesg). If you want to compile the
+  driver as a module ( = code which can be inserted in and removed
+  from the running kernel whenever you want), say M here and read
+  Documentation/modules.txt.  The module will be called ide-floppy.o.
 
 SCSI emulation support
 CONFIG_BLK_DEV_IDESCSI
@@ -293,7 +298,7 @@
 
 CMD640 chipset bugfix/support
 CONFIG_BLK_DEV_CMD640
-  The CMD-Technologies CMD640 chip is used on many common 486 and
+  The CMD-Technologies CMD640 IDE chip is used on many common 486 and
   Pentium motherboards, usually in combination with a "Neptune" or
   "SiS" chipset.  Unfortunately, it has a number of rather nasty
   design flaws that can cause severe data corruption under many common
@@ -303,9 +308,9 @@
   systems.  This driver will work automatically in PCI based systems
   (most new systems have PCI slots).  But if your system uses VESA
   local bus (VLB) instead of PCI, you must also supply a kernel boot
-  parameter to enable the CMD640 bugfix/support: "ide0=cmd640_vlb" 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
+  parameter to enable the CMD640 bugfix/support: "ide0=cmd640_vlb".
+  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.
 
 CMD640 enhanced support
@@ -335,15 +340,17 @@
 
 Generic PCI bus-master DMA support
 CONFIG_BLK_DEV_IDEDMA
-  If your PCI IDE controller is capable of bus-master DMA
-  (Direct Memory Access) transfers (most newer systems are),
-  then you will want to say Y here to reduce CPU overhead.
-  With this option, Linux will automatically enable DMA transfers
-  in most cases, noting this with "DMA" appended to the drive
-  identification info.  You can also use the "hdparm" utility to
-  enable DMA for drives which were not enabled automatically.
-  You can get the latest version of the hdparm utility via anonymous
-  FTP from sunsite.unc.edu/pub/Linux/system/hardware/
+  If your PCI system uses IDE drive(s) (as opposed to SCSI, say) and
+  is capable of bus-master DMA operation (most Pentium PCI systems),
+  you will want to say Y here to reduce CPU overhead.  With this
+  option, Linux will automatically enable DMA transfers in most cases,
+  noting this with "DMA" appended to the drive identification info.
+  You can also use the "hdparm" utility to enable DMA for drives which
+  were not enabled automatically.  You can get the latest version of
+  the hdparm utility via anonymous FTP from
+  sunsite.unc.edu/pub/Linux/system/hardware/. Read the comments at the
+  beginning of drivers/block/idedma.c and the file
+  Documentation/ide.txt for more information.
   It is safe to say Y to this question.
 
 Other IDE chipset support
@@ -427,7 +434,7 @@
   I/O speeds to be set as well.  See the Documentation/ide.txt and
   ali14xx.c files for more info.
 
-XT harddisk support
+XT hard disk support
 CONFIG_BLK_DEV_XD
   Very old 8 bit hard disk controllers used in the IBM XT computer. To
   include a driver for these, say Y. If you want to compile the driver
@@ -438,22 +445,25 @@
 
 Parallel port IDE device support
 CONFIG_PARIDE
-  There are many external CD-ROM and disk devices that connect
-  through your computer's parallel port.  Most of them are actually
-  IDE devices using a parallel port IDE adapter.  This option enables
-  the PARIDE subsystem which contains drivers for many of these
-  external drives.  Read linux/Documentation/paride.txt for more
-  information.  If you have enabled the parallel port support general
-  configuration option, you may share a single port between your
-  printer and other parallel port devices.  Answer Y to build PARIDE 
-  support into your kernel, or M if you would like to build it as a 
-  loadable module.  If your parallel port support is in a loadable 
-  module, you must build PARIDE as a module.  If you built PARIDE
-  support into your kernel, you may still build the individual 
-  protocol modules and high-level drivers as loadable modules.  To
-  use the PARIDE support, you must have this module as well as at
-  least one protocol module and one high-level driver.  If you build
-  this support as a module, it will be called paride.o.
+  There are many external CD-ROM and disk devices that connect through
+  your computer's parallel port. Most of them are actually IDE devices
+  using a parallel port IDE adapter. This option enables the PARIDE
+  subsystem which contains drivers for many of these external drives.
+  Read linux/Documentation/paride.txt for more information. If you
+  have said Y to the "Parallel-port support" configuration option, you
+  may share a single port between your printer and other parallel port
+  devices. Answer Y to build PARIDE support into your kernel, or M if
+  you would like to build it as a loadable module. If your parallel
+  port support is in a loadable module, you must build PARIDE as a
+  module. If you built PARIDE support into your kernel, you may still
+  build the individual protocol modules and high-level drivers as
+  loadable modules. To use the PARIDE support, you must say Y or M
+  here and also to at least one high-level driver (e.g. "Parallel port
+  IDE disks", "Parallel port ATAPI CD-ROMs", "Parallel port ATAPI
+  disks" etc.) and to at least one protocol driver (e.g. "ATEN EH-100
+  protocol", "MicroSolutions backpack protocol", "DataStor Commuter
+  protocol" etc.). If you build this support as a module, it will be
+  called paride.o.
 
 Parallel port IDE disks
 CONFIG_PARIDE_PD 
@@ -465,22 +475,25 @@
   must also have at least one parallel port protocol driver in your 
   system.  Among the devices supported by this driver are the SyQuest 
   EZ-135, EZ-230 and SparQ drives, the Avatar Shark and the backpack
-  hardrives from MicroSolutions.
+  hard drives from MicroSolutions.
 
 Parallel port ATAPI CD-ROMs
 CONFIG_PARIDE_PCD 
   This option enables the high-level driver for ATAPI CD-ROM devices
-  connected through a parallel port.  If you chose to build PARIDE
+  connected through a parallel port. If you chose to build PARIDE
   support into your kernel, you may answer Y here to build in the
-  parallel port ATAPI CD-ROM driver, otherwise you should answer M
-  to build it as a loadable module.  The module will be called pcd.o.
-  You must also have at least one parallel port protocol driver in
-  your system.  Among the devices supported by this driver are the
-  MicroSolutions backpack CD-ROM drives and the Freecom Power CD.
+  parallel port ATAPI CD-ROM driver, otherwise you should answer M to
+  build it as a loadable module. The module will be called pcd.o. You
+  must also have at least one parallel port protocol driver in your
+  system. Among the devices supported by this driver are the
+  MicroSolutions backpack CD-ROM drives and the Freecom Power CD. If
+  you have such a CD-ROM drive, you should also say Y to "ISO9660
+  cdrom filesystem support" below, because that's the filesystem used
+  on CDROMs.
 
 Parallel port ATAPI disks
 CONFIG_PARIDE_PF 
-  This option enable the high-level driver for ATAPI disk devices
+  This option enables the high-level driver for ATAPI disk devices
   connected through a parallel port.  If you chose to build PARIDE
   support into your kernel, you may answer Y here to build in the
   parallel port ATAPI disk driver, otherwise you should answer M
@@ -492,7 +505,7 @@
 
 Parallel port ATAPI tapes
 CONFIG_PARIDE_PT
-  This option enable the high-level driver for ATAPI tape devices
+  This option enables the high-level driver for ATAPI tape devices
   connected through a parallel port.  If you chose to build PARIDE
   support into your kernel, you may answer Y here to build in the
   parallel port ATAPI disk driver, otherwise you should answer M
@@ -584,7 +597,7 @@
 OnSpec 90c20 protocol
 CONFIG_PARIDE_ON20 
   This option enables support for the (obsolete) 90c20 parallel port 
-  IDE protocol from OnSpec (often marketted under the ValuStore brand
+  IDE protocol from OnSpec (often marketed under the ValuStore brand
   name).  If you chose to build PARIDE support into your kernel, you 
   may answer Y here to build in the protocol driver, otherwise you 
   should answer M to build it as a loadable module.  The module will 
@@ -594,7 +607,7 @@
 OnSpec 90c26 protocol
 CONFIG_PARIDE_ON26 
   This option enables support for the 90c26 parallel port IDE protocol 
-  from OnSpec Electronics (often marketted under the ValuStore brand
+  from OnSpec Electronics (often marketed under the ValuStore brand
   name).  If you chose to build PARIDE support into your kernel, you 
   may answer Y here to build in the protocol driver, otherwise you 
   should answer M to build it as a loadable module.  The module will 
@@ -603,17 +616,18 @@
 
 Multiple devices driver support
 CONFIG_BLK_DEV_MD
-  This driver lets you combine several harddisk partitions into one
-  logical block device. Information about how and why to use it and the
-  necessary tools are available over ftp (user: anonymous) from
-  sweet-smoke.ufr-info-p7.ibp.fr/pub/Linux in the md package
-  and the md-FAQ. Please read drivers/block/README.md. If unsure, say
-  N.
+  This driver lets you combine several hard disk partitions into one
+  logical block device. Information about how and why to use it and
+  the necessary tools are available over ftp (user: anonymous) from
+  sweet-smoke.ufr-info-p7.ibp.fr/pub/Linux in the md package and the
+  md-FAQ. Please read drivers/block/README.md and the relevant section
+  of the Disk-HOWTO, available via ftp (user: anonymous) from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. If unsure, say N.
 
 Linear (append) mode
 CONFIG_MD_LINEAR
   If you say Y here, then your multiple devices driver will be able to
-  use the so-called linear mode, i.e. it will combine the harddisk
+  use the so-called linear mode, i.e. it will combine the hard disk
   partitions by simply appending one to the other. If you want to
   compile this as a module ( = code which can be inserted in and
   removed from the running kernel whenever you want), say M here and
@@ -623,7 +637,7 @@
 RAID-0 (striping) mode
 CONFIG_MD_STRIPED
   If you say Y here, then your multiple devices driver will be able to
-  use the so-called raid0 mode, i.e. it will combine the harddisk
+  use the so-called raid0 mode, i.e. it will combine the hard disk
   partitions into one logical device in such a fashion as to fill them
   up evenly, one chunk here and one chunk there. This will increase
   the throughput rate if the partitions reside on distinct disks.  If
@@ -743,60 +757,64 @@
 
 Networking support
 CONFIG_NET
-  Unless you really know what you are doing, you should say Y
-  here. The reason is that some programs need kernel networking
-  support even if you configure a stand-alone machine that won't be
-  connected to any other computer.  If you are upgrading from an older
-  kernel, you should consider updating your networking tools too
-  because changes in the kernel and the tools often go hand in hand;
-  see http://www.inka.de/sites/lina/linux/NetTools/index_en.html for
-  details. 
+  Unless you really know what you are doing, you should say Y here.
+  The reason is that some programs need kernel networking support even
+  when running on a stand-alone machine that isn't connected to any
+  other computer. If you are upgrading from an older kernel, you
+  should consider updating your networking tools too because changes
+  in the kernel and the tools often go hand in hand. The tools are
+  contained in the package net-tools, the location and version number
+  of which is given in Documentation/Changes.
 
 Fast switching (read help!)
 CONFIG_NET_FASTROUTE
-  Enable direct NIC-to-NIC data transfers.
-  *** This option is NOT COMPATIBLE with several important ***
-  *** networking options: especially CONFIG*FIREWALL.      ***
+  Enables direct NIC-to-NIC data transfers, which is fast.
+    *** This option is NOT COMPATIBLE with several important ***
+    *** networking options: especially CONFIG*FIREWALL.      ***
   However, it will work with all options in CONFIG_IP_ADVANCED_ROUTER
-  section (except for CONFIG_IP_ROUTE_TOS). At the moment few of devices
-  supports it (tulip is one of them, modified 8390 can be found at
-  ftp://ftp.inr.ac.ru/ip-routing/fastroute-8390.tar.gz).
-  Remember, short cuts make long delays :-), say N.
+  section (except for CONFIG_IP_ROUTE_TOS). At the moment, few devices
+  support fast switching (tulip is one of them, modified 8390 can be
+  found at ftp://ftp.inr.ac.ru/ip-routing/fastroute-8390.tar.gz). If
+  unsure, say N.
 
 Forwarding between high speed interfaces
 CONFIG_NET_HW_FLOWCONTROL
   This option enables NIC hardware throttling during periods of
-  extremal congestion. At the moment only couple of device drivers
-  support it (really, one --- tulip, modified 8390 can be found
-  at ftp://ftp.inr.ac.ru/ip-routing/fastroute-8390.tar.gz).
-  Really, this option is applicable to any machine attached
-  to enough fast network, and even 10Mb NIC
-  is able to kill not very slow box, sort of 120MHz Pentium.
-  However, do not enable this option, if you did not experienced
+  extremal congestion. At the moment only a couple of device drivers
+  support it (really only one ---tulip, modified 8390 can be found at
+  ftp://ftp.inr.ac.ru/ip-routing/fastroute-8390.tar.gz).  Really, this
+  option is applicable to any machine attached to a fast enough
+  network, and even a 10Mb NIC is able to kill a not very slow box,
+  such as a 120MHz Pentium.
+  However, do not enable this option, if you did not experience
   any serious problems.
 
 Network aliasing
 CONFIG_NET_ALIAS
   This will allow you to set multiple network addresses on the same
   low-level network device driver. Typically used for services that
-  act differently based on the address they listen on
-  (e.g. "multihosting" or "virtual domains" on the web server apache
-  and the ftp server wuftpd) or for connecting to different logical
-  networks through the same physical interface (most commonly an
-  ethernet networking card).  This is the generic part, later when
-  configuring network protocol options you will be asked for
-  protocol-specific aliasing support, and you will have to say Y to at
-  least one of them.  See Documentation/networking/alias.txt for more
-  info.  If you need this feature (for any protocol, like IP) say Y;
-  if unsure, say N.
+  act differently based on the address they listen on (e.g.
+  "multihosting" or "virtual domains" or "virtual hosting services" on
+  the web server apache and the ftp server wuftpd -- read the
+  Virtual-Services-HOWTO, available via ftp (user: anonymous) from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO) or for connecting to
+  different logical networks through the same physical interface (most
+  commonly an Ethernet networking card). This is the generic part,
+  later when configuring network protocol options you will be asked
+  for protocol-specific aliasing support, and you will have to say Y
+  to at least one of them, most likely "IP: aliasing support". See
+  Documentation/networking/alias.txt for more info. If you need this
+  feature (for any protocol, like IP) say Y; if unsure, say N.
 
 Socket filtering
 CONFIG_FILTER
   The Linux Socket Filter is derived from the Berkeley Packet Filter.
-  Through Socket Filtering you can have the kernel decide whether the
-  data is good and to continue processing it. Linux Socket Filtering 
-  works on all socket types except TCP for now. See the text file
-  linux/Documentation/networking/filter.txt for more information.
+  If you say Y here, user-space programs can attach a filter onto any
+  socket and thereby tell the kernel that it should allow or disallow
+  certain types of data to get through the socket. Linux Socket
+  Filtering works on all socket types except TCP for now. See the text
+  file linux/Documentation/networking/filter.txt for more information.
+  If unsure, say N.
 
 Network firewalls
 CONFIG_FIREWALL
@@ -824,7 +842,7 @@
   Normal TCP/IP networking is open to an attack known as "SYN flooding".
   This denial-of-service attack prevents legitimate remote users from
   being able to connect to your computer and requires very little work
-  from the attacker, who can operate from anywhere on the internet.
+  from the attacker, who can operate from anywhere on the Internet.
   SYN cookies provide protection against this type of attack. With
   this option turned on, the TCP/IP stack will use a cryptographic
   challenge protocol known as SYN cookies to enable legitimate users
@@ -849,10 +867,12 @@
 CONFIG_ALPHA_AVANTI
   Find out what type of Alpha motherboard you have. You will probably
   want to read the Linux/Alpha homepage on the WWW at
-  http://www.azstarnet.com/~axplinux/ (To browse the WWW, you need to
+  http://www.azstarnet.com/~axplinux/ (to browse the WWW, you need to
   have access to a machine on the Internet that has a program like
-  lynx or netscape). For this question, it suffices to give a unique
-  prefix of the option you want to choose. The choices:
+  lynx or netscape) and also the Alpha-HOWTO, available via ftp (user:
+  anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. For this
+  question, it suffices to give a unique prefix of the option you want
+  to choose. The choices:
   ** Avanti: This is for Mustang (AS200), M3 (AS250), Avanti (AS400)
      AlphaStations.  These usually come with a TGA graphics adapter,
      so you'll want to say Y to "TGA Console support", below, if you
@@ -922,11 +942,13 @@
 Support more than 4 serial ports
 CONFIG_SERIAL_MANY_PORTS
   Enable this option if you have dumb serial boards other than the
-  four standard COM 1/2/3/4 ports.  This may happen if you have an AST
-  FourPort, Accent Async, Boca, or other custom serial port hardware
-  which acts similar to standard serial port hardware.  If you only
-  use the standard COM 1/2/3/4 ports, you can say N here to save some
-  memory.
+  four standard COM 1/2/3/4 ports. This may happen if you have an AST
+  FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available
+  via ftp (user: anonymous) from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini), or other custom serial
+  port hardware which acts similar to standard serial port hardware.
+  If you only use the standard COM 1/2/3/4 ports, you can say N here
+  to save some memory.
 
 Support for sharing serial interrupts
 CONFIG_SERIAL_SHARE_IRQ
@@ -995,6 +1017,12 @@
   Y if you think it might help, but try turning it off if you
   experience any problems with the PCI bus. N is the safe answer.
 
+Backward-compatible /proc/pci
+CONFIG_PCI_OLD_PROC
+  If you say Y here and to the "/proc filesystem support" below, you
+  will get a directory /proc/pci with information about your PCI
+  hardware. If unsure, say Y.
+
 MCA support
 CONFIG_MCA
   MicroChannel Architecture is found in some IBM PS/2 machines and
@@ -1019,25 +1047,38 @@
   or with the program info ("man info"). Saying Y here enlarges
   your kernel by about 7kB. Just say Y.
 
+BSD Process Accounting
+CONFIG_BSD_PROCESS_ACCT
+  If you say Y here, a user level program will be able to instruct the
+  kernel (via a special system call) to write process accounting
+  information to a file: whenever a process exits, information about
+  that process will be appended to the file by the kernel. The
+  information includes things such as creation time, owning user,
+  command name, memory usage, controlling terminal etc. (the complete
+  list is in the struct acct in include/linux/acct.h). It is up to the
+  user level program to do useful things with this information. This
+  is generally a good idea, so say Y.
+  
 Sysctl support
 CONFIG_SYSCTL
-  The sysctl interface provides a means of dynamically changing certain
-  kernel parameters and variables on the fly without requiring a
-  recompile of the kernel or reboot of the system. The primary interface
-  consists of a system call, but if the /proc filesystem is enabled, a
-  tree of modifiable sysctl entries will be generated beneath the 
-  /proc/sys directory. Note that enabling this option will enlarge the 
-  kernel by at least 8kB. As it is generally a good thing, you probably
-  want to say Y here unless building a kernel for install/rescue disks
-  or your system is very limited in memory.
+  The sysctl interface provides a means of dynamically changing
+  certain kernel parameters and variables on the fly without requiring
+  a recompile of the kernel or reboot of the system. The primary
+  interface consists of a system call, but if the /proc filesystem is
+  enabled, a tree of modifiable sysctl entries will be generated
+  beneath the /proc/sys directory. They are explained in the files in
+  Documentation/sysctl/. Note that enabling this option will enlarge
+  the kernel by at least 8kB. As it is generally a good thing, you
+  probably want to say Y here unless building a kernel for
+  install/rescue disks or your system is very limited in memory.
 
 Kernel support for ELF binaries
 CONFIG_BINFMT_ELF
   ELF (Executable and Linkable Format) is a format for libraries and
   executables used across different architectures and operating
   systems. This option will enable your kernel to run ELF binaries and
-  enlarge it by about 2kB. ELF support under Linux is quickly
-  replacing the traditional Linux a.out formats (QMAGIC and ZMAGIC)
+  enlarge it by about 2kB. ELF support under Linux has now all but
+  replaced the traditional Linux a.out formats (QMAGIC and ZMAGIC)
   because it is portable (this does *not* mean that you will be able
   to run executables from different architectures or operating
   systems!) and makes building run-time libraries very easy. Many new
@@ -1104,9 +1145,9 @@
   will be called binfmt_java.o. 
   The complete functionality of this Java support is also provided by
   the more general option "Kernel support for MISC binaries",
-  below. This option is therefore considered obsolete and you probably
-  want to say N here and Y to "Kernel support for MISC binaries" if
-  you're interested in Java.
+  below. This option is therefore considered obsolete and you should
+  say N here and Y to "Kernel support for MISC binaries" if you're 
+  interested in transparently executing Java programs.
 
 Kernel support for Linux/Intel ELF binaries
 CONFIG_BINFMT_EM86
@@ -1129,11 +1170,13 @@
   (CONFIG_BINFMT_JAVA) or "Kernel support for Linux/Intel ELF
   binaries" (CONFIG_BINFMT_EM86), as this is a more general solution.
   You can do other nice things, too. Read
-  Documentation/binfmt_misc.txt to learn how to use this feature.
+  Documentation/binfmt_misc.txt to learn how to use this feature, and
+  Documentation/java.txt for information about how to include Java
+  support. 
   You must enable the "proc filesystem support" (CONFIG_PROC_FS) to
   use this part of the kernel.
   You may answer M for module support and later load the module when
-  you have use for it.  
+  you have use for it; the module is called binfmt_misc.o.  
   If you don't know what to answer at this point, say Y.
 
 Solaris binary emulation
@@ -1155,10 +1198,21 @@
   (=586) and Pentium Pro (=686). In rare cases, it can make sense to
   specify "Pentium" even if running on a 486: the kernel will be
   smaller but slower. 
-  If you have a multiple processor machine and want Linux to use all
-  the processors in parallel, set the SMP variable in the toplevel
-  kernel Makefile.
-  If you don't know what to do, say "386".
+  If you have a single processor machine, make sure that the line
+  "SMP=1" at the top of the toplevel kernel Makefile is commented out;
+  if you have a multi processor machine and want Linux to use all the
+  processors in parallel (Symmetric Multi Processing), make sure that
+  the line "SMP=1" is not commented out and read Documentation/smp and
+  Documentation/IO-APIC.txt and the SMP-FAQ on the WWW at
+  http://www.irisa.fr/prive/mentre/smp-faq/ (to browse the WWW, you
+  need to have access to a machine on the Internet that has a programs
+  like lynx or netscape). People using multiprocessor machines should
+  also say Y to "Enhanced Real Time Clock Support", below.
+  If you want to compile a kernel that should work on both single
+  processor and multi processor machines, it is possible to set
+  SMP=1. The "Advance Power Management" code (see configuration option
+  below) will not work in that scenario, though.
+  If you don't know what to do, choose "386".
 
 Video mode selection support
 CONFIG_VIDEO_SELECT
@@ -1178,9 +1232,11 @@
 Parallel-port support
 CONFIG_PARPORT
   If you want to use devices connected to your parallel port (the
-  connector at the computers with 25 holes), e.g. printer, Zip drive,
-  PLIP link etc., then you need to enable this option; please read
-  Documentation/parport.txt and drivers/misc/BUGS-parport.  For
+  connector at the computer with 25 holes), e.g. printer, Zip drive,
+  PLIP link (Parallel Line Internet Protocol is mainly used to create
+  a mini network by connecting the parallel ports of two local
+  machines) etc., then you need to say Y here; please read
+  Documentation/parport.txt and drivers/misc/BUGS-parport. For
   extensive information about drivers for many devices attaching to
   the parallel port see http://www.torque.net/linux-pp.html on the WWW
   (To browse the WWW, you need to have access to a machine on the
@@ -1190,18 +1246,26 @@
   want to compile parallel port support as a module ( = code which can
   be inserted in and removed from the running kernel whenever you
   want), say M here and read Documentation/modules.txt. The module
-  will be called parport.o.  If you have more than one parallel port
-  and want to specify which port and IRQ to use by this driver at
+  will be called parport.o. If you have more than one parallel port
+  and want to specify which port and IRQ to be used by this driver at
   module load time, read Documentation/networking/net-modules.txt.
+  If unsure, say Y.
 
 PC-style hardware 
 CONFIG_PARPORT_PC
-  You should enable this option if you have a PC-style parallel
-  port. All IBM PC compatible computers and some Alphas have PC-style
-  parallel ports.  This code is also available as a module. If you
-  want to it as a module ( = code which can be inserted in and removed
-  from the running kernel whenever you want), say M here and read
+  You should say Y here if you have a PC-style parallel port. All IBM
+  PC compatible computers and some Alphas have PC-style parallel
+  ports. This code is also available as a module. If you want to it as
+  a module ( = code which can be inserted in and removed from the
+  running kernel whenever you want), say M here and read
   Documentation/modules.txt. The module will be called parport_pc.o.
+  If unsure, say Y.
+
+Support foreign hardware
+CONFIG_PARPORT_OTHER
+  Say Y here if you want to be able to load driver modules to support
+  other non-standard types of parallel ports. This causes a
+  performance loss, so most people say N.
 
 Sun Ultra/AX-style hardware 
 CONFIG_PARPORT_AX
@@ -1209,25 +1273,6 @@
   Ultra/AX machines.  This code is also available as a module (say M),
   called parport_ax.o.  If in doubt, saying N is the safe plan.
 
-Foreign parallel hardware
-CONFIG_PARPORT_OTHER
-  Say Y here if you want to be able to load driver modules to support
-  other types of parallel port.  This causes a performance loss, so most
-  people say N.
-
-Compile the kernel into the ELF object format 
-CONFIG_ELF_KERNEL
-  ELF (Executable and Linkable Format) is a format for libraries and
-  executables used across different architectures and operating
-  systems. This option will cause the resulting kernel to be in ELF
-  format, which is generally desirable, so say Y. However, it only
-  works if your compiler and linker can produce ELF code.
-
-Is your ELF compiler an extra compiler
-CONFIG_EXTRA_ELF_COMPILER
-  If you have a linuxelf-gcc as opposed to linux-gcc, say Y, otherwise
-  N.
-
 Generate little endian code
 CONFIG_CPU_LITTLE_ENDIAN
   If your compiler is mipsel-linux-gcc or mipsel-linuxelf-gcc (as
@@ -1243,7 +1288,7 @@
 
 Auto-probe for parallel devices
 CONFIG_PNP_PARPORT
-  Some IEEE-1284 conformant parallel-port devices can identify
+  Some IEEE-1284 conforming parallel-port devices can identify
   themselves when requested.  Say Y to enable this feature, or M to
   compile it as a module (parport_ieee1284.o).  If in doubt, say N.
 
@@ -1287,10 +1332,10 @@
 PnP Legacy device support
 CONFIG_PNP_LEGACY
   Before PnP ISA was standardized, several "jumperless", or
-  "soft-configurable" boards were finding there way onto the market.
+  "soft-configurable" boards were finding their way onto the market.
   These cards used somewhat proprietary mechanisms for configuring
   IRQs, DMAs, IO addresses, and memory ranges. These devices (mainly
-  network cards, but also some sound card) can be configured as any
+  network cards, but also some sound cards) can be configured as any
   other PnP device can by saying Y here, if appropriate drivers
   for these devices are available.
 
@@ -1308,20 +1353,21 @@
 PnP auto-configures all devices on startup
 CONFIG_PNP_BOOTINIT
   This option will allow the PnP subsystem to automatically configure
-  all the   PnP devices it finds upon system startup (or at least
-  attempt to). This is useful if you have older driver which do not use
+  all the PnP devices it finds upon system startup (or at least
+  attempt to). This is useful if you have older drivers which do not use
   the Linux-PnP system to configure PnP devices, and which you need
-  configured by PnP in order to use.
+  to be configured by PnP before you can use them.
 
 Enable loadable module support
 CONFIG_MODULES
-  Kernel modules are small pieces of compiled code which can be 
-  inserted in or removed from the running kernel, using the
-  programs insmod and rmmod. This is described in the file 
-  Documentation/modules.txt. Modules can be device drivers, file 
-  systems, binary executable formats, and so on. If you think that 
-  you may want to make use of modules with this kernel in the future, 
-  then say Y here. If unsure, say Y.
+  Kernel modules are small pieces of compiled code which can be
+  inserted in or removed from the running kernel, using the programs
+  insmod and rmmod. This is described in the file
+  Documentation/modules.txt, including the fact that you have to say
+  "make modules" in order to compile the modules. Modules can be
+  device drivers, file systems, binary executable formats, and so
+  on. If you think that you may want to make use of modules with this
+  kernel in the future, then say Y here. If unsure, say Y.
 
 Set version information on all symbols for modules
 CONFIG_MODVERSIONS
@@ -1339,10 +1385,14 @@
 
 Kernel module loader support
 CONFIG_KMOD
-  This feature allows the kernel to load modules for itself.  When
-  a part of the kernel needs a module, it runs modprobe with the
-  appropriate arguments.  Say Y here and read about configuring it
-  in Documentation/kmod.txt.  (this is a replacement of kerneld)
+  Normally when you have selected some drivers and/or filesystems to
+  be created as loadable modules, you also have the responsibility to
+  load the corresponding modules (using the programs insmod or
+  modprobe) before you can use them. If you say Y here however, the
+  kernel will be able to load modules for itself. When a part of the
+  kernel needs a module, it runs modprobe with the appropriate
+  arguments. (This is a replacement for kerneld.) Say Y here and read
+  about configuring it in Documentation/kmod.txt.
 
 ARP daemon support (EXPERIMENTAL)
 CONFIG_ARPD
@@ -1382,7 +1432,7 @@
   This is code for addressing several networked computers at once,
   enlarging your kernel by about 2 kB. You need multicasting if you
   intend to participate in the MBONE, a high bandwidth network on top
-  of the internet which carries audio and video broadcasts. More
+  of the Internet which carries audio and video broadcasts. More
   information about the MBONE is on the WWW at
   http://www.best.com/~prince/techinfo/mbone.html (to browse the WWW,
   you need to have access to a machine on the Internet that has a
@@ -1439,7 +1489,7 @@
   verbose messages regarding the routing, for example warnings about
   received packets which look strange and could be evidence of an
   attack or a misconfigured system somewhere. The information is
-  handled by the klogd demon which is responsible for kernel messages
+  handled by the klogd daemon which is responsible for kernel messages
   ("man klogd").
 
 IP: large routing tables
@@ -1475,10 +1525,17 @@
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. Also, you will need the
   ipfwadm tool (available via ftp (user: anonymous) from
   ftp.xos.nl/pub/linux/ipfwadm/) to allow selective blocking of
-  internet traffic based on type, origin and destination.  The
-  firewalling code will only work if you say Y to "/proc filesystem
-  support" below and IP forwarding is enabled in your kernel; do this
-  from within a boot-time script like so: 
+  Internet traffic based on type, origin and destination; this type of
+  firewall is called a "packet filter".  The other type of firewall,
+  "proxy-based" ones, is more secure but more intrusive and more
+  bothersome to set up; it inspects the network traffic much more
+  closely and has knowledge about the higher level protocols, which
+  packet filters lack. Proxy-based firewalls don't need support by the
+  kernel, but they are often combined with a packet filter, which only
+  works if you say Y here.
+  The firewalling code will only work if you say Y to "/proc
+  filesystem support" below and IP forwarding is enabled in your
+  kernel; do this from within a boot-time script like so:
     echo "1" > /proc/sys/net/ipv4/ip_forwarding
   after the /proc filesystem has been mounted.  
   You need to say Y to "IP firewalling" in order to be able to use IP
@@ -1509,7 +1566,7 @@
   This keeps track of your IP network traffic and produces some
   statistics. Usually, you only want to say Y here if your box will be
   a router or a firewall for some local network. For the latter, you
-  need to say Y to IP firewalling. The data is accessible with "cat
+  need to say Y to "IP firewalling". The data is accessible with "cat
   /proc/net/ip_acct", so you want to say Y to the /proc filesystem
   below, if you say Y here. To specify what exactly should be
   recorded, you need the tool ipfwadm (available via ftp (user:
@@ -1534,9 +1591,9 @@
   say Y here. In case the boot ROM of your network card was designed
   for booting Linux and does BOOTP itself, providing all necessary
   information on the kernel command line, you can say N here.  If
-  unsure, say Y. Note that in case you want to use BOOTP, a BOOTP
-  server must be operating on your network. Read
-  Documentation/nfsroot.txt for details.
+  unsure, say Y. Note that if you want to use BOOTP, a BOOTP server
+  must be operating on your network. Read Documentation/nfsroot.txt
+  for details.
 
 RARP support
 CONFIG_IP_PNP_RARP
@@ -1544,8 +1601,8 @@
   some other computer over the net via NFS and you want the IP address
   of your computer to be discovered automatically at boot time using
   the RARP protocol (an older protocol which is being obsoleted by
-  BOOTP and DHCP), say Y here. Note that in case you want to use RARP,
-  a RARP server must be operating on your network. Read
+  BOOTP and DHCP), say Y here. Note that if you want to use RARP, a
+  RARP server must be operating on your network. Read
   Documentation/nfsroot.txt for details.
 
 IP: tunneling
@@ -1571,23 +1628,23 @@
   another protocol and sending it over a channel that understands the
   encapsulating protocol. This particular tunneling driver implements
   GRE (Generic Routing Encapsulation) and at this time allows
-  encapsulating of IPv4 or IPv6 over existing IPv4
-  infrastructure. This driver is useful if the other endpoint is a
-  Cisco router: Cisco likes GRE much better than the other Linux
-  tunneling driver ("IP: tunneling" above). In addition, GRE allows
-  multicast redistribution through the tunnel.
+  encapsulating of IPv4 or IPv6 over existing IPv4 infrastructure.
+  This driver is useful if the other endpoint is a Cisco router: Cisco
+  likes GRE much better than the other Linux tunneling driver ("IP:
+  tunneling" above). In addition, GRE allows multicast redistribution
+  through the tunnel.
 
 IP: broadcast GRE over IP
 CONFIG_NET_IPGRE_BROADCAST
   One application of GRE/IP is to construct a broadcast WAN (Wide Area
-  Network), which looks like a normal1 ethernet LAN (Local Area
+  Network), which looks like a normal Ethernet LAN (Local Area
   Network), but can be distributed all over the Internet. If you want
   to do that, say Y here and to "IP: multicast routing" below.
 
 IP: firewall packet logging
 CONFIG_IP_FIREWALL_VERBOSE
   This gives you information about what your firewall did with
-  packets it received. The information is handled by the klogd demon
+  packets it received. The information is handled by the klogd daemon
   which is responsible for kernel messages ("man klogd").
 
 IP: transparent proxying
@@ -1613,14 +1670,19 @@
   local net are completely invisible to the outside world, even though
   they can reach the outside and can be reached. This makes it
   possible to have the computers on the local network participate on
-  the internet even if they don't have officially registered IP
-  addresses.  (This last problem can also be solved by connecting the
+  the Internet even if they don't have officially registered IP
+  addresses. (This last problem can also be solved by connecting the
   Linux box to the Internet using SLiRP [SLiRP is a SLIP/PPP emulator
   that works if you have a regular dial up shell account on some UNIX
   computer; get it via ftp (user: anonymous) from
-  ftp://sunsite.unc.edu/pub/Linux/system/network/serial/].)  Details
-  on how to set things up are contained in the IP Masquerading FAQ,
-  available at http://www.indyramp.com/masq/. If you say Y here, then
+  ftp://sunsite.unc.edu/pub/Linux/system/network/serial/].) The IP
+  masquerading code will only work if you say Y to "/proc filesystem
+  support" below and IP forwarding is enabled in your kernel; you can
+  do this from within a boot-time script like so: echo "1" >
+  /proc/sys/net/ipv4/ip_forwarding after the /proc filesystem has been
+  mounted. Details on how to set things up are contained in the IP
+  Masquerade mini-HOWTO, available via ftp (user: anonymous) from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you say Y here, then
   the modules ip_masq_ftp.o (for ftp transfers through the firewall),
   ip_masq_irc.o (for irc chats through the firewall), and
   ip_masq_raudio.o (for realaudio downloads through the firewall) will
@@ -1691,25 +1753,28 @@
 IP: aliasing support
 CONFIG_IP_ALIAS
   Sometimes it is useful to give several IP addresses to a single
-  physical network interface (= serial port or ethernet card). The
+  physical network interface (= serial port or Ethernet card). The
   most common case is that you want to serve different WWW or ftp
   documents to the outside according to which of your host names was
   used to connect to you. This is called "multihosting" or "virtual
-  domains" and is explained in detail on the WWW at
-  http://www.thesphere.com/~dlp/TwoServers/ (to browse the WWW, you
-  need to have access to a machine on the Internet that has a program
-  like lynx or netscape). Another scenario would be that there are two
-  logical networks living on your local ethernet and you want to
-  access them both with the same ethernet card. The configuration of
-  these alias addresses is done with a special name syntax explained
-  in Documentation/networking/alias.txt. If you want this, say Y. Most
-  people don't need it and say N.
+  domains" or "virtual hosting services" and is explained in detail on
+  the WWW at http://www.thesphere.com/~dlp/TwoServers/ (to browse the
+  WWW, you need to have access to a machine on the Internet that has a
+  program like lynx or netscape) and also in the
+  Virtual-Hosting-HOWTO, available via ftp (user: anonymous) from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. Another scenario would be
+  that there are two logical networks living on your local Ethernet
+  and you want to access them both with the same Ethernet card. The
+  configuration of these alias addresses is done with a special name
+  syntax explained in Documentation/networking/alias.txt and in the
+  IP-Alias mini-HOWTO. If you want this, say Y. Most people don't need
+  it and say N.
 
 IP: multicast routing
 CONFIG_IP_MROUTE
   This is used if you want your machine to act as a router for IP
   packets that have several destination addresses. It is needed on the
-  MBONE, a high bandwidth network on top of the internet which carries
+  MBONE, a high bandwidth network on top of the Internet which carries
   audio and video broadcasts. In order to do that, you would most
   likely run the program mrouted. Information about the multicast
   capabilities of the various network cards is contained in
@@ -1738,14 +1803,14 @@
 CONFIG_INET_PCTCP
   If you have been having difficulties telneting to your Linux machine
   from a DOS system that uses (broken) PC/TCP networking software (all
-  versions up to OnNet 2.0) over your local ethernet try saying Y
+  versions up to OnNet 2.0) over your local Ethernet try saying Y
   here.  Everyone else says N. People having problems with NCSA telnet
   should see the file linux/Documentation/networking/ncsa-telnet.
 
 Reverse ARP
 CONFIG_INET_RARP
   Since you asked: if there are (usually diskless or portable)
-  machines on your local network that know their hardware ethernet
+  machines on your local network that know their hardware Ethernet
   addresses but don't know their IP addresses upon startup, they can
   send out a Reverse Address Resolution Protocol (RARP) request to
   find out their own IP addresses. Diskless Sun 3 machines use this
@@ -1833,7 +1898,7 @@
   process can require a lot more memory for network buffers and thus this
   option is best only used on machines with 16Mb of memory or higher. 
   Unless you are using long links with end to end speeds of over 2Mbit
-  a second or   satellite links this option will make no difference to
+  a second or satellite links this option will make no difference to
   performance.
 
 Unix domain sockets
@@ -1852,17 +1917,16 @@
 The IPv6 protocol
 CONFIG_IPV6
   This is experimental support for the next version of the Internet
-  Protocol IP version 6 (also called IPng "IP next
-  generation"). Features of this new protocol include: expanded
-  address space, authentication and privacy, and seamless
-  interoperability with the current version of IP (IP version 4). For
-  general information about IPv6, see
-  http://playground.sun.com/pub/ipng/html/ipng-main.html (to browse
-  the WWW, you need to have access to a machine on the Internet that
-  has a program like lynx or netscape); for specific information about
-  IPv6 under Linux read the HOWTO at http://www.terra.net/ipv6/ and
-  the file net/ipv6/README in the kernel source. If you want to use
-  IPv6, please upgrade to the newest net-tools as given in
+  Protocol: IP version 6 (also called IPng "IP next generation"). 
+  Features of this new protocol include: expanded address space,
+  authentication and privacy, and seamless interoperability with the
+  current version of IP (IP version 4). For general information about
+  IPv6, see http://playground.sun.com/pub/ipng/html/ipng-main.html (to
+  browse the WWW, you need to have access to a machine on the Internet
+  that has a program like lynx or netscape); for specific information
+  about IPv6 under Linux read the HOWTO at http://www.terra.net/ipv6/
+  and the file net/ipv6/README in the kernel source. If you want to
+  use IPv6, please upgrade to the newest net-tools as given in
   Documentation/Changes. The IPv6 support 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
@@ -1889,13 +1953,13 @@
 CONFIG_IPX
   This is support for the Novell networking protocol, IPX, commonly
   used for local networks of Windows machines. You need it if you want
-  to access Novell Netware file or print servers using the Linux
+  to access Novell NetWare file or print servers using the Linux
   Novell client ncpfs (available via ftp (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/system/filesystems/) or from within the
   Linux DOS emulator dosemu (read the DOSEMU-HOWTO, available in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO). In order to do the former,
   you'll also have to say Y to "NCP filesystem support", below. To
-  turn your Linux box into a fully featured Netware file server and
+  turn your Linux box into a fully featured NetWare file server and
   IPX router, say Y here and fetch either lwared from
   sunsite.unc.edu:/pub/Linux/system/network/daemons/ or mars_nwe from
   ftp.gwdg.de:/pub/linux/misc/ncpfs. For more information, read the
@@ -1953,7 +2017,7 @@
   http://artoo.hitchcock.org/~flowerpt/projects/linux-netatalk/ on the
   WWW for details (to browse the WWW, you need to have access to a
   machine on the Internet that has a program like lynx or
-  netscape). EtherTalk is the name used for appletalk over ethernet
+  netscape). EtherTalk is the name used for appletalk over Ethernet
   and the cheaper and slower LocalTalk is appletalk over a proprietary
   apple network using serial links. Ethertalk and Localtalk are fully 
   supported by Linux. The NET-2-HOWTO, available via ftp (user: anonymous)
@@ -1971,7 +2035,7 @@
   networking available.  This feature is experimental. With this
   driver, you can either encapsulate IP inside Appletalk (e.g. if your
   Linux box is stuck on an appletalk only network) or decapsulate
-  (e.g. if you want your Linux box to act as a internet gateway for a
+  (e.g. if you want your Linux box to act as a Internet gateway for a
   zoo of appletalk connected Macs). You decide which one of the two
   you want in the following two questions; you can say Y to only one
   of them. Please see Documentation/networking/ipddp.txt for more
@@ -2050,7 +2114,7 @@
   microphone input and speaker output) supporting the KISS protocol or
   one of the various SCC cards that are supported by the Ottawa PI,
   the Gracilis Packetwin or the generic Z8530 driver. Another option
-  are the Baycom modem serial and parallel port hacks or the soundcard
+  are the Baycom modem serial and parallel port hacks or the sound card
   modem (supported by their own drivers). If you say Y here, you also
   have to say Y to one of those drivers. Information about where to
   get supporting software for Linux amateur radio as well as
@@ -2106,7 +2170,7 @@
 CONFIG_BPQETHER
   AX.25 is the protocol used for computer communication over amateur
   radio. If you say Y here, you will be able to send and receive AX.25
-  traffic over ethernet (also called "BPQ AX.25"), which could be
+  traffic over Ethernet (also called "BPQ AX.25"), which could be
   useful if some other computer on your local network has a direct
   amateur radio connection.
 
@@ -2151,9 +2215,9 @@
   network either with a dedicated network card using the X.21 protocol
   (not yet supported by Linux) or one can do X.25 over a standard
   telephone line using an ordinary modem (say Y to "X.25 async driver"
-  below) or over ethernet using an ordinary ethernet card and either
+  below) or over Ethernet using an ordinary Ethernet card and either
   the 802.2 LLC protocol (say Y to "802.2 LLC" below) or LAPB over
-  ethernet (say Y to "LAPB Data Link Driver" and "LAPB over Ethernet
+  Ethernet (say Y to "LAPB Data Link Driver" and "LAPB over Ethernet
   driver" below).  If you want to compile this driver as a module ( =
   code which can be inserted in and removed from the running kernel
   whenever you want), say M here and read
@@ -2168,8 +2232,8 @@
   it is used to transport higher level protocols (mostly X.25 Packet
   Layer, the higher part of X.25, but others are possible as
   well). Usually, LAPB is used with specialized X.21 network cards, but
-  Linux currently supports LAPB only over ethernet connections. If you
-  want to use LAPB connections over ethernet, say Y here and to "LAPB
+  Linux currently supports LAPB only over Ethernet connections. If you
+  want to use LAPB connections over Ethernet, say Y here and to "LAPB
   over Ethernet driver" below.  Read
   Documentation/networking/lapb-module.txt for technical details.  If
   you want to compile this driver as a module though ( = code which
@@ -2180,35 +2244,36 @@
 802.2 LLC (VERY EXPERIMENTAL)
 CONFIG_LLC
   This is a Logical Link Layer protocol used for X.25 connections over
-  ethernet, using ordinary ethernet cards. 
+  Ethernet, using ordinary Ethernet cards. 
 
 Bridging (EXPERIMENTAL)
 CONFIG_BRIDGE
   If you say Y here, then your Linux box will be able to act as an
-  ethernet bridge, which means that the different ethernet segments it
-  is connected to will appear as one ethernet to the
-  participants. Several such bridges can work together to create even
-  larger networks of ethernets using the IEEE802.1 spanning tree
-  algorithm. As this is a standard, Linux bridges will interwork
-  properly with other third party bridge products. In order to use
-  this, you'll need the bridge configuration tools available via ftp
-  (user: anonymous) from shadow.cabi.net in /pub/Linux. Note that if
-  your box acts as a bridge, it probably contains several ethernet
-  devices, but the kernel is not able to recognize more than one at
-  boot time without help; for details read the Ethernet-HOWTO, available 
-  via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/. 
-  The Bridging code is still in test. If unsure, say N.
+  Ethernet bridge, which means that the different Ethernet segments it
+  is connected to will appear as one Ethernet to the participants.
+  Several such bridges can work together to create even larger
+  networks of Ethernets using the IEEE802.1 spanning tree algorithm.
+  As this is a standard, Linux bridges will interwork properly with
+  other third party bridge products. In order to use this, you'll need
+  the bridge configuration tools available via ftp (user: anonymous)
+  from shadow.cabi.net in /pub/Linux. Please read the Bridge
+  mini-HOWTO for more information. Note that if your box acts as a
+  bridge, it probably contains several Ethernet devices, but the
+  kernel is not able to recognize more than one at boot time without
+  help; for details read the Ethernet-HOWTO, available via ftp (user:
+  anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. The
+  Bridging code is still in test. If unsure, say N.
 
 Packet socket
 CONFIG_PACKET
   The Packet protocol is used by applications which communicate
   directly with network devices without an intermediate network
-  protocol implemented in the kernel, e.g. tcpdump. If you want that
-  they work, choose Y. This driver is also available as a module
-  called af_packet.o ( = code which can be inserted in and removed
-  from the running kernel whenever you want). If you want to compile
-  it as a module, say M here and read Documentation/modules.txt. If
-  unsure, say Y.
+  protocol implemented in the kernel, e.g. tcpdump. If you want them
+  to work, choose Y. This driver is also available as a module called
+  af_packet.o ( = code which can be inserted in and removed from the
+  running kernel whenever you want). If you want to compile it as a
+  module, say M here and read Documentation/modules.txt. If unsure,
+  say Y.
 
 Kernel/User network link driver
 CONFIG_NETLINK
@@ -2217,13 +2282,14 @@
   able to read from and write to character special files in the /dev
   directory having major mode 36. So far, the kernel uses it to
   publish some network related information if you say Y to "Routing
-  messages", below. It is also used by the firewall code if you say Y
-  to "Kernel/User network link driver" further down.  You also need to
-  say Y here if you want to use arpd, a daemon that helps keep the
-  internal ARP cache (a mapping between IP addresses and hardware
-  addresses on the local network) small. The ethertap device, which
-  lets user space programs read and write raw ethernet frames, also
-  needs the network link driver. If unsure, say Y.
+  messages", below. It is also used by the firewall code to publish
+  information about possible attacks if you say Y to "IP: firewall
+  packet netlink device" further down. You also need to say Y here if
+  you want to use arpd, a daemon that helps keep the internal ARP
+  cache (a mapping between IP addresses and hardware addresses on the
+  local network) small. The ethertap device, which lets user space
+  programs read and write raw Ethernet frames, also needs the network
+  link driver. If unsure, say Y.
 
 Routing messages
 CONFIG_RTNETLINK
@@ -2239,7 +2305,7 @@
 
 SCSI support?
 CONFIG_SCSI
-  If you want to use a SCSI harddisk, SCSI tapedrive, SCSI CDROM or
+  If you want to use a SCSI hard disk, SCSI tape drive, SCSI CDROM or
   any other SCSI device under Linux, say Y and make sure that you know
   the name of your SCSI host adapter (the card inside your computer
   that "speaks" the SCSI protocol), because you will be asked for
@@ -2257,18 +2323,19 @@
 
 SCSI disk support
 CONFIG_BLK_DEV_SD
-  If you want to use a SCSI harddisk or the SCSI or parallel port
+  If you want to use a SCSI hard disk or the SCSI or parallel port
   version of the IOMEGA ZIP drive under Linux, say Y and read the
-  SCSI-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. This is NOT for SCSI
-  CDROMs. 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 sd_mod.o. If you want to compile it
-  as a module, say M here and read Documentation/modules.txt and
-  Documentation/scsi.txt. Do not compile this driver as a module if
-  your root filesystem (the one containing the directory /) is located
-  on a SCSI disk. In this case, do not compile the driver for your
-  SCSI host adapter (below) as a module either.
+  SCSI-HOWTO and the Disk-HOWTO, both available via ftp (user:
+  anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This is NOT for
+  SCSI CDROMs. 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 sd_mod.o. If you want
+  to compile it as a module, say M here and read
+  Documentation/modules.txt and Documentation/scsi.txt. Do not compile
+  this driver as a module if your root filesystem (the one containing
+  the directory /) is located on a SCSI disk. In this case, do not
+  compile the driver for your SCSI host adapter (below) as a module
+  either.
 
 SCSI tape support
 CONFIG_CHR_DEV_ST
@@ -2295,15 +2362,15 @@
 Enable vendor-specific extensions (for SCSI CDROM)
 CONFIG_BLK_DEV_SR_VENDOR
   This enables the usage of vendor specific SCSI commands. This is
-  required to support multisession CD's on with old NEC/TOSHIBA
-  cdrom drives (and HP Writers). If you have such a drive and get
-  the first session only, try to say Y here; everybody else says N.
+  required to support multisession CD's with old NEC/TOSHIBA cdrom
+  drives (and HP Writers). If you have such a drive and get the first
+  session only, try saying Y here; everybody else says N.
 
 SCSI generic support
 CONFIG_CHR_DEV_SG
   If you want to use SCSI scanners, synthesizers or CD-writers or just
-  about anything having "SCSI" in its name other than harddisks,
-  CDROMs or tapes, say Y here. Those won't be supported by the kernel
+  about anything having "SCSI" in its name other than hard disks,
+  CDROMs or tapes, say Y here. These won't be supported by the kernel
   directly, so you need some additional software which knows how to
   talk to these devices using the SCSI protocol. For CD-writers, you
   would need the program cdwrite, available via ftp (user: anonymous)
@@ -2343,7 +2410,7 @@
   find them in the source: drivers/scsi/scsi.c), and this allows you
   to select the types of information you want, and the level allows
   you to select the level of verbosity.  If you say 'N' here, it may
-  be harder to track down some types of scsi problems.  If you say 'Y'
+  be harder to track down some types of SCSI problems.  If you say 'Y'
   here your kernel will be somewhat larger, but there should be no
   noticeable performance impact as long as you have logging turned off.
 
@@ -2409,8 +2476,8 @@
 Enable tagged command queueing
 CONFIG_AIC7XXX_TAGGED_QUEUEING
   This option allows you to enable tagged command queueing for this
-  driver.  Some scsi devices do not properly support this
-  feature.  Tagged command queueing will improve performance.
+  driver. Some SCSI devices do not properly support this feature. 
+  Tagged command queueing will improve performance.
 
 Override driver defaults for commands per LUN
 CONFIG_OVERRIDE_CMDS
@@ -2515,7 +2582,7 @@
   This driver supports all EATA-PIO protocol compliant SCSI Host
   Adapters like the DPT PM2001 and the PM2012A. EATA-DMA compliant
   host adapters could also use this driver but are discouraged from
-  doing so, since this driver only supports harddisks and lacks
+  doing so, since this driver only supports hard disks and lacks
   numerous features.  You might want to have a look at the SCSI-HOWTO,
   available via ftp (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to compile this
@@ -2543,7 +2610,7 @@
   This is a feature of SCSI-2 which improves performance: the host
   adapter can send a whole list of commands to a device in one
   batch. Some SCSI devices might not implement this properly, so the
-  save answer is N.
+  safe answer is N.
 
 maximum number of queued commands
 CONFIG_SCSI_U14_34F_MAX_TAGS
@@ -2579,7 +2646,7 @@
 
 Enable NCR53c400 extensions
 CONFIG_SCSI_GENERIC_NCR53C400
-  This enables certain optimizations for the NCR53c400 scsi cards. You
+  This enables certain optimizations for the NCR53c400 SCSI cards. You
   might as well try it out. Note that this driver will only probe for
   the Trantor T130B in its default configuration; you might have to
   pass a command line option to the kernel at boot time if it doesn't
@@ -2634,28 +2701,28 @@
   This is the BSD ncr driver adapted to linux for the NCR53C8XX family
   of PCI-SCSI controllers. This driver supports parity checking,
   tagged command queuing, Fast-20 data transfer up to 20 MB/s with
-  narrow scsi devices and 40 MB/s with wide scsi devices.
+  narrow SCSI devices and 40 MB/s with wide SCSI devices.
   Please read drivers/scsi/README.ncr53c8xx for more information.
   Linux/i386, Linux/Alpha and Linux/PPC are supported by this driver.
 
 synchronous data transfers frequency
 CONFIG_SCSI_NCR53C8XX_SYNC
-  SCSI-2 specifications allow scsi devices to negotiate a synchronous 
+  SCSI-2 specifications allow SCSI devices to negotiate a synchronous 
   transfer period of 25 nano-seconds or more.
   The transfer period value is 4 times the agreed transfer period.
   So, data can be transferred at a 10 MHz frequency, allowing 10
-  MB/second throughput with 8 bits scsi-2 devices and 20 MB/second
+  MB/second throughput with 8 bits SCSI-2 devices and 20 MB/second
   with wide16 devices.  This frequency can be used safely with
-  differential devices but may cause problems with singled-ended
+  differential devices but may cause problems with single-ended
   devices.
   Specify 0 if you want to only use asynchronous data transfers.
   Otherwise, specify a value between 5 and 10.  Commercial O/Ses
   generally use 5 Mhz frequency for synchronous transfers.  It is a
   reasonable default value.
-  However, a flawless singled-ended scsi bus supports 10 MHz data
-  transfers.  Regardless the value chosen in the Linux configuration,
-  the synchronous period can be changed after boot-up through the
-  /proc/scsi file system. The generic command is:
+  However, a flawless single-ended SCSI bus supports 10 MHz data
+  transfers.  Regardless of the value chosen in the Linux
+  configuration, the synchronous period can be changed after boot-up
+  through the /proc/scsi file system. The generic command is:
       echo "setsync #target period" >/proc/scsi/ncr53c8xx/0
   Use a 25 ns period for 10 Mhz synchronous data transfers.
   If you don't know what to do now, go with the default.
@@ -2667,16 +2734,16 @@
   Intel-based hardware.  Under Linux/Alpha and Linux/PPC only normal 
   IO is currently supported by the driver and so, this option has no
   effect. On Linux/PPC MMIO and normal IO are done the same (all IO
-  is memory mapped) so you loose nothing by using normal IO. The normal
+  is memory mapped) so you lose nothing by using normal IO. The normal
   answer therefore is N. Try Y only if you have problems.
 
 not allow targets to disconnect
 CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
-  This option is only provided for safety if you suspect some scsi
+  This option is only provided for safety if you suspect some SCSI
   device of yours to not support properly the target-disconnect
   feature. In that case, you would say Y here. In general however, to
   not allow targets to disconnect is not reasonable if there is more
-  than 1 device on a scsi bus.  The normal answer therefore is N.
+  than 1 device on a SCSI bus.  The normal answer therefore is N.
 
 detect and read serial NVRAMs
 CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
@@ -2700,7 +2767,7 @@
 enable tagged command queuing
 CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
   This option allows you to enable tagged command queuing support at
-  linux start-up.  Some scsi devices do not properly support this
+  linux start-up.  Some SCSI devices do not properly support this
   feature.  The suggested method is to say N here and to use the
   "settags" control command after boot-up to enable this feature:
       echo "settags 2 4" >/proc/scsi/ncr53c8xx/0
@@ -2725,16 +2792,16 @@
   wiring.  These General Purpose Input/Output pins can be used for
   vendor specific features or implementation of the standard SYMBIOS
   features.  Genuine SYMBIOS boards use GPIO0 in output for controller
-  LED and GPIO3 bit as a flag indicating singled-ended/differential
+  LED and GPIO3 bit as a flag indicating single-ended/differential
   interface.
   If all the boards of your system are genuine SYMBIOS boards or use
   BIOS and drivers from SYMBIOS, you would want to enable this option.
   The driver behaves correctly on my system with this option enabled.
   (SDMS 4.0 + Promise SCSI ULTRA 875 rev 0x3 + ASUS SC200 810A rev
   0x12).  This option must be set to N if your system has at least one
-  53C8XX based scsi board with a vendor-specific BIOS (example: Tekram
+  53C8XX based SCSI board with a vendor-specific BIOS (example: Tekram
   DC-390/U/W/F). 
-  However, if all your non Symbios compatible boards have NvRAM,
+  However, if all your non Symbios compatible boards have NVRAM,
   setting option "detect and read serial NVRAMs"
   (CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT) above allows the driver to
   distinguish Symbios compatible boards from other ones.  So, you can
@@ -2744,53 +2811,64 @@
 IBMMCA SCSI support
 CONFIG_SCSI_IBMMCA
   This is support for the IBM SCSI adapter found in many of the PS/2
-  series.  CONFIG_MCA must be set for this to work.  If the adapter
-  isn't found during boot (a common problem for models 56, 57, 76, and
-  77) you'll need to use the 'ibmmcascsi=<pun>', where <pun> is the id
-  of the SCSI subsystem (usually 7, but if that doesn't work check your
-  reference diskette). Owners of model 95 with a LED-matrix-display
-  can in addition activate some activity info like under OS/2, but more
-  informative, by setting 'ibmmcascsi=display' as additional kernel-
-  parameter.
+  series computers. These machines have an MCA bus, so you need to say
+  Y to "MCA support" as well and read Documentation/mca.txt.
+  If the adapter isn't found during boot (a common problem for models
+  56, 57, 76, and 77) you'll need to use the 'ibmmcascsi=<pun>' kernel
+  option, where <pun> is the id of the SCSI subsystem (usually 7, but
+  if that doesn't work check your reference diskette). Owners of model
+  95 with a LED-matrix-display can in addition activate some activity
+  info like under OS/2, but more informative, by setting
+  'ibmmcascsi=display' as an additional kernel parameter. Try "man
+  bootparam" or see the documentation of your boot loader about how to
+  pass options to the kernel. The lilo procedure is also explained in
+  the SCSI-HOWTO, available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO.
+  If you want to compile this driver as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt. The module will be
+  called ibmmca.o.
 
 Standard SCSI-order
 CONFIG_IBMMCA_SCSI_ORDER_STANDARD
-  In the PC-world and in most modern SCSI-BIOS-setups, SCSI-harddisks
-  are assigned to the driveletters, starting with the lowest SCSI-id
+  In the PC-world and in most modern SCSI-BIOS-setups, SCSI-hard disks
+  are assigned to the drive letters, starting with the lowest SCSI-id
   (physical number - pun) to be drive C:, as seen from DOS and similar
-  operating systems. When looking into papers, describing the
+  operating systems. When looking into papers describing the
   ANSI-SCSI-standard, this assignment of drives appears to be wrong.
-  The SCSI-standard follows a hardware-hierarchy which says, that
+  The SCSI-standard follows a hardware-hierarchy which says that
   id 7 has the highest priority and id 0 the lowest. Therefore, the
-  hostadapters are still today everywhere placed as SCSI-id 7 by
-  default. In the SCSI-standard, the driveletters express the priority
-  of the disk. C: should be the harddisk or a partition on it, with the
+  host adapters are still today everywhere placed as SCSI-id 7 by
+  default. In the SCSI-standard, the drive letters express the priority
+  of the disk. C: should be the hard disk, or a partition on it, with the
   highest priority. This must therefore be the disk with the highest
   SCSI-id (e.g. 6) and not the one with the lowest! IBM-BIOS kept the
   original definition of the SCSI-standard as also industrial- and
   process-control-machines, like VME-CPUs running under realtime-OSs
   (e.g. LynxOS, OS9) do.
-  If you like to run Linux on your MCA-machine with the same assignment,
-  of harddisks, as seen from e.g. DOS or OS/2 on your machine, which
-  is in addition conform to the SCSI-standard, you must say 'y' here.
+  If you like to run Linux on your MCA-machine with the same assignment
+  of hard disks as seen from e.g. DOS or OS/2 on your machine, which
+  is in addition conformant to the SCSI-standard, you must say Y here.
   This is also necessary for MCA-Linux-users who want to keep downward-
   compatibility to older releases of the IBM-MCA-SCSI-driver (older than
   driver-release 2.00 and older than June 1997).
-  If you like to have the lowest SCSI-id assigned as drive C:, as modern
-  SCSI-BIOS do, which is not conform to the standard, but widely spread
-  and common in the PC-world of today, you must say 'n' here.
+  If you like to have the lowest SCSI-id assigned as drive C:, as
+  modern SCSI-BIOSes do, which does not conform to the standard, but
+  is widespread and common in the PC-world of today, you must say N
+  here.  If unsure, say Y.
 
-Reset SCSI-devices at boottime
+Reset SCSI-devices at boot time
 CONFIG_IBMMCA_SCSI_DEV_RESET
-  By default, SCSI-devices are reset, when the machine is powered on.
+  By default, SCSI-devices are reset when the machine is powered on.
   However, some devices exist, like special-control-devices,
-  SCSI-CNC-machines, SCSI-printer or scanners of older type, that
-  do not reset, when switched on. If you say 'y' here, each device
-  along your SCSI-bus will get a reset-command after it has been
-  probed, while the kernel is booting. Say always 'n' here, if you
-  have no such strange SCSI-devices on your bus. If you say 'y' and
-  some more modern devices, like harddisks, do not like too much
-  resets, your system will hang when booting.
+  SCSI-CNC-machines, SCSI-printer or scanners of older type, that do
+  not reset when switched on. If you say Y here, each device connected
+  to your SCSI-bus will be issued a reset-command after it has been
+  probed, while the kernel is booting. This may cause problems with
+  more modern devices, like hard disks, which do not appreciate these
+  reset commands, and can cause your system to hang. So say Y only if
+  you know that one of your older devices needs it; N is the safe
+  answer.
 
 Always IN2000 SCSI support
 CONFIG_SCSI_IN2000
@@ -2943,14 +3021,14 @@
   This is a feature of SCSI-2 which improves performance: the host
   adapter can send several SCSI commands to a device's queue even if
   previous commands haven't finished yet. Some SCSI devices don't
-  implement this properly, so the save answer is N.
+  implement this properly, so the safe answer is N.
 
 enable elevator sorting
 CONFIG_SCSI_EATA_LINKED_COMMANDS
   This is a feature of SCSI-2 which improves performance: the host
   adapter can send a whole list of commands to a device in one
   batch. Some SCSI devices might not implement this properly, so the
-  save answer is N.
+  safe answer is N.
 
 maximum number of queued commands
 CONFIG_SCSI_EATA_MAX_TAGS
@@ -3037,12 +3115,12 @@
   is that many hard to reproduce problems can be tested in a controlled
   environment where there is reduced risk of losing important data.
   This is primarily of use to people trying to debug the middle and upper
-  layers of the scsi subsystem. If unsure, say N.
+  layers of the SCSI subsystem. If unsure, say N.
 
 Network device support?
 CONFIG_NETDEVICES
-  You can say N here in case you don't intend to connect to any other
-  computer at all or all your connections will be either via UUCP
+  You can say N here if you don't intend to connect to any other
+  computer at all or if all your connections will be either via UUCP
   (UUCP is a protocol to forward mail and news between unix hosts over
   telephone lines; read the UUCP-HOWTO, available via ftp (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO) or dialing up a
@@ -3051,20 +3129,22 @@
   dial up shell account on some Internet connected Unix computer. Read
   http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html (to browse
   the WWW, you need to have access to a machine on the Internet that
-  has a program like lynx or netscape)).  You'll have to say Y if your
+  has a program like lynx or netscape)). You'll have to say Y if your
   computer contains a network card that you want to use under linux
-  (make sure you know its name because you will be asked for it and read
-  the Ethernet-HOWTO in sunsite.unc.edu:/pub/Linux/docs/HOWTO; or if you
-  want to use SLIP (Serial Line Internet Protocol is the protocol used
-  to send Internet traffic over telephone lines or nullmodem cables)
-  or CSLIP (compressed SLIP) or PPP (Point to Point Protocol, a better
-  and newer replacement for SLIP) or PLIP (Parallel Line Internet
-  Protocol is mainly used to create a mini network by connecting the
-  parallel ports of two local machines) or AX.25/KISS (protocol for
-  sending internet traffic over radio links).  Make sure to read the
-  NET-2-HOWTO.  Eventually, you will have to read Olaf Kirch's
-  excellent book "Network Administrator's Guide", to be found in
-  sunsite.unc.edu:/pub/Linux/docs/LDP.  If unsure, say Y.
+  (make sure you know its name because you will be asked for it and
+  read the Ethernet-HOWTO (especially if you plan to use more than one
+  network card under linux), available from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini) or if you want to use
+  SLIP (Serial Line Internet Protocol is the protocol used to send
+  Internet traffic over telephone lines or nullmodem cables) or CSLIP
+  (compressed SLIP) or PPP (Point to Point Protocol, a better and
+  newer replacement for SLIP) or PLIP (Parallel Line Internet Protocol
+  is mainly used to create a mini network by connecting the parallel
+  ports of two local machines) or AX.25/KISS (protocol for sending
+  Internet traffic over radio links). Make sure to read the
+  NET-2-HOWTO. Eventually, you will have to read Olaf Kirch's
+  excellent and free book "Network Administrator's Guide", to be found
+  in sunsite.unc.edu:/pub/Linux/docs/LDP. If unsure, say Y.
 
 Dummy net driver support
 CONFIG_DUMMY
@@ -3075,7 +3155,7 @@
   programs. If you use SLIP or PPP, you might want to say Y here. Read
   about it in the Network Administrator's Guide, available via ftp
   (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/LDP. Since
-  this thing comes often handy, the default is Y. It won't enlarge
+  this thing often comes in handy, the default is Y. It won't enlarge
   your kernel either. What a deal.  If you want to compile this as a
   module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
@@ -3192,7 +3272,7 @@
 CONFIG_HFMODEM
   This experimental driver is used by a package (to be released)
   that implements the shortwave radio protocols RTTY, Sitor (Amtor),
-  Pactor 1 and GTOR using a standard PC soundcard. If unsure,
+  Pactor 1 and GTOR using a standard PC sound card. If unsure,
   say N.
 
 Shortwave radio modem driver support for SoundBlaster and compatible cards
@@ -3253,8 +3333,8 @@
 CONFIG_LAPBETHER
   This is a driver for a pseudo device (typically called /dev/lapb0)
   which allows you to open an LAPB point-to-point connection to some
-  other computer on your ethernet network. In order to do this, you
-  need to say Y or M to the driver for your ethernet card as well as
+  other computer on your Ethernet network. In order to do this, you
+  need to say Y or M to the driver for your Ethernet card as well as
   to "LAPB Data Link Driver".  If you want to compile this driver as a
   module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
@@ -3297,7 +3377,7 @@
 High-speed (DMA) SCC driver for AX.25
 CONFIG_DMASCC
   This is a driver for high-speed SCC boards (used to connect your
-  computer to your amateur radio and send internet traffic over the
+  computer to your amateur radio and send Internet traffic over the
   radio), i.e. those supporting DMA on one port. Currently, only
   Ottawa PI/PI2 boards (see http://hydra.carleton.ca/info/pi2.html)
   and Gracilis PackeTwin boards (see http://www.paccomm.com/; to
@@ -3373,10 +3453,10 @@
   Documentation/modules.txt. This is recommended. The module will be
   called baycom_ser_hdx.o.
 
-Soundcard modem driver for AX.25
+Sound card modem driver for AX.25
 CONFIG_SOUNDMODEM
   This experimental driver allows a standard SoundBlaster or
-  WindowsSoundSystem compatible soundcard to be used as a packet radio
+  WindowsSoundSystem compatible sound card to be used as a packet radio
   modem (NOT as a telephone modem!), to send digital traffic over
   amateur radio. To configure the driver, use the sethdlc, smdiag and
   smmixer utilities available in the standard ax25 utilities
@@ -3390,16 +3470,16 @@
   Documentation/modules.txt. This is recommended. The module will be
   called soundmodem.o.
 
-Soundcard modem support for SoundBlaster and compatible cards
+Sound card modem support for SoundBlaster and compatible cards
 CONFIG_SOUNDMODEM_SBC
   This option enables the soundmodem driver to use SoundBlaster and
   compatible cards. If you have a dual mode card (i.e. a WSS cards
   with a SoundBlaster emulation) you should say N here and Y to
-  "Soundcard modem support for WSS and Crystal cards", below, because
+  "Sound card modem support for WSS and Crystal cards", below, because
   this usually results in better performance. This option also supports
   SB16/32/64 in full duplex mode.
 
-Soundcard modem support for WSS and Crystal cards
+Sound card modem support for WSS and Crystal cards
 CONFIG_SOUNDMODEM_WSS
   This option enables the soundmodem driver to use WindowsSoundSystem
   compatible cards. These cards feature a codec chip from either
@@ -3409,13 +3489,13 @@
   CS423x chips. If you don't need full duplex operation, do not enable
   it to save performance.
 
-Soundcard modem support for 1200 baud AFSK modulation
+Sound card modem support for 1200 baud AFSK modulation
 CONFIG_SOUNDMODEM_AFSK1200
   This option enables the soundmodem driver 1200 baud AFSK modem, 
   compatible to popular modems using TCM3105 or AM7911. The demodulator
   requires about 12% of the CPU power of a Pentium 75 CPU per channel.
 
-Soundcard modem support for 2400 baud AFSK modulation (7.3728MHz crystal)
+Sound card modem support for 2400 baud AFSK modulation (7.3728MHz crystal)
 CONFIG_SOUNDMODEM_AFSK2400_7
   This option enables the soundmodem driver 2400 baud AFSK modem,
   compatible to TCM3105 modems (over-)clocked with a 7.3728MHz
@@ -3426,7 +3506,7 @@
   with many transceiver designs and the fact that the TCM3105 (if
   used) is operated widely outside its specifications.
 
-Soundcard modem support for 2400 baud AFSK modulation (8MHz crystal)
+Sound card modem support for 2400 baud AFSK modulation (8MHz crystal)
 CONFIG_SOUNDMODEM_AFSK2400_8
   This option enables the soundmodem driver 2400 baud AFSK modem,
   compatible to TCM3105 modems (over-)clocked with an 8MHz crystal.
@@ -3437,26 +3517,26 @@
   with many transceiver designs and the fact that the TCM3105 (if
   used) is operated widely outside its specifications.
 
-Soundcard modem support for 2666 baud AFSK modulation
+Sound card modem support for 2666 baud AFSK modulation
 CONFIG_SOUNDMODEM_AFSK2666
   This option enables the soundmodem driver 2666 baud AFSK modem.
   This modem is experimental, and not compatible to anything
   else I know of.
 
-Soundcard modem support for 4800 baud 8PSK modulation
+Sound card modem support for 4800 baud 8PSK modulation
 CONFIG_SOUNDMODEM_PSK4800
   This option enables the soundmodem driver 4800 baud 8PSK modem.
   This modem is experimental, and not compatible to anything
   else I know of.
 
-Soundcard modem support for 4800 baud HAPN-1 modulation
+Sound card modem support for 4800 baud HAPN-1 modulation
 CONFIG_SOUNDMODEM_HAPN4800
   This option enables the soundmodem driver 4800 baud HAPN-1
   compatible modem. This modulation seems to be widely used 'down
   under' and in the Netherlands. Here, nobody uses it, so I could not
   test if it works.  It is compatible to itself, however :-)
 
-Soundcard modem support for 9600 baud FSK G3RUH modulation
+Sound card modem support for 9600 baud FSK G3RUH modulation
 CONFIG_SOUNDMODEM_FSK9600
   This option enables the soundmodem driver 9600 baud FSK modem,
   compatible to the G3RUH standard. The demodulator requires about 4%
@@ -3469,7 +3549,7 @@
 CONFIG_MKISS
   KISS is the 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,
+  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.  If you want to compile this driver as a module ( = code
@@ -3481,21 +3561,26 @@
 CONFIG_PLIP
   PLIP (Parallel Line Internet Protocol) is used to create a
   reasonably fast mini network consisting of two (or, rarely, more)
-  local machines. The PLIP driver has two modes, mode 0 and mode
-  1. The parallel ports (the connectors at the computers with 25
-  holes) are connected with "null printer" or "Turbo Laplink" cables
-  which can transmit 4 bits at a time (mode 0) or with special PLIP
-  cables, to be used on bidirectional parallel ports only, which can
-  transmit 8 bits at a time (mode 1); you can find the wiring of these
-  cables in Documentation/networking/PLIP.txt. The cables can be up to
-  15m long. Mode 0 works also if one of the machines runs DOS/Windows
-  and has some PLIP software installed, e.g. the Crynwr PLIP packet
-  driver (http://www.kanren.net/pktdrvr-info.html; to browse the WWW,
-  you need to have access to a machine on the Internet that has a
-  program like lynx or netscape) and winsock or NCSA's telnet.  If you
-  want to use PLIP, say Y and read the PLIP mini-HOWTO, available via
-  ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini
-  as well as the NET-2-HOWTO in
+  local machines. A PLIP link from a Linux box is a popular means to
+  install a Linux distribution on a machine which doesn't have a CDROM
+  drive (a minimal system has to be transferred with floppies
+  first). The kernels on both machines need to have this PLIP option
+  enabled for this to work.
+  The PLIP driver has two modes, mode 0 and mode 1. The parallel ports
+  (the connectors at the computers with 25 holes) are connected with
+  "null printer" or "Turbo Laplink" cables which can transmit 4 bits
+  at a time (mode 0) or with special PLIP cables, to be used on
+  bidirectional parallel ports only, which can transmit 8 bits at a
+  time (mode 1); you can find the wiring of these cables in
+  Documentation/networking/PLIP.txt. The cables can be up to 15m
+  long. Mode 0 works also if one of the machines runs DOS/Windows and
+  has some PLIP software installed, e.g. the Crynwr PLIP packet driver
+  (http://www.kanren.net/pktdrvr-info.html; to browse the WWW, you
+  need to have access to a machine on the Internet that has a program
+  like lynx or netscape) and winsock or NCSA's telnet.  If you want to
+  use PLIP, say Y and read the PLIP mini-HOWTO, available via ftp
+  (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini as
+  well as the NET-2-HOWTO in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the PLIP protocol
   was changed and this PLIP driver won't work together with the PLIP
   support in Linux versions 1.0.x.  This option enlarges your kernel
@@ -3509,7 +3594,7 @@
 CONFIG_EQUALIZER
   If you have two serial connections to some other computer (this
   usually requires two modems and two telephone lines) and you use
-  SLIP (= the protocol for sending internet traffic over telephone
+  SLIP (= the protocol for sending Internet traffic over telephone
   lines) or PPP (= a better SLIP) on them, you can make them behave
   like one double speed connection using this driver. Naturally, this
   has to be supported at the other end as well, either with a similar
@@ -3526,14 +3611,14 @@
   driver", above) and create a character special file /dev/tap0 with
   major number 36 and minor number 16 using mknod ("man mknod"), you
   will be able to have a user space program read and write raw
-  ethernet frames from/to that special file. tap0 can be configured
-  with ifconfig and route like any other ethernet device but it is not
+  Ethernet frames from/to that special file. tap0 can be configured
+  with ifconfig and route like any other Ethernet device but it is not
   connected to any physical LAN; everything written by the user to
   /dev/tap0 is treated by the kernel as if it had come in from a LAN
   to the device tap0; everything the kernel wants to send out over the
   device tap0 can instead be read by the user from /dev/tap0: the user
   mode program replaces the LAN that would be attached to an ordinary
-  ethernet device.  Please read the file
+  Ethernet device.  Please read the file
   Documentation/networking/ethertap.txt for more information.  This
   driver is also available as a module ( = code which can be inserted
   in and removed from the running kernel whenever you want). The
@@ -3544,7 +3629,7 @@
 Frame Relay (DLCI) support
 CONFIG_DLCI
   This is support for the frame relay protocol; frame relay is a fast
-  low-cost way to connect to a remote internet access provider or to
+  low-cost way to connect to a remote Internet access provider or to
   form a private wide area network. The one physical line from your
   box to the local "switch" (i.e. the entry point to the frame relay
   network, usually at the phone company) can carry several logical
@@ -3584,6 +3669,28 @@
   sdla.o. If you want to compile it as a module, say M here and read
   Documentation/modules.txt.
 
+WAN Router
+CONFIG_WAN_ROUTER
+  Wide Area Networks (WANs), such as X.25, frame relay and leased
+  lines, are used to interconnect Local Area Networks (LANs) over vast
+  distances with data transfer rates significantly higher than those
+  achievable with commonly used asynchronous modem connections.
+  Usually, a quite expensive external device called a `WAN router' is
+  needed to connect to a WAN.
+  As an alternative, WAN routing can be built into the Linux
+  kernel.  With relatively inexpensive WAN interface cards available
+  on the market, a perfectly usable router can be built for less than
+  half the price of an external router.  If you have one of those
+  cards (with appropriate WAN Link Driver) and wish to use your Linux
+  box as a WAN router, you may say 'Y' to this option.  You will also
+  need a wan-tools package available via FTP (user: anonymous) from
+  ftp.sangoma.com.  Read Documentation/networking/wan-router.txt for
+  more information.
+  WAN routing support is always built as a module ( = code which can
+  be inserted in and removed from the running kernel whenever you
+  want).  The module is called wanrouter.o.  For general information
+  about modules read Documentation/modules.txt.
+
 CPU is too slow to handle full bandwidth
 CONFIG_CPU_IS_SLOW
 ###
@@ -3596,7 +3703,7 @@
   devices, it has to make a decision which one to send first. This is
   especially important if some of the network devices are real time
   devices that need a certain minimum data flow rate.  There are
-  several different algorithms how to do this "fairly"; they are
+  several different algorithms for how to do this "fairly"; they are
   called packet schedulers. You can attach different schedulers to
   different network devices. If you want to stick to the default
   scheduling algorithm, say N here. If you want to experiment with a
@@ -3687,82 +3794,78 @@
 ### schedulers?
 ###
 
-WAN Router
-CONFIG_WAN_ROUTER
-      Wide Area Networks (WANs), such as X.25, frame relay and leased
-  lines, are used to interconnect Local Area Networks (LANs) over vast
-  distances with data transfer rates significantly higher than those
-  achievable with commonly used asynchronous modem connections.
-  Usually, a quite expensive external device called `WAN router' is
-  needed to connect to WAN.
-      As an alternative, WAN router can be build into Linux kernel.
-  With relatively inexpensive WAN interface cards available on the
-  market, a perfectly usable router can be built for less than half a
-  price of an external router.  If you have one of those cards (with
-  appropriate WAN Link Driver) and wish to use your Linux box as a WAN
-  router, you may say 'Y' to this option.  You will also need a
-  wan-tools package available via FTP (user: anonymous) from
-  ftp.sangoma.com.  Read Documentation/networking/wan-router.txt for
-  more information.
-      WAN router is always built as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want).
-  For general information about modules read Documentation/modules.txt.
+Network code profiler
+CONFIG_NET_PROFILE
+  If you say Y here and to "/proc filesystem support" below, some
+  obscure and undocumented information about the network code's
+  performance will be written to /proc/net/profile. If you don't know
+  what it is about, you don't need it: say N.
 
 WAN Drivers
 CONFIG_WAN_DRIVERS
-      Say 'Y' to this option if you are planning to use your Linux box
-  as a WAN router ( = device used to interconnect local area networks
-  over wide area communication links, such as leased lines and public
-  data networks, e.g. X.25 and frame relay) and you will be offered a
-  list of WAN drivers currently available.  For more information, read
-  Documentation/networking/wan-router.txt.
+  Say Y to this option if you are planning to use your Linux box as a
+  WAN ( = Wide Area Network) router ( = device used to interconnect
+  local area networks over wide area communication links, such as
+  leased lines and public data networks, e.g. X.25 and frame relay)
+  and you will be offered a list of WAN drivers currently available.
+  For more information, read
+  Documentation/networking/wan-router.txt. Note that the answer to
+  this question won't directly affect the kernel: saying N will just
+  cause this configure script to skip all the questions about WAN
+  drivers. If unsure, say N.
 
 Sangoma WANPIPE(tm) multiprotocol cards
 CONFIG_VENDOR_SANGOMA
-      WANPIPE from Sangoma Technologies Inc. (http://www.sangoma.com)
-  is a family of intelligent multiprotocol WAN adapter with data
-  transfer rates up to T1 (1.544 Mbps).  They are also known as
-  Synchronous Data Link Adapters (SDLA) and designated S502E(A), S503
-  or S508.  If you have one of these cards, say 'Y' to this option.
-      WANPIPE driver is always built as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want).
-  For general information about modules read Documentation/modules.txt.
+  WANPIPE from Sangoma Technologies Inc. (http://www.sangoma.com; to
+  browse the WWW, you need to have access to a machine on the Internet
+  that has a program like lynx or netscape) is a family of intelligent
+  multiprotocol WAN adapters with data transfer rates up to T1 (1.544
+  Mbps).  They are also known as Synchronous Data Link Adapters (SDLA)
+  and designated S502E(A), S503 or S508. These cards support the X.25,
+  Frame Relay, and PPP protocols. If you have one or more of these
+  cards, say Y to this option and read
+  Documentation/networking/wanpipe.txt.  The next questions will ask
+  you about the protocols you want the driver to support. The driver
+  will be compiled as a module ( = code which can be inserted in and
+  removed from the running kernel whenever you want).  The module will
+  be called wanpipe.o.  For general information about modules read
+  Documentation/modules.txt.
 
 Maximum number of cards
 CONFIG_WANPIPE_CARDS
-      Enter number of WANPIPE adapters installed in your machine.  The
-  driver can support up to 8 cards.  You may enter more that you
+  Enter number of WANPIPE adapters installed in your machine.  The
+  driver can support up to 8 cards.  You may enter more than you
   actually have if you plan to add more cards in the future without
   re-compiling the driver, but remember that in this case you'll waste
   some kernel memory (about 1K per card).
 
 WANPIPE X.25 support
 CONFIG_WANPIPE_X25
-      Say 'Y' to this option, if you are planning to connect WANPIPE
-  card to an X.25 network.  If you say 'N', the X.25 support will not
-  be included in the driver (saves about 16K of kernel memory).
+  Say Y to this option if you are planning to connect a WANPIPE card
+  to an X.25 network. You should then also have said Y to "CCITT X.25
+  Packet Layer" and "LAPB Data Link Driver", above. If you say N, the
+  X.25 support will not be included in the driver (saves about 16K of
+  kernel memory).
 
 WANPIPE Frame Relay support
 CONFIG_WANPIPE_FR
-      Say 'Y' to this option, if you are planning to connect WANPIPE
-  card to a frame relay network.  If you say 'N', the frame relay
+  Say Y to this option if you are planning to connect a WANPIPE card
+  to a frame relay network. You should then also have said Y to "Frame
+  Relay (DLCI) support", above. If you say N, the frame relay
   support will not be included in the driver (saves about 16K of
   kernel memory).
 
 WANPIPE PPP support
 CONFIG_WANPIPE_PPP
-      Say 'Y' to this option, if you are planning to connect WANPIPE
-  card to a leased line using Point-to-Point protocol (PPP).  If you
-  say 'N', the PPP support will not be included in the driver (saves
+  Say Y to this option if you are planning to connect a WANPIPE card
+  to a leased line using Point-to-Point protocol (PPP). You should
+  then also have said Y to "PPP (point-to-point) support", above. If
+  you say N, the PPP support will not be included in the driver (saves
   about 16K of kernel memory).
 
-
- Sun LANCE Ethernet support
- CONFIG_SUN_LANCE
-   This is support for lance ethernet cards on Sun workstations such as
 Sun LANCE Ethernet support
 CONFIG_SUN_LANCE
-  This is support for lance ethernet cards on Sun workstations such as
+  This is support for lance Ethernet cards on Sun workstations such as
   the Sparcstation IPC (any Sparc with a network interface 'le0' under
   SunOS basically). This driver is also available as a module ( = code
   which can be inserted in and removed from the running kernel
@@ -3772,7 +3875,7 @@
 
 Sun Intel Ethernet support
 CONFIG_SUN_INTEL
-  This is support for the intel ethernet cards on some Sun workstations
+  This is support for the Intel Ethernet cards on some Sun workstations
   (all those with a network interface 'ie0' under SunOS).
 
 Ethernet (10 or 100Mbit)
@@ -3782,9 +3885,9 @@
   companies. 10-base-2 or Thinnet (10 Mbps over coaxial cable, linking
   computers in a chain), 10-base-T (10 Mbps over twisted pair
   telephone cable, linking computers to a central hub) and
-  100-base-<whatever> (100 Mbps) are common types of ethernet. If your
+  100-base-<whatever> (100 Mbps) are common types of Ethernet. If your
   Linux machine will be connected to an Ethernet and you have an
-  ethernet network card installed in your computer, say Y here and
+  Ethernet network card installed in your computer, say Y here and
   read the Ethernet-HOWTO, available via ftp (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  Note that the answer to this
   question won't directly affect the kernel: saying N will just cause
@@ -3793,17 +3896,17 @@
 
 Western Digital/SMC cards
 CONFIG_NET_VENDOR_SMC
-  If you have a network (ethernet) card belonging to this class, say Y
+  If you have a network (Ethernet) card belonging to this class, say Y
   and read the Ethernet-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the
-  answer to this question doesn't directly affect the kernel:
-  saying N will just cause this configure script to skip all the
-  questions about Western Digital cards. If you say Y, you will be
-  asked for your specific card in the following questions.
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer to this
+  question doesn't directly affect the kernel: saying N will just
+  cause this configure script to skip all the questions about Western
+  Digital cards. If you say Y, you will be asked for your specific
+  card in the following questions.
 
 WD80*3 support
 CONFIG_WD80x3
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
   as a module ( = code which can be inserted in and removed from the
@@ -3814,14 +3917,14 @@
 
 SMC Ultra support
 CONFIG_ULTRA
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO.  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 smc-ultra.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. 
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. 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
+  smc-ultra.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.
   Important: There have been many reports that, with some motherboards 
   mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible,
   such as some BusLogic models) causes corruption problems with many 
@@ -3829,8 +3932,8 @@
   but keep it in mind if you have such a SCSI card and have problems.
 
 SMC Ultra32 EISA support
-CONFIG_ULTRA
-  If you have a network (ethernet) card of this type, say Y and read
+CONFIG_ULTRA32
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  This driver is also
   available as a module ( = code which can be inserted in and removed
@@ -3855,18 +3958,18 @@
 
 Racal-Interlan (Micom) NI cards
 CONFIG_NET_VENDOR_RACAL
-  If you have a network (ethernet) card belonging to this class, such
+  If you have a network (Ethernet) card belonging to this class, such
   as the NI5010, NI5210 or NI6210, say Y and read the Ethernet-HOWTO,
   available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer 
-  to this question doesn't directly affect the kernel:
-  saying N will just cause this configure script to skip all the
-  questions about NI cards. If you say Y, you will be asked for your
-  specific card in the following questions.
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO.
+  Note that the answer to this question doesn't directly affect the
+  kernel: saying N will just cause this configure script to skip all
+  the questions about NI cards. If you say Y, you will be asked for
+  your specific card in the following questions.
 
 NI5010 support
 CONFIG_NI5010
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that this is still
   experimental code. This driver is also available
@@ -3878,46 +3981,46 @@
 
 NI5210 support
 CONFIG_NI52
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. 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
   ni52.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. 
+  Documentation/networking/net-modules.txt.
 
 NI6510 support
 CONFIG_NI65
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. 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
   ni65.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. 
+  Documentation/networking/net-modules.txt.
 
 AMD LANCE and PCnet (AT1500 and NE2100) support
 CONFIG_LANCE
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. Some LinkSys cards are of
   this type.
 
 3COM cards
 CONFIG_NET_VENDOR_3COM
-  If you have a network (ethernet) card belonging to this class, say Y
+  If you have a network (Ethernet) card belonging to this class, say Y
   and read the Ethernet-HOWTO, available via ftp (user: anonymous)
   in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer to
   this question doesn't directly affect the kernel: saying N will just
   cause this configure script to skip all the questions about 3COM
   cards. If you say Y, you will be asked for your specific card in the
-  following questions. 
+  following questions.
 
 3c501 support
 CONFIG_EL1
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  Also, consider buying a new
   card, since the 3c501 is slow, broken, and obsolete: you will have
@@ -3931,7 +4034,7 @@
 
 3c503 support
 CONFIG_EL2
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
   as a module ( = code which can be inserted in and removed from the
@@ -3942,7 +4045,7 @@
 
 3c505 support
 CONFIG_ELPLUS
-  Information about this network (ethernet) card can be found in
+  Information about this network (Ethernet) card can be found in
   Documentation/networking/3c505.txt. If you have a card of this type,
   say Y and read the Ethernet-HOWTO, available via ftp (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to
@@ -3954,7 +4057,7 @@
 
 3c507 support
 CONFIG_EL16
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
   as a module ( = code which can be inserted in and removed from the
@@ -3965,7 +4068,7 @@
 
 3c523 support 
 CONFIG_ELMC
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
   as a module ( = code which can be inserted in and removed from the
@@ -3976,19 +4079,20 @@
 
 3c509/3c579 support
 CONFIG_EL3
-  If you have a network (ethernet) card belonging to the 3Com
+  If you have a network (Ethernet) card belonging to the 3Com
   EtherLinkIII series, say Y and read the Ethernet-HOWTO, available
   via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
   If you want to compile this as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
   say M here and read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt. The module will be called
-  3c509.o. If your card is not working you may need to use the DOS setup
-  disk to disable Plug & Play mode, and to select the default media type.
+  3c509.o. If your card is not working you may need to use the DOS
+  setup disk to disable Plug & Play mode, and to select the default
+  media type.
 
 3c590 series (592/595/597) "Vortex" support
 CONFIG_VORTEX
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. More specific information is
   in Documentation/networking/vortex.txt and in the comments at the
@@ -3996,12 +4100,11 @@
   module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
   Documentation/modules.txt as well as
-  Documentation/networking/net-modules.txt. The module will be called
-  3c59x.o.
+  Documentation/networking/net-modules.txt. 
 
 Other ISA cards
 CONFIG_NET_ISA
-  If your network (ethernet) card hasn't been mentioned yet and its
+  If your network (Ethernet) card hasn't been mentioned yet and its
   bus system (that's the way the components of the card talk to each
   other) is ISA (as opposed to EISA, VLB or PCI), say Y. Make sure you
   know the name of your card. Read the Ethernet-HOWTO, available via
@@ -4010,7 +4113,7 @@
   directly affect the kernel: saying N will just cause this configure
   script to skip all the remaining ISA network card questions.  If you
   say Y, you will be asked for your specific card in the following
-  questions. 
+  questions.
 
 Generic ARCnet support
 CONFIG_ARCNET
@@ -4022,7 +4125,7 @@
   below.
   You might also want to have a look at the Ethernet-HOWTO, available
   via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO
-  (even though ARCnet is not really ethernet). This driver is also
+  (even though ARCnet is not really Ethernet). 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 arcnet.o. If you want to compile it as a module, say M here
@@ -4031,7 +4134,7 @@
 
 Enable arc0e (ARCnet "ether-encap" packet format)
 CONFIG_ARCNET_ETH
-  This allows you to use "ethernet encapsulation" with your ARCnet
+  This allows you to use "Ethernet encapsulation" with your ARCnet
   card via the virtual arc0e device.  You only need arc0e if you want
   to talk to nonstandard ARCnet software, specifically,
   DOS/Windows-style "NDIS" drivers.  You do not need to say Y here to
@@ -4101,7 +4204,7 @@
 
 Cabletron E21xx support
 CONFIG_E2100
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
   as a module ( = code which can be inserted in and removed from the
@@ -4112,8 +4215,8 @@
 
 CS89x0 support
 CONFIG_CS89x0
-  Support for CS89x0 chipset based ethernet cards.  If you have a
-  network (ethernet) card of this type, say Y and read the
+  Support for CS89x0 chipset based Ethernet cards.  If you have a
+  network (Ethernet) card of this type, say Y and read the
   Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO as well as
   Documentation/networking/cs89x0.txt.  If you want to compile this as
@@ -4125,7 +4228,7 @@
 
 DEPCA support
 CONFIG_DEPCA
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO as well as
   drivers/net/depca.c.  If you want to compile this as a module ( =
@@ -4136,7 +4239,7 @@
 
 EtherWorks 3 support
 CONFIG_EWRK3
-  This driver supports the DE203, DE204 and DE205 network (ethernet)
+  This driver supports the DE203, DE204 and DE205 network (Ethernet)
   cards. If this is for you, say Y and read
   Documentation/networking/ewrk3.txt in the kernel source as well as
   the Ethernet-HOWTO, available via ftp (user: anonymous) from
@@ -4149,13 +4252,13 @@
 
 SEEQ8005 support
 CONFIG_SEEQ8005
-  This is a driver for the SEEQ 8005 network (ethernet) card. If this
+  This is a driver for the SEEQ 8005 network (Ethernet) card. If this
   is for you, read the Ethernet-HOWTO, available via ftp (user:
   anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO.
 
 AT1700 support
 CONFIG_AT1700
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
   as a module ( = code which can be inserted in and removed from the
@@ -4166,21 +4269,21 @@
 
 FMV-181/182/183/184 support
 CONFIG_FMV18X
-  If you have a Fujitsu FMV-181/182/183/184 network (ethernet) card,
+  If you have a Fujitsu FMV-181/182/183/184 network (Ethernet) card,
   say Y and read the Ethernet-HOWTO, available via ftp (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. 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 fmv18x.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.  If you use FMV-183 or
+  Documentation/networking/net-modules.txt. If you use an FMV-183 or
   FMV-184 and it is not working, you may need to disable Plug & Play
   mode of the card.
 
 EtherExpressPro support
 CONFIG_EEXPRESS_PRO
-  If you have a network (ethernet) card of this type, say Y. Note
-  however that the EtherExpressPro 100 ethernet card has its own
+  If you have a network (Ethernet) card of this type, say Y. Note
+  however that the EtherExpressPro 100 Ethernet card has its own
   separate driver. Please read the Ethernet-HOWTO, available via ftp
   (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This
   driver is also available as a module ( = code which can be inserted
@@ -4191,7 +4294,7 @@
 
 EtherExpress support
 CONFIG_EEXPRESS
-  If you have an EtherExpress16 network (ethernet) card, say Y and
+  If you have an EtherExpress16 network (Ethernet) card, say Y and
   read the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the Intel
   EtherExpress16 card used to be regarded as a very poor choice
@@ -4206,7 +4309,7 @@
 AT&T WaveLAN & DEC RoamAbout DS support
 CONFIG_WAVELAN
   The Lucent Wavelan (formerly NCR and AT&T ; or DEC RoamAbout DS) is
-  a Radio LAN (wireless ethernet-like Local Area Network) using the
+  a Radio LAN (wireless Ethernet-like Local Area Network) using the
   radio frequencies 900 MHz and 2.4 GHz.
   This driver support the ISA version of the Wavelan card. A driver
   for the pcmcia hardware is available in David Hinds's pcmcia
@@ -4226,7 +4329,7 @@
 
 HP PCLAN+ (27247B and 27252A) support
 CONFIG_HPLAN_PLUS
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
   as a module ( = code which can be inserted in and removed from the
@@ -4237,7 +4340,7 @@
 
 HP PCLAN (27245 and other 27xxx series) support
 CONFIG_HPLAN
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
   as a module ( = code which can be inserted in and removed from the
@@ -4248,7 +4351,7 @@
 
 HP 10/100VG PCLAN (ISA, EISA, PCI) support
 CONFIG_HP100
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to compile this
   as a module ( = code which can be inserted in and removed from the
@@ -4259,9 +4362,9 @@
 
 NE2000/NE1000 support
 CONFIG_NE2000
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. Many ethernet cards without a
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. Many Ethernet cards without a
   specific driver are compatible with NE2000. 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
@@ -4271,7 +4374,7 @@
 
 SK_G16 support
 CONFIG_SK_G16
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.
 
@@ -4280,22 +4383,21 @@
   This is another class of network cards which attach directly to the
   bus. If you have one of those, say Y and read the Ethernet-HOWTO,
   available via ftp (user: anonymous) from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you are unsure, say Y. 
-  Note that the answer to this question doesn't
-  directly affect the kernel: saying N will just cause this configure
-  script to skip all the questions about this class of network
-  cards. If you say Y, you will be asked for your specific card in the
-  following questions.
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer to this
+  question doesn't directly affect the kernel: saying N will just
+  cause this configure script to skip all the questions about this
+  class of network cards. If you say Y, you will be asked for your
+  specific card in the following questions. If you are unsure, say Y.
 
 AMD PCnet32 (VLB and PCI) support
 CONFIG_PCNET32
-  If you have a PCnet32 or PCnetPCI based network (ethernet) card, say
+  If you have a PCnet32 or PCnetPCI based network (Ethernet) card, say
   Y here and read the Ethernet-HOWTO, available via ftp (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. 
 
 Ansel Communications EISA 3200 support
 CONFIG_AC3200
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
   as a module ( = code which can be inserted in and removed from the
@@ -4306,7 +4408,7 @@
 
 Racal-Interlan EISA ES3210 support
 CONFIG_ES3210
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
   as a module ( = code which can be inserted in and removed from the
@@ -4315,9 +4417,9 @@
   Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt.
 
-Apricot Xen-II on board ethernet
+Apricot Xen-II on board Ethernet
 CONFIG_APRICOT
-  If you have a network (ethernet) controller of this type, say Y and
+  If you have a network (Ethernet) controller of this type, say Y and
   read the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to compile this
   as a module ( = code which can be inserted in and removed from the
@@ -4328,7 +4430,7 @@
 
 Generic DECchip & DIGITAL EtherWORKS PCI/EISA
 CONFIG_DE4X5
-  This is support for the DIGITAL series of PCI/EISA ethernet
+  This is support for the DIGITAL series of PCI/EISA Ethernet
   cards. These include the DE425, DE434, DE435, DE450 and DE500
   models. If you have a network card of this type, say Y and read the
   Ethernet-HOWTO, available via ftp (user: anonymous) in
@@ -4342,7 +4444,7 @@
 
 DECchip Tulip (dc21x4x) PCI support
 CONFIG_DEC_ELCP
-  This driver is developed for the SMC EtherPower series ethernet
+  This driver is developed for the SMC EtherPower series Ethernet
   cards and also works with cards based on the DECchip
   21040/21041/21140 (Tulip series) chips. Some LinkSys PCI cards are
   of this type. (If your card is NOT SMC EtherPower 10/100 PCI
@@ -4361,7 +4463,7 @@
 Digi Intl. RightSwitch support
 CONFIG_DGRS
   This is support for the Digi International RightSwitch series of
-  PCI/EISA ethernet switch cards.  These include the SE-4 and the SE-6
+  PCI/EISA Ethernet switch cards.  These include the SE-4 and the SE-6
   models. If you have a network card of this type, say Y and read the
   Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  More specific information is
@@ -4370,29 +4472,29 @@
   from the running kernel whenever you want). The module will be
   called dgrs.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.
+  Documentation/networking/net-modules.txt. 
 
 EtherExpressPro/100 support
 CONFIG_EEXPRESS_PRO100
-  If you have an Intel EtherExpressPro 100 PCI network (ethernet)
+  If you have an Intel EtherExpressPro 100 PCI network (Ethernet)
   card, say Y and read the Ethernet-HOWTO, available via ftp (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. 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 eepro100.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.
+  Documentation/networking/net-modules.txt. 
 
 ICL EtherTeam 16i/32 support
 CONFIG_ETH16I
-  If you have a network (ethernet) card of this type, say Y and read
+  If you have a network (Ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. 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
   eth16i.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.
+  Documentation/networking/net-modules.txt. 
 
 TI ThunderLAN support (EXPERIMENTAL)
 CONFIG_TLAN
@@ -4406,44 +4508,44 @@
 Zenith Z-Note support
 CONFIG_ZNET
   The Zenith Z-Note notebook computer has a built-in network
-  (ethernet) card, and this is the Linux driver for it. Note that the
+  (Ethernet) card, and this is the Linux driver for it. Note that the
   IBM Thinkpad 300 is compatible with the Z-Note and is also supported
   by this driver. Read the Ethernet-HOWTO, available via ftp (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
 
 Pocket and portable adapters
 CONFIG_NET_POCKET
-  Cute little network (ethernet) devices which attach to the parallel
+  Cute little network (Ethernet) devices which attach to the parallel
   port ("pocket adapters"), commonly used with laptops. If you have
   one of those, say Y and read the Ethernet-HOWTO, available via ftp
   (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you
   want to plug a network card into the PCMCIA slot of your laptop
   instead (PCMCIA is the standard for credit card size extension cards
-  used by all modern laptops), look in
-  cb-iris.stanford.edu:/pub/pcmcia and say N here.  Note that the
-  answer to this question doesn't directly affect the kernel: saying N
-  will just cause this configure script to skip all the questions
-  about this class of network devices.  If you say Y, you will be
-  asked for your specific device in the following questions.  If you
-  plan to use more than one network device under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  used by all modern laptops), look on the ftp site (user: anonymous)
+  cb-iris.stanford.edu:/pub/pcmcia and say N here.  
+  Laptop user want to read the Linux Laptop homepage at
+  http://www.cs.utexas.edu/users/kharker/linux-laptop/ (to browse the
+  WWW, you need to have access to a machine on the Internet that has a
+  program like lynx or netscape).
+  Note that the answer to this question doesn't directly affect the
+  kernel: saying N will just cause this configure script to skip all
+  the questions about this class of network devices.  If you say Y,
+  you will be asked for your specific device in the following
+  questions.
 
 AT-LAN-TEC/RealTek pocket adapter support
 CONFIG_ATP
-  This is a network (ethernet) device which attaches to your parallel
+  This is a network (Ethernet) device which attaches to your parallel
   port. Read drivers/net/atp.c as well as the Ethernet-HOWTO,
   available via ftp (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. If
-  you plan to use more than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you intend to use
-  this driver, you should have said N to the Parallel Printer support,
-  because the two drivers don't like each other.
+  you intend to use this driver, you should have said N to the
+  Parallel Printer support, because the two drivers don't like each
+  other.
 
 D-Link DE600 pocket adapter support
 CONFIG_DE600
-  This is a network (ethernet) device which attaches to your parallel
+  This is a network (Ethernet) device which attaches to your parallel
   port. Read Documentation/networking/DLINK.txt as well as the
   Ethernet-HOWTO, available via ftp (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. It is
@@ -4456,7 +4558,7 @@
 
 D-Link DE620 pocket adapter support
 CONFIG_DE620
-  This is a network (ethernet) device which attaches to your parallel
+  This is a network (Ethernet) device which attaches to your parallel
   port. Read Documentation/networking/DLINK.txt as well as the
   Ethernet-HOWTO, available via ftp (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. It is
@@ -4470,14 +4572,19 @@
 Token Ring driver support
 CONFIG_TR
   Token Ring is IBM's way of communication on a local network; the
-  rest of the world uses ethernet. If you are connected to a token
-  ring network and want to use your Token Ring card under Linux, say Y.
-  Most people can say N here.
+  rest of the world uses Ethernet. To participate on a Token Ring
+  network, you need a special Token ring network card. If you are
+  connected to such a Token Ring network and want to use your Token
+  Ring card under Linux, say Y here and read the Token-Ring
+  mini-HOWTO, available via ftp (user:anonymous) from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. Most people can say N here.
 
 IBM Tropic chipset based adapter support
 CONFIG_IBMTR
   This is support for all IBM Token Ring cards that don't use DMA. If
-  you have such a beast, say Y, otherwise N. Warning: this driver will
+  you have such a beast, say Y and read the Token-Ring mini-HOWTO,
+  available via ftp (user:anonymous) from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. Warning: this driver will
   almost definitely fail if more than one active Token Ring card is
   present. This driver is also available as a module ( = code which
   can be inserted in and removed from the running kernel whenever you
@@ -4500,7 +4607,7 @@
 FDDI driver support
 CONFIG_FDDI
   Fiber Distributed Data Interface is a high speed local area network
-  design; essentially a replacement for high speed ethernet. FDDI can
+  design; essentially a replacement for high speed Ethernet. FDDI can
   run over copper or fiber. If you are connected to such a network and
   want a driver for the FDDI card in your computer, say Y here (and
   then also Y to the driver for your FDDI card, below). Most people
@@ -4630,12 +4737,12 @@
   linux/include/linux/sbpcd.h before compiling the new kernel. Read
   the file Documentation/cdrom/sbpcd.
 
-Aztech/Orchid/Okano/Wearnes/TXC/CyDROM  CDROM support
+Aztech/Orchid/Okano/Wearnes/TXC/CyDROM CDROM support
 CONFIG_AZTCD
   This is your driver if you have an Aztech CDA268-01A, Orchid
   CD-3110, Okano or Wearnes CDD110, Conrad TXC, or CyCDROM CR520 or
   CR540 CDROM drive.  This driver - just like all these CDROM drivers
-  - is NOT for CDROM drives with IDE/ATAPI interface, such as Aztech
+  - is NOT for CDROM drives with IDE/ATAPI interfaces, such as Aztech
   CDA269-031SE. If you say Y here, you should also say Y to "ISO9660
   cdrom filesystem support" below, because that's the filesystem used
   on CDROMs. Please also read the file Documentation/cdrom/aztcd. This
@@ -4708,14 +4815,14 @@
 CONFIG_CDI_INIT
   If you want to include boot-time initialization of any cdrom
   interface card that is software configurable, say Y here.  Currently
-  only the ISP16/MAD16/Mozart soundcards with built-in cdrom
+  only the ISP16/MAD16/Mozart sound cards with built-in cdrom
   interfaces are supported.  Note that the answer to this question
   doesn't directly affect the kernel: saying N will just cause this
   configure script to skip all the questions about these CDROM drives.
 
 ISP16/MAD16/Mozart soft configurable cdrom interface support
 CONFIG_ISP16_CDI
-  These are soundcards with with built-in cdrom interfaces using the
+  These are sound cards with with built-in cdrom interfaces using the
   OPTi 82C928 or 82C929 chips. Say Y here to have them detected and
   possibly configured at boot time. In addition, You'll have to say Y
   to a driver for the particular cdrom drive you have attached to the
@@ -4738,7 +4845,7 @@
 Quota support
 CONFIG_QUOTA
   If you say Y here, you will be able to set per user limits for disk
-  usage (also called diskquotas). Currently, it works only for the
+  usage (also called disk quotas). Currently, it works only for the
   ext2 filesystem. You need additional software in order to use quota
   support; for details, read the Quota mini-HOWTO, available via ftp
   (user: anonymous) in
@@ -4748,7 +4855,7 @@
 Online mirror support
 CONFIG_OMIRR
   omirr is a package for _symmetric_ mirroring of files over the
-  internet. In contrast to rdist, the online mirror daemon (omirrd)
+  Internet. In contrast to rdist, the online mirror daemon (omirrd)
   is running all the time and transfers any changes on the file system
   as soon as possible to all other servers. Symmetric means that all
   servers have equal rights in changing a file: the last changer of a
@@ -4898,11 +5005,11 @@
 Minix fs support
 CONFIG_MINIX_FS
   Minix is a simple operating system used in many classes about
-  OS's. The minix filesystem (= method to organize files on a harddisk
+  OS's. The minix filesystem (= method to organize files on a hard disk
   partition or a floppy disk) was the original filesystem for Linux,
   has been superseded by the second extended filesystem ext2fs but is
   still used for root/boot and other floppies or ram disks since it is
-  leaner. You don't want to use it on your harddisk because of certain
+  leaner. You don't want to use it on your hard disk because of certain
   built-in restrictions. This option will enlarge your kernel by about
   25 kB. Everyone should say Y or M so that they are able to read this
   common floppy format.  If you want to compile this as a module ( =
@@ -4915,7 +5022,7 @@
 Second extended fs support
 CONFIG_EXT2_FS
   This is the de facto standard Linux filesystem (= method to organize
-  files on a storage device) for harddisks. You want to say Y, unless
+  files on a storage device) for hard disks. You want to say Y, unless
   you intend to use Linux exclusively from inside a DOS partition
   using the umsdos filesystem. The advantage of the latter is that you
   can get away without repartitioning your hard drive (which often
@@ -4968,23 +5075,25 @@
 fat fs support
 CONFIG_FAT_FS
   If you want to use one of the FAT-based filesystems (the MS-DOS,
-  VFAT (Windows'95) and UMSDOS (used to run Linux on top of an
+  VFAT (Windows 95) and UMSDOS (used to run Linux on top of an
   ordinary DOS partition) filesystems), then you must include FAT
   support. This is not a filesystem in itself, but it provides the
-  foundation for the other filesystems. This option will enlarge your
-  kernel by about 24 kB. If unsure, say Y. If you want to compile this
-  as a module however ( = code which can be inserted in and removed
-  from the running kernel whenever you want), say M here and read
-  Documentation/modules.txt. The module will be called fat.o. Note
-  that if you compile the FAT support as a module, you cannot compile
-  any of the FAT-based filesystems into the kernel - they will have to
-  be modules as well.  The filesystem of your root partition cannot be
-  a module, so don't say M here if you intend to use UMSDOS as your
-  root filesystem.
+  foundation for the other filesystems. It is now also becoming
+  possible to read and write compressed FAT filesystems; read
+  Documentation/filesystems/fat_cvf.txt for details. This option will
+  enlarge your kernel by about 24 kB. If unsure, say Y. If you want to
+  compile this as a module however ( = code which can be inserted in
+  and removed from the running kernel whenever you want), say M here
+  and read Documentation/modules.txt. The module will be called fat.o.
+  Note that if you compile the FAT support as a module, you cannot
+  compile any of the FAT-based filesystems into the kernel - they will
+  have to be modules as well. The filesystem of your root partition
+  cannot be a module, so don't say M here if you intend to use UMSDOS
+  as your root filesystem.
 
 msdos fs support
 CONFIG_MSDOS_FS
-  This allows you to mount MSDOS partitions of your harddrive (unless
+  This allows you to mount MSDOS partitions of your hard drive (unless
   they are compressed; to access compressed MSDOS partitions under
   Linux, you can either use the DOS emulator DOSEMU, described in the
   DOSEMU-HOWTO, available via ftp (user: anonymous) at
@@ -4998,10 +5107,10 @@
   which doesn't require the msdos filesystem support.  If you want to
   use umsdos, the Unix-like filesystem on top of DOS, which allows you
   to run Linux from within a DOS partition without repartitioning,
-  you'll have to say Y or M here. If your have Windows'95 or Windows
+  you'll have to say Y or M here. If you have Windows 95 or Windows
   NT installed on your MSDOS partitions, you should use the VFAT
   filesystem instead, or you will not be able to see the long
-  filenames generated by Windows'95 / Windows NT. This option will
+  filenames generated by Windows 95 / Windows NT. This option will
   enlarge your kernel by about 7 kB. If unsure, say Y. This will only
   work if you said Y to "fat fs support" as well. If you want to
   compile this as a module however ( = code which can be inserted in
@@ -5011,10 +5120,10 @@
 
 vfat fs support
 CONFIG_VFAT_FS
-  This allows you to mount MSDOS partitions of your harddrive. It
+  This allows you to mount MSDOS partitions of your hard drive. It
   will let you use filenames in a way compatible with the long 
-  filenames used by Windows'95 and Windows NT fat-based (not NTFS)
-  partitions. It does not support Windows'95 compressed filesystems.
+  filenames used by Windows 95 and Windows NT fat-based (not NTFS)
+  partitions. It does not support Windows 95 compressed filesystems.
   You cannot use the VFAT filesystem for your root partition; use
   UMSDOS instead. This option enlarges your kernel by about 10 kB and
   it only works if you said Y to the "fat fs support" above. Please read
@@ -5027,7 +5136,7 @@
 umsdos: Unix like fs on top of std MSDOS fs
 CONFIG_UMSDOS_FS
   Say Y here if you want to run Linux from within an existing DOS
-  partition of your harddrive. The advantage of this is that you can
+  partition of your hard drive. The advantage of this is that you can
   get away without repartitioning your hard drive (which often implies
   backing everything up and restoring afterwards) and hence you're
   able to quickly try out Linux or show it to your friends; the
@@ -5049,37 +5158,36 @@
 CONFIG_PROC_FS
   This is a virtual filesystem providing information about the status
   of the system. "Virtual" means that it doesn't take up any space on
-  your harddisk: the files are created on the fly when you access
-  them. Also, you cannot read the files with less: you need to use
-  more or cat. The filesystem is explained in the Kernel Hacker's
-  Guide at http://www.redhat.com:8080/HyperNews/get/khg.html on the
-  WWW (to browse the WWW, you need to have access to a machine on the
-  Internet that has a program like lynx or netscape), and also on the
-  proc(8) manpage ("man 8 proc").  This option will enlarge your
-  kernel by about 18 kB. It's totally cool; for example, "cat
-  /proc/interrupts" gives information about what the different IRQs
-  are used for at the moment (there is a small number of Interrupt
-  ReQuest lines in your computer that are used by the attached devices
-  to gain the CPU's attention - often a source of trouble if two
-  devices are mistakenly configured to use the same IRQ). Several
-  programs depend on this, so everyone should say Y here.
+  your hard disk: the files are created on the fly when you access
+  them. Also, you cannot read the files with older version of the
+  program less: you need to use more or cat. The filesystem is
+  explained in the Kernel Hacker's Guide at
+  http://www.redhat.com:8080/HyperNews/get/khg.html on the WWW (to
+  browse the WWW, you need to have access to a machine on the Internet
+  that has a program like lynx or netscape), and also on the proc(8)
+  manpage ("man 8 proc").  This option will enlarge your kernel by
+  about 18 kB. It's totally cool; for example, "cat /proc/interrupts"
+  gives information about what the different IRQs are used for at the
+  moment (there is a small number of Interrupt ReQuest lines in your
+  computer that are used by the attached devices to gain the CPU's
+  attention - often a source of trouble if two devices are mistakenly
+  configured to use the same IRQ). Several programs depend on this, so
+  everyone should say Y here.
 
 NFS filesystem support
 CONFIG_NFS_FS
   If you are connected to some other (usually local) Unix computer
-  (using SLIP, PLIP, PPP or ethernet) and want to mount files residing
+  (using SLIP, PLIP, PPP or Ethernet) and want to mount files residing
   on that computer (the NFS server) using the Network File Sharing
   protocol, say Y. "Mounting files" means that the client can access
   the files with usual UNIX commands as if they were sitting on the
-  client's harddisk. For this to work, the server must run the
+  client's hard disk. For this to work, the server must run the
   programs nfsd and mountd (but does not need to have NFS filesystem
   support enabled). NFS is explained in the Network Administrator's
   Guide, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/LDP, and on its man page: "man
-  nfs". There is also a NFS-FAQ in
-  sunsite.unc.edu:/pub/Linux/docs/faqs which presumes that you know
-  the basics of NFS already. If you say Y here, you should have said Y
-  to TCP/IP networking also. This option would enlarge your kernel by
+  sunsite.unc.edu:/pub/Linux/docs/LDP, on its man page: "man nfs", and
+  in the NFS-HOWTO. If you say Y here, you should have said Y to
+  TCP/IP networking also. This option would enlarge your kernel by
   about 27 kB. This filesystem 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 nfs.o. If you want to
@@ -5087,34 +5195,35 @@
   Documentation/modules.txt. If you configure a diskless machine which
   will mount its root filesystem over nfs (in order to do that, check
   out the netboot package, available via ftp (user: anonymous) from
-  sunsite.unc.edu in /pub/Linux/system/boot/ethernet/, extract with "tar
-  xzvf filename", and say Y to "Root file system on NFS" below), then
-  you cannot compile this driver as a module. If you don't know what
-  all this is about, say N.
+  sunsite.unc.edu in /pub/Linux/system/boot/ethernet/, extract with
+  "tar xzvf filename", and say Y to "Root file system on NFS" below),
+  then you cannot compile this driver as a module. If you don't know
+  what all this is about, say N.
 
 Root file system on NFS
 CONFIG_ROOT_NFS
   If you want your Linux box to mount its whole root filesystem from
   some other computer over the net via NFS (presumably because your
-  box doesn't have a harddisk), say Y. Read Documentation/nfsroot.txt
+  box doesn't have a hard disk), say Y. Read Documentation/nfsroot.txt
   for details. Most people say N here. 
 
 NFS server support
 CONFIG_NFSD
   If you want your Linux box to act as a NFS *server*, so that other
-  computers on your local network which support NFS can access files
-  on your box transparently, you have two options: you can use the
-  self-contained user space program nfsd, in which case you should say
-  N here, or you can say Y and use this new experimental kernel based
-  NFS server. The advantage of the kernel based solution is that it is
-  faster; it might not be completely stable yet, though. You will need
-  the support software from the linux-nfs package available at
-  ftp://ftp.mathematik.th-darmstadt.de/pub/linux/okir/.  
-  The nfs server is also available as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you
-  want). The module is called nfsd.o. If you want to compile it as a
-  module, say M here and read Documentation/modules.txt. If unsure,
-  say N.
+  computers on your local network which support NFS can access certain
+  directories on your box transparently, you have two options: you can
+  use the self-contained user space program nfsd, in which case you
+  should say N here, or you can say Y and use this new experimental
+  kernel based NFS server. The advantage of the kernel based solution
+  is that it is faster; it might not be completely stable yet, though.
+  You will need the support software from the linux-nfs package
+  available at ftp://ftp.mathematik.th-darmstadt.de/pub/linux/okir/.
+  Please read the NFS-HOWTO, available via ftp (user: anonymous) from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. The nfs server is also
+  available as a module ( = code which can be inserted in and removed
+  from the running kernel whenever you want). The module is called
+  nfsd.o. If you want to compile it as a module, say M here and read
+  Documentation/modules.txt. If unsure, say N.
 
 BOOTP support
 CONFIG_RNFS_BOOTP
@@ -5142,9 +5251,9 @@
 OS/2 HPFS filesystem support (read only)
 CONFIG_HPFS_FS
   OS/2 is IBM's operating system for PC's, the same as Warp, and HPFS
-  is the filesystem used for organizing files on OS/2 harddisk
+  is the filesystem used for organizing files on OS/2 hard disk
   partitions. Say Y if you want to be able to read files from an OS/2
-  HPFS partition of your harddrive. OS/2 floppies however are in
+  HPFS partition of your hard drive. OS/2 floppies however are in
   regular MSDOS format, so you don't need this option in order to be
   able to read them. Read Documentation/filesystems/hpfs.txt. This
   filesystem is also available as a module ( = code which can be
@@ -5175,14 +5284,14 @@
 
 System V and Coherent filesystem support
 CONFIG_SYSV_FS
-  SCO, Xenix and Coherent are commercial Unix systems for intel
+  SCO, Xenix and Coherent are commercial Unix systems for Intel
   machines. Saying Y here would allow you to read and write to and
-  from their floppies and harddisk partitions. If you have a floppy or
-  harddisk partition like that, it is probable that they contain
+  from their floppies and hard disk partitions. If you have a floppy or
+  hard disk partition like that, it is probable that they contain
   binaries from those other Unix systems; in order to run these
   binaries, you will want to install iBCS2 (iBCS2 [Intel Binary
   Compatibility Standard] is a kernel module which lets you run SCO,
-  Xenix, Wyse, Unix Ware, Dell Unix and System V programs under Linux
+  Xenix, Wyse, UnixWare, Dell Unix and System V programs under Linux
   and is often needed to run commercial software, most prominently
   WordPerfect. It's in tsx-11.mit.edu:/pub/linux/BETA). If you only
   intend to mount files from some other Unix over the network using
@@ -5200,6 +5309,40 @@
   read Documentation/modules.txt. The module will be called sysv.o. If
   you haven't heard about all of this before, it's safe to say N.
 
+Amiga FFS filesystem support
+CONFIG_AFFS_FS
+  If you say Y here, you will be able to mount floppies and hard drive
+  partitions which were formatted with the Amiga FFS filesystem. Full
+  read-write support is available for most versions of FFS, see
+  Documentation/filesystems/affs.txt for details. This filesystem
+  support 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 affs.o. If you want to compile it as a module, say M
+  here and read Documentation/modules.txt.
+
+Apple Macintosh filesystem support (experimental)
+CONFIG_HFS_FS
+  If you say Y here, you will be able to mount Macintosh-formatted
+  floppy disks and hard drive partitions with full read-write access.
+  Please read fs/hfs/HFS.txt to learn about the available mount
+  options. This filesystem support 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 hfs.o. If you want to
+  compile it as a module, say M here and read
+  Documentation/modules.txt.
+
+ROM filesystem support
+CONFIG_ROMFS_FS
+  This is a very small read-only filesystem mainly intended for
+  initial ram disks of installation disks, but it could be used for
+  other read-only media as well. Read
+  Documentation/filesystems/romfs.txt for details. This filesystem
+  support 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 romfs.o. If you want to compile it as a module, say
+  M here and read Documentation/modules.txt. If you don't know whether
+  you need it, then you don't need it: say N.
+
 Kernel automounter support (experimental)
 CONFIG_AUTOFS_FS
   The automounter is a tool to automatically mount remote filesystems
@@ -5219,7 +5362,7 @@
 CONFIG_UFS_FS
   BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD
   and NeXTstep) use a filesystem called UFS. Some System V Unixes can
-  create and mount harddisk partitions and diskettes using this
+  create and mount hard disk partitions and diskettes using this
   filesystem as well. Saying Y here allows you to mount these
   partitions and diskettes read-only. If you only intend to mount
   files from some other Unix over the network using NFS, you don't
@@ -5239,7 +5382,7 @@
 
 BSD disklabel (FreeBSD partition tables) support
 CONFIG_BSD_DISKLABEL
-  FreeBSD uses its own harddisk partition scheme on your PC. It
+  FreeBSD uses its own hard disk partition scheme on your PC. It
   requires only one entry in the primary partition table of your disk
   and manages it similarly to DOS extended partitions, putting in its
   first sector a new partition table in disklabel format. Saying Y
@@ -5250,7 +5393,7 @@
 
 SMD disklabel (Sun partition tables) support
 CONFIG_SMD_DISKLABEL
-  Like most systems, SunOS uses its own harddisk partition table
+  Like most systems, SunOS uses its own hard disk partition table
   format, incompatible with all others. Saying Y here allows you to
   read these partition tables and further mount SunOS disks read-only
   from within Linux if you have also said Y to "BSD ufs filesystem
@@ -5262,31 +5405,49 @@
   preferably "info tar"). If you don't know what all this is about,
   say N.
 
+Solaris (x86) partition table support
+CONFIG_SOLARIS_X86_PARTITION
+  Say Y here if you have a hard drive that will be accessed from Linux
+  and from Solaris x86. This is NOT for Solaris on the sparc
+  architecture. 
+
+ADFS filesystem support (read only) (EXPERIMENTAL)
+CONFIG_ADFS_FS
+  Acorn Disc Filing System is the standard filesystem of the Risc OS
+  operating system which runs on Acorn's StrongARM Risc PC computers.
+  If you say Y here, Linux will be able to read from ADFS partitions
+  on hard drives and from ADFS-formatted floppy disks. This code is
+  also available as a module called adfs.o ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  If you want to compile it as a module, say M here and read
+  Documentation/modules.txt.
+
 Macintosh partition map support
 CONFIG_MAC_PARTITION
-  Say Y here if you want your Linux system to be able to read
-  the partition tables of Macintosh hard drives, and thus use
-  partitions on those drives.
+  Say Y here if you want your Linux system to be able to read the
+  partition tables of Macintosh hard drives, and thus use partitions
+  on those drives.
 
 SMB filesystem support (to mount WfW shares etc..)
 CONFIG_SMB_FS
-  SMB (Server Message Buffer) is the protocol Windows for Workgroups
+  SMB (Server Message Block) is the protocol Windows for Workgroups
   (WfW), Windows 95, Windows NT and Lan Manager use to share files and
   printers over local networks. Saying Y here allows you to mount
   their filesystems (often called "shares" in this context) and access
   them just like any other unix directory. Currently, this works only
   if the Windows machines use TCP/IP as the underlying transport
-  protocol, and not Netbeui.  For details, read
-  Documentation/filesystems/smbfs.txt.  Note: if you just want your
-  box to act as an SMB *server* and make files and printing services
-  available to Windows clients (which need to have a TCP/IP stack),
-  you don't need to say Y here; you can use the program samba
-  (available via ftp (user: anonymous) in
+  protocol, and not Netbeui. For details, read
+  Documentation/filesystems/smbfs.txt and the SMB-HOWTO, available via
+  ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO.
+  Note: if you just want your box to act as an SMB *server* and make
+  files and printing services available to Windows clients (which need
+  to have a TCP/IP stack), you don't need to say Y here; you can use
+  the program samba (available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/system/network/samba) for that. General
   information about how to connect Linux, Windows machines and Macs is
   on the WWW at http://eats.com/linux_mac_win.html (to browse the WWW,
   you need to have access to a machine on the Internet that has a
-  program like lynx or netscape).  If you want to compile the SMB
+  program like lynx or netscape). If you want to compile the SMB
   support as a module ( = code which can be inserted in and removed
   from the running kernel whenever you want), say M here and read
   Documentation/modules.txt. The module will be called smbfs.o. Most
@@ -5294,22 +5455,23 @@
 
 Coda filesystem support
 CONFIG_CODA_FS
-  CODA is an advanced network filesystem.  It has support for
-  disconnected operation for laptops, read/write server replication,
-  persistent client caches and write back caching.
-  By saying Y here you are compiling kernel support for Coda clients
-  into the Linux kernel.  You will need user level code as well, both
-  for the client and server. Servers are currently user level,
-  i.e. need no kernel support. For technical information, read
+  CODA is an advanced network filesystem, similar to NFS in that it
+  enables you to mount filesystems of a remote server and access them
+  with regular Unix commands as if they were sitting on your hard
+  disk. It has support for disconnected operation for laptops,
+  read/write server replication, persistent client caches and write
+  back caching. By saying Y here you are compiling kernel support for
+  Coda clients into the Linux kernel. You will need user level code as
+  well, both for the client and server. Servers are currently user
+  level, i.e. need no kernel support. For technical information, read
   Documentation/filesystems/coda.txt. 
   If you want to compile the coda client support as a module ( = code
   which can be inserted in and removed from the running kernel
-  whenever you want), say M here and read
-  Documentation/modules.txt. The module will be called coda.o. 
-  For further information see http://www.coda.cs.cmu.edu (to browse
-  the WWW, you need to have access to a machine on the Internet that
-  has a program like lynx or netscape) or contact Peter Braam
-  <braam@cs.cmu.edu>.
+  whenever you want), say M here and read Documentation/modules.txt.
+  The module will be called coda.o. For further information see
+  http://www.coda.cs.cmu.edu (to browse the WWW, you need to have
+  access to a machine on the Internet that has a program like lynx or
+  netscape) or contact Peter Braam <braam@cs.cmu.edu>.
 
 SMB Win95 bug work-around
 CONFIG_SMB_WIN95
@@ -5322,7 +5484,7 @@
 CONFIG_NCP_FS
   NCP (NetWare Core Protocol) is a protocol that runs over IPX and is
   used by Novell NetWare clients to talk to file servers. It is to IPX
-  what nfs is to tcp/ip, if that helps. Saying Y here allows you to
+  what NFS is to TCP/IP, if that helps. Saying Y here allows you to
   mount NetWare file server volumes and to access them just like any
   other Unix directory. For details, please read the file
   Documentation/filesystems/ncpfs.txt in the kernel source and the
@@ -5334,84 +5496,71 @@
 
 Packet signatures
 CONFIG_NCPFS_PACKET_SIGNING
-  NCP allows to sign packets for stronger security. If you want
+  NCP allows packets to be signed for stronger security. If you want
   security, say Y. Normal users can leave it off. To be able to use
   packet signing you must use ncpfs > 2.0.12.
 
 Proprietary file locking
 CONFIG_NCPFS_IOCTL_LOCKING
-  Allows locking of records on remote volumes. Say N unless you have special
-  applications which are able to utilize this locking scheme.
+  Allows locking of records on remote volumes. Say N unless you have
+  special applications which are able to utilize this locking scheme.
 
 Clear remove/delete inhibit when needed
 CONFIG_NCPFS_STRONG
-  Allows manipulation of files flagged as Delete or Rename Inhibit.
-  To use this feature you must mount volumes with the ncpmount parameter
+  Allows manipulation of files flagged as Delete or Rename Inhibit. To
+  use this feature you must mount volumes with the ncpmount parameter
   "-s" (ncpfs-2.0.12 and newer). Say Y unless you are not mounting
   volumes with -f 444.
 
 Use NFS namespace when available
 CONFIG_NCPFS_NFS_NS
-  Allows you to utilize NFS namespace on NetWare servers. It brings you
-  case sensitive filesystems. Say Y. You can disable it at mount-time with
-  the -N nfs parameter of ncpmount.
+  Allows you to utilize NFS namespace on NetWare servers. It brings
+  you case sensitive filenames. Say Y. You can disable it at
+  mount-time with the `-N nfs' parameter of ncpmount.
 
 Use OS2/LONG namespace when available
 CONFIG_NCPFS_OS2_NS
-  Allows you to utilize OS2/LONG namespace on NetWare servers. Filenames 
-  in this namespace are limited to 255 characters, they are case 
-  insensitive, and case in names is preserved. 
-  Say Y. You can disable it at mount time with the -N os2 parameter of 
-  ncpmount.
+  Allows you to utilize OS2/LONG namespace on NetWare servers.
+  Filenames in this namespace are limited to 255 characters, they are
+  case insensitive, and case in names is preserved. Say Y. You can
+  disable it at mount time with the -N os2 parameter of ncpmount.
 
 Allow mounting of volume subdirectories
 CONFIG_NCPFS_MOUNT_SUBDIR
-  Allows you to mount not only whole servers or whole volumes, but also
-  subdirectory from a volume. It can be used to reexport data and so on.
-  There is no reason why to say N, so Y is recommended unless you count
-  every byte. 
+  Allows you to mount not only whole servers or whole volumes, but
+  also subdirectories from a volume. It can be used to reexport data
+  and so on. There is no reason to say N, so Y is recommended unless
+  you count every byte.
   To utilize this feature you must use ncpfs-2.0.12 or newer.
 
 NDS interserver authentication domains
 CONFIG_NCPFS_NDS_DOMAINS
-  This allows storing NDS private keys into kernel space where it can be
-  used to authenticate another server as interserver NDS accesses need
-  it. You must use ncpfs-2.0.12.1 or newer to utilize this feature.
-  Say Y if you are using NDS connections to NetWare servers. Do not say Y
-  if security is primary for you because root can read your session
-  key (from /proc/kcore).
+  This allows storing NDS private keys in kernel space where they
+  can be used to authenticate another server as interserver NDS
+  accesses need it. You must use ncpfs-2.0.12.1 or newer to utilize
+  this feature. Say Y if you are using NDS connections to NetWare
+  servers. Do not say Y if security is primary for you because root
+  can read your session key (from /proc/kcore).
 
 Amiga FFS filesystem support
 CONFIG_AFFS_FS
-  The Fast File System (FFS) is the common filesystem used on
-  harddisks by Amiga(tm) Systems since AmigaOS Version 1.3
-  (34.20). With this driver you can also mount diskfiles used by Bernd
-  Schmidt's Un*X Amiga Emulator (http://www.freiburg.linux.de/~uae/;
-  to browse the WWW, you need to have access to a machine on the
-  Internet that has a program like lynx or netscape).  If you want to
-  do the latter, you will also need to say Y to "Loop device support",
-  above. Say Y if you want to be able to read and write files from and
-  to an Amiga FFS partition on your harddrive. Amiga floppies however
-  cannot be read with this driver due to an incompatibility of the
-  floppy controller used in an Amiga and the standard floppy
-  controller in PCs and workstations. Read
-  Documentation/filesystems/affs.txt and fs/affs/Changes. This filesystem 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
-  affs.o. If you want to compile it as a module, say M here and read
-  Documentation/modules.txt.  If unsure, say N.
-
-ROM filesystem support
-CONFIG_ROMFS_FS
-  This is a very small read-only filesystem mainly intended for
-  initial ram disks of installation disk, but it could be used for
-  other read-only media as well. Read
-  Documentation/filesystems/romfs.txt for details. This filesystem 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 romfs.o. If you want to compile it as a module, say M here
-  and read Documentation/modules.txt.  If you don't know whether you
-  need it, then you don't need it: say N.
+  The Fast File System (FFS) is the common filesystem used on hard
+  disks by Amiga(tm) Systems since AmigaOS Version 1.3 (34.20). With
+  this driver you can also mount diskfiles used by Bernd Schmidt's
+  Un*X Amiga Emulator (http://www.freiburg.linux.de/~uae/; to browse
+  the WWW, you need to have access to a machine on the Internet that
+  has a program like lynx or netscape). If you want to do the latter,
+  you will also need to say Y to "Loop device support", above. Say Y
+  if you want to be able to read and write files from and to an Amiga
+  FFS partition on your hard drive. Amiga floppies however cannot be
+  read with this driver due to an incompatibility of the floppy
+  controller used in an Amiga and the standard floppy controller in
+  PCs and workstations. Read Documentation/filesystems/affs.txt and
+  fs/affs/Changes. This filesystem 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 affs.o. If you want to
+  compile it as a module, say M here and read
+  Documentation/modules.txt. If unsure, say N.
 
 nls: Native language codepages and Unicode support
 CONFIG_NLS
@@ -5462,7 +5611,7 @@
   codepage if you want to be able to read/write these filenames on
   DOS/Windows partitions correctly. This does apply to the filenames
   only, not to the file contents. You can include several codepages;
-  say Y here if you want to include the DOS codepage that is used for
+  say Y here if you want to include the DOS codepage that is used
   for the Baltic Rim Languages. If unsure, say N.
 
 nls codepage 850
@@ -5728,15 +5877,16 @@
 Software generated cursor
 CONFIG_SOFTCURSOR
   If you say Y here, you'll be able to do lots of nice things with the
-  cursors of your virtual consoles -- for example to turn them into
-  non-blinking block cursors which are more visible on laptop screens.
+  cursors of your virtual consoles -- for example turn them into
+  non-blinking block cursors which are more visible on laptop screens,
+  or change their color depending on the virtual console you're on.
   See Documentation/VGA-softcursor.txt for more information.
 
 Standard/generic serial support
 CONFIG_SERIAL
   This selects whether you want to include the driver for the standard
   serial ports.  People who might say N here are those that are
-  setting up dedicated ethernet WWW/ftp servers, or users that have
+  setting up dedicated Ethernet WWW/ftp servers, or users that have
   one of the various bus mice instead of a serial mouse.  (Note that
   the Cyclades and Stallion multi serial port drivers do not need this
   driver built in for them to work.)  If you want to compile this
@@ -5924,16 +6074,16 @@
   you want to compile this driver as a module however ( = code which
   can be inserted in and removed from the running kernel whenever you
   want), say M here and read Documentation/modules.txt. The module
-  will be called lp.o.  If you have several parallel ports, you
-  should specify the base address for the port to use by the printer
-  with the "lp" kernel command line option. (Try "man bootparam" or
-  see the documentation of your boot loader (lilo or loadlin) about
-  how to pass options to the kernel at boot time. The lilo procedure
-  is also explained in the SCSI-HOWTO, available via ftp (user:
-  anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.) The standard
-  base addresses as well as the syntax of the "lp" command line option
-  can be found in drivers/char/lp.c. If you have more than 3 printers,
-  you need to increase the LP_NO variable in lp.c.
+  will be called lp.o.  If you have several parallel ports, you should
+  specify the base address for the port to be used by the printer with
+  the "lp" kernel command line option. (Try "man bootparam" or see the
+  documentation of your boot loader (lilo or loadlin) about how to
+  pass options to the kernel at boot time. The lilo procedure is also
+  explained in the SCSI-HOWTO, available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO.) The standard base addresses
+  as well as the syntax of the "lp" command line option can be found
+  in drivers/char/lp.c. If you have more than 3 printers, you need to
+  increase the LP_NO variable in lp.c.
 
 CONFIG_PRINTER_READBACK
   If your printer conforms to IEEE 1284, it may be able to provide a
@@ -6110,11 +6260,11 @@
   Regardless of whether you say Y or M here, an additional runtime
   loadable module called `zft-compressor.o' which contains code to
   support user transparent on-the-fly compression based on Ross
-  William's lzrw3 algorithm will be produced. If you have enabled
-  the kernel module loader (i.e. have said `Y' to CONFIG_KMOD) then
-  `zft-compressor.o' will be loaded automatically by zftape when
-  needed.
-  Despite of its name zftape does NOT use compression by default. The
+  William's lzrw3 algorithm will be produced. If you have enabled the
+  kernel module loader (i.e. have said Y to "Kernel module loader
+  support", above) then `zft-compressor.o' will be loaded
+  automatically by zftape when needed.
+  Despite its name, zftape does NOT use compression by default. The
   file Documentation/ftape.txt contains a short description of the
   most important changes in the file system interface compared to
   previous versions of ftape. The ftape home page
@@ -6149,7 +6299,7 @@
 CONFIG_FT_NR_BUFFERS
   Please leave this at `3' unless you REALLY know what you are
   doing. It is not necessary to change this value. Values below 3 make
-  the proper use of ftape impossible, values greater than 3 are waste
+  the proper use of ftape impossible, values greater than 3 are a waste
   of memory. You can change the amount of DMA memory used by ftape at
   runtime with "mt -f /dev/qft0 setdrvbuffer #NUMBUFFERS". Each buffer
   wastes 32kb of memory. Please note that this memory cannot be
@@ -6157,8 +6307,8 @@
 
 Procfs entry for ftape
 CONFIG_FT_PROC_FS
-  Optional. Saying `Y' will result in creation of a file
-  `/proc/ftape' under the proc file system. This files can be viewed
+  Optional. Saying `Y' will result in creation of a directory
+  `/proc/ftape' under the proc file system. The files can be viewed
   with your favorite pager (i.e. use "more /proc/ftape/history" or
   "less /proc/ftape/history" or simply "cat /proc/ftape/history"). The
   file will contain some status information about the inserted
@@ -6303,16 +6453,16 @@
 FDC FIFO Threshold before requesting DMA service
 CONFIG_FT_FDC_THR
   Set the FIFO threshold of the FDC. If this is higher the DMA
-  controller may serve the FCD after a higher latency time. If this is
-  lower, less DMA transfers occur leading to less bus contention.
+  controller may serve the FDC after a higher latency time. If this is
+  lower, fewer DMA transfers occur leading to less bus contention.
   You may try to tune this if ftape annoys you with "reduced data
   rate because of excessive overrun errors" messages. However, this
-  doesn't seem to have too much an effect.
+  doesn't seem to have too much effect.
   If unsure, don't touch the initial value, i.e. leave it at "8".
 
 FDC maximum data rate
 CONFIG_FT_FDC_MAX_RATE
-  With some mother board/FDC combinations ftape will not be able to
+  With some motherboard/FDC combinations ftape will not be able to
   run your FDC/tape drive combination at the highest available
   speed. If this is the case you'll encounter "reduced data rate
   because of excessive overrun errors" messages and lots of retries
@@ -6343,20 +6493,25 @@
   notification of APM "events" (e.g., battery status
   change). Supporting software is available; for more information,
   read the Battery Powered Linux mini-HOWTO available via ftp (user:
-  anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
-  This driver does not spin down disk drives (see hdparm(8) for that);
-  and it doesn't turn off VESA-compliant "green" monitors.  This
-  driver does not support the TI 4000M TravelMate and the ACER
-  486/DX4/75 because they don't have compliant BIOSes.  Many "green"
-  desktop machines also don't have compliant BIOSes, and this driver
-  will cause those machines to panic during the boot phase (typically,
-  these machines are using a data segment of 0040, which is reserved
-  for the Linux kernel). Generally, if you don't have a battery in
-  your machine, there isn't much point in using this driver and you
-  should say N.  If you get random kernel OOPSes or reboots that don't
-  seem to be related to anything, try disabling/enabling this
-  option. Some other things to try when experiencing seemingly random,
-  "weird" problems:
+  anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.  This
+  driver does not spin down disk drives (see the hdparm(8) manpage
+  ("man 8 hdparm") for that), and it doesn't turn off VESA-compliant
+  "green" monitors.  This driver does not support the TI 4000M
+  TravelMate and the ACER 486/DX4/75 because they don't have compliant
+  BIOSes.  Many "green" desktop machines also don't have compliant
+  BIOSes, and this driver will cause those machines to panic during
+  the boot phase (typically, these machines are using a data segment
+  of 0040, which is reserved for the Linux kernel).
+  If you are running Linux on a laptop, you may also want to read the
+  Linux Laptop homepage on the WWW at
+  http://www.cs.utexas.edu/users/kharker/linux-laptop/ (to browse the
+  WWW, you need to have access to a machine on the Internet that has a
+  program like lynx or netscape).
+  Generally, if you don't have a battery in your machine, there isn't
+  much point in using this driver and you should say N.  If you get
+  random kernel OOPSes or reboots that don't seem to be related to
+  anything, try disabling/enabling this option. Some other things to
+  try when experiencing seemingly random, "weird" problems:
    1) passing the "no-hlt" option to the kernel 
    2) passing the "no-387" option to the kernel 
    3) passing the "floppy=nodma" option to the kernel
@@ -6378,16 +6533,17 @@
 CONFIG_APM_DO_ENABLE
   Enable APM features at boot time.  From page 36 of the APM BIOS
   specification: "When disabled, the APM BIOS does not automatically
-  power manage devices, enter the Standby State, enter the Suspend State,
-  or take power saving steps in response to CPU Idle calls."  This driver
-  will make CPU Idle calls when Linux is idle (unless this feature is
-  turned off -- see below).  This should always save battery power, but
-  more complicated APM features will be dependent on your BIOS
-  implementation.  You may need to turn this option off if your computer
-  hangs at boot time when using APM support, or if it beeps continuously
-  instead of suspending.  Turn this off if you have a NEC UltraLite Versa
-  33/C or a Toshiba T400CDT.  This is off by default since most machines
-  do fine without this feature.
+  power manage devices, enter the Standby State, enter the Suspend
+  State, or take power saving steps in response to CPU Idle calls."
+  This driver will make CPU Idle calls when Linux is idle (unless this
+  feature is turned off -- see "Do CPU IDLE calls", below).  This
+  should always save battery power, but more complicated APM features
+  will be dependent on your BIOS implementation.  You may need to turn
+  this option off if your computer hangs at boot time when using APM
+  support, or if it beeps continuously instead of suspending.  Turn
+  this off if you have a NEC UltraLite Versa 33/C or a Toshiba
+  T400CDT.  This is off by default since most machines do fine without
+  this feature.
 
 Do CPU IDLE calls
 CONFIG_APM_CPU_IDLE
@@ -6402,20 +6558,21 @@
 Enable console blanking using APM
 CONFIG_APM_DISPLAY_BLANK
   Enable console blanking using the APM.  Some laptops can use this to
-  turn off the LCD backlight when the VC screen blanker blanks the
-  screen.  Note that this is only used by the VC screen blanker, and
-  won't turn off the backlight when using X11 (this also doesn't have
-  anything to do with your VESA-compliant power-saving monitor).
-  Further, this option doesn't work for all laptops -- it might not turn
-  off your backlight at all, or it might print a lot of errors to the
-  console, especially if you are using gpm.
+  turn off the LCD backlight when the screen blanker of the Linux
+  virtual console blanks the screen.  Note that this is only used by
+  the virtual console screen blanker, and won't turn off the backlight
+  when using the X Window system. This also doesn't have anything to
+  do with your VESA-compliant power-saving monitor.  Further, this
+  option doesn't work for all laptops -- it might not turn off your
+  backlight at all, or it might print a lot of errors to the console,
+  especially if you are using gpm.
 
 Power off on shutdown 
 CONFIG_APM_POWER_OFF
   Enable the ability to power off the computer after the Linux kernel
   is halted.  You will need software (e.g., a suitable version of the
-  halt(8) command) to cause the computer to power down.  Recent
-  versions of the sysvinit package available from
+  halt(8) command ("man 8 halt")) to cause the computer to power down.
+  Recent versions of the sysvinit package available from
   ftp://sunsite.unc.edu/pub/Linux/system/daemons/init/ (user:
   anonymous) contain support for this ("halt -p" shuts down Linux and
   powers off the computer).  As with the other APM options, this
@@ -6423,10 +6580,11 @@
 
 Ignore multiple suspend/standby events
 CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
-  This option is necessary on the Thinkpad 560, but should work on all
-  other laptops.  When the APM BIOS returns multiple suspend or standby
-  events while one is already being processed they will be ignored.
-  Without this the Thinkpad 560 has troubles with apmd, and pcmcia-cs.
+  This option is necessary on the IBM Thinkpad 560, but should work on
+  all other laptops. When the APM BIOS returns multiple suspend or
+  standby events while one is already being processed they will be
+  ignored. Without this the Thinkpad 560 has troubles with the user
+  level daemon apmd, and with the PCMCIA package pcmcia-cs.
 
 Watchdog Timer Support 
 CONFIG_WATCHDOG
@@ -6526,11 +6684,17 @@
   generate signals from as low as 1Hz up to 8192Hz, and can also be
   used as a 24 hour alarm.  It reports status information via the file
   /proc/rtc and its behaviour is set by various ioctls on
-  /dev/rtc. People running SMP (= multiprocessor) versions of Linux
-  should say Y here to read and set the RTC clock in a SMP compatible
-  fashion. (They should also read Documentation/smp.) If you think you
-  have a use for such a device (such as periodic data sampling), then
-  say Y here, and go read the file Documentation/rtc.txt for details.
+  /dev/rtc. 
+  People running SMP (= multiprocessor) versions of Linux should say Y
+  here to read and set the RTC clock in a SMP compatible
+  fashion. (They should also Documentation/smp and
+  Documentation/IO-APIC.txt and the SMP-FAQ on the WWW at
+  http://www.irisa.fr/prive/mentre/smp-faq/ (to browse the WWW, you
+  need to have access to a machine on the Internet that has a programs
+  like lynx or netscape)).
+  If you think you have a use for such a device (such as periodic data
+  sampling), then say Y here, and go read the file
+  Documentation/rtc.txt for details.
 
 Tadpole ANA H8 Support
 CONFIG_H8
@@ -6573,30 +6737,39 @@
 
 Sound card support
 CONFIG_SOUND
-  If you have a Sound Card in your Computer, i.e. if it can say more
+  If you have a sound card in your computer, i.e. if it can say more
   than an occasional beep, say Y. Be sure to have all the information
   about your sound card and its configuration down (I/O port,
   interrupt and DMA channel), because you will be asked for it. You
   want to read the Sound-HOWTO, available via ftp (user: anonymous)
   from sunsite.unc.edu:/pub/Linux/docs/HOWTO. There is also some
-  information in various README files in drivers/sound.  If you want
-  to compile this as a module ( = code which can be inserted in and
-  removed from the running kernel whenever you want), say M here and
-  read Documentation/modules.txt. I'm told that even without a sound
-  card, you can make your computer say more than an occasional beep,
-  by programming the PC speaker. Kernel patches and programs to do
-  that are at
+  information in various README files in drivers/sound, esp. in
+  Readme.cards which you should read first to find out whether your
+  card is supported by Linux.
+  If you have a PnP sound card and you want to configure it at boot
+  time using the ISA PnP tools (read
+  http://www.roestock.demon.co.uk/isapnptools/ (to browse the WWW, you
+  need to have access to a machine on the Internet that has a program
+  like lynx or netscape)), then you need to compile the sound card
+  support as a module ( = code which can be inserted in and removed
+  from the running kernel whenever you want) and load that module
+  after the PnP configuration is finished. To do this, say M here and
+  read Documentation/modules.txt as well as
+  drivers/sound/Readme.modules; the module will be called sound.o.
+  I'm told that even without a sound card, you can make your computer
+  say more than an occasional beep, by programming the PC speaker.
+  Kernel patches and programs to do that are at
   sunsite.unc.edu:/pub/Linux/kernel/patches/console/pcsndrv-X.X.tar.gz,
   to be extracted with "tar xzvf filename".
 
 ProAudioSpectrum 16 support
 CONFIG_PAS
   Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio
-  16 or Logitech SoundMan 16.  Don't answer Y if you have some other
-  card made by Media Vision or Logitech since they are not PAS16
-  compatible.
+  16 or Logitech SoundMan 16 sound card.  Don't answer Y if you have
+  some other card made by Media Vision or Logitech since they are not
+  PAS16 compatible.
 
-SoundBlaster (SB, SBPro, SB16, clones) support
+100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support
 CONFIG_SB
   Answer Y if you have an original SoundBlaster card made by
   Creative Labs or a 100% hardware compatible clone (like the
@@ -6604,16 +6777,18 @@
   cards look at the card specific instructions in the
   drivers/sound/Readme.cards file before answering this question. For
   an unknown card you may answer Y if the card claims to be
-  SoundBlaster compatible.
+  SoundBlaster compatible. If you have an SB AWE 32 or SB AWE 64, say
+  Y here and to "Additional lowlevel drivers" and to "SB32/AWE
+  support" below.
 
 Are you using the IBM Mwave "emulation" of SB ?
 CONFIG_SB_MWAVE
-  The IBM Mwave can do whats loosely describable as emulation of an 8bit
-  soundblaster if you load the right firmware from DOS warm boot and pray
-  and your machine happens to like you. Say Y if you are doing this as the
-  IRQ test normally fails on the mwave emulation. If you'd like real MWAVE
-  support phone IBM (425-556-8822) and ask them why they still haven't 
-  released any documentation.
+  The IBM Mwave can do what's loosely describable as emulation of an
+  8bit SoundBlaster card if you load the right firmware from DOS warm
+  boot and pray and your machine happens to like you. Say Y if you are
+  doing this as the IRQ test normally fails on the Mwave emulation. If
+  you'd like real MWAVE support phone IBM (425-556-8822) and ask them
+  why they still haven't released any documentation.
   [http://204.200.238.31/cgi-bin/link.pl?co=i&cl=/ts/ibm/contact.html]
 
 Generic OPL2/OPL3 FM synthesizer support
@@ -6622,10 +6797,13 @@
   Answering Y is usually a safe and recommended choice, however some
   cards may have software (TSR) FM emulation. Enabling FM support with
   these cards may cause trouble (I don't currently know of any such
-  cards, however).
+  cards, however). If unsure, say Y.
 
 Loopback MIDI device support
 CONFIG_VMIDI
+###
+### somebody please fill this in.
+###
 
 Gravis Ultrasound support
 CONFIG_GUS
@@ -6635,7 +6813,7 @@
 MPU-401 support (NOT for SB16)
 CONFIG_MPU401
   Be careful with this question. The MPU401 interface is supported by
-  all soundcards. However, some natively supported cards have their
+  all sound cards. However, some natively supported cards have their
   own driver for MPU401. Enabling this MPU401 option with these cards
   will cause a conflict. Also, enabling MPU401 on a system that
   doesn't really have a MPU401 could cause some trouble. If your card
@@ -6691,7 +6869,7 @@
 
 Ensoniq Soundscape support
 CONFIG_SSCAPE
-  Answer Y if you have a soundcard based on the Ensoniq SoundScape
+  Answer Y if you have a sound card based on the Ensoniq SoundScape
   chipset. Such cards are being manufactured at least by Ensoniq, Spea
   and Reveal (Reveal makes also other cards).
 
@@ -6738,19 +6916,15 @@
 
 Sun Audio support
 CONFIG_SUN_AUDIO
-  This is support for the soundcards on Sun workstations. The code
+  This is support for the sound cards on Sun workstations. The code
   does not exist yet, so you might as well say N here.
 
-SB32/AWE support
-CONFIG_AWE32_SYNTH
-  Say Y here if you have a SB32 or SB AWE soundcard. See
-  drivers/sound/lowlevel/README.awe for more info.
-
 Additional low level drivers
 CONFIG_LOWLEVEL_SOUND
-  If you need additional low level sound drivers which are not part
-  of USS/Lite (UNIX Sound System), say Y.  The only such driver at
-  present is the ACI driver for the miroSOUND PCM12 and PCM20.
+  If you need additional low level sound drivers which have not yet
+  appeared, say Y. The answer to this question does not directly
+  affect the kernel; saying Y will simply cause this configure script
+  to present you with more options. If unsure, say Y.
 
 ACI mixer (miroPCM12)
 CONFIG_ACI_MIXER
@@ -6763,6 +6937,13 @@
   also controls the radio tuner on this card, however this is not
   yet supported in this software.
 
+SB32/AWE support
+CONFIG_AWE32_SYNTH
+  Say Y here if you have a SoundBlaster SB32, AWE32-PnP, SB AWE64 or
+  similar sound card. See drivers/sound/lowlevel/README.awe and the
+  Soundblaster-AWE mini-HOWTO, available via ftp (user: anonymous)
+  from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini for more info.
+
 Gallant's Audio Excel DSP 16 support (SC-6000 and SC-6600)
 CONFIG_AEDSP16
   Answer Y if you have a Gallant's Audio Excel DSP 16 card. This card
@@ -6915,7 +7096,7 @@
   S0-8 and many compatibles.
   See Documentation/isdn/README.HiSax on how to configure it 
   using the different cards, a different D-channel protocol, or
-  non-standard irq/port/shmem settings.
+  non-standard IRQ/port/shmem settings.
 
 HiSax Support for Teles 16.3 or PNP or PCMCIA
 CONFIG_HISAX_16_3
@@ -6923,14 +7104,14 @@
   the Teles/Creatix PnP and the Teles PCMCIA.
   See Documentation/isdn/README.HiSax on how to configure it 
   using the different cards, a different D-channel protocol, or
-  non-standard irq/port/shmem settings.
+  non-standard IRQ/port/shmem settings.
 
 HiSax Support for AVM A1 (Fritz)
 CONFIG_HISAX_AVM_A1
   This enables HiSax support for the AVM A1 (aka "Fritz").
   See Documentation/isdn/README.HiSax on how to configure it
   using the different cards, a different D-channel protocol, or
-  non-standard irq/port/shmem settings.
+  non-standard IRQ/port/shmem settings.
 
 HiSax Support for Elsa ISA cards
 CONFIG_HISAX_ELSA_PCC
@@ -6938,21 +7119,21 @@
   for the Elsa Quickstep series cards.
   See Documentation/isdn/README.HiSax on how to configure it 
   using the different cards, a different D-channel protocol, or
-  non-standard irq/port/shmem settings.
+  non-standard IRQ/port/shmem settings.
 
 HiSax Support for Elsa PCMCIA card
 CONFIG_HISAX_ELSA_PCMCIA
   This enables HiSax support for the Elsa PCMCIA cards.
   See Documentation/isdn/README.HiSax on how to configure it 
   using the different cards, a different D-channel protocol, or
-  non-standard irq/port/shmem settings.
+  non-standard IRQ/port/shmem settings.
 
 HiSax Support for ITK ix1-micro Revision 2
 CONFIG_HISAX_IX1MICROR2
   This enables HiSax support for the ITK ix1-micro Revision 2 card.
   See Documentation/isdn/README.HiSax on how to configure it 
   using the different cards, a different D-channel protocol, or
-  non-standard irq/port/shmem settings.
+  non-standard IRQ/port/shmem settings.
 
 HiSax Support for EURO/DSS1
 CONFIG_HISAX_EURO
@@ -7293,7 +7474,7 @@
 CONFIG_ATARI_SCSI
   If you have an Atari with built-in NCR5380 SCSI controller (TT,
   Falcon, ...) say Y to get it supported. Of course also, if you have
-  an compatible SCSI controller (e.g. for Medusa). This driver is also
+  a compatible SCSI controller (e.g. for Medusa). 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
   atari_scsi.o. If you want to compile it as a module, say M here and
@@ -7547,9 +7728,9 @@
   want). If you want to compile it as a module, say M here and read
   Documentation/modules.txt.
 
-MACE (Power Mac ethernet) support
+MACE (Power Mac Ethernet) support
 CONFIG_MACE
-  Power Macintoshes and clones with ethernet built-in on the
+  Power Macintoshes and clones with Ethernet built-in on the
   motherboard will usually use a MACE (Medium Access Control for
   Ethernet) interface.  Say Y to include support for the MACE chip.
 
@@ -7574,18 +7755,20 @@
 
 Quickcam BW Video For Linux
 CONFIG_VIDEO_BWQCAM
-  Say Y have if you have such a thing. This driver is also available
-  as a module called bw-qcam.o ( = code which can be inserted in and
-  removed from the running kernel whenever you want). If you want to
-  compile it as a module, say M here and read
+  Say Y have if you the black and white version of the QuickCam
+  camera. See the next option for the color version. This driver is
+  also available as a module called bw-qcam.o ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  If you want to compile it as a module, say M here and read
   Documentation/modules.txt.
 
 Colour QuickCam Video For Linux
 CONFIG_VIDEO_CQCAM
-  This is the video4linux driver for the colour version of the Connectix
-  Quickcam.  If you have one of these cameras, say Y here, otherwise say N.
-  This driver does not work with the original monochrome Quickcam,
-  Quickcam VC or QuickClip.  It is also available as a module (c-qcam.o).
+  This is the video4linux driver for the colour version of the
+  Connectix Quickcam. If you have one of these cameras, say Y here,
+  otherwise say N. This driver does not work with the original
+  monochrome Quickcam, Quickcam VC or QuickClip. It is also available
+  as a module (c-qcam.o).
 
 Mediavision Pro Movie Studio Video For Linux
 CONFIG_VIDEO_PMS
@@ -7595,22 +7778,26 @@
   it as a module, say M here and read Documentation/modules.txt.
 
 # need an empty line after last entry, for sed script in Configure.
-
+#
+# A couple of things I keep forgetting:
+#   capitalize: Internet, Intel, SCSI, NetWare, PCI, IRQ, DMA
+#   two words: hard drive, hard disk, sound card
+#   other: it's safe to save.
 #
 # This is used by ispell.el:
 #
 # LocalWords:  CONFIG coprocessor DX Pentium SX lilo loadlin HOWTO ftp sunsite
 # LocalWords:  unc edu docs emu README kB BLK DEV FD Thinkpad fd MFM RLL IDE gz
-# LocalWords:  cdrom harddisk diskless netboot nfs xzvf ATAPI MB harddrives ide
-# LocalWords:  HD harddisks CDROMs IDECD NEC MITSUMI filesystem XT XD PCI bios
+# LocalWords:  cdrom diskless netboot nfs xzvf ATAPI MB ide pavia rubini pl pd
+# LocalWords:  HD CDROMs IDECD NEC MITSUMI filesystem XT XD PCI bios cezar ATEN
 # LocalWords:  ISA EISA Microchannel VESA BIOSes IPC SYSVIPC ipc Ctrl dmesg hlt
 # LocalWords:  BINFMT Linkable http ac uk jo html GCC Sparc AVANTI CABRIOLET EB
 # LocalWords:  netscape gcc LD CC toplevel MODVERSIONS insmod rmmod modprobe IP
-# LocalWords:  genksyms INET loopback gatewaying ethernet internet PPP ARP Arp
+# LocalWords:  genksyms INET loopback gatewaying ethernet PPP ARP Arp MEMSIZE
 # LocalWords:  howto multicasting MULTICAST MBONE firewalling ipfw ACCT resp ip
 # LocalWords:  proc acct IPIP encapsulator decapsulator klogd PCTCP RARP EXT PS
 # LocalWords:  telneting subnetted NAGLE rlogin NOSR ttyS TGA techinfo mbone nl
-# LocalWords:  Mb SKB IPX Novell Netware dosemu Appletalk DDP ATALK tapedrive
+# LocalWords:  Mb SKB IPX Novell dosemu Appletalk DDP ATALK tapedrive vmalloc
 # LocalWords:  SD CHR scsi thingy SG CD LUNs LUN jukebox Adaptec BusLogic EATA
 # LocalWords:  buslogic DMA DPT ATT eata dma PIO UltraStor fdomain umsdos ext
 # LocalWords:  QLOGIC qlogic TMC seagate Trantor ultrastor FASST wd NETDEVICES
@@ -7623,21 +7810,21 @@
 # LocalWords:  TR Sony CDU caddyless cdu Mitsumi MCD cd mcd XA MultiSession CDA
 # LocalWords:  Matsushita Panasonic SBPCD Soundblaster Longshine sbpcd Aztech
 # LocalWords:  Okano Wearnes AZTCD CDD SE aztcd sonycd Goldstar GSCD Philips fs
-# LocalWords:  LMS OPTCD Sanyo SJCD minix faqs xiafs XIA msdos harddrive mtools
+# LocalWords:  LMS OPTCD Sanyo SJCD minix faqs xiafs XIA msdos mtools Cichocki
 # LocalWords:  std softlinks umssync NetworkFileSharing nfsd mountd CDs HPFS TI
-# LocalWords:  hpfs SYSV SCO intel iBCS Wyse WordPerfect tsx mit unixes sysv NR
+# LocalWords:  hpfs SYSV SCO iBCS Wyse WordPerfect tsx mit unixes sysv NR irisa
 # LocalWords:  SMB WfW Cyclades async mux Logitech busmouse MouseSystem aka AST
 # LocalWords:  PSMOUSE Compaq trackballs Travelmate Inport ATIXL ATI busmice ld
 # LocalWords:  gpm config QIC DYNCONF FTAPE Stor Ftape ftape pcsndrv manpage NT
 # LocalWords:  readprofile diskdrives org com masq EtherTalk tcp netrom sunacm
 # LocalWords:  misc AIC aic pio nullmodems scc Portmaster eql GIS PhotoCDs MCDX
-# LocalWords:  mcdx gscd optcd sjcd ISP soundcard hdparm Workgroups Lan samba
+# LocalWords:  mcdx gscd optcd sjcd ISP hdparm Workgroups Lan samba PARIDE PCD
 # LocalWords:  filesystems smbfs ATA ppp PCTech RZ www powerquest txt CMD ESDI
 # LocalWords:  chipset FB multicast MROUTE appletalk ifconfig IBMTR multiport
 # LocalWords:  Multisession STALDRV EasyIO EC EasyConnection ISTALLION ONboard
 # LocalWords:  Brumby pci TNC cis ohio faq usenet NETLINK dev hydra ca Tyne mem
 # LocalWords:  carleton Deskstation DECstation SUNFD JENSEN Noname XXXM SLiRP
-# LocalWords:  pppd Zilog ZS soundcards SRM bootloader ez mainmenu rarp ipfwadm
+# LocalWords:  pppd Zilog ZS SRM bootloader ez mainmenu rarp ipfwadm paride pcd
 # LocalWords:  RTNETLINK mknod xos MTU lwared Macs mac netatalk macs cs Wolff
 # LocalWords:  dartmouth flowerpt MultiMaster FlashPoint tudelft etherexpress
 # LocalWords:  ICL EtherTeam ETH IDESCSI TXC SmartRAID SmartCache httpd sjc dlp
@@ -7647,7 +7834,7 @@
 # LocalWords:  Vertos Genoa Funai hsfs NCP NetWare tgz APM apm ioctls UltraLite
 # LocalWords:  TravelMate CDT LCD backlight VC RPC Mips DECStation AXP barlow
 # LocalWords:  PMAX MILO Alphas Multia Tseng linuxelf endian mipsel mips drv HT
-# LocalWords:  KERNELD kerneld callouts AdvanSys advansys diskquotas Admin WDT
+# LocalWords:  KERNELD kerneld callouts AdvanSys advansys Admin WDT DataStor EP
 # LocalWords:  wdt hdb hdc bugfix SiS vlb Acculogic CSA DTC dtc Holtek ht QDI
 # LocalWords:  QD qd UMC umc ALI ali lena fnet fr homepage azstarnet axplinux
 # LocalWords:  Avanti XL AlphaStations Jensen DECpc AXPpci UDB Cabriolet MCA RC
@@ -7715,7 +7902,7 @@
 # LocalWords:  SYMBIOS COMPAT SDMS rev ASUS Tekram HX VX API ibmmcascsi ASY asy
 # LocalWords:  loader's PCnetPCI automounter AUTOFS amd autofs VT Gallant's Pnp
 # LocalWords:  AEDSP aedsp enskip tik Sysctl sysctl PARPORT parport pnp IDs EPP
-# LocalWords:  Autoprobe conformant bart patrickr HDLS READBACK AB usr DAMA DS
+# LocalWords:  Autoprobe bart patrickr HDLS READBACK AB usr DAMA DS SparQ aten
 # LocalWords:  Symbios PCscsi tmscsim RoamAbout GHz Hinds's contrib mathematik
 # LocalWords:  darmstadt okir DIGIEPCA International's Xem digiepca epca bootup
 # LocalWords:  zorro CAPI AVMB capi avmb VP SYN syncookies EM em pc Ethertalk
@@ -7741,16 +7928,23 @@
 # LocalWords:  DMASCC paccomm dmascc addr cfg oevsv oe kib picpar FDX baudrate
 # LocalWords:  baudrates fdx HDX hdx PSK kanren frforum QoS SCHED CBQ SCH sched
 # LocalWords:  sch cbq CSZ Shenker Zhang csz SFQ sfq TBF tbf PFIFO fifo PRIO RW
-# LocalWords:  prio Micom xIO dwmw rimi OMIRR omirr omirrd unicode ntfs cmu
+# LocalWords:  prio Micom xIO dwmw rimi OMIRR omirr omirrd unicode ntfs cmu NIC
 # LocalWords:  Braam braam Schmidt's freiburg nls codepages codepage Romanian
 # LocalWords:  Slovak Slovenian Sorbian Nordic iso Catalan Faeroese Galician SZ
 # LocalWords:  Valencian Slovene Esperanto Estonian Latvian Byelorussian KOI mt
 # LocalWords:  charset Inuit Greenlandic Sami Lappish koi SOFTCURSOR softcursor
-# LocalWords:  Specialix specialix DTR RTS RTSCTS cycladesZ Exabyte ftape's
+# LocalWords:  Specialix specialix DTR RTS RTSCTS cycladesZ Exabyte ftape's inr
 # LocalWords:  Iomega's LBFM claus ZFTAPE VFS zftape zft William's lzrw DFLT kb
 # LocalWords:  MTSETBLK MTIOCTOP qft setblk zftape's tar's afio's setdrvbuffer
 # LocalWords:  Procfs Exabyte's THR FCD sysvinit init PSC pscwdt VMIDI Euro SAB
 # LocalWords:  Mostek Fastlane PowerMac PReP PMAC PowerPC Macintoshes Starmax
 # LocalWords:  PowerStack Starmaxes MCOMMON DEVICETREE ATY IMS IMSTT videodev
 # LocalWords:  BT Hauppauge STB bttv Quickcam BW BWQCAM bw qcam Mediavision PMS
-# LocalWords:  pms
+# LocalWords:  pms Avatar Freecom Imation Superdisk BPCK bpck COMM comm DSTR ru
+# LocalWords:  dstr EPAT EPEZ epat EPIA epia FreeCom FRPW frpw KingByte KBIC HW
+# LocalWords:  KingByte's kbic OnSpec ValuStore FASTROUTE fastroute FLOWCONTROL
+# LocalWords:  struct APIC realtime OSs LynxOS CNC tmp cvf HFS hfs ADFS Risc os
+# LocalWords:  StrongARM adfs ncpmount namespace SUBDIR reexport NDS kcore FT
+# LocalWords:  interserver BLKSZ NUMBUFFERS apmd Tadpole ANA roestock QuickCam
+# LocalWords:  isapnptools Colour CQCAM colour Connectix QuickClip prive mentre
+# LocalWords:  KMOD kmod conformant utexas kharker UnixWare Mwave cgi cl ts ibm
diff -u --recursive --new-file v2.1.90/linux/Documentation/sound/CS4232 linux/Documentation/sound/CS4232
--- v2.1.90/linux/Documentation/sound/CS4232	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sound/CS4232	Sat Mar 21 10:56:26 1998
@@ -0,0 +1,23 @@
+
+insmod sound
+insmod ad1848
+insmod uart401
+insmod cs4232 io=* irq=* dma=* dma2=*
+
+This configures the crystal CS423x sound chip and activates its DSP
+functions. On some cards the non PnP setup the board attempts to do fails.
+If you have problems use the kernel PnP facilities.
+
+io is the I/O address of the WSS (normally 0x534)
+irq is the IRQ of this device
+dma/dma2 are the DMA channels. DMA2 may well be 0
+
+
+To get midi synth facilities add
+
+insmod opl3 io=*
+
+io= I/O address of the OPL3 synthesizer. This will be shown in /proc/sys/pnp
+and is normally 0x388
+
+
diff -u --recursive --new-file v2.1.90/linux/Documentation/sound/OPL3 linux/Documentation/sound/OPL3
--- v2.1.90/linux/Documentation/sound/OPL3	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sound/OPL3	Sat Mar 21 10:56:26 1998
@@ -0,0 +1,6 @@
+A pure OPL3 card is nice and easy to configure. Simply do
+
+insmod opl3 io=0x388
+
+Change the I/O address in the very unlikely case this card is differently
+configured
diff -u --recursive --new-file v2.1.90/linux/Documentation/sound/Soundblaster linux/Documentation/sound/Soundblaster
--- v2.1.90/linux/Documentation/sound/Soundblaster	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sound/Soundblaster	Sat Mar 21 10:56:26 1998
@@ -0,0 +1,38 @@
+
+insmod sound
+insmod uart401
+insmod sb ...
+
+This loads the driver for the soundblaster and assorted clones. Cards that
+are covered by other drivers should not be using with this driver.
+
+The soundblaster module takes the following arguments
+
+io		I/O address of the soundblaster chip
+irq		IRQ of the soundblaster chip
+dma     	8bit DMA channel for the soundblaster
+dma16   	16bit DMA channel for SB16 and equivalent cards
+mpu_io		I/O for MPU chip if present
+
+mad16=1 	Set when loading this as part of the MAD16 setup only
+trix=1  	Set when loading this as part of the Audiotrix setup only
+pas2=1		Set when loading this as part of the Pas2 setup only
+sm_games=1	Set if you have a Logitech soundman games
+acer=1		Set this to detect cards in some ACER notebooks
+mwave_bug=1	Set if you are trying to use this driver with mwave (see on)
+
+You may well want to load the opl3 driver for synth music on most SB and
+clone SB devices
+
+insmod opl3 io=0x388
+
+Using Mwave
+
+To make this driver work with Mwave you must set mwave_bug. You also need
+to warmboot from DOS/Windows with the required firmware loaded under this
+OS. IBM are being difficult about documenting how to load this firmware.
+
+Avance Logic ALS007
+
+This card isnt currently supported. I have patches to merge however that
+add limited support.
diff -u --recursive --new-file v2.1.90/linux/Documentation/stallion.txt linux/Documentation/stallion.txt
--- v2.1.90/linux/Documentation/stallion.txt	Thu Feb 12 20:56:03 1998
+++ linux/Documentation/stallion.txt	Mon Mar 23 13:49:28 1998
@@ -4,8 +4,8 @@
 
 Copyright (C) 1994-1998,  Stallion Technologies (support@stallion.com).
 
-Version:   5.4.3
-Date:      04FEB98
+Version:   5.4.5
+Date:      23MAR98
 
 
 
@@ -31,12 +31,12 @@
 have the latest version are tsx-11.mit.edu, sunsite.unc.edu and their
 mirrors.
 
-ftp.stallion.com:/drivers/ata5/Linux/stallion-5.4.2.tar.gz
-tsx-11.mit.edu:/pub/linux/packages/stallion/stallion-5.4.2.tar.gz
-sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-5.4.2.tar.gz
+ftp.stallion.com:/drivers/ata5/Linux/v544.tar.gz
+tsx-11.mit.edu:/pub/linux/packages/stallion/stallion-5.4.4.tar.gz
+sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-5.4.4.tar.gz
 
 As of the printing of this document the latest version of the driver
-utility package is 5.4.2. If a later version is now available then you
+utility package is 5.4.4. If a later version is now available then you
 should use the latest version.
 
 If you are using the EasyIO, EasyConnection 8/32 or EasyConnection 8/64-PCI
diff -u --recursive --new-file v2.1.90/linux/Documentation/sysctl/kernel.txt linux/Documentation/sysctl/kernel.txt
--- v2.1.90/linux/Documentation/sysctl/kernel.txt	Tue Mar 10 10:03:30 1998
+++ linux/Documentation/sysctl/kernel.txt	Thu Mar 19 15:04:30 1998
@@ -25,10 +25,14 @@
 - inode-max
 - inode-nr
 - inode-state
+- kmod_unload_delay           ==> Documentation/kmod.txt
+- modprobe                    ==> Documentation/kmod.txt
 - osrelease
 - ostype
 - panic
 - printk
+- real-root-dev               ==> Documentation/initrd.txt
+- reboot-cmd                  ==> SPARC specific
 - securelevel
 - version
 
diff -u --recursive --new-file v2.1.90/linux/Documentation/sysctl/vm.txt linux/Documentation/sysctl/vm.txt
--- v2.1.90/linux/Documentation/sysctl/vm.txt	Tue Mar 17 22:18:13 1998
+++ linux/Documentation/sysctl/vm.txt	Thu Mar 26 12:56:36 1998
@@ -19,6 +19,7 @@
 - buffermem
 - freepages
 - overcommit_memory
+- pagecache
 - swapctl
 - swapout_interval
 
@@ -93,17 +94,16 @@
 
 The three values in this file correspond to the values in
 the struct buffer_mem. It controls how much memory should
-be used for buffer and cache memory. Note that memorymapped
-files are also counted as cache memory...
+be used for buffer memory.
 
 The values are:
 min_percent	-- this is the minumum percentage of memory
-		   that should be spent on buffer + page cache
-borrow_percent  -- when Linux is short on memory, and buffer
-                   and cache use more than this percentage of
-                   memory, free pages are stolen from them
+		   that should be spent on buffer memory
+borrow_percent  -- when Linux is short on memory, and the
+                   buffer cache uses more memory, free pages
+                   are stolen from it
 max_percent     -- this is the maximum amount of memory that
-                   can be used for buffer and cache memory 
+                   can be used for buffer memory 
 
 ==============================================================
 freepages:
@@ -173,6 +173,18 @@
     freepages -= num_physpages >> 4;
     return freepages > pages;
 }
+
+==============================================================
+
+pagecache:
+
+This file does exactly the same as buffermem, only this
+file controls the struct page_cache, and thus controls
+the amount of memory allowed for memory mapping of files.
+
+You don't want the minimum level to be too low, otherwise
+your system might thrash when memory is tight or fragmentation
+is high...
 
 ==============================================================
 
diff -u --recursive --new-file v2.1.90/linux/Makefile linux/Makefile
--- v2.1.90/linux/Makefile	Tue Mar 17 22:18:13 1998
+++ linux/Makefile	Mon Mar 23 23:39:02 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 90
+SUBLEVEL = 91
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
@@ -241,11 +241,10 @@
 	fi
 
 include/linux/compile.h: $(CONFIGURATION) include/linux/version.h newversion
-	@if [ -f .name ]; then \
-	   echo \#define UTS_VERSION \"\#`cat .version`-`cat .name` `date`\"; \
-	 else \
-	   echo \#define UTS_VERSION \"\#`cat .version` `date`\";  \
-	 fi >> .ver
+	@echo -n \#define UTS_VERSION \"\#`cat .version` > .ver
+	@if [ -n "$(SMP)" ] ; then echo -n " SMP" >> .ver; fi
+	@if [ -f .name ]; then  echo -n \-`cat .name` >> .ver; fi
+	@echo ' '`date`'"' >> .ver
 	@echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> .ver
 	@echo \#define LINUX_COMPILE_BY \"`whoami`\" >> .ver
 	@echo \#define LINUX_COMPILE_HOST \"`hostname`\" >> .ver
diff -u --recursive --new-file v2.1.90/linux/arch/alpha/kernel/bios32.c linux/arch/alpha/kernel/bios32.c
--- v2.1.90/linux/arch/alpha/kernel/bios32.c	Tue Mar 10 10:03:30 1998
+++ linux/arch/alpha/kernel/bios32.c	Wed Mar 25 22:04:36 1998
@@ -813,7 +813,7 @@
  */
 static inline void eb66p_fixup(void)
 {
-	static char irq_tab[5][5] __initdata = {
+	static char irq_tab[5][5] __initlocaldata = {
 		{16+0, 16+0, 16+5,  16+9, 16+13},  /* IdSel 6,  slot 0, J25 */
 		{16+1, 16+1, 16+6, 16+10, 16+14},  /* IdSel 7,  slot 1, J26 */
 		{  -1,   -1,   -1,    -1,    -1},  /* IdSel 8,  SIO         */
@@ -869,7 +869,7 @@
 #if defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)
 static inline void alphapc164_fixup(void)
 {
-	static char irq_tab[7][5] __initdata = {
+	static char irq_tab[7][5] __initlocaldata = {
 		/*INT   INTA  INTB   INTC   INTD */
 		{ 16+2, 16+2, 16+9,  16+13, 16+17}, /* IdSel  5, slot 2, J20 */
 		{ 16+0, 16+0, 16+7,  16+11, 16+15}, /* IdSel  6, slot 0, J29 */
@@ -900,7 +900,7 @@
  */
 static inline void cabriolet_fixup(void)
 {
-	static char irq_tab[5][5] __initdata = {
+	static char irq_tab[5][5] __initlocaldata = {
 		{ 16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 5,  slot 2, J21 */
 		{ 16+0, 16+0, 16+5,  16+9, 16+13}, /* IdSel 6,  slot 0, J19 */
 		{ 16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7,  slot 1, J20 */
@@ -956,7 +956,7 @@
  */
 static inline void eb66_and_eb64p_fixup(void)
 {
-	static char irq_tab[5][5] __initdata = {
+	static char irq_tab[5][5] __initlocaldata = {
 		{16+7, 16+7, 16+7, 16+7,  16+7},  /* IdSel 5,  slot ?, ?? */
 		{16+0, 16+0, 16+2, 16+4,  16+9},  /* IdSel 6,  slot ?, ?? */
 		{16+1, 16+1, 16+3, 16+8, 16+10},  /* IdSel 7,  slot ?, ?? */
@@ -1005,7 +1005,7 @@
  */
 static inline void mikasa_fixup(void)
 {
-	static char irq_tab[8][5] __initdata = {
+	static char irq_tab[8][5] __initlocaldata = {
 		/*INT    INTA   INTB   INTC   INTD */
 		{16+12, 16+12, 16+12, 16+12, 16+12},	/* IdSel 17,  SCSI */
 		{   -1,    -1,    -1,    -1,    -1},	/* IdSel 18,  PCEB */
@@ -1076,7 +1076,7 @@
  */
 static inline void noritake_fixup(void)
 {
-	static char irq_tab[13][5] __initdata = {
+	static char irq_tab[13][5] __initlocaldata = {
 		/*INT    INTA   INTB   INTC   INTD */
 		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 18,  PCEB */
 		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 19,  PPB  */
@@ -1140,7 +1140,7 @@
  */
 static inline void alcor_fixup(void)
 {
-	static char irq_tab[6][5] __initdata = {
+	static char irq_tab[6][5] __initlocaldata = {
 		/*INT    INTA   INTB   INTC   INTD */
 		{ 16+8,  16+8,  16+9, 16+10, 16+11},	/* IdSel 18,  slot 0 */
 		{16+16, 16+16, 16+17, 16+18, 16+19},	/* IdSel 19,  slot 3 */
@@ -1195,7 +1195,7 @@
  */
 static inline void xlt_fixup(void)
 {
-	static char irq_tab[7][5] __initdata = {
+	static char irq_tab[7][5] __initlocaldata = {
 		/*INT    INTA   INTB   INTC   INTD */
 		{16+13, 16+13, 16+13, 16+13, 16+13},	/* IdSel 17,  TULIP  */
 		{ 16+8,  16+8,  16+9, 16+10, 16+11},	/* IdSel 18,  slot 0 */
@@ -1266,7 +1266,7 @@
 #ifdef CONFIG_ALPHA_SABLE
 static inline void sable_fixup(void)
 {
-        static char irq_tab[9][5] __initdata = {
+        static char irq_tab[9][5] __initlocaldata = {
 		/*INT    INTA   INTB   INTC   INTD */
 		{ 32+0,  32+0,  32+0,  32+0,  32+0},  /* IdSel 0,  TULIP  */
 		{ 32+1,  32+1,  32+1,  32+1,  32+1},  /* IdSel 1,  SCSI   */
@@ -1349,7 +1349,7 @@
 #ifdef CONFIG_ALPHA_MIATA
 static inline void miata_fixup(void)
 {
-        static char irq_tab[18][5] __initdata = {
+        static char irq_tab[18][5] __initlocaldata = {
 		/*INT    INTA   INTB   INTC   INTD */
 		{16+ 8, 16+ 8, 16+ 8, 16+ 8, 16+ 8},  /* IdSel 14,  DC21142  */
 		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 15,  EIDE   */
@@ -1420,7 +1420,7 @@
 #ifdef CONFIG_ALPHA_SX164
 static inline void sx164_fixup(void)
 {
-	static char irq_tab[5][5] __initdata = {
+	static char irq_tab[5][5] __initlocaldata = {
 		/*INT    INTA   INTB   INTC   INTD */
 		{ 16+ 9, 16+ 9, 16+13, 16+17, 16+21}, /* IdSel 5 slot 2 J17 */
 		{ 16+11, 16+11, 16+15, 16+19, 16+23}, /* IdSel 6 slot 0 J19 */
@@ -1461,7 +1461,7 @@
 	 * that they use the default INTA line, if they are interrupt
 	 * driven at all).
 	 */
-	static const char pirq_tab[][5] __initdata = {
+	static const char pirq_tab[][5] __initlocaldata = {
 #ifdef CONFIG_ALPHA_P2K
 		{ 0,  0, -1, -1, -1}, /* idsel  6 (53c810) */
 		{-1, -1, -1, -1, -1}, /* idsel  7 (SIO: PCI/ISA bridge) */
diff -u --recursive --new-file v2.1.90/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v2.1.90/linux/arch/i386/kernel/irq.c	Tue Mar 17 22:18:13 1998
+++ linux/arch/i386/kernel/irq.c	Wed Mar 18 09:55:07 1998
@@ -205,7 +205,7 @@
 #ifdef __SMP__
 
 /*
- * The IO-APIC (persent only in SMP boards) has 8 more hardware
+ * The IO-APIC (present only in SMP boards) has 8 more hardware
  * interrupt pins, for all of them we define an IRQ vector:
  *
  * raw PCI interrupts 0-3, basically these are the ones used
@@ -1003,7 +1003,7 @@
 	for (i=0; i<NR_IRQS; i++)
 		for (j=0; j<NR_CPUS; j++)
 			if (kstat.irqs[j][i] != probe_irqs[j][i])
-				irqs &= ~(i<<1);
+				irqs &= ~(1UL << i);
 
 	return irqs;
 }
@@ -1018,7 +1018,7 @@
 			sum += kstat.irqs[j][i];
 			sum -= probe_irqs[j][i];
 		}
-		if (sum && (irqs & (i<<1))) {
+		if (sum && (irqs & (1UL << i))) {
 			if (irq_found != -1) {
 				irq_found = -irq_found;
 				goto out;
diff -u --recursive --new-file v2.1.90/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v2.1.90/linux/arch/i386/kernel/process.c	Tue Mar 17 22:18:13 1998
+++ linux/arch/i386/kernel/process.c	Wed Mar 25 09:20:42 1998
@@ -171,6 +171,8 @@
 
 asmlinkage int sys_idle(void)
 {
+	if (current->pid != 0)
+		return -EPERM;
 	cpu_idle(NULL);
 	return 0;
 }
diff -u --recursive --new-file v2.1.90/linux/arch/i386/lib/locks.S linux/arch/i386/lib/locks.S
--- v2.1.90/linux/arch/i386/lib/locks.S	Sat May 24 09:10:22 1997
+++ linux/arch/i386/lib/locks.S	Thu Mar 26 09:25:16 1998
@@ -16,10 +16,10 @@
 	jnc	3f
 	sti
 2:
-	btl	%dl, SYMBOL_NAME(smp_invalidate_needed)
+	btl	%edx, SYMBOL_NAME(smp_invalidate_needed)
 	jnc	0f
 	lock
-	btrl	%dl, SYMBOL_NAME(smp_invalidate_needed)
+	btrl	%edx, SYMBOL_NAME(smp_invalidate_needed)
 	jnc	0f
 	pushl	%eax
 	movl	%cr3, %eax
diff -u --recursive --new-file v2.1.90/linux/arch/m68k/amiga/amiga_ksyms.c linux/arch/m68k/amiga/amiga_ksyms.c
--- v2.1.90/linux/arch/m68k/amiga/amiga_ksyms.c	Tue Mar 17 22:18:14 1998
+++ linux/arch/m68k/amiga/amiga_ksyms.c	Sat Mar 21 11:04:03 1998
@@ -1,8 +1,8 @@
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/zorro.h>
 #include <asm/amigahw.h>
-#include <linux/pci.h>
 
 extern volatile u_short amiga_audio_min_period;
 extern u_short amiga_audio_period;
@@ -17,6 +17,7 @@
 EXPORT_SYMBOL(amiga_chip_alloc);
 EXPORT_SYMBOL(amiga_chip_free);
 EXPORT_SYMBOL(amiga_chip_avail);
+EXPORT_SYMBOL(amiga_chip_size);
 EXPORT_SYMBOL(amiga_audio_period);
 EXPORT_SYMBOL(amiga_audio_min_period);
 
@@ -25,7 +26,3 @@
 EXPORT_SYMBOL(zorro_config_board);
 EXPORT_SYMBOL(zorro_unconfig_board);
 EXPORT_SYMBOL(zorro_unused_z2ram);
-
-#if CONFIG_PCI
-EXPORT_SYMBOL(pci_devices);
-#endif
diff -u --recursive --new-file v2.1.90/linux/arch/m68k/amiga/zorro.c linux/arch/m68k/amiga/zorro.c
--- v2.1.90/linux/arch/m68k/amiga/zorro.c	Tue Feb 17 13:12:44 1998
+++ linux/arch/m68k/amiga/zorro.c	Sat Mar 21 11:04:03 1998
@@ -651,7 +651,8 @@
 END
 
 BEGIN_PROD(PETSOFF_LP)
-    PROD("Delfina", DSP, PETSOFF_LP_DELFINA)
+    PROD("Delfina", AUDIO, PETSOFF_LP_DELFINA)
+    PROD("Delfina Lite", AUDIO, PETSOFF_LP_DELFINA_LITE)
 END
 
 BEGIN_PROD(UWE_GERLACH)
diff -u --recursive --new-file v2.1.90/linux/arch/m68k/atari/atakeyb.c linux/arch/m68k/atari/atakeyb.c
--- v2.1.90/linux/arch/m68k/atari/atakeyb.c	Tue Feb 17 13:12:44 1998
+++ linux/arch/m68k/atari/atakeyb.c	Sat Mar 21 11:04:03 1998
@@ -117,7 +117,7 @@
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
 };
 
-static u_short atashift_map[NR_KEYS] = {
+static u_short atashift_map[NR_KEYS] __initdata = {
 	0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e,
 	0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf008, 0xf009,
 	0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49,
@@ -136,7 +136,7 @@
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
 };
 
-static u_short atactrl_map[NR_KEYS] = {
+static u_short atactrl_map[NR_KEYS] __initdata = {
 	0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e,
 	0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200,
 	0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009,
@@ -155,7 +155,7 @@
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
 };
 
-static u_short atashift_ctrl_map[NR_KEYS] = {
+static u_short atashift_ctrl_map[NR_KEYS] __initdata = {
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200,
 	0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009,
@@ -174,7 +174,7 @@
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
 };
 
-static u_short ataalt_map[NR_KEYS] = {
+static u_short ataalt_map[NR_KEYS] __initdata = {
 	0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836,
 	0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf808, 0xf809,
 	0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869,
@@ -212,7 +212,7 @@
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
 };
 
-static u_short atactrl_alt_map[NR_KEYS] = {
+static u_short atactrl_alt_map[NR_KEYS] __initdata = {
 	0xf200, 0xf200, 0xf200, 0xf800, 0xf81b, 0xf81c, 0xf81d, 0xf81e,
 	0xf81f, 0xf87f, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf200,
 	0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809,
@@ -767,14 +767,14 @@
 __initfunc(int atari_keyb_init(void))
 {
     /* setup key map */
-    memcpy (plain_map, ataplain_map, sizeof(plain_map));
-    key_maps[1]  = atashift_map;
+    memcpy(plain_map, ataplain_map, sizeof(plain_map));
+    memcpy(shift_map, atashift_map, sizeof(shift_map));
     key_maps[2]  = 0; /* ataaltgr_map */
-    key_maps[4]  = atactrl_map;
-    key_maps[5]  = atashift_ctrl_map;
-    key_maps[8]  = ataalt_map;
+    memcpy(ctrl_map, atactrl_map, sizeof(ctrl_map));
+    memcpy(shift_ctrl_map, atashift_ctrl_map, sizeof(shift_ctrl_map));
+    memcpy(alt_map, ataalt_map, sizeof(alt_map));
     key_maps[9]  = atashift_alt_map;
-    key_maps[12] = atactrl_alt_map;
+    memcpy(ctrl_alt_map, atactrl_alt_map, sizeof(ctrl_alt_map));
     key_maps[13] = atashift_ctrl_alt_map;
     keymap_count = 8;
 
diff -u --recursive --new-file v2.1.90/linux/arch/m68k/atari/atari_ksyms.c linux/arch/m68k/atari/atari_ksyms.c
--- v2.1.90/linux/arch/m68k/atari/atari_ksyms.c	Tue Mar 17 22:18:14 1998
+++ linux/arch/m68k/atari/atari_ksyms.c	Sat Mar 21 11:04:03 1998
@@ -1,5 +1,5 @@
+#include <linux/config.h>
 #include <linux/module.h>
-#include <linux/pci.h>
 
 #include <asm/ptrace.h>
 #include <asm/traps.h>
@@ -42,7 +42,3 @@
 EXPORT_SYMBOL(ikbd_mouse_disable);
 
 EXPORT_SYMBOL(atari_microwire_cmd);
-
-#if CONFIG_PCI
-EXPORT_SYMBOL(pci_devices);
-#endif
diff -u --recursive --new-file v2.1.90/linux/arch/m68k/atari/time.c linux/arch/m68k/atari/time.c
--- v2.1.90/linux/arch/m68k/atari/time.c	Mon Feb 23 18:12:02 1998
+++ linux/arch/m68k/atari/time.c	Sat Mar 21 11:04:03 1998
@@ -83,14 +83,14 @@
 
 #define	RTC_READ(reg)				\
     ({	unsigned char	__val;			\
-		outb(reg,&tt_rtc.regsel);	\
+		writeb(reg,&tt_rtc.regsel);	\
 		__val = tt_rtc.data;		\
 		__val;				\
 	})
 
 #define	RTC_WRITE(reg,val)			\
     do {					\
-		outb(reg,&tt_rtc.regsel);	\
+		writeb(reg,&tt_rtc.regsel);	\
 		tt_rtc.data = (val);		\
 	} while(0)
 
diff -u --recursive --new-file v2.1.90/linux/arch/m68k/config.in linux/arch/m68k/config.in
--- v2.1.90/linux/arch/m68k/config.in	Tue Mar 17 22:18:14 1998
+++ linux/arch/m68k/config.in	Sat Mar 21 11:04:03 1998
@@ -37,6 +37,10 @@
 # bool 'BVME4000 and BVME6000 support' CONFIG_BVME6000
 fi
 
+if [ "$CONFIG_PCI" = "y" ]; then
+  bool 'Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC
+fi
+
 comment 'Processor type'
 bool '68020 support' CONFIG_M68020
 bool '68030 support' CONFIG_M68030
@@ -61,6 +65,7 @@
 
 bool 'Networking support' CONFIG_NET
 bool 'System V IPC' CONFIG_SYSVIPC
+bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
 bool 'Sysctl support' CONFIG_SYSCTL
 tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
@@ -138,6 +143,9 @@
   tristate '   Linear (append) mode' CONFIG_MD_LINEAR
   tristate '   RAID-0 (striping) mode' CONFIG_MD_STRIPED
 fi
+if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then
+  bool '      Boot support (linear, striped)' CONFIG_MD_BOOT
+fi
 tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
 if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
   bool '   Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD
@@ -188,7 +196,7 @@
     bool 'A4000T SCSI support' CONFIG_A4000T_SCSI
     bool 'A4091 SCSI support' CONFIG_A4091_SCSI
     bool 'WarpEngine SCSI support' CONFIG_WARPENGINE_SCSI
-    bool 'Cyberstorm Mk III SCSI support' CONFIG_CYBERSTORMIII_SCSI
+#    bool 'Cyberstorm Mk III SCSI support' CONFIG_CYBERSTORMIII_SCSI
 #    bool 'GVP Turbo 040/060 SCSI support' CONFIG_GVP_TURBO_SCSI
   fi
 fi
diff -u --recursive --new-file v2.1.90/linux/arch/m68k/kernel/m68k_defs.head linux/arch/m68k/kernel/m68k_defs.head
--- v2.1.90/linux/arch/m68k/kernel/m68k_defs.head	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/kernel/m68k_defs.head	Sat Mar 21 11:04:04 1998
@@ -0,0 +1,5 @@
+/*
+ * WARNING! This file is automatically generated - DO NOT EDIT!
+ */
+
+#define TS_MAGICKEY	0x5a5a5a5a
diff -u --recursive --new-file v2.1.90/linux/arch/m68k/kernel/m68k_ksyms.c linux/arch/m68k/kernel/m68k_ksyms.c
--- v2.1.90/linux/arch/m68k/kernel/m68k_ksyms.c	Tue Mar 17 22:18:14 1998
+++ linux/arch/m68k/kernel/m68k_ksyms.c	Sat Mar 21 11:04:04 1998
@@ -1,3 +1,4 @@
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/linkage.h>
 #include <linux/sched.h>
@@ -60,6 +61,6 @@
 EXPORT_SYMBOL_NOVERS(__down_failed_interruptible);
 EXPORT_SYMBOL_NOVERS(__up_wakeup);
 
-#if CONFIG_PCI
+#ifdef CONFIG_PCI
 EXPORT_SYMBOL(pci_devices);
 #endif
diff -u --recursive --new-file v2.1.90/linux/arch/m68k/kernel/ptrace.c linux/arch/m68k/kernel/ptrace.c
--- v2.1.90/linux/arch/m68k/kernel/ptrace.c	Tue Feb 17 13:12:45 1998
+++ linux/arch/m68k/kernel/ptrace.c	Sat Mar 21 11:04:04 1998
@@ -439,7 +439,7 @@
 			long tmp;
 
 			ret = -EIO;
-			if ((unsigned long) data >= _NSIG)
+			if ((unsigned long) data > _NSIG)
 				goto out;
 			if (request == PTRACE_SYSCALL)
 				child->flags |= PF_TRACESYS;
@@ -477,7 +477,7 @@
 			long tmp;
 
 			ret = -EIO;
-			if ((unsigned long) data >= _NSIG)
+			if ((unsigned long) data > _NSIG)
 				goto out;
 			child->flags &= ~PF_TRACESYS;
 			tmp = get_reg(child, PT_SR) | (TRACE_BITS << 16);
@@ -494,7 +494,7 @@
 			long tmp;
 
 			ret = -EIO;
-			if ((unsigned long) data >= _NSIG)
+			if ((unsigned long) data > _NSIG)
 				goto out;
 			child->flags &= ~(PF_PTRACED|PF_TRACESYS);
 			wake_up_process(child);
diff -u --recursive --new-file v2.1.90/linux/arch/m68k/mac/ksyms.c linux/arch/m68k/mac/ksyms.c
--- v2.1.90/linux/arch/m68k/mac/ksyms.c	Tue Mar 17 22:18:14 1998
+++ linux/arch/m68k/mac/ksyms.c	Sat Mar 21 11:04:04 1998
@@ -1,12 +1,8 @@
+#include <linux/config.h>
 #include <linux/module.h>
-#include <linux/pci.h>
 #include <asm/ptrace.h>
 #include <asm/traps.h>
 /* Hook for mouse driver */
 extern void (*mac_mouse_interrupt_hook) (char *);
 
 EXPORT_SYMBOL(mac_mouse_interrupt_hook);
-
-#if CONFIG_PCI
-EXPORT_SYMBOL(pci_devices);
-#endif
diff -u --recursive --new-file v2.1.90/linux/arch/m68k/mac/mackeyb.c linux/arch/m68k/mac/mackeyb.c
--- v2.1.90/linux/arch/m68k/mac/mackeyb.c	Mon Feb 23 18:12:02 1998
+++ linux/arch/m68k/mac/mackeyb.c	Sat Mar 21 11:04:04 1998
@@ -16,6 +16,7 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/interrupt.h>
+#include <linux/init.h>
 /* keyb */
 #include <linux/keyboard.h>
 #include <linux/random.h>
@@ -39,7 +40,6 @@
 #define MOUSE_DATAREG	0	/* reg# for movement/button codes from mouse */
 /* end keyboard_input stuff */
 
-#include <asm/keyboard.h>
 #include <linux/kbd_kern.h>
 #include <linux/kbd_ll.h>
 
@@ -81,7 +81,7 @@
 /*
  * Mac private key maps
  */
-u_short mac_plain_map[NR_KEYS] = {
+u_short mac_plain_map[NR_KEYS] __initdata = {
 	0xfb61,	0xfb73,	0xfb64,	0xfb66,	0xfb68,	0xfb67,	0xfb7a,	0xfb78,
 	0xfb63,	0xfb76,	0xf200,	0xfb62,	0xfb71,	0xfb77,	0xfb65,	0xfb72,
 	0xfb79,	0xfb74,	0xf031,	0xf032,	0xf033,	0xf034,	0xf036,	0xf035,
@@ -100,7 +100,7 @@
 	0xf101,	0xf119,	0xf100,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
 };
 
-u_short mac_shift_map[NR_KEYS] = {
+u_short mac_shift_map[NR_KEYS] __initdata = {
 	0xfb41,	0xfb53,	0xfb44,	0xfb46,	0xfb48,	0xfb47,	0xfb5a,	0xfb58,
 	0xfb43,	0xfb56,	0xf200,	0xfb42,	0xfb51,	0xfb57,	0xfb45,	0xfb52,
 	0xfb59,	0xfb54,	0xf021,	0xf040,	0xf023,	0xf024,	0xf05e,	0xf025,
@@ -119,7 +119,7 @@
 	0xf10b,	0xf20a,	0xf10a,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
 };
 
-u_short mac_altgr_map[NR_KEYS] = {
+u_short mac_altgr_map[NR_KEYS] __initdata = {
 	0xf914,	0xfb73,	0xf917,	0xf919,	0xfb68,	0xfb67,	0xfb7a,	0xfb78,
 	0xf916,	0xfb76,	0xf200,	0xf915,	0xfb71,	0xfb77,	0xf918,	0xfb72,
 	0xfb79,	0xfb74,	0xf200,	0xf040,	0xf200,	0xf024,	0xf200,	0xf200,
@@ -138,7 +138,7 @@
 	0xf50d,	0xf119,	0xf50c,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
 };
 
-u_short mac_ctrl_map[NR_KEYS] = {
+u_short mac_ctrl_map[NR_KEYS] __initdata = {
 	0xf001,	0xf013,	0xf004,	0xf006,	0xf008,	0xf007,	0xf01a,	0xf018,
 	0xf003,	0xf016,	0xf200,	0xf002,	0xf011,	0xf017,	0xf005,	0xf012,
 	0xf019,	0xf014,	0xf200,	0xf000,	0xf01b,	0xf01c,	0xf01e,	0xf01d,
@@ -157,7 +157,7 @@
 	0xf101,	0xf119,	0xf100,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
 };
 
-u_short mac_shift_ctrl_map[NR_KEYS] = {
+u_short mac_shift_ctrl_map[NR_KEYS] __initdata = {
 	0xf001,	0xf013,	0xf004,	0xf006,	0xf008,	0xf007,	0xf01a,	0xf018,
 	0xf003,	0xf016,	0xf200,	0xf002,	0xf011,	0xf017,	0xf005,	0xf012,
 	0xf019,	0xf014,	0xf200,	0xf000,	0xf200,	0xf200,	0xf200,	0xf200,
@@ -176,7 +176,7 @@
 	0xf200,	0xf119,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf20c,
 };
 
-u_short mac_alt_map[NR_KEYS] = {
+u_short mac_alt_map[NR_KEYS] __initdata = {
 	0xf861,	0xf873,	0xf864,	0xf866,	0xf868,	0xf867,	0xf87a,	0xf878,
 	0xf863,	0xf876,	0xf200,	0xf862,	0xf871,	0xf877,	0xf865,	0xf872,
 	0xf879,	0xf874,	0xf831,	0xf832,	0xf833,	0xf834,	0xf836,	0xf835,
@@ -195,7 +195,7 @@
 	0xf501,	0xf119,	0xf500,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
 };
 
-u_short mac_ctrl_alt_map[NR_KEYS] = {
+u_short mac_ctrl_alt_map[NR_KEYS] __initdata = {
 	0xf801,	0xf813,	0xf804,	0xf806,	0xf808,	0xf807,	0xf81a,	0xf818,
 	0xf803,	0xf816,	0xf200,	0xf802,	0xf811,	0xf817,	0xf805,	0xf812,
 	0xf819,	0xf814,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
@@ -216,15 +216,6 @@
 
 extern unsigned int keymap_count;
 
-#if 0
-ushort *mac_key_maps[MAX_NR_KEYMAPS] = {
-	mac_plain_map, mac_shift_map, mac_altgr_map, 0,
-	mac_ctrl_map, mac_shift_ctrl_map, 0, 0,
-	mac_alt_map, 0, 0, 0,
-	mac_ctrl_alt_map,	0
-};
-#endif
-
 /*
  * Misc. defines for testing 
  */
@@ -603,23 +594,19 @@
 	return 0;
 }
 
-int mac_keyb_init(void)
+__initfunc(int mac_keyb_init(void))
 {
 	static struct adb_request autopoll_req, confcod_req, mouse_req, readkey_req;
 	volatile int ct;
 
 	/* setup key map */
-	key_maps[0]  = mac_plain_map;
-	key_maps[1]  = mac_shift_map;
-	key_maps[2]  = mac_altgr_map;
-	key_maps[4]  = mac_ctrl_map;
-	key_maps[5]  = mac_shift_ctrl_map;
-	key_maps[8]  = mac_alt_map;
-	/* key_maps[9]  = atashift_alt_map; */
-	key_maps[12] = mac_ctrl_alt_map;
-	/* key_maps[13] = atashift_ctrl_alt_map; */
 	memcpy (plain_map, mac_plain_map, sizeof(plain_map));
-	keymap_count = 7;
+	memcpy(shift_map, mac_shift_map, sizeof(shift_map));
+	memcpy(altgr_map, mac_altgr_map, sizeof(altgr_map));
+	memcpy(ctrl_map, mac_ctrl_map, sizeof(ctrl_map));
+	memcpy(shift_ctrl_map, mac_shift_ctrl_map, sizeof(shift_ctrl_map));
+	memcpy(alt_map, mac_alt_map, sizeof(alt_map));
+	memcpy(ctrl_alt_map, mac_ctrl_alt_map, sizeof(ctrl_alt_map));
 
 	/* initialize mouse interrupt hook */
 	mac_mouse_interrupt_hook = NULL;
diff -u --recursive --new-file v2.1.90/linux/arch/ppc/kernel/misc.S linux/arch/ppc/kernel/misc.S
--- v2.1.90/linux/arch/ppc/kernel/misc.S	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/misc.S	Wed Mar 25 22:04:36 1998
@@ -413,6 +413,7 @@
 SYSCALL(open)
 SYSCALL(close)
 SYSCALL(waitpid)
+SYSCALL(delete_module)
 
 
 /* Why isn't this a) automatic, b) written in 'C'? */	
diff -u --recursive --new-file v2.1.90/linux/drivers/block/amiflop.c linux/drivers/block/amiflop.c
--- v2.1.90/linux/drivers/block/amiflop.c	Mon Feb 23 18:12:04 1998
+++ linux/drivers/block/amiflop.c	Sat Mar 21 11:06:17 1998
@@ -45,11 +45,16 @@
  *    major/minor handling that came with kdev_t. It seems to work for
  *    the time being, but I can't guarantee that it will stay like
  *    that when we start using 16 (24?) bit minors.
+ *
+ * restructured jan 1997 by Joerg Dorchain
+ * - Fixed Bug accessing multiple disks
+ * - some code cleanup
+ * - added trackbuffer for each drive to speed things up
+ * - fixed some race conditions (who finds the next may send it to me ;-)
  */
 
-#ifdef MODULE
 #include <linux/module.h>
-#endif
+
 #include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/fcntl.h>
@@ -61,7 +66,7 @@
 #include <linux/types.h>
 #include <linux/delay.h>
 #include <linux/string.h>
-#include <linux/mm.h>
+#include <linux/slab.h>
 #include <linux/init.h>
 
 #include <asm/setup.h>
@@ -82,20 +87,9 @@
 #define IOCTL_RAW_TRACK 0x5254524B  /* 'RTRK' */
 #endif
 
-/* prototypes */
-
-static int amiga_read(int,unsigned char *, unsigned long, int);
-static void amiga_write(int, unsigned long, unsigned char *, int);
-static int dos_read(int, unsigned char *, unsigned long, int);
-static void dos_write(int, unsigned long, unsigned char *,int);
-static ushort dos_crc(void *, int, int, int);
-static void fd_probe(int);
-
-
 /*
  *  Defines
  */
-#define MAX_SECTORS	22
 
 /*
  *  Error codes
@@ -107,6 +101,11 @@
 #define FD_NOTACTIVE	3	/* unit is not active */
 #define FD_NOTREADY	4	/* unit is not ready (motor not on/no disk) */
 
+#define MFM_NOSYNC	1
+#define MFM_HEADER	2
+#define MFM_DATA	3
+#define MFM_TRACK	4
+
 /*
  *  Floppy ID values
  */
@@ -115,8 +114,9 @@
 #define FD_HD_3 	0x55555555  /* high-density 3.5" (1760K) drive */
 #define FD_DD_5 	0xaaaaaaaa  /* double-density 5.25" (440K) drive */
 
-static int fd_def_df0 = 0;     /* default for df0 if it doesn't identify */
+static long int fd_def_df0 = 0;     /* default for df0 if it doesn't identify */
 
+MODULE_PARM(fd_def_df0,"l");
 
 /*
  *  Macros
@@ -147,26 +147,34 @@
 static int floppy_blocksizes[256]={0,};
 /* hardsector size assumed to be 512 */
 
+static int amiga_read(int), dos_read(int);
+static void amiga_write(int), dos_write(int);
 static struct fd_data_type data_types[] = {
   { "Amiga", 11 , amiga_read, amiga_write},
   { "MS-Dos", 9, dos_read, dos_write}
 };
 
 /* current info on each unit */
-static struct amiga_floppy_struct unit[FD_MAX_UNITS];
+static struct amiga_floppy_struct unit[FD_MAX_UNITS] = {{ 0,}};
 
-static struct timer_list flush_track_timer;
+static struct timer_list flush_track_timer[FD_MAX_UNITS];
 static struct timer_list post_write_timer;
 static struct timer_list motor_on_timer;
 static struct timer_list motor_off_timer[FD_MAX_UNITS];
 static int on_attempts;
 
-/* track buffer */
-static int lastdrive = -1;
-static int savedtrack = -1;
+/* Synchronization of FDC access */
+/* request loop (trackbuffer) */
+static volatile int fdc_busy = -1;
+static volatile int fdc_nested = 0;
+static struct wait_queue *fdc_wait = NULL;
+ 
+static struct wait_queue *motor_wait = NULL;
+
+static volatile int selected = -1;	/* currently selected drive */
+
 static int writepending = 0;
 static int writefromint = 0;
-static unsigned char trackdata[MAX_SECTORS * 512];
 static char *raw_buf;
 
 #define RAW_BUF_SIZE 30000  /* size of raw disk data */
@@ -177,21 +185,8 @@
  * request.
  */
 static volatile char block_flag = 0;
-static volatile int selected = 0;
 static struct wait_queue *wait_fd_block = NULL;
 
-/* Synchronization of FDC access */
-/* request loop (trackbuffer) */
-static volatile int fdc_busy = -1;
-static volatile int fdc_nested = 0;
-static struct wait_queue *fdc_wait = NULL;
-/* hardware */
-static volatile int hw_busy = -1;
-static volatile int hw_nested = 0;
-static struct wait_queue *hw_wait = NULL;
- 
-static struct wait_queue *motor_wait = NULL;
-
 /* MS-Dos MFM Coding tables (should go quick and easy) */
 static unsigned char mfmencode[16]={
   0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15,
@@ -211,13 +206,6 @@
  */
 #define MAX_ERRORS 12
 
-/*
- * The driver is trying to determine the correct media format
- * while probing is set. rw_interrupt() clears it after a
- * successful access.
- */
-static int probing = 0;
-
 /* Prevent "aliased" accesses. */
 static int fd_ref[4] = { 0,0,0,0 };
 static int fd_device[4] = { 0,0,0,0 };
@@ -231,61 +219,15 @@
 /* Current error count. */
 #define CURRENT_ERRORS (CURRENT->errors)
 
-static void get_fdc(int drive)
-{
-unsigned long flags;
 
-       drive &= 3;
-       save_flags(flags);
-       cli();
-       if (fdc_busy != drive)
-         while (!(fdc_busy < 0))
-           sleep_on(&fdc_wait);
-       fdc_busy = drive;
-       fdc_nested++;
-       restore_flags(flags);
-}
 
-static inline void rel_fdc(void)
-{
-#ifdef DEBUG
-       if (fdc_nested == 0)
-         printk("fd: unmatched rel_fdc\n");
-#endif
-       fdc_nested--;
-       if (fdc_nested == 0) {
-         fdc_busy = -1;
-         wake_up(&fdc_wait);
-       }
-}
-
-static void get_hw(int drive)
-{
-unsigned long flags;
+/*
+ * Here come the actual hardware access and helper functions.
+ * They are not reentrant and single threaded because all drives
+ * share the same hardware and the same trackbuffer.
+ */
 
-       drive &= 3;
-       save_flags(flags);
-       cli();
-       if (hw_busy != drive)
-         while (!(hw_busy < 0))
-           sleep_on(&hw_wait);
-       hw_busy = drive;
-       hw_nested++;
-       restore_flags(flags);
-}
-
-static inline void rel_hw(void)
-{
-#ifdef DEBUG
-       if (hw_nested == 0)
-         printk("fd: unmatched hw_rel\n");
-#endif
-       hw_nested--;
-       if (hw_nested == 0) {
-         hw_busy = -1;
-         wake_up(&hw_wait);
-       }
-}
+/* Milliseconds timer */
 
 static void ms_isr(int irq, void *dummy, struct pt_regs *fp)
 {
@@ -314,53 +256,102 @@
   }
 }
 
-/*
- * Functions
- */
-/*======================================================================
-  Turn off the motor of the given drive.  Unit must already be active.
-  Returns standard floppy error code.
-======================================================================*/
-static void fd_motor_off(unsigned long drive)
+/* Hardware semaphore */
+
+/* returns true when we would get the semaphore */
+static inline int try_fdc(int drive)
+{
+	drive &= 3;
+	return ((fdc_busy < 0) || (fdc_busy == drive));
+}
+
+static void get_fdc(int drive)
+{
+unsigned long flags;
+
+       drive &= 3;
+#ifdef DEBUG
+       printk("get_fdc: drive %d  fdc_busy %d  fdc_nested %d\n",drive,fdc_busy,fdc_nested);
+#endif
+       save_flags(flags);
+       cli();
+       while (!try_fdc(drive))
+	       sleep_on(&fdc_wait);
+       fdc_busy = drive;
+       fdc_nested++;
+       restore_flags(flags);
+}
+
+static inline void rel_fdc(void)
+{
+#ifdef DEBUG
+       if (fdc_nested == 0)
+         printk("fd: unmatched rel_fdc\n");
+       printk("rel_fdc: fdc_busy %d fdc_nested %d\n",fdc_busy,fdc_nested);
+#endif
+       fdc_nested--;
+       if (fdc_nested == 0) {
+         fdc_busy = -1;
+         wake_up(&fdc_wait);
+       }
+}
+
+static void fd_select (int drive)
 {
-	unsigned long flags;
 	unsigned char prb = ~0;
 
 	drive&=3;
-	get_hw(drive);
-	save_flags(flags);
-	cli();
+#ifdef DEBUG
+	printk("selecting %d\n",drive);
+#endif
+	if (drive == selected)
+		return;
+	get_fdc(drive);
+	selected = drive;
 
 	if (unit[drive].track % 2 != 0)
 		prb &= ~DSKSIDE;
+	if (unit[drive].motor == 1)
+		prb &= ~DSKMOTOR;
 	ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
 	ciab.prb = prb;
 	prb &= ~SELMASK(drive);
 	ciab.prb = prb;
-	udelay (1);
+	rel_fdc();
+}
+
+static void fd_deselect (int drive)
+{
+	unsigned char prb;
+	unsigned long flags;
+
+	drive&=3;
+#ifdef DEBUG
+	printk("deselecting %d\n",drive);
+#endif
+	if (drive != selected) {
+		printk(KERN_WARNING "Deselecting drive %d while %d was selected!\n",drive,selected);
+		return;
+	}
+
+	get_fdc(drive);
+	save_flags (flags);
+	sti();
+
+	selected = -1;
+
+	prb = ciab.prb;
 	prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
 	ciab.prb = prb;
-	selected = -1;
-	unit[drive].motor = 0;
 
-	rel_hw();
-#ifdef MODULE
-/*
-this is the last interrupt for any drive access, happens after
-release. So we have to wait until now to decrease the use count.
-*/
-       if (fd_ref[drive] == 0)
-         MOD_DEC_USE_COUNT;
-#endif
-	restore_flags(flags);
+	restore_flags (flags);
+	rel_fdc();
+
 }
 
 static void motor_on_callback(unsigned long nr)
 {
-  nr &= 3;
-
 	if (!(ciaa.pra & DSKRDY) || --on_attempts == 0) {
-		unit[nr].motor = 1;
 		wake_up (&motor_wait);
 	} else {
 		motor_on_timer.expires = jiffies + HZ/10;
@@ -368,39 +359,25 @@
 	}
 }
 
-static int motor_on(int nr)
+static int fd_motor_on(int nr)
 {
-	unsigned long flags;
-	unsigned char prb = ~0;
-
 	nr &= 3;
-	get_hw(nr);
-	save_flags (flags);
-	cli();
+
 	del_timer(motor_off_timer + nr);
 
 	if (!unit[nr].motor) {
+		unit[nr].motor = 1;
+		fd_select(nr);
+
 		del_timer(&motor_on_timer);
 		motor_on_timer.data = nr;
 		motor_on_timer.expires = jiffies + HZ/2;
 		add_timer(&motor_on_timer);
-		on_attempts = 10;
-
-
-		prb &= ~DSKMOTOR;
-		if (unit[nr].track % 2 != 0)
-			prb &= ~DSKSIDE;
-		ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
-		ciab.prb = prb;
-		prb &= ~SELMASK(nr);
-		ciab.prb = prb;
-		selected = nr;
 
-		while (!unit[nr].motor)
-			sleep_on (&motor_wait);
+		on_attempts = 10;
+		sleep_on (&motor_wait);
+		fd_deselect(nr);
 	}
-	rel_hw();
-	restore_flags(flags);
 
 	if (on_attempts == 0) {
 		on_attempts = -1;
@@ -416,72 +393,58 @@
 	return 1;
 }
 
-static void floppy_off (unsigned int nr)
-{
-	nr&=3;
-	del_timer(motor_off_timer+nr);
-	motor_off_timer[nr].expires = jiffies + 3*HZ;
-	add_timer(motor_off_timer+nr);
-}
-
-static void fd_select (int drive)
+static void fd_motor_off(unsigned long drive)
 {
-	unsigned char prb = ~0;
+long calledfromint;
+#ifdef MODULE
+long decusecount;
 
+	decusecount = drive & 0x40000000;
+#endif
+	calledfromint = drive & 0x80000000;
 	drive&=3;
-	if (drive == selected)
+	if (calledfromint && !try_fdc(drive)) {
+		/* We would be blocked in an interrupt, so try again later */
+		motor_off_timer[drive].expires = jiffies + 1;
+		add_timer(motor_off_timer + drive);
 		return;
-	get_hw(drive);
-	selected = drive;
+	}
+	unit[drive].motor = 0;
+	fd_select(drive);
+	udelay (1);
+	fd_deselect(drive);
 
-	if (unit[drive].track % 2 != 0)
-		prb &= ~DSKSIDE;
-	if (unit[drive].motor == 1)
-		prb &= ~DSKMOTOR;
-	ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
-	ciab.prb = prb;
-	prb &= ~SELMASK(drive);
-	ciab.prb = prb;
-	rel_hw();
+#ifdef MODULE
+/*
+this is the last interrupt for any drive access, happens after
+release (from floppy_off). So we have to wait until now to decrease
+the use count.
+*/
+       if (decusecount)
+         MOD_DEC_USE_COUNT;
+#endif
 }
 
-static void fd_deselect (int drive)
+static void floppy_off (unsigned int nr)
 {
-	unsigned char prb;
-	unsigned long flags;
-
-	drive&=3;
-	if (drive != selected)
-		return;
-
-	get_hw(drive);
-	save_flags (flags);
-	sti();
-
-	selected = -1;
-
-	prb = ciab.prb;
-	prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
-	ciab.prb = prb;
-
-	restore_flags (flags);
-	rel_hw();
+int drive;
 
+	drive = nr & 3;
+	del_timer(motor_off_timer + drive);
+	motor_off_timer[drive].expires = jiffies + 3*HZ;
+	/* called this way it is always from interrupt */
+	motor_off_timer[drive].data = nr | 0x80000000;
+	add_timer(motor_off_timer + nr);
 }
 
-/*======================================================================
-  Seek the drive to track 0.
-  The drive must be active and the motor must be running.
-  Returns standard floppy error code.
-======================================================================*/
 static int fd_calibrate(int drive)
 {
 	unsigned char prb;
 	int n;
 
 	drive &= 3;
-	get_hw(drive);
-	if (!motor_on (drive))
+	get_fdc(drive);
+	if (!fd_motor_on (drive))
 		return 0;
 	fd_select (drive);
 	prb = ciab.prb;
@@ -511,46 +474,45 @@
 		if ((ciaa.pra & DSKTRACK0) == 0)
 			break;
 		if (--n == 0) {
-			printk (KERN_ERR "calibrate failed, turning motor off\n");
+			printk (KERN_ERR "fd%d: calibrate failed, turning motor off\n", drive);
 			fd_motor_off (drive);
 			unit[drive].track = -1;
-			rel_hw();
+			rel_fdc();
 			return 0;
 		}
 	}
 	unit[drive].track = 0;
 	ms_delay(unit[drive].type->settle_time);
 
-	rel_hw();
+	rel_fdc();
+	fd_deselect(drive);
 	return 1;
 }
 
-/*======================================================================
-  Seek the drive to the requested cylinder.
-  The drive must have been calibrated at some point before this.
-  The drive must also be active and the motor must be running.
-======================================================================*/
 static int fd_seek(int drive, int track)
 {
 	unsigned char prb;
 	int cnt;
 
+#ifdef DEBUG
+	printk("seeking drive %d to track %d\n",drive,track);
+#endif
 	drive &= 3;
-	get_hw(drive);
+	get_fdc(drive);
 	if (unit[drive].track == track) {
-		rel_hw();
+		rel_fdc();
 		return 1;
 	}
-	if (!motor_on(drive)) {
-		rel_hw();
+	if (!fd_motor_on(drive)) {
+		rel_fdc();
 		return 0;
 	}
-	fd_select (drive);
 	if (unit[drive].track < 0 && !fd_calibrate(drive)) {
-		rel_hw();
+		rel_fdc();
 		return 0;
 	}
 
+	fd_select (drive);
 	cnt = unit[drive].track/2 - track/2;
 	prb = ciab.prb;
 	prb |= DSKSIDE | DSKDIREC;
@@ -565,7 +527,8 @@
 		ms_delay (unit[drive].type->side_time);
 	unit[drive].track = track;
 	if (cnt == 0) {
-		rel_hw();
+		rel_fdc();
+		fd_deselect(drive);
 		return 1;
 	}
 	do {
@@ -578,119 +541,189 @@
 	} while (--cnt != 0);
 	ms_delay (unit[drive].type->settle_time);
 
-	rel_hw();
+	rel_fdc();
+	fd_deselect(drive);
 	return 1;
 }
 
-static void encode(unsigned long data, unsigned long *dest)
+static unsigned long fd_get_drive_id(int drive)
 {
-  unsigned long data2;
+	int i;
+	ulong id = 0;
 
-  data &= 0x55555555;
-  data2 = data ^ 0x55555555;
-  data |= ((data2 >> 1) | 0x80000000) & (data2 << 1);
+  	drive&=3;
+  	get_fdc(drive);
+	/* set up for ID */
+	MOTOR_ON;
+	udelay(2);
+	SELECT(SELMASK(drive));
+	udelay(2);
+	DESELECT(SELMASK(drive));
+	udelay(2);
+	MOTOR_OFF;
+	udelay(2);
+	SELECT(SELMASK(drive));
+	udelay(2);
+	DESELECT(SELMASK(drive));
+	udelay(2);
 
-  if (*(dest - 1) & 0x00000001)
-    data &= 0x7FFFFFFF;
+	/* loop and read disk ID */
+	for (i=0; i<32; i++) {
+		SELECT(SELMASK(drive));
+		udelay(2);
 
-  *dest = data;
+		/* read and store value of DSKRDY */
+		id <<= 1;
+		id |= (ciaa.pra & DSKRDY) ? 0 : 1;	/* cia regs are low-active! */
+
+		DESELECT(SELMASK(drive));
+	}
+
+	rel_fdc();
+
+        /*
+         * RB: At least A500/A2000's df0: don't identify themselves.
+         * As every (real) Amiga has at least a 3.5" DD drive as df0:
+         * we default to that if df0: doesn't identify as a certain
+         * type.
+         */
+        if(drive == 0 && id == FD_NODRIVE)
+         {
+                id = fd_def_df0;
+                printk(KERN_NOTICE "fd: drive 0 didn't identify, setting default %08lx\n", (ulong)fd_def_df0);
+         }
+	/* return the ID value */
+	return (id);
 }
 
-static void encode_block(unsigned long *dest, unsigned long *src, int len)
+static void fd_block_done(int irq, void *dummy, struct pt_regs *fp)
 {
-  int cnt, to_cnt = 0;
-  unsigned long data;
+  if (block_flag)
+    custom.dsklen = 0x4000;
 
-  /* odd bits */
-  for (cnt = 0; cnt < len / 4; cnt++) {
-    data = src[cnt] >> 1;
-    encode(data, dest + to_cnt++);
+  if (block_flag == 2) { /* writing */
+    writepending = 2;
+    post_write_timer.expires = jiffies + 1; /* at least 2 ms */
+    post_write_timer.data = selected;
+    add_timer(&post_write_timer);
   }
-
-  /* even bits */
-  for (cnt = 0; cnt < len / 4; cnt++) {
-    data = src[cnt];
-    encode(data, dest + to_cnt++);
+  else {                /* reading */
+    block_flag = 0;
+    wake_up (&wait_fd_block);
   }
 }
 
-unsigned long checksum(unsigned long *addr, int len)
+static void raw_read(int drive)
 {
-	unsigned long csum = 0;
+	drive&=3;
+	get_fdc(drive);
+	while (block_flag)
+		sleep_on(&wait_fd_block);
+	fd_select(drive);
+	/* setup adkcon bits correctly */
+	custom.adkcon = ADK_MSBSYNC;
+	custom.adkcon = ADK_SETCLR|ADK_WORDSYNC|ADK_FAST;
 
-	len /= sizeof(*addr);
-	while (len-- > 0)
-		csum ^= *addr++;
-	csum = ((csum>>1) & 0x55555555)  ^  (csum & 0x55555555);
+	custom.dsksync = MFM_SYNC;
 
-	return csum;
-}
+	custom.dsklen = 0;
+	custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)raw_buf);
+	custom.dsklen = unit[drive].type->read_size/sizeof(short) | DSKLEN_DMAEN;
+	custom.dsklen = unit[drive].type->read_size/sizeof(short) | DSKLEN_DMAEN;
 
-struct header {
-	unsigned char magic;
-	unsigned char track;
-	unsigned char sect;
-	unsigned char ord;
-	unsigned char labels[16];
-	unsigned long hdrchk;
-	unsigned long datachk;
-};
+	block_flag = 1;
+
+	while (block_flag)
+		sleep_on (&wait_fd_block);
+
+	custom.dsklen = 0;
+	fd_deselect(drive);
+	rel_fdc();
+}
 
-static unsigned long *putsec(int disk, unsigned long *raw, int track, int cnt,
-			     unsigned char *data)
+static int raw_write(int drive)
 {
-	struct header hdr;
-	int i;
+	ushort adk;
 
-	disk&=3;
-	*raw = (raw[-1]&1) ? 0x2AAAAAAA : 0xAAAAAAAA;
-	raw++;
-	*raw++ = 0x44894489;
+	drive&=3;
+	get_fdc(drive); /* corresponds to rel_fdc() in post_write() */
+	if ((ciaa.pra & DSKPROT) == 0) {
+		rel_fdc();
+		return 0;
+	}
+	while (block_flag)
+		sleep_on(&wait_fd_block);
+	fd_select(drive);
+	/* clear adkcon bits */
+	custom.adkcon = ADK_PRECOMP1|ADK_PRECOMP0|ADK_WORDSYNC|ADK_MSBSYNC;
+	/* set appropriate adkcon bits */
+	adk = ADK_SETCLR|ADK_FAST;
+	if ((ulong)unit[drive].track >= unit[drive].type->precomp2)
+		adk |= ADK_PRECOMP1;
+	else if ((ulong)unit[drive].track >= unit[drive].type->precomp1)
+		adk |= ADK_PRECOMP0;
+	custom.adkcon = adk;
 
-	hdr.magic = 0xFF;
-	hdr.track = track;
-	hdr.sect = cnt;
-	hdr.ord = unit[disk].sects-cnt;
-	for (i = 0; i < 16; i++)
-		hdr.labels[i] = 0;
-	hdr.hdrchk = checksum((ulong *)&hdr,
-			      (char *)&hdr.hdrchk-(char *)&hdr);
-	hdr.datachk = checksum((ulong *)data, 512);
+	custom.dsklen = DSKLEN_WRITE;
+	custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)raw_buf);
+	custom.dsklen = unit[drive].type->write_size/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE;
+	custom.dsklen = unit[drive].type->write_size/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE;
 
-	encode_block(raw, (ulong *)&hdr.magic, 4);
-	raw += 2;
-	encode_block(raw, (ulong *)&hdr.labels, 16);
-	raw += 8;
-	encode_block(raw, (ulong *)&hdr.hdrchk, 4);
-	raw += 2;
-	encode_block(raw, (ulong *)&hdr.datachk, 4);
-	raw += 2;
-	encode_block(raw, (ulong *)data, 512);
-	raw += 256;
+	block_flag = 2;
+	return 1;
+}
 
-	return raw;
+/*
+ * to be called at least 2ms after the write has finished but before any
+ * other access to the hardware.
+ */
+static void post_write (unsigned long drive)
+{
+#ifdef DEBUG
+  printk("post_write for drive %ld\n",drive);
+#endif
+  drive &= 3;
+  custom.dsklen = 0;
+  block_flag = 0;
+  writepending = 0;
+  writefromint = 0;
+  unit[drive].dirty = 0;
+  wake_up(&wait_fd_block);
+  fd_deselect(drive);
+  rel_fdc(); /* corresponds to get_fdc() in raw_write */
 }
 
 
-/*==========================================================================
-  amiga_write converts track/labels data to raw track data
-==========================================================================*/
-static void amiga_write(int disk, unsigned long raw, unsigned char *data,
-			int track)
+/*
+ * The following functions are to convert the block contents into raw data
+ * written to disk and vice versa.
+ * (Add other formats here ;-))
+ */
+
+static unsigned long scan_sync(unsigned long raw, unsigned long end)
 {
-	unsigned int cnt;
-	unsigned long *ptr = (unsigned long *)raw;
+	ushort *ptr = (ushort *)raw, *endp = (ushort *)end;
 
-	disk&=3;
-	/* gap space */
-	for (cnt = 0; cnt < 415 * unit[disk].type->sect_mult; cnt++)
-		*ptr++ = 0xaaaaaaaa;
+	while (ptr < endp && *ptr++ != 0x4489)
+		;
+	if (ptr < endp) {
+		while (*ptr == 0x4489 && ptr < endp)
+			ptr++;
+		return (ulong)ptr;
+	}
+	return 0;
+}
 
-	/* sectors */
-	for (cnt = 0; cnt < unit[disk].sects; cnt++)
-		ptr = putsec (disk, ptr, track, cnt, data + cnt*512);
-	*(ushort *)ptr = (ptr[-1]&1) ? 0x2AA8 : 0xAAA8;
-	raw = (unsigned long)ptr + 2;
+static inline unsigned long checksum(unsigned long *addr, int len)
+{
+	unsigned long csum = 0;
+
+	len /= sizeof(*addr);
+	while (len-- > 0)
+		csum ^= *addr++;
+	csum = ((csum>>1) & 0x55555555)  ^  (csum & 0x55555555);
+
+	return csum;
 }
 
 static unsigned long decode (unsigned long *data, unsigned long *raw,
@@ -713,45 +746,29 @@
 	return (ulong)raw;
 }
 
-#define MFM_NOSYNC	1
-#define MFM_HEADER	2
-#define MFM_DATA	3
-#define MFM_TRACK	4
-
-/*==========================================================================
- scan_sync - looks for the next start of sector marked by a sync. d3 is the
-		sector number (10..0). When d3 = 10, can't be certain of a
-		starting sync.
-==========================================================================*/
-static unsigned long scan_sync(unsigned long raw, unsigned long end)
-{
-	ushort *ptr = (ushort *)raw, *endp = (ushort *)end;
-
-	while (ptr < endp && *ptr++ != 0x4489)
-		;
-	if (ptr < endp) {
-		while (*ptr == 0x4489 && ptr < endp)
-			ptr++;
-		return (ulong)ptr;
-	}
-	return 0;
-}
+struct header {
+	unsigned char magic;
+	unsigned char track;
+	unsigned char sect;
+	unsigned char ord;
+	unsigned char labels[16];
+	unsigned long hdrchk;
+	unsigned long datachk;
+};
 
-/*==========================================================================
-  amiga_read reads a raw track of data into a track buffer
-==========================================================================*/
-static int amiga_read(int drive, unsigned char *track_data,
-		      unsigned long raw, int track)
+static int amiga_read(int drive)
 {
+	unsigned long raw;
 	unsigned long end;
 	int scnt;
 	unsigned long csum;
 	struct header hdr;
 
 	drive&=3;
+	raw = (long) raw_buf;
 	end = raw + unit[drive].type->read_size;
 
-	for (scnt = 0;scnt < unit[drive].sects; scnt++) {
+	for (scnt = 0;scnt < unit[drive].dtype->sects * unit[drive].type->sect_mult; scnt++) {
 		if (!(raw = scan_sync(raw, end))) {
 			printk (KERN_INFO "can't find sync for sector %d\n", scnt);
 			return MFM_NOSYNC;
@@ -778,24 +795,24 @@
 		}
 
 		/* verify track */
-		if (hdr.track != track) {
-			printk(KERN_INFO "MFM_TRACK: %d, %d\n", hdr.track, track);
+		if (hdr.track != unit[drive].track) {
+			printk(KERN_INFO "MFM_TRACK: %d, %d\n", hdr.track, unit[drive].track);
 			return MFM_TRACK;
 		}
 
-		raw = decode ((ulong *)(track_data + hdr.sect*512),
+		raw = decode ((ulong *)(unit[drive].trackbuf + hdr.sect*512),
 			      (ulong *)raw, 512);
-		csum = checksum((ulong *)(track_data + hdr.sect*512), 512);
+		csum = checksum((ulong *)(unit[drive].trackbuf + hdr.sect*512), 512);
 
 		if (hdr.datachk != csum) {
 			printk(KERN_INFO "MFM_DATA: (%x:%d:%d:%d) sc=%d %lx, %lx\n",
 			       hdr.magic, hdr.track, hdr.sect, hdr.ord, scnt,
 			       hdr.datachk, csum);
 			printk (KERN_INFO "data=(%lx,%lx,%lx,%lx)\n",
-				((ulong *)(track_data+hdr.sect*512))[0],
-				((ulong *)(track_data+hdr.sect*512))[1],
-				((ulong *)(track_data+hdr.sect*512))[2],
-				((ulong *)(track_data+hdr.sect*512))[3]);
+				((ulong *)(unit[drive].trackbuf+hdr.sect*512))[0],
+				((ulong *)(unit[drive].trackbuf+hdr.sect*512))[1],
+				((ulong *)(unit[drive].trackbuf+hdr.sect*512))[2],
+				((ulong *)(unit[drive].trackbuf+hdr.sect*512))[3]);
 			return MFM_DATA;
 		}
 	}
@@ -803,6 +820,89 @@
 	return 0;
 }
 
+static void encode(unsigned long data, unsigned long *dest)
+{
+  unsigned long data2;
+
+  data &= 0x55555555;
+  data2 = data ^ 0x55555555;
+  data |= ((data2 >> 1) | 0x80000000) & (data2 << 1);
+
+  if (*(dest - 1) & 0x00000001)
+    data &= 0x7FFFFFFF;
+
+  *dest = data;
+}
+
+static void encode_block(unsigned long *dest, unsigned long *src, int len)
+{
+  int cnt, to_cnt = 0;
+  unsigned long data;
+
+  /* odd bits */
+  for (cnt = 0; cnt < len / 4; cnt++) {
+    data = src[cnt] >> 1;
+    encode(data, dest + to_cnt++);
+  }
+
+  /* even bits */
+  for (cnt = 0; cnt < len / 4; cnt++) {
+    data = src[cnt];
+    encode(data, dest + to_cnt++);
+  }
+}
+
+static unsigned long *putsec(int disk, unsigned long *raw, int cnt)
+{
+	struct header hdr;
+	int i;
+
+	disk&=3;
+	*raw = (raw[-1]&1) ? 0x2AAAAAAA : 0xAAAAAAAA;
+	raw++;
+	*raw++ = 0x44894489;
+
+	hdr.magic = 0xFF;
+	hdr.track = unit[disk].track;
+	hdr.sect = cnt;
+	hdr.ord = unit[disk].dtype->sects * unit[disk].type->sect_mult - cnt;
+	for (i = 0; i < 16; i++)
+		hdr.labels[i] = 0;
+	hdr.hdrchk = checksum((ulong *)&hdr,
+			      (char *)&hdr.hdrchk-(char *)&hdr);
+	hdr.datachk = checksum((ulong *)(unit[disk].trackbuf+cnt*512), 512);
+
+	encode_block(raw, (ulong *)&hdr.magic, 4);
+	raw += 2;
+	encode_block(raw, (ulong *)&hdr.labels, 16);
+	raw += 8;
+	encode_block(raw, (ulong *)&hdr.hdrchk, 4);
+	raw += 2;
+	encode_block(raw, (ulong *)&hdr.datachk, 4);
+	raw += 2;
+	encode_block(raw, (ulong *)(unit[disk].trackbuf+cnt*512), 512);
+	raw += 256;
+
+	return raw;
+}
+
+static void amiga_write(int disk)
+{
+	unsigned int cnt;
+	unsigned long *ptr = (unsigned long *)raw_buf;
+
+	disk&=3;
+	/* gap space */
+	for (cnt = 0; cnt < 415 * unit[disk].type->sect_mult; cnt++)
+		*ptr++ = 0xaaaaaaaa;
+
+	/* sectors */
+	for (cnt = 0; cnt < unit[disk].dtype->sects * unit[disk].type->sect_mult; cnt++)
+		ptr = putsec (disk, ptr, cnt);
+	*(ushort *)ptr = (ptr[-1]&1) ? 0x2AA8 : 0xAAA8;
+}
+
+
 struct dos_header {
 unsigned char track,   /* 0-80 */
               side,    /* 0-1 */
@@ -811,22 +911,12 @@
 unsigned short crc;     /* on 68000 we got an alignment problem, 
                            but this compiler solves it  by adding silently 
                            adding a pad byte so data won't fit
-                           and this cost about 3h to discover.... */
+                           and this took about 3h to discover.... */
 unsigned char gap1[22];     /* for longword-alignedness (0x4e) */
 };
 
 /* crc routines are borrowed from the messydos-handler  */
 
-static inline ushort dos_hdr_crc (struct dos_header *hdr)
-{
-return dos_crc(&(hdr->track), 0xb2, 0x30, 3); /* precomputed magic */
-}
-
-static inline ushort dos_data_crc(unsigned char *data)
-{
-return dos_crc(data, 0xe2, 0x95 ,511); /* precomputed magic */
-}
-
 /* excerpt from the messydos-device           
 ; The CRC is computed not only over the actual data, but including
 ; the SYNC mark (3 * $a1) and the 'ID/DATA - Address Mark' ($fe/$fb).
@@ -937,6 +1027,16 @@
 return (crch<<8)|crcl;
 }
 
+static inline ushort dos_hdr_crc (struct dos_header *hdr)
+{
+return dos_crc(&(hdr->track), 0xb2, 0x30, 3); /* precomputed magic */
+}
+
+static inline ushort dos_data_crc(unsigned char *data)
+{
+return dos_crc(data, 0xe2, 0x95 ,511); /* precomputed magic */
+}
+
 static inline unsigned char dos_decode_byte(ushort word)
 {
 register ushort w2;
@@ -965,30 +1065,28 @@
 #ifdef DEBUG
 static void dbg(unsigned long ptr)
 {
-printk("raw data @%08lx: %08lx, %08lx ,%08lx, %08lx\n",ptr,
-  ((ulong *)ptr)[0],((ulong *)ptr)[1],((ulong *)ptr)[2],((ulong *)ptr)[3]);
+  printk("raw data @%08lx: %08lx, %08lx ,%08lx, %08lx\n",ptr,
+    ((ulong *)ptr)[0],((ulong *)ptr)[1],((ulong *)ptr)[2],((ulong *)ptr)[3]);
 }
 #endif
 
-/*******************************************************************
-   this reads a raw track of data into trackbuffer for ms-disks 
-*******************************************************************/
-static int dos_read(int drive, unsigned char *track_data,
-	unsigned long raw, int track)
+static int dos_read(int drive)
 {
   unsigned long end;
+  unsigned long raw;
   int scnt;
   unsigned short crc,data_crc[2];
   struct dos_header hdr;
 
   drive&=3;
+  raw = (long) raw_buf;
   end = raw + unit[drive].type->read_size;
 
-  for (scnt=0;scnt<unit[drive].sects;scnt++) {
+  for (scnt=0; scnt < unit[drive].dtype->sects * unit[drive].type->sect_mult; scnt++) {
   do { /* search for the right sync of each sec-hdr */
     if (!(raw = scan_sync (raw, end))) {
       printk(KERN_INFO "dos_read: no hdr sync on track %d, unit %d for sector %d\n",
-        track,drive,scnt);
+        unit[drive].track,drive,scnt);
       return MFM_NOSYNC;
     }
 #ifdef DEBUG
@@ -1008,15 +1106,15 @@
     printk(KERN_INFO "dos_read: MFM_HEADER %04x,%04x\n", hdr.crc, crc);
     return MFM_HEADER;
   }
-  if (hdr.track != track/unit[drive].type->heads) {
+  if (hdr.track != unit[drive].track/unit[drive].type->heads) {
     printk(KERN_INFO "dos_read: MFM_TRACK %d, %d\n", hdr.track,
-      track/unit[drive].type->heads);
+      unit[drive].track/unit[drive].type->heads);
     return MFM_TRACK;
   }
 
-  if (hdr.side != track%unit[drive].type->heads) {
+  if (hdr.side != unit[drive].track%unit[drive].type->heads) {
     printk(KERN_INFO "dos_read: MFM_SIDE %d, %d\n", hdr.side,
-      track%unit[drive].type->heads);
+      unit[drive].track%unit[drive].type->heads);
     return MFM_TRACK;
   }
 
@@ -1029,7 +1127,7 @@
 #endif
   if (!(raw = scan_sync (raw, end))) {
     printk(KERN_INFO "dos_read: no data sync on track %d, unit %d for sector%d, disk sector %d\n",
-      track, drive, scnt, hdr.sec);
+      unit[drive].track, drive, scnt, hdr.sec);
     return MFM_NOSYNC;
   }
 #ifdef DEBUG
@@ -1043,19 +1141,19 @@
   }
 
   raw+=2;  /* skip data mark (included in checksum) */
-  raw = dos_decode((unsigned char *)(track_data + (hdr.sec - 1) * 512), (ushort *) raw, 512);
+  raw = dos_decode((unsigned char *)(unit[drive].trackbuf + (hdr.sec - 1) * 512), (ushort *) raw, 512);
   raw = dos_decode((unsigned char  *)data_crc,(ushort *) raw,4);
-  crc = dos_data_crc(track_data + (hdr.sec - 1) * 512);
+  crc = dos_data_crc(unit[drive].trackbuf + (hdr.sec - 1) * 512);
 
   if (crc != data_crc[0]) {
     printk(KERN_INFO "dos_read: MFM_DATA (%d,%d,%d,%d) sc=%d, %x %x\n",
       hdr.track, hdr.side, hdr.sec, hdr.len_desc,
       scnt,data_crc[0], crc);
     printk(KERN_INFO "data=(%lx,%lx,%lx,%lx,...)\n",
-      ((ulong *)(track_data+(hdr.sec-1)*512))[0],
-      ((ulong *)(track_data+(hdr.sec-1)*512))[1],
-      ((ulong *)(track_data+(hdr.sec-1)*512))[2],
-      ((ulong *)(track_data+(hdr.sec-1)*512))[3]);
+      ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[0],
+      ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[1],
+      ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[2],
+      ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[3]);
     return MFM_DATA;
   }
   }
@@ -1086,8 +1184,7 @@
 }
 }
 
-static unsigned long *ms_putsec(int drive, unsigned long *raw, int track, int cnt,
-   unsigned char *data)
+static unsigned long *ms_putsec(int drive, unsigned long *raw, int cnt)
 {
 static struct dos_header hdr={0,0,0,2,0,
   {78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78}};
@@ -1104,8 +1201,8 @@
 *raw++=0x44895554;
 
 /* fill in the variable parts of the header */
-hdr.track=track/unit[drive].type->heads;
-hdr.side=track%unit[drive].type->heads;
+hdr.track=unit[drive].track/unit[drive].type->heads;
+hdr.side=unit[drive].track%unit[drive].type->heads;
 hdr.sec=cnt+1;
 hdr.crc=dos_hdr_crc(&hdr);
 
@@ -1122,229 +1219,82 @@
 *raw++=0x44895545;
 
 /* data */
-dos_encode_block((ushort *)raw,(unsigned char *)data,512);
-raw+=256;
-
-/*data crc + jd's special gap (long words :-/) */
-crc[0]=dos_data_crc(data);
-dos_encode_block((ushort *) raw,(unsigned char *)crc,4);
-raw+=2;
-
-/* data gap */
-for(i=0;i<38;i++)
-  *raw++=0x92549254;
-
-return raw; /* wrote 652 MFM words */
-}
-
-
-/**************************************************************
-  builds encoded track data from trackbuffer data
-**************************************************************/
-static void dos_write(int disk, unsigned long raw, unsigned char *data,
-    int track)
-{
-int cnt;
-unsigned long *ptr=(unsigned long *)raw;
-
-disk&=3;
-/* really gap4 + indexgap , but we write it first and round it up */
-for (cnt=0;cnt<425;cnt++)
-  *ptr++=0x92549254;
-
-/* the following is just guessed */
-if (unit[disk].type->sect_mult==2)  /* check for HD-Disks */
-  for(cnt=0;cnt<473;cnt++)
-    *ptr++=0x92549254;
-
-/* now the index marks...*/
-for (cnt=0;cnt<20;cnt++)
-  *ptr++=0x92549254;
-for (cnt=0;cnt<6;cnt++)
-  *ptr++=0xaaaaaaaa;
-*ptr++=0x52245224;
-*ptr++=0x52245552;
-for (cnt=0;cnt<20;cnt++)
-  *ptr++=0x92549254;
-
-/* sectors */
-for(cnt=0;cnt<unit[disk].sects;cnt++)
-  ptr=ms_putsec(disk,ptr,track,cnt,data+cnt*512);
-
-*(ushort *)ptr = 0xaaa8; /* MFM word before is always 0x9254 */
-}
-
-static void request_done(int uptodate)
-{
-  timer_active &= ~(1 << FLOPPY_TIMER);
-  end_request(uptodate);
-}
-
-/*
- * floppy-change is never called from an interrupt, so we can relax a bit
- * here, sleep etc. Note that floppy-on tries to set current_DOR to point
- * to the desired drive, but it will probably not survive the sleep if
- * several floppies are used at the same time: thus the loop.
- */
-static int amiga_floppy_change(kdev_t dev)
-{
-	int drive = dev & 3;
-	int changed;
-	static int first_time = 1;
-
-	if (MAJOR(dev) != MAJOR_NR) {
-		printk(KERN_CRIT "floppy_change: not a floppy\n");
-		return 0;
-	}
-
-	if (first_time)
-		changed = first_time--;
-	else {
-		get_hw(drive);
-		fd_select (drive);
-		changed = !(ciaa.pra & DSKCHANGE);
-		fd_deselect (drive);
-		rel_hw();
-	}
-
-	if (changed) {
-		fd_probe(dev);
-		unit[drive].track = -1;
-		selected = -1;
-		savedtrack = -1;
-		writepending = 0; /* if this was true before, too bad! */
-		writefromint = 0;
-		return 1;
-	}
-	return 0;
-}
-
-static __inline__ void copy_buffer(void *from, void *to)
-{
-  ulong *p1,*p2;
-  int cnt;
-
-  p1 = (ulong *)from;
-  p2 = (ulong *)to;
-
-  for (cnt = 0; cnt < 512/4; cnt++)
-    *p2++ = *p1++;
-}
-
-static void raw_read(int drive, int track, char *ptrack, int len)
-{
-	drive&=3;
-	get_hw(drive);
-	/* setup adkcon bits correctly */
-	custom.adkcon = ADK_MSBSYNC;
-	custom.adkcon = ADK_SETCLR|ADK_WORDSYNC|ADK_FAST;
-
-	custom.dsksync = MFM_SYNC;
-
-	custom.dsklen = 0;
-#if 0
-	ms_delay (unit[drive].type->side_time);
-#endif
-	custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)ptrack);
-	custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN;
-	custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN;
+dos_encode_block((ushort *)raw,(unsigned char *)unit[drive].trackbuf+cnt*512,512);
+raw+=256;
 
-	block_flag = 1;
+/*data crc + jd's special gap (long words :-/) */
+crc[0]=dos_data_crc(unit[drive].trackbuf+cnt*512);
+dos_encode_block((ushort *) raw,(unsigned char *)crc,4);
+raw+=2;
 
-	while (block_flag == 1)
-		sleep_on (&wait_fd_block);
+/* data gap */
+for(i=0;i<38;i++)
+  *raw++=0x92549254;
 
-	custom.dsklen = 0;
-	rel_hw();
+return raw; /* wrote 652 MFM words */
 }
 
-static int raw_write(int drive, int track, char *ptrack, int len)
+static void dos_write(int disk)
 {
-	ushort adk;
+int cnt;
+unsigned long raw = (unsigned long) raw_buf;
+unsigned long *ptr=(unsigned long *)raw;
 
-	drive&=3;
-	if ((ciaa.pra & DSKPROT) == 0)
-		return 0;
+disk&=3;
+/* really gap4 + indexgap , but we write it first and round it up */
+for (cnt=0;cnt<425;cnt++)
+  *ptr++=0x92549254;
 
-	get_hw(drive); /* corresponds to rel_hw() in post_write() */
-	/* clear adkcon bits */
-	custom.adkcon = ADK_PRECOMP1|ADK_PRECOMP0|ADK_WORDSYNC|ADK_MSBSYNC;
-	/* set appropriate adkcon bits */
-	adk = ADK_SETCLR|ADK_FAST;
-	if ((ulong)track >= unit[drive].type->precomp2)
-		adk |= ADK_PRECOMP1;
-	else if ((ulong)track >= unit[drive].type->precomp1)
-		adk |= ADK_PRECOMP0;
-	custom.adkcon = adk;
+/* the following is just guessed */
+if (unit[disk].type->sect_mult==2)  /* check for HD-Disks */
+  for(cnt=0;cnt<473;cnt++)
+    *ptr++=0x92549254;
 
-	custom.dsklen = DSKLEN_WRITE;
-#if 0
-	ms_delay (unit[drive].type->side_time);
-#endif
-	custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)ptrack);
-	custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE;
-	custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE;
+/* now the index marks...*/
+for (cnt=0;cnt<20;cnt++)
+  *ptr++=0x92549254;
+for (cnt=0;cnt<6;cnt++)
+  *ptr++=0xaaaaaaaa;
+*ptr++=0x52245224;
+*ptr++=0x52245552;
+for (cnt=0;cnt<20;cnt++)
+  *ptr++=0x92549254;
 
-	block_flag = 2;
-	return 1;
-}
+/* sectors */
+for(cnt = 0; cnt < unit[disk].dtype->sects * unit[disk].type->sect_mult; cnt++)
+  ptr=ms_putsec(disk,ptr,cnt);
 
-static void post_write (unsigned long dummy)
-{
-  custom.dsklen = 0;
-  writepending = 0;
-  writefromint = 0;
-  rel_hw(); /* corresponds to get_hw() in raw_write */
+*(ushort *)ptr = 0xaaa8; /* MFM word before is always 0x9254 */
 }
 
-static int get_track(int drive, int track)
-{
-	int error, errors;
-
-	drive&=3;
-        get_hw(drive);
-        if (!motor_on(drive)) {
-          rel_hw();
-          return -1;
-        }
-        fd_select(drive);
-        errors = 0;
-        while (errors < MAX_ERRORS) {
-          if (!fd_seek(drive, track)) {
-            rel_hw();
-            return -1; /* we can not calibrate - no chance */
-          }
-          if ((lastdrive == drive) && (savedtrack == track)) {
-            rel_hw();
-            return 0;
-          }
-          lastdrive = drive;
-          raw_read(drive, track, raw_buf, unit[drive].type->read_size);
-          savedtrack = -1;
-          error = (*unit[drive].dtype->read_fkt)(drive, trackdata, (unsigned long)raw_buf, track);
-          if (error == 0) {
-            savedtrack = track;
-            rel_hw();
-            return 0;
-          }
-          if (error == MFM_TRACK)
-            unit[drive].track = -1;
-          errors++;
-	}
-	rel_hw();
-	return -1;
-}
+/*
+ * Here comes the high level stuff (i.e. the filesystem interface)
+ * and helper functions.
+ * Normally this should be the only part that has to be adapted to
+ * different kernel versions.
+ */
 
+/* FIXME: this assumes the drive is still spinning -
+ * which is only true if we complete writing a track within three seconds
+ */
 static void flush_track_callback(unsigned long nr)
 {
-  nr&=3;
-  writefromint = 1;
-  (*unit[nr].dtype->write_fkt)(nr, (unsigned long)raw_buf, trackdata, savedtrack);
-  if (!raw_write(nr, savedtrack, raw_buf, unit[nr].type->write_size)) {
-    printk (KERN_NOTICE "floppy disk write protected\n");
-    writefromint = 0;
-    writepending = 0;
-  }
+	nr&=3;
+	writefromint = 1;
+	if (!try_fdc(nr)) {
+		/* we might block in an interrupt, so try again later */
+		flush_track_timer[nr].expires = jiffies + 1;
+		add_timer(flush_track_timer + nr);
+		return;
+	}
+	get_fdc(nr);
+	(*unit[nr].dtype->write_fkt)(nr);
+	if (!raw_write(nr)) {
+		printk (KERN_NOTICE "floppy disk write protected\n");
+		writefromint = 0;
+		writepending = 0;
+	}
+	rel_fdc();
 }
 
 static int non_int_flush_track (unsigned long nr)
@@ -1354,12 +1304,18 @@
   nr&=3;
   writefromint = 0;
   del_timer(&post_write_timer);
+  get_fdc(nr);
+  if (!fd_motor_on(nr)) {
+  	writepending = 0;
+  	rel_fdc();
+  	return 0;
+  }
   save_flags(flags);
   cli();
   if (writepending != 2) {
     restore_flags(flags);
-    (*unit[nr].dtype->write_fkt)(nr, (unsigned long)raw_buf, trackdata, savedtrack);
-    if (!raw_write(nr, savedtrack, raw_buf, unit[nr].type->write_size)) {
+    (*unit[nr].dtype->write_fkt)(nr);
+    if (!raw_write(nr)) {
       printk (KERN_NOTICE "floppy disk write protected in write!\n");
       writepending = 0;
       return 0;
@@ -1367,13 +1323,50 @@
     while (block_flag == 2)
       sleep_on (&wait_fd_block);
   }
-  else
+  else {
     restore_flags(flags);
-  ms_delay(2); /* 2 ms post_write delay */
-  post_write(0);
+    ms_delay(2); /* 2 ms post_write delay */
+    post_write(nr);
+  }
+  rel_fdc();
   return 1;
 }
 
+static int get_track(int drive, int track)
+{
+	int error, errcnt;
+
+	drive&=3;
+	if (unit[drive].track == track)
+		return 0;
+	get_fdc(drive);
+	if (!fd_motor_on(drive)) {
+		rel_fdc();
+		return -1;
+	}
+
+	if (unit[drive].dirty == 1) {
+		del_timer (flush_track_timer + drive);
+		non_int_flush_track (drive);
+	}
+	errcnt = 0;
+	while (errcnt < MAX_ERRORS) {
+		if (!fd_seek(drive, track))
+			return -1;
+		raw_read(drive);
+		error = (*unit[drive].dtype->read_fkt)(drive);
+		if (error == 0) {
+			rel_fdc();
+			return 0;
+		}
+		/* Read Error Handling: recalibrate and try again */
+		unit[drive].track = -1;
+		errcnt++;
+	}
+	rel_fdc();
+	return -1;
+}
+
 static void redo_fd_request(void)
 {
 	unsigned int cnt, block, track, sector;
@@ -1388,10 +1381,7 @@
 
     repeat:
 	if (!CURRENT) {
-		if (fdc_busy < 0)
-			printk(KERN_CRIT "FDC access conflict!");
-		rel_fdc();
-		CLEAR_INTR;
+		/* Nothing left to do */
 		return;
 	}
 
@@ -1401,74 +1391,64 @@
 	if (CURRENT->bh && !buffer_locked(CURRENT->bh))
 		panic(DEVICE_NAME ": block not locked");
 
-	probing = 0;
 	device = MINOR(CURRENT_DEVICE);
-	if (device > 3) {
+	if (device < 8) {
 		/* manual selection */
 		drive = device & 3;
 		floppy = unit + drive;
 	} else {
 		/* Auto-detection */
-		/* printk("redo_fd_request: can't handle auto detect\n");*/
-		/* printk("redo_fd_request: default to normal\n");*/
+#ifdef DEBUG
+		printk("redo_fd_request: can't handle auto detect\n");
+		printk("redo_fd_request: default to normal\n");
+#endif
 		drive = device & 3;
 		floppy = unit + drive;
 	}
 
-	save_flags (flags);
-	cli();
-	if (drive != selected && writepending) {
-	  del_timer (&flush_track_timer);
-	  restore_flags (flags);
-	  if (!non_int_flush_track (selected)) {
-	    end_request(0);
-	    goto repeat;
-	  }
-	} else
-	  restore_flags (flags);
-
  /* Here someone could investigate to be more efficient */
 	for (cnt = 0; cnt < CURRENT->current_nr_sectors; cnt++) { 
 #ifdef DEBUG
-		printk("fd: sector %d + %d requested\n",CURRENT->sector,cnt);
+		printk("fd: sector %ld + %d requested for %s\n",CURRENT->sector,cnt,
+			(CURRENT->cmd==READ)?"read":"write");
 #endif
 		block = CURRENT->sector + cnt;
 		if ((int)block > floppy->blocks) {
-			request_done(0);
+			end_request(0);
 			goto repeat;
 		}
 
-		track = block / floppy->sects;
-		sector = block % floppy->sects;
+		track = block / (floppy->dtype->sects * floppy->type->sect_mult);
+		sector = block % (floppy->dtype->sects * floppy->type->sect_mult);
 		data = CURRENT->buffer + 512 * cnt;
+#ifdef DEBUG
+		printk("access to track %d, sector %d, with buffer at 0x%08lx\n",
+			track, sector, data);
+#endif
 
-		save_flags (flags);
-		cli();
-		if (track != savedtrack && writepending) {
-		  del_timer (&flush_track_timer);
-		  restore_flags (flags);
-		  if (!non_int_flush_track (selected)) {
-		    end_request(0);
-		    goto repeat;
-		  }
-		} else
-		  restore_flags (flags);
+		if ((CURRENT->cmd != READ) && (CURRENT->cmd != WRITE)) {
+			printk(KERN_WARNING "do_fd_request: unknown command\n");
+			end_request(0);
+			goto repeat;
+		}
+		if (get_track(drive, track) == -1) {
+			end_request(0);
+			goto repeat;
+		}
 
 		switch (CURRENT->cmd) {
 		    case READ:
-			if (get_track(drive, track) == -1) {
-				end_request(0);
-				goto repeat;
-			}
-			copy_buffer(trackdata + sector * 512, data);
+			memcpy(data, unit[drive].trackbuf + sector * 512, 512);
 			break;
 
 		    case WRITE:
-			if (get_track(drive, track) == -1) {
+			memcpy(unit[drive].trackbuf + sector * 512, data, 512);
+
+			/* keep the drive spinning while writes are scheduled */
+			if (!fd_motor_on(drive)) {
 				end_request(0);
 				goto repeat;
 			}
-			copy_buffer(data, trackdata + sector * 512);
 			/*
 			 * setup a callback to write the track buffer
 			 * after a short (1 tick) delay.
@@ -1476,33 +1456,25 @@
 			save_flags (flags);
 			cli();
 
-			if (writepending)
-			    /* reset the timer */
-			    del_timer (&flush_track_timer);
+			unit[drive].dirty = 1;
+		        /* reset the timer */
+		        del_timer (flush_track_timer + drive);
 			    
-			writepending = 1;
-			flush_track_timer.data = drive;
-			flush_track_timer.expires = jiffies + 1;
-			add_timer (&flush_track_timer);
+			flush_track_timer[drive].expires = jiffies + 1;
+			add_timer (flush_track_timer + drive);
 			restore_flags (flags);
 			break;
-
-		    default:
-			printk(KERN_WARNING "do_fd_request: unknown command\n");
-			request_done(0);
-			goto repeat;
 		}
 	}
 	CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
 	CURRENT->sector += CURRENT->current_nr_sectors;
 
-	request_done(1);
+	end_request(1);
 	goto repeat;
 }
 
 static void do_fd_request(void)
 {
-	get_fdc(CURRENT_DEVICE & 3);
 	redo_fd_request();
 }
 
@@ -1512,53 +1484,48 @@
 	int drive = inode->i_rdev & 3;
 	static struct floppy_struct getprm;
 	struct super_block * sb;
-	unsigned long flags;
 
 	switch(cmd){
 	case HDIO_GETGEO:
 	{
 		struct hd_geometry loc;
 		loc.heads = unit[drive].type->heads;
-		loc.sectors = unit[drive].sects;
+		loc.sectors = unit[drive].dtype->sects * unit[drive].type->sect_mult;
 		loc.cylinders = unit[drive].type->tracks;
 		loc.start = 0;
 		if (copy_to_user((void *)param, (void *)&loc,
-				 sizeof(struct hd_geometry)))
+					  sizeof(struct hd_geometry)))
 			return -EFAULT;
 		break;
 	}
 	case FDFMTBEG:
-		get_hw(drive);
+		get_fdc(drive);
 		if (fd_ref[drive] > 1) {
-			rel_hw();
+			rel_fdc();
 			return -EBUSY;
 		}
 		fsync_dev(inode->i_rdev);
-		if (motor_on(drive) == 0) {
-			rel_hw();
+		if (fd_motor_on(drive) == 0) {
+			rel_fdc();
 			return -ENODEV;
 		}
 		if (fd_calibrate(drive) == 0) {
-			rel_hw();
+			rel_fdc();
 			return -ENXIO;
 		}
 		floppy_off(drive);
-		rel_hw();
+		rel_fdc();
 		break;
 	case FDFMTTRK:
 		if (param < unit[drive].type->tracks * unit[drive].type->heads)
 		{
 			get_fdc(drive);
-			get_hw(drive);
-			fd_select(drive);
-			if (fd_seek(drive,param)!=0){
-				savedtrack=param;
-				memset(trackdata, FD_FILL_BYTE,
-				       unit[drive].sects*512);
+			if (fd_seek(drive,param) != 0){
+				memset(unit[drive].trackbuf, FD_FILL_BYTE,
+				       unit[drive].dtype->sects * unit[drive].type->sect_mult * 512);
 				non_int_flush_track(drive);
 			}
 			floppy_off(drive);
-			rel_hw();
 			rel_fdc();
 		}
 		else
@@ -1575,33 +1542,27 @@
 		memset((void *)&getprm, 0, sizeof (getprm));
 		getprm.track=unit[drive].type->tracks;
 		getprm.head=unit[drive].type->heads;
-		getprm.sect=unit[drive].sects;
+		getprm.sect=unit[drive].dtype->sects * unit[drive].type->sect_mult;
 		getprm.size=unit[drive].blocks;
 		if (copy_to_user((void *)param,
-				 (void *)&getprm,
-				 sizeof(struct floppy_struct)))
+					  (void *)&getprm,
+					  sizeof(struct floppy_struct)))
 			return -EFAULT;
 	    break;
 	case BLKGETSIZE:
 		return put_user(unit[drive].blocks,(long *)param);
+		break;
 	case FDSETPRM:
 	case FDDEFPRM:
 		return -EINVAL;
-	case FDFLUSH:
-		save_flags(flags);
-		cli();
-		if ((drive == selected) && (writepending)) {
-			del_timer (&flush_track_timer);
-			restore_flags(flags);
-			non_int_flush_track(selected);
-		}
-		else
-			restore_flags(flags);
+	case FDFLUSH: /* unconditionally, even if not needed */
+		del_timer (flush_track_timer + drive);
+		non_int_flush_track(drive);
 		break;
 #ifdef RAW_IOCTL
 	case IOCTL_RAW_TRACK:
 		if (copy_to_user((void *)param, raw_buf,
-				 unit[drive].type->read_size))
+				     unit[drive].type->read_size))
 			return -EFAULT;
 		else
 			return unit[drive].type->read_size;
@@ -1613,76 +1574,16 @@
 	return 0;
 }
 
-/*======================================================================
-  Return unit ID number of given disk
-======================================================================*/
-static unsigned long get_drive_id(int drive)
-{
-	static int called = 0;
-	int i;
-	ulong id = 0;
-
-  	drive&=3;
-  	get_hw(drive);
-	/* set up for ID */
-	MOTOR_ON;
-	udelay(2);
-	SELECT(SELMASK(drive));
-	udelay(2);
-	DESELECT(SELMASK(drive));
-	udelay(2);
-	MOTOR_OFF;
-	udelay(2);
-	SELECT(SELMASK(drive));
-	udelay(2);
-	DESELECT(SELMASK(drive));
-	udelay(2);
-
-	/* loop and read disk ID */
-	for (i=0; i<32; i++) {
-		SELECT(SELMASK(drive));
-		udelay(2);
-
-		/* read and store value of DSKRDY */
-		id <<= 1;
-		id |= (ciaa.pra & DSKRDY) ? 0 : 1;	/* cia regs are low-active! */
-
-		DESELECT(SELMASK(drive));
-	}
-
-	selected = -1;
-	rel_hw();
-
-        /*
-         * RB: At least A500/A2000's df0: don't identify themselves.
-         * As every (real) Amiga has at least a 3.5" DD drive as df0:
-         * we default to that if df0: doesn't identify as a certain
-         * type.
-         */
-        if(drive == 0 && id == FD_NODRIVE)
-         {
-                id = fd_def_df0;
-                printk("%sfd: drive 0 didn't identify, setting default %08lx\n",
-			(called == 0)? KERN_NOTICE:"", (ulong)fd_def_df0);
-		if (called == 0)
-			called++;
-         }
-	/* return the ID value */
-	return (id);
-}
-
 static void fd_probe(int dev)
 {
 	unsigned long code;
 	int type;
 	int drive;
-	int system;
 
 	drive = dev & 3;
-	code = get_drive_id(drive);
+	code = fd_get_drive_id(drive);
 
 	/* get drive type */
-	unit[drive].type = NULL;
 	for (type = 0; type < num_dr_types; type++)
 		if (drive_types[type].code == code)
 			break;
@@ -1694,39 +1595,13 @@
 		return;
 	}
 
-	unit[drive].type = &drive_types[type];
+	unit[drive].type = drive_types + type;
 	unit[drive].track = -1;
 
 	unit[drive].disk = -1;
 	unit[drive].motor = 0;
 	unit[drive].busy = 0;
 	unit[drive].status = -1;
-
-
-	system=(dev & 4)>>2;
-	unit[drive].dtype=&data_types[system];
-	unit[drive].sects=data_types[system].sects*unit[drive].type->sect_mult;
-	unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks*
-	    unit[drive].sects;
-
-	floppy_sizes[MINOR(dev)] = unit[drive].blocks >> 1;
-
-}
-
-__initfunc(static void probe_drives(void))
-{
-	int drive,found;
-
-	printk(KERN_INFO "FD: probing units\n" KERN_INFO "found ");
-	found=0;
-	for(drive=0;drive<FD_MAX_UNITS;drive++) {
-	  fd_probe(drive);
-	  if (unit[drive].type->code != FD_NODRIVE) {
-	    printk("fd%d ",drive);
-	    found=1;
-	  }
-	}
-	printk("%s\n",(found==0)?" no drives":"");
 }
 
 /*
@@ -1741,7 +1616,7 @@
   int system;
   unsigned long flags;
 
-  drive = inode->i_rdev & 3;
+  drive = MINOR(inode->i_rdev) & 3;
   old_dev = fd_device[drive];
 
   if (fd_ref[drive])
@@ -1751,17 +1626,20 @@
   if (unit[drive].type->code == FD_NODRIVE)
     return -ENODEV;
 
-  if (filp && (filp->f_flags & (O_WRONLY|O_RDWR))) {
+  if (filp && filp->f_mode & 3) {
+    check_disk_change(inode->i_rdev);
+    if (filp->f_mode & 2 ) {
 	  int wrprot;
 
-	  get_hw(drive);
+	  get_fdc(drive);
 	  fd_select (drive);
 	  wrprot = !(ciaa.pra & DSKPROT);
 	  fd_deselect (drive);
-	  rel_hw();
+	  rel_fdc();
 
 	  if (wrprot)
 		  return -EROFS;
+    }
   }
 
   save_flags(flags);
@@ -1777,55 +1655,87 @@
   if (old_dev && old_dev != inode->i_rdev)
     invalidate_buffers(old_dev);
 
-  if (filp && filp->f_mode)
-    check_disk_change(inode->i_rdev);
-
   system=(inode->i_rdev & 4)>>2;
   unit[drive].dtype=&data_types[system];
-  unit[drive].sects=data_types[system].sects*unit[drive].type->sect_mult;
   unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks*
-        unit[drive].sects;
+		data_types[system].sects*unit[drive].type->sect_mult;
+  floppy_sizes[MINOR(inode->i_rdev)] = unit[drive].blocks >> 1;
 
-printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive,unit[drive].type->name,
-  data_types[system].name);
+  printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive,
+	unit[drive].type->name, data_types[system].name);
 
   return 0;
 }
 
 static int floppy_release(struct inode * inode, struct file * filp)
 {
-  unsigned long flags;
+#ifdef DEBUG
   struct super_block * sb;
+#endif
+  int drive = MINOR(inode->i_rdev) & 3;
 
   fsync_dev(inode->i_rdev);
+
+#ifdef DEBUG
+  /* This is now handled in floppy_change, but still useful for debugging */
   sb = get_super(inode->i_rdev);
   if (sb)
 	  invalidate_inodes(sb);
   invalidate_buffers(inode->i_rdev);
-  save_flags (flags);
-  cli();
-  if ((inode->i_rdev & 3) == selected && writepending) {
-    del_timer (&flush_track_timer);
-    restore_flags (flags);
-    non_int_flush_track (selected);
-  } else
-    restore_flags (flags);
+#endif
+
+  if (unit[drive].dirty == 1) {
+	  del_timer (flush_track_timer + drive);
+	  non_int_flush_track (drive);
+  }
   
-  if (!fd_ref[inode->i_rdev & 3]--) {
+  if (!fd_ref[drive]--) {
     printk(KERN_CRIT "floppy_release with fd_ref == 0");
-    fd_ref[inode->i_rdev & 3] = 0;
+    fd_ref[drive] = 0;
   }
 #ifdef MODULE
 /* the mod_use counter is handled this way */
-  floppy_off (inode->i_rdev & 3);
+  floppy_off (drive | 0x40000000);
 #endif
   return 0;
 }
 
-__initfunc(void amiga_floppy_setup (char *str, int *ints))
+/*
+ * floppy-change is never called from an interrupt, so we can relax a bit
+ * here, sleep etc. Note that floppy-on tries to set current_DOR to point
+ * to the desired drive, but it will probably not survive the sleep if
+ * several floppies are used at the same time: thus the loop.
+ */
+static int amiga_floppy_change(kdev_t dev)
 {
-	printk ("amiflop: Setting default df0 to %x\n", ints[1]);
-	fd_def_df0 = ints[1];
+	int drive = MINOR(dev) & 3;
+	int changed;
+	static int first_time = 1;
+
+	if (MAJOR(dev) != MAJOR_NR) {
+		printk(KERN_CRIT "floppy_change: not a floppy\n");
+		return 0;
+	}
+
+	if (first_time)
+		changed = first_time--;
+	else {
+		get_fdc(drive);
+		fd_select (drive);
+		changed = !(ciaa.pra & DSKCHANGE);
+		fd_deselect (drive);
+		rel_fdc();
+	}
+
+	if (changed) {
+		fd_probe(drive);
+		unit[drive].track = -1;
+		unit[drive].dirty = 0;
+		writepending = 0; /* if this was true before, too bad! */
+		writefromint = 0;
+		return 1;
+	}
+	return 0;
 }
 
 static struct file_operations floppy_fops = {
@@ -1844,24 +1754,40 @@
 	NULL,			/* revalidate */
 };
 
-static void fd_block_done(int irq, void *dummy, struct pt_regs *fp)
+__initfunc(void amiga_floppy_setup (char *str, int *ints))
 {
-  if (block_flag)
-    custom.dsklen = 0x4000;
-
-  block_flag = 0;
-  wake_up (&wait_fd_block);
+	printk (KERN_INFO "amiflop: Setting default df0 to %x\n", ints[1]);
+	fd_def_df0 = ints[1];
+}
 
-  if (writefromint) {
-    /* 
-     * if it was a write from an interrupt,
-     * we will call post_write from here
-     */
-    writepending = 2;
-    post_write_timer.expires = 1; /* at least 2 ms */
-    add_timer(&post_write_timer);
-  }
+__initfunc(static int fd_probe_drives(void))
+{
+	int drive,drives,nomem;
 
+	printk(KERN_INFO "FD: probing units\n" KERN_INFO "found ");
+	drives=0;
+	nomem=0;
+	for(drive=0;drive<FD_MAX_UNITS;drive++) {
+	  fd_probe(drive);
+	  if (unit[drive].type->code != FD_NODRIVE) {
+	    drives++;
+	    if ((unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL)) == NULL) {
+	      printk("no mem for ");
+	      unit[drive].type = &drive_types[num_dr_types - 1]; /* FD_NODRIVE */
+	      drives--;
+	      nomem = 1;
+	    }
+	    printk("fd%d ",drive);
+	  }
+	}
+	if ((drives > 0) || (nomem == 0)) {
+	  if (drives == 0)
+	    printk("no drives");
+	  printk("\n");
+	  return drives;
+	}
+	printk("\n");
+	return -ENOMEM;
 }
 
 __initfunc(int amiga_floppy_init(void))
@@ -1870,11 +1796,36 @@
 
   if (!AMIGAHW_PRESENT(AMI_FLOPPY))
     return -ENXIO;
-
   if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
-    printk("Unable to get major %d for floppy\n",MAJOR_NR);
+    printk("fd: Unable to get major %d for floppy\n",MAJOR_NR);
     return -EBUSY;
   }
+  if ((raw_buf = (char *)amiga_chip_alloc (RAW_BUF_SIZE)) == NULL) {
+  	printk("fd: cannot get chip mem buffer\n");
+  	unregister_blkdev(MAJOR_NR,"fd");
+  	return -ENOMEM;
+  }
+
+  if (request_irq(IRQ_FLOPPY, fd_block_done, 0, "floppy_dma", NULL) != 0) {
+  	printk("fd: cannot get irq for dma\n");
+  	amiga_chip_free(raw_buf);
+  	unregister_blkdev(MAJOR_NR,"fd");
+  	return -EBUSY;
+  }
+  if (request_irq(IRQ_AMIGA_CIAA_TB, ms_isr, 0, "floppy_timer", NULL) != 0) {
+  	printk("fd: cannot get irq for timer\n");
+  	free_irq(IRQ_FLOPPY, NULL);
+  	amiga_chip_free(raw_buf);
+  	unregister_blkdev(MAJOR_NR,"fd");
+  	return -EBUSY;
+  }
+  if (fd_probe_drives() < 1) { /* No usable drives */
+  	free_irq(IRQ_AMIGA_CIAA_TB, NULL);
+  	free_irq(IRQ_FLOPPY, NULL);
+  	amiga_chip_free(raw_buf);
+  	unregister_blkdev(MAJOR_NR,"fd");
+  	return -ENXIO;
+  }
 
   /* initialize variables */
   motor_on_timer.next = NULL;
@@ -1886,18 +1837,17 @@
 	  motor_off_timer[i].next = NULL;
 	  motor_off_timer[i].prev = NULL;
 	  motor_off_timer[i].expires = 0;
-	  motor_off_timer[i].data = i;
+	  motor_off_timer[i].data = i|0x80000000;
 	  motor_off_timer[i].function = fd_motor_off;
+	  flush_track_timer[i].next = NULL;
+	  flush_track_timer[i].prev = NULL;
+	  flush_track_timer[i].expires = 0;
+	  flush_track_timer[i].data = i;
+	  flush_track_timer[i].function = flush_track_callback;
 
 	  unit[i].track = -1;
   }
 
-  flush_track_timer.next = NULL;
-  flush_track_timer.prev = NULL;
-  flush_track_timer.expires = 0;
-  flush_track_timer.data = 0;
-  flush_track_timer.function = flush_track_callback;
-
   post_write_timer.next = NULL;
   post_write_timer.prev = NULL;
   post_write_timer.expires = 0;
@@ -1909,9 +1859,6 @@
   blk_size[MAJOR_NR] = floppy_sizes;
 
 
-  timer_table[FLOPPY_TIMER].fn = NULL;
-  timer_active &= ~(1 << FLOPPY_TIMER);
-
   #if 0 /* Doesn't seem to be correct */
   if (fd_def_df0==0) {
     if ((amiga.model == AMI_3000) || (amiga.model == AMI_3000T) ||
@@ -1925,14 +1872,10 @@
   fd_def_df0 = FD_DD_3;
   #endif
 
-  probe_drives();
-
-  raw_buf = (char *)amiga_chip_alloc (RAW_BUF_SIZE);
-
   for (i = 0; i < 128; i++)
-    mfmdecode[i]=255;
+	  mfmdecode[i]=255;
   for (i = 0; i < 16; i++)
-    mfmdecode[mfmencode[i]]=i;
+	  mfmdecode[mfmencode[i]]=i;
 
   /* make sure that disk DMA is enabled */
   custom.dmacon = DMAF_SETCLR | DMAF_DISK;
@@ -1940,9 +1883,7 @@
   /* init ms timer */
   ciaa.crb = 8; /* one-shot, stop */
 
-  request_irq(IRQ_FLOPPY, fd_block_done, 0, "floppy_dma", NULL);
-  request_irq(IRQ_AMIGA_CIAA_TB, ms_isr, 0, "floppy_timer", NULL);
-
+  (void)do_floppy; /* avoid warning about unused variable */
   return 0;
 }
 
@@ -1958,11 +1899,15 @@
 
 void cleanup_module(void)
 {
+int i;
+
+for( i = 0; i < FD_MAX_UNITS; i++)
+  if (unit[i].type->code != FD_NODRIVE)
+    kfree(unit[i].trackbuf);
 free_irq(IRQ_AMIGA_CIAA_TB, NULL);
 free_irq(IRQ_FLOPPY, NULL);
 custom.dmacon = DMAF_DISK; /* disable DMA */
 amiga_chip_free(raw_buf);
-timer_active &= ~(1 << FLOPPY_TIMER);
 blk_size[MAJOR_NR] = NULL;
 blksize_size[MAJOR_NR] = NULL;
 blk_dev[MAJOR_NR].request_fn = NULL;
diff -u --recursive --new-file v2.1.90/linux/drivers/block/loop.c linux/drivers/block/loop.c
--- v2.1.90/linux/drivers/block/loop.c	Wed Jul 16 20:37:21 1997
+++ linux/drivers/block/loop.c	Mon Mar 23 10:13:52 1998
@@ -19,7 +19,9 @@
 #include <linux/module.h>
 
 #include <linux/config.h>
+#include <linux/sched.h>
 #include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/stat.h>
 #include <linux/errno.h>
 #include <linux/major.h>
@@ -277,7 +279,7 @@
 	end_request(1);
 	goto repeat;
 error_out:
-    current_request->next=CURRENT;
+	current_request->next=CURRENT;
 	CURRENT=current_request;
 	end_request(0);
 	goto repeat;
@@ -287,27 +289,36 @@
 {
 	struct file	*file;
 	struct inode	*inode;
+	int error;
+
+	MOD_INC_USE_COUNT;
+	error = -EBADF;
+	file = fget(arg);
+	if (!file)
+		goto out;
 
-	if (arg >= NR_OPEN || !(file = current->files->fd[arg]))
-		return -EBADF;
+	error = -EBUSY;
 	if (lo->lo_inode)
-		return -EBUSY;
+		goto out_putf;
+
+	error = -EINVAL;
 	inode = file->f_dentry->d_inode;
 	if (!inode) {
 		printk("loop_set_fd: NULL inode?!?\n");
-		return -EINVAL;
+		goto out_putf;
 	}
+
 	if (S_ISBLK(inode->i_mode)) {
-		int error = blkdev_open(inode, file);
-		if (error)
-			return error;
+		error = blkdev_open(inode, file);
 		lo->lo_device = inode->i_rdev;
 		lo->lo_flags = 0;
 	} else if (S_ISREG(inode->i_mode)) {
 		lo->lo_device = inode->i_dev;
 		lo->lo_flags = LO_FLAGS_DO_BMAP;
-	} else
-		return -EINVAL;
+		error = 0;
+	}
+	if (error)
+		goto out_putf;
 
 	if (IS_RDONLY (inode) || is_read_only(lo->lo_device)) {
 		lo->lo_flags |= LO_FLAGS_READ_ONLY;
@@ -317,25 +328,34 @@
 		set_device_ro(dev, 0);
 	}
 
+	/* N.B. Should keep the file or dentry ... */
+	inode->i_count++;
 	lo->lo_inode = inode;
-	lo->lo_inode->i_count++;
 	lo->transfer = NULL;
 	figure_loop_size(lo);
-	MOD_INC_USE_COUNT;
-	return 0;
+
+out_putf:
+	fput(file);
+out:
+	if (error)
+		MOD_DEC_USE_COUNT;
+	return error;
 }
 
 static int loop_clr_fd(struct loop_device *lo, kdev_t dev)
 {
-	if (!lo->lo_inode)
+	struct inode *inode = lo->lo_inode;
+
+	if (!inode)
 		return -ENXIO;
 	if (lo->lo_refcnt > 1)	/* we needed one fd for the ioctl */
 		return -EBUSY;
-	if (S_ISBLK(lo->lo_inode->i_mode))
-		blkdev_release (lo->lo_inode);
-	iput(lo->lo_inode);
-	lo->lo_device = 0;
+
+	if (S_ISBLK(inode->i_mode))
+		blkdev_release (inode);
 	lo->lo_inode = NULL;
+	iput(inode);
+	lo->lo_device = 0;
 	lo->lo_encrypt_type = 0;
 	lo->lo_offset = 0;
 	lo->lo_encrypt_key_size = 0;
diff -u --recursive --new-file v2.1.90/linux/drivers/block/nbd.c linux/drivers/block/nbd.c
--- v2.1.90/linux/drivers/block/nbd.c	Wed Dec  3 15:26:21 1997
+++ linux/drivers/block/nbd.c	Mon Mar 23 10:13:52 1998
@@ -313,7 +313,7 @@
 		     unsigned int cmd, unsigned long arg)
 {
 	struct nbd_device *lo;
-	int dev;
+	int dev, error;
 
 	if (!suser())
 		return -EPERM;
@@ -322,6 +322,7 @@
 	dev = MINOR(inode->i_rdev);
 	if (dev >= MAX_NBD)
 		return -ENODEV;
+
 	lo = &nbd_dev[dev];
 	switch (cmd) {
 	case NBD_CLEAR_SOCK:
@@ -329,19 +330,26 @@
 			printk(KERN_ERR "nbd: Some requests are in progress -> can not turn off.\n");
 			return -EBUSY;
 		}
-		if (!lo->file)
+		file = lo->file;
+		if (!file)
 			return -EINVAL;
-		lo->file->f_count--;
 		lo->file = NULL;
 		lo->sock = NULL;
+		fput(file);
 		return 0;
 	case NBD_SET_SOCK:
-		file = current->files->fd[arg];
-		inode = file->f_dentry->d_inode;
-		file->f_count++;
-		lo->sock = &inode->u.socket_i;
-		lo->file = file;
-		return 0;
+		if (lo->file)
+			return -EBUSY;
+		error = -EINVAL;
+		file = fget(arg);
+		if (file) {
+			inode = file->f_dentry->d_inode;
+			/* N.B. Should verify that it's a socket */
+			lo->file = file;
+			lo->sock = &inode->u.socket_i;
+			error = 0;
+		}
+		return error;
 	case NBD_SET_BLKSIZE:
 		if ((arg & 511) || (arg > PAGE_SIZE))
 			return -EINVAL;
@@ -383,6 +391,7 @@
 	if (lo->refcnt <= 0)
 		printk(KERN_ALERT "nbd_release: refcount(%d) <= 0\n", lo->refcnt);
 	lo->refcnt--;
+	/* N.B. Doesn't lo->file need an fput?? */
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
diff -u --recursive --new-file v2.1.90/linux/drivers/char/amigamouse.c linux/drivers/char/amigamouse.c
--- v2.1.90/linux/drivers/char/amigamouse.c	Tue Mar 10 10:03:31 1998
+++ linux/drivers/char/amigamouse.c	Sat Mar 21 11:06:17 1998
@@ -267,7 +267,7 @@
 	mouse.ready = 0;
 	AMI_MSE_INT_ON();
 
-	if ((put_user(buttons | 0x80, buffer++)) ||
+	if (put_user(buttons | 0x80, buffer++) ||
 	    put_user((char)dx, buffer++) ||
 	    put_user((char)dy, buffer++))
 		return -EINVAL;
diff -u --recursive --new-file v2.1.90/linux/drivers/char/amikeyb.c linux/drivers/char/amikeyb.c
--- v2.1.90/linux/drivers/char/amikeyb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/amikeyb.c	Sat Mar 21 11:06:17 1998
@@ -0,0 +1,343 @@
+/*
+ * linux/arch/m68k/amiga/amikeyb.c
+ *
+ * Amiga Keyboard driver for Linux/m68k
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+/*
+ * Amiga support by Hamish Macdonald
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/keyboard.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/kd.h>
+#include <linux/random.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/kbd_ll.h>
+
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/irq.h>
+
+#define AMIKEY_CAPS	(0x62)
+#define BREAK_MASK	(0x80)
+#define RESET_WARNING	(0xf0)	/* before rotation */
+
+static u_short amiplain_map[NR_KEYS] __initdata = {
+	0xf060, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037,
+	0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf05c, 0xf200, 0xf300,
+	0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69,
+	0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf200, 0xf301, 0xf302, 0xf303,
+	0xfb61, 0xfb73, 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b,
+	0xfb6c, 0xf03b, 0xf027, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306,
+	0xf200, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 0xfb62, 0xfb6e, 0xfb6d,
+	0xf02c, 0xf02e, 0xf02f, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309,
+	0xf020, 0xf07f, 0xf009, 0xf30e, 0xf201, 0xf01b, 0xf07f, 0xf200,
+	0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601,
+	0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, 0xf107,
+	0xf108, 0xf109, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf30a, 0xf11b,
+	0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static u_short amishift_map[NR_KEYS] __initdata = {
+	0xf07e, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 0xf026,
+	0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07c, 0xf200, 0xf300,
+	0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49,
+	0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf200, 0xf301, 0xf302, 0xf303,
+	0xfb41, 0xfb53, 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b,
+	0xfb4c, 0xf03a, 0xf022, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306,
+	0xf200, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 0xfb42, 0xfb4e, 0xfb4d,
+	0xf03c, 0xf03e, 0xf03f, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309,
+	0xf020, 0xf07f, 0xf009, 0xf30e, 0xf201, 0xf01b, 0xf07f, 0xf200,
+	0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601,
+	0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, 0xf111,
+	0xf112, 0xf113, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf203,
+	0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static u_short amialtgr_map[NR_KEYS] __initdata = {
+	0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, 0xf07b,
+	0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200, 0xf300,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf07e, 0xf200, 0xf301, 0xf302, 0xf303,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309,
+	0xf200, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601,
+	0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 0xf511, 0xf512, 0xf513,
+	0xf514, 0xf515, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf204,
+	0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static u_short amictrl_map[NR_KEYS] __initdata = {
+	0xf000, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f,
+	0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf01c, 0xf200, 0xf300,
+	0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009,
+	0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf200, 0xf301, 0xf302, 0xf303,
+	0xf001, 0xf013, 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b,
+	0xf00c, 0xf200, 0xf007, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306,
+	0xf200, 0xf01a, 0xf018, 0xf003, 0xf016, 0xf002, 0xf00e, 0xf00d,
+	0xf200, 0xf200, 0xf07f, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309,
+	0xf000, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601,
+	0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, 0xf107,
+	0xf108, 0xf109, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf202,
+	0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static u_short amishift_ctrl_map[NR_KEYS] __initdata = {
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf300,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf301, 0xf302, 0xf303,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309,
+	0xf200, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf200,
+	0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static u_short amialt_map[NR_KEYS] __initdata = {
+	0xf860, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 0xf837,
+	0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf85c, 0xf200, 0xf900,
+	0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869,
+	0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf200, 0xf901, 0xf902, 0xf903,
+	0xf861, 0xf873, 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b,
+	0xf86c, 0xf83b, 0xf827, 0xf200, 0xf200, 0xf904, 0xf905, 0xf906,
+	0xf200, 0xf87a, 0xf878, 0xf863, 0xf876, 0xf862, 0xf86e, 0xf86d,
+	0xf82c, 0xf82e, 0xf82f, 0xf200, 0xf310, 0xf907, 0xf908, 0xf909,
+	0xf820, 0xf87f, 0xf809, 0xf30e, 0xf80d, 0xf81b, 0xf87f, 0xf200,
+	0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601,
+	0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 0xf505, 0xf506, 0xf507,
+	0xf508, 0xf509, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf204,
+	0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static u_short amictrl_alt_map[NR_KEYS] __initdata = {
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf300,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf301, 0xf302, 0xf303,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf20c, 0xf307, 0xf308, 0xf309,
+	0xf200, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf200,
+	0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+#define	DEFAULT_KEYB_REP_DELAY	(HZ/4)
+#define	DEFAULT_KEYB_REP_RATE	(HZ/25)
+
+/* These could be settable by some ioctl() in future... */
+static unsigned int key_repeat_delay = DEFAULT_KEYB_REP_DELAY;
+static unsigned int key_repeat_rate  = DEFAULT_KEYB_REP_RATE;
+
+static unsigned char rep_scancode;
+static void amikeyb_rep(unsigned long ignore);
+static struct timer_list amikeyb_rep_timer = {NULL, NULL, 0, 0, amikeyb_rep};
+
+static void amikeyb_rep(unsigned long ignore)
+{
+    unsigned long flags;
+    save_flags(flags);
+    cli();
+
+    kbd_pt_regs = NULL;
+
+    amikeyb_rep_timer.expires = jiffies + key_repeat_rate;
+    amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL;
+    add_timer(&amikeyb_rep_timer);
+    handle_scancode(rep_scancode);
+
+    restore_flags(flags);
+}
+
+static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+    unsigned char scancode, break_flag, keycode;
+    static int reset_warning = 0;
+
+    /* save frame for register dump */
+    kbd_pt_regs = fp;
+
+    /* get and invert scancode (keyboard is active low) */
+    scancode = ~ciaa.sdr;
+
+    /* switch SP pin to output for handshake */
+    ciaa.cra |= 0x40;
+
+#if 0 /* No longer used */
+    /*
+     *  On receipt of the second RESET_WARNING, we must not pull KDAT high
+     *  again to delay the hard reset as long as possible.
+     *
+     *  Note that not all keyboards send reset warnings...
+     */
+    if (reset_warning)
+	if (scancode == RESET_WARNING) {
+	    printk(KERN_ALERT "amikeyb: Ctrl-Amiga-Amiga reset warning!!\n"
+		   "The system will be reset within 10 seconds!!\n");
+	    /* Panic doesn't sync from within an interrupt, so we do nothing */
+	    return;
+	} else
+	    /* Probably a mistake, cancel the alert */
+	    reset_warning = 0;
+#endif
+
+    /* wait until 85 us have expired */
+    udelay(85);
+    /* switch CIA serial port to input mode */
+    ciaa.cra &= ~0x40;
+
+    mark_bh(KEYBOARD_BH);
+
+    /* rotate scan code to get up/down bit in proper position */
+    scancode = ((scancode >> 1) & 0x7f) | ((scancode << 7) & 0x80);
+
+    /*
+     * Check make/break first
+     */
+    break_flag = scancode & BREAK_MASK;
+    keycode = scancode & (unsigned char)~BREAK_MASK;
+
+    if (keycode == AMIKEY_CAPS) {
+	/* if the key is CAPS, fake a press/release. */
+	handle_scancode(AMIKEY_CAPS);
+	handle_scancode(BREAK_MASK | AMIKEY_CAPS);
+    } else if (keycode < 0x78) {
+	/* handle repeat */
+	if (break_flag) {
+	    del_timer(&amikeyb_rep_timer);
+	    rep_scancode = 0;
+	} else {
+	    del_timer(&amikeyb_rep_timer);
+	    rep_scancode = keycode;
+	    amikeyb_rep_timer.expires = jiffies + key_repeat_delay;
+	    amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL;
+	    add_timer(&amikeyb_rep_timer);
+	}
+	handle_scancode(scancode);
+    } else
+	switch (keycode) {
+	    case 0x78:
+		reset_warning = 1;
+		break;
+	    case 0x79:
+		printk(KERN_WARNING "amikeyb: keyboard lost sync\n");
+		break;
+	    case 0x7a:
+		printk(KERN_WARNING "amikeyb: keyboard buffer overflow\n");
+		break;
+#if 0 /* obsolete according to the HRM */
+	    case 0x7b:
+		printk(KERN_WARNING "amikeyb: keyboard controller failure\n");
+		break;
+#endif
+	    case 0x7c:
+		printk(KERN_ERR "amikeyb: keyboard selftest failure\n");
+		break;
+	    case 0x7d:
+		printk(KERN_INFO "amikeyb: initiate power-up key stream\n");
+		break;
+	    case 0x7e:
+		printk(KERN_INFO "amikeyb: terminate power-up key stream\n");
+		break;
+#if 0 /* obsolete according to the HRM */
+	    case 0x7f:
+		printk(KERN_WARNING "amikeyb: keyboard interrupt\n");
+		break;
+#endif
+	    default:
+		printk(KERN_WARNING "amikeyb: unknown keyboard communication code 0x%02x\n",
+		       scancode);
+		break;
+	}
+}
+
+__initfunc(int amiga_keyb_init(void))
+{
+    if (!AMIGAHW_PRESENT(AMI_KEYBOARD))
+        return -EIO;
+
+    /* setup key map */
+    memcpy(plain_map, amiplain_map, sizeof(plain_map));
+    memcpy(shift_map, amishift_map, sizeof(shift_map));
+    memcpy(altgr_map, amialtgr_map, sizeof(altgr_map));
+    memcpy(ctrl_map, amictrl_map, sizeof(ctrl_map));
+    memcpy(shift_ctrl_map, amishift_ctrl_map, sizeof(shift_ctrl_map));
+    memcpy(alt_map, amialt_map, sizeof(alt_map));
+    memcpy(ctrl_alt_map, amictrl_alt_map, sizeof(ctrl_alt_map));
+
+    /*
+     * Initialize serial data direction.
+     */
+    ciaa.cra &= ~0x41;	     /* serial data in, turn off TA */
+
+    /*
+     * arrange for processing of keyboard interrupt
+     */
+    request_irq(IRQ_AMIGA_CIAA_SP, keyboard_interrupt, 0, "keyboard", NULL);
+
+    return 0;
+}
+
+int amiga_kbdrate( struct kbd_repeat *k )
+{
+    if (k->delay > 0) {
+	/* convert from msec to jiffies */
+	key_repeat_delay = (k->delay * HZ + 500) / 1000;
+	if (key_repeat_delay < 1)
+            key_repeat_delay = 1;
+    }
+    if (k->rate > 0) {
+	key_repeat_rate = (k->rate * HZ + 500) / 1000;
+	if (key_repeat_rate < 1)
+            key_repeat_rate = 1;
+    }
+
+    k->delay = key_repeat_delay * 1000 / HZ;
+    k->rate  = key_repeat_rate  * 1000 / HZ;
+    
+    return( 0 );
+}
diff -u --recursive --new-file v2.1.90/linux/drivers/char/dn_keyb.c linux/drivers/char/dn_keyb.c
--- v2.1.90/linux/drivers/char/dn_keyb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/dn_keyb.c	Sat Mar 21 11:06:17 1998
@@ -0,0 +1,614 @@
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/keyboard.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/kd.h>
+#include <linux/random.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+
+#include <asm/setup.h>
+#include <asm/irq.h>
+#include <asm/apollohw.h>
+#include <asm/uaccess.h>
+
+
+extern void handle_scancode(unsigned char);
+
+#define DNKEY_CAPS 0x7e
+#define BREAK_FLAG 0x80
+#define DNKEY_REPEAT_DELAY 50
+#define DNKEY_CTRL 0x43
+#define DNKEY_LSHIFT 0x5e
+#define DNKEY_RSHIFT 0x6a
+#define DNKEY_REPT 0x5d
+#define DNKEY_REPEAT 0x7f
+#define DNKEY_LALT 0x75
+#define DNKEY_RALT 0x77
+
+#define APOLLO_KEYB_CMD_ENTRIES 16
+#define APOLLO_KBD_MODE_KEYB   0x01
+#define APOLLO_KBD_MODE_MOUSE   0x02
+#define APOLLO_KBD_MODE_CHANGE 0xff
+
+#define MSE_UPDATE_ON() mouse_update_allowed=1
+#define MSE_UPDATE_OFF() mouse_update_allowed=0
+
+static u_char keyb_cmds[APOLLO_KEYB_CMD_ENTRIES];
+static short keyb_cmd_read=0, keyb_cmd_write=0;
+static int keyb_cmd_transmit=0;
+
+static unsigned int kbd_mode=APOLLO_KBD_MODE_KEYB;
+static short mouse_dx,mouse_dy,mouse_buttons;
+static int mouse_ready=0,mouse_update_allowed=0,mouse_active=0;
+static struct wait_queue *mouse_wait=NULL;
+static struct fasync_struct *mouse_fasyncptr=NULL;
+
+#if 0
+static void debug_keyb_timer_handler(unsigned long ignored);
+static u_char debug_buf1[4096],debug_buf2[4096],*debug_buf=&debug_buf1[0];
+static u_char *shadow_buf=&debug_buf2[0];
+static short debug_buf_count=0;
+static int debug_buf_overrun=0,debug_timer_running=0,debug_buffer_updated=0;
+static struct timer_list debug_keyb_timer = { NULL, NULL, 0, 0,
+											  debug_keyb_timer_handler };
+#endif
+
+static u_short dnplain_map[NR_KEYS] __initdata = {
+/*         ins     del     del     F1      F2      F3      F4  
+           mark    line    char                                 */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+/* F5      F6      F7      F8      F9      F0      Again   Read */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+/* Edit    Exit    Hold    Copy    Paste   Grow            ESC  */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf01b,
+/* 1       2       3       4       5       6       7       8    */
+   0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, 0xf038,
+/* 9       0       -       =       `       Back            |<--
+                                           Space                */
+   0xf039, 0xf030, 0xf02d, 0xf03d, 0xf060, 0xf07f, 0xf200, 0xf200,
+/* Shell   -->|                    Tab     q       w       e
+   Cmd                                                          */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf009, 0xfb71, 0xfb77, 0xfb65,
+/* r       t       y       u       i       o       p       [    */
+   0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 0xfb6f, 0xfb70, 0xf05b,
+/* ]               Del             7       8       9       +    */
+   0xf05d, 0xf200, 0xf200, 0xf200, 0xf307, 0xf308, 0xf300, 0xf30a,
+/* [<--]   Up      [-->]   Ctrl                    a       s    */
+   0xf200, 0xf600, 0xf200, 0xf702, 0xf200, 0xf200, 0xfb61, 0xfb73,
+/* d       f       g       h       j       k       l       ;    */
+   0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b,
+/* '               Return  \               4       5       6    */
+   0xf027, 0xf200, 0xf201, 0xf05c, 0xf200, 0xf304, 0xf305, 0xf306,
+/* -       <--     Next    -->             Rept    Shift        
+                   Window                                       */
+   0xf30b, 0xf601, 0xf200, 0xf602, 0xf200, 0xf200, 0xf700, 0xf200,
+/* z       x       c       v       b       n       m       ,    */
+   0xfb7a, 0xfb78, 0xfb63, 0xfb76, 0xfb62, 0xfb6e, 0xfb6d, 0xf02c,
+/* .       /       Shift           Pop             1       2    */
+   0xf02e, 0xf02f, 0xf700, 0xf200, 0xf200, 0xf200, 0xf301, 0xf302,
+/* 3               PgUp    Down    PgDn    Alt     Space   Alt  */
+   0xf303, 0xf200, 0xf118, 0xf603, 0xf119, 0xf703, 0xf020, 0xf701,
+/*         0               .       Enter                        */
+   0xf200, 0xf300, 0xf200, 0xf310, 0xf30e, 0xf200, 0xf700, 0xf200,
+};
+
+static u_short dnshift_map[NR_KEYS] __initdata = {
+/*         ins     del     del     F1      F2      F3      F4
+           mark    line    char                                 */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+/* F5      F6      F7      F8      F9      F0      Again   Read */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+/* Save    Abort   Help    Cut     Undo    Grow            ESC  */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf01b,
+/* !       @       #       $       %       ^       &       *    */
+   0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 0xf026, 0xf02a,
+/* (       )       _       +       ~       Back            |<--
+                                           Space                */
+   0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07e, 0xf07f, 0xf200, 0xf200,
+/* Shell   -->|                    Tab     Q       W       E
+   Cmd                                                          */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf009, 0xfb51, 0xfb57, 0xfb45,
+/* R       T       Y       U       I       O       P       {    */
+   0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 0xfb4f, 0xfb50, 0xf07b,
+/* }               Del             7       8       9       +    */
+   0xf07d, 0xf200, 0xf200, 0xf200, 0xf307, 0xf308, 0xf300, 0xf30a,
+/* [<--]   Up      [-->]   Ctrl                    A       S    */
+   0xf200, 0xf600, 0xf200, 0xf702, 0xf200, 0xf200, 0xfb41, 0xfb53,
+/* D       F       G       H       J       K       L       :    */
+   0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a,
+/* "               Return  |               4       5       6    */
+   0xf022, 0xf200, 0xf201, 0xf07c, 0xf200, 0xf304, 0xf305, 0xf306,
+/* -       <--     Next    -->             Rept    Shift        
+                   Window                                       */
+   0xf30b, 0xf601, 0xf200, 0xf602, 0xf200, 0xf200, 0xf700, 0xf200,
+/* Z       X       C       V       B       N       M       <    */
+   0xfb5a, 0xfb58, 0xfb43, 0xfb56, 0xfb42, 0xfb4e, 0xfb4d, 0xf03c,
+/* >       ?       Shift           Pop             1       2    */
+   0xf03e, 0xf03f, 0xf700, 0xf200, 0xf200, 0xf200, 0xf301, 0xf302,
+/* 3               PgUp    Down    PgDn    Alt     Space   Alt  */
+   0xf303, 0xf200, 0xf118, 0xf603, 0xf119, 0xf703, 0xf020, 0xf701,
+/*         0               .       Enter                        */
+   0xf200, 0xf300, 0xf200, 0xf310, 0xf30e, 0xf200, 0xf708, 0xf200,
+};
+
+static u_short dnctrl_map[NR_KEYS] __initdata = {
+/*         ins     del     del     F1      F2      F3      F4
+           mark    line    char                                 */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+/* F5      F6      F7      F8      F9      F0      Again   Read */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+/* Save    Abort   Help    Cut     Undo    Grow            ESC  */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf01b,
+/* !       @       #       $       %       ^       &       *    */
+   0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, 0xf07f,
+/* (       )       _       +       ~       Back            |<--
+                                           Space                */
+   0xf200, 0xf200, 0xf01f, 0xf200, 0xf01c, 0xf200, 0xf200, 0xf200,
+/* Shell   -->|                    Tab     Q       W       E
+   Cmd                                                          */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf009, 0xf011, 0xf017, 0xf005,
+/* R       T       Y       U       I       O       P       {    */
+   0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 0xf00f, 0xf010, 0xf01b,
+/* }               Del             7       8       9       +    */
+   0xf01d, 0xf200, 0xf200, 0xf200, 0xf307, 0xf308, 0xf300, 0xf30a,
+/* [<--]   Up      [-->]   Ctrl                    A       S    */
+   0xf200, 0xf600, 0xf200, 0xf702, 0xf200, 0xf200, 0xfb01, 0xfb53,
+/* D       F       G       H       J       K       L       :    */
+   0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200,
+/* "               Return  |               4       5       6    */
+   0xf200, 0xf200, 0xf201, 0xf01c, 0xf200, 0xf304, 0xf305, 0xf306,
+/* -       <--     Next    -->             Rept    Shift        
+                   Window                                       */
+   0xf30b, 0xf601, 0xf200, 0xf602, 0xf200, 0xf200, 0xf704, 0xf200,
+/* Z       X       C       V       B       N       M       <    */
+   0xf01a, 0xf018, 0xf003, 0xf016, 0xf002, 0xf00e, 0xf01d, 0xf03c,
+/* >       ?       Shift           Pop             1       2    */
+   0xf03e, 0xf03f, 0xf705, 0xf200, 0xf200, 0xf200, 0xf301, 0xf302,
+/* 3               PgUp    Down    PgDn    Alt     Space   Alt  */
+   0xf303, 0xf200, 0xf118, 0xf603, 0xf119, 0xf703, 0xf020, 0xf701,
+/*         0               .       Enter                        */
+   0xf200, 0xf300, 0xf200, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200,
+};
+
+static u_short dnalt_map[NR_KEYS] __initdata = {
+/*         ins     del     del     F1      F2      F3      F4  
+           mark    line    char                                 */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf500, 0xf501, 0xf502, 0xf503,
+/* F5      F6      F7      F8      F9      F0      Again   Read */
+   0xf504, 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf200, 0xf200,
+/* Edit    Exit    Hold    Copy    Paste   Grow            ESC  */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf01b,
+/* 1       2       3       4       5       6       7       8    */
+   0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 0xf837, 0xf838,
+/* 9       0       -       =       `       Back            |<--
+                                           Space                */
+   0xf839, 0xf830, 0xf82d, 0xf83d, 0xf860, 0xf87f, 0xf200, 0xf200,
+/* Shell   -->|                    Tab     q       w       e
+   Cmd                                                          */
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf809, 0xf871, 0xf877, 0xf865,
+/* r       t       y       u       i       o       p       [    */
+   0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 0xf86f, 0xf870, 0xf85b,
+/* ]               Del             7       8       9       +    */
+   0xf05d, 0xf200, 0xf200, 0xf200, 0xf307, 0xf308, 0xf300, 0xf30a,
+/* [<--]   Up      [-->]   Ctrl                    a       s    */
+   0xf200, 0xf600, 0xf200, 0xf702, 0xf200, 0xf200, 0xf861, 0xf873,
+/* d       f       g       h       j       k       l       ;    */
+   0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf03b,
+/* '               Return  \               4       5       6    */
+   0xf027, 0xf200, 0xf201, 0xf05c, 0xf200, 0xf304, 0xf305, 0xf306,
+/* -       <--     Next    -->             Rept    Shift        
+                   Window                                       */
+   0xf30b, 0xf601, 0xf200, 0xf602, 0xf200, 0xf200, 0xf704, 0xf200,
+/* z       x       c       v       b       n       m       ,    */
+   0xf87a, 0xf878, 0xf863, 0xf876, 0xf862, 0xf86e, 0xf86d, 0xf82c,
+/* .       /       Shift           Pop             1       2    */
+   0xf82e, 0xf82f, 0xf705, 0xf200, 0xf200, 0xf200, 0xf301, 0xf302,
+/* 3               PgUp    Down    PgDn    Alt     Space   Alt  */
+   0xf303, 0xf200, 0xf118, 0xf603, 0xf119, 0xf703, 0xf820, 0xf701,
+/*         0               .       Enter                        */
+   0xf200, 0xf300, 0xf200, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200,
+};
+
+static u_short dnaltgr_map[NR_KEYS] __initdata = {
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
+};
+
+static u_short dnshift_ctrl_map[NR_KEYS] __initdata = {
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
+};
+
+static u_short dnctrl_alt_map[NR_KEYS] __initdata = {
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+   0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
+};
+
+#if 0
+static void debug_keyb_timer_handler(unsigned long ignored) {
+
+	unsigned long flags;
+	u_char *swap;
+	short length,i;
+
+	if((jiffies-debug_buffer_updated) > 100) {
+		save_flags(flags);
+		cli();
+		length=debug_buf_count;		
+		swap=debug_buf;	
+		debug_buf=shadow_buf;
+		shadow_buf=swap;
+		debug_buf_count=0;
+		debug_timer_running=0;
+		restore_flags(flags);
+		for(i=1;length;length--,i++)	
+			printk("%02x%c",*(swap++), (i % 25) ? ' ' : '\n');
+		printk("\n");
+	}
+	else {
+		debug_keyb_timer.expires=jiffies+10;
+		add_timer(&debug_keyb_timer);
+	}
+}
+#endif
+
+static unsigned int mouse_poll(struct file *file, poll_table * wait)
+{
+        poll_wait(&mouse_wait, wait);
+        if (mouse_ready)
+                return POLLIN | POLLRDNORM;
+        return 0;
+}
+
+static ssize_t write_mouse(struct file * file, const char * buffer,
+			   size_t count, loff_t *ppos)
+{
+        return -EINVAL;
+}
+
+static ssize_t read_mouse(struct file * file, char * buffer,
+			  size_t count, loff_t *ppos)
+{
+	int dx,dy,r;
+	unsigned char buttons;
+
+	if (count < 3)
+		return -EINVAL;
+	if ((r = verify_area(VERIFY_WRITE, buffer, count)))
+   		return r;
+	if (!mouse_ready)
+		return -EAGAIN;	
+
+	MSE_UPDATE_OFF();
+	dx=mouse_dx;	
+	dy=mouse_dy;	
+	if (dx < -127)
+		dx = -127;
+	else
+	if (dx > 127)
+   		dx = 127;
+	if (dy < -127)
+   		dy = -127;
+	else
+	if (dy > 127)
+   		dy = 127;
+	buttons=(mouse_buttons & 1 ? 4 : 0) |
+			(mouse_buttons & 2 ? 1 : 0) |
+			(mouse_buttons & 4 ? 2 : 0);
+
+	mouse_dx-=dx;
+	mouse_dy-=dy;
+	MSE_UPDATE_ON();
+
+	if (put_user(buttons | 0x80, buffer++) ||
+	    put_user((char)dx, buffer++) ||
+	    put_user((char)dy, buffer++))
+		return -EINVAL;
+
+	if (count > 3)
+		if (clear_user(buffer, count - 3))
+			return -EFAULT;
+	return count;
+}
+
+static int fasync_mouse(struct file *filp, int on)
+{
+        int retval;
+
+        retval = fasync_helper(filp, on, &mouse_fasyncptr);
+        if (retval < 0)
+                return retval;
+        return 0;
+}
+
+
+static int release_mouse(struct inode * inode, struct file * file)
+{
+        fasync_mouse(file, 0);
+        if (--mouse_active)
+                return 0;
+        MSE_UPDATE_OFF();
+        MOD_DEC_USE_COUNT;
+        return 0;
+}
+
+static int open_mouse(struct inode * inode, struct file * file)
+{
+        if (mouse_active++)
+                return 0;
+        /*
+         *  use VBL to poll mouse deltas
+         */
+
+        mouse_dx = 0;
+        mouse_dy = 0;
+        mouse_buttons = 0;
+        mouse_active = 1;
+        MOD_INC_USE_COUNT;
+        MSE_UPDATE_ON();
+        return 0;
+}
+
+static void dn_keyb_process_key_event(unsigned char scancode) {
+
+	static unsigned char lastscancode;
+	unsigned char prev_scancode=lastscancode;
+	static unsigned int lastkeypress;
+	
+	lastscancode=scancode;
+
+	/*  printk("scan: %02x, lastscan: %02X, prev_scancode: %02X\n",scancode,lastscancode,prev_scancode); */
+
+	if(prev_scancode==APOLLO_KBD_MODE_CHANGE) {
+		kbd_mode=scancode;
+/*		printk("modechange: %d\n",scancode);*/
+	}
+	else if((scancode & (~BREAK_FLAG)) == DNKEY_CAPS) {
+    	/* printk("handle_scancode: %02x\n",DNKEY_CAPS); */
+		handle_scancode(DNKEY_CAPS);
+		/*    printk("handle_scancode: %02x\n",BREAK_FLAG | DNKEY_CAPS); */
+		handle_scancode(BREAK_FLAG | DNKEY_CAPS);
+	}
+	else if( (scancode == DNKEY_REPEAT) && (prev_scancode < 0x7e) &&
+   			!(prev_scancode==DNKEY_CTRL || prev_scancode==DNKEY_LSHIFT ||
+       	   	prev_scancode==DNKEY_RSHIFT || prev_scancode==DNKEY_REPT ||
+       	  	prev_scancode==DNKEY_LALT || prev_scancode==DNKEY_RALT)) {
+        		if(jiffies-lastkeypress > DNKEY_REPEAT_DELAY) {
+			/*    	printk("handle_scancode: %02x\n",prev_scancode); */
+           			handle_scancode(prev_scancode);
+			  	}
+	   			lastscancode=prev_scancode;
+  			}
+  	else {
+	/*    	printk("handle_scancode: %02x\n",scancode);  */
+   			handle_scancode(scancode);
+   			lastkeypress=jiffies;
+  	}
+}
+
+static void dn_keyb_process_mouse_event(unsigned char mouse_data) {
+
+	static short mouse_byte_count=0;
+	static u_char mouse_packet[3];
+	
+	mouse_packet[mouse_byte_count++]=mouse_data;
+
+	if(mouse_byte_count==3) {
+		if(mouse_packet[0]==APOLLO_KBD_MODE_CHANGE) {
+			kbd_mode=mouse_packet[1];
+			mouse_byte_count=0;
+/*			printk("modechange: %d\n",mouse_packet[1]); */
+			if(kbd_mode==APOLLO_KBD_MODE_KEYB)
+				dn_keyb_process_key_event(mouse_packet[2]);
+		}				
+		if((mouse_packet[0] & 0x8f) == 0x80) {
+			if(mouse_update_allowed) {
+				mouse_ready=1;
+				mouse_buttons=(mouse_packet[0] >> 4) & 0x7;
+				mouse_dx+=mouse_packet[1] == 0xff ? 0 : (signed char)mouse_packet[1];
+				mouse_dy+=mouse_packet[2] == 0xff ? 0 : (signed char)mouse_packet[2];
+				wake_up_interruptible(&mouse_wait);
+				if (mouse_dx < -2048)
+              		mouse_dx = -2048;
+          		else
+          		if (mouse_dx >  2048)
+              		mouse_dx =  2048;
+          		if (mouse_dy < -2048)
+              		mouse_dy = -2048;
+          		else
+          		if (mouse_dy >  2048)
+              		mouse_dy =  2048;
+				if (mouse_fasyncptr)
+              		kill_fasync(mouse_fasyncptr, SIGIO);
+			}
+			mouse_byte_count=0;
+/*			printk("mouse: %d, %d, %x\n",mouse_x,mouse_y,buttons); */
+		}
+	}
+}
+
+static void dn_keyb_int(int irq, void *dummy, struct pt_regs *fp) {
+
+	unsigned char data;
+  	unsigned long flags;
+  	int scn2681_ints;
+
+	do {
+		scn2681_ints=sio01.isr_imr & 3;
+		if(scn2681_ints & 2) {
+			data=sio01.rhra_thra;
+#if 0
+			if(debug_buf_count<4096) {
+				debug_buf[debug_buf_count++]=data;
+				debug_buffer_updated=jiffies;	
+				if(!debug_timer_running) {
+					add_timer(&debug_keyb_timer);
+					debug_keyb_timer.expires=jiffies+10;
+					debug_timer_running=1;
+				}
+			}
+			else
+				debug_buf_overrun=1;
+#endif
+			if(sio01.sra_csra & 0x10) {
+				printk("whaa overrun !\n");
+				continue;
+			}
+
+			if(kbd_mode==APOLLO_KBD_MODE_KEYB)
+				dn_keyb_process_key_event(data);
+			else
+				dn_keyb_process_mouse_event(data);
+		}
+	
+		if(scn2681_ints & 1) {
+			save_flags(flags);
+			cli();
+			if(keyb_cmd_write!=keyb_cmd_read) {
+				sio01.rhra_thra=keyb_cmds[keyb_cmd_read++];
+				if(keyb_cmd_read==APOLLO_KEYB_CMD_ENTRIES)
+					keyb_cmd_read=0;
+				keyb_cmd_transmit=1;
+			}
+			else {
+				keyb_cmd_transmit=0;
+				sio01.BRGtest_cra=9;
+			}
+			restore_flags(flags);
+		}
+	} while(scn2681_ints) ;
+}
+
+void write_keyb_cmd(u_short length, u_char *cmd) {
+
+  	unsigned long flags;
+
+	if((keyb_cmd_write==keyb_cmd_read) && keyb_cmd_transmit)
+		return;
+
+	save_flags(flags);
+	cli();
+	for(;length;length--) {
+		keyb_cmds[keyb_cmd_write++]=*(cmd++);
+		if(keyb_cmd_write==keyb_cmd_read)
+			return;
+		if(keyb_cmd_write==APOLLO_KEYB_CMD_ENTRIES)
+			keyb_cmd_write=0;
+	}
+	if(!keyb_cmd_transmit)  {
+ 	   sio01.BRGtest_cra=5;
+	}
+	restore_flags(flags);
+
+}
+
+struct file_operations apollo_mouse_fops = {
+        NULL,           /* mouse_seek */
+        read_mouse,
+        write_mouse,
+        NULL,           /* mouse_readdir */
+        mouse_poll,     /* mouse_poll */
+        NULL,           /* mouse_ioctl */
+        NULL,           /* mouse_mmap */
+        open_mouse,
+        release_mouse,
+        NULL,
+        fasync_mouse,
+};
+
+static struct miscdevice apollo_mouse = {
+        APOLLO_MOUSE_MINOR, "apollomouse", &apollo_mouse_fops
+};
+
+__initfunc(int dn_keyb_init(void)) {
+
+/*  printk("dn_keyb_init\n"); */
+
+  memcpy(plain_map, dnplain_map, sizeof(plain_map));
+  memcpy(shift_map, dnshift_map, sizeof(shift_map));
+  memcpy(altgr_map, dnaltgr_map, sizeof(altgr_map));
+  memcpy(ctrl_map, dnctrl_map, sizeof(ctrl_map));
+  memcpy(shift_ctrl_map, dnshift_ctrl_map, sizeof(shift_ctrl_map));
+  memcpy(alt_map, dnalt_map, sizeof(alt_map));
+  memcpy(ctrl_alt_map, dnctrl_alt_map, sizeof(ctrl_alt_map));
+
+  mouse_dx=0; 
+  mouse_dy=0; 
+  mouse_buttons=0; 
+  mouse_wait=NULL;
+
+  misc_register(&apollo_mouse);
+
+  /* program UpDownMode */
+
+  while(!(sio01.sra_csra & 0x4));
+  sio01.rhra_thra=0xff;
+
+  while(!(sio01.sra_csra & 0x4));
+  sio01.rhra_thra=0x1;
+
+  request_irq(1, dn_keyb_int,0,NULL,NULL);
+  
+  /* enable receive int on DUART */
+  sio01.isr_imr=3;
+
+  return 0;
+
+}
+
+int dn_dummy_kbdrate(struct kbd_repeat *k) {
+
+  printk("dn_dummy_kbdrate\n");
+
+  return 0;
+
+}
diff -u --recursive --new-file v2.1.90/linux/drivers/char/hfmodem/gentbl.c linux/drivers/char/hfmodem/gentbl.c
--- v2.1.90/linux/drivers/char/hfmodem/gentbl.c	Fri Jan 23 18:10:31 1998
+++ linux/drivers/char/hfmodem/gentbl.c	Wed Mar 18 09:50:16 1998
@@ -62,7 +62,7 @@
 	printf("/*\n * This file is automatically generated by %s, DO NOT EDIT!\n*/\n\n",
 	       argv[0]);
 	gensintbl();
-	exit(0);
+	return(0);
 }
 
 /* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.90/linux/drivers/char/istallion.c linux/drivers/char/istallion.c
--- v2.1.90/linux/drivers/char/istallion.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/char/istallion.c	Mon Mar 23 13:49:29 1998
@@ -170,7 +170,7 @@
  */
 static char	*stli_drvtitle = "Stallion Intelligent Multiport Serial Driver";
 static char	*stli_drvname = "istallion";
-static char	*stli_drvversion = "5.4.3";
+static char	*stli_drvversion = "5.4.5";
 static char	*stli_serialname = "ttyE";
 static char	*stli_calloutname = "cue";
 
diff -u --recursive --new-file v2.1.90/linux/drivers/char/macmouse.c linux/drivers/char/macmouse.c
--- v2.1.90/linux/drivers/char/macmouse.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/macmouse.c	Sat Mar 21 11:06:17 1998
@@ -0,0 +1,311 @@
+/*
+ * Macintosh ADB Mouse driver for Linux
+ *
+ * 27 Oct 1997 Michael Schmitz
+ *
+ * Apple mouse protocol according to:
+ *
+ * Device code shamelessly stolen from:
+ */
+/*
+ * Atari Mouse Driver for Linux
+ * by Robert de Vries (robert@and.nl) 19Jul93
+ *
+ * 16 Nov 1994 Andreas Schwab
+ * Compatibility with busmouse
+ * Support for three button mouse (shamelessly stolen from MiNT)
+ * third button wired to one of the joystick directions on joystick 1
+ *
+ * 1996/02/11 Andreas Schwab
+ * Module support
+ * Allow multiple open's
+ */
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+
+#include <asm/setup.h>
+#include <asm/mac_mouse.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+static struct mouse_status mouse;
+static int mac_mouse_x_threshold = 2, mac_mouse_y_threshold = 2;
+static int mac_mouse_buttons = 0;
+
+extern void (*mac_mouse_interrupt_hook) (char *, int);
+extern int mac_emulate_button2;
+extern int mac_emulate_button3;
+
+extern int console_loglevel;
+
+/*
+ *	XXX: need to figure out what ADB mouse packets mean ... 
+ *	This is the stuff stolen from the Atari driver ...
+ */
+static void mac_mouse_interrupt(char *buf, int nb)
+{
+    static int buttons = 7;	/* all mouse buttons _up_ !! */
+
+  /*
+    Handler 1 -- 100cpi original Apple mouse protocol.
+    Handler 2 -- 200cpi original Apple mouse protocol.
+
+    For Apple's standard one-button mouse protocol the data array will
+    contain the following values:
+
+                BITS    COMMENTS
+    data[0] = 0000 0000 ADB packet identifer.
+    data[1] = ???? ???? (?)
+    data[2] = ???? ??00 Bits 0-1 should be zero for a mouse device.
+    data[3] = bxxx xxxx First button and x-axis motion.
+    data[4] = byyy yyyy Second button and y-axis motion.
+
+    NOTE: data[0] is confirmed by the parent function and need not be
+    checked here.
+  */
+
+  /*
+    Handler 4 -- Apple Extended mouse protocol.
+
+    For Apple's 3-button mouse protocol the data array will contain the
+    following values:
+
+		BITS    COMMENTS
+    data[0] = 0000 0000 ADB packet identifer.
+    data[1] = 0100 0000 Extended protocol register.
+	      Bits 6-7 are the device id, which should be 1.
+	      Bits 4-5 are resolution which is in "units/inch".
+	      The Logitech MouseMan returns these bits clear but it has
+	      200/300cpi resolution.
+	      Bits 0-3 are unique vendor id.
+    data[2] = 0011 1100 Bits 0-1 should be zero for a mouse device.
+	      Bits 2-3 should be 8 + 4.
+		      Bits 4-7 should be 3 for a mouse device.
+    data[3] = bxxx xxxx Left button and x-axis motion.
+    data[4] = byyy yyyy Second button and y-axis motion.
+    data[5] = byyy bxxx Third button and fourth button.  
+    	      Y is additiona. high bits of y-axis motion.  
+    	      X is additional high bits of x-axis motion.
+
+    NOTE: data[0] and data[2] are confirmed by the parent function and
+    need not be checked here.
+  */
+
+    /*
+     * 'buttons' here means 'button down' states!
+     * Button 1 (left)  : bit 2, busmouse button 3
+     * Button 2 (right) : bit 0, busmouse button 1
+     * Button 3 (middle): bit 1, busmouse button 2
+     */
+
+    /* x/y and buttons swapped */
+    
+    if (buf[0] ==  0)	{				/* real packet : use buttons? */
+#ifdef DEBUG_ADBMOUSE
+	if (console_loglevel >= 8)
+	    printk("mac_mouse: real data; "); 
+#endif
+	/* button 1 (left, bit 2) : always significant ! */
+	buttons = (buttons&3) | (buf[3] & 0x80 ? 4 : 0); /* 1+2 unchanged */
+	/* button 2 (right, bit 0) present ? */
+	if ( !mac_emulate_button2 ) 
+	    buttons = (buttons&6) | (buf[4] & 0x80 ? 1 : 0); /* 2+3 unchanged */
+	/* button 2 (middle) present? */
+	/* data valid only if extended mouse format ! (buf[3] = 0 else)*/
+	if ( !mac_emulate_button3 && buf[1]&0x40 )
+	    buttons = (buttons&5) | (buf[5] & 0x80 ? 2 : 0); /* 1+3 unchanged */
+    } else {					/* fake packet : use 2+3 */
+#ifdef DEBUG_ADBMOUSE
+	if (console_loglevel >= 8)
+	    printk("mac_mouse: fake data; "); 
+#endif
+	/* we only see state changes here, but the fake driver takes care
+	 * to preserve state... button 1 state must stay unchanged! */
+	buttons = (buttons&4) | ((buf[4] & 0x80 ? 1 : 0) | (buf[5] & 0x80 ? 2 : 0));
+    }
+
+    add_mouse_randomness(((~buttons & 7) << 16) + ((buf[2]&0x7f) << 8) + (buf[1]&0x7f));
+    mouse.buttons = buttons & 7;
+    mouse.dx += ((buf[4]&0x7f) < 64 ? (buf[4]&0x7f) : (buf[4]&0x7f)-128 );
+    mouse.dy -= ((buf[3]&0x7f) < 64 ? (buf[3]&0x7f) : (buf[3]&0x7f)-128 );
+
+#ifdef DEBUG_ADBMOUSE
+    if (console_loglevel >= 8)
+        printk(" %X %X %X buttons %x dx %d dy %d \n", 
+               buf[3], buf[4], buf[5], mouse.buttons, mouse.dx, mouse.dy);
+#endif
+ 
+    mouse.ready = 1;
+    wake_up_interruptible(&mouse.wait);
+    if (mouse.fasyncptr)
+	kill_fasync(mouse.fasyncptr, SIGIO);
+
+}
+
+static int fasync_mouse(struct file *filp, int on)
+{
+	int retval;
+
+	retval = fasync_helper(filp, on, &mouse.fasyncptr);
+	if (retval < 0)
+		return retval;
+	return 0;
+}
+
+static int release_mouse(struct inode *inode, struct file *file)
+{
+    fasync_mouse(file, 0);
+    if (--mouse.active)
+      return 0;
+
+    mac_mouse_interrupt_hook = NULL;
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+
+static int open_mouse(struct inode *inode, struct file *file)
+{
+    if (mouse.active++)
+	return 0;
+	
+    mouse.ready = 0;
+
+    mouse.dx = mouse.dy = 0;
+    mac_mouse_buttons = 0;
+    MOD_INC_USE_COUNT;
+    mac_mouse_interrupt_hook = mac_mouse_interrupt;
+    return 0;
+}
+
+static ssize_t write_mouse(struct file *file, const char *buffer,
+			   size_t count, loff_t *ppos)
+{
+    return -EINVAL;
+}
+
+static ssize_t read_mouse(struct file *file, char *buffer, size_t count,
+			  loff_t *ppos)
+{
+    int dx, dy, buttons;
+
+    if (count < 3)
+	return -EINVAL;
+    if (!mouse.ready)
+	return -EAGAIN;
+    dx = mouse.dx;
+    dy = mouse.dy;
+    buttons = mouse.buttons;
+    if (dx > 127)
+      dx = 127;
+    else if (dx < -128)
+      dx = -128;
+    if (dy > 127)
+      dy = 127;
+    else if (dy < -128)
+      dy = -128;
+    mouse.dx -= dx;
+    mouse.dy -= dy;
+    if (mouse.dx == 0 && mouse.dy == 0)
+      mouse.ready = 0;
+    if (put_user(buttons | 0x80, buffer++) ||
+	put_user((char) dx, buffer++) ||
+	put_user((char) dy, buffer++))
+      return -EFAULT;
+    if (count > 3)
+      if (clear_user(buffer, count - 3))
+	return -EFAULT;
+    return count;
+}
+
+static unsigned int mouse_poll(struct file *file, poll_table *wait)
+{
+	poll_wait(&mouse.wait, wait);
+	if (mouse.ready)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+struct file_operations mac_mouse_fops = {
+    NULL,		/* mouse_seek */
+    read_mouse,
+    write_mouse,
+    NULL,		/* mouse_readdir */
+    mouse_poll,
+    NULL,		/* mouse_ioctl */
+    NULL,		/* mouse_mmap */
+    open_mouse,
+    release_mouse,
+    NULL,
+    fasync_mouse,
+};
+
+#define ADB_MOUSE_MINOR	10
+
+static struct miscdevice mac_mouse = {
+    ADB_MOUSE_MINOR, "adbmouse", &mac_mouse_fops
+};
+
+__initfunc(int mac_mouse_init(void))
+{
+    mouse.active = 0;
+    mouse.ready = 0;
+    mouse.wait = NULL;
+
+    if (!MACH_IS_MAC)
+	return -ENODEV;
+
+    printk(KERN_INFO "Macintosh ADB mouse installed.\n");
+    misc_register(&mac_mouse);
+    return 0;
+}
+
+
+#define	MIN_THRESHOLD 1
+#define	MAX_THRESHOLD 20	/* more seems not reasonable... */
+
+__initfunc(void mac_mouse_setup(char *str, int *ints))
+{
+    if (ints[0] < 1) {
+	printk( "mac_mouse_setup: no arguments!\n" );
+	return;
+    }
+    else if (ints[0] > 2) {
+	printk( "mac_mouse_setup: too many arguments\n" );
+    }
+
+    if (ints[1] < MIN_THRESHOLD || ints[1] > MAX_THRESHOLD)
+	printk( "mac_mouse_setup: bad threshold value (ignored)\n" );
+    else {
+	mac_mouse_x_threshold = ints[1];
+	mac_mouse_y_threshold = ints[1];
+	if (ints[0] > 1) {
+	    if (ints[2] < MIN_THRESHOLD || ints[2] > MAX_THRESHOLD)
+		printk("mac_mouse_setup: bad threshold value (ignored)\n" );
+	    else
+		mac_mouse_y_threshold = ints[2];
+	}
+    }
+	
+}
+
+#ifdef MODULE
+#include <asm/setup.h>
+
+int init_module(void)
+{
+    return mac_mouse_init();
+}
+
+void cleanup_module(void)
+{
+    misc_deregister(&mac_mouse);
+}
+#endif
diff -u --recursive --new-file v2.1.90/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v2.1.90/linux/drivers/char/mem.c	Tue Mar 10 10:03:31 1998
+++ linux/drivers/char/mem.c	Sat Mar 21 11:04:03 1998
@@ -38,6 +38,9 @@
 #ifdef CONFIG_VIDEO_DEV
 extern int videodev_init(void);
 #endif
+#if defined(CONFIG_FB)
+extern void fbmem_init( void );
+#endif
 
 static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp,
 			    const char * buf, size_t count, loff_t *ppos)
@@ -522,6 +525,9 @@
 	if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
 		printk("unable to get major %d for memory devs\n", MEM_MAJOR);
 	rand_initialize();
+#if defined (CONFIG_FB)
+	fbmem_init();
+#endif
 	tty_init();
 #ifdef CONFIG_PRINTER
 	lp_init();
diff -u --recursive --new-file v2.1.90/linux/drivers/char/stallion.c linux/drivers/char/stallion.c
--- v2.1.90/linux/drivers/char/stallion.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/char/stallion.c	Mon Mar 23 13:49:29 1998
@@ -143,7 +143,7 @@
  */
 static char	*stl_drvtitle = "Stallion Multiport Serial Driver";
 static char	*stl_drvname = "stallion";
-static char	*stl_drvversion = "5.4.3";
+static char	*stl_drvversion = "5.4.5";
 static char	*stl_serialname = "ttyE";
 static char	*stl_calloutname = "cue";
 
@@ -278,6 +278,7 @@
 #define	EIO_INTRPEND	0x08
 #define	EIO_INTEDGE	0x00
 #define	EIO_INTLEVEL	0x08
+#define	EIO_0WS		0x10
 
 #define	ECH_ID		0xa0
 #define	ECH_IDBITMASK	0xe0
@@ -2153,6 +2154,10 @@
 	brdp->ioctrl = brdp->ioaddr1 + 1;
 	brdp->iostatus = brdp->ioaddr1 + 2;
 
+	status = inb(brdp->iostatus);
+	if ((status & EIO_IDBITMASK) == EIO_MK3)
+		brdp->ioctrl++;
+
 /*
  *	Handle board specific stuff now. The real difference is PCI
  *	or not PCI.
@@ -2171,7 +2176,7 @@
 				brdp->irq, brdp->brdnr);
 			return(-EINVAL);
 		}
-		outb((stl_vecmap[brdp->irq] |
+		outb((stl_vecmap[brdp->irq] | EIO_0WS |
 			((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)),
 			brdp->ioctrl);
 	}
@@ -2194,7 +2199,6 @@
 	brdp->clk = CD1400_CLK;
 	brdp->isr = stl_eiointr;
 
-	status = inb(brdp->iostatus);
 	switch (status & EIO_IDBITMASK) {
 	case EIO_8PORTM:
 		brdp->clk = CD1400_CLK8M;
@@ -2220,7 +2224,6 @@
 		default:
 			return(-ENODEV);
 		}
-		brdp->ioctrl++;
 		break;
 	default:
 		return(-ENODEV);
diff -u --recursive --new-file v2.1.90/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.90/linux/drivers/char/tty_io.c	Tue Mar 17 22:18:14 1998
+++ linux/drivers/char/tty_io.c	Mon Mar 23 10:13:52 1998
@@ -66,6 +66,7 @@
 #include <linux/interrupt.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
+#include <linux/file.h>
 #include <linux/console.h>
 #include <linux/timer.h>
 #include <linux/ctype.h>
@@ -1734,8 +1735,8 @@
 		    ((session > 0) && (p->session == session)))
 			send_sig(SIGKILL, p, 1);
 		else if (p->files) {
-			for (i=0; i < NR_OPEN; i++) {
-				filp = p->files->fd[i];
+			for (i=0; i < p->files->max_fds; i++) {
+				filp = fcheck_task(p, i);
 				if (filp && (filp->f_op == &tty_fops) &&
 				    (filp->private_data == tty)) {
 					send_sig(SIGKILL, p, 1);
diff -u --recursive --new-file v2.1.90/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
--- v2.1.90/linux/drivers/misc/parport_pc.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/misc/parport_pc.c	Sun Mar 22 14:39:24 1998
@@ -134,12 +134,12 @@
 
 void parport_pc_write_fifo(struct parport *p, unsigned char v)
 {
-	/* FIXME */
+	outb (v, p->base+CONFIGA);
 }
 
 unsigned char parport_pc_read_fifo(struct parport *p)
 {
-	return 0; /* FIXME */
+	return inb (p->base+CONFIGA);
 }
 
 void parport_pc_disable_irq(struct parport *p)
@@ -186,22 +186,34 @@
 
 size_t parport_pc_epp_read_block(struct parport *p, void *buf, size_t length)
 {
-	return 0; /* FIXME */
+	size_t got = 0;
+	for (; got < length; got++) {
+		*((char*)buf)++ = inb (p->base+EPPREG);
+		if (inb (p->base+STATUS) & 0x01)
+			break;
+	}
+	return got;
 }
 
 size_t parport_pc_epp_write_block(struct parport *p, void *buf, size_t length)
 {
-	return 0; /* FIXME */
+	size_t written = 0;
+	for (; written < length; written++) {
+		outb (*((char*)buf)++, p->base+EPPREG);
+		if (inb (p->base+STATUS) & 0x01)
+			break;
+	}
+	return written;
 }
 
 int parport_pc_ecp_read_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle)
 {
-	return 0; /* FIXME */
+	return -ENOSYS; /* FIXME */
 }
 
 int parport_pc_ecp_write_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle)
 {
-	return 0; /* FIXME */
+	return -ENOSYS; /* FIXME */
 }
 
 int parport_pc_examine_irq(struct parport *p)
diff -u --recursive --new-file v2.1.90/linux/drivers/misc/parport_share.c linux/drivers/misc/parport_share.c
--- v2.1.90/linux/drivers/misc/parport_share.c	Tue Mar 17 22:18:14 1998
+++ linux/drivers/misc/parport_share.c	Thu Mar 19 14:51:01 1998
@@ -46,7 +46,9 @@
 {
 #ifdef CONFIG_KMOD
 	if (portlist == NULL) {
+#if defined(CONFIG_PARPORT_PC_MODULE) || defined(CONFIG_PARPORT_AX_MODULE) || defined(CONFIG_PARPORT_ARC_MODULE)
 		request_module("parport_lowlevel");
+#endif /* CONFIG_PARPORT_LOWLEVEL_MODULE */
 #ifdef CONFIG_PNP_PARPORT_MODULE
 		request_module("parport_probe");
 #endif /* CONFIG_PNP_PARPORT_MODULE */
diff -u --recursive --new-file v2.1.90/linux/drivers/net/3c523.c linux/drivers/net/3c523.c
--- v2.1.90/linux/drivers/net/3c523.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/net/3c523.c	Mon Mar 23 10:10:34 1998
@@ -383,8 +383,8 @@
 }
 
 /*****************************************************************/
-__initfunc(static int
-elmc_getinfo( char* buf, int slot, void* d )) {
+static int
+elmc_getinfo( char* buf, int slot, void* d ) {
 	int len = 0;
 	struct device* dev = (struct device*) d;
 	int i;
diff -u --recursive --new-file v2.1.90/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v2.1.90/linux/drivers/net/3c59x.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/net/3c59x.c	Wed Mar 18 09:50:17 1998
@@ -83,7 +83,7 @@
 #if LINUX_VERSION_CODE < 0x10300
 #define RUN_AT(x) (x)			/* What to put in timer->expires.  */
 #define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
-#if defined(__alpha)
+#if defined(__alpha__)
 #error "The Alpha architecture is only support with kernel version 2.0."
 #endif
 #define virt_to_bus(addr)  ((unsigned long)addr)
diff -u --recursive --new-file v2.1.90/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.90/linux/drivers/net/Makefile	Tue Mar 17 22:18:14 1998
+++ linux/drivers/net/Makefile	Sat Mar 21 10:56:27 1998
@@ -739,7 +739,21 @@
 endif
 
 ifeq ($(CONFIG_VENDOR_SANGOMA),y)
-  M_OBJS += sdladrv.o
+  L_OBJS += sdladrv.o
+  L_OBJS += sdlamain.o
+  ifeq ($(CONFIG_WANPIPE_X25),y)
+    L_OBJS += sdla_x25.o
+  endif
+  ifeq ($(CONFIG_WANPIPE_FR),y)
+    L_OBJS += sdla_fr.o
+  endif
+  ifeq ($(CONFIG_WANPIPE_PPP),y)
+    L_OBJS += sdla_ppp.o
+  endif
+endif
+
+ifeq ($(CONFIG_VENDOR_SANGOMA),m)
+  MX_OBJS += sdladrv.o
   M_OBJS += wanpipe.o
   WANPIPE_OBJS = sdlamain.o
   ifeq ($(CONFIG_WANPIPE_X25),y)
diff -u --recursive --new-file v2.1.90/linux/drivers/net/hamradio/soundmodem/gentbl.c linux/drivers/net/hamradio/soundmodem/gentbl.c
--- v2.1.90/linux/drivers/net/hamradio/soundmodem/gentbl.c	Fri Jan 30 11:28:07 1998
+++ linux/drivers/net/hamradio/soundmodem/gentbl.c	Wed Mar 18 09:50:17 1998
@@ -631,7 +631,7 @@
 
 /* -------------------------------------------------------------------- */
 
-void main(int argc, char *argv[])
+int main(int argc, char *argv[])
 {
 	FILE *f;
 	
@@ -681,7 +681,7 @@
 	gentbl_costab(f, 6);
 	gentbl_afsk2400(f, 7372800);
 	fclose(f);
-	exit(0);
+	return(0);
 }
 
 
diff -u --recursive --new-file v2.1.90/linux/drivers/net/sdla_fr.c linux/drivers/net/sdla_fr.c
--- v2.1.90/linux/drivers/net/sdla_fr.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/sdla_fr.c	Sat Mar 21 10:56:27 1998
@@ -1,4 +1,4 @@
-/****************************************************************************
+/*****************************************************************************
 * sdla_fr.c	WANPIPE(tm) Multiprotocol WAN Link Driver. Frame relay module.
 *
 * Author(s):	Gene Kozin	
@@ -83,85 +83,83 @@
 * Jan 02, 1997	Gene Kozin	Initial version.
 *****************************************************************************/
 
-#if	!defined(__KERNEL__) || !defined(MODULE)
-#error	This code MUST be compiled as a kernel module!
-#endif
-
 #include <linux/config.h>	/* OS configuration options */
 #include <linux/kernel.h>	/* printk(), and other useful stuff */
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
 #include <linux/string.h>	/* inline memset(), etc. */
 #include <linux/malloc.h>	/* kmalloc(), kfree() */
-#include <linux/init.h>		/* __initfunc */
 #include <linux/wanrouter.h>	/* WAN router definitions */
 #include <linux/wanpipe.h>	/* WANPIPE common user API definitions */
 #include <linux/if_arp.h>	/* ARPHRD_* defines */
 #include <asm/byteorder.h>	/* htons(), etc. */
 #include <asm/io.h>		/* for inb(), outb(), etc. */
-#include <asm/uaccess.h>
-#include <linux/time.h>	 	/* for do_gettimeofday */	
-
+#include <linux/time.h>		/* for do_gettimeofday */
 #define	_GNUC_
 #include <linux/sdla_fr.h>	/* frame relay firmware API definitions */
-/****** Defines & Macros ****************************************************/
+#include <asm/uaccess.h>
 
-#define	MAX_CMD_RETRY	10		/* max number of firmware retries */
+/****** Defines & Macros ****************************************************/
 
-#define	FR_HEADER_LEN	8		/* max encapsulation header size */
-#define	FR_CHANNEL_MTU	1500		/* unfragmented logical channel MTU */
+#define	MAX_CMD_RETRY	10	/* max number of firmware retries */
+#define	FR_HEADER_LEN	8	/* max encapsulation header size */
+#define	FR_CHANNEL_MTU	1500	/* unfragmented logical channel MTU */
 
 /* Q.922 frame types */
-#define	Q922_UI		0x03		/* Unnumbered Info frame */
-#define	Q922_XID	0xAF		/* ??? */
+
+#define	Q922_UI		0x03	/* Unnumbered Info frame */
+#define	Q922_XID	0xAF	/* ??? */
 
 /* DLCI configured or not */
+
 #define DLCI_NOT_CONFIGURED	0x00
 #define DLCI_CONFIG_PENDING	0x01
 #define DLCI_CONFIGURED		0x02
 
 /* CIR enabled or not */
+
 #define CIR_ENABLED	0x00
 #define CIR_DISABLED	0x01
 
 /* Interrupt mode for DLCI = 0 */
+
 #define BUFFER_INTR_MODE	0x00
 #define DLCI_LIST_INTR_MODE	0x01
 
 /* Transmit Interrupt Status */
+
 #define DISABLED 		0x00
 #define WAITING_TO_BE_ENABLED	0x01
 
 /* For handle_IPXWAN() */
+
 #define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b)))
- 
+
 /****** Data Structures *****************************************************/
 
 /* This is an extention of the 'struct device' we create for each network
  * interface to keep the rest of channel-specific data.
  */
-typedef struct fr_channel
-{
-	char name[WAN_IFNAME_SZ+1];	/* interface name, ASCIIZ */
-	unsigned dlci_configured  ;	/* check whether configured or not */
-	unsigned cir_status;		/* check whether CIR enabled or not */
-	unsigned dlci;			/* logical channel number */
-	unsigned cir;			/* committed information rate */
-	unsigned bc;			/* committed burst size */
-	unsigned be;			/* excess burst size */
-	unsigned mc;			/* multicast support on or off */
-	unsigned tx_int_status;		/* Transmit Interrupt Status */	
+typedef struct fr_channel {
+	char name[WAN_IFNAME_SZ + 1];	/* interface name, ASCIIZ */
+	unsigned dlci_configured;	/* check whether configured or not */
+	unsigned cir_status;	/* check whether CIR enabled or not */
+	unsigned dlci;		/* logical channel number */
+	unsigned cir;		/* committed information rate */
+	unsigned bc;		/* committed burst size */
+	unsigned be;		/* excess burst size */
+	unsigned mc;		/* multicast support on or off */
+	unsigned tx_int_status;	/* Transmit Interrupt Status */
 	unsigned short pkt_length;	/* Packet Length */
-	unsigned long router_start_time;/* Router start time in seconds */
+	unsigned long router_start_time;	/* Router start time in seconds */
 	unsigned long tick_counter;	/* counter for transmit time out */
 	char dev_pending_devtint;	/* interface pending dev_tint() */
-	char state;			/* channel state */
-	void* dlci_int_interface;	/* pointer to the DLCI Interface */ 
-	unsigned long IB_addr;		/* physical address of Interface Byte */
+	char state;		/* channel state */
+	void *dlci_int_interface;	/* pointer to the DLCI Interface */
+	unsigned long IB_addr;	/* physical address of Interface Byte */
 	unsigned long state_tick;	/* time of the last state change */
-	sdla_t* card;			/* -> owner */
-	struct enet_statistics ifstats;	/* interface statistics */
-	
+	sdla_t *card;		/* -> owner */
+	struct enet_statistics ifstats;		/* interface statistics */
 	unsigned long if_send_entry;
 	unsigned long if_send_skb_null;
 	unsigned long if_send_broadcast;
@@ -177,14 +175,12 @@
 	unsigned long if_send_no_bfrs;
 	unsigned long if_send_adptr_bfrs_full;
 	unsigned long if_send_bfrs_passed_to_adptr;
-
 	unsigned long rx_intr_no_socket;
 	unsigned long rx_intr_dev_not_started;
 	unsigned long rx_intr_FPIPE_request;
 	unsigned long rx_intr_DRVSTATS_request;
 	unsigned long rx_intr_bfr_not_passed_to_stack;
 	unsigned long rx_intr_bfr_passed_to_stack;
-
 	unsigned long UDP_FPIPE_mgmt_kmalloc_err;
 	unsigned long UDP_FPIPE_mgmt_direction_err;
 	unsigned long UDP_FPIPE_mgmt_adptr_type_err;
@@ -206,37 +202,32 @@
 	unsigned long router_up_time;
 } fr_channel_t;
 
-typedef struct dlci_status
-{
-	unsigned short dlci	PACKED;
-	unsigned char state	PACKED;
+typedef struct dlci_status {
+	unsigned short dlci PACKED;
+	unsigned char state PACKED;
 } dlci_status_t;
 
-typedef struct dlci_IB_mapping
-{
-	unsigned short dlci		PACKED;
-	unsigned long  addr_value	PACKED;
+typedef struct dlci_IB_mapping {
+	unsigned short dlci PACKED;
+	unsigned long addr_value PACKED;
 } dlci_IB_mapping_t;
 
 /* This structure is used for DLCI list Tx interrupt mode.  It is used to
    enable interrupt bit and set the packet length for transmission
  */
-typedef struct fr_dlci_interface 
-{
-	unsigned char gen_interrupt	PACKED;
-	unsigned short packet_length	PACKED;
-	unsigned char reserved		PACKED;
-} fr_dlci_interface_t; 
+
+typedef struct fr_dlci_interface {
+	unsigned char gen_interrupt PACKED;
+	unsigned short packet_length PACKED;
+	unsigned char reserved PACKED;
+} fr_dlci_interface_t;
 
 static unsigned short num_frames;
 static unsigned long curr_trace_addr;
 static unsigned long start_trace_addr;
 static unsigned short available_buffer_space;
-static char TracingEnabled;
-
-/* variable for keeping track of enabling/disabling FT1 monitor status */
+static char TracingEnabled; /* variable for keeping track of enabling/disabling FT1 monitor status */
 static int rCount = 0;
-
 extern void disable_irq(unsigned int);
 extern void enable_irq(unsigned int);
 
@@ -248,73 +239,63 @@
 /****** Function Prototypes *************************************************/
 
 /* WAN link driver entry points. These are called by the WAN router module. */
-static int update (wan_device_t* wandev);
-static int new_if (wan_device_t* wandev, struct device* dev,
-	wanif_conf_t* conf);
-static int del_if (wan_device_t* wandev, struct device* dev);
-
+static int update(wan_device_t * wandev);
+static int new_if(wan_device_t * wandev, struct device *dev,
+		  wanif_conf_t * conf);
+static int del_if(wan_device_t * wandev, struct device *dev);
 /* WANPIPE-specific entry points */
-static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data);
-
+static int wpf_exec(struct sdla *card, void *u_cmd, void *u_data);
 /* Network device interface */
-static int if_init   (struct device* dev);
-static int if_open   (struct device* dev);
-static int if_close  (struct device* dev);
-static int if_header (struct sk_buff* skb, struct device* dev,
-	unsigned short type, void* daddr, void* saddr, unsigned len);
-static int if_rebuild_hdr (struct sk_buff* skb);
-static int if_send (struct sk_buff* skb, struct device* dev);
-static struct enet_statistics* if_stats (struct device* dev);
-
+static int if_init(struct device *dev);
+static int if_open(struct device *dev);
+static int if_close(struct device *dev);
+static int if_header(struct sk_buff *skb, struct device *dev,
+	    unsigned short type, void *daddr, void *saddr, unsigned len);
+static int if_rebuild_hdr(struct sk_buff *skb);
+static int if_send(struct sk_buff *skb, struct device *dev);
+static struct enet_statistics *if_stats(struct device *dev);
 /* Interrupt handlers */
-static void fr502_isr (sdla_t* card);
-static void fr508_isr (sdla_t* card);
-static void fr502_rx_intr (sdla_t* card);
-static void fr508_rx_intr (sdla_t* card);
-static void tx_intr (sdla_t* card);
-static void spur_intr (sdla_t* card);
-
+static void fr502_isr(sdla_t * card);
+static void fr508_isr(sdla_t * card);
+static void fr502_rx_intr(sdla_t * card);
+static void fr508_rx_intr(sdla_t * card);
+static void tx_intr(sdla_t * card);
+static void spur_intr(sdla_t * card);
 /* Background polling routines */
-static void wpf_poll (sdla_t* card);
-
+static void wpf_poll(sdla_t * card);
 /* Frame relay firmware interface functions */
-static int fr_read_version (sdla_t* card, char* str);
-static int fr_configure (sdla_t* card, fr_conf_t *conf);
-static int fr_dlci_configure (sdla_t* card, fr_dlc_conf_t *conf, unsigned dlci);
-static int fr_set_intr_mode (sdla_t* card, unsigned mode, unsigned mtu);
-static int fr_comm_enable (sdla_t* card);
-static int fr_comm_disable (sdla_t* card);
-static int fr_get_err_stats (sdla_t* card);
-static int fr_get_stats (sdla_t* card);
-static int fr_add_dlci (sdla_t* card, int dlci, int num);
-static int fr_activate_dlci (sdla_t* card, int dlci, int num);
-static int fr_issue_isf (sdla_t* card, int isf);
-static int fr502_send (sdla_t* card, int dlci, int attr, int len, void *buf);
-static int fr508_send (sdla_t* card, int dlci, int attr, int len, void *buf);
-
+static int fr_read_version(sdla_t * card, char *str);
+static int fr_configure(sdla_t * card, fr_conf_t * conf);
+static int fr_dlci_configure(sdla_t * card, fr_dlc_conf_t * conf, unsigned dlci);
+static int fr_set_intr_mode(sdla_t * card, unsigned mode, unsigned mtu);
+static int fr_comm_enable(sdla_t * card);
+static int fr_comm_disable(sdla_t * card);
+static int fr_get_err_stats(sdla_t * card);
+static int fr_get_stats(sdla_t * card);
+static int fr_add_dlci(sdla_t * card, int dlci, int num);
+static int fr_activate_dlci(sdla_t * card, int dlci, int num);
+static int fr_issue_isf(sdla_t * card, int isf);
+static int fr502_send(sdla_t * card, int dlci, int attr, int len, void *buf);
+static int fr508_send(sdla_t * card, int dlci, int attr, int len, void *buf);
 /* Firmware asynchronous event handlers */
-static int fr_event (sdla_t* card, int event, fr_mbox_t* mbox);
-static int fr_modem_failure (sdla_t *card, fr_mbox_t* mbox);
-static int fr_dlci_change (sdla_t *card, fr_mbox_t* mbox);
-
+static int fr_event(sdla_t * card, int event, fr_mbox_t * mbox);
+static int fr_modem_failure(sdla_t * card, fr_mbox_t * mbox);
+static int fr_dlci_change(sdla_t * card, fr_mbox_t * mbox);
 /* Miscellaneous functions */
-static int update_chan_state (struct device* dev);
-static void set_chan_state (struct device* dev, int state);
-static struct device* find_channel (sdla_t* card, unsigned dlci);
-static int is_tx_ready (sdla_t* card, fr_channel_t* chan);
-static unsigned int dec_to_uint (unsigned char* str, int len);
-static int reply_udp( unsigned char *data, unsigned int mbox_len );
-
-static int intr_test( sdla_t* card );
-static void init_chan_statistics( fr_channel_t* chan );
-static void init_global_statistics( sdla_t* card );
-static void read_DLCI_IB_mapping( sdla_t* card, fr_channel_t* chan );
-
+static int update_chan_state(struct device *dev);
+static void set_chan_state(struct device *dev, int state);
+static struct device *find_channel(sdla_t * card, unsigned dlci);
+static int is_tx_ready(sdla_t * card, fr_channel_t * chan);
+static unsigned int dec_to_uint(unsigned char *str, int len);
+static int reply_udp(unsigned char *data, unsigned int mbox_len);
+static int intr_test(sdla_t * card);
+static void init_chan_statistics(fr_channel_t * chan);
+static void init_global_statistics(sdla_t * card);
+static void read_DLCI_IB_mapping(sdla_t * card, fr_channel_t * chan);
 /* Udp management functions */
-static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, int dlci, fr_channel_t* chan);
-static int process_udp_driver_call(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, int dlci, fr_channel_t* chan);
-static int udp_pkt_type( struct sk_buff *skb, sdla_t* card );
-
+static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, int dlci, fr_channel_t * chan);
+static int process_udp_driver_call(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, int dlci, fr_channel_t * chan);
+static int udp_pkt_type(struct sk_buff *skb, sdla_t * card);
 /* IPX functions */
 static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming);
 static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number);
@@ -333,15 +314,16 @@
  * Return:	0	o.k.
  *		< 0	failure.
  */
-__initfunc(int wpf_init(sdla_t * card, wandev_conf_t * conf))
+int wpf_init(sdla_t * card, wandev_conf_t * conf)
 {
 	union {
 		char str[80];
 		fr_conf_t cfg;
 	} u;
-
+	int i;
 	/* Verify configuration ID */
-	if (conf->config_id != WANCONFIG_FR) {
+	if (conf->config_id != WANCONFIG_FR) 
+	{
 		printk(KERN_INFO "%s: invalid configuration ID %u!\n",
 		       card->devname, conf->config_id);
 		return -EINVAL;
@@ -355,45 +337,41 @@
 			card->flags = (void *) (card->hw.dpmbase + FR502_FLAG_OFFS);
 			card->isr = &fr502_isr;
 			break;
-
 		case SFID_FR508:
 			card->mbox = (void *) (card->hw.dpmbase + FR508_MBOX_OFFS);
 			card->flags = (void *) (card->hw.dpmbase + FR508_FLAG_OFFS);
 			card->isr = &fr508_isr;
 			break;
-
 		default:
 			return -EINVAL;
 	}
-
 	/* Read firmware version.  Note that when adapter initializes, it
 	 * clears the mailbox, so it may appear that the first command was
 	 * executed successfully when in fact it was merely erased. To work
 	 * around this, we execute the first command twice.
 	 */
 	if (fr_read_version(card, NULL) || fr_read_version(card, u.str))
-		return -EIO
-		    ;
+		return -EIO;
 	printk(KERN_INFO "%s: running frame relay firmware v%s\n",
 	       card->devname, u.str);
-
 	/* Adjust configuration */
 	conf->mtu = max(min(conf->mtu, 4080), FR_CHANNEL_MTU + FR_HEADER_LEN);
 	conf->bps = min(conf->bps, 2048000);
-
 	/* Configure adapter firmware */
 	memset(&u.cfg, 0, sizeof(u.cfg));
 	u.cfg.mtu = conf->mtu;
-	u.cfg.t391 = 10;
-	u.cfg.t392 = 15;
-	u.cfg.n391 = 6;
-	u.cfg.n392 = 3;
-	u.cfg.n393 = 4;
 	u.cfg.kbps = conf->bps / 1000;
-	u.cfg.cir_fwd = 16;
-	u.cfg.cir_bwd = u.cfg.bc_fwd = u.cfg.bc_bwd = u.cfg.cir_fwd;
-	u.cfg.options = 0x0081;	/* direct Rx, no CIR check */
-	
+	u.cfg.cir_fwd = u.cfg.cir_bwd = 16;
+	u.cfg.bc_fwd = u.cfg.bc_bwd = 16;
+	if (conf->station == WANOPT_CPE) 
+	{
+		u.cfg.options = 0x0080;
+		printk(KERN_INFO "%s: Global CIR enabled by Default\n", card->devname);
+	}
+	else
+	{
+		u.cfg.options = 0x0081;
+	}
 	switch (conf->u.fr.signalling) 
 	{
 		case WANOPT_FR_Q933:
@@ -403,54 +381,59 @@
 			u.cfg.options |= 0x0400;
 			break;
 	}
-
 	if (conf->station == WANOPT_CPE) 
 	{
 		u.cfg.options |= 0x8000;	/* auto config DLCI */
+		card->u.f.dlci_num = 0;
 	}
 	else
 	{
 		u.cfg.station = 1;	/* switch emulation mode */
-		card->u.f.node_dlci = conf->u.fr.dlci ? conf->u.fr.dlci : 16;
+		/* For switch emulation we have to create a list of dlci(s)
+		 * that will be sent to be global SET_DLCI_CONFIGURATION 
+		 * command in fr_configure() routine. 
+		 */
 		card->u.f.dlci_num = min(max(conf->u.fr.dlci_num, 1), 100);
+		for (i = 0; i < card->u.f.dlci_num; i++) 
+		{
+			card->u.f.node_dlci[i] = (unsigned short)
+			    conf->u.fr.dlci[i] ? conf->u.fr.dlci[i] : 16;
+		}
 	}
-
 	if (conf->clocking == WANOPT_INTERNAL)
-		u.cfg.port |= 0x0001
-		    ;
+		u.cfg.port |= 0x0001;
 	if (conf->interface == WANOPT_RS232)
-		u.cfg.port |= 0x0002
-		    ;
+		u.cfg.port |= 0x0002;
 	if (conf->u.fr.t391)
 		u.cfg.t391 = min(conf->u.fr.t391, 30);
+	else
+		u.cfg.t391 = 5;
 	if (conf->u.fr.t392)
 		u.cfg.t392 = min(conf->u.fr.t392, 30);
+	else
+		u.cfg.t392 = 15;
 	if (conf->u.fr.n391)
 		u.cfg.n391 = min(conf->u.fr.n391, 255);
+	else
+		u.cfg.n391 = 2;
 	if (conf->u.fr.n392)
 		u.cfg.n392 = min(conf->u.fr.n392, 10);
+	else
+		u.cfg.n392 = 3;
 	if (conf->u.fr.n393)
 		u.cfg.n393 = min(conf->u.fr.n393, 10);
-
+	else
+		u.cfg.n393 = 4;
 	if (fr_configure(card, &u.cfg))
-		return -EIO
-		    ;
-
+		return -EIO;
 	if (card->hw.fwid == SFID_FR508) 
 	{
 		fr_buf_info_t *buf_info =
 		(void *) (card->hw.dpmbase + FR508_RXBC_OFFS);
-
-		card->rxmb =
-		    (void *) (buf_info->rse_next -
-			      FR_MB_VECTOR + card->hw.dpmbase);
-		card->u.f.rxmb_base =
-		    (void *) (buf_info->rse_base -
-			      FR_MB_VECTOR + card->hw.dpmbase);
-		card->u.f.rxmb_last =
-		    (void *) (buf_info->rse_base +
-			 (buf_info->rse_num - 1) * sizeof(fr_buf_ctl_t) -
-			      FR_MB_VECTOR + card->hw.dpmbase);
+		card->rxmb = (void *) (buf_info->rse_next - FR_MB_VECTOR + card->hw.dpmbase);
+		card->u.f.rxmb_base = (void *) (buf_info->rse_base - FR_MB_VECTOR + card->hw.dpmbase);
+		card->u.f.rxmb_last = (void *) (buf_info->rse_base + (buf_info->rse_num - 1) * 
+				sizeof(fr_buf_ctl_t) - FR_MB_VECTOR + card->hw.dpmbase);
 		card->u.f.rx_base = buf_info->buf_base;
 		card->u.f.rx_top = buf_info->buf_top;
 	}
@@ -465,8 +448,19 @@
 	card->wandev.new_if = &new_if;
 	card->wandev.del_if = &del_if;
 	card->wandev.state = WAN_DISCONNECTED;
+	card->wandev.ttl = conf->ttl;
 	card->wandev.udp_port = conf->udp_port;
-	TracingEnabled = '0';
+	card->wandev.enable_tx_int = 0;
+	card->irq_dis_if_send_count = 0;
+	card->irq_dis_poll_count = 0;
+	card->wandev.enable_IPX = conf->enable_IPX;
+	if (conf->network_number)
+		card->wandev.network_number = conf->network_number;
+	else
+		card->wandev.network_number = 0xDEADBEEF;
+	/* Intialize global statistics for a card */
+	init_global_statistics(card);
+	TracingEnabled = 0;
 	return 0;
 }
 
@@ -475,20 +469,17 @@
 /*============================================================================
  * Update device status & statistics.
  */
+
 static int update(wan_device_t * wandev)
 {
 	sdla_t *card;
-
 	/* sanity checks */
 	if ((wandev == NULL) || (wandev->private == NULL))
-		return -EFAULT
-		    ;
+		return -EFAULT;
 	if (wandev->state == WAN_UNCONFIGURED)
-		return -ENODEV
-		    ;
+		return -ENODEV;
 	if (test_and_set_bit(0, (void *) &wandev->critical))
-		return -EAGAIN
-		    ;
+		return -EAGAIN;
 	card = wandev->private;
 	fr_get_err_stats(card);
 	fr_get_stats(card);
@@ -508,13 +499,14 @@
  * Return:	0	o.k.
  *		< 0	failure (channel will not be created)
  */
+
 static int new_if(wan_device_t * wandev, struct device *dev, wanif_conf_t * conf)
 {
 	sdla_t *card = wandev->private;
 	fr_channel_t *chan;
 	int err = 0;
-
-	if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) {
+	if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) 
+	{
 		printk(KERN_INFO "%s: invalid interface name!\n",
 		       card->devname);
 		return -EINVAL;
@@ -522,73 +514,67 @@
 	/* allocate and initialize private data */
 	chan = kmalloc(sizeof(fr_channel_t), GFP_KERNEL);
 	if (chan == NULL)
-		return -ENOMEM
-		    ;
+		return -ENOMEM;
 	memset(chan, 0, sizeof(fr_channel_t));
 	strcpy(chan->name, conf->name);
 	chan->card = card;
-
 	/* verify media address */
-	if (is_digit(conf->addr[0])) {
+	if (is_digit(conf->addr[0])) 
+	{
 		int dlci = dec_to_uint(conf->addr, 0);
-
-		if (dlci && (dlci <= 4095)) {
+		if (dlci && (dlci <= 4095)) 
+		{
 			chan->dlci = dlci;
-		} else {
-			printk(KERN_ERR
-			       "%s: invalid DLCI %u on interface %s!\n",
+		}
+		else
+		{
+			printk(KERN_ERR "%s: invalid DLCI %u on interface %s!\n",
 			       wandev->name, dlci, chan->name);
 			err = -EINVAL;
 		}
-	} else {
-		printk(KERN_ERR
-		       "%s: invalid media address on interface %s!\n",
+	} 
+	else 
+	{
+		printk(KERN_ERR "%s: invalid media address on interface %s!\n",
 		       wandev->name, chan->name);
 		err = -EINVAL;
 	}
-	if (err) {
+	if (err) 
+	{
 		kfree(chan);
 		return err;
 	}
-
 	/* place cir,be,bc and other channel specific information into the
 	 * chan structure 
-         */
+	 */
 	if (conf->cir) 
 	{
-		chan->cir = max( 1, min( conf->cir, 512 ) );
-		chan->cir_status = CIR_ENABLED; 
-
+		chan->cir = max(1, min(conf->cir, 512));
+		chan->cir_status = CIR_ENABLED;
 		if (conf->bc)
-			chan->bc = max( 1, min( conf->bc, 512 ) );
+			chan->bc = max(1, min(conf->bc, 512));
 		if (conf->be)
-			chan->be = max( 0, min( conf->be, 511) ); 
-
+			chan->be = max(0, min(conf->be, 511));
 	}
 	else
 		chan->cir_status = CIR_DISABLED;
-
 	chan->mc = conf->mc;
-
-	chan->dlci_configured = DLCI_NOT_CONFIGURED;	
-
-	chan->tx_int_status = DISABLED;	
-
-	init_chan_statistics( chan );
-
+	chan->dlci_configured = DLCI_NOT_CONFIGURED;
+	chan->tx_int_status = DISABLED;
+	init_chan_statistics(chan);
 	/* prepare network device data space for registration */
 	dev->name = chan->name;
 	dev->init = &if_init;
 	dev->priv = chan;
 	return 0;
 }
-
 /*============================================================================
  * Delete logical channel.
  */
 static int del_if(wan_device_t * wandev, struct device *dev)
 {
-	if (dev->priv) {
+	if (dev->priv) 
+	{
 		kfree(dev->priv);
 		dev->priv = NULL;
 	}
@@ -606,24 +592,27 @@
 	int retry = MAX_CMD_RETRY;
 	int err, len;
 	fr_cmd_t cmd;
-
-	if (copy_from_user((void *) &cmd, u_cmd, sizeof(cmd)))
-		return -EFAULT;
+	if(copy_from_user((void *) &cmd, u_cmd, sizeof(cmd)))
+		return -EFAULT;		
 	/* execute command */
-	do {
+	do 
+	{
 		memcpy(&mbox->cmd, &cmd, sizeof(cmd));
 		if (cmd.length)
-			if (copy_from_user((void *) &mbox->data, u_data, cmd.length))
+		{
+			if(copy_from_user((void *) &mbox->data, u_data, cmd.length))
 				return -EFAULT;
+		}
 		if (sdla_exec(mbox))
 			err = mbox->cmd.result;
 		else
 			return -EIO;
-	}
+	} 
 	while (err && retry-- && fr_event(card, err, mbox));
 
 	/* return result */
-	if (copy_to_user(u_cmd, (void *) &mbox->cmd, sizeof(fr_cmd_t)))
+
+	if(copy_to_user(u_cmd, (void *) &mbox->cmd, sizeof(fr_cmd_t)))
 		return -EFAULT;
 	len = mbox->cmd.length;
 	if (len && u_data && copy_to_user(u_data, (void *) &mbox->data, len))
@@ -632,7 +621,6 @@
 }
 
 /****** Network Device Interface ********************************************/
-
 /*============================================================================
  * Initialize Linux network interface.
  *
@@ -645,7 +633,6 @@
 	fr_channel_t *chan = dev->priv;
 	sdla_t *card = chan->card;
 	wan_device_t *wandev = &card->wandev;
-	int i;
 
 	/* Initialize device driver entry points */
 	dev->open = &if_open;
@@ -654,27 +641,22 @@
 	dev->rebuild_header = &if_rebuild_hdr;
 	dev->hard_start_xmit = &if_send;
 	dev->get_stats = &if_stats;
-
 	/* Initialize media-specific parameters */
-	dev->family = AF_INET;	/* address family */
-	dev->type = ARPHRD_DLCI;	/* ARP h/w type */
+	dev->type = ARPHRD_DLCI;			/* ARP h/w type */
 	dev->mtu = FR_CHANNEL_MTU;
-	dev->hard_header_len = FR_HEADER_LEN;	/* media header length */
-	dev->addr_len = 2;	/* hardware address length */
+	dev->hard_header_len = FR_HEADER_LEN;		/* media header length */
+	dev->addr_len = 2;				/* hardware address length */
 	*(unsigned short *) dev->dev_addr = htons(chan->dlci);
-
 	/* Initialize hardware parameters (just for reference) */
 	dev->irq = wandev->irq;
 	dev->dma = wandev->dma;
 	dev->base_addr = wandev->ioport;
 	dev->mem_start = wandev->maddr;
 	dev->mem_end = wandev->maddr + wandev->msize - 1;
-
 	/* Set transmit buffer queue length */
-	dev->tx_queue_len = 30;
-
+	dev->tx_queue_len = 10;
+	/* Initialize socket buffers */
 	dev_init_buffers(dev);
-	
 	set_chan_state(dev, WAN_DISCONNECTED);
 	return 0;
 }
@@ -695,124 +677,93 @@
 	int err = 0;
 	fr508_flags_t *flags = card->flags;
 	struct timeval tv;
-	
 	if (dev->start)
 		return -EBUSY;	/* only one open is allowed */
-
 	if (test_and_set_bit(0, (void *) &card->wandev.critical))
 		return -EAGAIN;
-
 	if (!card->open_cnt) 
 	{
 		Intr_test_counter = 0;
 		card->intr_mode = INTR_TEST_MODE;
-		err = intr_test( card );
-		
-		if ((err) || (Intr_test_counter !=(MAX_INTR_TEST_COUNTER +1))) {
-			printk(KERN_INFO 
-				"%s: Interrupt Test Failed, Counter: %i\n", 
-				card->devname, Intr_test_counter);
+		err = intr_test(card);
+		if ((err) || (Intr_test_counter != (MAX_INTR_TEST_COUNTER + 1))) {
+			printk(KERN_INFO
+			       "%s: Interrupt Test Failed, Counter: %i\n",
+			       card->devname, Intr_test_counter);
 			err = -EIO;
 			card->wandev.critical = 0;
 			return err;
 		}
-
 		printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n"
-			,card->devname, Intr_test_counter);
-		
+		       ,card->devname, Intr_test_counter);
 		/* The following allocates and intializes a circular
 		 * link list of interfaces per card.
 		 */
-		
 		card->devs_struct = kmalloc(sizeof(load_sharing_t), GFP_KERNEL);
 		if (card->devs_struct == NULL)
-			return -ENOMEM;	
+			return -ENOMEM;
 		card->dev_to_devtint_next = card->devs_struct;
-		
 		for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) {
 			(card->devs_struct)->dev_ptr = dev2;
-			if(dev2->slave == NULL)
+			if (dev2->slave == NULL)
 				(card->devs_struct)->next = card->dev_to_devtint_next;
 			else {
 				(card->devs_struct)->next = kmalloc(
-					sizeof(load_sharing_t), GFP_KERNEL);
+				     sizeof(load_sharing_t), GFP_KERNEL);
 				if ((card->devs_struct)->next == NULL)
-					return -ENOMEM;	
+					return -ENOMEM;
 				card->devs_struct = (card->devs_struct)->next;
-			}	
+			}
 		}
-
 		card->devs_struct = card->dev_to_devtint_next;
-	
-		card->intr_mode = BUFFER_INTR_MODE;	
-
+		card->intr_mode = BUFFER_INTR_MODE;
 		/* 
-		check all the interfaces for the device to see if CIR has
-	 	been enabled for any DLCI(s). If so then use the DLCI list
-		Interrupt mode for fr_set_intr_mode(), otherwise use the			default global interrupt mode
-		*/ 
-	
+		   check all the interfaces for the device to see if CIR has
+		   been enabled for any DLCI(s). If so then use the DLCI list
+		   Interrupt mode for fr_set_intr_mode(), otherwise use the                     default global interrupt mode
+		 */
 		for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) {
-			
-			if( ((fr_channel_t *)dev2->priv)->cir_status 
-							== CIR_ENABLED)  {
+			if (((fr_channel_t *) dev2->priv)->cir_status
+			    == CIR_ENABLED) {
 				card->intr_mode = DLCI_LIST_INTR_MODE;
 				break;
 			}
 		}
-
 		/* 
-		 *	If you enable comms and then set ints, you get a Tx int as you
-		 *	perform the SET_INT_TRIGGERS command. So, we only set int
-		 *	triggers and then adjust the interrupt mask (to disable Tx ints)		before enabling comms. 
+		   If you enable comms and then set ints, you get a Tx int as you
+		   perform the SET_INT_TRIGGERS command. So, we only set int
+		   triggers and then adjust the interrupt mask (to disable Tx ints)             before enabling comms. 
 		 */
-		if (card->intr_mode == BUFFER_INTR_MODE) 
-		{
-			if (fr_set_intr_mode(card, 0x03, card->wandev.mtu)) 
-			{
+		if (card->intr_mode == BUFFER_INTR_MODE) {
+			if (fr_set_intr_mode(card, 0x03, card->wandev.mtu)) {
 				err = -EIO;
 				card->wandev.critical = 0;
 				return err;
 			}
-
-			printk( KERN_INFO 
-				"%s: Global Buffering Tx Interrupt Mode\n"
-				, card->devname);
-
-		} 
-		else if (card->intr_mode == DLCI_LIST_INTR_MODE) 
-		{	
-			if (fr_set_intr_mode(card, 0x83, card->wandev.mtu)) 
-			{
+			printk(KERN_INFO
+			       "%s: Global Buffering Tx Interrupt Mode\n"
+			       ,card->devname);
+		} else if (card->intr_mode == DLCI_LIST_INTR_MODE) {
+			if (fr_set_intr_mode(card, 0x83, card->wandev.mtu)) {
 				err = -EIO;
 				card->wandev.critical = 0;
 				return err;
 			}
-
-			printk( KERN_INFO "%s: DLCI list Tx Interrupt Mode\n", 
-				card->devname);
-
+			printk(KERN_INFO
+			       "%s: DLCI list Tx Interrupt Mode\n",
+			       card->devname);
 		}
-
 		flags->imask &= ~0x02;
-	
-		if (fr_comm_enable(card)) 
-		{
+		if (fr_comm_enable(card)) {
 			err = -EIO;
 			card->wandev.critical = 0;
 			return err;
-		}	
-
+		}
 		wanpipe_set_state(card, WAN_CONNECTED);
-
-		if (card->wandev.station == WANOPT_CPE) 
-		{
+		if (card->wandev.station == WANOPT_CPE) {
 			/* CPE: issue full status enquiry */
 			fr_issue_isf(card, FR_ISF_FSE);
-		}
-		else
-		{	/* FR switch: activate DLCI(s) */
-		
+		} else {	/* FR switch: activate DLCI(s) */
 			/* For Switch emulation we have to ADD and ACTIVATE
 			 * the DLCI(s) that were configured with the SET_DLCI_
 			 * CONFIGURATION command. Add and Activate will fail if
@@ -820,12 +771,13 @@
 			 *
 			 * Also If_open is called once for each interface. But
 			 * it does not get in here for all the interface. So
-		 	 * we have to pass the entire list of DLCI(s) to add 
+			 * we have to pass the entire list of DLCI(s) to add 
 			 * activate routines.  
-			 */ 
-	
-			fr_add_dlci(card, card->u.f.node_dlci[0], card->u.f.dlci_num);
-			fr_activate_dlci(card, card->u.f.node_dlci, card->u.f.dlci_num);
+			 */
+			fr_add_dlci(card,
+			     card->u.f.node_dlci[0], card->u.f.dlci_num);
+			fr_activate_dlci(card,
+			     card->u.f.node_dlci[0], card->u.f.dlci_num);
 		}
 	}
 	dev->mtu = min(dev->mtu, card->wandev.mtu - FR_HEADER_LEN);
@@ -834,8 +786,7 @@
 	dev->start = 1;
 	wanpipe_open(card);
 	update_chan_state(dev);
-
-	do_gettimeofday( &tv );
+	do_gettimeofday(&tv);
 	chan->router_start_time = tv.tv_sec;
 	card->wandev.critical = 0;
 	return err;
@@ -846,17 +797,17 @@
  * o if this is the last open, then disable communications and interrupts.
  * o reset flags.
  */
+
 static int if_close(struct device *dev)
 {
 	fr_channel_t *chan = dev->priv;
 	sdla_t *card = chan->card;
-
 	if (test_and_set_bit(0, (void *) &card->wandev.critical))
 		return -EAGAIN;
-	;
 	dev->start = 0;
 	wanpipe_close(card);
-	if (!card->open_cnt) {
+	if (!card->open_cnt) 
+	{
 		wanpipe_set_state(card, WAN_DISCONNECTED);
 		fr_set_intr_mode(card, 0, 0);
 		fr_comm_disable(card);
@@ -875,14 +826,15 @@
  *
  * Return:	media header length.
  */
+
 static int if_header(struct sk_buff *skb, struct device *dev,
 	     unsigned short type, void *daddr, void *saddr, unsigned len)
 {
 	int hdr_len = 0;
-
 	skb->protocol = type;
 	hdr_len = wan_encapsulate(skb, dev);
-	if (hdr_len < 0) {
+	if (hdr_len < 0) 
+	{
 		hdr_len = 0;
 		skb->protocol = 0;
 	}
@@ -898,13 +850,14 @@
  * Return:	1	physical address resolved.
  *		0	physical address not resolved
  */
+
 static int if_rebuild_hdr(struct sk_buff *skb)
 {
-	fr_channel_t *chan = skb->dev->priv;
+	struct device *dev=skb->dev;
+	fr_channel_t *chan = dev->priv;
 	sdla_t *card = chan->card;
-
 	printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n",
-	       card->devname, skb->dev->name);
+	       card->devname, dev->name);
 	return 1;
 }
 
@@ -926,80 +879,101 @@
  * 2. Setting tbusy flag will inhibit further transmit requests from the
  *    protocol stack and can be used for flow control with protocol layer.
  */
+
 static int if_send(struct sk_buff *skb, struct device *dev)
 {
 	fr_channel_t *chan = dev->priv;
 	sdla_t *card = chan->card;
 	int retry = 0, err;
+	unsigned char *sendpacket;
 	struct device *dev2;
-    	fr508_flags_t* adptr_flags = card->flags;
-	fr_dlci_interface_t* dlci_interface = chan->dlci_int_interface;
+	unsigned long check_braddr, check_mcaddr;
+	fr508_flags_t *adptr_flags = card->flags;
+	int udp_type, send_data;
+	fr_dlci_interface_t *dlci_interface = chan->dlci_int_interface;
 	unsigned long host_cpu_flags;
-	int send_data = 0;
-   	++chan->if_send_entry;
+	++chan->if_send_entry;
 
-    	if (dev->tbusy) 
-    	{
+	if (dev->tbusy) 
+	{
 		/* If our device stays busy for at least 5 seconds then we will
-                 * kick start the device by making dev->tbusy = 0.  We expect
-                 * that our device never stays busy more than 5 seconds. So this                 * is only used as a last resort.
-                 */
-		
+		 * kick start the device by making dev->tbusy = 0.  We expect
+		 * that our device never stays busy more than 5 seconds. So this                 * is only used as a last resort.
+		 */
 		++chan->if_send_busy;
 		++chan->ifstats.collisions;
-
-		if ((jiffies - chan->tick_counter) < (5*HZ))
+		if ((jiffies - chan->tick_counter) < (5 * HZ))
 			return 1;
 
 		printk(KERN_INFO "%s: Transmit timed out\n", chan->name);
-		
 		++chan->if_send_busy_timeout;
-
 		/* unbusy all the interfaces on the card */
-       		for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave)
+		for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave)
 			dev2->tbusy = 0;
-    	}
-
-	if (test_and_set_bit(0, (void *) &card->wandev.critical)) {
-#ifdef _DEBUG_
-		printk(KERN_INFO "%s: if_send() hit critical section!\n",
-		       card->devname);
-#endif
+	}
+	sendpacket = skb->data;
+	udp_type = udp_pkt_type(skb, card);
+	if (udp_type == UDP_DRVSTATS_TYPE) 
+	{
+		++chan->if_send_DRVSTATS_request;
+		process_udp_driver_call(UDP_PKT_FRM_STACK, card, skb, dev, 0,
+					chan);
+		dev_kfree_skb(skb);
+		return 0;
+	}
+	else if (udp_type == UDP_FPIPE_TYPE)
+		++chan->if_send_FPIPE_request;
+	/* retreive source address in two forms: broadcast & multicast */
+	check_braddr = sendpacket[17];
+	check_mcaddr = sendpacket[14];
+	check_braddr = check_braddr << 8;
+	check_mcaddr = check_mcaddr << 8;
+	check_braddr |= sendpacket[16];
+	check_mcaddr |= sendpacket[15];
+	check_braddr = check_braddr << 8;
+	check_mcaddr = check_mcaddr << 8;
+	check_braddr |= sendpacket[15];
+	check_mcaddr |= sendpacket[16];
+	check_braddr = check_braddr << 8;
+	check_mcaddr = check_mcaddr << 8;
+	check_braddr |= sendpacket[14];
+	check_mcaddr |= sendpacket[17];
+	/* if the Source Address is a Multicast address */
+	if ((chan->mc == WANOPT_NO) && (check_mcaddr >= 0xE0000001) &&
+	    (check_mcaddr <= 0xFFFFFFFE)) 
+	{
+		printk(KERN_INFO "%s: Multicast Src. Addr. silently discarded\n"
+		       ,card->devname);
 		dev_kfree_skb(skb);
+		++chan->ifstats.tx_dropped;
+		++chan->if_send_multicast;
 		return 0;
 	}
-    	disable_irq(card->hw.irq);
+	disable_irq(card->hw.irq);
 	++card->irq_dis_if_send_count;
-
-	if (test_and_set_bit(0, (void*)&card->wandev.critical)) 
+	if (test_and_set_bit(0, (void *) &card->wandev.critical)) 
 	{
 		if (card->wandev.critical == CRITICAL_IN_ISR) 
-		{	
+		{
 			++chan->if_send_critical_ISR;
-
-			if (card->intr_mode == DLCI_LIST_INTR_MODE)
+			if (card->intr_mode == DLCI_LIST_INTR_MODE) 
 			{
 				/* The enable_tx_int flag is set here so that if
-			 	 * the critical flag is set due to an interrupt 
+				 * the critical flag is set due to an interrupt 
 				 * then we want to enable transmit interrupts 
 				 * again.
-			 	 */ 
-		
+				 */
 				card->wandev.enable_tx_int = 1;
-
 				/* Setting this flag to WAITING_TO_BE_ENABLED 
 				 * specifies that interrupt bit has to be 
 				 * enabled for that particular interface. 
 				 * (delayed interrupt)
-			 	 */
-				
+				 */
 				chan->tx_int_status = WAITING_TO_BE_ENABLED;
-
 				/* This is used for enabling dynamic calculation
 				 * of CIRs relative to the packet length.
-			 	 */
-	
-	       			chan->pkt_length = skb->len;			
+				 */
+				chan->pkt_length = skb->len;
 				dev->tbusy = 1;
 				chan->tick_counter = jiffies;
 			}
@@ -1011,34 +985,36 @@
 			}
 			save_flags(host_cpu_flags);
 			cli();
-			if ((!(--card->irq_dis_if_send_count)) && 
-					(!card->irq_dis_poll_count))
+			if ((!(--card->irq_dis_if_send_count)) &&
+			    (!card->irq_dis_poll_count))
 				enable_irq(card->hw.irq);
 			restore_flags(host_cpu_flags);
 			return 1;
 		}
-
 		++chan->if_send_critical_non_ISR;
 		++chan->ifstats.tx_dropped;
 		dev_kfree_skb(skb);
 		save_flags(host_cpu_flags);
 		cli();
-                if ((!(--card->irq_dis_if_send_count)) &&
-					 (!card->irq_dis_poll_count))
-                	enable_irq(card->hw.irq);
+		if ((!(--card->irq_dis_if_send_count)) &&
+		    (!card->irq_dis_poll_count))
+			enable_irq(card->hw.irq);
 		restore_flags(host_cpu_flags);
 		return 0;
 	}
- 	
 	card->wandev.critical = 0x21;
-    
-	if (card->wandev.state != WAN_CONNECTED) 
+	if (udp_type == UDP_FPIPE_TYPE) 
+	{
+		err = process_udp_mgmt_pkt(UDP_PKT_FRM_STACK, card, skb,
+					   dev, 0, chan);
+	}
+	else if (card->wandev.state != WAN_CONNECTED) 
 	{
 		++chan->if_send_wan_disconnected;
 		++chan->ifstats.tx_dropped;
 		++card->wandev.stats.tx_dropped;
 	}
-	else if (chan->state != WAN_CONNECTED)
+	else if (chan->state != WAN_CONNECTED) 
 	{
 		++chan->if_send_dlci_disconnected;
 		update_chan_state(dev);
@@ -1047,313 +1023,361 @@
 	}
 	else if (!is_tx_ready(card, chan)) 
 	{
-		if (card->intr_mode == DLCI_LIST_INTR_MODE )
+		if (card->intr_mode == DLCI_LIST_INTR_MODE) 
 		{
 			dlci_interface->gen_interrupt |= 0x40;
-			dlci_interface->packet_length = skb->len;	
-		}  
-       		dev->tbusy = 1;
+			dlci_interface->packet_length = skb->len;
+		}
+		dev->tbusy = 1;
 		chan->tick_counter = jiffies;
-
 		adptr_flags->imask |= 0x02;
-
-		++ chan->if_send_no_bfrs;
+		++chan->if_send_no_bfrs;
 		retry = 1;
 	}
-	else 
+	else
 	{
 		send_data = 1;
 		/* If it's an IPX packet */
-		if( sendpacket[1] == 0x00 &&
+		if (sendpacket[1] == 0x00 &&
 		    sendpacket[2] == 0x80 &&
 		    sendpacket[6] == 0x81 &&
 		    sendpacket[7] == 0x37) 
 		{
-			if( card->wandev.enable_IPX ) 
+			if (card->wandev.enable_IPX) 
 			{
-				switch_net_numbers(sendpacket, 
-					card->wandev.network_number, 0);
-			}
-			else
+				switch_net_numbers(sendpacket,
+					 card->wandev.network_number, 0);
+			} 
+			else 
 			{
 				/* increment some statistic here! */
 				send_data = 0;
 			}
 		}
-
-		if (send_data)
+		if (send_data) 
 		{
-        		err = (card->hw.fwid == SFID_FR508) ?
-        		fr508_send(card, chan->dlci, 0, skb->len, skb->data) :
-        		fr502_send(card, chan->dlci, 0, skb->len, skb->data);
-
+			err = (card->hw.fwid == SFID_FR508) ?
+			    fr508_send(card, chan->dlci, 0, skb->len, skb->data) :
+			    fr502_send(card, chan->dlci, 0, skb->len, skb->data);
 			if (err) 
 			{
 				if (card->intr_mode == DLCI_LIST_INTR_MODE) 
 				{
 					dlci_interface->gen_interrupt |= 0x40;
-					dlci_interface->packet_length = skb->len;	
-				} 
-                		dev->tbusy = 1;
+					dlci_interface->packet_length = skb->len;
+				}
+				dev->tbusy = 1;
 				chan->tick_counter = jiffies;
-            			adptr_flags->imask |= 0x02;
+				adptr_flags->imask |= 0x02;
 				retry = 1;
-           			++ chan->if_send_adptr_bfrs_full;
-				++ chan->ifstats.tx_errors;
-            			++ card->wandev.stats.tx_errors;
-			} 
-			else
+				++chan->if_send_adptr_bfrs_full;
+				++chan->ifstats.tx_errors;
+				++card->wandev.stats.tx_errors;
+			}
+			else 
 			{
-				++ chan->if_send_bfrs_passed_to_adptr;
+				++chan->if_send_bfrs_passed_to_adptr;
 				++chan->ifstats.tx_packets;
 				++card->wandev.stats.tx_packets;
 			}
 		}
 	}
-
 	if (!retry)
 		dev_kfree_skb(skb);
 
 	card->wandev.critical = 0;
 	save_flags(host_cpu_flags);
 	cli();
-        if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count))
-        	enable_irq(card->hw.irq);
+	if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count))
+		enable_irq(card->hw.irq);
 	restore_flags(host_cpu_flags);
 	return retry;
 }
 
+/*============================================================================
+ * Reply to UDP Management system.
+ * Return nothing.
+ */
+
+static int reply_udp(unsigned char *data, unsigned int mbox_len)
+{
+	unsigned short len, udp_length, temp, i, ip_length;
+	unsigned long sum;
+	/* Set length of packet */
+	len = mbox_len + 62;
+	/* fill in UDP reply */
+	data[38] = 0x02;
+	/* fill in UDP length */
+	udp_length = mbox_len + 40;
+	/* put it on an even boundary */
+	if (udp_length & 0x0001) 
+	{
+		udp_length += 1;
+		len += 1;
+	}
+	temp = (udp_length << 8) | (udp_length >> 8);
+	memcpy(&data[26], &temp, 2);
+	/* swap UDP ports */
+	memcpy(&temp, &data[22], 2);
+	memcpy(&data[22], &data[24], 2);
+	memcpy(&data[24], &temp, 2);
+	/* add UDP pseudo header */
+	temp = 0x1100;
+	memcpy(&data[udp_length + 22], &temp, 2);
+	temp = (udp_length << 8) | (udp_length >> 8);
+	memcpy(&data[udp_length + 24], &temp, 2);
+	/* calculate UDP checksum */
+	data[28] = data[29] = 0;
+	sum = 0;
+	for (i = 0; i < udp_length + 12; i += 2) 
+	{
+		memcpy(&temp, &data[14 + i], 2);
+		sum += (unsigned long) temp;
+	}
+	while (sum >> 16)
+		sum = (sum & 0xffffUL) + (sum >> 16);
+
+	temp = (unsigned short) sum;
+	temp = ~temp;
+	if (temp == 0)
+		temp = 0xffff;
+	memcpy(&data[28], &temp, 2);
+	/* fill in IP length */
+	ip_length = udp_length + 20;
+	temp = (ip_length << 8) | (ip_length >> 8);
+	memcpy(&data[4], &temp, 2);
+	/* swap IP addresses */
+	memcpy(&temp, &data[14], 2);
+	memcpy(&data[14], &data[18], 2);
+	memcpy(&data[18], &temp, 2);
+	memcpy(&temp, &data[16], 2);
+	memcpy(&data[16], &data[20], 2);
+	memcpy(&data[20], &temp, 2);
+	/* fill in IP checksum */
+	data[12] = data[13] = 0;
+	sum = 0;
+	for (i = 0; i < 20; i += 2) 
+	{
+		memcpy(&temp, &data[2 + i], 2);
+		sum += (unsigned long) temp;
+	}
+	while (sum >> 16)
+		sum = (sum & 0xffffUL) + (sum >> 16);
+	temp = (unsigned short) sum;
+	temp = ~temp;
+	if (temp == 0)
+		temp = 0xffff;
+	memcpy(&data[12], &temp, 2);
+	return len;
+}				/* reply_udp */
 /*
- *	If incoming is 0 (outgoing)- if the net numbers is ours make it 0
- *	if incoming is 1 - if the net number is 0 make it ours 
- *
+   If incoming is 0 (outgoing)- if the net numbers is ours make it 0
+   if incoming is 1 - if the net number is 0 make it ours 
  */
- 
+
 static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming)
 {
 	unsigned long pnetwork_number;
-
-	pnetwork_number = (unsigned long)((sendpacket[14] << 24) + 
-			  (sendpacket[15] << 16) + (sendpacket[16] << 8) + 
-			  sendpacket[17]);
-
+	pnetwork_number = (unsigned long) ((sendpacket[14] << 24) +
+			 (sendpacket[15] << 16) + (sendpacket[16] << 8) +
+					   sendpacket[17]);
 	if (!incoming) {
-		if( pnetwork_number == network_number) {
-			sendpacket[14] = sendpacket[15] = sendpacket[16] = 
-					 sendpacket[17] = 0x00;
+		/* If the destination network number is ours, make it 0 */
+		if (pnetwork_number == network_number) {
+			sendpacket[14] = sendpacket[15] = sendpacket[16] =
+			    sendpacket[17] = 0x00;
 		}
 	} else {
-		if( pnetwork_number == 0) {
-			sendpacket[14] = (unsigned char)(network_number >> 24);
-			sendpacket[15] = (unsigned char)((network_number & 
-					 0x00FF0000) >> 16);
-			sendpacket[16] = (unsigned char)((network_number & 
-					 0x0000FF00) >> 8);
-			sendpacket[17] = (unsigned char)(network_number & 
-					 0x000000FF);
+		/* If the incoming network is 0, make it ours */
+		if (pnetwork_number == 0) 
+		{
+			sendpacket[14] = (unsigned char) (network_number >> 24);
+			sendpacket[15] = (unsigned char) ((network_number &
+						      0x00FF0000) >> 16);
+			sendpacket[16] = (unsigned char) ((network_number &
+						       0x0000FF00) >> 8);
+			sendpacket[17] = (unsigned char) (network_number &
+							  0x000000FF);
 		}
 	}
-
-
-	pnetwork_number = (unsigned long)((sendpacket[26] << 24) + 
-			  (sendpacket[27] << 16) + (sendpacket[28] << 8) + 
-			  sendpacket[29]);
-
-	if( !incoming ) {
-		if( pnetwork_number == network_number) {
-			sendpacket[26] = sendpacket[27] = sendpacket[28] = 
-					 sendpacket[29] = 0x00;
+	pnetwork_number = (unsigned long) ((sendpacket[26] << 24) +
+			 (sendpacket[27] << 16) + (sendpacket[28] << 8) +
+					   sendpacket[29]);
+	if (!incoming) {
+		/* If the source network is ours, make it 0 */
+		if (pnetwork_number == network_number) 
+		{
+			sendpacket[26] = sendpacket[27] = sendpacket[28] =
+			    sendpacket[29] = 0x00;
 		}
 	} else {
-		if( pnetwork_number == 0 ) {
-			sendpacket[26] = (unsigned char)(network_number >> 24);
-			sendpacket[27] = (unsigned char)((network_number & 
-					 0x00FF0000) >> 16);
-			sendpacket[28] = (unsigned char)((network_number & 
-					 0x0000FF00) >> 8);
-			sendpacket[29] = (unsigned char)(network_number & 
-					 0x000000FF);
+		/* If the source network is 0, make it ours */
+		if (pnetwork_number == 0) {
+			sendpacket[26] = (unsigned char) (network_number >> 24);
+			sendpacket[27] = (unsigned char) ((network_number &
+						      0x00FF0000) >> 16);
+			sendpacket[28] = (unsigned char) ((network_number &
+						       0x0000FF00) >> 8);
+			sendpacket[29] = (unsigned char) (network_number &
+							  0x000000FF);
 		}
 	}
-} /* switch_net_numbers */
-
+}				/* switch_net_numbers */
 
 /*============================================================================
  * Get ethernet-style interface statistics.
  * Return a pointer to struct enet_statistics.
  */
+
 static struct net_device_stats *if_stats(struct device *dev)
 {
 	fr_channel_t *chan = dev->priv;
-
 	return &chan->ifstats;
 }
 
 /****** Interrupt Handlers **************************************************/
-
 /*============================================================================
  * S502 frame relay interrupt service routine.
  */
+
 static void fr502_isr(sdla_t * card)
 {
 	fr502_flags_t *flags = card->flags;
-
 	switch (flags->iflag) 
 	{
 		case 0x01:		/* receive interrupt */
 			fr502_rx_intr(card);
 			break;
-
 		case 0x02:		/* transmit interrupt */
 			flags->imask &= ~0x02;
 			tx_intr(card);
 			break;
-
 		default:
 			spur_intr(card);
 	}
 	flags->iflag = 0;
 }
-
 /*============================================================================
  * S508 frame relay interrupt service routine.
  */
+
 static void fr508_isr(sdla_t * card)
 {
 	fr508_flags_t *flags = card->flags;
 	fr_buf_ctl_t *bctl;
 	char *ptr = &flags->iflag;
-	struct device* dev = card->wandev.dev;
-	struct device* dev2;
+	struct device *dev = card->wandev.dev;
+	struct device *dev2;
 	int i;
 	unsigned long host_cpu_flags;
-	unsigned disable_tx_intr =1;
-	fr_channel_t* chan;
-	fr_dlci_interface_t* dlci_interface;	
-
+	unsigned disable_tx_intr = 1;
+	fr_channel_t *chan;
+	fr_dlci_interface_t *dlci_interface;
 	/* This flag prevents nesting of interrupts.  See sdla_isr() routine
-         * in sdlamain.c. 
+	 * in sdlamain.c. 
 	 */
 	card->in_isr = 1;
-	
 	++card->statistics.isr_entry;
-
-	if (test_and_set_bit(0, (void*)&card->wandev.critical)) 
+	if (test_and_set_bit(0, (void *) &card->wandev.critical)) 
 	{
- 		printk(KERN_INFO "fr508_isr: %s, wandev.critical set to 0x%02X, int type = 0x%02X\n", card->devname, card->wandev.critical, flags->iflag);
+		printk(KERN_INFO "fr508_isr: %s, wandev.critical set to 0x%02X, int type = 0x%02X\n", card->devname, card->wandev.critical, flags->iflag);
 		++card->statistics.isr_already_critical;
 		card->in_isr = 0;
 		return;
 	}
-	int_occur = 1;
-
 	/* For all interrupts set the critical flag to CRITICAL_RX_INTR.
-         * If the if_send routine is called with this flag set it will set
-         * the enable transmit flag to 1. (for a delayed interrupt)
-         */
+	 * If the if_send routine is called with this flag set it will set
+	 * the enable transmit flag to 1. (for a delayed interrupt)
+	 */
 	card->wandev.critical = CRITICAL_IN_ISR;
-
 	card->dlci_int_mode_unbusy = 0;
-	card->buff_int_mode_unbusy = 0;	
-
-	switch (flags->iflag) {
-		case 0x01:  /* receive interrupt */
-	    		++card->statistics.isr_rx;
-            		fr508_rx_intr(card);
-            		break;
-
-		case 0x02:  /* transmit interrupt */
-	    		++card->statistics.isr_tx;  
-	    		bctl = (void*)(flags->tse_offs - FR_MB_VECTOR + 
-				card->hw.dpmbase);
+	card->buff_int_mode_unbusy = 0;
+	switch (flags->iflag) 
+	{
+		case 0x01:		/* receive interrupt */
+			++card->statistics.isr_rx;
+			fr508_rx_intr(card);
+			break;
+		case 0x02:		/* transmit interrupt */
+			++card->statistics.isr_tx;
+			bctl = (void *) (flags->tse_offs - FR_MB_VECTOR +
+				 card->hw.dpmbase);
 			bctl->flag = 0xA0;
-		
-			if (card->intr_mode == DLCI_LIST_INTR_MODE )
+			if (card->intr_mode == DLCI_LIST_INTR_MODE) 
 			{
 				/* Find the structure and make it unbusy */
-				dev = find_channel( card, flags->dlci);
+				dev = find_channel(card, flags->dlci);
 				dev->tbusy = 0;
-
 				/* This is used to perform devtint at the
 				 * end of the isr 
 				 */
 				card->dlci_int_mode_unbusy = 1;
-	
 				/* check to see if any other interfaces are
 				 * busy. If so then do not disable the tx
 				 * interrupts 
-				 */		 
-				for (dev2 = card->wandev.dev; dev2; 
-						dev2 = dev2->slave)
+				 */
+				for (dev2 = card->wandev.dev; dev2;
+					dev2 = dev2->slave) 
 				{
-					if ( dev2->tbusy == 1)
+					if (dev2->tbusy == 1) 
 					{
 						disable_tx_intr = 0;
 						break;
-            				}
+					}
 				}
 				if (disable_tx_intr)
 					flags->imask &= ~0x02;
-				  
-			}
+			} 
 			else if (card->intr_mode == BUFFER_INTR_MODE) 
 			{
-				for (dev2 = card->wandev.dev; dev2; 
-							dev2 = dev2->slave) 
+				for (dev2 = card->wandev.dev; dev2;
+					dev2 = dev2->slave) 
 				{
-					if ( !dev2 || !dev2->start ) 
+					if (!dev2 || !dev2->start) 
 					{
-						++card->statistics.
-						tx_intr_dev_not_started;
+						++card->statistics.tx_intr_dev_not_started;
 						continue;
 					}
-					if(dev2->tbusy)
+					if (dev2->tbusy) 
 					{
 						card->buff_int_mode_unbusy = 1;
-						((fr_channel_t*)dev2->priv)->dev_pending_devtint = 1;
+						((fr_channel_t *) dev2->priv)->dev_pending_devtint = 1;
 						dev2->tbusy = 0;
-					} else 
-						((fr_channel_t*)dev2->priv)->dev_pending_devtint = 0;
+					} 
+					else
+						((fr_channel_t *) dev2->priv)->dev_pending_devtint = 0;
 				}
-				flags->imask &= ~0x02;	
+				flags->imask &= ~0x02;
 			}
-            		break;
-
+			break;
 		case 0x08:
-	    		Intr_test_counter++;
+			Intr_test_counter++;
 			++card->statistics.isr_intr_test;
-	    		break;	
-
+			break;
 		default:
-	    		++card->statistics.isr_spurious;
-            		spur_intr(card);
-	    		printk(KERN_INFO "%s: Interrupt Type 0x%02X!\n", 
-				card->devname, flags->iflag);
-	    
-			printk(KERN_INFO "%s: ID Bytes = ",card->devname);
- 	    		for(i = 0; i < 8; i ++)
+			++card->statistics.isr_spurious;
+			spur_intr(card);
+			printk(KERN_INFO "%s: Interrupt Type 0x%02X!\n",
+			       card->devname, flags->iflag);
+			printk(KERN_INFO "%s: ID Bytes = ", card->devname);
+			for (i = 0; i < 8; i++)
 				printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i));
-	   	 	printk(KERN_INFO "\n");	
-            
+			printk(KERN_INFO "\n");
 			break;
-    	}
-
+	}
 	card->wandev.critical = CRITICAL_INTR_HANDLED;
 	if (card->wandev.enable_tx_int) 
 	{
-		if( card->intr_mode == DLCI_LIST_INTR_MODE )
-		{	
-			for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave)
-			{
-	 			chan = dev2->priv;
-				if ( chan->tx_int_status == 
-						WAITING_TO_BE_ENABLED ) 
+		if (card->intr_mode == DLCI_LIST_INTR_MODE) 
+		{
+			for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) 
+			{
+				chan = dev2->priv;
+				if (chan->tx_int_status == WAITING_TO_BE_ENABLED) 
 				{
-					dlci_interface = 
-						chan->dlci_int_interface;
+					dlci_interface = chan->dlci_int_interface;
 					dlci_interface->gen_interrupt |= 0x40;
-					dlci_interface->packet_length = 
-							chan->pkt_length;	
+					dlci_interface->packet_length = chan->pkt_length;
 					chan->tx_int_status = DISABLED;
 				}
 			}
@@ -1361,7 +1385,7 @@
 		card->wandev.enable_tx_int = 0;
 		flags->imask |= 0x02;
 		++card->statistics.isr_enable_tx_int;
-   	}
+	}
 	save_flags(host_cpu_flags);
 	cli();
 	card->in_isr = 0;
@@ -1369,35 +1393,30 @@
 	flags->iflag = 0;
 	card->wandev.critical = 0;
 	restore_flags(host_cpu_flags);
-
-	/*
-	 *	Device is now ready to send. The instant this is executed the If_Send
-	 *	routine is called. That is why this is put at the bottom of the ISR
-	 *	to prevent a endless loop condition caused by repeated Interrupts and
-	 *	enable_tx_int flag.
+	/* Device is now ready to send. The instant this is executed the If_Send
+	   routine is called. That is why this is put at the bottom of the ISR
+	   to prevent a endless loop condition caused by repeated Interrupts and
+	   enable_tx_int flag.
 	 */
-
-	if(card->dlci_int_mode_unbusy)
-		dev_tint(dev);
-	
-	if(card->buff_int_mode_unbusy)
+	if (card->dlci_int_mode_unbusy)
+		mark_bh(NET_BH);
+	if (card->buff_int_mode_unbusy) 
 	{
 		for (;;) 
 		{
-			if (((fr_channel_t*)((card->devs_struct)->dev_ptr)->priv)->dev_pending_devtint == 1){
-		
-				((fr_channel_t*)((card->devs_struct)->dev_ptr)->priv)->dev_pending_devtint=0;
-				dev_tint((card->devs_struct)->dev_ptr);
+			if (((fr_channel_t *) ((card->devs_struct)->dev_ptr)->priv)->dev_pending_devtint == 1) 
+			{
+				((fr_channel_t *) ((card->devs_struct)->dev_ptr)->priv)->dev_pending_devtint = 0;
+				mark_bh(NET_BH);
 			}
 			if ((card->devs_struct)->next == card->dev_to_devtint_next)
 				break;
 			card->devs_struct = (card->devs_struct)->next;
 		}
 		card->devs_struct = (card->dev_to_devtint_next)->next;
-		card->dev_to_devtint_next = card->devs_struct; 
+		card->dev_to_devtint_next = card->devs_struct;
 	}
 }
-
 /*============================================================================
  * Receive interrupt handler.
  */
@@ -1410,28 +1429,31 @@
 	fr_channel_t *chan;
 	unsigned dlci, len;
 	void *buf;
-
+	unsigned char *sendpacket;
+	unsigned char buf2[3];
+	int udp_type;
 	sdla_mapmem(&card->hw, FR502_RX_VECTOR);
-
 	dlci = mbox->cmd.dlci;
 	len = mbox->cmd.length;
-
 	/* Find network interface for this packet */
 	dev = find_channel(card, dlci);
-	if (dev == NULL) {
+	if (dev == NULL) 
+	{
 		/* Invalid channel, discard packet */
 		printk(KERN_INFO "%s: receiving on orphaned DLCI %d!\n",
 		       card->devname, dlci);
 		sdla_mapmem(&card->hw, FR_MB_VECTOR);
 	}
 	chan = dev->priv;
-	if (!dev->start) {
+	if (!dev->start) 
+	{
 		++chan->ifstats.rx_dropped;
 		sdla_mapmem(&card->hw, FR_MB_VECTOR);
 	}
 	/* Allocate socket buffer */
 	skb = dev_alloc_skb(len);
-	if (skb == NULL) {
+	if (skb == NULL) 
+	{
 		printk(KERN_INFO "%s: no socket buffers available!\n",
 		       card->devname);
 		++chan->ifstats.rx_dropped;
@@ -1441,26 +1463,50 @@
 	buf = skb_put(skb, len);
 	memcpy(buf, mbox->data, len);
 	sdla_mapmem(&card->hw, FR_MB_VECTOR);
-
-	/* Decapsulate packet and pass it up the protocol stack */
-	skb->dev = dev;
-	buf = skb_pull(skb, 1);	/* remove hardware header */
-	if (!wan_type_trans(skb, dev)) {
-		/* can't decapsulate packet */
-		dev_kfree_skb(skb);
-		++chan->ifstats.rx_errors;
-		++card->wandev.stats.rx_errors;
-	} else {
-		netif_rx(skb);
-		++chan->ifstats.rx_packets;
-		++card->wandev.stats.rx_packets;
+	/* Check if it's a UDP management packet */
+	sendpacket = skb->data;
+	memcpy(&buf2, &card->wandev.udp_port, 2);
+	udp_type = udp_pkt_type(skb, card);
+	if ((udp_type == UDP_FPIPE_TYPE) || (udp_type == UDP_DRVSTATS_TYPE)) 
+	{
+		if (udp_type == UDP_DRVSTATS_TYPE) 
+		{
+			++chan->rx_intr_DRVSTATS_request;
+			process_udp_driver_call(UDP_PKT_FRM_NETWORK, card, skb,
+						dev, dlci, chan);
+		}
+		else
+		{
+			++chan->rx_intr_FPIPE_request;
+			process_udp_mgmt_pkt(UDP_PKT_FRM_NETWORK, card, skb,
+					     dev, dlci, chan);
+		}
+	}
+	else
+	{
+		/* Decapsulate packet and pass it up the protocol stack */
+		skb->dev = dev;
+		buf = skb_pull(skb, 1);		/* remove hardware header */
+		if (!wan_type_trans(skb, dev)) 
+		{
+			/* can't decapsulate packet */
+			dev_kfree_skb(skb);
+			++chan->ifstats.rx_errors;
+			++card->wandev.stats.rx_errors;
+		}
+		else 
+		{
+			netif_rx(skb);
+			++chan->ifstats.rx_packets;
+			++card->wandev.stats.rx_packets;
+		}
 	}
 	sdla_mapmem(&card->hw, FR_MB_VECTOR);
 }
-
 /*============================================================================
  * Receive interrupt handler.
  */
+
 static void fr508_rx_intr(sdla_t * card)
 {
 	fr_buf_ctl_t *frbuf = card->rxmb;
@@ -1470,134 +1516,127 @@
 	unsigned dlci, len, offs;
 	void *buf;
 	unsigned rx_count = 0;
-	fr508_flags_t* flags = card->flags;
+	fr508_flags_t *flags = card->flags;
 	char *ptr = &flags->iflag;
-	int i, err;
-
-	if (frbuf->flag != 0x01) {
-		printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n",
-		       card->devname, (unsigned) frbuf);
-		printk(KERN_INFO "%s: ID Bytes = ",card->devname);
- 		for(i = 0; i < 8; i ++)
+	int i, err, udp_type;
+	if (frbuf->flag != 0x01) 
+	{
+		printk(KERN_INFO
+		       "%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n",
+		       card->devname, (unsigned) frbuf, frbuf->flag);
+		printk(KERN_INFO "%s: ID Bytes = ", card->devname);
+		for (i = 0; i < 8; i++)
 			printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i));
 		printk(KERN_INFO "\n");
-	
 		++card->statistics.rx_intr_corrupt_rx_bfr;
 		return;
 	}
-
-	do
+	
+	do 
 	{
-		len  = frbuf->length;
+		len = frbuf->length;
 		dlci = frbuf->dlci;
 		offs = frbuf->offset;
-
 		/* Find network interface for this packet */
 		dev = find_channel(card, dlci);
 		chan = dev->priv;
-        
 		if (dev == NULL) 
 		{
 			/* Invalid channel, discard packet */
 			printk(KERN_INFO "%s: receiving on orphaned DLCI %d!\n"
-				, card->devname, dlci);
-			++card->statistics.rx_intr_on_orphaned_DLCI; 
-        	}
+			       ,card->devname, dlci);
+			++card->statistics.rx_intr_on_orphaned_DLCI;
+		}
 		else
 		{
-	   		skb = dev_alloc_skb(len); 
- 			if (!dev->start || (skb == NULL)) 
- 			{ 
+			skb = dev_alloc_skb(len);
+			if (!dev->start || (skb == NULL)) 
+			{
 				++chan->ifstats.rx_dropped;
-				if(dev->start) 
+				if (dev->start) 
 				{
-					printk(KERN_INFO 
-					"%s: no socket buffers available!\n", 
-					card->devname);
-		    			++chan->rx_intr_no_socket;
-                		
-				} 
-				else
-					++ chan->rx_intr_dev_not_started;
-			} 
+					printk(KERN_INFO
+					       "%s: no socket buffers available!\n",
+					       card->devname);
+					++chan->rx_intr_no_socket;
+				} else
+					++chan->rx_intr_dev_not_started;
+			}
 			else
 			{
 				/* Copy data to the socket buffer */
 				if ((offs + len) > card->u.f.rx_top + 1) 
 				{
-					unsigned tmp = card->u.f.rx_top - 
-							offs + 1;
-
+					unsigned tmp = card->u.f.rx_top - offs + 1;
 					buf = skb_put(skb, tmp);
 					sdla_peek(&card->hw, offs, buf, tmp);
 					offs = card->u.f.rx_base;
-                    			len -= tmp;
-                		}
-
+					len -= tmp;
+				}
 				buf = skb_put(skb, len);
 				sdla_peek(&card->hw, offs, buf, len);
-#ifdef CONFIG_SANGOMA_MANAGER
-				if (management_check(skb,card))
+				udp_type = udp_pkt_type(skb, card);
+				if (udp_type == UDP_DRVSTATS_TYPE) 
 				{
 					++chan->rx_intr_DRVSTATS_request;
-				}				
-				else
-#endif				
-				if (handle_IPXWAN(skb->data,card->devname, card->wandev.enable_IPX, card->wandev.network_number)) 
+					process_udp_driver_call(
+					  UDP_PKT_FRM_NETWORK, card, skb,
+							dev, dlci, chan);
+				}
+				else if (udp_type == UDP_FPIPE_TYPE) 
+				{
+					++chan->rx_intr_FPIPE_request;
+					err = process_udp_mgmt_pkt(
+					       UDP_PKT_FRM_NETWORK, card,
+						   skb, dev, dlci, chan);
+				}
+				else if (handle_IPXWAN(skb->data, card->devname, card->wandev.enable_IPX, card->wandev.network_number)) 
 				{
 					if (card->wandev.enable_IPX) 
-					{
 						fr508_send(card, dlci, 0, skb->len, skb->data);
-					} else {
-						/* increment some statistic! */
-					}
-				}
-				else 
+				} 
+				else
 				{
-                    			/* Decapsulate packet and pass it up the
-				   	   protocol stack */
+					/* Decapsulate packet and pass it up the
+					   protocol stack */
 					skb->dev = dev;
-
 					/* remove hardware header */
-					buf = skb_pull(skb, 1); 
-				
+					buf = skb_pull(skb, 1);
 					if (!wan_type_trans(skb, dev)) 
 					{
 						/* can't decapsulate packet */
 						dev_kfree_skb(skb);
-						++chan->rx_intr_bfr_not_passed_to_stack;
- 						++chan->ifstats.rx_errors;
-		                        	++card->wandev.stats.rx_errors;
+						++chan->
+						    rx_intr_bfr_not_passed_to_stack;
+						++chan->
+						    ifstats.rx_errors;
+						++card->
+						    wandev.stats.rx_errors;
 					}
 					else
 					{
 						netif_rx(skb);
-						++ chan->rx_intr_bfr_passed_to_stack;
-						++ chan->ifstats.rx_packets;
-						++ card->wandev.stats.rx_packets;
+						++chan->rx_intr_bfr_passed_to_stack;
+						++chan->ifstats.rx_packets;
+						++card->wandev.stats.rx_packets;
 					}
-                		}
-            		}
-       		}
-
-        	/* Release buffer element and calculate a pointer to the next 
+				}
+			}
+		}
+		/* Release buffer element and calculate a pointer to the next 
 		   one */
-        	frbuf->flag = 0;
+		frbuf->flag = 0;
 		card->rxmb = ++frbuf;
-        
-		if ((void*)frbuf > card->u.f.rxmb_last)
+		if ((void *) frbuf > card->u.f.rxmb_last)
 			card->rxmb = card->u.f.rxmb_base;
-
 		/* The loop put in is temporary, that is why the break is
- 		 * placed here. (?????)
+		 * placed here. (?????)
 		 */
 		break;
-
-	        frbuf = card->rxmb;
-
-    	} while (frbuf->flag && ((++ rx_count) < 4));
+		frbuf = card->rxmb;
+	}
+	while (frbuf->flag && ((++rx_count) < 4));
 }
-
 /*============================================================================
  * Transmit interrupt handler.
  * o print a warning
@@ -1607,25 +1646,23 @@
 static void tx_intr(sdla_t * card)
 {
 	struct device *dev = card->wandev.dev;
-
 	if (card->intr_mode == BUFFER_INTR_MODE) 
-	{ 
+	{
 		for (; dev; dev = dev->slave) 
 		{
-			if ( !dev || !dev->start ) 
-			{ 
-				++ card->statistics.tx_intr_dev_not_started;
+			if (!dev || !dev->start) 
+			{
+				++card->statistics.tx_intr_dev_not_started;
 				continue;
 			}
-	
 			dev->tbusy = 0;
-			dev_tint(dev);
+			mark_bh(NET_BH);
 		}
 	}
 	else
 	{
 		dev->tbusy = 0;
-		dev_tint(dev);
+		mark_bh(NET_BH);
 	}
 }
 
@@ -1635,70 +1672,75 @@
  * o 
  * If number of spurious interrupts exceeded some limit, then ???
  */
+
 static void spur_intr(sdla_t * card)
 {
 	printk(KERN_INFO "%s: spurious interrupt!\n", card->devname);
 }
 
 /*
-  Return 0 for non-IPXWAN packet
-         1 for IPXWAN packet or IPX is not enabled!
-
-*/
+   Return 0 for non-IPXWAN packet
+   1 for IPXWAN packet or IPX is not enabled!
+ */
 
 static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number)
 {
 	int i;
-
-	if( sendpacket[1] == 0x00 &&
+	if (sendpacket[1] == 0x00 &&
 	    sendpacket[2] == 0x80 &&
 	    sendpacket[6] == 0x81 &&
-	    sendpacket[7] == 0x37) { 
-
-		if(!enable_IPX) {
+	    sendpacket[7] == 0x37) 
+	{
+		/* It's an IPX packet */
+		if (!enable_IPX) {
+			/* Return 1 so we don't pass it up the stack. */
 			return 1;
 		}
-	} else {
+	}
+	else
+	{
+		/* It's not IPX so return and pass it up the stack. */
 		return 0;
 	}
-
-	if( sendpacket[24] == 0x90 &&
-	    sendpacket[25] == 0x04)
+	if (sendpacket[24] == 0x90 &&
+	    sendpacket[25] == 0x04) 
 	{
-		if( sendpacket[10] == 0x02 &&
-		    sendpacket[42] == 0x00)
+		/* It's IPXWAN */
+		if (sendpacket[10] == 0x02 &&
+		    sendpacket[42] == 0x00) 
 		{
-			printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname);
-
-			for(i = 49; sendpacket[i] == 0x00; i += 5)
+			/* It's a timer request packet */
+			printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n", devname);
+			/* Go through the routing options and answer no to every */
+			/* option except Unnumbered RIP/SAP */
+			for (i = 49; sendpacket[i] == 0x00; i += 5) 
 			{
-				if( sendpacket[i + 4] != 0x02)
+				/* 0x02 is the option for Unnumbered RIP/SAP */
+				if (sendpacket[i + 4] != 0x02) 
 				{
 					sendpacket[i + 1] = 0;
 				}
 			}
-
-			if( sendpacket[i] == 0x04 )
-			{
+			/* Skip over the extended Node ID option */
+			if (sendpacket[i] == 0x04) 
 				i += 8;
-			}
-
-			for(; sendpacket[i] == 0x80 ;)
+			/* We also want to turn off all header compression opt. */
+			for (; sendpacket[i] == 0x80;) 
 			{
 				sendpacket[i + 1] = 0;
 				i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4;
 			}
-
+			/* Set the packet type to timer response */
 			sendpacket[42] = 0x01;
-
-			printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname);
+			printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n", devname);
 		}
-		else if( sendpacket[42] == 0x02 )
+		else if (sendpacket[42] == 0x02) 
 		{
-			printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname);
-
+			/* This is an information request packet */
+			printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n", devname);
+			/* Set the packet type to information response */
 			sendpacket[42] = 0x03;
-
+			/* Set the router name */
 			sendpacket[59] = 'F';
 			sendpacket[60] = 'P';
 			sendpacket[61] = 'I';
@@ -1706,39 +1748,35 @@
 			sendpacket[63] = 'E';
 			sendpacket[64] = '-';
 			sendpacket[65] = CVHexToAscii(network_number >> 28);
-			sendpacket[66] = CVHexToAscii((network_number & 0x0F000000)>> 24);
-			sendpacket[67] = CVHexToAscii((network_number & 0x00F00000)>> 20);
-			sendpacket[68] = CVHexToAscii((network_number & 0x000F0000)>> 16);
-			sendpacket[69] = CVHexToAscii((network_number & 0x0000F000)>> 12);
-			sendpacket[70] = CVHexToAscii((network_number & 0x00000F00)>> 8);
-			sendpacket[71] = CVHexToAscii((network_number & 0x000000F0)>> 4);
+			sendpacket[66] = CVHexToAscii((network_number & 0x0F000000) >> 24);
+			sendpacket[67] = CVHexToAscii((network_number & 0x00F00000) >> 20);
+			sendpacket[68] = CVHexToAscii((network_number & 0x000F0000) >> 16);
+			sendpacket[69] = CVHexToAscii((network_number & 0x0000F000) >> 12);
+			sendpacket[70] = CVHexToAscii((network_number & 0x00000F00) >> 8);
+			sendpacket[71] = CVHexToAscii((network_number & 0x000000F0) >> 4);
 			sendpacket[72] = CVHexToAscii(network_number & 0x0000000F);
-			for(i = 73; i < 107; i+= 1)
-			{
+			for (i = 73; i < 107; i += 1) 
 				sendpacket[i] = 0;
-			}
-
-			printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname);
+			printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n", devname);
 		}
 		else
 		{
-			printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname);
+			printk(KERN_INFO "%s: Unknown IPXWAN packet!\n", devname);
 			return 0;
 		}
-
-		sendpacket[43] = (unsigned char)(network_number >> 24);
-		sendpacket[44] = (unsigned char)((network_number & 0x00FF0000) >> 16);
-		sendpacket[45] = (unsigned char)((network_number & 0x0000FF00) >> 8);
-		sendpacket[46] = (unsigned char)(network_number & 0x000000FF);
-
+		/* Set the WNodeID to our network address */
+		sendpacket[43] = (unsigned char) (network_number >> 24);
+		sendpacket[44] = (unsigned char) ((network_number & 0x00FF0000) >> 16);
+		sendpacket[45] = (unsigned char) ((network_number & 0x0000FF00) >> 8);
+		sendpacket[46] = (unsigned char) (network_number & 0x000000FF);
 		return 1;
 	}
-
-	switch_net_numbers(sendpacket, network_number ,1);
+	/* If we get here, its an IPX-data packet so it'll get passed up the stack. */
+	/* switch the network numbers */
+	switch_net_numbers(sendpacket, network_number, 1);
 	return 0;
 }
 
-
 /****** Background Polling Routines  ****************************************/
 
 /*============================================================================
@@ -1755,136 +1793,127 @@
 
 static void wpf_poll(sdla_t * card)
 {
-	fr508_flags_t *flags;
+/*      struct device* dev = card->wandev.dev;  */
+	fr508_flags_t *flags = card->flags;
 	unsigned long host_cpu_flags;
-
 	++card->statistics.poll_entry;
-
- 	if (((jiffies - card->state_tick) < HZ) || 
-				(card->intr_mode == INTR_TEST_MODE))
+	if (((jiffies - card->state_tick) < HZ) ||
+	    (card->intr_mode == INTR_TEST_MODE))
 		return;
-
 	disable_irq(card->hw.irq);
 	++card->irq_dis_poll_count;
-
-	if (test_and_set_bit(0, (void *)&card->wandev.critical)) 
+	if (test_and_set_bit(0, (void *) &card->wandev.critical)) 
 	{
-		++ card->statistics.poll_already_critical;
+		++card->statistics.poll_already_critical;
 		save_flags(host_cpu_flags);
 		cli();
 		if ((!card->irq_dis_if_send_count) &&
-                		(!(--card->irq_dis_poll_count)))
-                	enable_irq(card->hw.irq);
+		    (!(--card->irq_dis_poll_count)))
+			enable_irq(card->hw.irq);
 		restore_flags(host_cpu_flags);
-
 		return;
 	}
-
 	card->wandev.critical = 0x11;
-
-	++ card->statistics.poll_processed;
-
-	if (flags->event) {
-		fr_mbox_t* mbox = card->mbox;
+	++card->statistics.poll_processed;
+	/* This is to be changed later ??? */
+	/*
+	   if( dev && dev->tbusy && !(flags->imask & 0x02) ) {
+	   printk(KERN_INFO "%s: Wpf_Poll: tbusy = 0x01, imask = 0x%02X\n",             card->devname, flags->imask);
+	   }
+	 */
+	if (flags->event) 
+	{
+		fr_mbox_t *mbox = card->mbox;
 		int err;
-
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		mbox->cmd.command = FR_READ_STATUS;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 		if (err)
 			fr_event(card, err, mbox);
 	}
-
 	card->wandev.critical = 0;
-
 	save_flags(host_cpu_flags);
 	cli();
-        if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count)))
+	if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count)))
 		enable_irq(card->hw.irq);
 	restore_flags(host_cpu_flags);
-
 	card->state_tick = jiffies;
 }
 
-
 /****** Frame Relay Firmware-Specific Functions *****************************/
 
 /*============================================================================
  * Read firmware code version.
  * o fill string str with firmware version info. 
  */
+
 static int fr_read_version(sdla_t * card, char *str)
 {
 	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err;
-
-	do {
+	do 
+	{
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		mbox->cmd.command = FR_READ_CODE_VERSION;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	}
-	while (err && retry-- && fr_event(card, err, mbox));
-	if (!err && str) {
+	} while (err && retry-- && fr_event(card, err, mbox));
+	
+	if (!err && str) 
+	{
 		int len = mbox->cmd.length;
-
 		memcpy(str, mbox->data, len);
 		str[len] = '\0';
 	}
 	return err;
 }
-
 /*============================================================================
  * Set global configuration.
  */
+ 
 static int fr_configure(sdla_t * card, fr_conf_t * conf)
 {
 	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int dlci_num = card->u.f.dlci_num;
 	int err, i;
-
-	do {
+	do 
+	{
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		memcpy(mbox->data, conf, sizeof(fr_conf_t));
-
-		if (dlci_num) for (i = 0; i < dlci_num; ++i)
-			((fr_conf_t*)mbox->data)->dlci[i] = 
-					card->u.f.node_dlci[i]; 
-		
+		if (dlci_num)
+			for (i = 0; i < dlci_num; ++i)
+				((fr_conf_t *) mbox->data)->dlci[i] =
+				    card->u.f.node_dlci[i];
 		mbox->cmd.command = FR_SET_CONFIG;
 		mbox->cmd.length =
 		    sizeof(fr_conf_t) + dlci_num * sizeof(short);
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	}
 	while (err && retry-- && fr_event(card, err, mbox));
+	
 	return err;
 }
-
 /*============================================================================
  * Set DLCI configuration.
  */
-static int fr_dlci_configure (sdla_t* card, fr_dlc_conf_t *conf, unsigned dlci)
+static int fr_dlci_configure(sdla_t * card, fr_dlc_conf_t * conf, unsigned dlci)
 {
-	fr_mbox_t* mbox = card->mbox;
+	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err;
-
 	do
 	{
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		memcpy(mbox->data, conf, sizeof(fr_dlc_conf_t));
-		mbox->cmd.dlci = (unsigned short) dlci; 
+		mbox->cmd.dlci = (unsigned short) dlci;
 		mbox->cmd.command = FR_SET_CONFIG;
 		mbox->cmd.length = 0x0E;
-
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	
-	} while (err && retry--);
-	
+	}
+	while (err && retry--);
 	return err;
 }
-
 /*============================================================================
  * Set interrupt mode.
  */
@@ -1893,19 +1922,20 @@
 	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err;
-
-	do {
+	do
+	{
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
-		if (card->hw.fwid == SFID_FR502) {
+		if (card->hw.fwid == SFID_FR502) 
+		{
 			fr502_intr_ctl_t *ictl = (void *) mbox->data;
-
 			memset(ictl, 0, sizeof(fr502_intr_ctl_t));
 			ictl->mode = mode;
 			ictl->tx_len = mtu;
 			mbox->cmd.length = sizeof(fr502_intr_ctl_t);
-		} else {
+		}
+		else
+		{
 			fr508_intr_ctl_t *ictl = (void *) mbox->data;
-
 			memset(ictl, 0, sizeof(fr508_intr_ctl_t));
 			ictl->mode = mode;
 			ictl->tx_len = mtu;
@@ -1916,9 +1946,9 @@
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	}
 	while (err && retry-- && fr_event(card, err, mbox));
+	
 	return err;
 }
-
 /*============================================================================
  * Enable communications.
  */
@@ -1927,16 +1957,16 @@
 	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err;
-
-	do {
+	do 
+	{
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		mbox->cmd.command = FR_COMM_ENABLE;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	}
 	while (err && retry-- && fr_event(card, err, mbox));
+	
 	return err;
 }
-
 /*============================================================================
  * Disable communications. 
  */
@@ -1945,16 +1975,16 @@
 	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err;
-
-	do {
+	do
+	{
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		mbox->cmd.command = FR_COMM_DISABLE;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	}
 	while (err && retry-- && fr_event(card, err, mbox));
+	
 	return err;
 }
-
 /*============================================================================
  * Get communications error statistics. 
  */
@@ -1963,26 +1993,26 @@
 	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err;
-
-	do {
+	
+	do
+	{
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		mbox->cmd.command = FR_READ_ERROR_STATS;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	}
 	while (err && retry-- && fr_event(card, err, mbox));
-
-	if (!err) {
-		fr_comm_stat_t* stats = (void*)mbox->data;
-
-		card->wandev.stats.rx_over_errors    = stats->rx_overruns;
-		card->wandev.stats.rx_crc_errors     = stats->rx_bad_crc;
-		card->wandev.stats.rx_missed_errors  = stats->rx_aborts;
-		card->wandev.stats.rx_length_errors  = stats->rx_too_long;
+	
+	if (!err) 
+	{
+		fr_comm_stat_t *stats = (void *) mbox->data;
+		card->wandev.stats.rx_over_errors = stats->rx_overruns;
+		card->wandev.stats.rx_crc_errors = stats->rx_bad_crc;
+		card->wandev.stats.rx_missed_errors = stats->rx_aborts;
+		card->wandev.stats.rx_length_errors = stats->rx_too_long;
 		card->wandev.stats.tx_aborted_errors = stats->tx_aborts;
 	}
 	return err;
 }
-
 /*============================================================================
  * Get statistics. 
  */
@@ -1991,74 +2021,68 @@
 	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err;
-
-	do {
+	do 
+	{
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		mbox->cmd.command = FR_READ_STATISTICS;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	}
 	while (err && retry-- && fr_event(card, err, mbox));
-
-	if (!err) {
+	
+	if (!err) 
+	{
 		fr_link_stat_t *stats = (void *) mbox->data;
-
 		card->wandev.stats.rx_frame_errors = stats->rx_bad_format;
-		card->wandev.stats.rx_dropped =
-		    stats->rx_dropped + stats->rx_dropped2
-		    ;
+		card->wandev.stats.rx_dropped = stats->rx_dropped + stats->rx_dropped2;
 	}
 	return err;
 }
-
 /*============================================================================
- * Add DLCI(s) (Access Node only!). 
+ * Add DLCI(s) (Access Node only!).
+ * This routine will perform the ADD_DLCIs command for the specified DLCI.
  */
- 
 static int fr_add_dlci(sdla_t * card, int dlci, int num)
 {
 	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err, i;
-
-	do {
+	do 
+	{
 		unsigned short *dlci_list = (void *) mbox->data;
-
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		for (i = 0; i < num; ++i)
 			dlci_list[i] = card->u.f.node_dlci[i];
-
 		mbox->cmd.length = num * sizeof(short);
 		mbox->cmd.command = FR_ADD_DLCI;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	}
 	while (err && retry-- && fr_event(card, err, mbox));
+	
 	return err;
 }
-
 /*============================================================================
  * Activate DLCI(s) (Access Node only!). 
+ * This routine will perform the ACTIVATE_DLCIs command with a list of DLCIs. 
  */
 static int fr_activate_dlci(sdla_t * card, int dlci, int num)
 {
 	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err, i;
-
-	do {
+	do
+	{
 		unsigned short *dlci_list = (void *) mbox->data;
-
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		for (i = 0; i < num; ++i)
 			dlci_list[i] = card->u.f.node_dlci[i];
-
 		mbox->cmd.length = num * sizeof(short);
 		mbox->cmd.command = FR_ACTIVATE_DLCI;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	}
 	while (err && retry-- && fr_event(card, err, mbox));
+	
 	return err;
 }
-
 /*============================================================================
  * Issue in-channel signalling frame. 
  */
@@ -2067,8 +2091,8 @@
 	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err;
-
-	do {
+	do
+	{
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		mbox->data[0] = isf;
 		mbox->cmd.length = 1;
@@ -2076,9 +2100,9 @@
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	}
 	while (err && retry-- && fr_event(card, err, mbox));
+	
 	return err;
 }
-
 /*============================================================================
  * Send a frame (S502 version). 
  */
@@ -2087,8 +2111,9 @@
 	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err;
-
-	do {
+	
+	do 
+	{
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		memcpy(mbox->data, buf, len);
 		mbox->cmd.dlci = dlci;
@@ -2098,9 +2123,9 @@
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	}
 	while (err && retry-- && fr_event(card, err, mbox));
+	
 	return err;
 }
-
 /*============================================================================
  * Send a frame (S508 version). 
  */
@@ -2109,8 +2134,9 @@
 	fr_mbox_t *mbox = card->mbox;
 	int retry = MAX_CMD_RETRY;
 	int err;
-
-	do {
+	
+	do
+	{
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		mbox->cmd.dlci = dlci;
 		mbox->cmd.attr = attr;
@@ -2119,8 +2145,9 @@
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	}
 	while (err && retry-- && fr_event(card, err, mbox));
-
-	if (!err) {
+	
+	if (!err) 
+	{
 		fr_buf_ctl_t *frbuf = (void *) (*(unsigned long *) mbox->data -
 					FR_MB_VECTOR + card->hw.dpmbase);
 		sdla_poke(&card->hw, frbuf->offset, buf, len);
@@ -2138,53 +2165,47 @@
  *
  * Return zero if previous command has to be cancelled.
  */
+
 static int fr_event(sdla_t * card, int event, fr_mbox_t * mbox)
 {
-	fr508_flags_t* flags = card->flags;
+	fr508_flags_t *flags = card->flags;
 	char *ptr = &flags->iflag;
 	int i;
-
 	switch (event) 
 	{
 		case FRRES_MODEM_FAILURE:
 			return fr_modem_failure(card, mbox);
-
 		case FRRES_CHANNEL_DOWN:
 			wanpipe_set_state(card, WAN_DISCONNECTED);
 			return 1;
-
 		case FRRES_CHANNEL_UP:
 			wanpipe_set_state(card, WAN_CONNECTED);
 			return 1;
-
 		case FRRES_DLCI_CHANGE:
 			return fr_dlci_change(card, mbox);
-
 		case FRRES_DLCI_MISMATCH:
-			printk(KERN_INFO "%s: DLCI list mismatch!\n", card->devname);
+			printk(KERN_INFO "%s: DLCI list mismatch!\n",
+			       card->devname);
 			return 1;
-
 		case CMD_TIMEOUT:
 			printk(KERN_ERR "%s: command 0x%02X timed out!\n",
 			       card->devname, mbox->cmd.command);
-			printk(KERN_INFO "%s: ID Bytes = ",card->devname);
- 	    		for(i = 0; i < 8; i ++)
+			printk(KERN_INFO "%s: ID Bytes = ", card->devname);
+			for (i = 0; i < 8; i++)
 				printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i));
-	   	 	printk(KERN_INFO "\n");	
-            		break;
-
+			printk(KERN_INFO "\n");
+			break;
 		case FRRES_DLCI_INACTIVE:
-			printk(KERN_ERR "%s: DLCI %u is inactive!\n", 
-				card->devname, mbox->cmd.dlci);
+			printk(KERN_ERR "%s: DLCI %u is inactive!\n",
+			       card->devname, mbox->cmd.dlci);
 			break;
- 
 		case FRRES_CIR_OVERFLOW:
 			break;
 		case FRRES_BUFFER_OVERFLOW:
-			break; 
+			break;
 		default:
-			printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n",
-				card->devname, mbox->cmd.command, event);
+			printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n"
+			       ,card->devname, mbox->cmd.command, event);
 	}
 	return 0;
 }
@@ -2206,7 +2227,6 @@
 	}
 	return 1;
 }
-
 /*============================================================================
  * Handle DLCI status change.
  *
@@ -2218,18 +2238,18 @@
 	int cnt = mbox->cmd.length / sizeof(dlci_status_t);
 	fr_dlc_conf_t cfg;
 	fr_channel_t *chan;
-	struct device* dev2;
-	
-	for (; cnt; --cnt, ++status) {
+	struct device *dev2;
+	for (; cnt; --cnt, ++status) 
+	{
 		unsigned short dlci = status->dlci;
 		struct device *dev = find_channel(card, dlci);
-
-		if (dev == NULL)
+		if (dev == NULL) 
 		{
-			printk(KERN_INFO "%s: CPE contains unconfigured DLCI= %d\n", 
-				card->devname, dlci); 	
+			printk(KERN_INFO
+			       "%s: CPE contains unconfigured DLCI= %d\n",
+			       card->devname, dlci);
 		}
-		else
+		else 
 		{
 			if (status->state & 0x01) 
 			{
@@ -2242,68 +2262,56 @@
 			else if (status->state & 0x02) 
 			{
 				printk(KERN_INFO
-					"%s: DLCI %u becomes active!\n",
-					card->devname, dlci);
+				       "%s: DLCI %u becomes active!\n",
+				       card->devname, dlci);
 				chan = dev->priv;
 				/* This flag is used for configuring specific 
 				   DLCI(s) when they become active.
-			 	*/ 
+				 */
 				chan->dlci_configured = DLCI_CONFIG_PENDING;
-			
 				if (dev && dev->start)
 					set_chan_state(dev, WAN_CONNECTED);
 			}
 		}
 	}
-	
-	for (dev2 =card->wandev.dev; dev2; dev2 = dev2->slave)
-	{	
+	for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) 
+	{
 		chan = dev2->priv;
-		
-		if (chan->dlci_configured == DLCI_CONFIG_PENDING)
+		if (chan->dlci_configured == DLCI_CONFIG_PENDING) 
 		{
 			memset(&cfg, 0, sizeof(cfg));
-	
-			if ( chan->cir_status == CIR_DISABLED) 
+			if (chan->cir_status == CIR_DISABLED) 
 			{
-				cfg.cir_fwd = cfg.cir_bwd  = 16;
+				cfg.cir_fwd = cfg.cir_bwd = 16;
 				cfg.bc_fwd = cfg.bc_bwd = 16;
-				cfg.conf_flags = 0x0001;	
-				printk(KERN_INFO "%s: CIR Disabled for %s\n", 
-					card->devname, chan->name);	
-			}
-			else if (chan->cir_status == CIR_ENABLED) 
-			{	
+				cfg.conf_flags = 0x0001;
+				printk(KERN_INFO "%s: CIR Disabled for %s\n",
+				       card->devname, chan->name);
+			} else if (chan->cir_status == CIR_ENABLED) {
 				cfg.cir_fwd = cfg.cir_bwd = chan->cir;
-				cfg.bc_fwd  = cfg.bc_bwd  = chan->bc;
-				cfg.be_fwd  = cfg.be_bwd  = chan->be;
+				cfg.bc_fwd = cfg.bc_bwd = chan->bc;
+				cfg.be_fwd = cfg.be_bwd = chan->be;
 				cfg.conf_flags = 0x0000;
 				printk(KERN_INFO "%s: CIR Enabled for %s\n",
-					card->devname, chan->name);
-			
+				       card->devname, chan->name);
 			}
-			
-			if (fr_dlci_configure( card, &cfg , chan->dlci))
+			if (fr_dlci_configure(card, &cfg, chan->dlci)) 
 			{
-				printk(KERN_INFO 
-					"%s: DLCI Configure failed for %d\n",
-					card->devname, chan->dlci);
-				return 1;	
+				printk(KERN_INFO
+				    "%s: DLCI Configure failed for %d\n",
+				       card->devname, chan->dlci);
+				return 1;
 			}
-			
 			chan->dlci_configured = DLCI_CONFIGURED;
-			
-			/*
-			 *	Read the interface byte mapping into the channel 
-			 *	structure.
+			/* Read the interface byte mapping into the channel 
+			   structure.
 			 */
 			if (card->intr_mode == DLCI_LIST_INTR_MODE)
-				read_DLCI_IB_mapping( card, chan );
-		}		
+				read_DLCI_IB_mapping(card, chan);
+		}
 	}
 	return 1;
 }
-
 /******* Miscellaneous ******************************************************/
 
 /*============================================================================
@@ -2318,17 +2326,18 @@
 	int err;
 	int dlci_found = 0;
 
-	do {
+	do 
+	{
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		mbox->cmd.command = FR_LIST_ACTIVE_DLCI;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	}
 	while (err && retry-- && fr_event(card, err, mbox));
 
-	if (!err) {
+	if (!err) 
+	{
 		unsigned short *list = (void *) mbox->data;
 		int cnt = mbox->cmd.length / sizeof(short);
-
 		for (; cnt; --cnt, ++list) 
 		{
 			if (*list == chan->dlci) 
@@ -2338,14 +2347,13 @@
 				break;
 			}
 		}
-
-		if(!dlci_found) 
-			printk(KERN_INFO "%s: DLCI %u is inactive\n", 
-				card->devname, chan->dlci);
+		if (!dlci_found)
+			printk(KERN_INFO "%s: DLCI %u is inactive\n",
+			       card->devname, chan->dlci);
 	}
+	
 	return err;
 }
-
 /*============================================================================
  * Set channel state.
  */
@@ -2354,27 +2362,27 @@
 	fr_channel_t *chan = dev->priv;
 	sdla_t *card = chan->card;
 	unsigned long flags;
-
+	
 	save_flags(flags);
 	cli();
-
+	
 	if (chan->state != state) 
 	{
 		switch (state) 
 		{
 			case WAN_CONNECTED:
 				printk(KERN_INFO "%s: interface %s connected!\n"
-					, card->devname, dev->name);
+				       ,card->devname, dev->name);
 				break;
 			case WAN_CONNECTING:
-				printk(KERN_INFO 
-					"%s: interface %s connecting...\n",
-					card->devname, dev->name);
+				printk(KERN_INFO
+				       "%s: interface %s connecting...\n",
+				       card->devname, dev->name);
 				break;
 			case WAN_DISCONNECTED:
-				printk (KERN_INFO 
-					"%s: interface %s disconnected!\n",
-					card->devname, dev->name);
+				printk(KERN_INFO
+				       "%s: interface %s disconnected!\n",
+				       card->devname, dev->name);
 				break;
 		}
 		chan->state = state;
@@ -2389,14 +2397,11 @@
 static struct device *find_channel(sdla_t * card, unsigned dlci)
 {
 	struct device *dev;
-
 	for (dev = card->wandev.dev; dev; dev = dev->slave)
 		if (((fr_channel_t *) dev->priv)->dlci == dlci)
-			break
-			    ;
+			break;
 	return dev;
 }
-
 /*============================================================================
  * Check to see if a frame can be sent. If no transmit buffers available,
  * enable transmit interrupts.
@@ -2405,18 +2410,18 @@
  *		0 - no buffers available
  */
 
-static int is_tx_ready(sdla_t * card)
+static int is_tx_ready(sdla_t * card, fr_channel_t * chan)
 {
 	if (card->hw.fwid == SFID_FR508) 
 	{
 		unsigned char sb = inb(card->hw.port);
-
 		if (sb & 0x02)
 			return 1;
-	} else {
-		fr502_flags_t* flags = card->flags;
-
-		if (flags->tx_ready) 
+	}
+	else 
+	{
+		fr502_flags_t *flags = card->flags;
+		if (flags->tx_ready)
 			return 1;
 		flags->imask |= 0x02;
 	}
@@ -2430,7 +2435,6 @@
 static unsigned int dec_to_uint(unsigned char *str, int len)
 {
 	unsigned val;
-
 	if (!len)
 		len = strlen(str);
 	for (val = 0; len && is_digit(*str); ++str, --len)
@@ -2439,109 +2443,677 @@
 }
 
 /*==============================================================================
+ * Process UDP call of type FPIPE8ND
+ */
+
+static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, int dlci, fr_channel_t * chan)
+{
+	int c_retry = MAX_CMD_RETRY;
+	unsigned char *data;
+	unsigned char *buf;
+	unsigned char buf2[5];
+	unsigned int loops, frames, len;
+	unsigned long data_ptr;
+	unsigned short real_len, buffer_length;
+	struct sk_buff *new_skb;
+	unsigned char *sendpacket;
+	fr_mbox_t *mbox = card->mbox;
+	int err;
+	struct timeval tv;
+	int udp_mgmt_req_valid = 1;
+	sendpacket = skb->data;
+	memcpy(&buf2, &card->wandev.udp_port, 2);
+	if ((data = kmalloc(2000, GFP_ATOMIC)) == NULL) 
+	{
+		printk(KERN_INFO
+		       "%s: Error allocating memory for UDP management cmnd 0x%02X",
+		       card->devname, data[47]);
+		++chan->UDP_FPIPE_mgmt_kmalloc_err;
+		return 1;
+	}
+	memcpy(data, sendpacket, skb->len);
+	switch (data[47]) 
+	{
+		/* FPIPE_ENABLE_TRACE */
+		case 0x41:
+		/* FPIPE_DISABLE_TRACE */
+		case 0x42:
+		/* FPIPE_GET_TRACE_INFO */
+		case 0x43:
+		/* SET FT1 MODE */
+		case 0x81:
+			if (udp_pkt_src == UDP_PKT_FRM_NETWORK) 
+			{
+				++chan->UDP_FPIPE_mgmt_direction_err;
+				udp_mgmt_req_valid = 0;
+				break;
+			}
+		/* FPIPE_FT1_READ_STATUS */
+		case 0x44:
+		/* FT1 MONITOR STATUS */
+		case 0x80:
+			if (card->hw.fwid != SFID_FR508) 
+			{
+				++chan->UDP_FPIPE_mgmt_adptr_type_err;
+				udp_mgmt_req_valid = 0;
+			}
+			break;
+		default:
+			break;
+	}
+	if (!udp_mgmt_req_valid) 
+	{
+		/* set length to 0 */
+		data[48] = data[49] = 0;
+		/* set return code */
+		data[50] = (card->hw.fwid != SFID_FR508) ? 0x1F : 0xCD;
+	}
+	else
+	{
+		switch (data[47]) 
+		{
+			/* FPIPE_ENABLE_TRACE */
+			case 0x41:
+				if (!TracingEnabled) 
+				{
+					do 
+					{
+						/* SET_TRACE_CONFIGURATION */
+						mbox->cmd.command = 0x60;
+						mbox->cmd.length = 1;
+						mbox->cmd.dlci = 0x00;
+						mbox->data[0] = 0x37;
+						err = sdla_exec(mbox) ?
+							mbox->cmd.result : CMD_TIMEOUT;
+					}
+					while (err && c_retry-- && fr_event(card, err, mbox));
+	
+					if (err) 
+					{
+						TracingEnabled = 0;
+						/* set the return code */
+						data[50] = mbox->cmd.result;
+						mbox->cmd.length = 0;
+						break;
+					}
+					/* get num_frames */
+					sdla_peek(&card->hw, 0x9000, &num_frames, 2);
+					sdla_peek(&card->hw, 0x9002, &curr_trace_addr,4);
+					start_trace_addr = curr_trace_addr;
+					/* MAX_SEND_BUFFER_SIZE - 
+					 * sizeof(UDP_MGMT_PACKET) - 41 */
+					available_buffer_space = 1926;
+					/* set return code */
+					data[50] = 0;
+				} 
+				else
+				{
+					/* set return code to line trace already 
+					   enabled */
+					data[50] = 1;
+				}
+				mbox->cmd.length = 0;
+				TracingEnabled = 1;
+				break;
+			/* FPIPE_DISABLE_TRACE */
+			case 0x42:
+				if (TracingEnabled) 
+				{
+					do 
+					{
+						/* SET_TRACE_CONFIGURATION */
+						mbox->cmd.command = 0x60;
+						mbox->cmd.length = 1;
+						mbox->cmd.dlci = 0x00;
+						mbox->data[0] = 0x36;
+						err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+					}
+					while (err && c_retry-- && fr_event(card, err, mbox));
+				}
+				/* set return code */
+				data[50] = 0;
+				mbox->cmd.length = 0;
+				TracingEnabled = 0;
+				break;
+			/* FPIPE_GET_TRACE_INFO */
+			case 0x43:
+				/* Line trace cannot be performed on the 502 */
+				if (!TracingEnabled) 
+				{
+					/* set return code */
+					data[50] = 1;
+					mbox->cmd.length = 0;
+					break;
+				}
+				buffer_length = 0;
+				loops = (num_frames < 20) ? num_frames : 20;
+				for (frames = 0; frames < loops; frames += 1) 
+				{
+					sdla_peek(&card->hw, curr_trace_addr, &buf2, 1);
+					/* no data on board so exit */
+					if (buf2[0] == 0x00)
+						break;
+					/* 1+sizeof(FRAME_DATA) = 9 */
+					if ((available_buffer_space - buffer_length) < 9) 
+					{
+						/* indicate we have more frames on board
+						   and exit */
+						data[62] |= 0x02;
+						break;
+					}
+					/* get frame status */
+					sdla_peek(&card->hw, curr_trace_addr + 0x05, &data[62 + buffer_length], 1);
+					/* get time stamp */
+					sdla_peek(&card->hw, curr_trace_addr + 0x06, &data[66 + buffer_length], 2);
+					/* get frame length */
+					sdla_peek(&card->hw, curr_trace_addr + 0x01, &data[64 + buffer_length], 2);
+					/* get pointer to real data */
+					sdla_peek(&card->hw, curr_trace_addr + 0x0C,&data_ptr, 4);
+					/* see if we can fit the frame into the user buffer */
+					memcpy(&real_len, &data[64 + buffer_length], 2);
+					if (data_ptr == 0 || real_len + 8 > available_buffer_space) 
+					{
+						data[63 + buffer_length] = 0x00;
+					}
+					else
+					{
+						/* we can take it next time */
+						if (available_buffer_space - buffer_length < real_len + 8) 
+						{
+							data[62] |= 0x02;
+							break;
+						}
+						/* ok, get the frame */
+						data[63 + buffer_length] = 0x01;
+						/* get the data */
+						sdla_peek(&card->hw, data_ptr, &data[68 + buffer_length], real_len);
+						/* zero the opp flag to show we got the frame */
+						buf2[0] = 0x00;
+						sdla_poke(&card->hw, curr_trace_addr, &buf2, 1);
+						/* now move onto the next frame */
+						curr_trace_addr += 16;
+						/* check if we passed the last address */
+						if (curr_trace_addr >= (start_trace_addr + num_frames * 16)) 
+							curr_trace_addr = start_trace_addr;
+						/* update buffer length and make sure 
+						   its even */
+						if (data[63 + buffer_length] == 0x01) 
+							buffer_length += real_len - 1;
+						/* for the header */
+						buffer_length += 8;
+						if (buffer_length & 0x0001)
+							buffer_length += 1;
+					}
+				}
+				/* ok now set the total number of frames passed in the 
+				   high 5 bits */
+				data[62] = (frames << 3) | data[62];
+				/* set the data length */
+				mbox->cmd.length = buffer_length;
+				memcpy(&data[48], &buffer_length, 2);
+				data[50] = 0;
+				break;
+			/* FPIPE_FT1_READ_STATUS */
+			case 0x44:
+				sdla_peek(&card->hw, 0xF020, &data[62], 2);
+				data[48] = 2;
+				data[49] = 0;
+				data[50] = 0;
+				mbox->cmd.length = 2;
+				break;
+			/* FPIPE_FLUSH_DRIVER_STATS */
+			case 0x48:
+				init_chan_statistics(chan);
+				init_global_statistics(card);
+				mbox->cmd.length = 0;
+				break;
+			case 0x49:
+				do_gettimeofday(&tv);
+				chan->router_up_time = tv.tv_sec - chan->router_start_time;
+				*(unsigned long *) &data[62] = chan->router_up_time;
+				mbox->cmd.length = 4;
+				break;
+			/* FPIPE_KILL_BOARD */
+			case 0x50:
+				break;
+			/* FT1 MONITOR STATUS */
+			case 0x80:
+				if (data[62] == 1) 
+				{
+					if (rCount++ != 0) 
+					{
+						data[50] = 0;
+						mbox->cmd.length = 1;
+						break;
+					}
+				}
+				/* Disable FT1 MONITOR STATUS */
+				if (data[62] == 0) 
+				{
+					if (--rCount != 0) 
+					{
+						data[50] = 0;
+						mbox->cmd.length = 1;
+						break;
+					}
+				}
+			default:
+				do 
+				{
+					memcpy(&mbox->cmd, &sendpacket[47], sizeof(fr_cmd_t));
+					if (mbox->cmd.length) 
+						memcpy(&mbox->data, &sendpacket[62],mbox->cmd.length);
+					err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+				}
+				while (err && c_retry-- && fr_event(card, err, mbox));
+			
+				if (!err) 
+				{
+					++chan->UDP_FPIPE_mgmt_adptr_cmnd_OK;
+					memcpy(data, sendpacket, skb->len);
+					memcpy(&data[47], &mbox->cmd, sizeof(fr_cmd_t));
+					if (mbox->cmd.length) 
+					{
+						memcpy(&data[62], &mbox->data,mbox->cmd.length);
+					}
+				}
+				else
+				{
+					++chan->UDP_FPIPE_mgmt_adptr_cmnd_timeout;
+				}
+			}
+	}
+	/* Fill UDP TTL */
+	data[10] = card->wandev.ttl;
+	len = reply_udp(data, mbox->cmd.length);
+	if (udp_pkt_src == UDP_PKT_FRM_NETWORK) 
+	{
+		err = fr508_send(card, dlci, 0, len, data);
+		if (err)
+			++chan->UDP_FPIPE_mgmt_adptr_send_passed;
+		else
+			++chan->UDP_FPIPE_mgmt_adptr_send_failed;
+		dev_kfree_skb(skb);
+	}
+	else 
+	{
+		/* Allocate socket buffer */
+		if ((new_skb = dev_alloc_skb(len)) != NULL) 
+		{
+			/* copy data into new_skb */
+			buf = skb_put(new_skb, len);
+			memcpy(buf, data, len);
+			/* Decapsulate packet and pass it up the protocol 
+			   stack */
+			new_skb->dev = dev;
+			buf = skb_pull(new_skb, 1);	/* remove hardware header */
+			if (!wan_type_trans(new_skb, dev)) 
+			{
+				++chan->UDP_FPIPE_mgmt_not_passed_to_stack;
+				/* can't decapsulate packet */
+				dev_kfree_skb(new_skb);
+			}
+			else 
+			{
+				++chan->UDP_FPIPE_mgmt_passed_to_stack;
+				netif_rx(new_skb);
+			}
+		}
+		else 
+		{
+			++chan->UDP_FPIPE_mgmt_no_socket;
+			printk(KERN_INFO
+			       "%s: UDP mgmt cmnd, no socket buffers available!\n",
+			       card->devname);
+		}
+	}
+	kfree(data);
+	return 0;
+}
+/*==============================================================================
  * Perform the Interrupt Test by running the READ_CODE_VERSION command MAX_INTR_
  * TEST_COUNTER times.
  */
-static int intr_test( sdla_t* card )
+ 
+static int intr_test(sdla_t * card)
 {
-	fr_mbox_t* mb = card->mbox;
-	int err,i;
-
-	/*
-	 *	 The critical flag is unset here because we want to get into the
-	 *	ISR without the flag already set. The If_open sets the flag.
+	fr_mbox_t *mb = card->mbox;
+	int err, i;
+	/* The critical flag is unset here because we want to get into the
+	   ISR without the flag already set. The If_open sets the flag.
 	 */
-	 
 	card->wandev.critical = 0;
-
-	err = fr_set_intr_mode( card, 0x08, card->wandev.mtu );
-	
+	err = fr_set_intr_mode(card, 0x08, card->wandev.mtu);
 	if (err == CMD_OK) 
 	{
-		for ( i = 0; i < MAX_INTR_TEST_COUNTER; i++ ) 
+		for (i = 0; i < MAX_INTR_TEST_COUNTER; i++) 
 		{
- 			/* Run command READ_CODE_VERSION */
+			/* Run command READ_CODE_VERSION */
 			memset(&mb->cmd, 0, sizeof(fr_cmd_t));
-			mb->cmd.length  = 0;
+			mb->cmd.length = 0;
 			mb->cmd.command = 0x40;
 			err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
-			
-			if (err != CMD_OK) 
+			if (err != CMD_OK)
 				fr_event(card, err, mb);
 		}
+	} 
+	else 
+	{
+		return err;
 	}
-	else
-		return err;	
-
-	err = fr_set_intr_mode( card, 0, card->wandev.mtu );
-
-	if( err != CMD_OK ) 
+	err = fr_set_intr_mode(card, 0, card->wandev.mtu);
+	if (err != CMD_OK)
 		return err;
-
 	card->wandev.critical = 1;
 	return 0;
 }
+/*============================================================================
+ * Process UDP call of type DRVSTATS.  
+ */
+static int process_udp_driver_call(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, int dlci, fr_channel_t * chan)
+{
+	int c_retry = MAX_CMD_RETRY;
+	unsigned char *sendpacket;
+	unsigned char buf2[5];
+	unsigned char *data;
+	unsigned char *buf;
+	unsigned int len;
+	fr_mbox_t *mbox = card->mbox;
+	struct sk_buff *new_skb;
+	int err;
+	sendpacket = skb->data;
+	memcpy(&buf2, &card->wandev.udp_port, 2);
+	if ((data = kmalloc(2000, GFP_ATOMIC)) == NULL) 
+	{
+		printk(KERN_INFO
+		       "%s: Error allocating memory for UDP DRIVER STATS cmnd0x%02X"
+		       ,card->devname, data[45]);
+		++chan->UDP_DRVSTATS_mgmt_kmalloc_err;
+		return 1;
+	}
+	memcpy(data, sendpacket, skb->len);
+	switch (data[47]) 
+	{
+		case 0x45:
+			*(unsigned long *) &data[62] = chan->if_send_entry;
+			*(unsigned long *) &data[66] = chan->if_send_skb_null;
+			*(unsigned long *) &data[70] = chan->if_send_broadcast;
+			*(unsigned long *) &data[74] = chan->if_send_multicast;
+			*(unsigned long *) &data[78] = chan->if_send_critical_ISR;
+			*(unsigned long *) &data[82] = chan->if_send_critical_non_ISR;
+			*(unsigned long *) &data[86] = chan->if_send_busy;
+			*(unsigned long *) &data[90] = chan->if_send_busy_timeout;
+			*(unsigned long *) &data[94] = chan->if_send_DRVSTATS_request;
+			*(unsigned long *) &data[98] = chan->if_send_FPIPE_request;
+			*(unsigned long *) &data[102] = chan->if_send_wan_disconnected;
+			*(unsigned long *) &data[106] = chan->if_send_dlci_disconnected;
+			*(unsigned long *) &data[110] = chan->if_send_no_bfrs;
+			*(unsigned long *) &data[114] = chan->if_send_adptr_bfrs_full;
+			*(unsigned long *) &data[118] = chan->if_send_bfrs_passed_to_adptr;
+			*(unsigned long *) &data[120] = card->irq_dis_if_send_count;
+			mbox->cmd.length = 62;
+			break;
+		case 0x46:
+			*(unsigned long *) &data[62] = card->statistics.isr_entry;
+			*(unsigned long *) &data[66] = card->statistics.isr_already_critical;
+			*(unsigned long *) &data[70] = card->statistics.isr_rx;
+			*(unsigned long *) &data[74] = card->statistics.isr_tx;
+			*(unsigned long *) &data[78] = card->statistics.isr_intr_test;
+			*(unsigned long *) &data[82] = card->statistics.isr_spurious;
+			*(unsigned long *) &data[86] = card->statistics.isr_enable_tx_int;
+			*(unsigned long *) &data[90] = card->statistics.tx_intr_dev_not_started;
+			*(unsigned long *) &data[94] = card->statistics.rx_intr_corrupt_rx_bfr;
+			*(unsigned long *) &data[98] = card->statistics.rx_intr_on_orphaned_DLCI;
+			*(unsigned long *) &data[102] = chan->rx_intr_no_socket;
+			*(unsigned long *) &data[106] = chan->rx_intr_dev_not_started;
+			*(unsigned long *) &data[110] = chan->rx_intr_DRVSTATS_request;
+			*(unsigned long *) &data[114] = chan->rx_intr_FPIPE_request;
+			*(unsigned long *) &data[118] = chan->rx_intr_bfr_not_passed_to_stack;
+			*(unsigned long *) &data[122] = chan->rx_intr_bfr_passed_to_stack;
+			mbox->cmd.length = 64;
+			break;
+		case 0x47:
+			*(unsigned long *) &data[62] = chan->UDP_FPIPE_mgmt_kmalloc_err;
+			*(unsigned long *) &data[66] = chan->UDP_FPIPE_mgmt_adptr_type_err;
+			*(unsigned long *) &data[70] = chan->UDP_FPIPE_mgmt_direction_err;
+			*(unsigned long *) &data[74] = chan->UDP_FPIPE_mgmt_adptr_cmnd_timeout;
+			*(unsigned long *) &data[78] = chan->UDP_FPIPE_mgmt_adptr_cmnd_OK;
+			*(unsigned long *) &data[82] = chan->UDP_FPIPE_mgmt_adptr_send_passed;
+			*(unsigned long *) &data[86] = chan->UDP_FPIPE_mgmt_adptr_send_failed;
+			*(unsigned long *) &data[90] = chan->UDP_FPIPE_mgmt_no_socket;
+			*(unsigned long *) &data[94] = chan->UDP_FPIPE_mgmt_not_passed_to_stack;
+			*(unsigned long *) &data[98] = chan->UDP_FPIPE_mgmt_passed_to_stack;
+			*(unsigned long *) &data[102] = chan->UDP_DRVSTATS_mgmt_kmalloc_err;
+			*(unsigned long *) &data[106] = chan->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout;
+			*(unsigned long *) &data[110] = chan->UDP_DRVSTATS_mgmt_adptr_cmnd_OK;
+			*(unsigned long *) &data[114] = chan->UDP_DRVSTATS_mgmt_adptr_send_passed;
+			*(unsigned long *) &data[118] = chan->UDP_DRVSTATS_mgmt_adptr_send_failed;
+			*(unsigned long *) &data[122] = chan->UDP_DRVSTATS_mgmt_no_socket;
+			*(unsigned long *) &data[126] = chan->UDP_DRVSTATS_mgmt_not_passed_to_stack;
+			*(unsigned long *) &data[130] = chan->UDP_DRVSTATS_mgmt_passed_to_stack;
+			*(unsigned long *) &data[134] = card->statistics.poll_entry;
+			*(unsigned long *) &data[138] = card->statistics.poll_already_critical;
+			*(unsigned long *) &data[142] = card->statistics.poll_processed;
+			*(unsigned long *) &data[144] = card->irq_dis_poll_count;
+			mbox->cmd.length = 86;
+			break;
+		default:
+			do 
+			{
+				memcpy(&mbox->cmd, &sendpacket[47], sizeof(fr_cmd_t));
+				if (mbox->cmd.length) 
+					memcpy(&mbox->data, &sendpacket[62], mbox->cmd.length);
+				err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+			} 
+			while (err && c_retry-- && fr_event(card, err, mbox));
+			
+			if (!err) 
+			{
+				++chan->UDP_DRVSTATS_mgmt_adptr_cmnd_OK;
+				memcpy(data, sendpacket, skb->len);
+				memcpy(&data[47], &mbox->cmd, sizeof(fr_cmd_t));
+				if (mbox->cmd.length) 
+					memcpy(&data[62], &mbox->data, mbox->cmd.length);
+			} 
+			else 
+			{
+				++chan->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout;
+			}
+	}
+	/* Fill UDP TTL */
+	data[10] = card->wandev.ttl;
+	len = reply_udp(data, mbox->cmd.length);
+	if (udp_pkt_src == UDP_PKT_FRM_NETWORK) 
+	{
+		err = fr508_send(card, dlci, 0, len, data);
+		if (err)
+			++chan->UDP_DRVSTATS_mgmt_adptr_send_failed;
+		else
+			++chan->UDP_DRVSTATS_mgmt_adptr_send_passed;
+		dev_kfree_skb(skb);
+	}
+	else
+	{
+		/* Allocate socket buffer */
+		if ((new_skb = dev_alloc_skb(len)) != NULL) 
+		{
+			/* copy data into new_skb */
+			buf = skb_put(new_skb, len);
+			memcpy(buf, data, len);
+			/* Decapsulate packet and pass it up the 
+			   protocol stack */
+			new_skb->dev = dev;
+			/* remove hardware header */
+			buf = skb_pull(new_skb, 1);
+			if (!wan_type_trans(new_skb, dev)) 
+			{
+				/* can't decapsulate packet */
+				++chan->UDP_DRVSTATS_mgmt_not_passed_to_stack;
+				dev_kfree_skb(new_skb);
+			}
+			else
+			{
+				++chan->UDP_DRVSTATS_mgmt_passed_to_stack;
+				netif_rx(new_skb);
+			}
+		}
+		else
+		{
+			++chan->UDP_DRVSTATS_mgmt_no_socket;
+			printk(KERN_INFO "%s: UDP mgmt cmnd, no socket buffers available!\n", card->devname);
+		}
+	}
+	kfree(data);
+	return 0;
+}
+
+/*==============================================================================
+ * Determine what type of UDP call it is. DRVSTATS or FPIPE8ND ?
+ */
 
+static int udp_pkt_type(struct sk_buff *skb, sdla_t * card)
+{
+	unsigned char *sendpacket;
+	unsigned char buf2[5];
+	sendpacket = skb->data;
+	memcpy(&buf2, &card->wandev.udp_port, 2);
+	if (sendpacket[2] == 0x45 &&	/* IP packet */
+	    sendpacket[11] == 0x11 &&	/* UDP packet */
+	    sendpacket[24] == buf2[1] &&	/* UDP Port */
+	    sendpacket[25] == buf2[0] &&
+	    sendpacket[38] == 0x01) 
+	{
+		if (sendpacket[30] == 0x46 &&	/* FPIPE8ND: Signature */
+		    sendpacket[31] == 0x50 &&
+		    sendpacket[32] == 0x49 &&
+		    sendpacket[33] == 0x50 &&
+		    sendpacket[34] == 0x45 &&
+		    sendpacket[35] == 0x38 &&
+		    sendpacket[36] == 0x4E &&
+		    sendpacket[37] == 0x44) 
+		{
+			return UDP_FPIPE_TYPE;
+		} else if (sendpacket[30] == 0x44 &&	/* DRVSTATS: Signature */
+			   sendpacket[31] == 0x52 &&
+			   sendpacket[32] == 0x56 &&
+			   sendpacket[33] == 0x53 &&
+			   sendpacket[34] == 0x54 &&
+			   sendpacket[35] == 0x41 &&
+			   sendpacket[36] == 0x54 &&
+			   sendpacket[37] == 0x53) 
+		{
+			return UDP_DRVSTATS_TYPE;
+		}
+		else
+			return UDP_INVALID_TYPE;
+	}
+	else
+		return UDP_INVALID_TYPE;
+}
+/*==============================================================================
+ * Initializes the Statistics values in the fr_channel structure.
+ */
+ 
+void init_chan_statistics(fr_channel_t * chan)
+{
+	chan->if_send_entry = 0;
+	chan->if_send_skb_null = 0;
+	chan->if_send_broadcast = 0;
+	chan->if_send_multicast = 0;
+	chan->if_send_critical_ISR = 0;
+	chan->if_send_critical_non_ISR = 0;
+	chan->if_send_busy = 0;
+	chan->if_send_busy_timeout = 0;
+	chan->if_send_FPIPE_request = 0;
+	chan->if_send_DRVSTATS_request = 0;
+	chan->if_send_wan_disconnected = 0;
+	chan->if_send_dlci_disconnected = 0;
+	chan->if_send_no_bfrs = 0;
+	chan->if_send_adptr_bfrs_full = 0;
+	chan->if_send_bfrs_passed_to_adptr = 0;
+	chan->rx_intr_no_socket = 0;
+	chan->rx_intr_dev_not_started = 0;
+	chan->rx_intr_FPIPE_request = 0;
+	chan->rx_intr_DRVSTATS_request = 0;
+	chan->rx_intr_bfr_not_passed_to_stack = 0;
+	chan->rx_intr_bfr_passed_to_stack = 0;
+	chan->UDP_FPIPE_mgmt_kmalloc_err = 0;
+	chan->UDP_FPIPE_mgmt_direction_err = 0;
+	chan->UDP_FPIPE_mgmt_adptr_type_err = 0;
+	chan->UDP_FPIPE_mgmt_adptr_cmnd_OK = 0;
+	chan->UDP_FPIPE_mgmt_adptr_cmnd_timeout = 0;
+	chan->UDP_FPIPE_mgmt_adptr_send_passed = 0;
+	chan->UDP_FPIPE_mgmt_adptr_send_failed = 0;
+	chan->UDP_FPIPE_mgmt_not_passed_to_stack = 0;
+	chan->UDP_FPIPE_mgmt_passed_to_stack = 0;
+	chan->UDP_FPIPE_mgmt_no_socket = 0;
+	chan->UDP_DRVSTATS_mgmt_kmalloc_err = 0;
+	chan->UDP_DRVSTATS_mgmt_adptr_cmnd_OK = 0;
+	chan->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout = 0;
+	chan->UDP_DRVSTATS_mgmt_adptr_send_passed = 0;
+	chan->UDP_DRVSTATS_mgmt_adptr_send_failed = 0;
+	chan->UDP_DRVSTATS_mgmt_not_passed_to_stack = 0;
+	chan->UDP_DRVSTATS_mgmt_passed_to_stack = 0;
+	chan->UDP_DRVSTATS_mgmt_no_socket = 0;
+}
 /*==============================================================================
  * Initializes the Statistics values in the Sdla_t structure.
  */
 
-void init_global_statistics( sdla_t* card )
+void init_global_statistics(sdla_t * card)
 {
 	/* Intialize global statistics for a card */
-	card->statistics.isr_entry 		  = 0;
-	card->statistics.isr_already_critical 	  = 0;
-	card->statistics.isr_rx			  = 0;
-	card->statistics.isr_tx			  = 0;
-	card->statistics.isr_intr_test		  = 0;
-	card->statistics.isr_spurious		  = 0;
-	card->statistics.isr_enable_tx_int	  = 0;
-	card->statistics.rx_intr_corrupt_rx_bfr	  = 0;
+	card->statistics.isr_entry = 0;
+	card->statistics.isr_already_critical = 0;
+	card->statistics.isr_rx = 0;
+	card->statistics.isr_tx = 0;
+	card->statistics.isr_intr_test = 0;
+	card->statistics.isr_spurious = 0;
+	card->statistics.isr_enable_tx_int = 0;
+	card->statistics.rx_intr_corrupt_rx_bfr = 0;
 	card->statistics.rx_intr_on_orphaned_DLCI = 0;
-	card->statistics.tx_intr_dev_not_started  = 0;
-	card->statistics.poll_entry		  = 0;
-	card->statistics.poll_already_critical	  = 0;
-	card->statistics.poll_processed		  = 0;
+	card->statistics.tx_intr_dev_not_started = 0;
+	card->statistics.poll_entry = 0;
+	card->statistics.poll_already_critical = 0;
+	card->statistics.poll_processed = 0;
 }
 
-static void read_DLCI_IB_mapping( sdla_t* card, fr_channel_t* chan )
+static void read_DLCI_IB_mapping(sdla_t * card, fr_channel_t * chan)
 {
-	fr_mbox_t* mbox = card->mbox;
-	int retry = MAX_CMD_RETRY;	
-	dlci_IB_mapping_t* result; 
-	int err, counter, found;	
-
-	do {
+	fr_mbox_t *mbox = card->mbox;
+	int retry = MAX_CMD_RETRY;
+	dlci_IB_mapping_t *result;
+	int err, counter, found;
+	do 
+	{
 		memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
 		mbox->cmd.command = FR_READ_DLCI_IB_MAPPING;
-		
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+	}
+	while (err && retry-- && fr_event(card, err, mbox));
 	
-	} while (err && retry-- && fr_event(card, err, mbox));
-
-	if( mbox->cmd.result != 0)
-		printk(KERN_INFO "%s: Read DLCI IB Mapping failed\n", 
-			chan->name);
+	if (mbox->cmd.result != 0)
+		printk(KERN_INFO "%s: Read DLCI IB Mapping failed\n", chan->name);
 
 	counter = mbox->cmd.length / sizeof(dlci_IB_mapping_t);
-	result = (void *)mbox->data;
-	
+	result = (void *) mbox->data;
 	found = 0;
-	for (; counter; --counter, ++result) {
-		if ( result->dlci == chan->dlci ) {
-			printk( KERN_INFO "%s: DLCI= %d, IB addr = %lx for %s\n"
-				,card->devname,result->dlci, result->addr_value
-				,chan->name);
+	for (; counter; --counter, ++result) 
+	{
+		if (result->dlci == chan->dlci) 
+		{
+			printk(KERN_INFO "%s: DLCI= %d, IB addr = %lx for %s\n"
+			 ,card->devname, result->dlci, result->addr_value ,chan->name);
 			chan->IB_addr = result->addr_value;
-			chan->dlci_int_interface = (void*)(card->hw.dpmbase + 
-					( chan->IB_addr & 0x00001FFF));
+			chan->dlci_int_interface = (void *) (card->hw.dpmbase +
+					   (chan->IB_addr & 0x00001FFF));
 			found = 1;
-			break;	
-		} 
+			break;
+		}
 	}
 	if (!found)
-		printk( KERN_INFO "%s: DLCI %d not found by IB MAPPING cmd\n", 
-		card->devname, chan->dlci);
+		printk(KERN_INFO "%s: DLCI %d not found by IB MAPPING cmd\n",
+		       card->devname, chan->dlci);
 }
 
 /****** End *****************************************************************/
diff -u --recursive --new-file v2.1.90/linux/drivers/net/sdla_ppp.c linux/drivers/net/sdla_ppp.c
--- v2.1.90/linux/drivers/net/sdla_ppp.c	Mon Feb 23 18:12:06 1998
+++ linux/drivers/net/sdla_ppp.c	Sat Mar 21 10:56:27 1998
@@ -10,6 +10,7 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
+* Mar 15, 1998  Alan Cox	o 2.1.8x basic port.
 * Nov 27, 1997	Jaspreet Singh	o Added protection against enabling of irqs 
 *				  while they have been disabled.
 * Nov 24, 1997  Jaspreet Singh  o Fixed another RACE condition caused by
@@ -55,11 +56,7 @@
 * Jan 06, 1997	Gene Kozin	Initial version.
 *****************************************************************************/
 
-#if	!defined(__KERNEL__) || !defined(MODULE)
-#error	This code MUST be compiled as a kernel module!
-#endif
-
-#include <linux/config.h>	/* CONFIG_SANGOMA_MANAGER */
+#include <linux/config.h>	/* OS configuration options */
 #include <linux/kernel.h>	/* printk(), and other useful stuff */
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
@@ -68,10 +65,8 @@
 #include <linux/wanrouter.h>	/* WAN router definitions */
 #include <linux/wanpipe.h>	/* WANPIPE common user API definitions */
 #include <linux/if_arp.h>	/* ARPHRD_* defines */
-#include <linux/init.h>		/* __initfunc et al. */
 #include <asm/byteorder.h>	/* htons(), etc. */
-#include <asm/uaccess.h>
-
+#include <asm/uaccess.h>	/* copyto/from user */
 #define	_GNUC_
 #include <linux/sdla_ppp.h>	/* PPP firmware API definitions */
 
@@ -82,30 +77,27 @@
 #else
 #define	STATIC		static
 #endif
-
-#define	PPP_DFLT_MTU	1500		/* default MTU */
-#define	PPP_MAX_MTU	4000		/* maximum MTU */
+#define	PPP_DFLT_MTU	1500	/* default MTU */
+#define	PPP_MAX_MTU	4000	/* maximum MTU */
 #define PPP_HDR_LEN	1
-
-#define	CONNECT_TIMEOUT	(90*HZ)		/* link connection timeout */
-#define	HOLD_DOWN_TIME	(30*HZ)		/* link hold down time */
+#define	CONNECT_TIMEOUT	(90*HZ)	/* link connection timeout */
+#define	HOLD_DOWN_TIME	(30*HZ)	/* link hold down time */
 
 /* For handle_IPXWAN() */
 #define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b)))
- 
-/******Data Structures*****************************************************/
 
+/******Data Structures*****************************************************/
 /* This structure is placed in the private data area of the device structure.
  * The card structure used to occupy the private area but now the following 
  * structure will incorporate the card structure along with PPP specific data
  */
-  
-typedef struct ppp_private_area
+
+typedef struct ppp_private_area 
 {
-	sdla_t* card;	
+	sdla_t *card;
 	unsigned long router_start_time;	/*router start time in sec */
-	unsigned long tick_counter;		/*used for 5 second counter*/
-	unsigned mc;				/*multicast support on or off*/
+	unsigned long tick_counter;	/*used for 5 second counter */
+	unsigned mc;		/*multicast support on or off */
 	/* PPP specific statistics */
 	unsigned long if_send_entry;
 	unsigned long if_send_skb_null;
@@ -122,13 +114,11 @@
 	unsigned long if_send_protocol_error;
 	unsigned long if_send_tx_int_enabled;
 	unsigned long if_send_bfr_passed_to_adptr;
-	
 	unsigned long rx_intr_no_socket;
 	unsigned long rx_intr_DRVSTATS_request;
 	unsigned long rx_intr_PTPIPE_request;
 	unsigned long rx_intr_bfr_not_passed_to_stack;
 	unsigned long rx_intr_bfr_passed_to_stack;
-
 	unsigned long UDP_PTPIPE_mgmt_kmalloc_err;
 	unsigned long UDP_PTPIPE_mgmt_adptr_type_err;
 	unsigned long UDP_PTPIPE_mgmt_direction_err;
@@ -136,8 +126,7 @@
 	unsigned long UDP_PTPIPE_mgmt_adptr_cmnd_OK;
 	unsigned long UDP_PTPIPE_mgmt_passed_to_adptr;
 	unsigned long UDP_PTPIPE_mgmt_passed_to_stack;
-	unsigned long UDP_PTPIPE_mgmt_no_socket;	
-	
+	unsigned long UDP_PTPIPE_mgmt_no_socket;
 	unsigned long UDP_DRVSTATS_mgmt_kmalloc_err;
 	unsigned long UDP_DRVSTATS_mgmt_adptr_type_err;
 	unsigned long UDP_DRVSTATS_mgmt_direction_err;
@@ -145,84 +134,74 @@
 	unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_OK;
 	unsigned long UDP_DRVSTATS_mgmt_passed_to_adptr;
 	unsigned long UDP_DRVSTATS_mgmt_passed_to_stack;
-	unsigned long UDP_DRVSTATS_mgmt_no_socket;	
-
-	unsigned long router_up_time; 
-
-}ppp_private_area_t;
+	unsigned long UDP_DRVSTATS_mgmt_no_socket;
+	unsigned long router_up_time;
+} ppp_private_area_t;
 
 /* variable for keeping track of enabling/disabling FT1 monitor status */
-static int rCount = 0;
 
+static int rCount = 0;
 extern void disable_irq(unsigned int);
 extern void enable_irq(unsigned int);
 
 /****** Function Prototypes *************************************************/
 
 /* WAN link driver entry points. These are called by the WAN router module. */
-static int update (wan_device_t* wandev);
-static int new_if (wan_device_t* wandev, struct device* dev,
-	wanif_conf_t* conf);
-static int del_if (wan_device_t* wandev, struct device* dev);
-
+static int update(wan_device_t * wandev);
+static int new_if(wan_device_t * wandev, struct device *dev,
+		  wanif_conf_t * conf);
+static int del_if(wan_device_t * wandev, struct device *dev);
 /* WANPIPE-specific entry points */
-static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data);
-
+static int wpp_exec(struct sdla *card, void *u_cmd, void *u_data);
 /* Network device interface */
-static int if_init   (struct device* dev);
-static int if_open   (struct device* dev);
-static int if_close  (struct device* dev);
-static int if_header (struct sk_buff* skb, struct device* dev,
-	unsigned short type, void* daddr, void* saddr, unsigned len);
-static int if_rebuild_hdr (struct sk_buff* skb);
-static int if_send (struct sk_buff* skb, struct device* dev);
-static struct enet_statistics* if_stats (struct device* dev);
-
-
+static int if_init(struct device *dev);
+static int if_open(struct device *dev);
+static int if_close(struct device *dev);
+static int if_header(struct sk_buff *skb, struct device *dev,
+	    unsigned short type, void *daddr, void *saddr, unsigned len);
+static int if_rebuild_hdr(struct sk_buff *skb);
+static int if_send(struct sk_buff *skb, struct device *dev);
+static struct enet_statistics *if_stats(struct device *dev);
 /* PPP firmware interface functions */
-static int ppp_read_version (sdla_t* card, char* str);
-static int ppp_configure (sdla_t* card, void* data);
-static int ppp_set_intr_mode (sdla_t* card, unsigned mode);
-static int ppp_comm_enable (sdla_t* card);
-static int ppp_comm_disable (sdla_t* card);
-static int ppp_get_err_stats (sdla_t* card);
-static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto);
-static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb);
-
+static int ppp_read_version(sdla_t * card, char *str);
+static int ppp_configure(sdla_t * card, void *data);
+static int ppp_set_intr_mode(sdla_t * card, unsigned mode);
+static int ppp_comm_enable(sdla_t * card);
+static int ppp_comm_disable(sdla_t * card);
+static int ppp_get_err_stats(sdla_t * card);
+static int ppp_send(sdla_t * card, void *data, unsigned len, unsigned proto);
+static int ppp_error(sdla_t * card, int err, ppp_mbox_t * mb);
 /* Interrupt handlers */
-STATIC void wpp_isr (sdla_t* card);
-static void rx_intr (sdla_t* card);
-static void tx_intr (sdla_t* card);
-
+STATIC void wpp_isr(sdla_t * card);
+static void rx_intr(sdla_t * card);
+static void tx_intr(sdla_t * card);
 /* Background polling routines */
-static void wpp_poll (sdla_t* card);
-static void poll_active (sdla_t* card);
-static void poll_connecting (sdla_t* card);
-static void poll_disconnected (sdla_t* card);
-
+static void wpp_poll(sdla_t * card);
+static void poll_active(sdla_t * card);
+static void poll_connecting(sdla_t * card);
+static void poll_disconnected(sdla_t * card);
 /* Miscellaneous functions */
-static int config502 (sdla_t* card);
-static int config508 (sdla_t* card);
-static void show_disc_cause (sdla_t* card, unsigned cause);
-static unsigned char bps_to_speed_code (unsigned long bps);
-static int reply_udp( unsigned char *data, unsigned int mbox_len );
-static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, ppp_private_area_t* ppp_priv_area);
-static int process_udp_driver_call(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, ppp_private_area_t* ppp_priv_area); 
-static void init_ppp_tx_rx_buff( sdla_t* card );
-static int intr_test( sdla_t* card );
-static int udp_pkt_type( struct sk_buff *skb , sdla_t* card);
-static void init_ppp_priv_struct( ppp_private_area_t* ppp_priv_area);
-static void init_global_statistics( sdla_t* card );
-
-static int  Intr_test_counter;
+static int config502(sdla_t * card);
+static int config508(sdla_t * card);
+static void show_disc_cause(sdla_t * card, unsigned cause);
+static unsigned char bps_to_speed_code(unsigned long bps);
+static int reply_udp(unsigned char *data, unsigned int mbox_len);
+static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, ppp_private_area_t * ppp_priv_area);
+static int process_udp_driver_call(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, ppp_private_area_t * ppp_priv_area);
+static void init_ppp_tx_rx_buff(sdla_t * card);
+static int intr_test(sdla_t * card);
+static int udp_pkt_type(struct sk_buff *skb, sdla_t * card);
+static void init_ppp_priv_struct(ppp_private_area_t * ppp_priv_area);
+static void init_global_statistics(sdla_t * card);
+static int Intr_test_counter;
 static char TracingEnabled;
 static unsigned long curr_trace_addr;
 static unsigned long start_trace_addr;
 static unsigned short available_buffer_space;
-
 /* IPX functions */
 static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming);
 static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto);
+
 /****** Public Functions ****************************************************/
 
 /*============================================================================
@@ -237,40 +216,30 @@
  * Return:	0	o.k.
  *		< 0	failure.
  */
-int wpp_init (sdla_t* card, wandev_conf_t* conf)
+int wpp_init(sdla_t * card, wandev_conf_t * conf)
 {
-	union
-	{
+	union {
 		char str[80];
 	} u;
-
 	/* Verify configuration ID */
 	if (conf->config_id != WANCONFIG_PPP) {
-		
 		printk(KERN_INFO "%s: invalid configuration ID %u!\n",
-			card->devname, conf->config_id);
+		       card->devname, conf->config_id);
 		return -EINVAL;
-
 	}
-
 	/* Initialize protocol-specific fields */
 	switch (card->hw.fwid) {
-
-		case SFID_PPP502:
-			card->mbox =(void*)(card->hw.dpmbase + PPP502_MB_OFFS);
-			card->flags=(void*)(card->hw.dpmbase + PPP502_FLG_OFFS);
-			break;
-
-		case SFID_PPP508:
-			card->mbox =(void*)(card->hw.dpmbase + PPP508_MB_OFFS);
-			card->flags=(void*)(card->hw.dpmbase + PPP508_FLG_OFFS);
-			break;
-
-		default:
-			return -EINVAL;
-
+	case SFID_PPP502:
+		card->mbox = (void *) (card->hw.dpmbase + PPP502_MB_OFFS);
+		card->flags = (void *) (card->hw.dpmbase + PPP502_FLG_OFFS);
+		break;
+	case SFID_PPP508:
+		card->mbox = (void *) (card->hw.dpmbase + PPP508_MB_OFFS);
+		card->flags = (void *) (card->hw.dpmbase + PPP508_FLG_OFFS);
+		break;
+	default:
+		return -EINVAL;
 	}
-
 	/* Read firmware version.  Note that when adapter initializes, it
 	 * clears the mailbox, so it may appear that the first command was
 	 * executed successfully when in fact it was merely erased. To work
@@ -278,38 +247,33 @@
 	 */
 	if (ppp_read_version(card, NULL) || ppp_read_version(card, u.str))
 		return -EIO;
-	
-	printk(KERN_INFO "%s: running PPP firmware v%s\n",card->devname, u.str); 
+	printk(KERN_INFO "%s: running PPP firmware v%s\n", card->devname, u.str);
 	/* Adjust configuration and set defaults */
 	card->wandev.mtu = (conf->mtu) ?
-		min(conf->mtu, PPP_MAX_MTU) : PPP_DFLT_MTU;
-
-	card->wandev.bps	= conf->bps;
-	card->wandev.interface	= conf->interface;
-	card->wandev.clocking	= conf->clocking;
-	card->wandev.station	= conf->station;
-	card->isr		= &wpp_isr;
-	card->poll		= &wpp_poll;
-	card->exec		= &wpp_exec;
-	card->wandev.update	= &update;
-	card->wandev.new_if	= &new_if;
-	card->wandev.del_if	= &del_if;
-	card->wandev.state	= WAN_DISCONNECTED;
-        card->wandev.udp_port   = conf->udp_port;
-	card->wandev.ttl	= conf->ttl;
+	    min(conf->mtu, PPP_MAX_MTU) : PPP_DFLT_MTU;
+	card->wandev.bps = conf->bps;
+	card->wandev.interface = conf->interface;
+	card->wandev.clocking = conf->clocking;
+	card->wandev.station = conf->station;
+	card->isr = &wpp_isr;
+	card->poll = &wpp_poll;
+	card->exec = &wpp_exec;
+	card->wandev.update = &update;
+	card->wandev.new_if = &new_if;
+	card->wandev.del_if = &del_if;
+	card->wandev.state = WAN_DISCONNECTED;
+	card->wandev.udp_port = conf->udp_port;
+	card->wandev.ttl = conf->ttl;
 	card->irq_dis_if_send_count = 0;
-        card->irq_dis_poll_count = 0;
-        TracingEnabled          = 0;
-
+	card->irq_dis_poll_count = 0;
+	TracingEnabled = 0;
 	card->wandev.enable_IPX = conf->enable_IPX;
 	if (conf->network_number)
 		card->wandev.network_number = conf->network_number;
 	else
 		card->wandev.network_number = 0xDEADBEEF;
-
 	/* initialize global statistics */
-	init_global_statistics( card );
-
+	init_global_statistics(card);
 	return 0;
 }
 
@@ -321,19 +285,14 @@
 static int update(wan_device_t * wandev)
 {
 	sdla_t *card;
-
 	/* sanity checks */
 	if ((wandev == NULL) || (wandev->private == NULL))
-		return -EFAULT
-		    ;
+		return -EFAULT;
 	if (wandev->state == WAN_UNCONFIGURED)
-		return -ENODEV
-		    ;
+		return -ENODEV;
 	if (test_and_set_bit(0, (void *) &wandev->critical))
-		return -EAGAIN
-		    ;
+		return -EAGAIN;
 	card = wandev->private;
-
 	ppp_get_err_stats(card);
 	wandev->critical = 0;
 	return 0;
@@ -351,41 +310,29 @@
  * Return:	0	o.k.
  *		< 0	failure (channel will not be created)
  */
-static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf)
-{
-	sdla_t* card = wandev->private;
-	ppp_private_area_t* ppp_priv_area;
 
+static int new_if(wan_device_t * wandev, struct device *dev, wanif_conf_t * conf)
+{
+	sdla_t *card = wandev->private;
+	ppp_private_area_t *ppp_priv_area;
 	if (wandev->ndev)
 		return -EEXIST;
-	
 	if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) {
-
 		printk(KERN_INFO "%s: invalid interface name!\n",
-			card->devname);
+		       card->devname);
 		return -EINVAL;
-
 	}
-
 	/* allocate and initialize private data */
 	ppp_priv_area = kmalloc(sizeof(ppp_private_area_t), GFP_KERNEL);
-	
-	if( ppp_priv_area == NULL )
-		return	-ENOMEM;
-	
+	if (ppp_priv_area == NULL)
+		return -ENOMEM;
 	memset(ppp_priv_area, 0, sizeof(ppp_private_area_t));
-	
-	ppp_priv_area->card = card; 
-	
+	ppp_priv_area->card = card;
 	/* initialize data */
 	strcpy(card->u.p.if_name, conf->name);
-
 	/* initialize data in ppp_private_area structure */
-	
-	init_ppp_priv_struct( ppp_priv_area );
-
+	init_ppp_priv_struct(ppp_priv_area);
 	ppp_priv_area->mc = conf->mc;
-
 	/* prepare network device data space for registration */
 	dev->name = card->u.p.if_name;
 	dev->init = &if_init;
@@ -396,14 +343,13 @@
 /*============================================================================
  * Delete logical channel.
  */
-static int del_if (wan_device_t* wandev, struct device* dev)
+
+static int del_if(wan_device_t * wandev, struct device *dev)
 {
 	if (dev->priv) {
-
-                kfree(dev->priv);
-                dev->priv = NULL;
-        }
-
+		kfree(dev->priv);
+		dev->priv = NULL;
+	}
 	return 0;
 }
 
@@ -412,24 +358,23 @@
 /*============================================================================
  * Execute adapter interface command.
  */
+
 static int wpp_exec(struct sdla *card, void *u_cmd, void *u_data)
 {
 	ppp_mbox_t *mbox = card->mbox;
 	int len;
-
-	if (copy_from_user((void *) &mbox->cmd, u_cmd, sizeof(ppp_cmd_t)))
+	if(copy_from_user((void *) &mbox->cmd, u_cmd, sizeof(ppp_cmd_t)))
 		return -EFAULT;
 	len = mbox->cmd.length;
 	if (len) {
-		if (copy_from_user((void *) &mbox->data, u_data, len))
+		if(copy_from_user((void *) &mbox->data, u_data, len))
 			return -EFAULT;
 	}
 	/* execute command */
 	if (!sdla_exec(mbox))
 		return -EIO;
-
 	/* return result */
-	if (copy_to_user(u_cmd, (void *) &mbox->cmd, sizeof(ppp_cmd_t)))
+	if(copy_to_user(u_cmd, (void *) &mbox->cmd, sizeof(ppp_cmd_t)))
 		return -EFAULT;
 	len = mbox->cmd.length;
 	if (len && u_data && copy_to_user(u_data, (void *) &mbox->data, len))
@@ -446,41 +391,34 @@
  * interface registration.  Returning anything but zero will fail interface
  * registration.
  */
-static int if_init (struct device* dev)
+
+static int if_init(struct device *dev)
 {
-	ppp_private_area_t* ppp_priv_area = dev->priv;
-	sdla_t* card = ppp_priv_area->card;
-	wan_device_t* wandev = &card->wandev;
-	int i;
+	ppp_private_area_t *ppp_priv_area = dev->priv;
+	sdla_t *card = ppp_priv_area->card;
+	wan_device_t *wandev = &card->wandev;
 
 	/* Initialize device driver entry points */
-	dev->open		= &if_open;
-	dev->stop		= &if_close;
-	dev->hard_header	= &if_header;
-	dev->rebuild_header	= &if_rebuild_hdr;
-	dev->hard_start_xmit	= &if_send;
-	dev->get_stats		= &if_stats;
-
-
+	dev->open = &if_open;
+	dev->stop = &if_close;
+	dev->hard_header = &if_header;
+	dev->rebuild_header = &if_rebuild_hdr;
+	dev->hard_start_xmit = &if_send;
+	dev->get_stats = &if_stats;
 	/* Initialize media-specific parameters */
-	dev->family		= AF_INET;	/* address family */
-	dev->type		= ARPHRD_PPP;	/* ARP h/w type */
-	dev->mtu		= wandev->mtu;
-	dev->hard_header_len	= PPP_HDR_LEN;	/* media header length */
-
+	dev->type = ARPHRD_PPP;	/* ARP h/w type */
+	dev->mtu = wandev->mtu;
+	dev->hard_header_len = PPP_HDR_LEN;	/* media header length */
 	/* Initialize hardware parameters (just for reference) */
-	dev->irq		= wandev->irq;
-	dev->dma		= wandev->dma;
-	dev->base_addr		= wandev->ioport;
-	dev->mem_start		= wandev->maddr;
-	dev->mem_end		= wandev->maddr + wandev->msize - 1;
-
-        /* Set transmit buffer queue length */
-        dev->tx_queue_len = 100;
-   
+	dev->irq = wandev->irq;
+	dev->dma = wandev->dma;
+	dev->base_addr = wandev->ioport;
+	dev->mem_start = wandev->maddr;
+	dev->mem_end = wandev->maddr + wandev->msize - 1;
+	/* Set transmit buffer queue length */
+	dev->tx_queue_len = 100;
 	/* Initialize socket buffers */
 	dev_init_buffers(dev);
-
 	return 0;
 }
 
@@ -491,72 +429,54 @@
  *
  * Return 0 if O.k. or errno.
  */
-static int if_open (struct device* dev)
+
+static int if_open(struct device *dev)
 {
-	ppp_private_area_t* ppp_priv_area = dev->priv;
-	sdla_t* card = ppp_priv_area->card;
-	ppp_flags_t* flags = card->flags;
+	ppp_private_area_t *ppp_priv_area = dev->priv;
+	sdla_t *card = ppp_priv_area->card;
+	ppp_flags_t *flags = card->flags;
 	struct timeval tv;
 	int err = 0;
-
 	if (dev->start)
-		return -EBUSY;		/* only one open is allowed */
-	
-	if (test_and_set_bit(0, (void*)&card->wandev.critical))
+		return -EBUSY;	/* only one open is allowed */
+	if (test_and_set_bit(0, (void *) &card->wandev.critical))
 		return -EAGAIN;
-	
-	if ((card->hw.fwid == SFID_PPP502) ? config502(card) : config508(card)){
-
+	if ((card->hw.fwid == SFID_PPP502) ? config502(card) : config508(card)) {
 		err = -EIO;
 		card->wandev.critical = 0;
 		return err;
-
 	}
-
 	Intr_test_counter = 0;
-	err = intr_test( card );
-
-	if( (err) || (Intr_test_counter != (MAX_INTR_TEST_COUNTER + 1))) {
-
-		printk(KERN_INFO "%s: Interrupt Test Failed, Counter: %i\n", 
-			card->devname, Intr_test_counter);
+	err = intr_test(card);
+	if ((err) || (Intr_test_counter != (MAX_INTR_TEST_COUNTER + 1))) {
+		printk(KERN_INFO "%s: Interrupt Test Failed, Counter: %i\n",
+		       card->devname, Intr_test_counter);
 		err = -EIO;
 		card->wandev.critical = 0;
 		return err;
-
 	}
-	
-	printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n", 
-		card->devname, Intr_test_counter);
-	
+	printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n",
+	       card->devname, Intr_test_counter);
 	/* Initialize Rx/Tx buffer control fields */
-	init_ppp_tx_rx_buff( card );
-
+	init_ppp_tx_rx_buff(card);
 	if (ppp_set_intr_mode(card, 0x03)) {
-	
 		err = -EIO;
 		card->wandev.critical = 0;
 		return err;
-
 	}
-
 	flags->imask &= ~0x02;
-
 	if (ppp_comm_enable(card)) {
-
 		err = -EIO;
 		card->wandev.critical = 0;
 		return err;
-
 	}
-	
 	wanpipe_set_state(card, WAN_CONNECTING);
 	wanpipe_open(card);
 	dev->mtu = min(dev->mtu, card->wandev.mtu);
 	dev->interrupt = 0;
 	dev->tbusy = 0;
 	dev->start = 1;
-	do_gettimeofday( &tv );
+	do_gettimeofday(&tv);
 	ppp_priv_area->router_start_time = tv.tv_sec;
 	card->wandev.critical = 0;
 	return err;
@@ -567,14 +487,13 @@
  * o if this is the last open, then disable communications and interrupts.
  * o reset flags.
  */
-static int if_close (struct device* dev)
-{
-	ppp_private_area_t* ppp_priv_area = dev->priv;
-	sdla_t* card = ppp_priv_area->card;
 
-	if (test_and_set_bit(0, (void*)&card->wandev.critical))
+static int if_close(struct device *dev)
+{
+	ppp_private_area_t *ppp_priv_area = dev->priv;
+	sdla_t *card = ppp_priv_area->card;
+	if (test_and_set_bit(0, (void *) &card->wandev.critical))
 		return -EAGAIN;
-	
 	dev->start = 0;
 	wanpipe_close(card);
 	wanpipe_set_state(card, WAN_DISCONNECTED);
@@ -593,21 +512,19 @@
  *
  * Return:	media header length.
  */
-static int if_header (struct sk_buff* skb, struct device* dev,
-	unsigned short type, void* daddr, void* saddr, unsigned len)
+
+static int if_header(struct sk_buff *skb, struct device *dev,
+	     unsigned short type, void *daddr, void *saddr, unsigned len)
 {
-	switch (type)
+	switch (type) 
 	{
 		case ETH_P_IP:
-	
 		case ETH_P_IPX:
 			skb->protocol = type;
 			break;
-
 		default:
 			skb->protocol = 0;
 	}
-
 	return PPP_HDR_LEN;
 }
 
@@ -617,13 +534,14 @@
  * Return:	1	physical address resolved.
  *		0	physical address not resolved
  */
-static int if_rebuild_hdr (struct sk_buff* skb)
-{
-	ppp_private_area_t* ppp_priv_area = skb->dev->priv;
-	sdla_t* card = ppp_priv_area->card;
 
+static int if_rebuild_hdr(struct sk_buff *skb)
+{
+	struct device *dev=skb->dev;
+	ppp_private_area_t *ppp_priv_area = dev->priv;
+	sdla_t *card = ppp_priv_area->card;
 	printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n",
-		card->devname, dev->name);
+	       card->devname, dev->name);
 	return 1;
 }
 
@@ -644,133 +562,133 @@
  * 2. Setting tbusy flag will inhibit further transmit requests from the
  *    protocol stack and can be used for flow control with protocol layer.
  */
-static int if_send (struct sk_buff* skb, struct device* dev)
+
+static int if_send(struct sk_buff *skb, struct device *dev)
 {
-	ppp_private_area_t* ppp_priv_area = dev->priv;
-	sdla_t* card = ppp_priv_area->card;
+	ppp_private_area_t *ppp_priv_area = dev->priv;
+	sdla_t *card = ppp_priv_area->card;
 	unsigned char *sendpacket;
 	unsigned long check_braddr, check_mcaddr;
 	unsigned long host_cpu_flags;
-	ppp_flags_t* flags = card->flags;
+	ppp_flags_t *flags = card->flags;
 	int retry = 0;
 	int err, udp_type;
-
 	++ppp_priv_area->if_send_entry;
-
 	if (skb == NULL) {
-
 		/* If we get here, some higher layer thinks we've missed an
 		 * tx-done interrupt.
 		 */
 		printk(KERN_INFO "%s: interface %s got kicked!\n",
-			card->devname, dev->name);
-		
+		       card->devname, dev->name);
 		++ppp_priv_area->if_send_skb_null;
-		
-		dev_tint(dev);
+		mark_bh(NET_BH);
 		return 0;
-
 	}
-
 	if (dev->tbusy) {
-
 		/* If our device stays busy for at least 5 seconds then we will
 		 * kick start the device by making dev->tbusy = 0.  We expect 
 		 * that our device never stays busy more than 5 seconds. So this
 		 * is only used as a last resort. 
 		 */
-              
 		++ppp_priv_area->if_send_busy;
-        	++card->wandev.stats.collisions;
-
-		if ((jiffies - ppp_priv_area->tick_counter) < (5*HZ)) {
+		++card->wandev.stats.collisions;
+		if ((jiffies - ppp_priv_area->tick_counter) < (5 * HZ)) {
 			return 1;
 		}
-
-		printk (KERN_INFO "%s: Transmit times out\n",card->devname);
-	
+		printk(KERN_INFO "%s: Transmit times out\n", card->devname);
 		++ppp_priv_area->if_send_busy_timeout;
-
-		/* unbusy the card (because only one interface per card)*/
+		/* unbusy the card (because only one interface per card) */
 		dev->tbusy = 0;
-	}	
+	}
 	sendpacket = skb->data;
-#ifdef CONFIG_SANGOMA_MANAGER
-	if(sangoma_ppp_manager(skb,card))
-	{
+	udp_type = udp_pkt_type(skb, card);
+	if (udp_type == UDP_DRVSTATS_TYPE) {
+		++ppp_priv_area->if_send_DRVSTATS_request;
+		process_udp_driver_call(UDP_PKT_FRM_STACK, card, skb, dev,
+					ppp_priv_area);
 		dev_kfree_skb(skb);
 		return 0;
+	} else if (udp_type == UDP_PTPIPE_TYPE)
+		++ppp_priv_area->if_send_PTPIPE_request;
+	/* retreive source address in two forms: broadcast & multicast */
+	check_braddr = sendpacket[15];
+	check_mcaddr = sendpacket[12];
+	check_braddr = check_braddr << 8;
+	check_mcaddr = check_mcaddr << 8;
+	check_braddr |= sendpacket[14];
+	check_mcaddr |= sendpacket[13];
+	check_braddr = check_braddr << 8;
+	check_mcaddr = check_mcaddr << 8;
+	check_braddr |= sendpacket[13];
+	check_mcaddr |= sendpacket[14];
+	check_braddr = check_braddr << 8;
+	check_mcaddr = check_mcaddr << 8;
+	check_braddr |= sendpacket[12];
+	check_mcaddr |= sendpacket[15];
+	/* if the Source Address is a Multicast address */
+	if ((ppp_priv_area->mc == WANOPT_NO) && (check_mcaddr >= 0xE0000001)
+	    && (check_mcaddr <= 0xFFFFFFFE)) {
+		printk(KERN_INFO "%s: Mutlicast Src. Addr. silently discarded\n"
+		       ,card->devname);
+		dev_kfree_skb(skb);
+		++ppp_priv_area->if_send_multicast;
+		++card->wandev.stats.tx_dropped;
+		return 0;
 	}
-#endif
 	disable_irq(card->hw.irq);
 	++card->irq_dis_if_send_count;
-
-    	if (test_and_set_bit(0, (void*)&card->wandev.critical)) 
-    	{
-    		if (card->wandev.critical == CRITICAL_IN_ISR) 
-    		{	
+	if (test_and_set_bit(0, (void *) &card->wandev.critical)) {
+		if (card->wandev.critical == CRITICAL_IN_ISR) {
 			/* If the critical flag is set due to an Interrupt
 			 * then set enable transmit interrupt flag to enable
 			 * transmit interrupt. (delay interrupt)
 			 */
 			card->wandev.enable_tx_int = 1;
-	   		dev->tbusy = 1;
-			
+			dev->tbusy = 1;
 			/* set the counter to see if we get the interrupt in
 			 * 5 seconds. 
 			 */
 			ppp_priv_area->tick_counter = jiffies;
-	   		++ppp_priv_area->if_send_critical_ISR;
-			
+			++ppp_priv_area->if_send_critical_ISR;
 			save_flags(host_cpu_flags);
-                        cli();
-                        if ((!(--card->irq_dis_if_send_count)) &&
-                                        (!card->irq_dis_poll_count))
-                                enable_irq(card->hw.irq);
-                        restore_flags(host_cpu_flags);
-
+			cli();
+			if ((!(--card->irq_dis_if_send_count)) &&
+			    (!card->irq_dis_poll_count))
+				enable_irq(card->hw.irq);
+			restore_flags(host_cpu_flags);
 			return 1;
-
 		}
-
-    		dev_kfree_skb(skb);
+		dev_kfree_skb(skb);
 		++ppp_priv_area->if_send_critical_non_ISR;
-		
 		save_flags(host_cpu_flags);
-                cli();
-                if ((!(--card->irq_dis_if_send_count)) &&
-                                         (!card->irq_dis_poll_count))
-                        enable_irq(card->hw.irq);
-                restore_flags(host_cpu_flags);
-	
+		cli();
+		if ((!(--card->irq_dis_if_send_count)) &&
+		    (!card->irq_dis_poll_count))
+			enable_irq(card->hw.irq);
+		restore_flags(host_cpu_flags);
 		return 0;
 	}
-
-
-	if (card->wandev.state != WAN_CONNECTED) {
-
+	if (udp_type == UDP_PTPIPE_TYPE) {
+		err = process_udp_mgmt_pkt(UDP_PKT_FRM_STACK, card, skb,
+					   dev, ppp_priv_area);
+	} else if (card->wandev.state != WAN_CONNECTED) {
 		++ppp_priv_area->if_send_wan_disconnected;
-        	++card->wandev.stats.tx_dropped;
-
-     	} else if (!skb->protocol) {
+		++card->wandev.stats.tx_dropped;
+	} else if (!skb->protocol) {
 		++ppp_priv_area->if_send_protocol_error;
-        	++card->wandev.stats.tx_errors;
-
+		++card->wandev.stats.tx_errors;
 	} else {
-
-		/*If it's IPX change the network numbers to 0 if they're ours.*/
-		if( skb->protocol == ETH_P_IPX ) {
-			if(card->wandev.enable_IPX) {
-				switch_net_numbers( skb->data, 
-					card->wandev.network_number, 0);
+		/*If it's IPX change the network numbers to 0 if they're ours. */
+		if (skb->protocol == ETH_P_IPX) {
+			if (card->wandev.enable_IPX) {
+				switch_net_numbers(skb->data,
+					 card->wandev.network_number, 0);
 			} else {
 				++card->wandev.stats.tx_dropped;
 				goto tx_done;
 			}
 		}
-
 		if (ppp_send(card, skb->data, skb->len, skb->protocol)) {
-
 			retry = 1;
 			dev->tbusy = 1;
 			++ppp_priv_area->if_send_adptr_bfrs_full;
@@ -778,105 +696,160 @@
 			ppp_priv_area->tick_counter = jiffies;
 			++card->wandev.stats.tx_errors;
 			flags->imask |= 0x02;	/* unmask Tx interrupts */
-
 		} else {
 			++ppp_priv_area->if_send_bfr_passed_to_adptr;
 			++card->wandev.stats.tx_packets;
 		}
-    	}
-	
-tx_done:	
-	if (!retry){
+	}
+tx_done:
+	if (!retry) {
 		dev_kfree_skb(skb);
 	}
-
 	card->wandev.critical = 0;
-	
 	save_flags(host_cpu_flags);
-        cli();
-        if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count))
-                enable_irq(card->hw.irq);
-        restore_flags(host_cpu_flags);
-	
+	cli();
+	if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count))
+		enable_irq(card->hw.irq);
+	restore_flags(host_cpu_flags);
 	return retry;
 }
 
+/*============================================================================
+ * Reply to UDP Management system.
+ * Return length of reply.
+ */
+
+static int reply_udp(unsigned char *data, unsigned int mbox_len)
+{
+	unsigned short len, udp_length, temp, i, ip_length;
+	unsigned long sum;
+	/* Set length of packet */
+	len = mbox_len + 60;
+	/* fill in UDP reply */
+	data[36] = 0x02;
+	/* fill in UDP length */
+	udp_length = mbox_len + 40;
+	/* put it on an even boundary */
+	if (udp_length & 0x0001) {
+		udp_length += 1;
+		len += 1;
+	}
+	temp = (udp_length << 8) | (udp_length >> 8);
+	memcpy(&data[24], &temp, 2);
+	/* swap UDP ports */
+	memcpy(&temp, &data[20], 2);
+	memcpy(&data[20], &data[22], 2);
+	memcpy(&data[22], &temp, 2);
+	/* add UDP pseudo header */
+	temp = 0x1100;
+	memcpy(&data[udp_length + 20], &temp, 2);
+	temp = (udp_length << 8) | (udp_length >> 8);
+	memcpy(&data[udp_length + 22], &temp, 2);
+	/* calculate UDP checksum */
+	data[26] = data[27] = 0;
+	sum = 0;
+	for (i = 0; i < udp_length + 12; i += 2) {
+		memcpy(&temp, &data[12 + i], 2);
+		sum += (unsigned long) temp;
+	}
+	while (sum >> 16) {
+		sum = (sum & 0xffffUL) + (sum >> 16);
+	}
+	temp = (unsigned short) sum;
+	temp = ~temp;
+	if (temp == 0)
+		temp = 0xffff;
+	memcpy(&data[26], &temp, 2);
+	/* fill in IP length */
+	ip_length = udp_length + 20;
+	temp = (ip_length << 8) | (ip_length >> 8);
+	memcpy(&data[2], &temp, 2);
+	/* swap IP addresses */
+	memcpy(&temp, &data[12], 2);
+	memcpy(&data[12], &data[16], 2);
+	memcpy(&data[16], &temp, 2);
+	memcpy(&temp, &data[14], 2);
+	memcpy(&data[14], &data[18], 2);
+	memcpy(&data[18], &temp, 2);
+	/* fill in IP checksum */
+	data[10] = data[11] = 0;
+	sum = 0;
+	for (i = 0; i < 20; i += 2) {
+		memcpy(&temp, &data[i], 2);
+		sum += (unsigned long) temp;
+	}
+	while (sum >> 16) {
+		sum = (sum & 0xffffUL) + (sum >> 16);
+	}
+	temp = (unsigned short) sum;
+	temp = ~temp;
+	if (temp == 0)
+		temp = 0xffff;
+	memcpy(&data[10], &temp, 2);
+	return len;
+}				/* reply_udp */
+
 /*
    If incoming is 0 (outgoing)- if the net numbers is ours make it 0
    if incoming is 1 - if the net number is 0 make it ours 
+ */
 
-*/
 static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming)
 {
 	unsigned long pnetwork_number;
-
-	pnetwork_number = (unsigned long)((sendpacket[6] << 24) + 
-			  (sendpacket[7] << 16) + (sendpacket[8] << 8) + 
-			  sendpacket[9]);
-
-	if (!incoming) 
-	{
+	pnetwork_number = (unsigned long) ((sendpacket[6] << 24) +
+			   (sendpacket[7] << 16) + (sendpacket[8] << 8) +
+					   sendpacket[9]);
+	if (!incoming) {
 		/* If the destination network number is ours, make it 0 */
-		if( pnetwork_number == network_number) 
-		{
-			sendpacket[6] = sendpacket[7] = sendpacket[8] = 
-					 sendpacket[9] = 0x00;
+		if (pnetwork_number == network_number) {
+			sendpacket[6] = sendpacket[7] = sendpacket[8] =
+			    sendpacket[9] = 0x00;
 		}
-	} 
-	else
-	{
+	} else {
 		/* If the incoming network is 0, make it ours */
-		if( pnetwork_number == 0) 
-		{
-			sendpacket[6] = (unsigned char)(network_number >> 24);
-			sendpacket[7] = (unsigned char)((network_number & 
-					 0x00FF0000) >> 16);
-			sendpacket[8] = (unsigned char)((network_number & 
-					 0x0000FF00) >> 8);
-			sendpacket[9] = (unsigned char)(network_number & 
-					 0x000000FF);
+		if (pnetwork_number == 0) {
+			sendpacket[6] = (unsigned char) (network_number >> 24);
+			sendpacket[7] = (unsigned char) ((network_number &
+						      0x00FF0000) >> 16);
+			sendpacket[8] = (unsigned char) ((network_number &
+						       0x0000FF00) >> 8);
+			sendpacket[9] = (unsigned char) (network_number &
+							 0x000000FF);
 		}
 	}
-
-
-	pnetwork_number = (unsigned long)((sendpacket[18] << 24) + 
-			  (sendpacket[19] << 16) + (sendpacket[20] << 8) + 
-			  sendpacket[21]);
-
-	if( !incoming ) 
-	{
+	pnetwork_number = (unsigned long) ((sendpacket[18] << 24) +
+			 (sendpacket[19] << 16) + (sendpacket[20] << 8) +
+					   sendpacket[21]);
+	if (!incoming) {
 		/* If the source network is ours, make it 0 */
-		if( pnetwork_number == network_number) 
-		{
-			sendpacket[18] = sendpacket[19] = sendpacket[20] = 
-					 sendpacket[21] = 0x00;
+		if (pnetwork_number == network_number) {
+			sendpacket[18] = sendpacket[19] = sendpacket[20] =
+			    sendpacket[21] = 0x00;
 		}
-	} 
-	else
-	{
+	} else {
 		/* If the source network is 0, make it ours */
-		if( pnetwork_number == 0 ) 
-		{
-			sendpacket[18] = (unsigned char)(network_number >> 24);
-			sendpacket[19] = (unsigned char)((network_number & 
-					 0x00FF0000) >> 16);
-			sendpacket[20] = (unsigned char)((network_number & 
-					 0x0000FF00) >> 8);
-			sendpacket[21] = (unsigned char)(network_number & 
-					 0x000000FF);
+		if (pnetwork_number == 0) {
+			sendpacket[18] = (unsigned char) (network_number >> 24);
+			sendpacket[19] = (unsigned char) ((network_number &
+						      0x00FF0000) >> 16);
+			sendpacket[20] = (unsigned char) ((network_number &
+						       0x0000FF00) >> 8);
+			sendpacket[21] = (unsigned char) (network_number &
+							  0x000000FF);
 		}
 	}
-} /* switch_net_numbers */
+}				/* switch_net_numbers */
 
 /*============================================================================
  * Get ethernet-style interface statistics.
  * Return a pointer to struct enet_statistics.
  */
-static struct net_device_stats* if_stats (struct device* dev)
-{
-	ppp_private_area_t* ppp_priv_area = dev->priv;
-	sdla_t* card = ppp_priv_area->card;
 
+static struct enet_statistics *if_stats(struct device *dev)
+{
+	ppp_private_area_t *ppp_priv_area = dev->priv;
+	sdla_t *card = ppp_priv_area->card;
 	return &card->wandev.stats;
 }
 
@@ -886,146 +859,122 @@
  * Read firmware code version.
  *	Put code version as ASCII string in str. 
  */
-static int ppp_read_version (sdla_t* card, char* str)
+
+static int ppp_read_version(sdla_t * card, char *str)
 {
-	ppp_mbox_t* mb = card->mbox;
+	ppp_mbox_t *mb = card->mbox;
 	int err;
-
 	memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
 	mb->cmd.command = PPP_READ_CODE_VERSION;
 	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
-
 	if (err != CMD_OK)
- 
 		ppp_error(card, err, mb);
-
 	else if (str) {
-
 		int len = mb->cmd.length;
-
 		memcpy(str, mb->data, len);
 		str[len] = '\0';
-
 	}
-
 	return err;
 }
 
 /*============================================================================
  * Configure PPP firmware.
  */
-static int ppp_configure (sdla_t* card, void* data)
+
+static int ppp_configure(sdla_t * card, void *data)
 {
-	ppp_mbox_t* mb = card->mbox;
+	ppp_mbox_t *mb = card->mbox;
 	int data_len = (card->hw.fwid == SFID_PPP502) ?
-		sizeof(ppp502_conf_t) : sizeof(ppp508_conf_t);
+	sizeof(ppp502_conf_t) : sizeof(ppp508_conf_t);
 	int err;
-
 	memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
 	memcpy(mb->data, data, data_len);
-	mb->cmd.length  = data_len;
+	mb->cmd.length = data_len;
 	mb->cmd.command = PPP_SET_CONFIG;
 	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
-	
-	if (err != CMD_OK) 
+	if (err != CMD_OK)
 		ppp_error(card, err, mb);
-	
 	return err;
 }
 
 /*============================================================================
  * Set interrupt mode.
  */
-static int ppp_set_intr_mode (sdla_t* card, unsigned mode)
+
+static int ppp_set_intr_mode(sdla_t * card, unsigned mode)
 {
-	ppp_mbox_t* mb = card->mbox;
+	ppp_mbox_t *mb = card->mbox;
 	int err;
-
 	memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
 	mb->data[0] = mode;
-	
-	switch (card->hw.fwid)
-	{
-		case SFID_PPP502:
-			mb->cmd.length  = 1;
-			break;
-
-		case SFID_PPP508:
-		
-		default:
-			mb->data[1] = card->hw.irq;
-			mb->cmd.length = 2;
+	switch (card->hw.fwid) {
+	case SFID_PPP502:
+		mb->cmd.length = 1;
+		break;
+	case SFID_PPP508:
+	default:
+		mb->data[1] = card->hw.irq;
+		mb->cmd.length = 2;
 	}
-	
 	mb->cmd.command = PPP_SET_INTR_FLAGS;
 	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
-	
-	if (err != CMD_OK) 
+	if (err != CMD_OK)
 		ppp_error(card, err, mb);
-	
 	return err;
 }
 
 /*============================================================================
  * Enable communications.
  */
-static int ppp_comm_enable (sdla_t* card)
+
+static int ppp_comm_enable(sdla_t * card)
 {
-	ppp_mbox_t* mb = card->mbox;
+	ppp_mbox_t *mb = card->mbox;
 	int err;
-
 	memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
 	mb->cmd.command = PPP_COMM_ENABLE;
 	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
-	
-	if (err != CMD_OK) 
+	if (err != CMD_OK)
 		ppp_error(card, err, mb);
-	
 	return err;
 }
 
 /*============================================================================
  * Disable communications.
  */
-static int ppp_comm_disable (sdla_t* card)
+
+static int ppp_comm_disable(sdla_t * card)
 {
-	ppp_mbox_t* mb = card->mbox;
+	ppp_mbox_t *mb = card->mbox;
 	int err;
-
 	memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
 	mb->cmd.command = PPP_COMM_DISABLE;
 	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
-	
-	if (err != CMD_OK) 
+	if (err != CMD_OK)
 		ppp_error(card, err, mb);
-	
 	return err;
 }
 
 /*============================================================================
  * Get communications error statistics.
  */
-static int ppp_get_err_stats (sdla_t* card)
+
+static int ppp_get_err_stats(sdla_t * card)
 {
-	ppp_mbox_t* mb = card->mbox;
+	ppp_mbox_t *mb = card->mbox;
 	int err;
-
 	memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
 	mb->cmd.command = PPP_READ_ERROR_STATS;
 	err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
-	
-	if (err == CMD_OK) 
-	{	
-		ppp_err_stats_t* stats = (void*)mb->data;
-		card->wandev.stats.rx_over_errors    = stats->rx_overrun;
-		card->wandev.stats.rx_crc_errors     = stats->rx_bad_crc;
-		card->wandev.stats.rx_missed_errors  = stats->rx_abort;
-		card->wandev.stats.rx_length_errors  = stats->rx_lost;
+	if (err == CMD_OK) {
+		ppp_err_stats_t *stats = (void *) mb->data;
+		card->wandev.stats.rx_over_errors = stats->rx_overrun;
+		card->wandev.stats.rx_crc_errors = stats->rx_bad_crc;
+		card->wandev.stats.rx_missed_errors = stats->rx_abort;
+		card->wandev.stats.rx_length_errors = stats->rx_lost;
 		card->wandev.stats.tx_aborted_errors = stats->tx_abort;
-	
-	} else 
+	} else
 		ppp_error(card, err, mb);
-	
 	return err;
 }
 
@@ -1034,36 +983,27 @@
  *	Return:	0 - o.k.
  *		1 - no transmit buffers available
  */
- 
-static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto)
+
+static int ppp_send(sdla_t * card, void *data, unsigned len, unsigned proto)
 {
-	ppp_buf_ctl_t* txbuf = card->u.p.txbuf;
+	ppp_buf_ctl_t *txbuf = card->u.p.txbuf;
 	unsigned long addr;
-
 	if (txbuf->flag)
-                return 1;
-	
+		return 1
+		    ;
 	if (card->hw.fwid == SFID_PPP502)
-		addr = (txbuf->buf.o_p[1] << 8) + txbuf->buf.o_p[0]; 
-	else 
+		addr = (txbuf->buf.o_p[1] << 8) + txbuf->buf.o_p[0];
+	else
 		addr = txbuf->buf.ptr;
-
-	
 	sdla_poke(&card->hw, addr, data, len);
-
-	txbuf->length = len;		/* frame length */
-	
+	txbuf->length = len;	/* frame length */
 	if (proto == ETH_P_IPX)
 		txbuf->proto = 0x01;	/* protocol ID */
-	
-	txbuf->flag = 1;		/* start transmission */
-
+	txbuf->flag = 1;	/* start transmission */
 	/* Update transmit buffer control fields */
 	card->u.p.txbuf = ++txbuf;
-
-	if ((void*)txbuf > card->u.p.txbuf_last)
+	if ((void *) txbuf > card->u.p.txbuf_last)
 		card->u.p.txbuf = card->u.p.txbuf_base;
-
 	return 0;
 }
 
@@ -1076,23 +1016,19 @@
  *
  * Return zero if previous command has to be cancelled.
  */
- 
-static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb)
+
+static int ppp_error(sdla_t * card, int err, ppp_mbox_t * mb)
 {
 	unsigned cmd = mb->cmd.command;
-
-	switch (err) 
-	{
-		case CMD_TIMEOUT:
-			printk(KERN_ERR "%s: command 0x%02X timed out!\n",
-				card->devname, cmd);
-			break;
-
-		default:
-			printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n"
-				, card->devname, cmd, err);
+	switch (err) {
+	case CMD_TIMEOUT:
+		printk(KERN_ERR "%s: command 0x%02X timed out!\n",
+		       card->devname, cmd);
+		break;
+	default:
+		printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n"
+		       ,card->devname, cmd, err);
 	}
-
 	return 0;
 }
 
@@ -1101,81 +1037,65 @@
 /*============================================================================
  * PPP interrupt service routine.
  */
-STATIC void wpp_isr (sdla_t* card)
+
+STATIC void wpp_isr(sdla_t * card)
 {
-	ppp_flags_t* flags = card->flags;
+	ppp_flags_t *flags = card->flags;
 	char *ptr = &flags->iflag;
 	unsigned long host_cpu_flags;
-	struct device* dev = card->wandev.dev;
+	struct device *dev = card->wandev.dev;
 	int i;
-	
 	card->in_isr = 1;
-	
 	++card->statistics.isr_entry;
-
-	if (set_bit(0, (void*)&card->wandev.critical)) {
-        	
+	if (test_and_set_bit(0, (void *) &card->wandev.critical)) {
 		++card->statistics.isr_already_critical;
-		printk (KERN_INFO "%s: Critical while in ISR!\n",card->devname);
+		printk(KERN_INFO "%s: Critical while in ISR!\n", card->devname);
 		card->in_isr = 0;
 		return;
-	
 	}
-
 	/* For all interrupts set the critical flag to CRITICAL_IN_ISR. 
 	 * If the if_send routine is called with this flag set it will set 
 	 * the enable transmit flag to 1. (for a delayed interrupt) 
 	 */
 	card->wandev.critical = CRITICAL_IN_ISR;
-
 	card->buff_int_mode_unbusy = 0;
-
 	switch (flags->iflag) {
-
-		case 0x01:	/* receive interrupt */
-			++card->statistics.isr_rx;
-			rx_intr(card);
-			break;
-
-		case 0x02:	/* transmit interrupt */
-			++card->statistics.isr_tx;
-			flags->imask &= ~0x02;
-			dev->tbusy = 0;		
-			card->buff_int_mode_unbusy = 1;
-			break;
-
-		case 0x08:
-			++Intr_test_counter;
-			++card->statistics.isr_intr_test;
-			break;
-
-		default:	/* unexpected interrupt */
-			++card->statistics.isr_spurious;
-			printk(KERN_INFO "%s: spurious interrupt 0x%02X!\n", 
-				card->devname, flags->iflag);
-			printk(KERN_INFO "%s: ID Bytes = ",card->devname);
-	 		for(i = 0; i < 8; i ++)
-				printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i));
-			printk(KERN_INFO "\n");	
+	case 0x01:		/* receive interrupt */
+		++card->statistics.isr_rx;
+		rx_intr(card);
+		break;
+	case 0x02:		/* transmit interrupt */
+		++card->statistics.isr_tx;
+		flags->imask &= ~0x02;
+		dev->tbusy = 0;
+		card->buff_int_mode_unbusy = 1;
+		break;
+	case 0x08:
+		++Intr_test_counter;
+		++card->statistics.isr_intr_test;
+		break;
+	default:		/* unexpected interrupt */
+		++card->statistics.isr_spurious;
+		printk(KERN_INFO "%s: spurious interrupt 0x%02X!\n",
+		       card->devname, flags->iflag);
+		printk(KERN_INFO "%s: ID Bytes = ", card->devname);
+		for (i = 0; i < 8; i++)
+			printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i));
+		printk(KERN_INFO "\n");
 	}
-	
 	/* The critical flag is set to CRITICAL_INTR_HANDLED to let the
 	 * if_send call know that the interrupt is handled so that 
 	 * transmit interrupts are not enabled again.
-	 */ 
-
+	 */
 	card->wandev.critical = CRITICAL_INTR_HANDLED;
-
 	/* If the enable transmit interrupt flag is set then enable transmit 
 	 * interrupt on the board. This only goes through if if_send is called 
 	 * and the critical flag is set due to an Interrupt. 
 	 */
-	if(card->wandev.enable_tx_int) {
-
+	if (card->wandev.enable_tx_int) {
 		flags->imask |= 0x02;
 		card->wandev.enable_tx_int = 0;
 		++card->statistics.isr_enable_tx_int;
-	
 	}
 	save_flags(host_cpu_flags);
 	cli();
@@ -1183,236 +1103,174 @@
 	flags->iflag = 0;
 	card->wandev.critical = 0;
 	restore_flags(host_cpu_flags);
-
-	if(card->buff_int_mode_unbusy)	
+	if (card->buff_int_mode_unbusy)
 		mark_bh(NET_BH);
-	
 }
 
 /*============================================================================
  * Receive interrupt handler.
  */
-static void rx_intr (sdla_t* card)
+
+static void rx_intr(sdla_t * card)
 {
-	ppp_buf_ctl_t* rxbuf = card->rxmb;
-	struct device* dev = card->wandev.dev;
-	ppp_private_area_t* ppp_priv_area;
-	struct sk_buff* skb;
+	ppp_buf_ctl_t *rxbuf = card->rxmb;
+	struct device *dev = card->wandev.dev;
+	ppp_private_area_t *ppp_priv_area;
+	struct sk_buff *skb;
 	unsigned len;
-	void* buf;
+	void *buf;
 	int i, err;
-        ppp_flags_t* flags = card->flags;
-        char *ptr = &flags->iflag;
+	ppp_flags_t *flags = card->flags;
+	char *ptr = &flags->iflag;
 	int udp_type;
-	
-
 	if (rxbuf->flag != 0x01) {
-	
-
-		printk(KERN_INFO 
-			"%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n", 
-			card->devname, (unsigned)rxbuf, rxbuf->flag);
-	
-		printk(KERN_INFO "%s: ID Bytes = ",card->devname);
-	 	
-		for(i = 0; i < 8; i ++)
+		printk(KERN_INFO
+		       "%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n",
+		       card->devname, (unsigned) rxbuf, rxbuf->flag);
+		printk(KERN_INFO "%s: ID Bytes = ", card->devname);
+		for (i = 0; i < 8; i++)
 			printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i));
-		printk(KERN_INFO "\n");	
-		
+		printk(KERN_INFO "\n");
 		++card->statistics.rx_intr_corrupt_rx_bfr;
 		return;
-
 	}
-	
-
 	if (dev && dev->start) {
-	
-		len  = rxbuf->length;
+		len = rxbuf->length;
 		ppp_priv_area = dev->priv;
-
 		/* Allocate socket buffer */
 		skb = dev_alloc_skb(len);
-
 		if (skb != NULL) {
-		
 			/* Copy data to the socket buffer */
 			if (card->hw.fwid == SFID_PPP502) {
-		
-				unsigned addr = (rxbuf->buf.o_p[1] << 8) + 
-						rxbuf->buf.o_p[0]; 
+				unsigned addr = (rxbuf->buf.o_p[1] << 8) +
+				rxbuf->buf.o_p[0];
 				buf = skb_put(skb, len);
 				sdla_peek(&card->hw, addr, buf, len);
-
 			} else {
-		
 				unsigned addr = rxbuf->buf.ptr;
-
 				if ((addr + len) > card->u.p.rx_top + 1) {
-			
-					unsigned tmp = card->u.p.rx_top - addr 
-							+ 1;
+					unsigned tmp = card->u.p.rx_top - addr
+					+ 1;
 					buf = skb_put(skb, tmp);
 					sdla_peek(&card->hw, addr, buf, tmp);
 					addr = card->u.p.rx_base;
 					len -= tmp;
-
 				}
-		
 				buf = skb_put(skb, len);
 				sdla_peek(&card->hw, addr, buf, len);
 			}
-
 			/* Decapsulate packet */
-        		switch (rxbuf->proto) {
-	
-				case 0x00:
-					skb->protocol = htons(ETH_P_IP);
-					break;
-
-				case 0x01:
-					skb->protocol = htons(ETH_P_IPX);
-					break;
+			switch (rxbuf->proto) {
+			case 0x00:
+				skb->protocol = htons(ETH_P_IP);
+				break;
+			case 0x01:
+				skb->protocol = htons(ETH_P_IPX);
+				break;
 			}
-#ifdef CONFIG_SANGOMA_MANAGER
-			udp_type = udp_pkt_type( skb, card );
-
-			if (udp_type == UDP_DRVSTATS_TYPE){ 
-				++ppp_priv_area->rx_intr_DRVSTATS_request; 
+			udp_type = udp_pkt_type(skb, card);
+			if (udp_type == UDP_DRVSTATS_TYPE) {
+				++ppp_priv_area->rx_intr_DRVSTATS_request;
 				process_udp_driver_call(
-					UDP_PKT_FRM_NETWORK, card, skb,
-					dev, ppp_priv_area);
-                                 dev_kfree_skb(skb);
-
-			} else if (udp_type == UDP_PTPIPE_TYPE){
+					  UDP_PKT_FRM_NETWORK, card, skb,
+						     dev, ppp_priv_area);
+				dev_kfree_skb(skb);
+			} else if (udp_type == UDP_PTPIPE_TYPE) {
 				++ppp_priv_area->rx_intr_PTPIPE_request;
 				err = process_udp_mgmt_pkt(
-					UDP_PKT_FRM_NETWORK, card, 
-					skb, dev, ppp_priv_area);
+					       UDP_PKT_FRM_NETWORK, card,
+						skb, dev, ppp_priv_area);
 				dev_kfree_skb(skb);
-			} else
-#endif
-				if (handle_IPXWAN(skb->data,card->devname, card->wandev.enable_IPX, card->wandev.network_number, skb->protocol)) {
-				
-				if( card->wandev.enable_IPX) {
+			} else if (handle_IPXWAN(skb->data, card->devname, card->wandev.enable_IPX, card->wandev.network_number, skb->protocol)) {
+				if (card->wandev.enable_IPX) {
 					ppp_send(card, skb->data, skb->len, ETH_P_IPX);
-                        		dev_kfree_skb(skb);
-
+					dev_kfree_skb(skb);
 				} else {
 					++card->wandev.stats.rx_dropped;
 				}
 			} else {
 				/* Pass it up the protocol stack */
-	    			skb->dev = dev;
-				skb->mac.raw  = skb->data;
-	    			netif_rx(skb);
-			    	++card->wandev.stats.rx_packets;
-		    		++ppp_priv_area->rx_intr_bfr_passed_to_stack;	
+				skb->dev = dev;
+				skb->mac.raw = skb->data;
+				netif_rx(skb);
+				++card->wandev.stats.rx_packets;
+				++ppp_priv_area->rx_intr_bfr_passed_to_stack;
 			}
-
 		} else {
-		
 			printk(KERN_INFO "%s: no socket buffers available!\n",
-				card->devname);
+			       card->devname);
 			++card->wandev.stats.rx_dropped;
 			++ppp_priv_area->rx_intr_no_socket;
-
 		}
-
 	} else
 		++card->statistics.rx_intr_dev_not_started;
-
 	/* Release buffer element and calculate a pointer to the next one */
 	rxbuf->flag = (card->hw.fwid == SFID_PPP502) ? 0xFF : 0x00;
 	card->rxmb = ++rxbuf;
-	
-	if ((void*)rxbuf > card->u.p.rxbuf_last)
+	if ((void *) rxbuf > card->u.p.rxbuf_last)
 		card->rxmb = card->u.p.rxbuf_base;
 }
 
 /*============================================================================
  * Transmit interrupt handler.
  */
-static void tx_intr (sdla_t* card)
-{
-	struct device* dev = card->wandev.dev;
 
-	if (!dev || !dev->start) 
-	{
+static void tx_intr(sdla_t * card)
+{
+	struct device *dev = card->wandev.dev;
+	if (!dev || !dev->start) {
 		++card->statistics.tx_intr_dev_not_started;
-        	return;
-     	}
-
+		return;
+	}
 	dev->tbusy = 0;
-	dev_tint(dev);
+	mark_bh(NET_BH);
 }
 
 static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto)
 {
 	int i;
-
-	if( proto == htons(ETH_P_IPX) ) 
-	{
+	if (proto == htons(ETH_P_IPX)) {
 		/* It's an IPX packet */
-		if(!enable_IPX) 
-		{
-			//Return 1 so we don't pass it up the stack.
+		if (!enable_IPX) {
+			/* Return 1 so we don't pass it up the stack. */
 			return 1;
 		}
-	}
-	else
-	{
+	} else {
 		/* It's not IPX so pass it up the stack. */
 		return 0;
 	}
-
-	if( sendpacket[16] == 0x90 &&
-	    sendpacket[17] == 0x04)
-	{
+	if (sendpacket[16] == 0x90 &&
+	    sendpacket[17] == 0x04) {
 		/* It's IPXWAN */
-
-		if( sendpacket[2] == 0x02 &&
-		    sendpacket[34] == 0x00)
-		{
+		if (sendpacket[2] == 0x02 &&
+		    sendpacket[34] == 0x00) {
 			/* It's a timer request packet */
-			printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname);
-
+			printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n", devname);
 			/* Go through the routing options and answer no to every */
 			/* option except Unnumbered RIP/SAP */
-			for(i = 41; sendpacket[i] == 0x00; i += 5)
-			{
+			for (i = 41; sendpacket[i] == 0x00; i += 5) {
 				/* 0x02 is the option for Unnumbered RIP/SAP */
-				if( sendpacket[i + 4] != 0x02)
-				{
+				if (sendpacket[i + 4] != 0x02) {
 					sendpacket[i + 1] = 0;
 				}
 			}
-
 			/* Skip over the extended Node ID option */
-			if( sendpacket[i] == 0x04 )
-			{
+			if (sendpacket[i] == 0x04) {
 				i += 8;
 			}
-
 			/* We also want to turn off all header compression opt. */
-			for(; sendpacket[i] == 0x80 ;)
-			{
+			for (; sendpacket[i] == 0x80;) {
 				sendpacket[i + 1] = 0;
 				i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4;
 			}
-
 			/* Set the packet type to timer response */
 			sendpacket[34] = 0x01;
-
-			printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname);
-		}
-		else if( sendpacket[34] == 0x02 )
-		{
+			printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n", devname);
+		} else if (sendpacket[34] == 0x02) {
 			/* This is an information request packet */
-			printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname);
-
+			printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n", devname);
 			/* Set the packet type to information response */
 			sendpacket[34] = 0x03;
-
 			/* Set the router name */
 			sendpacket[51] = 'P';
 			sendpacket[52] = 'T';
@@ -1422,38 +1280,30 @@
 			sendpacket[56] = 'E';
 			sendpacket[57] = '-';
 			sendpacket[58] = CVHexToAscii(network_number >> 28);
-			sendpacket[59] = CVHexToAscii((network_number & 0x0F000000)>> 24);
-			sendpacket[60] = CVHexToAscii((network_number & 0x00F00000)>> 20);
-			sendpacket[61] = CVHexToAscii((network_number & 0x000F0000)>> 16);
-			sendpacket[62] = CVHexToAscii((network_number & 0x0000F000)>> 12);
-			sendpacket[63] = CVHexToAscii((network_number & 0x00000F00)>> 8);
-			sendpacket[64] = CVHexToAscii((network_number & 0x000000F0)>> 4);
+			sendpacket[59] = CVHexToAscii((network_number & 0x0F000000) >> 24);
+			sendpacket[60] = CVHexToAscii((network_number & 0x00F00000) >> 20);
+			sendpacket[61] = CVHexToAscii((network_number & 0x000F0000) >> 16);
+			sendpacket[62] = CVHexToAscii((network_number & 0x0000F000) >> 12);
+			sendpacket[63] = CVHexToAscii((network_number & 0x00000F00) >> 8);
+			sendpacket[64] = CVHexToAscii((network_number & 0x000000F0) >> 4);
 			sendpacket[65] = CVHexToAscii(network_number & 0x0000000F);
-			for(i = 66; i < 99; i+= 1)
-			{
+			for (i = 66; i < 99; i += 1)
 				sendpacket[i] = 0;
-			}
-
-			printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname);
-		}
-		else
-		{
-			printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname);
+			printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n", devname);
+		} else {
+			printk(KERN_INFO "%s: Unknown IPXWAN packet!\n", devname);
 			return 0;
 		}
-
 		/* Set the WNodeID to our network address */
-		sendpacket[35] = (unsigned char)(network_number >> 24);
-		sendpacket[36] = (unsigned char)((network_number & 0x00FF0000) >> 16);
-		sendpacket[37] = (unsigned char)((network_number & 0x0000FF00) >> 8);
-		sendpacket[38] = (unsigned char)(network_number & 0x000000FF);
-
+		sendpacket[35] = (unsigned char) (network_number >> 24);
+		sendpacket[36] = (unsigned char) ((network_number & 0x00FF0000) >> 16);
+		sendpacket[37] = (unsigned char) ((network_number & 0x0000FF00) >> 8);
+		sendpacket[38] = (unsigned char) (network_number & 0x000000FF);
 		return 1;
 	} else {
 		/* If we get here's its an IPX-data packet, so it'll get passed up the stack. */
-
 		/* switch the network numbers */
-		switch_net_numbers(sendpacket, network_number, 1);	
+		switch_net_numbers(sendpacket, network_number, 1);
 		return 0;
 	}
 }
@@ -1469,100 +1319,80 @@
  * 1. This routine may be called on interrupt context with all interrupts
  *    enabled. Beware!
  */
-static void wpp_poll (sdla_t* card)
+
+static void wpp_poll(sdla_t * card)
 {
-	struct device* dev = card->wandev.dev;
-	ppp_flags_t* adptr_flags = card->flags;
+	struct device *dev = card->wandev.dev;
+	ppp_flags_t *adptr_flags = card->flags;
 	unsigned long host_cpu_flags;
-
 	++card->statistics.poll_entry;
-
 	/* The wpp_poll is called continously by the WANPIPE thread to allow
 	 * for line state housekeeping. However if we are in a connected state
 	 * then we do not need to go through all the checks everytime. When in
 	 * connected state execute wpp_poll once every second.
 	 */
-
-	if (card->wandev.state == WAN_CONNECTED)
-	{ 	
-		if ((jiffies - card->state_tick) < HZ )
-         		return;
+	if (card->wandev.state == WAN_CONNECTED) {
+		if ((jiffies - card->state_tick) < HZ)
+			return;
 	}
-
- 	disable_irq(card->hw.irq);
-  	++card->irq_dis_poll_count; 
-
-	if (set_bit(0, (void *)&card->wandev.critical)) 
-	{
+	disable_irq(card->hw.irq);
+	++card->irq_dis_poll_count;
+	if (test_and_set_bit(0, (void *) &card->wandev.critical)) {
 		++card->statistics.poll_already_critical;
-		printk(KERN_INFO "%s: critical inside wpp_poll\n", 
-			card->devname);
+		printk(KERN_INFO "%s: critical inside wpp_poll\n",
+		       card->devname);
 		save_flags(host_cpu_flags);
-                cli();
-                if ((!card->irq_dis_if_send_count) &&
-                                (!(--card->irq_dis_poll_count)))
-                        enable_irq(card->hw.irq);
-                restore_flags(host_cpu_flags);
-
+		cli();
+		if ((!card->irq_dis_if_send_count) &&
+		    (!(--card->irq_dis_poll_count)))
+			enable_irq(card->hw.irq);
+		restore_flags(host_cpu_flags);
 		return;
 	}
-
-   	++card->statistics.poll_processed;
-    	
-    	if (dev && dev->tbusy && !(adptr_flags->imask & 0x02)) 
-    	{	
+	++card->statistics.poll_processed;
+	if (dev && dev->tbusy && !(adptr_flags->imask & 0x02)) {
 		++card->statistics.poll_tbusy_bad_status;
 		printk(KERN_INFO "%s: Wpp_Poll: tbusy = 0x01, imask = 0x%02X\n"
-			, card->devname, adptr_flags->imask);
-    	}
-
-	switch(card->wandev.state)
-	{
-		case WAN_CONNECTED:
-			card->state_tick = jiffies;
-			poll_active(card);
-			break;
-
-		case WAN_CONNECTING:
-			poll_connecting(card);
-			break;
-
-		case WAN_DISCONNECTED:
-			poll_disconnected(card);
-			break;
-
-		default:
-			printk(KERN_INFO "%s: Unknown Poll State 0x%02X\n", 
-				card->devname, card->wandev.state);
-			break;
+		       ,card->devname, adptr_flags->imask);
+	}
+	switch (card->wandev.state) {
+	case WAN_CONNECTED:
+		card->state_tick = jiffies;
+		poll_active(card);
+		break;
+	case WAN_CONNECTING:
+		poll_connecting(card);
+		break;
+	case WAN_DISCONNECTED:
+		poll_disconnected(card);
+		break;
+	default:
+		printk(KERN_INFO "%s: Unknown Poll State 0x%02X\n",
+		       card->devname, card->wandev.state);
+		break;
 	}
-
 	card->wandev.critical = 0;
- 	
 	save_flags(host_cpu_flags);
-        cli();
-        if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count)))
-                enable_irq(card->hw.irq);
-        restore_flags(host_cpu_flags);
-
+	cli();
+	if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count)))
+		enable_irq(card->hw.irq);
+	restore_flags(host_cpu_flags);
 }
 
 /*============================================================================
  * Monitor active link phase.
  */
-static void poll_active (sdla_t* card)
-{
-	ppp_flags_t* flags = card->flags;
 
+static void poll_active(sdla_t * card)
+{
+	ppp_flags_t *flags = card->flags;
 	/* We check the lcp_state to see if we are in DISCONNECTED state.
 	 * We are considered to be connected for lcp states 0x06, 0x07, 0x08
 	 * and 0x09.
 	 */
 	if ((flags->lcp_state <= 0x05) || (flags->disc_cause & 0x03)) {
-
 		wanpipe_set_state(card, WAN_DISCONNECTED);
 		show_disc_cause(card, flags->disc_cause);
-	
 	}
 }
 
@@ -1570,16 +1400,13 @@
  * Monitor link establishment phase.
  * o if connection timed out, disconnect the link.
  */
-static void poll_connecting (sdla_t* card)
+
+static void poll_connecting(sdla_t * card)
 {
-	ppp_flags_t* flags = card->flags;
-	
-	if (flags->lcp_state == 0x09) 
-	{
+	ppp_flags_t *flags = card->flags;
+	if (flags->lcp_state == 0x09) {
 		wanpipe_set_state(card, WAN_CONNECTED);
-	}
-	else if (flags->disc_cause & 0x03)
-	{	
+	} else if (flags->disc_cause & 0x03) {
 		wanpipe_set_state(card, WAN_DISCONNECTED);
 		show_disc_cause(card, flags->disc_cause);
 	}
@@ -1590,16 +1417,15 @@
  *  o if interface is up and the hold-down timeout has expired, then retry
  *    connection.
  */
-static void poll_disconnected (sdla_t* card)
-{
-	struct device* dev = card->wandev.dev;
 
+static void poll_disconnected(sdla_t * card)
+{
+	struct device *dev = card->wandev.dev;
 	if (dev && dev->start &&
-	    ((jiffies - card->state_tick) > HOLD_DOWN_TIME)) 
-	{	
-		wanpipe_set_state(card, WAN_CONNECTING);	
-		if(ppp_comm_enable(card) == CMD_OK)
-			init_ppp_tx_rx_buff( card );
+	    ((jiffies - card->state_tick) > HOLD_DOWN_TIME)) {
+		wanpipe_set_state(card, WAN_CONNECTING);
+		if (ppp_comm_enable(card) == CMD_OK)
+			init_ppp_tx_rx_buff(card);
 	}
 }
 
@@ -1608,319 +1434,818 @@
 /*============================================================================
  * Configure S502 adapter.
  */
-static int config502 (sdla_t* card)
+
+static int config502(sdla_t * card)
 {
 	ppp502_conf_t cfg;
-
 	/* Prepare PPP configuration structure */
 	memset(&cfg, 0, sizeof(ppp502_conf_t));
-
 	if (card->wandev.clocking)
 		cfg.line_speed = bps_to_speed_code(card->wandev.bps);
-		
-	cfg.txbuf_num		= 4;
-	cfg.mtu_local		= card->wandev.mtu;
-	cfg.mtu_remote		= card->wandev.mtu;
-	cfg.restart_tmr		= 30;
-	cfg.auth_rsrt_tmr	= 30;
-	cfg.auth_wait_tmr	= 300;
-	cfg.mdm_fail_tmr	= 5;
-	cfg.dtr_drop_tmr	= 1;
-	cfg.connect_tmout	= 0;	/* changed it from 900 */
-	cfg.conf_retry		= 10;
-	cfg.term_retry		= 2;
-	cfg.fail_retry		= 5;
-	cfg.auth_retry		= 10;
-	cfg.ip_options		= 0x80;
-	cfg.ipx_options		= 0xA0;
-        cfg.conf_flags         |= 0x0E;
+	cfg.txbuf_num = 4;
+	cfg.mtu_local = card->wandev.mtu;
+	cfg.mtu_remote = card->wandev.mtu;
+	cfg.restart_tmr = 30;
+	cfg.auth_rsrt_tmr = 30;
+	cfg.auth_wait_tmr = 300;
+	cfg.mdm_fail_tmr = 5;
+	cfg.dtr_drop_tmr = 1;
+	cfg.connect_tmout = 0;	/* changed it from 900 */
+	cfg.conf_retry = 10;
+	cfg.term_retry = 2;
+	cfg.fail_retry = 5;
+	cfg.auth_retry = 10;
+	cfg.ip_options = 0x80;
+	cfg.ipx_options = 0xA0;
+	cfg.conf_flags |= 0x0E;
 /*
-	cfg.ip_local		= dev->pa_addr;
-	cfg.ip_remote		= dev->pa_dstaddr;
-*/
+   cfg.ip_local         = dev->pa_addr;
+   cfg.ip_remote                = dev->pa_dstaddr;
+ */
 	return ppp_configure(card, &cfg);
 }
 
 /*============================================================================
  * Configure S508 adapter.
  */
-static int config508 (sdla_t* card)
+
+static int config508(sdla_t * card)
 {
 	ppp508_conf_t cfg;
-
 	/* Prepare PPP configuration structure */
 	memset(&cfg, 0, sizeof(ppp508_conf_t));
-
 	if (card->wandev.clocking)
 		cfg.line_speed = card->wandev.bps;
-
 	if (card->wandev.interface == WANOPT_RS232)
 		cfg.conf_flags |= 0x0020;
-
-        cfg.conf_flags 	|= 0x300; /*send Configure-Request packets forever*/
-	cfg.txbuf_percent	= 60;	/* % of Tx bufs */
-	cfg.mtu_local		= card->wandev.mtu;
-	cfg.mtu_remote		= card->wandev.mtu;
-	cfg.restart_tmr		= 30;
-	cfg.auth_rsrt_tmr	= 30;
-	cfg.auth_wait_tmr	= 300;
-	cfg.mdm_fail_tmr	= 100;
-	cfg.dtr_drop_tmr	= 1;
-	cfg.connect_tmout	= 0; 	/* changed it from 900 */
-	cfg.conf_retry		= 10;
-	cfg.term_retry		= 2;
-	cfg.fail_retry		= 5;
-	cfg.auth_retry		= 10;
-	cfg.ip_options		= 0x80;
-	cfg.ipx_options		= 0xA0;
+	cfg.conf_flags |= 0x300;	/*send Configure-Request packets forever */
+	cfg.txbuf_percent = 60;	/* % of Tx bufs */
+	cfg.mtu_local = card->wandev.mtu;
+	cfg.mtu_remote = card->wandev.mtu;
+	cfg.restart_tmr = 30;
+	cfg.auth_rsrt_tmr = 30;
+	cfg.auth_wait_tmr = 300;
+	cfg.mdm_fail_tmr = 100;
+	cfg.dtr_drop_tmr = 1;
+	cfg.connect_tmout = 0;	/* changed it from 900 */
+	cfg.conf_retry = 10;
+	cfg.term_retry = 2;
+	cfg.fail_retry = 5;
+	cfg.auth_retry = 10;
+	cfg.ip_options = 0x80;
+	cfg.ipx_options = 0xA0;
 /*
-	cfg.ip_local		= dev->pa_addr;
-	cfg.ip_remote		= dev->pa_dstaddr;
-*/
+   cfg.ip_local         = dev->pa_addr;
+   cfg.ip_remote                = dev->pa_dstaddr;
+ */
 	return ppp_configure(card, &cfg);
 }
 
 /*============================================================================
  * Show disconnection cause.
  */
-static void show_disc_cause (sdla_t* card, unsigned cause)
-{
-	if (cause & 0x0002) 
-		printk(KERN_INFO "%s: link terminated by peer\n", 
-			card->devname);
-
-	else if (cause & 0x0004) 
-		printk(KERN_INFO "%s: link terminated by user\n", 
-			card->devname);
 
-	else if (cause & 0x0008) 
+static void show_disc_cause(sdla_t * card, unsigned cause)
+{
+	if (cause & 0x0002)
+		printk(KERN_INFO "%s: link terminated by peer\n",
+		       card->devname);
+	else if (cause & 0x0004)
+		printk(KERN_INFO "%s: link terminated by user\n",
+		       card->devname);
+	else if (cause & 0x0008)
 		printk(KERN_INFO "%s: authentication failed\n", card->devname);
-	
 	else if (cause & 0x0010)
-		printk(KERN_INFO 
-			"%s: authentication protocol negotiation failed\n", 
-			card->devname);
-
-	else if (cause & 0x0020) 
 		printk(KERN_INFO
-		"%s: peer's request for authentication rejected\n",
-		card->devname);
-
-	else if (cause & 0x0040) 
-		printk(KERN_INFO "%s: MRU option rejected by peer\n", 
-		card->devname);
-
-	else if (cause & 0x0080) 
-		printk(KERN_INFO "%s: peer's MRU was too small\n", 
-		card->devname);
-
-	else if (cause & 0x0100) 
+		       "%s: authentication protocol negotiation failed\n",
+		       card->devname);
+	else if (cause & 0x0020)
+		printk(KERN_INFO
+		       "%s: peer's request for authentication rejected\n",
+		       card->devname);
+	else if (cause & 0x0040)
+		printk(KERN_INFO "%s: MRU option rejected by peer\n",
+		       card->devname);
+	else if (cause & 0x0080)
+		printk(KERN_INFO "%s: peer's MRU was too small\n",
+		       card->devname);
+	else if (cause & 0x0100)
 		printk(KERN_INFO "%s: failed to negotiate peer's LCP options\n",
-		card->devname);
-
-	else if (cause & 0x0200) 
+		       card->devname);
+	else if (cause & 0x0200)
 		printk(KERN_INFO "%s: failed to negotiate peer's IPCP options\n"
-		, card->devname);
-
-	else if (cause & 0x0400) 
-		printk(KERN_INFO 
-			"%s: failed to negotiate peer's IPXCP options\n",
-			card->devname);
+		       ,card->devname);
+	else if (cause & 0x0400)
+		printk(KERN_INFO
+		       "%s: failed to negotiate peer's IPXCP options\n",
+		       card->devname);
 }
 
 /*============================================================================
  * Convert line speed in bps to a number used by S502 code.
  */
-static unsigned char bps_to_speed_code (unsigned long bps)
-{
-	unsigned char	number;
 
-	if (bps <= 1200)        
+static unsigned char bps_to_speed_code(unsigned long bps)
+{
+	unsigned char number;
+	if (bps <= 1200)
 		number = 0x01;
-	else if (bps <= 2400)   
+	else if (bps <= 2400)
 		number = 0x02;
-	else if (bps <= 4800)   
+	else if (bps <= 4800)
 		number = 0x03;
-	else if (bps <= 9600)   
+	else if (bps <= 9600)
 		number = 0x04;
-	else if (bps <= 19200)  
+	else if (bps <= 19200)
 		number = 0x05;
-	else if (bps <= 38400)  
+	else if (bps <= 38400)
 		number = 0x06;
-	else if (bps <= 45000)  
+	else if (bps <= 45000)
 		number = 0x07;
-	else if (bps <= 56000)  
+	else if (bps <= 56000)
 		number = 0x08;
-	else if (bps <= 64000)  
+	else if (bps <= 64000)
 		number = 0x09;
-	else if (bps <= 74000)  
+	else if (bps <= 74000)
 		number = 0x0A;
-	else if (bps <= 112000) 	
+	else if (bps <= 112000)
 		number = 0x0B;
-	else if (bps <= 128000) 
+	else if (bps <= 128000)
 		number = 0x0C;
-	else 
+	else
 		number = 0x0D;
-
 	return number;
 }
 
+/*============================================================================
+ * Process UDP call of type DRVSTATS.  
+ */
+
+static int process_udp_driver_call(char udp_pkt_src, sdla_t * card, struct sk_buff *skb, struct device *dev, ppp_private_area_t * ppp_priv_area)
+{
+	unsigned char *sendpacket;
+	unsigned char buf2[5];
+	unsigned char *data;
+	unsigned char *buf;
+	unsigned int len;
+	ppp_mbox_t *mbox = card->mbox;
+	struct sk_buff *new_skb;
+	int err;
+	sendpacket = skb->data;
+	memcpy(&buf2, &card->wandev.udp_port, 2);
+	if ((data = kmalloc(2000, GFP_ATOMIC)) == NULL) {
+		printk(KERN_INFO
+		       "%s: Error allocating memory for UDP DRIVER STATS cmnd0x%02X"
+		       ,card->devname, data[45]);
+		++ppp_priv_area->UDP_DRVSTATS_mgmt_kmalloc_err;
+		return 1;
+	}
+	memcpy(data, sendpacket, skb->len);
+	switch (data[45]) {
+		/* PPIPE_DRIVER_STATISTICS */
+	case 0x26:
+		*(unsigned long *) &data[60] =
+		    ppp_priv_area->if_send_entry;
+		*(unsigned long *) &data[64] =
+		    ppp_priv_area->if_send_skb_null;
+		*(unsigned long *) &data[68] =
+		    ppp_priv_area->if_send_broadcast;
+		*(unsigned long *) &data[72] =
+		    ppp_priv_area->if_send_multicast;
+		*(unsigned long *) &data[76] =
+		    ppp_priv_area->if_send_critical_ISR;
+		*(unsigned long *) &data[80] =
+		    ppp_priv_area->if_send_critical_non_ISR;
+		*(unsigned long *) &data[84] =
+		    ppp_priv_area->if_send_busy;
+		*(unsigned long *) &data[88] =
+		    ppp_priv_area->if_send_busy_timeout;
+		*(unsigned long *) &data[92] =
+		    ppp_priv_area->if_send_DRVSTATS_request;
+		*(unsigned long *) &data[96] =
+		    ppp_priv_area->if_send_PTPIPE_request;
+		*(unsigned long *) &data[100] =
+		    ppp_priv_area->if_send_wan_disconnected;
+		*(unsigned long *) &data[104] =
+		    ppp_priv_area->if_send_adptr_bfrs_full;
+		*(unsigned long *) &data[108] =
+		    ppp_priv_area->if_send_protocol_error;
+		*(unsigned long *) &data[112] =
+		    ppp_priv_area->if_send_tx_int_enabled;
+		*(unsigned long *) &data[116] =
+		    ppp_priv_area->if_send_bfr_passed_to_adptr;
+		*(unsigned long *) &data[118] =
+		    card->irq_dis_if_send_count;
+		mbox->cmd.length = 62;
+		break;
+	case 0x27:
+		*(unsigned long *) &data[60] = card->statistics.isr_entry;
+		*(unsigned long *) &data[64] =
+		    card->statistics.isr_already_critical;
+		*(unsigned long *) &data[68] = card->statistics.isr_rx;
+		*(unsigned long *) &data[72] = card->statistics.isr_tx;
+		*(unsigned long *) &data[76] =
+		    card->statistics.isr_intr_test;
+		*(unsigned long *) &data[80] =
+		    card->statistics.isr_spurious;
+		*(unsigned long *) &data[84] =
+		    card->statistics.isr_enable_tx_int;
+		*(unsigned long *) &data[88] =
+		    card->statistics.rx_intr_corrupt_rx_bfr;
+		*(unsigned long *) &data[92] =
+		    ppp_priv_area->rx_intr_no_socket;
+		*(unsigned long *) &data[96] =
+		    ppp_priv_area->rx_intr_DRVSTATS_request;
+		*(unsigned long *) &data[100] =
+		    ppp_priv_area->rx_intr_PTPIPE_request;
+		*(unsigned long *) &data[104] =
+		    ppp_priv_area->rx_intr_bfr_passed_to_stack;
+		*(unsigned long *) &data[108] =
+		    card->statistics.rx_intr_dev_not_started;
+		*(unsigned long *) &data[112] =
+		    card->statistics.tx_intr_dev_not_started;
+		mbox->cmd.length = 56;
+		break;
+	case 0x28:
+		*(unsigned long *) &data[60] =
+		    ppp_priv_area->UDP_PTPIPE_mgmt_kmalloc_err;
+		*(unsigned long *) &data[64] =
+		    ppp_priv_area->UDP_PTPIPE_mgmt_adptr_type_err;
+		*(unsigned long *) &data[68] =
+		    ppp_priv_area->UDP_PTPIPE_mgmt_direction_err;
+		*(unsigned long *) &data[72] =
+		    ppp_priv_area->
+		    UDP_PTPIPE_mgmt_adptr_cmnd_timeout;
+		*(unsigned long *) &data[76] =
+		    ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_OK;
+		*(unsigned long *) &data[80] =
+		    ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_adptr;
+		*(unsigned long *) &data[84] =
+		    ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_stack;
+		*(unsigned long *) &data[88] =
+		    ppp_priv_area->UDP_PTPIPE_mgmt_no_socket;
+		*(unsigned long *) &data[92] =
+		    ppp_priv_area->UDP_DRVSTATS_mgmt_kmalloc_err;
+		*(unsigned long *) &data[96] =
+		    ppp_priv_area->
+		    UDP_DRVSTATS_mgmt_adptr_cmnd_timeout;
+		*(unsigned long *) &data[100] =
+		    ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_OK;
+		*(unsigned long *) &data[104] =
+		    ppp_priv_area->
+		    UDP_DRVSTATS_mgmt_passed_to_adptr;
+		*(unsigned long *) &data[108] =
+		    ppp_priv_area->
+		    UDP_DRVSTATS_mgmt_passed_to_stack;
+		*(unsigned long *) &data[112] =
+		    ppp_priv_area->UDP_DRVSTATS_mgmt_no_socket;
+		*(unsigned long *) &data[116] =
+		    card->statistics.poll_entry;
+		*(unsigned long *) &data[120] =
+		    card->statistics.poll_already_critical;
+		*(unsigned long *) &data[124] =
+		    card->statistics.poll_processed;
+		*(unsigned long *) &data[126] =
+		    card->irq_dis_poll_count;
+		mbox->cmd.length = 70;
+		break;
+	default:
+		/* it's a board command */
+		memcpy(&mbox->cmd, &sendpacket[45], sizeof(ppp_cmd_t));
+		if (mbox->cmd.length) {
+			memcpy(&mbox->data, &sendpacket[60],
+			       mbox->cmd.length);
+		}
+		/* run the command on the board */
+		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+		if (err != CMD_OK) {
+			ppp_error(card, err, mbox);
+			++ppp_priv_area->
+			    UDP_DRVSTATS_mgmt_adptr_cmnd_timeout;
+			break;
+		}
+		++ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_OK;
+		/* copy the result back to our buffer */
+		memcpy(data, sendpacket, skb->len);
+		memcpy(&data[45], &mbox->cmd, sizeof(ppp_cmd_t));
+		if (mbox->cmd.length) {
+			memcpy(&data[60], &mbox->data, mbox->cmd.length);
+		}
+	}
+	/* Fill UDP TTL */
+	data[8] = card->wandev.ttl;
+	len = reply_udp(data, mbox->cmd.length);
+	if (udp_pkt_src == UDP_PKT_FRM_NETWORK) {
+		++ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_adptr;
+		ppp_send(card, data, len, skb->protocol);
+	} else {
+		/* Pass it up the stack
+		   Allocate socket buffer */
+		if ((new_skb = dev_alloc_skb(len)) != NULL) {
+			/* copy data into new_skb */
+			buf = skb_put(new_skb, len);
+			memcpy(buf, data, len);
+			++ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_stack;
+			/* Decapsulate packet and pass it up the protocol 
+			   stack */
+			new_skb->protocol = htons(ETH_P_IP);
+			new_skb->dev = dev;
+			new_skb->mac.raw = new_skb->data;
+			netif_rx(new_skb);
+		} else {
+			++ppp_priv_area->UDP_DRVSTATS_mgmt_no_socket;
+			printk(KERN_INFO "no socket buffers available!\n");
+		}
+	}
+	kfree(data);
+	return 0;
+}
+
+/*=============================================================================
+ * Process UDP call of type PTPIPEAB.
+ */
+
+static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t * card,
+				struct sk_buff *skb, struct device *dev,
+				ppp_private_area_t * ppp_priv_area)
+{
+	unsigned char *sendpacket;
+	unsigned char buf2[5];
+	unsigned char *data;
+	unsigned char *buf;
+	unsigned int frames, len;
+	struct sk_buff *new_skb;
+	unsigned short buffer_length, real_len;
+	unsigned long data_ptr;
+	int udp_mgmt_req_valid = 1;
+	ppp_mbox_t *mbox = card->mbox;
+	struct timeval tv;
+	int err;
+	sendpacket = skb->data;
+	memcpy(&buf2, &card->wandev.udp_port, 2);
+	if ((data = kmalloc(2000, GFP_ATOMIC)) == NULL) {
+		printk(KERN_INFO
+		       "%s: Error allocating memory for UDP management cmnd0x%02X"
+		       ,card->devname, data[45]);
+		++ppp_priv_area->UDP_PTPIPE_mgmt_kmalloc_err;
+		return 1;
+	}
+	memcpy(data, sendpacket, skb->len);
+	switch (data[45]) {
+		/* FT1 MONITOR STATUS */
+	case 0x80:
+		if (card->hw.fwid != SFID_PPP508) {
+			++ppp_priv_area->UDP_PTPIPE_mgmt_adptr_type_err;
+			udp_mgmt_req_valid = 0;
+			break;
+		}
+		/* PPIPE_ENABLE_TRACING */
+	case 0x20:
+		/* PPIPE_DISABLE_TRACING */
+	case 0x21:
+		/* PPIPE_GET_TRACE_INFO */
+	case 0x22:
+		/* SET FT1 MODE */
+	case 0x81:
+		if (udp_pkt_src == UDP_PKT_FRM_NETWORK) {
+			++ppp_priv_area->UDP_PTPIPE_mgmt_direction_err;
+			udp_mgmt_req_valid = 0;
+		}
+		break;
+	default:
+		break;
+	}
+	if (!udp_mgmt_req_valid) {
+		/* set length to 0 */
+		data[46] = data[47] = 0;
+		/* set return code */
+		data[48] = 0xCD;
+	} else {
+		switch (data[45]) {
+			/* PPIPE_ENABLE_TRACING */
+		case 0x20:
+			if (!TracingEnabled) {
+				/* OPERATE_DATALINE_MONITOR */
+				mbox->cmd.command = 0x33;
+				mbox->cmd.length = 1;
+				mbox->data[0] = 0x03;
+				err = sdla_exec(mbox) ?
+				    mbox->cmd.result : CMD_TIMEOUT;
+				if (err != CMD_OK) {
+					ppp_error(card, err, mbox);
+					TracingEnabled = 0;
+					/* set the return code */
+					data[48] = mbox->cmd.result;
+					mbox->cmd.length = 0;
+					break;
+				}
+				if (card->hw.fwid == SFID_PPP502) {
+					sdla_peek(&card->hw, 0x9000, &buf2, 2);
+				} else {
+					sdla_peek(&card->hw, 0xC000, &buf2, 2);
+				}
+				curr_trace_addr = 0;
+				memcpy(&curr_trace_addr, &buf2, 2);
+				start_trace_addr = curr_trace_addr;
+				/* MAX_SEND_BUFFER_SIZE -sizeof(UDP_MGMT_PACKET)
+				   - 41 */
+				available_buffer_space = 1926;
+			}
+			data[48] = 0;
+			mbox->cmd.length = 0;
+			TracingEnabled = 1;
+			break;
+			/* PPIPE_DISABLE_TRACING */
+		case 0x21:
+			if (TracingEnabled) {
+				/* OPERATE_DATALINE_MONITOR */
+				mbox->cmd.command = 0x3;
+				mbox->cmd.length = 1;
+				mbox->data[0] = 0x00;
+				err = sdla_exec(mbox) ?
+				    mbox->cmd.result : CMD_TIMEOUT;
+			}
+			/*set return code */
+			data[48] = 0;
+			mbox->cmd.length = 0;
+			TracingEnabled = 0;
+			break;
+			/* PPIPE_GET_TRACE_INFO */
+		case 0x22:
+			if (TracingEnabled) {
+				buffer_length = 0;
+				/* frames < NUM_TRACE_FRAMES */
+				for (frames = 0; frames < 62; frames += 1) {
+					sdla_peek(&card->hw, curr_trace_addr,
+						  &buf2, 1);
+					/* no data on board so exit */
+					if (buf2[0] == 0x00)
+						break;
+					/*1+sizeof(FRAME_DATA) = 9 */
+					if ((available_buffer_space -
+					     buffer_length) < 9) {
+						/*indicate we have more frames 
+						   on board and exit */
+						data[60] |= 0x02;
+						break;
+					}
+					/* get frame status */
+					sdla_peek(&card->hw, curr_trace_addr +
+						  0x01, &data[60 + buffer_length], 1);
+					/* get time stamp */
+					sdla_peek(&card->hw, curr_trace_addr +
+						  0x06, &data[64 + buffer_length], 2);
+					/* get frame length */
+					sdla_peek(&card->hw, curr_trace_addr +
+						  0x02, &data[62 + buffer_length], 2);
+					/* get pointer to real data */
+					sdla_peek(&card->hw, curr_trace_addr +
+						  0x04, &buf2, 2);
+					data_ptr = 0;
+					memcpy(&data_ptr, &buf2, 2);
+					/* see if we can fit the frame into the 
+					   user buffer */
+					memcpy(&real_len,
+					   &data[62 + buffer_length], 2);
+					if ((data_ptr == 0) ||
+					    ((real_len + 8) >
+					     available_buffer_space)) {
+						data[61 + buffer_length] = 0x00;
+					} else {
+						/* we can take it next time */
+						if ((available_buffer_space -
+						     buffer_length) <
+						    (real_len + 8)) {
+							data[60] |= 0x02;
+							break;
+						}
+						/* ok, get the frame */
+						data[61 + buffer_length] = 0x01;
+						/* get the data */
+						sdla_peek(&card->hw, data_ptr,
+						&data[66 + buffer_length],
+							  real_len);
+						/* zero the opp flag to 
+						   show we got the frame */
+						buf2[0] = 0x00;
+						sdla_poke(&card->hw,
+							  curr_trace_addr, &buf2, 1);
+						/* now move onto the next 
+						   frame */
+						curr_trace_addr += 8;
+						/* check if we passed the last 
+						   address */
+						if (curr_trace_addr >=
+						    start_trace_addr + 0x1F0) {
+							curr_trace_addr =
+							    start_trace_addr;
+						}
+						/* update buffer length and make                                                   sure its even */
+						if (data[61 + buffer_length]
+						    == 0x01) {
+							buffer_length +=
+							    real_len - 1;
+						}
+						/* for the header */
+						buffer_length += 8;
+						if (buffer_length & 0x0001)
+							buffer_length += 1;
+					}
+				}
+				/* ok now set the total number of frames passed
+				   in the high 5 bits */
+				data[60] = (frames << 2) | data[60];
+				/* set the data length */
+				mbox->cmd.length = buffer_length;
+				memcpy(&data[46], &buffer_length, 2);
+				/* set return code */
+				data[48] = 0;
+			} else {
+				/* set return code */
+				data[48] = 1;
+				mbox->cmd.length = 0;
+			}
+			break;
+			/* PPIPE_GET_IBA_DATA */
+		case 0x23:
+			mbox->cmd.length = 0x09;
+			if (card->hw.fwid == SFID_PPP502) {
+				sdla_peek(&card->hw, 0xA003, &data[60],
+					  mbox->cmd.length);
+			} else {
+				sdla_peek(&card->hw, 0xF003, &data[60],
+					  mbox->cmd.length);
+			}
+			/* set the length of the data */
+			data[46] = 0x09;
+			/* set return code */
+			data[48] = 0x00;
+			break;
+			/* PPIPE_KILL_BOARD */
+		case 0x24:
+			break;
+			/* PPIPE_FT1_READ_STATUS */
+		case 0x25:
+			sdla_peek(&card->hw, 0xF020, &data[60], 2);
+			data[46] = 2;
+			data[47] = 0;
+			data[48] = 0;
+			mbox->cmd.length = 2;
+			break;
+		case 0x29:
+			init_ppp_priv_struct(ppp_priv_area);
+			init_global_statistics(card);
+			mbox->cmd.length = 0;
+			break;
+		case 0x30:
+			do_gettimeofday(&tv);
+			ppp_priv_area->router_up_time = tv.tv_sec -
+			    ppp_priv_area->router_start_time;
+			*(unsigned long *) &data[60] =
+			    ppp_priv_area->router_up_time;
+			mbox->cmd.length = 4;
+			break;
+			/* FT1 MONITOR STATUS */
+		case 0x80:
+			/* Enable FT1 MONITOR STATUS */
+			if (data[60] == 1) {
+				if (rCount++ != 0) {
+					data[48] = 0;
+					mbox->cmd.length = 1;
+					break;
+				}
+			}
+			/* Disable FT1 MONITOR STATUS */
+			if (data[60] == 0) {
+				if (--rCount != 0) {
+					data[48] = 0;
+					mbox->cmd.length = 1;
+					break;
+				}
+			}
+		default:
+			/* it's a board command */
+			memcpy(&mbox->cmd, &sendpacket[45], sizeof(ppp_cmd_t));
+			if (mbox->cmd.length) {
+				memcpy(&mbox->data, &sendpacket[60],
+				       mbox->cmd.length);
+			}
+			/* run the command on the board */
+			err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+			if (err != CMD_OK) {
+				ppp_error(card, err, mbox);
+				++ppp_priv_area->
+				    UDP_PTPIPE_mgmt_adptr_cmnd_timeout;
+				break;
+			}
+			++ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_OK;
+			/* copy the result back to our buffer */
+			memcpy(data, sendpacket, skb->len);
+			memcpy(&data[45], &mbox->cmd, sizeof(ppp_cmd_t));
+			if (mbox->cmd.length) {
+				memcpy(&data[60], &mbox->data, mbox->cmd.length);
+			}
+		}		/* end of switch */
+	}			/* end of else */
+	/* Fill UDP TTL */
+	data[8] = card->wandev.ttl;
+	len = reply_udp(data, mbox->cmd.length);
+	if (udp_pkt_src == UDP_PKT_FRM_NETWORK) {
+		++ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_adptr;
+		ppp_send(card, data, len, skb->protocol);
+	} else {
+		/* Pass it up the stack
+		   Allocate socket buffer */
+		if ((new_skb = dev_alloc_skb(len)) != NULL) {
+			/* copy data into new_skb */
+			buf = skb_put(new_skb, len);
+			memcpy(buf, data, len);
+			++ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_stack;
+			/* Decapsulate packet and pass it up the protocol 
+			   stack */
+			new_skb->protocol = htons(ETH_P_IP);
+			new_skb->dev = dev;
+			new_skb->mac.raw = new_skb->data;
+			netif_rx(new_skb);
+		} else {
+			++ppp_priv_area->UDP_PTPIPE_mgmt_no_socket;
+			printk(KERN_INFO "no socket buffers available!\n");
+		}
+	}
+	kfree(data);
+	return 0;
+}
+
 /*=============================================================================
  * Initial the ppp_private_area structure.
  */
 
-static void init_ppp_priv_struct( ppp_private_area_t* ppp_priv_area )
+static void init_ppp_priv_struct(ppp_private_area_t * ppp_priv_area)
 {
-	ppp_priv_area->if_send_entry 			= 0;
-	ppp_priv_area->if_send_skb_null 		= 0;
-	ppp_priv_area->if_send_broadcast 		= 0;
-	ppp_priv_area->if_send_multicast 		= 0;
-	ppp_priv_area->if_send_critical_ISR 		= 0;
-	ppp_priv_area->if_send_critical_non_ISR 	= 0;
-	ppp_priv_area->if_send_busy 			= 0;
-	ppp_priv_area->if_send_busy_timeout 		= 0;
-	ppp_priv_area->if_send_DRVSTATS_request		= 0;
-	ppp_priv_area->if_send_PTPIPE_request 		= 0;
-	ppp_priv_area->if_send_wan_disconnected 	= 0;
-	ppp_priv_area->if_send_adptr_bfrs_full 		= 0;
-	ppp_priv_area->if_send_bfr_passed_to_adptr 	= 0;
-	
-	ppp_priv_area->rx_intr_no_socket 		= 0;
-	ppp_priv_area->rx_intr_DRVSTATS_request 	= 0;
-	ppp_priv_area->rx_intr_PTPIPE_request 		= 0;
-	ppp_priv_area->rx_intr_bfr_not_passed_to_stack 	= 0;
-	ppp_priv_area->rx_intr_bfr_passed_to_stack 	= 0;
-
-	ppp_priv_area->UDP_PTPIPE_mgmt_kmalloc_err 		= 0;
-	ppp_priv_area->UDP_PTPIPE_mgmt_adptr_type_err 		= 0;
-	ppp_priv_area->UDP_PTPIPE_mgmt_direction_err 		= 0;
-	ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_timeout	= 0;
-	ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_OK 		= 0;
-	ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_adptr 		= 0;
-	ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_stack 		= 0;
-	ppp_priv_area->UDP_PTPIPE_mgmt_no_socket 		= 0; 	
-
-	ppp_priv_area->UDP_DRVSTATS_mgmt_kmalloc_err 		= 0;
-	ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_type_err 	= 0;
-	ppp_priv_area->UDP_DRVSTATS_mgmt_direction_err 		= 0;
-	ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout	= 0;
-	ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_OK 		= 0;
-	ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_adptr 	= 0;
-	ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_stack 	= 0;
-	ppp_priv_area->UDP_DRVSTATS_mgmt_no_socket 		= 0; 	
+	ppp_priv_area->if_send_entry = 0;
+	ppp_priv_area->if_send_skb_null = 0;
+	ppp_priv_area->if_send_broadcast = 0;
+	ppp_priv_area->if_send_multicast = 0;
+	ppp_priv_area->if_send_critical_ISR = 0;
+	ppp_priv_area->if_send_critical_non_ISR = 0;
+	ppp_priv_area->if_send_busy = 0;
+	ppp_priv_area->if_send_busy_timeout = 0;
+	ppp_priv_area->if_send_DRVSTATS_request = 0;
+	ppp_priv_area->if_send_PTPIPE_request = 0;
+	ppp_priv_area->if_send_wan_disconnected = 0;
+	ppp_priv_area->if_send_adptr_bfrs_full = 0;
+	ppp_priv_area->if_send_bfr_passed_to_adptr = 0;
+	ppp_priv_area->rx_intr_no_socket = 0;
+	ppp_priv_area->rx_intr_DRVSTATS_request = 0;
+	ppp_priv_area->rx_intr_PTPIPE_request = 0;
+	ppp_priv_area->rx_intr_bfr_not_passed_to_stack = 0;
+	ppp_priv_area->rx_intr_bfr_passed_to_stack = 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_kmalloc_err = 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_adptr_type_err = 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_direction_err = 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_timeout = 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_OK = 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_adptr = 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_stack = 0;
+	ppp_priv_area->UDP_PTPIPE_mgmt_no_socket = 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_kmalloc_err = 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_type_err = 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_direction_err = 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout = 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_OK = 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_adptr = 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_stack = 0;
+	ppp_priv_area->UDP_DRVSTATS_mgmt_no_socket = 0;
 }
 
 /*============================================================================
  * Initialize Global Statistics
  */
-static void init_global_statistics( sdla_t* card )
+
+static void init_global_statistics(sdla_t * card)
 {
-	card->statistics.isr_entry 		= 0;
-	card->statistics.isr_already_critical 	= 0;
-	card->statistics.isr_tx 		= 0;
-	card->statistics.isr_rx 		= 0;
-	card->statistics.isr_intr_test		= 0;
-	card->statistics.isr_spurious 		= 0;
-	card->statistics.isr_enable_tx_int 	= 0;
+	card->statistics.isr_entry = 0;
+	card->statistics.isr_already_critical = 0;
+	card->statistics.isr_tx = 0;
+	card->statistics.isr_rx = 0;
+	card->statistics.isr_intr_test = 0;
+	card->statistics.isr_spurious = 0;
+	card->statistics.isr_enable_tx_int = 0;
 	card->statistics.rx_intr_corrupt_rx_bfr = 0;
-	card->statistics.rx_intr_dev_not_started= 0;
-	card->statistics.tx_intr_dev_not_started= 0;
-	card->statistics.poll_entry		= 0;
-	card->statistics.poll_already_critical 	= 0;
-	card->statistics.poll_processed 	= 0;
-	card->statistics.poll_tbusy_bad_status 	= 0;
-
+	card->statistics.rx_intr_dev_not_started = 0;
+	card->statistics.tx_intr_dev_not_started = 0;
+	card->statistics.poll_entry = 0;
+	card->statistics.poll_already_critical = 0;
+	card->statistics.poll_processed = 0;
+	card->statistics.poll_tbusy_bad_status = 0;
 }
 
 /*============================================================================
  * Initialize Receive and Transmit Buffers.
  */
-static void init_ppp_tx_rx_buff( sdla_t* card )
-{
-
-	if (card->hw.fwid == SFID_PPP502) 
-	{	
-		ppp502_buf_info_t* info =
-			(void*)(card->hw.dpmbase + PPP502_BUF_OFFS);
-
-		card->u.p.txbuf_base = 
-			(void*)(card->hw.dpmbase + info->txb_offs);
-
-		card->u.p.txbuf_last = (ppp_buf_ctl_t*)card->u.p.txbuf_base +
-			(info->txb_num - 1);
-
-		card->u.p.rxbuf_base = 
-			(void*)(card->hw.dpmbase + info->rxb_offs);
-
-		card->u.p.rxbuf_last = (ppp_buf_ctl_t*)card->u.p.rxbuf_base +
-			(info->rxb_num - 1);
-	}
-	else
-	{	
-		ppp508_buf_info_t* info =
-			(void*)(card->hw.dpmbase + PPP508_BUF_OFFS);
-
-		card->u.p.txbuf_base = (void*)(card->hw.dpmbase +
-			(info->txb_ptr - PPP508_MB_VECT));
-
-		card->u.p.txbuf_last = (ppp_buf_ctl_t*)card->u.p.txbuf_base +
-			(info->txb_num - 1);
-
-		card->u.p.rxbuf_base = (void*)(card->hw.dpmbase +
-			(info->rxb_ptr - PPP508_MB_VECT));
-
-		card->u.p.rxbuf_last = (ppp_buf_ctl_t*)card->u.p.rxbuf_base +
-			(info->rxb_num - 1);
 
+static void init_ppp_tx_rx_buff(sdla_t * card)
+{
+	if (card->hw.fwid == SFID_PPP502) {
+		ppp502_buf_info_t *info =
+		(void *) (card->hw.dpmbase + PPP502_BUF_OFFS);
+		card->u.p.txbuf_base =
+		    (void *) (card->hw.dpmbase + info->txb_offs);
+		card->u.p.txbuf_last = (ppp_buf_ctl_t *) card->u.p.txbuf_base +
+		    (info->txb_num - 1);
+		card->u.p.rxbuf_base =
+		    (void *) (card->hw.dpmbase + info->rxb_offs);
+		card->u.p.rxbuf_last = (ppp_buf_ctl_t *) card->u.p.rxbuf_base +
+		    (info->rxb_num - 1);
+	} else {
+		ppp508_buf_info_t *info =
+		(void *) (card->hw.dpmbase + PPP508_BUF_OFFS);
+		card->u.p.txbuf_base = (void *) (card->hw.dpmbase +
+				       (info->txb_ptr - PPP508_MB_VECT));
+		card->u.p.txbuf_last = (ppp_buf_ctl_t *) card->u.p.txbuf_base +
+		    (info->txb_num - 1);
+		card->u.p.rxbuf_base = (void *) (card->hw.dpmbase +
+				       (info->rxb_ptr - PPP508_MB_VECT));
+		card->u.p.rxbuf_last = (ppp_buf_ctl_t *) card->u.p.rxbuf_base +
+		    (info->rxb_num - 1);
 		card->u.p.rx_base = info->rxb_base;
-		card->u.p.rx_top  = info->rxb_end;
+		card->u.p.rx_top = info->rxb_end;
 	}
-
 	card->u.p.txbuf = card->u.p.txbuf_base;
 	card->rxmb = card->u.p.rxbuf_base;
-
 }
 
 /*=============================================================================
  * Perform the Interrupt Test by running the READ_CODE_VERSION command MAX_INTR
  * _TEST_COUNTER times.
  */
-static int intr_test( sdla_t* card )
-{
-	ppp_mbox_t* mb = card->mbox;
-	int err,i;
 
+static int intr_test(sdla_t * card)
+{
+	ppp_mbox_t *mb = card->mbox;
+	int err, i;
 	/* The critical flag is unset because during intialization (if_open) 
 	 * we want the interrupts to be enabled so that when the wpp_isr is
 	 * called it does not exit due to critical flag set.
-	 */ 
-	 
+	 */
 	card->wandev.critical = 0;
-
-	err = ppp_set_intr_mode( card, 0x08 );
-	
-	if ( err == CMD_OK ) 
-	{ 	
-		for (i=0; i<MAX_INTR_TEST_COUNTER; i++) 
-		{	
+	err = ppp_set_intr_mode(card, 0x08);
+	if (err == CMD_OK) {
+		for (i = 0; i < MAX_INTR_TEST_COUNTER; i++) {
 			/* Run command READ_CODE_VERSION */
 			memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
-			mb->cmd.length  = 0;
+			mb->cmd.length = 0;
 			mb->cmd.command = 0x10;
 			err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
-			
-			if (err != CMD_OK) 
+			if (err != CMD_OK)
 				ppp_error(card, err, mb);
 		}
-	}
-	else return err;
-
-	err = ppp_set_intr_mode( card, 0 );
-	if (err != CMD_OK) 
+	} else
+		return err;
+	err = ppp_set_intr_mode(card, 0);
+	if (err != CMD_OK)
 		return err;
-
 	card->wandev.critical = 1;
 	return 0;
+}
+
+/*==============================================================================
+ * Determine what type of UDP call it is. DRVSTATS or PTPIPEAB ?
+ */
+
+static int udp_pkt_type(struct sk_buff *skb, sdla_t * card)
+{
+	unsigned char *sendpacket;
+	unsigned char buf2[5];
+	sendpacket = skb->data;
+	memcpy(&buf2, &card->wandev.udp_port, 2);
+	if (sendpacket[0] == 0x45 &&	/* IP packet */
+	    sendpacket[9] == 0x11 &&	/* UDP packet */
+	    sendpacket[22] == buf2[1] &&	/* UDP Port */
+	    sendpacket[23] == buf2[0] &&
+	    sendpacket[36] == 0x01) {
+		if (sendpacket[28] == 0x50 &&	/* PTPIPEAB: Signature */
+		    sendpacket[29] == 0x54 &&
+		    sendpacket[30] == 0x50 &&
+		    sendpacket[31] == 0x49 &&
+		    sendpacket[32] == 0x50 &&
+		    sendpacket[33] == 0x45 &&
+		    sendpacket[34] == 0x41 &&
+		    sendpacket[35] == 0x42) {
+			return UDP_PTPIPE_TYPE;
+		} else if (sendpacket[28] == 0x44 &&	/* DRVSTATS: Signature */
+			   sendpacket[29] == 0x52 &&
+			   sendpacket[30] == 0x56 &&
+			   sendpacket[31] == 0x53 &&
+			   sendpacket[32] == 0x54 &&
+			   sendpacket[33] == 0x41 &&
+			   sendpacket[34] == 0x54 &&
+			   sendpacket[35] == 0x53) {
+			return UDP_DRVSTATS_TYPE;
+		} else
+			return UDP_INVALID_TYPE;
+	} else
+		return UDP_INVALID_TYPE;
 }
 
 /****** End *****************************************************************/
diff -u --recursive --new-file v2.1.90/linux/drivers/net/sdla_x25.c linux/drivers/net/sdla_x25.c
--- v2.1.90/linux/drivers/net/sdla_x25.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/sdla_x25.c	Sat Mar 21 10:56:27 1998
@@ -10,6 +10,7 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
+* Mar 15, 1998  Alan Cox	 o 2.1.x porting
 * Nov 27, 1997	Jaspreet Singh	 o Added protection against enabling of irqs
 *				   when they are disabled.
 * Nov 17, 1997  Farhan Thawar    o Added IPX support
@@ -41,6 +42,7 @@
 #error	This code MUST be compiled as a kernel module!
 #endif
 
+#include <linux/config.h>	/* OS configuration options */
 #include <linux/kernel.h>	/* printk(), and other useful stuff */
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
@@ -48,9 +50,8 @@
 #include <linux/malloc.h>	/* kmalloc(), kfree() */
 #include <linux/wanrouter.h>	/* WAN router definitions */
 #include <linux/wanpipe.h>	/* WANPIPE common user API definitions */
-#include <linux/init.h>		/* __initfunc et al. */
 #include <asm/byteorder.h>	/* htons(), etc. */
-#include <asm/uaccess.h>	/* copy_from_user, etc */
+#include <asm/uaccess.h>
 
 #define	_GNUC_
 #include <linux/sdla_x25.h>	/* X.25 firmware API definitions */
@@ -132,7 +133,7 @@
 	unsigned short type, void* daddr, void* saddr, unsigned len);
 static int if_rebuild_hdr (struct sk_buff* skb);
 static int if_send (struct sk_buff* skb, struct device* dev);
-static struct enet_statistics* if_stats (struct device* dev);
+static struct net_device_stats * if_stats (struct device* dev);
 
 /* Interrupt handlers */
 static void wpx_isr	(sdla_t* card);
@@ -211,7 +212,7 @@
  * Return:	0	o.k.
  *		< 0	failure.
  */
-__initfunc(int wpx_init (sdla_t* card, wandev_conf_t* conf))
+int wpx_init (sdla_t* card, wandev_conf_t* conf)
 {
 	union
 	{
@@ -372,14 +373,11 @@
 
 	/* sanity checks */
 	if ((wandev == NULL) || (wandev->private == NULL))
-		return -EFAULT
-	;
+		return -EFAULT;
 	if (wandev->state == WAN_UNCONFIGURED)
-		return -ENODEV
-	;
+		return -ENODEV;
 	if (test_and_set_bit(0, (void*)&wandev->critical))
-		return -EAGAIN
-	;
+		return -EAGAIN;
 	card = wandev->private;
 
 	x25_get_err_stats(card);
@@ -476,7 +474,6 @@
 /*============================================================================
  * Delete logical channel.
  */
-
 static int del_if (wan_device_t* wandev, struct device* dev)
 {
 	if (dev->priv)
@@ -502,18 +499,21 @@
 
 	if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd)))
 		return -EFAULT;
-
+		
 	/* execute command */
+
 	do
 	{
 		memcpy(&mbox->cmd, &cmd, sizeof(cmd));
 		if (cmd.length)
+		{
 			if(copy_from_user((void*)&mbox->data, u_data, cmd.length))
-				return -EFAULT;
+				return-EFAULT;
+		}
 		if (sdla_exec(mbox))
-			err = mbox->cmd.result;
-		else
-			return -EIO;
+			err = mbox->cmd.result
+		;
+		else return -EIO;
 	}
 	while (err && retry-- && x25_error(card, err, cmd.command, cmd.lcn));
 
@@ -521,9 +521,8 @@
 	if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(TX25Cmd)))
 		return -EFAULT;
 	len = mbox->cmd.length;
-	if (len && u_data)
-		if(copy_to_user(u_data, (void*)&mbox->data, len))
-			return -EFAULT;
+	if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len))
+		return -EFAULT;
 	return 0;
 }
 
@@ -541,7 +540,6 @@
 	x25_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
 	wan_device_t* wandev = &card->wandev;
-	int i;
 
 	/* Initialize device driver entry points */
 	dev->open		= &if_open;
@@ -552,14 +550,12 @@
 	dev->get_stats		= &if_stats;
 
 	/* Initialize media-specific parameters */
-	dev->family		= AF_INET;	/* address family */
 	dev->type		= 30;		/* ARP h/w type */
 	dev->mtu		= X25_CHAN_MTU;
 	dev->hard_header_len	= X25_HRDHDR_SZ; /* media header length */
 	dev->addr_len		= 2;		/* hardware address length */
 	if (!chan->svc)
-		*(unsigned short*)dev->dev_addr = htons(chan->lcn)
-	;
+		*(unsigned short*)dev->dev_addr = htons(chan->lcn);
 
 	/* Initialize hardware parameters (just for reference) */
 	dev->irq	= wandev->irq;
@@ -572,8 +568,8 @@
         dev->tx_queue_len = 10;
 
 	/* Initialize socket buffers */
-	dev_init_buffers(dev);
 	
+	dev_init_buffers(dev);
 	set_chan_state(dev, WAN_DISCONNECTED);
 	return 0;
 }
@@ -591,11 +587,10 @@
 	sdla_t* card = chan->card;
 
 	if (dev->start)
-		return -EBUSY		/* only one open is allowed */
-	;
+		return -EBUSY;		/* only one open is allowed */
+	
 	if (test_and_set_bit(0, (void*)&card->wandev.critical))
 		return -EAGAIN;
-	;
 
 	dev->interrupt = 0;
 	dev->tbusy = 0;
@@ -604,8 +599,7 @@
 
 	/* If this is the first open, initiate physical connection */
 	if (card->open_cnt == 1)
-		connect(card)
-	;
+		connect(card);
 	card->wandev.critical = 0;
 	return 0;
 }
@@ -622,17 +616,17 @@
 
 	if (test_and_set_bit(0, (void*)&card->wandev.critical))
 		return -EAGAIN;
-	;
+
 	dev->start = 0;
 	if ((chan->state == WAN_CONNECTED) || (chan->state == WAN_CONNECTING))
-		chan_disc(dev)
-	;
+		chan_disc(dev);
+		
 	wanpipe_close(card);
 
 	/* If this is the last close, disconnect physical link */
 	if (!card->open_cnt)
-		disconnect(card)
-	;
+		disconnect(card);
+		
 	card->wandev.critical = 0;
 	return 0;
 }
@@ -672,14 +666,15 @@
  * Return:	1	physical address resolved.
  *		0	physical address not resolved
  */
+ 
 static int if_rebuild_hdr (struct sk_buff* skb)
 {
-	x25_channel_t* chan = skb->dev->priv;
+	struct device *dev=skb->dev;
+	x25_channel_t* chan = dev->priv;
 	sdla_t* card = chan->card;
 
 	printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n",
-		card->devname, skb->dev->name)
-	;
+		card->devname, dev->name);
 	return 1;
 }
 
@@ -700,6 +695,7 @@
  * 2. Setting tbusy flag will inhibit further transmit requests from the
  *    protocol stack and can be used for flow control with protocol layer.
  */
+
 static int if_send (struct sk_buff* skb, struct device* dev)
 {
 	x25_channel_t* chan = dev->priv;
@@ -708,20 +704,6 @@
 	TX25Status* status = card->flags;
 	unsigned long host_cpu_flags;
 
-	if (skb == NULL)
-	{
-		/* If we get here, some higher layer thinks we've missed a
-		 * tx-done interrupt.
-		 */
-#ifdef _DEBUG_
-		printk(KERN_INFO "%s: interface %s got kicked!\n",
-			card->devname, dev->name)
-		;
-#endif
-		dev_tint(dev);
-		return 0;
-	}
-
 	if (dev->tbusy)
 	{
 		++chan->ifstats.rx_dropped;	
@@ -742,7 +724,7 @@
 	disable_irq(card->hw.irq);
 	++card->irq_dis_if_send_count;
 
-	if (set_bit(0, (void*)&card->wandev.critical)) 
+	if (test_and_set_bit(0, (void*)&card->wandev.critical)) 
 	{
 		printk(KERN_INFO "Hit critical in if_send()!\n");
 		if (card->wandev.critical == CRITICAL_IN_ISR) 
@@ -773,85 +755,81 @@
 
 	/* Below is only until we have per-channel IPX going.... */
 	if(!(chan->svc))
-	{
 		chan->protocol = skb->protocol;
-	}
 
 	if (card->wandev.state != WAN_CONNECTED)
-	{
-		++chan->ifstats.tx_dropped
-	;
-	}
+		++chan->ifstats.tx_dropped;
+
 	/* Below is only until we have per-channel IPX going.... */
 	else if ( (chan->svc) && (chan->protocol && (chan->protocol != skb->protocol)))
 	{
 		printk(KERN_INFO
 			"%s: unsupported Ethertype 0x%04X on interface %s!\n",
-			card->devname, skb->protocol, dev->name)
-		;
+			card->devname, skb->protocol, dev->name);
 		++chan->ifstats.tx_errors;
 	}
 	else switch (chan->state)
 	{
-	case WAN_DISCONNECTED:
-		/* Try to establish connection. If succeded, then start
-		 * transmission, else drop a packet.
-		 */
-		if (chan_connect(dev) != 0)
-		{
-			++chan->ifstats.tx_dropped;
-			++card->wandev.stats.tx_dropped;
-			break;
-		}
-		/* fall through */
-
-	case WAN_CONNECTED:
-		if( skb->protocol == ETH_P_IPX ) {
-			if(card->wandev.enable_IPX) {
-				switch_net_numbers( skb->data, 
-					card->wandev.network_number, 0);
-			} else {
-				++card->wandev.stats.tx_dropped;
+		case WAN_DISCONNECTED:
+			/* Try to establish connection. If succeded, then start
+			 * transmission, else drop a packet.
+			 */
+			if (chan_connect(dev) != 0)
+			{
 				++chan->ifstats.tx_dropped;
-				goto tx_done;
+				++card->wandev.stats.tx_dropped;
+				break;
 			}
-		}
-		dev->trans_start = jiffies;
-		if(chan_send(dev, skb))
-		{
-			dev->tbusy = 1;
-			status->imask |= 0x2;
-		}
-		break;
+			/* fall through */
 
-	default:
-		++chan->ifstats.tx_dropped;	
-		++card->wandev.stats.tx_dropped;
-	}
+		case WAN_CONNECTED:
+			if( skb->protocol == ETH_P_IPX ) 
+			{
+				if(card->wandev.enable_IPX) 
+				{
+					switch_net_numbers( skb->data, 
+						card->wandev.network_number, 0);
+				}
+				else 
+				{
+					++card->wandev.stats.tx_dropped;
+					++chan->ifstats.tx_dropped;
+					goto tx_done;
+				}
+			}
+			dev->trans_start = jiffies;
+			if(chan_send(dev, skb))
+			{
+				dev->tbusy = 1;
+				status->imask |= 0x2;
+			}
+			break;
 
+		default:
+			++chan->ifstats.tx_dropped;	
+			++card->wandev.stats.tx_dropped;
+	}
 tx_done:
 	if (!dev->tbusy)
-	{
 		dev_kfree_skb(skb);
-	}
+
 	card->wandev.critical = 0;
 	save_flags(host_cpu_flags);
         cli();
         if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count))
                 enable_irq(card->hw.irq);
         restore_flags(host_cpu_flags);
-
 	return dev->tbusy;
 }
 
 /*============================================================================
  * Get ethernet-style interface statistics.
- * Return a pointer to struct enet_statistics.
+ * Return a pointer to struct net_device_stats
  */
-static struct enet_statistics* if_stats (struct device* dev)
+ 
+static struct net_device_stats* if_stats (struct device* dev)
 {
 	x25_channel_t* chan = dev->priv;
-
 	return &chan->ifstats;
 }
 
@@ -860,6 +838,7 @@
 /*============================================================================
  * X.25 Interrupt Service Routine.
  */
+ 
 static void wpx_isr (sdla_t* card)
 {
 	TX25Status* status = card->flags;
@@ -869,7 +848,8 @@
 	card->in_isr = 1;
 	card->buff_int_mode_unbusy = 0;
 
-	if (test_and_set_bit(0, (void*)&card->wandev.critical)) {
+	if (test_and_set_bit(0, (void*)&card->wandev.critical)) 
+	{
 
  		printk(KERN_INFO "wpx_isr: %s, wandev.critical set to 0x%02X, int type = 0x%02X\n", card->devname, card->wandev.critical, status->iflags);
 		card->in_isr = 0;
@@ -884,28 +864,27 @@
 
 	switch (status->iflags)
 	{
-	case 0x01:		/* receive interrupt */
-		rx_intr(card);
-		break;
-
-	case 0x02:		/* transmit interrupt */
-		tx_intr(card);
-		card->buff_int_mode_unbusy = 1;
-		status->imask &= ~0x2;
-		break;
-
-	case 0x04:		/* modem status interrupt */
-		status_intr(card);
-		break;
-
-	case 0x10:		/* network event interrupt */
-		event_intr(card);
-		break;
+		case 0x01:		/* receive interrupt */
+			rx_intr(card);
+			break;
 
-	default:		/* unwanted interrupt */
-		spur_intr(card);
-	}
+		case 0x02:		/* transmit interrupt */
+			tx_intr(card);
+			card->buff_int_mode_unbusy = 1;
+			status->imask &= ~0x2;
+			break;
 
+		case 0x04:		/* modem status interrupt */
+			status_intr(card);
+			break;
+
+		case 0x10:		/* network event interrupt */
+			event_intr(card);
+			break;
+
+		default:		/* unwanted interrupt */
+			spur_intr(card);
+	}
 	card->wandev.critical = CRITICAL_INTR_HANDLED;
 	if( card->wandev.enable_tx_int)
 	{
@@ -925,7 +904,8 @@
 		{
 			if(((x25_channel_t*)dev->priv)->devtint)
 			{
-				dev_tint(dev);
+				mark_bh(NET_BH);
+				return;
 			}	
 		}
 	}
@@ -947,6 +927,7 @@
  * 2. If something goes wrong and X.25 packet has to be dropped (e.g. no
  *    socket buffers available) the whole packet sequence must be discarded.
  */
+
 static void rx_intr (sdla_t* card)
 {
 	TX25Mbox* rxmb = card->rxmb;
@@ -963,8 +944,7 @@
 	{
 		/* Invalid channel, discard packet */
 		printk(KERN_INFO "%s: receiving on orphaned LCN %d!\n",
-			card->devname, lcn)
-		;
+			card->devname, lcn);
 		return;
 	}
 
@@ -986,8 +966,7 @@
 		if (skb == NULL)
 		{
 			printk(KERN_INFO "%s: no socket buffers available!\n",
-				card->devname)
-			;
+				card->devname);
 			chan->drop_sequence = 1;	/* set flag */
 			++chan->ifstats.rx_dropped;
 			return;
@@ -1005,8 +984,7 @@
 		if (qdm & 0x01) chan->drop_sequence = 1;
 
 		printk(KERN_INFO "%s: unexpectedly long packet sequence "
-			"on interface %s!\n", card->devname, dev->name)
-		;
+			"on interface %s!\n", card->devname, dev->name);
 		++chan->ifstats.rx_length_errors;
 		return;
 	}
@@ -1014,7 +992,9 @@
 	/* Append packet to the socket buffer */
 	bufptr = skb_put(skb, len);
 	memcpy(bufptr, rxmb->data, len);
-	if (qdm & 0x01) return;		/* more data is comming */
+
+	if (qdm & 0x01)
+		return;		/* more data is comming */
 
 	dev->last_rx = jiffies;		/* timestamp */
 	chan->rx_skb = NULL;		/* dequeue packet */
@@ -1043,7 +1023,7 @@
 			}
 			else
 			{
-				/* increment IPX packet dropped statistic */
+				/* FIXME: increment IPX packet dropped statistic */
 			}
 		}
 		else
@@ -1059,11 +1039,11 @@
  *	o Release socket buffer
  *	o Clear 'tbusy' flag
  */
+
 static void tx_intr (sdla_t* card)
 {
 	struct device *dev;
 
-	
 	/* unbusy all devices and then dev_tint(); */
 	for(dev = card->wandev.dev; dev; dev = dev->slave)
 	{
@@ -1102,13 +1082,14 @@
 
 /*============================================================================
  * Main polling routine.
- * This routine is repeatedly called by the WANPIPE 'thead' to allow for
+ * This routine is repeatedly called by the WANPIPE 'thread' to allow for
  * time-dependent housekeeping work.
  *
  * Notes:
  * 1. This routine may be called on interrupt context with all interrupts
  *    enabled. Beware!
  */
+
 static void wpx_poll (sdla_t* card)
 {
 	unsigned long host_cpu_flags;
@@ -1116,42 +1097,37 @@
 	disable_irq(card->hw.irq);
 	++card->irq_dis_poll_count;
 
-	if (set_bit(0, (void*)&card->wandev.critical)) {
-
- 		printk(KERN_INFO "%s: critical in polling!\n",card->devname);
-	 	
+	if (test_and_set_bit(0, (void*)&card->wandev.critical)) 
+	{
+ 		printk(KERN_INFO "%s: critical in polling!\n",card->devname);	
 		save_flags(host_cpu_flags);
                 cli();
 		if ((!card->irq_dis_if_send_count) &&
                                 (!(--card->irq_dis_poll_count)))
                         enable_irq(card->hw.irq);
                 restore_flags(host_cpu_flags);
-		
 		return;
 	}
 
 	switch(card->wandev.state)
 	{
-	case WAN_CONNECTED:
-		poll_active(card);
-		break;
-
-	case WAN_CONNECTING:
-		poll_connecting(card);
-		break;
+		case WAN_CONNECTED:
+			poll_active(card);
+			break;
 
-	case WAN_DISCONNECTED:
-		poll_disconnected(card);
-	}
+		case WAN_CONNECTING:
+			poll_connecting(card);
+			break;
 
+		case WAN_DISCONNECTED:
+			poll_disconnected(card);
+	}
 	card->wandev.critical = 0;
-
 	save_flags(host_cpu_flags);
         cli();
         if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count)))
                 enable_irq(card->hw.irq);
         restore_flags(host_cpu_flags);
-
 }
 
 /*============================================================================
@@ -1169,8 +1145,7 @@
 		status->imask &= ~0x2;		/* mask Tx interupts */
 	}
 	else if ((jiffies - card->state_tick) > CONNECT_TIMEOUT)
-	    disconnect(card)
-	;
+	    disconnect(card);
 }
 
 /*============================================================================
@@ -1181,8 +1156,7 @@
 static void poll_disconnected (sdla_t* card)
 {
 	if (card->open_cnt && ((jiffies - card->state_tick) > HOLD_DOWN_TIME))
-		connect(card)
-	;
+		connect(card);
 }
 
 /*============================================================================
@@ -1220,7 +1194,7 @@
 		{
 			if( (jiffies - chan->i_timeout_sofar) / HZ > chan->idle_timeout )
 			{
-				//Close svc
+				/* Close svc */
 				printk(KERN_INFO "%s: Closing down Idle link %s on LCN %d\n",card->devname,chan->name,chan->lcn); 
 				chan->i_timeout_sofar = jiffies;
 				chan_disc(dev);
@@ -1255,13 +1229,11 @@
 		mbox->cmd.command = X25_READ_CODE_VERSION;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
 	} while (err && retry-- &&
-		 x25_error(card, err, X25_READ_CODE_VERSION, 0))
-	;
+		 x25_error(card, err, X25_READ_CODE_VERSION, 0));
 
 	if (!err && str)
 	{
 		int len = mbox->cmd.length;
-
 		memcpy(str, mbox->data, len);
 		str[len] = '\0';
 	}
@@ -1271,6 +1243,7 @@
 /*============================================================================
  * Configure adapter.
  */
+
 static int x25_configure (sdla_t* card, TX25Config* conf)
 {
 	TX25Mbox* mbox = card->mbox;
@@ -1284,9 +1257,7 @@
 		mbox->cmd.length  = sizeof(TX25Config);
 		mbox->cmd.command = X25_SET_CONFIGURATION;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- &&
-		 x25_error(card, err, X25_SET_CONFIGURATION, 0))
-	;
+	} while (err && retry-- && x25_error(card, err, X25_SET_CONFIGURATION, 0));
 	return err;
 }
 
@@ -1304,9 +1275,8 @@
 		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
 		mbox->cmd.command = X25_HDLC_READ_COMM_ERR;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- &&
-		 x25_error(card, err, X25_HDLC_READ_COMM_ERR, 0))
-	;
+	} while (err && retry-- && x25_error(card, err, X25_HDLC_READ_COMM_ERR, 0));
+
 	if (!err)
 	{
 		THdlcCommErr* stats = (void*)mbox->data;
@@ -1333,9 +1303,8 @@
 		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
 		mbox->cmd.command = X25_READ_STATISTICS;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- &&
-		 x25_error(card, err, X25_READ_STATISTICS, 0))
-	;
+	} while (err && retry-- && x25_error(card, err, X25_READ_STATISTICS, 0));
+	
 	if (!err)
 	{
 		TX25Stats* stats = (void*)mbox->data;
@@ -1360,9 +1329,8 @@
 		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
 		mbox->cmd.command = X25_HDLC_LINK_CLOSE;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- &&
-		 x25_error(card, err, X25_HDLC_LINK_CLOSE, 0))
-	;
+	} while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_CLOSE, 0));
+
 	return err;
 }
 
@@ -1380,9 +1348,8 @@
 		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
 		mbox->cmd.command = X25_HDLC_LINK_OPEN;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- &&
-		 x25_error(card, err, X25_HDLC_LINK_OPEN, 0))
-	;
+	} while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_OPEN, 0));
+	
 	return err;
 }
 
@@ -1400,9 +1367,8 @@
 		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
 		mbox->cmd.command = X25_HDLC_LINK_SETUP;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- &&
-		 x25_error(card, err, X25_HDLC_LINK_SETUP, 0))
-	;
+	} while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_SETUP, 0));
+	
 	return err;
 }
 
@@ -1424,9 +1390,8 @@
 		mbox->cmd.length  = 3;
 		mbox->cmd.command = X25_SET_GLOBAL_VARS;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- &&
-		 x25_error(card, err, X25_SET_GLOBAL_VARS, 0))
-	;
+	} while (err && retry-- && x25_error(card, err, X25_SET_GLOBAL_VARS, 0));
+
 	return err;
 }
 
@@ -1451,9 +1416,7 @@
 		else mbox->cmd.length  = 1;
 		mbox->cmd.command = X25_SET_INTERRUPT_MODE;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- &&
-		 x25_error(card, err, X25_SET_INTERRUPT_MODE, 0))
-	;
+	} while (err && retry-- && x25_error(card, err, X25_SET_INTERRUPT_MODE, 0)) ;
 	return err;
 }
 
@@ -1473,9 +1436,7 @@
 		mbox->cmd.lcn     = lcn;
 		mbox->cmd.command = X25_READ_CHANNEL_CONFIG;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- &&
-		 x25_error(card, err, X25_READ_CHANNEL_CONFIG, lcn))
-	;
+	} while (err && retry-- && x25_error(card, err, X25_READ_CHANNEL_CONFIG, lcn));
 
 	if (!err)
 	{
@@ -1483,19 +1444,23 @@
 
 		/* calculate an offset into the array of status bytes */
 		if (card->u.x.hi_svc <= 255) 
-			chan->ch_idx = lcn - 1
-		;
+			chan->ch_idx = lcn - 1;
 		else
 		{
 			int offset;
 
 			switch (mbox->data[0] && 0x1F)
 			{
-			case 0x01: offset = status->pvc_map; break;
-			case 0x03: offset = status->icc_map; break;
-			case 0x07: offset = status->twc_map; break;
-			case 0x0B: offset = status->ogc_map; break;
-			default: offset = 0;
+				case 0x01:
+					offset = status->pvc_map; break;
+				case 0x03:
+					offset = status->icc_map; break;
+				case 0x07:
+					offset = status->twc_map; break;
+				case 0x0B: 
+					offset = status->ogc_map; break;
+				default: 
+					offset = 0;
 			}
 			chan->ch_idx = lcn - 1 - offset;
 		}
@@ -1503,17 +1468,30 @@
 		/* get actual transmit packet size on this channel */
 		switch(mbox->data[1] & 0x38)
 		{
-		case 0x00: chan->tx_pkt_size = 16; break;
-		case 0x08: chan->tx_pkt_size = 32; break;
-		case 0x10: chan->tx_pkt_size = 64; break;
-		case 0x18: chan->tx_pkt_size = 128; break;
-		case 0x20: chan->tx_pkt_size = 256; break;
-		case 0x28: chan->tx_pkt_size = 512; break;
-		case 0x30: chan->tx_pkt_size = 1024; break;
+			case 0x00:
+				chan->tx_pkt_size = 16;
+				break;
+			case 0x08:
+				chan->tx_pkt_size = 32;
+				break;
+			case 0x10:
+				chan->tx_pkt_size = 64;
+				break;
+			case 0x18:
+				chan->tx_pkt_size = 128;
+				break;
+			case 0x20:
+				chan->tx_pkt_size = 256;
+				break;
+			case 0x28:
+				chan->tx_pkt_size = 512;
+				break;
+			case 0x30:
+				chan->tx_pkt_size = 1024;
+				break;
 		}
 		printk(KERN_INFO "%s: X.25 packet size on LCN %d is %d.\n",
-			card->devname, lcn, chan->tx_pkt_size)
-		;
+			card->devname, lcn, chan->tx_pkt_size);
 	}
 	return err;
 }
@@ -1521,6 +1499,7 @@
 /*============================================================================
  * Place X.25 call.
  */
+
 static int x25_place_call (sdla_t* card, x25_channel_t* chan)
 {
 	TX25Mbox* mbox = card->mbox;
@@ -1536,9 +1515,8 @@
 		mbox->cmd.length  = strlen(str);
 		mbox->cmd.command = X25_PLACE_CALL;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- &&
-		 x25_error(card, err, X25_PLACE_CALL, 0))
-	;
+	} while (err && retry-- && x25_error(card, err, X25_PLACE_CALL, 0));
+
 	if (!err)
 	{
 		chan->lcn = mbox->cmd.lcn;
@@ -1550,6 +1528,7 @@
 /*============================================================================
  * Accept X.25 call.
  */
+
 static int x25_accept_call (sdla_t* card, int lcn, int qdm)
 {
 	TX25Mbox* mbox = card->mbox;
@@ -1563,9 +1542,8 @@
 		mbox->cmd.qdm     = qdm;
 		mbox->cmd.command = X25_ACCEPT_CALL;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- &&
-		 x25_error(card, err, X25_ACCEPT_CALL, lcn))
-	;
+	} while (err && retry-- && x25_error(card, err, X25_ACCEPT_CALL, lcn));
+
 	return err;
 }
 
@@ -1586,9 +1564,8 @@
 		mbox->cmd.diagn   = diagn;
 		mbox->cmd.command = X25_CLEAR_CALL;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
-	} while (err && retry-- &&
-		 x25_error(card, err, X25_CLEAR_CALL, lcn))
-	;
+	} while (err && retry-- && x25_error(card, err, X25_CLEAR_CALL, lcn));
+
 	return err;
 }
 
@@ -1600,7 +1577,7 @@
 	TX25Mbox* mbox = card->mbox;
   	int retry = MAX_CMD_RETRY;
 	int err;
-
+	
 	do
 	{
 		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
@@ -1628,7 +1605,8 @@
 		memset(&mbox->cmd, 0, sizeof(TX25Cmd));
 		mbox->cmd.command = X25_IS_DATA_AVAILABLE;
 		err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
- 		if (err) x25_error(card, err, X25_IS_DATA_AVAILABLE, 0);
+ 		if (err)
+ 			x25_error(card, err, X25_IS_DATA_AVAILABLE, 0);
 	}
 	return err;
 }
@@ -1656,99 +1634,90 @@
 	if (mb == NULL)
 	{
 		printk(KERN_ERR "%s: x25_error() out of memory!\n",
-			card->devname)
-		;
+			card->devname);
 		return 0;
 	}
 	memcpy(mb, card->mbox, sizeof(TX25Mbox) + dlen);
 	switch (err)
 	{
-	case 0x40:	/* X.25 asynchronous packet was received */
-		mb->data[dlen] = '\0';
-		switch (mb->cmd.pktType & 0x7F)
-		{
-		case 0x30:		/* incomming call */
-			retry = incomming_call(card, cmd, lcn, mb);
+		case 0x40:	/* X.25 asynchronous packet was received */
+			mb->data[dlen] = '\0';
+			switch (mb->cmd.pktType & 0x7F)
+			{
+				case 0x30:		/* incomming call */
+					retry = incomming_call(card, cmd, lcn, mb);
+					break;
+
+				case 0x31:		/* connected */
+					retry = call_accepted(card, cmd, lcn, mb);
+					break;
+
+				case 0x02:		/* call clear request */
+					retry = call_cleared(card, cmd, lcn, mb);
+					break;
+
+				case 0x04:		/* reset request */
+					printk(KERN_INFO "%s: X.25 reset request on LCN %d! "
+						"Cause:0x%02X Diagn:0x%02X\n",
+						card->devname, mb->cmd.lcn, mb->cmd.cause,
+						mb->cmd.diagn);
+					break;
+
+				case 0x08:		/* restart request */
+					retry = restart_event(card, cmd, lcn, mb);
+					break;
+
+				default:
+					printk(KERN_INFO "%s: X.25 event 0x%02X on LCN %d! "
+						"Cause:0x%02X Diagn:0x%02X\n",
+						card->devname, mb->cmd.pktType,
+						mb->cmd.lcn, mb->cmd.cause, mb->cmd.diagn);
+			}
 			break;
 
-		case 0x31:		/* connected */
-			retry = call_accepted(card, cmd, lcn, mb);
+		case 0x41:	/* X.25 protocol violation indication */
+			printk(KERN_INFO
+				"%s: X.25 protocol violation on LCN %d! "
+				"Packet:0x%02X Cause:0x%02X Diagn:0x%02X\n",
+				card->devname, mb->cmd.lcn,
+				mb->cmd.pktType & 0x7F, mb->cmd.cause, mb->cmd.diagn);
 			break;
 
-		case 0x02:		/* call clear request */
-			retry = call_cleared(card, cmd, lcn, mb);
+		case 0x42:	/* X.25 timeout */
+			retry = timeout_event(card, cmd, lcn, mb);
 			break;
 
-		case 0x04:		/* reset request */
-			printk(KERN_INFO "%s: X.25 reset request on LCN %d! "
-				"Cause:0x%02X Diagn:0x%02X\n",
-				card->devname, mb->cmd.lcn, mb->cmd.cause,
-				mb->cmd.diagn)
-			;
+		case 0x43:	/* X.25 retry limit exceeded */
+			printk(KERN_INFO
+				"%s: exceeded X.25 retry limit on LCN %d! "
+				"Packet:0x%02X Diagn:0x%02X\n", card->devname,
+				mb->cmd.lcn, mb->cmd.pktType, mb->cmd.diagn);
 			break;
 
-		case 0x08:		/* restart request */
-			retry = restart_event(card, cmd, lcn, mb);
+		case 0x08:	/* modem failure */
+			printk(KERN_INFO "%s: modem failure!\n", card->devname);
 			break;
 
-		default:
-			printk(KERN_INFO "%s: X.25 event 0x%02X on LCN %d! "
-				"Cause:0x%02X Diagn:0x%02X\n",
-				card->devname, mb->cmd.pktType,
-				mb->cmd.lcn, mb->cmd.cause, mb->cmd.diagn)
-			;
-		}
-		break;
-
-	case 0x41:	/* X.25 protocol violation indication */
-		printk(KERN_INFO
-			"%s: X.25 protocol violation on LCN %d! "
-			"Packet:0x%02X Cause:0x%02X Diagn:0x%02X\n",
-			card->devname, mb->cmd.lcn,
-			mb->cmd.pktType & 0x7F, mb->cmd.cause, mb->cmd.diagn)
-		;
-		break;
-
-	case 0x42:	/* X.25 timeout */
-		retry = timeout_event(card, cmd, lcn, mb);
-		break;
-
-	case 0x43:	/* X.25 retry limit exceeded */
-		printk(KERN_INFO
-			"%s: exceeded X.25 retry limit on LCN %d! "
-			"Packet:0x%02X Diagn:0x%02X\n", card->devname,
-			mb->cmd.lcn, mb->cmd.pktType, mb->cmd.diagn)
-		;
-		break;
-
-	case 0x08:	/* modem failure */
-		printk(KERN_INFO "%s: modem failure!\n", card->devname);
-		break;
-
-	case 0x09:	/* N2 retry limit */
-		printk(KERN_INFO "%s: exceeded HDLC retry limit!\n",
-			card->devname)
-		;
-		break;
+		case 0x09:	/* N2 retry limit */
+			printk(KERN_INFO "%s: exceeded HDLC retry limit!\n",
+				card->devname);
+			break;
 
-	case 0x06:	/* unnumbered frame was received while in ABM */
-		printk(KERN_INFO "%s: received Unnumbered frame 0x%02X!\n",
-			card->devname, mb->data[0])
-		;
-		break;
+		case 0x06:	/* unnumbered frame was received while in ABM */
+			printk(KERN_INFO "%s: received Unnumbered frame 0x%02X!\n",
+				card->devname, mb->data[0]);
+			break;
 
-	case CMD_TIMEOUT:
-		printk(KERN_ERR "%s: command 0x%02X timed out!\n",
-			card->devname, cmd)
-		;
-		retry = 0;	/* abort command */
-		break;
+		case CMD_TIMEOUT:
+			printk(KERN_ERR "%s: command 0x%02X timed out!\n",
+				card->devname, cmd);
+			retry = 0;	/* abort command */
+			break;
 
-	default:
-		printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n",
-			card->devname, cmd, err)
-		;
-		retry = 0;	/* abort command */
+		default:
+			printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n",
+				card->devname, cmd, err);
+			retry = 0;	/* abort command */
 	}
 	kfree(mb);
 	return retry;
@@ -1772,6 +1741,7 @@
  *	   (i.e. call collision has occured), the incomming call shall be
  *	   rejected and call request shall be retried.
  */
+
 static int incomming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
 {
 	wan_device_t* wandev = &card->wandev;
@@ -1786,8 +1756,7 @@
 	{
 		printk(KERN_INFO
 			"%s: X.25 incomming call collision on LCN %d!\n",
-			card->devname, new_lcn)
-		;
+			card->devname, new_lcn);
 		x25_clear_call(card, new_lcn, 0, 0);
 		return 1;
 	}
@@ -1797,8 +1766,7 @@
 	{
 		printk(KERN_INFO
 			"%s: X.25 incomming call on LCN %d with D-bit set!\n",
-			card->devname, new_lcn)
-		;
+			card->devname, new_lcn);
 		x25_clear_call(card, new_lcn, 0, 0);
 		return 1;
 	}
@@ -1815,8 +1783,7 @@
 	}
 	parse_call_info(mb->data, info);
 	printk(KERN_INFO "%s: X.25 incomming call on LCN %d! Call data: %s\n",
-		card->devname, new_lcn, mb->data)
-	;
+		card->devname, new_lcn, mb->data);
 
 	/* Find available channel */
 	for (dev = wandev->dev; dev; dev = dev->slave)
@@ -1824,22 +1791,18 @@
 		chan = dev->priv;
 
 		if (!chan->svc || (chan->state != WAN_DISCONNECTED))
-			continue
-		;
+			continue;
 		if (strcmp(info->src, chan->addr) == 0)
-			break
-		;
-	        // If just an '@' is specified, accept all incomming calls
+			break;
+	        /* If just an '@' is specified, accept all incomming calls */
 	        if (strcmp(chan->addr, "") == 0)
-	                break
-	        ;
+	                break;
 	}
 
 	if (dev == NULL)
 	{
 		printk(KERN_INFO "%s: no channels available!\n",
-			card->devname)
-		;
+			card->devname);
 		x25_clear_call(card, new_lcn, 0, 0);
 	}
 
@@ -1848,41 +1811,39 @@
 	{
 		printk(KERN_INFO
 			"%s: no user data in incomming call on LCN %d!\n",
-			card->devname, new_lcn)
-		;
+			card->devname, new_lcn);
 		x25_clear_call(card, new_lcn, 0, 0);
 	}
 	else switch (info->user[0])
 	{
-	case 0:		/* multiplexed */
-		chan->protocol = 0;
-		accept = 1;
-		break;
+		case 0:		/* multiplexed */
+			chan->protocol = 0;
+			accept = 1;
+			break;
 
-	case NLPID_IP:	/* IP datagrams */
-		chan->protocol = ETH_P_IP;
-		accept = 1;
-		break;
+		case NLPID_IP:	/* IP datagrams */
+			chan->protocol = ETH_P_IP;
+			accept = 1;
+			break;
 
-	case NLPID_SNAP: /* IPX datagrams */
-		chan->protocol = ETH_P_IPX;
-		accept = 1;
-		break;
-	default:
-		printk(KERN_INFO
-			"%s: unsupported NLPID 0x%02X in incomming call "
-			"on LCN %d!\n", card->devname, info->user[0], new_lcn)
-		;
-		x25_clear_call(card, new_lcn, 0, 249);
+		case NLPID_SNAP: /* IPX datagrams */
+			chan->protocol = ETH_P_IPX;
+			accept = 1;
+			break;
+		default:
+			printk(KERN_INFO
+				"%s: unsupported NLPID 0x%02X in incomming call "
+				"on LCN %d!\n", card->devname, info->user[0], new_lcn);
+			x25_clear_call(card, new_lcn, 0, 249);
 	}
 
 	if (accept && (x25_accept_call(card, new_lcn, 0) == CMD_OK))
 	{
 		chan->lcn = new_lcn;
 		if (x25_get_chan_conf(card, chan) == CMD_OK)
-			set_chan_state(dev, WAN_CONNECTED)
-		;
-		else x25_clear_call(card, new_lcn, 0, 0);
+			set_chan_state(dev, WAN_CONNECTED);
+		else
+			x25_clear_call(card, new_lcn, 0, 0);
 	}
 	kfree(info);
 	return 1;
@@ -1891,6 +1852,7 @@
 /*============================================================================
  * Handle accepted call.
  */
+
 static int call_accepted (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
 {
 	unsigned new_lcn = mb->cmd.lcn;
@@ -1898,14 +1860,12 @@
 	x25_channel_t* chan;
 
 	printk(KERN_INFO "%s: X.25 call accepted on LCN %d!\n",
-		card->devname, new_lcn)
-	;
+		card->devname, new_lcn);
 	if (dev == NULL)
 	{
 		printk(KERN_INFO
 			"%s: clearing orphaned connection on LCN %d!\n",
-			card->devname, new_lcn)
-		;
+			card->devname, new_lcn);
 		x25_clear_call(card, new_lcn, 0, 0);
 		return 1;
 	}
@@ -1924,6 +1884,7 @@
 /*============================================================================
  * Handle cleared call.
  */
+
 static int call_cleared (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
 {
 	unsigned new_lcn = mb->cmd.lcn;
@@ -1931,17 +1892,17 @@
 
 	printk(KERN_INFO "%s: X.25 clear request on LCN %d! Cause:0x%02X "
 		"Diagn:0x%02X\n",
-		card->devname, new_lcn, mb->cmd.cause, mb->cmd.diagn)
-	;
-	if (dev == NULL) return 1;
+		card->devname, new_lcn, mb->cmd.cause, mb->cmd.diagn);
+	if (dev == NULL)
+		return 1;
 	set_chan_state(dev, WAN_DISCONNECTED);
-
 	return ((cmd == X25_WRITE) && (lcn == new_lcn)) ? 0 : 1;
 }
 
 /*============================================================================
  * Handle X.25 restart event.
  */
+ 
 static int restart_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
 {
 	wan_device_t* wandev = &card->wandev;
@@ -1949,13 +1910,11 @@
 
 	printk(KERN_INFO
 		"%s: X.25 restart request! Cause:0x%02X Diagn:0x%02X\n",
-		card->devname, mb->cmd.cause, mb->cmd.diagn)
-	;
+		card->devname, mb->cmd.cause, mb->cmd.diagn);
 
 	/* down all logical channels */
 	for (dev = wandev->dev; dev; dev = dev->slave)
-		set_chan_state(dev, WAN_DISCONNECTED)
-	;
+		set_chan_state(dev, WAN_DISCONNECTED);
 	return (cmd == X25_WRITE) ? 0 : 1;
 }
 
@@ -1971,13 +1930,12 @@
 		struct device* dev = get_dev_by_lcn(&card->wandev, new_lcn);
 
 		printk(KERN_INFO "%s: X.25 call timed timeout on LCN %d!\n",
-			card->devname, new_lcn)
-		;
-		if (dev) set_chan_state(dev, WAN_DISCONNECTED);
+			card->devname, new_lcn);
+		if (dev)
+			set_chan_state(dev, WAN_DISCONNECTED);
 	}
 	else printk(KERN_INFO "%s: X.25 packet 0x%02X timeout on LCN %d!\n",
-		card->devname, mb->cmd.pktType, new_lcn)
-	;
+		card->devname, mb->cmd.pktType, new_lcn);
 	return 1;
 }
 
@@ -1994,8 +1952,7 @@
 static int connect (sdla_t* card)
 {
 	if (x25_open_hdlc(card) || x25_setup_hdlc(card))
-		return -EIO
-	;
+		return -EIO;
 	wanpipe_set_state(card, WAN_CONNECTING);
 	return 1;
 }
@@ -2025,8 +1982,8 @@
 	struct device* dev;
 
 	for (dev = wandev->dev; dev; dev = dev->slave)
-		if (((x25_channel_t*)dev->priv)->lcn == lcn) break
-	;
+		if (((x25_channel_t*)dev->priv)->lcn == lcn)
+			break;
 	return dev;
 }
 
@@ -2047,22 +2004,18 @@
 	if (chan->svc)
 	{
 		if (!chan->addr[0])
-			return -EINVAL	/* no destination address */
-		;
+			return -EINVAL;	/* no destination address */
 		printk(KERN_INFO "%s: placing X.25 call to %s ...\n",
-			card->devname, chan->addr)
-		;
+			card->devname, chan->addr);
 		if (x25_place_call(card, chan) != CMD_OK)
-			return -EIO
-		;
+			return -EIO;
 		set_chan_state(dev, WAN_CONNECTING);
 		return 1;
 	}
 	else
 	{
 		if (x25_get_chan_conf(card, chan) != CMD_OK)
-			return -EIO
-		;
+			return -EIO;
 		set_chan_state(dev, WAN_CONNECTED);
 	}
 	return 0;
@@ -2076,7 +2029,8 @@
 {
 	x25_channel_t* chan = dev->priv;
 
-	if (chan->svc) x25_clear_call(chan->card, chan->lcn, 0, 0);
+	if (chan->svc)
+		x25_clear_call(chan->card, chan->lcn, 0, 0);
 	set_chan_state(dev, WAN_DISCONNECTED);
 	return 0;
 }
@@ -2096,29 +2050,27 @@
 	{
 		switch (state)
 		{
-		case WAN_CONNECTED:
-			printk (KERN_INFO "%s: interface %s connected!\n",
-			card->devname, dev->name)
-			;
-			*(unsigned short*)dev->dev_addr = htons(chan->lcn);
-			chan->i_timeout_sofar = jiffies;
-			break;
-
-		case WAN_CONNECTING:
-			printk (KERN_INFO "%s: interface %s connecting...\n",
-				card->devname, dev->name)
-			;
-			break;
+			case WAN_CONNECTED:
+				printk (KERN_INFO "%s: interface %s connected!\n",
+					card->devname, dev->name);
+				*(unsigned short*)dev->dev_addr = htons(chan->lcn);
+				chan->i_timeout_sofar = jiffies;
+				break;
 
-		case WAN_DISCONNECTED:
-			printk (KERN_INFO "%s: interface %s disconnected!\n",
-				card->devname, dev->name)
-			;
-			if (chan->svc) {
-				*(unsigned short*)dev->dev_addr = 0;
-		                chan->lcn = 0;
-			}
-			break;
+			case WAN_CONNECTING:
+				printk (KERN_INFO "%s: interface %s connecting...\n",
+					card->devname, dev->name);
+				break;
+
+			case WAN_DISCONNECTED:
+				printk (KERN_INFO "%s: interface %s disconnected!\n",
+					card->devname, dev->name);
+				if (chan->svc) 
+				{
+					*(unsigned short*)dev->dev_addr = 0;
+		                	chan->lcn = 0;
+				}
+				break;
 		}
 		chan->state = state;
 	}
@@ -2150,8 +2102,8 @@
 
 	/* Check to see if channel is ready */
 	if (!(status->cflags[chan->ch_idx] & 0x40))
-		return 1
-	;
+		return 1;
+
 	if (skb->len > chan->tx_pkt_size)
 	{
 		len = chan->tx_pkt_size;
@@ -2164,22 +2116,22 @@
 	}
 	switch(x25_send(card, chan->lcn, qdm, len, skb->data))
 	{
-	case 0x00:	/* success */
-		chan->i_timeout_sofar = jiffies;
-		if (qdm)
-		{
-			skb_pull(skb, len);
-			return 1;
-		}
-		++chan->ifstats.tx_packets;
-		break;
+		case 0x00:	/* success */
+			chan->i_timeout_sofar = jiffies;
+			if (qdm)
+			{
+				skb_pull(skb, len);
+				return 1;
+			}
+			++chan->ifstats.tx_packets;
+			break;
 
-	case 0x33:	/* Tx busy */
-		return 1;
+		case 0x33:	/* Tx busy */
+			return 1;
 
-	default:	/* failure */
-		++chan->ifstats.tx_errors;
-/*		return 1; */
+		default:	/* failure */
+			++chan->ifstats.tx_errors;
+/*			return 1; */
 	}
 	return 0;
 }
@@ -2187,6 +2139,7 @@
 /*============================================================================
  * Parse X.25 call request data and fill x25_call_info_t structure.
  */
+
 static void parse_call_info (unsigned char* str, x25_call_info_t* info)
 {
 	memset(info, 0, sizeof(x25_call_info_t));
@@ -2197,50 +2150,53 @@
 
 		if (*str == '-') switch (str[1])
 		{
-		case 'd':	/* destination address */
-			for (i = 0; i < 16; ++i)
-			{
-				ch = str[2+i];
-				if (!is_digit(ch)) break;
-				info->dest[i] = ch;
-			}
-			break;
-
-		case 's':	/* source address */
-			for (i = 0; i < 16; ++i)
-			{
-				ch = str[2+i];
-				if (!is_digit(ch)) break;
-				info->src[i] = ch;
-			}
-			break;
+			case 'd':	/* destination address */
+				for (i = 0; i < 16; ++i)
+				{
+					ch = str[2+i];
+					if (!is_digit(ch)) 
+						break;
+					info->dest[i] = ch;
+				}
+				break;
+	
+			case 's':	/* source address */
+				for (i = 0; i < 16; ++i)
+				{
+					ch = str[2+i];
+					if (!is_digit(ch))
+						break;
+					info->src[i] = ch;
+				}
+				break;
 
-		case 'u':	/* user data */
-			for (i = 0; i < 127; ++i)
-			{
-				ch = str[2+2*i];
-				if (!is_hex_digit(ch)) break;
-				info->user[i] = hex_to_uint(&str[2+2*i], 2);
-			}
-			info->nuser = i;
-			break;
+			case 'u':	/* user data */
+				for (i = 0; i < 127; ++i)
+				{
+					ch = str[2+2*i];
+					if (!is_hex_digit(ch)) 
+						break;
+					info->user[i] = hex_to_uint(&str[2+2*i], 2);
+				}
+				info->nuser = i;
+				break;
 
-		case 'f':	/* facilities */
-			for (i = 0; i < 64; ++i)
-			{
-				ch = str[2+4*i];
-				if (!is_hex_digit(ch)) break;
-				info->facil[i].code =
-					hex_to_uint(&str[2+4*i], 2)
-				;
-				ch = str[4+4*i];
-				if (!is_hex_digit(ch)) break;
-				info->facil[i].parm =
-					hex_to_uint(&str[4+4*i], 2)
-				;
-			}
-			info->nfacil = i;
-			break;
+			case 'f':	/* facilities */
+				for (i = 0; i < 64; ++i)
+				{
+					ch = str[2+4*i];
+					if (!is_hex_digit(ch))
+						break;
+					info->facil[i].code =
+						hex_to_uint(&str[2+4*i], 2);
+					ch = str[4+4*i];
+					if (!is_hex_digit(ch))
+						break;
+					info->facil[i].parm =
+						hex_to_uint(&str[4+4*i], 2);
+				}
+				info->nfacil = i;
+				break;
 		}
 	}
 }
@@ -2279,8 +2235,7 @@
 
 	if (!len) len = strlen(str);
 	for (val = 0; len && is_digit(*str); ++str, --len)
-		val = (val * 10) + (*str - (unsigned)'0')
-	;
+		val = (val * 10) + (*str - (unsigned)'0');
 	return val;
 }
 
@@ -2297,12 +2252,11 @@
 	{
 		ch = *str;
 		if (is_digit(ch))
-			val = (val << 4) + (ch - (unsigned)'0')
-		;
+			val = (val << 4) + (ch - (unsigned)'0');
 		else if (is_hex_digit(ch))
-			val = (val << 4) + ((ch & 0xDF) - (unsigned)'A' + 10)
-		;
-		else break;
+			val = (val << 4) + ((ch & 0xDF) - (unsigned)'A' + 10);
+		else
+			break;
 	}
 	return val;
 }
@@ -2334,22 +2288,18 @@
 			/* It's a timer request packet */
 			printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname);
 
-			/* Go through the routing options and answer no to every */
-			/* option except Unnumbered RIP/SAP */
+			/* Go through the routing options and answer no to every
+			 * option except Unnumbered RIP/SAP */
 			for(i = 41; sendpacket[i] == 0x00; i += 5)
 			{
 				/* 0x02 is the option for Unnumbered RIP/SAP */
 				if( sendpacket[i + 4] != 0x02)
-				{
 					sendpacket[i + 1] = 0;
-				}
 			}
 
 			/* Skip over the extended Node ID option */
 			if( sendpacket[i] == 0x04 )
-			{
 				i += 8;
-			}
 
 			/* We also want to turn off all header compression opt. */
 			for(; sendpacket[i] == 0x80 ;)
@@ -2388,15 +2338,13 @@
 			sendpacket[64] = CVHexToAscii((network_number & 0x000000F0)>> 4);
 			sendpacket[65] = CVHexToAscii(network_number & 0x0000000F);
 			for(i = 66; i < 99; i+= 1)
-			{
 				sendpacket[i] = 0;
-			}
 
-			/* printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname); */
+			printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname);
 		}
 		else
 		{
-			printk(KERN_WARNING "%s: Unknown IPXWAN packet!\n",devname);
+			printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname);
 			return 0;
 		}
 
@@ -2408,9 +2356,8 @@
 
 		return 1;
 	} else {
-		/* If we get here its an IPX-data packet, so it'll get passed up the stack. */
-
-		/* switch the network numbers */
+		/* If we get here its an IPX-data packet, so it'll get passed up the stack.
+		   switch the network numbers */
 		switch_net_numbers(sendpacket, network_number, 1);	
 		return 0;
 	}
@@ -2421,6 +2368,7 @@
    if incoming is 1 - if the net number is 0 make it ours 
 
 */
+
 static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming)
 {
 	unsigned long pnetwork_number;
@@ -2429,15 +2377,20 @@
 			  (sendpacket[7] << 16) + (sendpacket[8] << 8) + 
 			  sendpacket[9]);
 
-	if (!incoming) {
+	if (!incoming) 
+	{
 		/* If the destination network number is ours, make it 0 */
-		if( pnetwork_number == network_number) {
+		if( pnetwork_number == network_number) 
+		{
 			sendpacket[6] = sendpacket[7] = sendpacket[8] = 
 					 sendpacket[9] = 0x00;
 		}
-	} else {
+	} 
+	else 
+	{
 		/* If the incoming network is 0, make it ours */
-		if( pnetwork_number == 0) {
+		if( pnetwork_number == 0) 
+		{
 			sendpacket[6] = (unsigned char)(network_number >> 24);
 			sendpacket[7] = (unsigned char)((network_number & 
 					 0x00FF0000) >> 16);
@@ -2453,15 +2406,20 @@
 			  (sendpacket[19] << 16) + (sendpacket[20] << 8) + 
 			  sendpacket[21]);
 
-	if( !incoming ) {
+	if( !incoming ) 
+	{
 		/* If the source network is ours, make it 0 */
-		if( pnetwork_number == network_number) {
+		if( pnetwork_number == network_number) 
+		{
 			sendpacket[18] = sendpacket[19] = sendpacket[20] = 
 					 sendpacket[21] = 0x00;
 		}
-	} else {
+	}
+	else
+	{
 		/* If the source network is 0, make it ours */
-		if( pnetwork_number == 0 ) {
+		if( pnetwork_number == 0 ) 
+		{
 			sendpacket[18] = (unsigned char)(network_number >> 24);
 			sendpacket[19] = (unsigned char)((network_number & 
 					 0x00FF0000) >> 16);
diff -u --recursive --new-file v2.1.90/linux/drivers/net/sdladrv.c linux/drivers/net/sdladrv.c
--- v2.1.90/linux/drivers/net/sdladrv.c	Mon Feb 23 18:12:06 1998
+++ linux/drivers/net/sdladrv.c	Sat Mar 21 10:56:27 1998
@@ -324,6 +324,9 @@
  * Return:	0	ok.
  *		< 0	error
  */
+ 
+EXPORT_SYMBOL(sdla_setup);
+
 int sdla_setup (sdlahw_t* hw, void* sfm, unsigned len)
 {
 	unsigned* irq_opt	= NULL;	/* IRQ options */
@@ -449,6 +452,9 @@
 /*============================================================================
  * Shut down SDLA: disable shared memory access and interrupts, stop CPU, etc.
  */
+
+EXPORT_SYMBOL(sdla_down);
+
 int sdla_down (sdlahw_t* hw)
 {
 	unsigned port = hw->port;
@@ -491,6 +497,9 @@
 /*============================================================================
  * Map shared memory window into SDLA adress space.
  */
+
+EXPORT_SYMBOL(sdla_mapmem);
+
 int sdla_mapmem (sdlahw_t* hw, unsigned long addr)
 {
 	unsigned port = hw->port;
@@ -552,6 +561,9 @@
 /*============================================================================
  * Enable interrupt generation.
  */
+ 
+EXPORT_SYMBOL(sdla_inten);
+
 int sdla_inten (sdlahw_t* hw)
 {
 	unsigned port = hw->port;
@@ -606,6 +618,9 @@
 /*============================================================================
  * Disable interrupt generation.
  */
+
+EXPORT_SYMBOL(sdla_intde);
+
 int sdla_intde (sdlahw_t* hw)
 {
 	unsigned port = hw->port;
@@ -662,6 +677,9 @@
 /*============================================================================
  * Acknowledge SDLA hardware interrupt.
  */
+
+EXPORT_SYMBOL(sdla_intack);
+
 int sdla_intack (sdlahw_t* hw)
 {
 	unsigned port = hw->port;
@@ -711,6 +729,9 @@
 /*============================================================================
  * Generate an interrupt to adapter's CPU.
  */
+
+EXPORT_SYMBOL(sdla_intr);
+
 int sdla_intr (sdlahw_t* hw)
 {
 	unsigned port = hw->port;
@@ -756,6 +777,9 @@
  * o Busy-wait until flag is reset.
  * o Return number of loops made, or 0 if command timed out.
  */
+
+EXPORT_SYMBOL(sdla_exec);
+
 int sdla_exec (void* opflag)
 {
 	volatile unsigned char* flag = opflag;
@@ -784,6 +808,9 @@
  * This function is not atomic, so caller must disable interrupt if
  * interrupt routines are accessing adapter shared memory.
  */
+ 
+EXPORT_SYMBOL(sdla_peek);
+
 int sdla_peek (sdlahw_t* hw, unsigned long addr, void* buf, unsigned len)
 {
 	unsigned long oldvec = hw->vector;
@@ -823,6 +850,9 @@
  * This function is not atomic, so caller must disable interrupt if
  * interrupt routines are accessing adapter shared memory.
  */
+ 
+EXPORT_SYMBOL(sdla_poke);
+ 
 int sdla_poke (sdlahw_t* hw, unsigned long addr, void* buf, unsigned len)
 {
 	unsigned long oldvec = hw->vector;
@@ -1826,5 +1856,6 @@
 	}
 	return crc;
 }
+
 
 /****** End *****************************************************************/
diff -u --recursive --new-file v2.1.90/linux/drivers/net/sk_g16.c linux/drivers/net/sk_g16.c
--- v2.1.90/linux/drivers/net/sk_g16.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/sk_g16.c	Wed Mar 18 09:55:40 1998
@@ -762,16 +762,16 @@
 	    dev->dev_addr[4],
 	    dev->dev_addr[5]);
 
-    /* Grab the I/O Port region */
-    request_region(ioaddr, ETHERCARD_TOTAL_SIZE,"sk_g16");
-
-    /* Initialize device structure */
-
     /* Allocate memory for private structure */
     p = dev->priv = (void *) kmalloc(sizeof(struct priv), GFP_KERNEL);
-    if (p == NULL)
+    if (p == NULL) {
+	   printk("%s: ERROR - no memory for driver data!\n", dev->name);
 	   return -ENOMEM;
+    }
     memset((char *) dev->priv, 0, sizeof(struct priv)); /* clear memory */
+
+    /* Grab the I/O Port region */
+    request_region(ioaddr, ETHERCARD_TOTAL_SIZE,"sk_g16");
 
     /* Assign our Device Driver functions */
 
diff -u --recursive --new-file v2.1.90/linux/drivers/nubus/Makefile linux/drivers/nubus/Makefile
--- v2.1.90/linux/drivers/nubus/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/nubus/Makefile	Sat Mar 21 11:06:51 1998
@@ -0,0 +1,15 @@
+#
+# Makefile for the nubus specific 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 definition is now inherited from the
+# parent makefile.
+#
+
+L_OBJS   := nubus.o
+L_TARGET := nubus.a
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.90/linux/drivers/nubus/nubus.c linux/drivers/nubus/nubus.c
--- v2.1.90/linux/drivers/nubus/nubus.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/nubus/nubus.c	Sat Mar 21 11:06:51 1998
@@ -0,0 +1,629 @@
+/*
+ *	Macintosh Nubus Interface Code
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/nubus.h>
+#include <linux/errno.h>
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/page.h>
+/* for LCIII stuff; better find a general way like MACH_HAS_NUBUS */
+#include <asm/macintosh.h>
+
+#undef LCIII_WEIRDNESS
+ 
+static struct nubus_slot nubus_slots[16];
+ 
+/*
+ *	Please skip to the bottom of this file if you ate lunch recently
+ *				-- Alan
+ */
+ 
+ 
+ 
+/* This function tests for the presence of an address, specially a
+ * hardware register address. It is called very early in the kernel
+ * initialization process, when the VBR register isn't set up yet. On
+ * an Atari, it still points to address 0, which is unmapped. So a bus
+ * error would cause another bus error while fetching the exception
+ * vector, and the CPU would do nothing at all. So we needed to set up
+ * a temporary VBR and a vector table for the duration of the test.
+ *
+ * See the atari/config.c code we nicked it from for more clues.
+ */
+
+int nubus_hwreg_present( volatile void *regp )
+{
+    int	ret = 0;
+    long	save_sp, save_vbr;
+    long	tmp_vectors[3];
+    unsigned long flags;
+    
+    save_flags(flags);
+    cli();
+
+    __asm__ __volatile__
+	(	"movec	%/vbr,%2\n\t"
+		"movel	#Lberr1,%4@(8)\n\t"
+                "movec	%4,%/vbr\n\t"
+		"movel	%/sp,%1\n\t"
+		"moveq	#0,%0\n\t"
+		"tstb	%3@\n\t"  
+		"nop\n\t"
+		"moveq	#1,%0\n"
+                "Lberr1:\n\t"
+		"movel	%1,%/sp\n\t"
+		"movec	%2,%/vbr"
+		: "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
+		: "a" (regp), "a" (tmp_vectors)
+                );
+    restore_flags(flags);
+    return( ret );
+}
+
+
+
+/*
+ *	Yes this sucks. The ROM can appear on arbitary bytes of the long
+ *	word. We are not amused.
+ */
+ 
+extern __inline__ int not_useful(void *p, int map)
+{
+	unsigned long pv=(unsigned long)p;
+	pv&=3;
+	if(map&(1<<pv))
+		return 0;
+	return 1;
+}
+ 
+static unsigned long nubus_get_rom(unsigned char **ptr, int len, int map)
+{
+	unsigned long v=0;
+	unsigned char *p=*ptr;	/* as v|=*((*ptr)++) upset someone */
+	while(len)
+	{
+		v<<=8;
+		while(not_useful(p,map))
+			p++;
+		v|=*p++;
+		len--;
+	}
+	*ptr=p;
+	return v;
+}
+
+static void nubus_rewind(unsigned char **ptr, int len, int map)
+{
+	unsigned char *p=*ptr;
+	
+	if(len>8192)
+		printk("rewind of %d!\n", len);
+	while(len)
+	{
+		do
+		{
+			p--;
+		}
+		while(not_useful(p,map));
+		len--;
+	}
+	*ptr=p;
+}
+
+static void nubus_advance(unsigned char **ptr, int len, int map)
+{
+	unsigned char *p=*ptr;
+	if(len>8192)
+		printk("advance of %d!\n", len);
+	while(len)
+	{
+		while(not_useful(p,map))
+			p++;
+			p++;
+		len--;
+	}
+	*ptr=p;
+}
+
+/*
+ *	24bit signed offset to 32bit
+ */
+ 
+static unsigned long nubus_expand32(unsigned long foo)
+{
+	if(foo&0x00800000)	/* 24bit negative */
+		foo|=0xFF000000;
+	return foo;
+}
+
+static void nubus_move(unsigned char **ptr, int len, int map)
+{
+	if(len>0)
+		nubus_advance(ptr,len,map);
+	else if(len<0)
+		nubus_rewind(ptr,-len,map);
+}
+
+static void *nubus_rom_addr(int slot)
+{	
+	/*
+	 *	Returns the first byte after the card. We then walk
+	 *	backwards to get the lane register and the config
+	 */
+	return (void *)(0xF1000000+(slot<<24));
+}
+
+void nubus_memcpy(int slot, void *to, unsigned char *p, int len)
+{
+	unsigned char *t=(unsigned char *)to;
+	while(len)
+	{
+		*t++=nubus_get_rom(&p,1, nubus_slots[slot].slot_lanes);
+		len--;
+	}
+}
+
+void nubus_strncpy(int slot, void *to, unsigned char *p, int len)
+{
+	unsigned char *t=(unsigned char *)to;
+	while(len)
+	{
+		*t=nubus_get_rom(&p,1, nubus_slots[slot].slot_lanes);
+		if(!*t++)
+			break;
+		len--;
+	}
+}
+
+
+
+	
+unsigned char *nubus_dirptr(struct nubus_dirent *nd)
+{
+	unsigned char *p=(unsigned char *)(nd->base);
+	
+	nubus_move(&p, nubus_expand32(nd->value), nd->mask);
+	return p;
+}
+
+	
+struct nubus_dir *nubus_openrootdir(int slot)
+{
+	static struct nubus_dir nbdir;
+	unsigned char *rp=nubus_rom_addr(slot);
+	
+	nubus_rewind(&rp,20, nubus_slots[slot].slot_lanes);
+
+	nubus_move(&rp, nubus_expand32(nubus_slots[slot].slot_directory),
+		nubus_slots[slot].slot_lanes);
+		
+	nbdir.base=rp;
+	nbdir.length=nubus_slots[slot].slot_dlength;
+	nbdir.count=0;
+	nbdir.mask=nubus_slots[slot].slot_lanes;
+	return &nbdir;
+}
+
+struct nubus_dir *nubus_opensubdir(struct nubus_dirent *d)
+{
+	static struct nubus_dir nbdir;
+	unsigned char *rp=nubus_dirptr(d);
+	nbdir.base=rp;
+	nbdir.length=99999;/*slots[i].slot_dlength;*/
+	nbdir.count=0;
+	nbdir.mask=d->mask;
+	return &nbdir;
+}
+
+void nubus_closedir(struct nubus_dir *nd)
+{
+	;
+}
+
+struct nubus_dirent *nubus_readdir(struct nubus_dir *nd)
+{
+	u32 resid;
+	u8 rescode;
+	static struct nubus_dirent d;
+	
+	if(nd->count==nd->length)
+		return NULL;
+
+	d.base=(unsigned long)nd->base;
+		
+	resid=nubus_get_rom(&nd->base, 4, nd->mask);
+	nd->count++;
+	rescode=resid>>24;
+	if(rescode==0xFF)
+	{
+		nd->count=nd->length;
+		return NULL;
+	}
+	d.type=rescode;
+	d.value=resid&0xFFFFFF;
+	d.mask=nd->mask;
+	return &d;
+}
+
+/*
+ *	MAC video handling irritations
+ */
+
+static unsigned char nubus_vid_byte[16];
+static unsigned long nubus_vid_offset[16];
+
+static void nubus_irqsplat(int slot, void *dev_id, struct pt_regs *regs)
+{
+	unsigned char *p=((unsigned char *)nubus_slot_addr(slot))+
+			nubus_vid_offset[slot];
+	*p=nubus_vid_byte[slot];
+}
+
+static int nubus_add_irqsplatter(int slot, unsigned long ptr, unsigned char v)
+{
+	nubus_vid_byte[slot]=v;
+	nubus_vid_offset[slot]=ptr;
+	nubus_request_irq(slot, NULL, nubus_irqsplat);
+	return 0;
+}
+ 
+void nubus_video_shutup(int slot, struct nubus_type *nt)
+{
+	if(nt->category!=3 /* Display */ || nt->type!=1 /* Video */
+		|| nt->DrSW!=1 /* Quickdraw device */)
+		return;
+	switch(nt->DrHW)
+	{
+		/*
+		 *	Toby and MacII Hires cards. These behave in a MacII
+		 *	anyway but not on an RBV box
+		 */
+		case 0x0001:
+		case 0x0013:
+			nubus_add_irqsplatter(slot, 0xA0000, 0);
+			break;
+		/*
+		 *	Apple workstation video card.
+		 */
+		case 0x0006:
+			nubus_add_irqsplatter(slot, 0xA00000, 0);
+			break;
+		/*
+		 *	Futura cards
+		 */
+		case 0x0417:
+		case 0x042F:
+			nubus_add_irqsplatter(slot, 0xF05000, 0x80);
+			break;
+			
+		/*
+		 *	Fingers crossed 8)
+		 *	
+		 *	If you have another card and an RBV based mac you'll
+		 *	almost certainly have to add it here to make it work.
+		 */
+		
+		default:
+			break;
+	}
+}
+
+/*
+ *	Device list
+ */
+
+static struct nubus_device_specifier *nubus_device_list=NULL;
+ 
+void register_nubus_device(struct nubus_device_specifier *d)
+{
+	d->next=nubus_device_list;
+	nubus_device_list=d;
+}
+
+void unregister_nubus_device(struct nubus_device_specifier *nb)
+{
+	struct nubus_device_specifier **t=&nubus_device_list;
+	while(*t!=nb && *t)
+		t=&((*t)->next);
+	*t=nb->next;
+}
+
+static struct nubus_device_specifier *find_nubus_device(int slot, struct nubus_type *nt)
+{
+	struct nubus_device_specifier *t=nubus_device_list; 
+	while(t!=NULL)
+	{
+		if(t->setup(t,slot, nt)==0)
+			return t;
+		t=t->next;
+	}
+	printk("No driver for device [%d %d %d %d]\n",
+		nt->category, nt->type, nt->DrHW, nt->DrSW);
+	return NULL;
+}
+
+/*
+ *	Probe a nubus slot
+ */
+
+void nubus_probe_slot(int slot, int mode)
+{
+	unsigned char *rp;
+	unsigned char dp;
+	int lanes;
+	int i;
+	unsigned long dpat;
+	struct nubus_dir *dir;
+	struct nubus_dirent *nd;
+	struct nubus_type type_info;
+
+	/*
+	 *	Ok see whats cooking in the bytelanes
+	 */
+	
+	rp=nubus_rom_addr(slot);
+	
+	for(i=4;i;i--)
+	{
+		rp--;
+		
+		if(!nubus_hwreg_present(rp))
+			continue;
+			
+		dp=*rp;
+	
+		if(dp==0)
+			continue;
+		
+		/*
+		 *	Valid ?
+		 */
+		 
+		if((((dp>>4)^dp)&0x0F)!=0x0F)
+			continue;
+			
+		if((dp&0x0F) >= 1<<i)
+			continue;
+			
+		/*
+		 *	Looks promising
+		 */
+		
+		nubus_slots[slot].slot_flags|=NUBUS_DEVICE_PRESENT;
+		lanes=dp;
+
+		if (mode==0)
+			printk("nubus%c: ",
+				"0123456789abcdef"[slot]);
+		
+		
+		/*
+		 *	Time to dig deeper. Find the ROM base
+		 *	and read it
+		 */
+		 
+		rp=nubus_rom_addr(slot); 
+		
+		/*
+		 *	Now to make this more fun the ROM is only visible
+		 *	on its bytelanes - that is smeared across the address
+		 *	space.
+		 */
+		
+		nubus_rewind(&rp,20,lanes);
+		
+		nubus_slots[slot].slot_directory=	nubus_get_rom(&rp,4,lanes);
+		nubus_slots[slot].slot_dlength	=	nubus_get_rom(&rp,4,lanes);
+		nubus_slots[slot].slot_crc	=	nubus_get_rom(&rp,4,lanes);
+		nubus_slots[slot].slot_rev	=	nubus_get_rom(&rp,1,lanes);
+		nubus_slots[slot].slot_format	=	nubus_get_rom(&rp,1,lanes);
+		nubus_slots[slot].slot_lanes	=	lanes;
+		
+		dpat=nubus_get_rom(&rp,4,lanes);
+		
+		/*
+		 *	Ok now check what we got
+		 */
+		
+		if(!(nubus_slots[slot].slot_directory&0x00FF0000))
+			printk("Dodgy doffset ??\n");
+		if(dpat!=0x5A932BC7)
+			printk("Wrong test pattern %lx\n",dpat);
+		
+		/*
+		 *	I wonder how the CRC is meant to work -
+		 *		any takers ?
+		 */
+		
+		
+		/*
+		 *	Now parse the directories on the card
+		 */
+		 
+		
+		dir=nubus_openrootdir(slot);
+		
+		/*
+		 *	Find the board resource
+		 */
+		 
+		while((nd=nubus_readdir(dir))!=NULL)
+		{
+			/*
+			 *	This ought to be 1. 1 doesn't work, 0x80
+			 *	does. Seems the Apple docs are wrong.
+			 */
+			if(nd->type==0x80/*RES_ID_BOARD_DIR*/)
+				break;
+		}
+		
+		nubus_closedir(dir);
+		
+		if(nd==NULL)
+		{
+			printk("board resource not found!\n");
+			return;
+		}
+		
+		dir=nubus_opensubdir(nd);
+				
+		/*
+		 *	Walk the board resource
+		 */
+		 
+		while((nd=nubus_readdir(dir))!=NULL)
+		{
+			switch(nd->type)
+			{
+				case RES_ID_TYPE:
+				{
+					unsigned short nbtdata[4];
+					nubus_memcpy(slot, nbtdata, nubus_dirptr(nd), 8);
+					type_info.category=nbtdata[0];
+					type_info.type=nbtdata[1];
+					type_info.DrHW=nbtdata[2];
+					type_info.DrSW=nbtdata[3];
+					break;
+				}
+				case RES_ID_NAME:
+					nubus_strncpy(slot, nubus_slots[slot].slot_cardname,nubus_dirptr(nd),64);
+					break;
+				default:
+					;
+			}
+		}
+		
+		nubus_closedir(dir);
+
+		/*
+		 *	Attempt to bind a driver to this slot
+		 */
+		
+		if (mode==0) {
+			printk("%s\n",
+				nubus_slots[slot].slot_cardname);
+			find_nubus_device(slot,&type_info);
+		}
+		if (mode==1)
+			nubus_video_shutup(slot, &type_info);
+
+		return;
+	}
+}
+
+
+void nubus_probe_bus(void)
+{
+	int i;
+	for(i=9;i<15;i++)
+	{
+		/* printk("nubus: probing slot %d !\n", i); */
+		nubus_probe_slot(i, 0);
+	}
+}
+
+/*
+ *	RBV machines have level triggered video interrupts, and a VIA
+ *	emulation that doesn't always seem to include being able to disable 
+ *	an interrupt. Totally lusing hardware. Before we can init irq's we
+ *	have to install a handler to shut the bloody things up.
+ */
+
+void nubus_sweep_video(void)
+{
+	int i;
+	return; /* XXX why ?? */
+	for(i=9;i<15;i++)
+	{
+		nubus_probe_slot(i,1);
+	}
+}
+
+/*
+ *	Support functions
+ */
+ 
+int nubus_ethernet_addr(int slot, unsigned char *addr)
+{
+	struct nubus_dir *nb;
+	struct nubus_dirent *d;
+	int ng=-ENOENT;
+		
+	nb=nubus_openrootdir(slot);
+	
+	if(nb==NULL)
+		return -ENOENT;
+		
+	while((d=nubus_readdir(nb))!=NULL)
+	{
+		if(d->type==0x80)	/* First private resource */
+			break;
+	}
+	if(d==NULL)
+		return -ENOENT;
+	
+	nb=nubus_opensubdir(d);
+	
+	while((d=nubus_readdir(nb))!=NULL)
+	{
+		if(d->type==0x80)	/* First private field is the mac */
+		{
+			int i;
+			nubus_memcpy(slot, addr, nubus_dirptr(d), 6);
+/*			printk("d.base=%lX, d.value=%lX\n",
+				d->base,d->value);
+			memcpy(addr,"\xC0\xC1\xC2\xC3\xC4\xC5",6);*/
+			printk("MAC address: ");
+			for(i=0;i<6;i++)
+			{
+				printk("%s%02X", i?":":"", addr[i]);
+			}
+			ng=0;
+			break;
+		}
+		else
+			printk("ID=%d val=%x\n",
+				d->type, d->value);
+	}
+	return ng;
+}
+
+void nubus_init(void)
+{
+	/* 
+	 *	Register cards
+	 */
+#ifdef CONFIG_DAYNAPORT
+	extern struct nubus_device_specifier nubus_8390;	
+#endif
+
+	if (!MACH_IS_MAC) 
+		return;
+
+#ifdef LCIII_WEIRDNESS
+	if (macintosh_config->ident == MAC_MODEL_LCIII) {
+	        printk("nubus init: LCIII has no nubus!\n");
+		return;
+	}
+#endif
+
+#ifdef CONFIG_DAYNAPORT
+	register_nubus_device(&nubus_8390);
+#endif
+
+	/*
+	 *	And probe
+	 */
+	 
+	nubus_init_via();
+	printk("Scanning nubus slots.\n");
+	nubus_probe_bus();
+}
diff -u --recursive --new-file v2.1.90/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v2.1.90/linux/drivers/pci/pci.c	Fri Jan 30 11:28:08 1998
+++ linux/drivers/pci/pci.c	Thu Mar 19 11:47:05 1998
@@ -71,7 +71,7 @@
 unsigned int pci_scan_bus(struct pci_bus *bus, unsigned long *mem_startp)
 {
 	unsigned int devfn, l, max, class;
-	unsigned char cmd, irq, tmp, hdr_type = 0;
+	unsigned char cmd, irq, tmp, hdr_type, is_multi = 0;
 	struct pci_dev *dev;
 	struct pci_bus *child;
 	int reg;
@@ -82,12 +82,13 @@
 
 	max = bus->secondary;
 	for (devfn = 0; devfn < 0xff; ++devfn) {
-		if (PCI_FUNC(devfn) == 0) {
-			pcibios_read_config_byte(bus->number, devfn, PCI_HEADER_TYPE, &hdr_type);
-		} else if (!(hdr_type & 0x80)) {
+		if (PCI_FUNC(devfn) && !is_multi) {
 			/* not a multi-function device */
 			continue;
 		}
+		pcibios_read_config_byte(bus->number, devfn, PCI_HEADER_TYPE, &hdr_type);
+		if (!PCI_FUNC(devfn))
+			is_multi = hdr_type & 0x80;
 
 		pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &l);
 		/* some broken boards return 0 if a slot is empty: */
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/53c7xx.c linux/drivers/scsi/53c7xx.c
--- v2.1.90/linux/drivers/scsi/53c7xx.c	Mon Feb 23 18:12:06 1998
+++ linux/drivers/scsi/53c7xx.c	Sat Mar 21 11:09:46 1998
@@ -2,7 +2,7 @@
  * 53c710 driver.  Modified from Drew Eckhardts driver
  * for 53c810 by Richard Hirst [richard@sleepie.demon.co.uk]
  * Check out PERM_OPTIONS and EXPECTED_CLOCK, which may be defined in the
- * relevant machine specific file (eg. mvme166.[ch], amiga7xx.[ch]).
+ * relevant machine specific file (eg. mvme16x.[ch], amiga7xx.[ch]).
  * There are also currently some defines at the top of 53c7xx.scr.
  * The chip type is #defined in script_asm.pl, as well as the Makefile.
  * Host scsi ID expected to be 7 - see NCR53c7x0_init().
@@ -10,9 +10,9 @@
  * I have removed the PCI code and some of the 53c8xx specific code - 
  * simply to make this file smaller and easier to manage.
  *
- * MVME166 issues:
+ * MVME16x issues:
  *   Problems trying to read any chip registers in NCR53c7x0_init(), as they
- *   may never have been set by 166Bug (eg. If kernel has come in over tftp).
+ *   may never have been set by 16xBug (eg. If kernel has come in over tftp).
  */
 
 /*
@@ -45,6 +45,9 @@
  * validids:0x??	-	Bitmask field that disallows certain ID's.
  *			-	e.g.	0x03	allows ID 0,1
  *			-		0x1F	allows ID 0,1,2,3,4
+ * opthi:n		-	replace top word of options with 'n'
+ * optlo:n		-	replace bottom word of options with 'n'
+ *			-	ALWAYS SPECIFY opthi THEN optlo <<<<<<<<<<
  */
 
 /*
@@ -60,7 +63,7 @@
  *	out brain damaged main boards.
  *
  * Other PERM_OPTIONS settings are listed below.  Note the actual options
- * required are set in the relevant file (mvme166.c, amiga7xx.c, etc):
+ * required are set in the relevant file (mvme16x.c, amiga7xx.c, etc):
  *
  *   OPTION_NO_ASYNC
  *	Don't negotiate for asynchronous transfers on the first command 
@@ -231,6 +234,9 @@
 #endif
 
 #include <linux/config.h>
+
+#include <linux/types.h>
+#include <asm/setup.h>
 #include <asm/dma.h>
 #include <asm/io.h>
 #include <asm/system.h>
@@ -259,11 +265,13 @@
 #define NO_IO_SPACE
 #endif
 
-#ifdef CONFIG_MVME166
-#include <asm/mvme166hw.h>
+#ifdef CONFIG_MVME16x
+#include <asm/pgtable.h>
+#include <asm/mvme16xhw.h>
 
 #define BIG_ENDIAN
 #define NO_IO_SPACE
+#define VALID_IDS
 #endif
 
 #include "scsi.h"
@@ -636,6 +644,7 @@
     EXTENDED_MESSAGE, 2 /* length */, EXTENDED_WDTR, 1 /* 2^1 bytes */
 };
 
+#if 0
 /*
  * Function : struct Scsi_Host *find_host (int host)
  * 
@@ -734,6 +743,7 @@
 	hostdata->options &= ~OPTION_DISCONNECT;
     return 0;
 }
+#endif
 
 /*
  * Function : static void NCR53c7x0_driver_init (struct Scsi_Host *host)
@@ -751,10 +761,6 @@
     int i, j;
     u32 *ncrcurrent;
 
-flush_cache_all();
-cache_push(virt_to_bus(hostdata->script), flushsize);
-cache_clear(virt_to_bus(hostdata->script), flushsize);
-
     for (i = 0; i < 16; ++i) {
 	hostdata->request_sense[i] = 0;
     	for (j = 0; j < 8; ++j) 
@@ -783,9 +789,9 @@
     	hostdata->initiate_sdtr = 0;
     hostdata->talked_to = 0;
     hostdata->idle = 1;
-flush_cache_all();
-cache_push(virt_to_bus(hostdata->script), flushsize);
-cache_clear(virt_to_bus(hostdata->script), flushsize);
+
+    if (!MACH_IS_MVME16x)
+	cache_push(virt_to_bus(hostdata->script), flushsize);
 }
 
 /* 
@@ -877,18 +883,22 @@
 		setup_used[--i] = 1;
     }
 
+    if (check_setup_strings("opthi",&flags,&val,buf))
+	hostdata->options = (long long)val << 32;
+    if (check_setup_strings("optlo",&flags,&val,buf))
+	hostdata->options |= val;
 
     NCR53c7x0_local_setup(host);
-
     switch (hostdata->chip) {
     case 710:
+    case 770:
     	hostdata->dstat_sir_intr = NCR53c7x0_dstat_sir_intr;
     	hostdata->init_save_regs = NULL;
     	hostdata->dsa_fixup = NCR53c7xx_dsa_fixup;
     	hostdata->init_fixup = NCR53c7x0_init_fixup;
     	hostdata->soft_reset = NCR53c7x0_soft_reset;
 	hostdata->run_tests = NCR53c7xx_run_tests;
-	expected_clock = hostdata->scsi_clock = 50000000;
+	expected_clock = hostdata->scsi_clock;
 	expected_id = 7;
     	break;
     default:
@@ -904,7 +914,6 @@
     hostdata->NCR53c7xx_msg_abort = ABORT;
     hostdata->NCR53c7xx_msg_nop = NOP;
     hostdata->NOP_insn = (DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24;
-
     if (expected_mapping == -1 || 
 	(hostdata->options & (OPTION_MEMORY_MAPPED)) != 
 	(expected_mapping & OPTION_MEMORY_MAPPED))
@@ -917,6 +926,12 @@
     hostdata->istat = ((hostdata->chip / 100) == 8) ? 
     	ISTAT_REG_800 : ISTAT_REG_700;
 
+/* We have to assume that this may be the first access to the chip, so
+ * we must set EA in DCNTL. */
+
+    NCR53c7x0_write8 (DCNTL_REG, DCNTL_10_EA|DCNTL_10_COM);
+
+
 /* Only the ISTAT register is readable when the NCR is running, so make 
    sure it's halted. */
     ncr_halt(host);
@@ -990,7 +1005,7 @@
      * On NCR53c700 series chips, DCNTL controls the SCSI clock divisor,
      * on 800 series chips, it allows for a totem-pole IRQ driver.
      * NOTE saved_dcntl currently overwritten in init function.
-     * The value read here may be garbage anyway, MVME166 board at least
+     * The value read here may be garbage anyway, MVME16x board at least
      * does not initialise chip if kernel arrived via tftp.
      */
 
@@ -999,7 +1014,7 @@
     /*
      * DMODE controls DMA burst length, and on 700 series chips,
      * 286 mode and bus width  
-     * NOTE:  On MVME166, chip may have been reset, so this could be a
+     * NOTE:  On MVME16x, chip may have been reset, so this could be a
      * power-on/reset default value.
      */
     hostdata->saved_dmode = NCR53c7x0_read8(hostdata->dmode);
@@ -1053,11 +1068,11 @@
      * with another board.
      */
 
-#ifdef CONFIG_MVME166
-    if (request_irq(IRQ_MVME166_SCSI, NCR53c7x0_intr, 0, "SCSI-script", NULL))
+#ifdef CONFIG_MVME16x
+    if (request_irq(IRQ_MVME16x_SCSI, NCR53c7x0_intr, 0, "SCSI-script", NULL))
 	panic ("Couldn't get SCSI IRQ");
-#ifdef MVME166_INTFLY
-    else if (request_irq(IRQ_MVME166_FLY, NCR53c7x0_intr, 0, "SCSI-intfly", NULL))
+#ifdef MVME16x_INTFLY
+    else if (request_irq(IRQ_MVME16x_FLY, NCR53c7x0_intr, 0, "SCSI-intfly", NULL))
 	panic ("Couldn't get INT_FLY IRQ");
 #endif
 #else
@@ -1105,10 +1120,9 @@
 }
 
 /* 
- * Function : static int normal_init(Scsi_Host_Template *tpnt, int board, 
- *	int chip, u32 base, int io_port, int irq, int dma, int pcivalid,
- *	unsigned char pci_bus, unsigned char pci_device_fn,
- *	long long options);
+ * Function : static int ncr53c7xx_init(Scsi_Host_Template *tpnt, int board, 
+ *	int chip, u32 base, int io_port, int irq, int dma,
+ *	long long options, int clock);
  *
  * Purpose : initializes a NCR53c7,8x0 based on base addresses,
  *	IRQ, and DMA channel.	
@@ -1137,6 +1151,7 @@
 
     switch (chip) {
     case 710:
+    case 770:
 	schedule_size = (tpnt->can_queue + 1) * 8 /* JUMP instruction size */;
 	script_len = NCR53c7xx_script_len;
     	dsa_len = NCR53c7xx_dsa_len;
@@ -1214,7 +1229,7 @@
     /* FIXME : if we ever support an ISA NCR53c7xx based board, we
        need to check if the chip is running in a 16 bit mode, and if so 
        unregister it if it is past the 16M (0x1000000) mark */
-   	
+
     hostdata = (struct NCR53c7x0_hostdata *) 
     	instance->hostdata;
     hostdata->size = size;
@@ -1269,6 +1284,7 @@
     hostdata->dsa_len = dsa_len;
     hostdata->max_cmd_size = max_cmd_size;
     hostdata->num_cmds = 1;
+    hostdata->scsi_clock = clock;
     /* Initialize single command */
     tmp = (hostdata->script + hostdata->script_count);
 #ifdef FORCE_DSA_ALIGNMENT
@@ -1393,8 +1409,8 @@
      * register.  Make sure SCRIPTS start automagically.
      */
 
-#if defined(CONFIG_MVME166)
-    /* We know better what we want than 166Bug does! */
+#if defined(CONFIG_MVME16x)
+    /* We know better what we want than 16xBug does! */
     tmp = DMODE_10_BL_8 | DMODE_10_FC2;
 #else
     tmp = NCR53c7x0_read8(DMODE_REG_10);
@@ -1543,9 +1559,9 @@
 
     printk("scsi%d : NCR code relocated to 0x%lx (virt 0x%p)\n", host->host_no,
 	virt_to_bus(hostdata->script), hostdata->script);
-flush_cache_all();
-cache_push(virt_to_bus(hostdata->script), flushsize);
-cache_clear(virt_to_bus(hostdata->script), flushsize);
+
+    if (!MACH_IS_MVME16x)
+	cache_push(virt_to_bus(hostdata->script), flushsize);
 }
 
 /*
@@ -1599,9 +1615,8 @@
 	start = virt_to_bus (hostdata->script) + hostdata->E_test_1;
     	hostdata->state = STATE_RUNNING;
 	printk ("scsi%d : test 1", host->host_no);
-	flush_cache_all();
-	cache_push(virt_to_bus(hostdata->script), flushsize);
-	cache_clear(virt_to_bus(hostdata->script), flushsize);
+	if (!MACH_IS_MVME16x)
+	    cache_push(virt_to_bus(hostdata->script), flushsize);
 	NCR53c7x0_write32 (DSP_REG, start);
 	if (hostdata->options & OPTION_DEBUG_TRACE)
 	    NCR53c7x0_write8 (DCNTL_REG, hostdata->saved_dcntl | DCNTL_SSM |
@@ -1695,8 +1710,8 @@
 	    hostdata->test_completed = -1;
 	    start = virt_to_bus(hostdata->script) + hostdata->E_test_2;
 	    hostdata->state = STATE_RUNNING;
-	    flush_cache_all();
-	    cache_clear(virt_to_bus(hostdata->script), flushsize);
+	    if(!MACH_IS_MVME16x)
+		cache_clear(virt_to_bus(hostdata->script), flushsize);
 	    NCR53c7x0_write32 (DSA_REG, virt_to_bus(dsa));
 	    NCR53c7x0_write32 (DSP_REG, start);
 	    if (hostdata->options & OPTION_DEBUG_TRACE)
@@ -1803,9 +1818,10 @@
     patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
 	dsa_temp_addr_dsa_value, virt_to_bus(&cmd->dsa_addr));
 
-flush_cache_all();
-cache_push(virt_to_bus(hostdata->script), flushsize);
-cache_clear(virt_to_bus(hostdata->script), flushsize);
+    if (!MACH_IS_MVME16x) {
+	cache_push(virt_to_bus(hostdata->script), flushsize);
+    	cache_push(virt_to_bus(cmd->dsa), flushsize);
+    }
 }
 
 /* 
@@ -1988,8 +2004,6 @@
      */
     save_flags(flags);
     cli();
-    flush_cache_all();
-    cache_push(virt_to_bus(hostdata->script), flushsize);
     dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG));
     for (bp = hostdata->breakpoints; bp && bp->address != dsp; 
     	bp = bp->next);
@@ -2258,14 +2272,20 @@
 	host->hostdata;		
     u32 dsps,*dsp;	/* Argument of the INT instruction */
 
-flush_cache_all();
-cache_push(virt_to_bus(hostdata->script), flushsize);
-cache_clear(virt_to_bus(hostdata->script), flushsize);
-
     NCR53c7x0_local_setup(host);
     dsps = NCR53c7x0_read32(DSPS_REG);
     dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG));
 
+    /* RGH 150597:  Frig.  Commands which fail with Check Condition are
+     * Flagged as successful - hack dsps to indicate check condition */
+#if 0
+    /* RGH 200597:  Need to disable for BVME6000, as it gets Check Conditions
+     * and then dies.  Seems to handle Check Condition at startup, but
+     * not mid kernel build. */
+    if (dsps == A_int_norm_emulateintfly && c && c->result == 2)
+        dsps = A_int_err_check_condition;
+#endif
+
     if (hostdata->options & OPTION_DEBUG_INTR) 
 	printk ("scsi%d : DSPS = 0x%x\n", host->host_no, dsps);
 
@@ -2870,9 +2890,9 @@
 	    host->host_no, (unsigned) dsps);
 	return SPECIFIC_INT_PANIC;
     }
-flush_cache_all();
-cache_push(virt_to_bus(hostdata->script), flushsize);
-cache_clear(virt_to_bus(hostdata->script), flushsize);
+
+    if (!MACH_IS_MVME16x)
+	flush_cache_all();
 }
 
 /* 
@@ -2910,7 +2930,7 @@
 NCR53c7x0_soft_reset (struct Scsi_Host *host) {
     NCR53c7x0_local_declare();
     unsigned long flags;
-#ifdef CONFIG_MVME166
+#ifdef CONFIG_MVME16x
     volatile unsigned long v;
 #endif
     struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
@@ -2922,7 +2942,7 @@
 
     /* Disable scsi chip and s/w level 7 ints */
 
-#ifdef CONFIG_MVME166
+#ifdef CONFIG_MVME16x
     v = *(volatile unsigned long *)0xfff4006c;
     v &= ~0x8000;
     *(volatile unsigned long *)0xfff4006c = v;
@@ -2997,7 +3017,7 @@
 	    SIEN_PAR : 0) | SIEN_700_STO | SIEN_RST | SIEN_UDC |
 		SIEN_SGE | SIEN_MA);
 
-#ifdef CONFIG_MVME166
+#ifdef CONFIG_MVME16x
     /* Enable scsi chip and s/w level 7 ints */
 
     v = *(volatile unsigned long *)0xfff40080;
@@ -3285,10 +3305,6 @@
     patch_dsa_32(tmp->dsa, dsa_next, 0, 0);
     patch_dsa_32(tmp->dsa, dsa_cmnd, 0, virt_to_bus(cmd));
 
-flush_cache_all();
-cache_push(virt_to_bus(hostdata->script), flushsize);
-cache_clear(virt_to_bus(hostdata->script), flushsize);
-
     if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS) {
 
 	exp_select_indirect = ((1 << cmd->target) << 16) |
@@ -3306,11 +3322,6 @@
     patch_dsa_32(tmp->dsa, dsa_select, 0,
 		hostdata->sync[cmd->target].select_indirect);
 
-
-flush_cache_all();
-cache_push(virt_to_bus(hostdata->script), flushsize);
-cache_clear(virt_to_bus(hostdata->script), flushsize);
-
     /*
      * Right now, we'll do the WIDE and SYNCHRONOUS negotiations on
      * different commands; although it should be trivial to do them
@@ -3339,10 +3350,6 @@
     else if (!(hostdata->talked_to & (1 << cmd->target)) && 
 		!(hostdata->options & OPTION_NO_ASYNC)) {
 
-flush_cache_all();
-cache_push(virt_to_bus(hostdata->script), flushsize);
-cache_clear(virt_to_bus(hostdata->script), flushsize);
-
 	memcpy ((void *) (tmp->select + 1), (void *) async_message, 
 	    sizeof(async_message));
     	patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(async_message));
@@ -3352,10 +3359,6 @@
     else 
     	patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1);
 
-flush_cache_all();
-cache_push(virt_to_bus(hostdata->script), flushsize);
-cache_clear(virt_to_bus(hostdata->script), flushsize);
-
     hostdata->talked_to |= (1 << cmd->target);
     tmp->select[0] = (hostdata->options & OPTION_DISCONNECT) ? 
 	IDENTIFY (1, cmd->lun) : IDENTIFY (0, cmd->lun);
@@ -3429,11 +3432,6 @@
  * Not bad, not good. We'll see.
  */
 
-flush_cache_all();
-cache_push(virt_to_bus(hostdata->script), flushsize);
-cache_clear(virt_to_bus(hostdata->script), flushsize);
-
-
     for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; cmd_datain += 4, 
 	cmd_dataout += 4, ++i) {
 	u32 buf = cmd->use_sg ? 
@@ -3476,11 +3474,6 @@
 	}
     }
 
-flush_cache_all();
-cache_push(virt_to_bus(hostdata->script), flushsize);
-cache_clear(virt_to_bus(hostdata->script), flushsize);
-
-
     /*
      * Install JUMP instructions after the data transfer routines to return
      * control to the do_other_transfer routines.
@@ -3515,10 +3508,6 @@
 	cmd_dataout += 2;
     }
 
-flush_cache_all();
-cache_push(virt_to_bus(hostdata->script), flushsize);
-cache_clear(virt_to_bus(hostdata->script), flushsize);
-
     return tmp;
 }
 
@@ -3693,11 +3682,6 @@
 	i > 0  && ncrcurrent[0] != hostdata->NOP_insn;
 	--i, ncrcurrent += 2 /* JUMP instructions are two words */);
 
-
-flush_cache_all();
-cache_push(virt_to_bus(hostdata->script), flushsize);
-cache_clear(virt_to_bus(hostdata->script), flushsize);
-
     if (i > 0) {
 	++hostdata->busy[tmp->target][tmp->lun];
 	cmd->next = hostdata->running_list;
@@ -3724,15 +3708,15 @@
 	return;
     }
 
-    cache_push(virt_to_bus(cmd->dsa), hostdata->dsa_len);
-    cache_push(virt_to_bus(ncrcurrent), sizeof(ncrcurrent));
-
     /* 
      * If the NCR chip is in an idle state, start it running the scheduler
      * immediately.  Otherwise, signal the chip to jump to schedule as 
      * soon as it is idle.
      */
 
+    if (!MACH_IS_MVME16x)
+	flush_cache_all();
+
     if (hostdata->idle) {
 	hostdata->idle = 0;
 	hostdata->state = STATE_RUNNING;
@@ -4133,7 +4117,7 @@
 	done = 1;
 	for (host = first_host; host; host = host->next) 
 	    if (host->hostt == the_template
-#if defined(MVME166_INTFLY)
+#if defined(MVME16x_INTFLY)
 			/* We have two different interrupts pointing
 			 * at this routine, so remove this check */
 #else
@@ -4157,7 +4141,7 @@
 		istat = NCR53c7x0_read8(hostdata->istat);
 
 		if ((hostdata->options & OPTION_INTFLY) && 
-#ifdef MVME166_INTFLY
+#ifdef MVME16x_INTFLY
                     /* the bit is set which indicates an on-the-fly int */
                         (*(volatile unsigned long *)0xfff40068 & 0x8000))
 #else
@@ -4168,7 +4152,7 @@
 		    done = 0;
 		    interrupted = 1;
 
-#ifdef MVME166_INTFLY
+#ifdef MVME16x_INTFLY
                     /* clear the INTFLY bit */
                     *(volatile unsigned long *)0xfff40074 = 0x8000;
 #endif
@@ -4345,6 +4329,8 @@
 #endif
 		
 		hostdata->state = STATE_RUNNING;
+		if (!MACH_IS_MVME16x)
+		    flush_cache_all();
 		NCR53c7x0_write32 (DSP_REG, virt_to_bus(hostdata->dsp));
 		if (hostdata->options & OPTION_DEBUG_TRACE) {
 #ifdef CYCLIC_TRACE
@@ -4669,7 +4655,9 @@
 	    where = "non-BMI dynamic DSA code";
 	    action = ACTION_ABORT_PRINT;
 	}
-    } else if (dsp == (hostdata->script + hostdata->E_select_msgout / 4)) {
+    } else if (dsp == (hostdata->script + hostdata->E_select_msgout / 4 + 2)) {
+	/* RGH 290697:  Added +2 above, to compensate for the script
+	 * instruction which disables the selection timer. */
 	/* Release ATN */
 	NCR53c7x0_write8 (SOCL_REG, 0);
 	switch (sbcl) {
@@ -4751,8 +4739,9 @@
 	print_insn (host, hostdata->dsp, "", 1);
     }
 #endif
-    
-    cache_push(virt_to_bus(hostdata->script), flushsize);
+
+    if (!MACH_IS_MVME16x)
+	cache_push(virt_to_bus(hostdata->script), flushsize);
 }
 
 /*
@@ -4826,7 +4815,7 @@
      */
 
     if (retry == NEVER) {
-    	printk(KERN_ALERT "          mail drew@PoohSticks.ORG\n");
+    	printk(KERN_ALERT "          mail ricahrd@sleepie.demon.co.uk\n");
     	FATAL (host);
     }
 }
@@ -5097,7 +5086,7 @@
  * FIXME : (void *) cast in virt_to_bus should be unnecessary, because
  * 	it should take const void * as argument.
  */
-#ifndef CONFIG_MVME166
+#ifndef CONFIG_MVME16x
 	sprintf(buf, "%s0x%lx (virt 0x%p) : 0x%08x 0x%08x (virt 0x%p)", 
 	    (prefix ? prefix : ""), virt_to_bus((void *) insn), insn,  
 	    insn[0], insn[1], bus_to_virt (insn[1]));
@@ -5110,7 +5099,7 @@
 #endif
 	tmp = buf + strlen(buf);
 	if ((dcmd & DCMD_TYPE_MASK) == DCMD_TYPE_MMI)  {
-#ifndef CONFIG_MVME166
+#ifndef CONFIG_MVME16x
 	    sprintf (tmp, " 0x%08x (virt 0x%p)\n", insn[2], 
 		bus_to_virt(insn[2]));
 #else
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/53c7xx.h linux/drivers/scsi/53c7xx.h
--- v2.1.90/linux/drivers/scsi/53c7xx.h	Sat May 24 09:10:24 1997
+++ linux/drivers/scsi/53c7xx.h	Sat Mar 21 11:09:47 1998
@@ -893,7 +893,7 @@
 					 * NCR53c710, this bit moved to CTEST8
 					 */
 #define DCNTL_10_COM		0x01	/* 700 software compatibility mode */
-#define DCNTL_10_EA		0x20	/* Enable Ack - needed for MVME166 */
+#define DCNTL_10_EA		0x20	/* Enable Ack - needed for MVME16x */
 
 #define DCNTL_700_SAVE ( DCNTL_CF_MASK | DCNTL_S16)
 
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/53c7xx.scr linux/drivers/scsi/53c7xx.scr
--- v2.1.90/linux/drivers/scsi/53c7xx.scr	Sat May 24 09:10:24 1997
+++ linux/drivers/scsi/53c7xx.scr	Sat Mar 21 11:09:47 1998
@@ -315,7 +315,7 @@
 	MOVE MEMORY 1, reselected_identify, addr_scratch
 	DMODE_MEMORY_TO_MEMORY
 #ifdef BIG_ENDIAN
-	; BIG ENDIAN ON MVME166
+	; BIG ENDIAN ON MVME16x
 	MOVE SCRATCH3 TO SFBR
 #else
 	MOVE SCRATCH0 TO SFBR
@@ -1134,8 +1134,8 @@
     JUMP command_failed, IF 0x02
 #endif
 #if (CHIP == 710)
-#if defined(MVME166_INTFLY)
-; For MVME166 (ie CHIP=710) we will force an INTFLY by triggering a software
+#if defined(MVME16x_INTFLY)
+; For MVME16x (ie CHIP=710) we will force an INTFLY by triggering a software
 ; interupt (SW7).  We can use SCRATCH, as we are about to jump to
 ; schedule, which corrupts it anyway.  Will probably remove this later,
 ; but want to check performance effects first.
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/NCR53C9x.c linux/drivers/scsi/NCR53C9x.c
--- v2.1.90/linux/drivers/scsi/NCR53C9x.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/NCR53C9x.c	Sat Mar 21 11:09:47 1998
@@ -0,0 +1,3733 @@
+/* NCR53C9x.c:  Generic SCSI driver code for NCR53C9x chips.
+ *
+ * Originally esp.c : EnhancedScsiProcessor Sun SCSI driver code.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Most DMA dependencies put in driver specific files by 
+ * Jesper Skov (jskov@cygnus.co.uk)
+ */
+
+/* TODO:
+ *
+ * 1) Maybe disable parity checking in config register one for SCSI1
+ *    targets.  (Gilmore says parity error on the SBus can lock up
+ *    old sun4c's)
+ * 2) Add support for DMA2 pipelining.
+ * 3) Add tagged queueing.
+ * 4) Maybe change use of "esp" to something more "NCR"'ish.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/blk.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "NCR53C9x.h"
+
+#ifdef CONFIG_SCSI_SUNESP
+#include "sparc_esp.h"
+#include <asm/sbus.h>
+#include <asm/dma.h>
+#include <asm/machines.h>
+#include <asm/oplib.h>
+#include <asm/idprom.h>
+#endif
+
+#if defined(CONFIG_BLZ1230_SCSI)||defined(CONFIG_BLZ2060_SCSI)||defined(CONFIG_CYBERSTORMII_SCSI)
+#define SYMBIOS_HACK
+#else
+#undef SYMBIOS_HACK
+#endif
+
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+/* Command phase enumeration. */
+enum {
+	not_issued    = 0x00,  /* Still in the issue_SC queue.          */
+
+	/* Various forms of selecting a target. */
+#define in_slct_mask    0x10
+	in_slct_norm  = 0x10,  /* ESP is arbitrating, normal selection  */
+	in_slct_stop  = 0x11,  /* ESP will select, then stop with IRQ   */
+	in_slct_msg   = 0x12,  /* select, then send a message           */
+	in_slct_tag   = 0x13,  /* select and send tagged queue msg      */
+	in_slct_sneg  = 0x14,  /* select and acquire sync capabilities  */
+
+	/* Any post selection activity. */
+#define in_phases_mask  0x20
+	in_datain     = 0x20,  /* Data is transferring from the bus     */
+	in_dataout    = 0x21,  /* Data is transferring to the bus       */
+	in_data_done  = 0x22,  /* Last DMA data operation done (maybe)  */
+	in_msgin      = 0x23,  /* Eating message from target            */
+	in_msgincont  = 0x24,  /* Eating more msg bytes from target     */
+	in_msgindone  = 0x25,  /* Decide what to do with what we got    */
+	in_msgout     = 0x26,  /* Sending message to target             */
+	in_msgoutdone = 0x27,  /* Done sending msg out                  */
+	in_cmdbegin   = 0x28,  /* Sending cmd after abnormal selection  */
+	in_cmdend     = 0x29,  /* Done sending slow cmd                 */
+	in_status     = 0x2a,  /* Was in status phase, finishing cmd    */
+	in_freeing    = 0x2b,  /* freeing the bus for cmd cmplt or disc */
+	in_the_dark   = 0x2c,  /* Don't know what bus phase we are in   */
+
+	/* Special states, ie. not normal bus transitions... */
+#define in_spec_mask    0x80
+	in_abortone   = 0x80,  /* Aborting one command currently        */
+	in_abortall   = 0x81,  /* Blowing away all commands we have     */
+	in_resetdev   = 0x82,  /* SCSI target reset in progress         */
+	in_resetbus   = 0x83,  /* SCSI bus reset in progress            */
+	in_tgterror   = 0x84,  /* Target did something stupid           */
+};
+
+struct proc_dir_entry proc_scsi_esp = {
+	PROC_SCSI_ESP, 3, "esp",
+	S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/* The master ring of all esp hosts we are managing in this driver. */
+struct NCR_ESP *espchain = 0;
+int nesps = 0, esps_in_use = 0, esps_running = 0;
+
+void esp_intr(int irq, void *dev_id, struct pt_regs *pregs);
+
+/* Debugging routines */
+struct esp_cmdstrings {
+	unchar cmdchar;
+	char *text;
+} esp_cmd_strings[] = {
+	/* Miscellaneous */
+	{ ESP_CMD_NULL, "ESP_NOP", },
+	{ ESP_CMD_FLUSH, "FIFO_FLUSH", },
+	{ ESP_CMD_RC, "RSTESP", },
+	{ ESP_CMD_RS, "RSTSCSI", },
+	/* Disconnected State Group */
+	{ ESP_CMD_RSEL, "RESLCTSEQ", },
+	{ ESP_CMD_SEL, "SLCTNATN", },
+	{ ESP_CMD_SELA, "SLCTATN", },
+	{ ESP_CMD_SELAS, "SLCTATNSTOP", },
+	{ ESP_CMD_ESEL, "ENSLCTRESEL", },
+	{ ESP_CMD_DSEL, "DISSELRESEL", },
+	{ ESP_CMD_SA3, "SLCTATN3", },
+	{ ESP_CMD_RSEL3, "RESLCTSEQ", },
+	/* Target State Group */
+	{ ESP_CMD_SMSG, "SNDMSG", },
+	{ ESP_CMD_SSTAT, "SNDSTATUS", },
+	{ ESP_CMD_SDATA, "SNDDATA", },
+	{ ESP_CMD_DSEQ, "DISCSEQ", },
+	{ ESP_CMD_TSEQ, "TERMSEQ", },
+	{ ESP_CMD_TCCSEQ, "TRGTCMDCOMPSEQ", },
+	{ ESP_CMD_DCNCT, "DISC", },
+	{ ESP_CMD_RMSG, "RCVMSG", },
+	{ ESP_CMD_RCMD, "RCVCMD", },
+	{ ESP_CMD_RDATA, "RCVDATA", },
+	{ ESP_CMD_RCSEQ, "RCVCMDSEQ", },
+	/* Initiator State Group */
+	{ ESP_CMD_TI, "TRANSINFO", },
+	{ ESP_CMD_ICCSEQ, "INICMDSEQCOMP", },
+	{ ESP_CMD_MOK, "MSGACCEPTED", },
+	{ ESP_CMD_TPAD, "TPAD", },
+	{ ESP_CMD_SATN, "SATN", },
+	{ ESP_CMD_RATN, "RATN", },
+};
+#define NUM_ESP_COMMANDS  ((sizeof(esp_cmd_strings)) / (sizeof(struct esp_cmdstrings)))
+
+/* Print textual representation of an ESP command */
+static inline void esp_print_cmd(unchar espcmd)
+{
+	unchar dma_bit = espcmd & ESP_CMD_DMA;
+	int i;
+
+	espcmd &= ~dma_bit;
+	for(i=0; i<NUM_ESP_COMMANDS; i++)
+		if(esp_cmd_strings[i].cmdchar == espcmd)
+			break;
+	if(i==NUM_ESP_COMMANDS)
+		printk("ESP_Unknown");
+	else
+		printk("%s%s", esp_cmd_strings[i].text,
+		       ((dma_bit) ? "+DMA" : ""));
+}
+
+/* Print the status register's value */
+static inline void esp_print_statreg(unchar statreg)
+{
+	unchar phase;
+
+	printk("STATUS<");
+	phase = statreg & ESP_STAT_PMASK;
+	printk("%s,", (phase == ESP_DOP ? "DATA-OUT" :
+		       (phase == ESP_DIP ? "DATA-IN" :
+			(phase == ESP_CMDP ? "COMMAND" :
+			 (phase == ESP_STATP ? "STATUS" :
+			  (phase == ESP_MOP ? "MSG-OUT" :
+			   (phase == ESP_MIP ? "MSG_IN" :
+			    "unknown")))))));
+	if(statreg & ESP_STAT_TDONE)
+		printk("TRANS_DONE,");
+	if(statreg & ESP_STAT_TCNT)
+		printk("TCOUNT_ZERO,");
+	if(statreg & ESP_STAT_PERR)
+		printk("P_ERROR,");
+	if(statreg & ESP_STAT_SPAM)
+		printk("SPAM,");
+	if(statreg & ESP_STAT_INTR)
+		printk("IRQ,");
+	printk(">");
+}
+
+/* Print the interrupt register's value */
+static inline void esp_print_ireg(unchar intreg)
+{
+	printk("INTREG< ");
+	if(intreg & ESP_INTR_S)
+		printk("SLCT_NATN ");
+	if(intreg & ESP_INTR_SATN)
+		printk("SLCT_ATN ");
+	if(intreg & ESP_INTR_RSEL)
+		printk("RSLCT ");
+	if(intreg & ESP_INTR_FDONE)
+		printk("FDONE ");
+	if(intreg & ESP_INTR_BSERV)
+		printk("BSERV ");
+	if(intreg & ESP_INTR_DC)
+		printk("DISCNCT ");
+	if(intreg & ESP_INTR_IC)
+		printk("ILL_CMD ");
+	if(intreg & ESP_INTR_SR)
+		printk("SCSI_BUS_RESET ");
+	printk(">");
+}
+
+/* Print the sequence step registers contents */
+static inline void esp_print_seqreg(unchar stepreg)
+{
+	stepreg &= ESP_STEP_VBITS;
+	printk("STEP<%s>",
+	       (stepreg == ESP_STEP_ASEL ? "SLCT_ARB_CMPLT" :
+		(stepreg == ESP_STEP_SID ? "1BYTE_MSG_SENT" :
+		 (stepreg == ESP_STEP_NCMD ? "NOT_IN_CMD_PHASE" :
+		  (stepreg == ESP_STEP_PPC ? "CMD_BYTES_LOST" :
+		   (stepreg == ESP_STEP_FINI4 ? "CMD_SENT_OK" :
+		    "UNKNOWN"))))));
+}
+
+#if defined(DEBUG_STATE_MACHINE) || defined(DEBUG_ESP)
+static char *phase_string(int phase)
+{
+	switch(phase) {
+	case not_issued:
+		return "UNISSUED";
+	case in_slct_norm:
+		return "SLCTNORM";
+	case in_slct_stop:
+		return "SLCTSTOP";
+	case in_slct_msg:
+		return "SLCTMSG";
+	case in_slct_tag:
+		return "SLCTTAG";
+	case in_slct_sneg:
+		return "SLCTSNEG";
+	case in_datain:
+		return "DATAIN";
+	case in_dataout:
+		return "DATAOUT";
+	case in_data_done:
+		return "DATADONE";
+	case in_msgin:
+		return "MSGIN";
+	case in_msgincont:
+		return "MSGINCONT";
+	case in_msgindone:
+		return "MSGINDONE";
+	case in_msgout:
+		return "MSGOUT";
+	case in_msgoutdone:
+		return "MSGOUTDONE";
+	case in_cmdbegin:
+		return "CMDBEGIN";
+	case in_cmdend:
+		return "CMDEND";
+	case in_status:
+		return "STATUS";
+	case in_freeing:
+		return "FREEING";
+	case in_the_dark:
+		return "CLUELESS";
+	case in_abortone:
+		return "ABORTONE";
+	case in_abortall:
+		return "ABORTALL";
+	case in_resetdev:
+		return "RESETDEV";
+	case in_resetbus:
+		return "RESETBUS";
+	case in_tgterror:
+		return "TGTERROR";
+	default:
+		return "UNKNOWN";
+	};
+}
+#endif
+
+static inline void esp_advance_phase(Scsi_Cmnd *s, int newphase)
+{
+#ifdef DEBUG_STATE_MACHINE
+	ESPLOG(("<%s>", phase_string(newphase)));
+#endif
+	s->SCp.sent_command = s->SCp.phase;
+	s->SCp.phase = newphase;
+}
+
+extern inline void esp_cmd(struct NCR_ESP *esp, struct ESP_regs *eregs,
+			   unchar cmd)
+{
+#ifdef DEBUG_ESP_CMDS
+	esp->espcmdlog[esp->espcmdent] = cmd;
+	esp->espcmdent = (esp->espcmdent + 1) & 31;
+#endif
+	eregs->esp_cmd = cmd;
+}
+
+/* How we use the various Linux SCSI data structures for operation.
+ *
+ * struct scsi_cmnd:
+ *
+ *   We keep track of the syncronous capabilities of a target
+ *   in the device member, using sync_min_period and
+ *   sync_max_offset.  These are the values we directly write
+ *   into the ESP registers while running a command.  If offset
+ *   is zero the ESP will use asynchronous transfers.
+ *   If the borken flag is set we assume we shouldn't even bother
+ *   trying to negotiate for synchronous transfer as this target
+ *   is really stupid.  If we notice the target is dropping the
+ *   bus, and we have been allowing it to disconnect, we clear
+ *   the disconnect flag.
+ */
+
+
+/* Manipulation of the ESP command queues.  Thanks to the aha152x driver
+ * and its author, Juergen E. Fischer, for the methods used here.
+ * Note that these are per-ESP queues, not global queues like
+ * the aha152x driver uses.
+ */
+static inline void append_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC)
+{
+	Scsi_Cmnd *end;
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	new_SC->host_scribble = (unsigned char *) NULL;
+	if(!*SC)
+		*SC = new_SC;
+	else {
+		for(end=*SC;end->host_scribble;end=(Scsi_Cmnd *)end->host_scribble)
+			;
+		end->host_scribble = (unsigned char *) new_SC;
+	}
+	restore_flags(flags);
+}
+
+static inline void prepend_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC)
+{
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	new_SC->host_scribble = (unsigned char *) *SC;
+	*SC = new_SC;
+	restore_flags(flags);
+}
+
+static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd **SC)
+{
+	Scsi_Cmnd *ptr;
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	ptr = *SC;
+	if(ptr)
+		*SC = (Scsi_Cmnd *) (*SC)->host_scribble;
+	restore_flags(flags);
+	return ptr;
+}
+
+static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, int target, int lun)
+{
+	Scsi_Cmnd *ptr, *prev;
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	for(ptr = *SC, prev = NULL;
+	    ptr && ((ptr->target != target) || (ptr->lun != lun));
+	    prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble)
+		;
+	if(ptr) {
+		if(prev)
+			prev->host_scribble=ptr->host_scribble;
+		else
+			*SC=(Scsi_Cmnd *)ptr->host_scribble;
+	}
+	restore_flags(flags);
+	return ptr;
+}
+
+/* Resetting various pieces of the ESP scsi driver chipset */
+
+/* Reset the ESP chip, _not_ the SCSI bus. */
+static inline void esp_reset_esp(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	int family_code, version, i;
+	volatile int trash;
+
+	/* Now reset the ESP chip */
+	esp_cmd(esp, eregs, ESP_CMD_RC);
+	esp_cmd(esp, eregs, ESP_CMD_NULL | ESP_CMD_DMA);
+	esp_cmd(esp, eregs, ESP_CMD_NULL | ESP_CMD_DMA);
+
+	/* Reload the configuration registers */
+	eregs->esp_cfact = esp->cfact;
+	eregs->esp_stp   = 0;
+	eregs->esp_soff  = 0;
+	eregs->esp_timeo = esp->neg_defp;
+
+	/* This is the only point at which it is reliable to read
+	 * the ID-code for a fast ESP chip variant.
+	 */
+	esp->max_period = ((35 * esp->ccycle) / 1000);
+	if(esp->erev == fast) {
+		version = eregs->esp_uid;
+		family_code = (version & 0xf8) >> 3;
+#ifdef SYMBIOS_HACK
+		if (version == 0 && family_code == 0)
+		  {
+		    printk ("Detected SymBIOS chip with no family code.\n");
+		    version = 3;
+		    family_code = 2;
+		  }
+#endif
+		if(family_code == 0x02)
+			esp->erev = fas236;
+		else if(family_code == 0x0a)
+			esp->erev = fashme; /* Version is usually '5'. */
+		else
+			esp->erev = fas100a;
+		printk("esp%d: FAST chip is %s (family=%d, version=%d)\n",
+		       esp->esp_id,
+		       (esp->erev == fas236) ? "fas236" :
+		       ((esp->erev == fas100a) ? "fas100a" :
+		       "fasHME"), family_code, (version & 7));
+
+		esp->min_period = ((4 * esp->ccycle) / 1000);
+	} else {
+		esp->min_period = ((5 * esp->ccycle) / 1000);
+	}
+	esp->max_period = (esp->max_period + 3)>>2;
+	esp->min_period = (esp->min_period + 3)>>2;
+
+	eregs->esp_cfg1  = esp->config1;
+	switch(esp->erev) {
+	case esp100:
+		/* nothing to do */
+		break;
+	case esp100a:
+		eregs->esp_cfg2 = esp->config2;
+		break;
+	case esp236:
+		/* Slow 236 */
+		eregs->esp_cfg2 = esp->config2;
+		eregs->esp_cfg3 = esp->config3[0];
+		break;
+	case fashme:
+		esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB);
+		/* fallthrough... */
+	case fas236:
+		/* Fast 236 or HME */
+		eregs->esp_cfg2 = esp->config2;
+		for(i=0; i<8; i++) {
+			if(esp->erev == fashme)
+				esp->config3[i] |=
+					(ESP_CONFIG3_FCLOCK | ESP_CONFIG3_BIGID | ESP_CONFIG3_OBPUSH);
+			else
+				esp->config3[i] |= ESP_CONFIG3_FCLK;
+		}
+		eregs->esp_cfg3 = esp->config3[0];
+		if(esp->erev == fashme) {
+			esp->radelay = 80;
+		} else {
+			if(esp->diff)
+				esp->radelay = 0;
+			else
+				esp->radelay = 96;
+		}
+		break;
+	case fas100a:
+		/* Fast 100a */
+		eregs->esp_cfg2 = esp->config2;
+		for(i=0; i<8; i++)
+			esp->config3[i] |= ESP_CONFIG3_FCLOCK;
+		eregs->esp_cfg3 = esp->config3[0];
+		esp->radelay = 32;
+		break;
+	default:
+		panic("esp: what could it be... I wonder...");
+		break;
+	};
+
+	/* Eat any bitrot in the chip */
+	trash = eregs->esp_intrpt;
+	udelay(100);
+}
+
+/* This places the ESP into a known state at boot time. */
+inline void esp_bootup_reset(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	volatile unchar trash;
+
+	/* Reset the DMA */
+	if(esp->dma_reset)
+		esp->dma_reset(esp);
+
+	/* Reset the ESP */
+	esp_reset_esp(esp, eregs);
+
+	/* Reset the SCSI bus, but tell ESP not to generate an irq */
+	eregs->esp_cfg1 |= ESP_CONFIG1_SRRDISAB;
+	esp_cmd(esp, eregs, ESP_CMD_RS);
+	udelay(400);
+	eregs->esp_cfg1 = esp->config1;
+
+	/* Eat any bitrot in the chip and we are done... */
+	trash = eregs->esp_intrpt;
+}
+
+/* Allocate structure and insert basic data such as SCSI chip frequency
+ * data and a pointer to the device
+ */
+struct NCR_ESP* esp_allocate(Scsi_Host_Template *tpnt, void *esp_dev)
+{
+	struct NCR_ESP *esp, *elink;
+	struct Scsi_Host *esp_host;
+
+	esp_host = scsi_register(tpnt, sizeof(struct NCR_ESP));
+	if(!esp_host)
+		panic("Cannot register ESP SCSI host");
+	esp = (struct NCR_ESP *) esp_host->hostdata;
+	if(!esp)
+		panic("No esp in hostdata");
+	esp->ehost = esp_host;
+	esp->edev = esp_dev;
+	esp->esp_id = nesps++;
+
+	/* Put into the chain of esp chips detected */
+	if(espchain) {
+		elink = espchain;
+		while(elink->next) elink = elink->next;
+		elink->next = esp;
+	} else {
+		espchain = esp;
+	}
+	esp->next = 0;
+
+	return esp;
+}
+
+/* Complete initialization of ESP structure and device
+ * Caller must have initialized appropriate parts of the ESP structure
+ * between the call to esp_allocate and this function.
+ */
+void esp_initialize(struct NCR_ESP *esp)
+{
+	struct ESP_regs *eregs = esp->eregs;
+	unsigned int fmhz;
+	unchar ccf;
+	int i;
+	
+	/* Check out the clock properties of the chip. */
+
+	/* This is getting messy but it has to be done
+	 * correctly or else you get weird behavior all
+	 * over the place.  We are trying to basically
+	 * figure out three pieces of information.
+	 *
+	 * a) Clock Conversion Factor
+	 *
+	 *    This is a representation of the input
+	 *    crystal clock frequency going into the
+	 *    ESP on this machine.  Any operation whose
+	 *    timing is longer than 400ns depends on this
+	 *    value being correct.  For example, you'll
+	 *    get blips for arbitration/selection during
+	 *    high load or with multiple targets if this
+	 *    is not set correctly.
+	 *
+	 * b) Selection Time-Out
+	 *
+	 *    The ESP isn't very bright and will arbitrate
+	 *    for the bus and try to select a target
+	 *    forever if you let it.  This value tells
+	 *    the ESP when it has taken too long to
+	 *    negotiate and that it should interrupt
+	 *    the CPU so we can see what happened.
+	 *    The value is computed as follows (from
+	 *    NCR/Symbios chip docs).
+	 *
+	 *          (Time Out Period) *  (Input Clock)
+	 *    STO = ----------------------------------
+	 *          (8192) * (Clock Conversion Factor)
+	 *
+	 *    You usually want the time out period to be
+	 *    around 250ms, I think we'll set it a little
+	 *    bit higher to account for fully loaded SCSI
+	 *    bus's and slow devices that don't respond so
+	 *    quickly to selection attempts. (yeah, I know
+	 *    this is out of spec. but there is a lot of
+	 *    buggy pieces of firmware out there so bite me)
+	 *
+	 * c) Imperical constants for synchronous offset
+	 *    and transfer period register values
+	 *
+	 *    This entails the smallest and largest sync
+	 *    period we could ever handle on this ESP.
+	 */
+	
+	fmhz = esp->cfreq;
+
+	if(fmhz <= (5000000))
+		ccf = 0;
+	else
+		ccf = (((5000000 - 1) + (fmhz))/(5000000));
+	if(!ccf || ccf > 8) {
+		/* If we can't find anything reasonable,
+		 * just assume 20MHZ.  This is the clock
+		 * frequency of the older sun4c's where I've
+		 * been unable to find the clock-frequency
+		 * PROM property.  All other machines provide
+		 * useful values it seems.
+		 */
+		ccf = ESP_CCF_F4;
+		fmhz = (20000000);
+	}
+	if(ccf==(ESP_CCF_F7+1))
+		esp->cfact = ESP_CCF_F0;
+	else if(ccf == ESP_CCF_NEVER)
+		esp->cfact = ESP_CCF_F2;
+	else
+		esp->cfact = ccf;
+	esp->cfreq = fmhz;
+	esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz);
+	esp->ctick = ESP_TICK(ccf, esp->ccycle);
+	esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf);
+	esp->sync_defp = SYNC_DEFP_SLOW;
+
+
+	/* Fill in ehost data */
+	esp->ehost->base = (unsigned char *) eregs;
+	esp->ehost->this_id = esp->scsi_id;
+	esp->ehost->irq = esp->irq;
+
+	/* SCSI id mask */
+	esp->scsi_id_mask = (1 << esp->scsi_id);
+
+	/* Probe the revision of this esp */
+	esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
+	esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
+	eregs->esp_cfg2 = esp->config2;
+#ifndef SYMBIOS_HACK
+	if((eregs->esp_cfg2 & ~(ESP_CONFIG2_MAGIC)) !=
+	   (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
+		printk("NCR53C90(esp100) detected\n");
+		esp->erev = esp100;
+	} else {
+#endif
+		eregs->esp_cfg2 = esp->config2 = 0;
+		eregs->esp_cfg3 = 0;
+		eregs->esp_cfg3 = esp->config3[0] = 5;
+#ifndef SYMBIOS_HACK
+		if(eregs->esp_cfg3 != 5) {
+			printk("NCR53C90A(esp100a) detected\n");
+			esp->erev = esp100a;
+		} else {
+#else
+		  {
+#endif
+			int target;
+			
+			for(target=0; target<8; target++)
+				esp->config3[target] = 0;
+			eregs->esp_cfg3 = 0;
+#ifndef SYMBIOS_HACK
+			if(ccf > ESP_CCF_F5) {
+#endif
+				printk("NCR53C9XF(espfast) detected\n");
+				esp->erev = fast;
+				eregs->esp_cfg2 = esp->config2 = 0;
+				esp->sync_defp = SYNC_DEFP_FAST;
+#ifndef SYMBIOS_HACK
+			} else {
+				printk("NCR53C9x(esp236) detected\n");
+				esp->erev = esp236;
+				eregs->esp_cfg2 = esp->config2 = 0;
+			}
+		}
+#endif
+	}				
+	
+	/* Initialize the command queues */
+	esp->current_SC = 0;
+	esp->disconnected_SC = 0;
+	esp->issue_SC = 0;
+	
+	/* Clear the state machines. */
+	esp->targets_present = 0;
+	esp->resetting_bus = 0;
+	esp->snip = 0;
+	esp->targets_present = 0;
+	for(i = 0; i < 32; i++)
+		esp->espcmdlog[i] = 0;
+	esp->espcmdent = 0;
+	for(i = 0; i < 16; i++) {
+		esp->cur_msgout[i] = 0;
+		esp->cur_msgin[i] = 0;
+	}
+	esp->prevmsgout = esp->prevmsgin = 0;
+	esp->msgout_len = esp->msgin_len = 0;
+
+	/* Reset the thing before we try anything... */
+	esp_bootup_reset(esp, eregs);
+	
+	esps_in_use++;
+
+	printk("SCSI ID %d  Clock %d MHz CCF=%d Time-Out %d ",
+	       esp->scsi_id, (esp->cfreq / 1000000),
+	       esp->ccf, (int) esp->neg_defp);
+}
+
+/* The info function will return whatever useful
+ * information the developer sees fit.  If not provided, then
+ * the name field will be used instead.
+ */
+const char *esp_info(struct Scsi_Host *host)
+{
+	struct NCR_ESP *esp;
+
+	esp = (struct NCR_ESP *) host->hostdata;
+	switch(esp->erev) {
+	case esp100:
+		return "Sparc ESP100 (NCR53C90)";
+	case esp100a:
+		return "Sparc ESP100A (NCR53C90A)";
+	case esp236:
+		return "Sparc ESP236";
+	case fas236:
+		return "Sparc ESP236-FAST";
+	case fashme:
+		return "Sparc ESP366-HME";
+	case fas100a:
+		return "Sparc ESP100A-FAST";
+	default:
+		panic("Bogon ESP revision");
+	};
+}
+
+/* From Wolfgang Stanglmeier's NCR scsi driver. */
+struct info_str
+{
+	char *buffer;
+	int length;
+	int offset;
+	int pos;
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int len)
+{
+	if (info->pos + len > info->length)
+		len = info->length - info->pos;
+
+	if (info->pos + len < info->offset) {
+		info->pos += len;
+		return;
+	}
+	if (info->pos < info->offset) {
+		data += (info->offset - info->pos);
+		len  -= (info->offset - info->pos);
+	}
+
+	if (len > 0) {
+		memcpy(info->buffer + info->pos, data, len);
+		info->pos += len;
+	}
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+	va_list args;
+	char buf[81];
+	int len;
+
+	va_start(args, fmt);
+	len = vsprintf(buf, fmt, args);
+	va_end(args);
+
+	copy_mem_info(info, buf, len);
+	return len;
+}
+
+static int esp_host_info(struct NCR_ESP *esp, char *ptr, off_t offset, int len)
+{
+	struct info_str info;
+	int i;
+
+	info.buffer	= ptr;
+	info.length	= len;
+	info.offset	= offset;
+	info.pos	= 0;
+
+	copy_info(&info, "Sparc ESP Host Adapter:\n");
+	copy_info(&info, "\tPROM node\t\t%08lx\n", (unsigned long) esp->prom_node);
+	copy_info(&info, "\tPROM name\t\t%s\n", esp->prom_name);
+	copy_info(&info, "\tESP Model\t\t");
+	switch(esp->erev) {
+	case esp100:
+		copy_info(&info, "ESP100\n");
+		break;
+	case esp100a:
+		copy_info(&info, "ESP100A\n");
+		break;
+	case esp236:
+		copy_info(&info, "ESP236\n");
+		break;
+	case fas236:
+		copy_info(&info, "FAS236\n");
+		break;
+	case fas100a:
+		copy_info(&info, "FAS100A\n");
+		break;
+	case fast:
+		copy_info(&info, "FAST\n");
+		break;
+	case fashme:
+		copy_info(&info, "Happy Meal FAS\n");
+		break;
+	case espunknown:
+	default:
+		copy_info(&info, "Unknown!\n");
+		break;
+	};
+#ifdef CONFIG_SCSI_SUNESP
+	copy_info(&info, "\tDMA Revision\t\t");
+	switch(((struct Linux_SBus_DMA*) (esp->dma))->revision) {
+	case dvmarev0:
+		copy_info(&info, "Rev 0\n");
+		break;
+	case dvmaesc1:
+		copy_info(&info, "ESC Rev 1\n");
+		break;
+	case dvmarev1:
+		copy_info(&info, "Rev 1\n");
+		break;
+	case dvmarev2:
+		copy_info(&info, "Rev 2\n");
+		break;
+	case dvmarev3:
+		copy_info(&info, "Rev 3\n");
+		break;
+	case dvmarevplus:
+		copy_info(&info, "Rev 1+\n");
+		break;
+	case dvmahme:
+		copy_info(&info, "Rev HME/FAS\n");
+		break;
+	default:
+		copy_info(&info, "Unknown!\n");
+		break;
+	};
+#endif
+	copy_info(&info, "\tLive Targets\t\t[ ");
+	for(i = 0; i < 15; i++) {
+		if(esp->targets_present & (1 << i))
+			copy_info(&info, "%d ", i);
+	}
+	copy_info(&info, "]\n\n");
+	
+	/* Now describe the state of each existing target. */
+	copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\tWide\n");
+	for(i = 0; i < 15; i++) {
+		if(esp->targets_present & (1 << i)) {
+			Scsi_Device *SDptr = esp->ehost->host_queue;
+
+			while((SDptr->host != esp->ehost) &&
+			      (SDptr->id != i) &&
+			      (SDptr->next))
+				SDptr = SDptr->next;
+
+			copy_info(&info, "%d\t\t", i);
+			copy_info(&info, "%08lx\t", esp->config3[i]);
+			copy_info(&info, "[%02lx,%02lx]\t\t\t", SDptr->sync_max_offset,
+				  SDptr->sync_min_period);
+			copy_info(&info, "%s\t\t", SDptr->disconnect ? "yes" : "no");
+			copy_info(&info, "%s\n",
+				  (esp->config3[i] & ESP_CONFIG3_EWIDE) ? "yes" : "no");
+		}
+	}
+
+	return info.pos > info.offset? info.pos - info.offset : 0;
+}
+
+/* ESP proc filesystem code. */
+int esp_proc_info(char *buffer, char **start, off_t offset, int length,
+		  int hostno, int inout)
+{
+	struct NCR_ESP *esp;
+
+	if(inout)
+		return -EINVAL; /* not yet */
+
+	for_each_esp(esp) {
+		if(esp->ehost->host_no == hostno)
+			break;
+	}
+	if(!esp)
+		return -EINVAL;
+
+	if(start)
+		*start = buffer;
+
+	return esp_host_info(esp, buffer, offset, length);
+}
+
+/* Some rules:
+ *
+ *   1) Never ever panic while something is live on the bus.
+ *      If there is to be any chance of syncing the disks this
+ *      rule is to be obeyed.
+ *
+ *   2) Any target that causes a foul condition will no longer
+ *      have synchronous transfers done to it, no questions
+ *      asked.
+ *
+ *   3) Keep register accesses to a minimum.  Think about some
+ *      day when we have Xbus machines this is running on and
+ *      the ESP chip is on the other end of the machine on a
+ *      different board from the cpu where this is running.
+ */
+
+/* Fire off a command.  We assume the bus is free and that the only
+ * case where we could see an interrupt is where we have disconnected
+ * commands active and they are trying to reselect us.
+ */
+static inline void esp_check_cmd(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+	switch(sp->cmd_len) {
+	case 6:
+	case 10:
+	case 12:
+		esp->esp_slowcmd = 0;
+		break;
+
+	default:
+		esp->esp_slowcmd = 1;
+		esp->esp_scmdleft = sp->cmd_len;
+		esp->esp_scmdp = &sp->cmnd[0];
+		break;
+	};
+}
+
+static inline void build_sync_nego_msg(struct NCR_ESP *esp, int period, int offset)
+{
+	esp->cur_msgout[0] = EXTENDED_MESSAGE;
+	esp->cur_msgout[1] = 3;
+	esp->cur_msgout[2] = EXTENDED_SDTR;
+	esp->cur_msgout[3] = period;
+	esp->cur_msgout[4] = offset;
+	esp->msgout_len = 5;
+}
+
+/* SIZE is in bits, currently HME only supports 16 bit wide transfers. */
+static inline void build_wide_nego_msg(struct NCR_ESP *esp, int size)
+{
+	esp->cur_msgout[0] = EXTENDED_MESSAGE;
+	esp->cur_msgout[1] = 2;
+	esp->cur_msgout[2] = EXTENDED_WDTR;
+	switch(size) {
+	case 32:
+		esp->cur_msgout[3] = 2;
+		break;
+	case 16:
+		esp->cur_msgout[3] = 1;
+		break;
+	case 8:
+	default:
+		esp->cur_msgout[3] = 0;
+		break;
+	};
+
+	esp->msgout_len = 4;
+}
+
+static inline void esp_exec_cmd(struct NCR_ESP *esp)
+{
+	struct ESP_regs *eregs = esp->eregs;
+	Scsi_Cmnd *SCptr;
+	Scsi_Device *SDptr;
+	volatile unchar *cmdp = esp->esp_command;
+	unsigned char the_esp_command;
+	int lun, target;
+	int i;
+
+	/* Hold off if we've been reselected or an IRQ is showing... */
+	if(esp->disconnected_SC || esp->dma_irq_p(esp))
+		return;
+
+	/* Grab first member of the issue queue. */
+	SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC);
+
+	/* Safe to panic here because current_SC is null. */
+	if(!SCptr) panic("esp: esp_exec_cmd and issue queue is NULL");
+
+	SDptr = SCptr->device;
+	lun = SCptr->lun;
+	target = SCptr->target;
+
+	esp->snip = 0;
+	esp->msgout_len = 0;
+
+	/* Send it out whole, or piece by piece?   The ESP
+	 * only knows how to automatically send out 6, 10,
+	 * and 12 byte commands.  I used to think that the
+	 * Linux SCSI code would never throw anything other
+	 * than that to us, but then again there is the
+	 * SCSI generic driver which can send us anything.
+	 */
+	esp_check_cmd(esp, SCptr);
+
+	/* If arbitration/selection is successful, the ESP will leave
+	 * ATN asserted, causing the target to go into message out
+	 * phase.  The ESP will feed the target the identify and then
+	 * the target can only legally go to one of command,
+	 * datain/out, status, or message in phase, or stay in message
+	 * out phase (should we be trying to send a sync negotiation
+	 * message after the identify).  It is not allowed to drop
+	 * BSY, but some buggy targets do and we check for this
+	 * condition in the selection complete code.  Most of the time
+	 * we'll make the command bytes available to the ESP and it
+	 * will not interrupt us until it finishes command phase, we
+	 * cannot do this for command sizes the ESP does not
+	 * understand and in this case we'll get interrupted right
+	 * when the target goes into command phase.
+	 *
+	 * It is absolutely _illegal_ in the presence of SCSI-2 devices
+	 * to use the ESP select w/o ATN command.  When SCSI-2 devices are
+	 * present on the bus we _must_ always go straight to message out
+	 * phase with an identify message for the target.  Being that
+	 * selection attempts in SCSI-1 w/o ATN was an option, doing SCSI-2
+	 * selections should not confuse SCSI-1 we hope.
+	 */
+
+	if(SDptr->sync) {
+		/* this targets sync is known */
+#ifdef CONFIG_SCSI_SUNESP
+do_sync_known:
+#endif
+		if(SDptr->disconnect)
+			*cmdp++ = IDENTIFY(1, lun);
+		else
+			*cmdp++ = IDENTIFY(0, lun);
+
+		if(esp->esp_slowcmd) {
+			the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA);
+			esp_advance_phase(SCptr, in_slct_stop);
+		} else {
+			the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA);
+			esp_advance_phase(SCptr, in_slct_norm);
+		}
+	} else if(!(esp->targets_present & (1<<target)) || !(SDptr->disconnect)) {
+		/* After the bootup SCSI code sends both the
+		 * TEST_UNIT_READY and INQUIRY commands we want
+		 * to at least attempt allowing the device to
+		 * disconnect.
+		 */
+		ESPMISC(("esp: Selecting device for first time. target=%d "
+			 "lun=%d\n", target, SCptr->lun));
+		if(!SDptr->borken && !SDptr->disconnect)
+			SDptr->disconnect = 1;
+
+		*cmdp++ = IDENTIFY(0, lun);
+		esp->prevmsgout = NOP;
+		esp_advance_phase(SCptr, in_slct_norm);
+		the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA);
+
+		/* Take no chances... */
+		SDptr->sync_max_offset = 0;
+		SDptr->sync_min_period = 0;
+	} else {
+		int toshiba_cdrom_hwbug_wkaround = 0;
+
+#ifdef CONFIG_SCSI_SUNESP
+		/* Never allow disconnects or synchronous transfers on
+		 * SparcStation1 and SparcStation1+.  Allowing those
+		 * to be enabled seems to lockup the machine completely.
+		 */
+		if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
+		   (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
+			/* But we are nice and allow tapes to disconnect. */
+			if(SDptr->type == TYPE_TAPE)
+				SDptr->disconnect = 1;
+			else
+				SDptr->disconnect = 0;
+			SDptr->sync_max_offset = 0;
+			SDptr->sync_min_period = 0;
+			SDptr->sync = 1;
+			esp->snip = 0;
+			goto do_sync_known;
+		}
+#endif
+		/* We've talked to this guy before,
+		 * but never negotiated.. lets try,
+		 * need to attempt WIDE first, before
+		 * sync nego, as per SCSI 2 standard.
+		 */
+		if(esp->erev == fashme && !SDptr->wide) {
+			if(!SDptr->borken &&
+			   (SDptr->type != TYPE_ROM ||
+			    strncmp(SDptr->vendor, "TOSHIBA", 7))) {
+				build_wide_nego_msg(esp, 16);
+				esp->config3[SCptr->target] |= ESP_CONFIG3_EWIDE;
+				SDptr->wide = 1;
+				esp->wnip = 1;
+				goto after_nego_msg_built;
+			} else {
+				SDptr->wide = 1;
+				/* Fall through and try sync. */
+			}
+		}
+
+		if(!SDptr->borken) {
+			if((SDptr->type == TYPE_ROM) &&
+			   (!strncmp(SDptr->vendor, "TOSHIBA", 7))) {
+				/* Nice try sucker... */
+				printk(KERN_INFO "esp%d: Disabling sync for buggy "
+				       "Toshiba CDROM.\n", esp->esp_id);
+				toshiba_cdrom_hwbug_wkaround = 1;
+				build_sync_nego_msg(esp, 0, 0);
+			} else {
+				build_sync_nego_msg(esp, esp->sync_defp, 15);
+			}
+		} else {
+			build_sync_nego_msg(esp, 0, 0);
+		}
+		SDptr->sync = 1;
+		esp->snip = 1;
+
+after_nego_msg_built:
+		/* A fix for broken SCSI1 targets, when they disconnect
+		 * they lock up the bus and confuse ESP.  So disallow
+		 * disconnects for SCSI1 targets for now until we
+		 * find a better fix.
+		 *
+		 * Addendum: This is funny, I figured out what was going
+		 *           on.  The blotzed SCSI1 target would disconnect,
+		 *           one of the other SCSI2 targets or both would be
+		 *           disconnected as well.  The SCSI1 target would
+		 *           stay disconnected long enough that we start
+		 *           up a command on one of the SCSI2 targets.  As
+		 *           the ESP is arbitrating for the bus the SCSI1
+		 *           target begins to arbitrate as well to reselect
+		 *           the ESP.  The SCSI1 target refuses to drop it's
+		 *           ID bit on the data bus even though the ESP is
+		 *           at ID 7 and is the obvious winner for any
+		 *           arbitration.  The ESP is a poor sport and refuses
+		 *           to lose arbitration, it will continue indefinately
+		 *           trying to arbitrate for the bus and can only be
+		 *           stopped via a chip reset or SCSI bus reset.
+		 *           Therefore _no_ disconnects for SCSI1 targets
+		 *           thank you very much. ;-)
+		 */
+		if(((SDptr->scsi_level < 3) && (SDptr->type != TYPE_TAPE)) ||
+#if 1 /* Until I find out why HME barfs with disconnects enabled... */
+		   toshiba_cdrom_hwbug_wkaround || SDptr->borken || esp->erev == fashme) {
+#else
+		   toshiba_cdrom_hwbug_wkaround || SDptr->borken) {
+#endif
+			printk(KERN_INFO "esp%d: Disabling DISCONNECT for target %d "
+			       "lun %d\n", esp->esp_id, SCptr->target, SCptr->lun);
+			SDptr->disconnect = 0;
+			*cmdp++ = IDENTIFY(0, lun);
+		} else {
+			*cmdp++ = IDENTIFY(1, lun);
+		}
+
+		/* ESP fifo is only so big...
+		 * Make this look like a slow command.
+		 */
+		esp->esp_slowcmd = 1;
+		esp->esp_scmdleft = SCptr->cmd_len;
+		esp->esp_scmdp = &SCptr->cmnd[0];
+
+		the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA);
+		esp_advance_phase(SCptr, in_slct_msg);
+	}
+
+	if(!esp->esp_slowcmd)
+		for(i = 0; i < SCptr->cmd_len; i++)
+			*cmdp++ = SCptr->cmnd[i];
+
+	/* HME sucks... */
+	if(esp->erev == fashme)
+		eregs->esp_busid = (target & 0xf) |
+			(ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT);
+	else
+		eregs->esp_busid = (target & 7);
+	eregs->esp_soff = SDptr->sync_max_offset;
+	eregs->esp_stp  = SDptr->sync_min_period;
+	if(esp->erev > esp100a)
+		eregs->esp_cfg3 = esp->config3[target];
+
+	i = (cmdp - esp->esp_command);
+
+	/* Set up the DMA and ESP counters */
+	if(esp->do_pio_cmds){
+		int j = 0;
+
+		for(;j<i;j++)
+			eregs->esp_fdata = esp->esp_command[j];
+		the_esp_command &= ~ESP_CMD_DMA;
+
+		/* Tell ESP to "go". */
+		esp_cmd(esp, eregs, the_esp_command);
+	} else {
+		if(esp->erev == fashme) {
+			esp_cmd(esp, eregs, ESP_CMD_FLUSH); /* Grrr! */
+
+			/* Set up the HME counters */
+			eregs->esp_tclow = i;
+			eregs->esp_tcmed = 0;
+			eregs->fas_rlo = 0;
+			eregs->fas_rhi = 0;
+			esp_cmd(esp, eregs, the_esp_command);
+			esp->dma_init_write(esp, esp->esp_command_dvma, 16);
+		} else {
+			/* Set up the ESP counters */
+			eregs->esp_tclow = i;
+			eregs->esp_tcmed = 0;
+			esp->dma_init_write(esp, esp->esp_command_dvma, i);
+
+			/* Tell ESP to "go". */
+			esp_cmd(esp, eregs, the_esp_command);
+		}
+	}
+}
+
+/* Queue a SCSI command delivered from the mid-level Linux SCSI code. */
+int esp_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+	struct NCR_ESP *esp;
+	unsigned long flags;
+
+	/* Set up func ptr and initial driver cmd-phase. */
+	SCpnt->scsi_done = done;
+	SCpnt->SCp.phase = not_issued;
+
+	esp = (struct NCR_ESP *) SCpnt->host->hostdata;
+
+	if(esp->dma_led_on)
+		esp->dma_led_on(esp);
+
+	/* We use the scratch area. */
+	ESPQUEUE(("esp_queue: target=%d lun=%d ", SCpnt->target, SCpnt->lun));
+	ESPDISC(("N<%02x,%02x>", SCpnt->target, SCpnt->lun));
+	if(!SCpnt->use_sg) {
+		ESPQUEUE(("!use_sg\n"));
+		SCpnt->SCp.this_residual    = SCpnt->request_bufflen;
+		SCpnt->SCp.buffer           =
+			(struct scatterlist *) SCpnt->request_buffer;
+		SCpnt->SCp.buffers_residual = 0;
+#ifdef CONFIG_SCSI_SUNESP
+		/* Sneaky. */
+		SCpnt->SCp.have_data_in = mmu_get_scsi_one((char *)SCpnt->SCp.buffer,
+							   SCpnt->SCp.this_residual,
+							   ((struct linux_sbus_device*) (esp->edev))->my_bus);
+		/* XXX The casts are extremely gross, but with 64-bit kernel
+		 * XXX and 32-bit SBUS what am I to do? -DaveM
+		 */
+		SCpnt->SCp.ptr = (char *)((unsigned long)SCpnt->SCp.have_data_in);
+#else
+		SCpnt->SCp.have_data_in = (int) SCpnt->SCp.ptr =
+			(char *) VTOP((unsigned long) SCpnt->request_buffer);
+#endif
+
+	} else {
+		ESPQUEUE(("use_sg "));
+#ifdef DEBUG_ESP_SG
+		printk("esp%d: sglist at %p with %d buffers\n",
+		       esp->esp_id, SCpnt->buffer, SCpnt->use_sg);
+#endif
+		SCpnt->SCp.buffer           = (struct scatterlist *) SCpnt->buffer;
+		SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
+		SCpnt->SCp.this_residual    = SCpnt->SCp.buffer->length;
+#ifdef CONFIG_SCSI_SUNESP
+		mmu_get_scsi_sgl((struct mmu_sglist *) SCpnt->SCp.buffer,
+				 SCpnt->SCp.buffers_residual,
+				 ((struct linux_sbus_device *) (esp->edev))->my_bus);
+		/* XXX Again these casts are sick... -DaveM */
+		SCpnt->SCp.ptr=(char *)((unsigned long)SCpnt->SCp.buffer->dvma_address);
+#else
+		SCpnt->SCp.ptr = 
+		  (char *) VTOP((unsigned long) SCpnt->SCp.buffer->address);
+#endif
+	}
+	SCpnt->SCp.Status           = CHECK_CONDITION;
+	SCpnt->SCp.Message          = 0xff;
+	SCpnt->SCp.sent_command     = 0;
+
+	/* Place into our queue. */
+	if(SCpnt->cmnd[0] == REQUEST_SENSE) {
+		ESPQUEUE(("RQSENSE\n"));
+		prepend_SC(&esp->issue_SC, SCpnt);
+	} else {
+		ESPQUEUE(("\n"));
+		append_SC(&esp->issue_SC, SCpnt);
+	}
+
+ 	save_and_cli(flags);
+
+	/* Run it now if we can. */
+	if(!esp->current_SC && !esp->resetting_bus)
+		esp_exec_cmd(esp);
+
+	restore_flags(flags);
+	return 0;
+}
+
+/* Only queuing supported in this ESP driver. */
+int esp_command(Scsi_Cmnd *SCpnt)
+{
+#ifdef DEBUG_ESP
+	struct NCR_ESP *esp = (struct NCR_ESP *) SCpnt->host->hostdata;
+#endif
+
+	ESPLOG(("esp%d: esp_command() called...\n", esp->esp_id));
+	return -1;
+}
+
+/* Dump driver state. */
+static inline void esp_dump_cmd(Scsi_Cmnd *SCptr)
+{
+	ESPLOG(("[tgt<%02x> lun<%02x> "
+		"pphase<%s> cphase<%s>]",
+		SCptr->target, SCptr->lun,
+		phase_string(SCptr->SCp.sent_command),
+		phase_string(SCptr->SCp.phase)));
+}
+
+static inline void esp_dump_state(struct NCR_ESP *esp, 
+				  struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+#ifdef DEBUG_ESP_CMDS
+	int i;
+#endif
+
+	ESPLOG(("esp%d: dumping state\n", esp->esp_id));
+	
+	/* Print DMA status */
+	esp->dma_dump_state(esp);
+
+	ESPLOG(("esp%d: SW [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
+		esp->esp_id, esp->sreg, esp->seqreg, esp->ireg));
+	ESPLOG(("esp%d: HW reread [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
+		esp->esp_id, eregs->esp_status, eregs->esp_sstep, eregs->esp_intrpt));
+#ifdef DEBUG_ESP_CMDS
+	printk("esp%d: last ESP cmds [", esp->esp_id);
+	i = (esp->espcmdent - 1) & 31;
+	printk("<");
+	esp_print_cmd(esp->espcmdlog[i]);
+	printk(">");
+	i = (i - 1) & 31;
+	printk("<");
+	esp_print_cmd(esp->espcmdlog[i]);
+	printk(">");
+	i = (i - 1) & 31;
+	printk("<");
+	esp_print_cmd(esp->espcmdlog[i]);
+	printk(">");
+	i = (i - 1) & 31;
+	printk("<");
+	esp_print_cmd(esp->espcmdlog[i]);
+	printk(">");
+	printk("]\n");
+#endif /* (DEBUG_ESP_CMDS) */
+
+	if(SCptr) {
+		ESPLOG(("esp%d: current command ", esp->esp_id));
+		esp_dump_cmd(SCptr);
+	}
+	ESPLOG(("\n"));
+	SCptr = esp->disconnected_SC;
+	ESPLOG(("esp%d: disconnected ", esp->esp_id));
+	while(SCptr) {
+		esp_dump_cmd(SCptr);
+		SCptr = (Scsi_Cmnd *) SCptr->host_scribble;
+	}
+	ESPLOG(("\n"));
+}
+
+/* Abort a command. */
+int esp_abort(Scsi_Cmnd *SCptr)
+{
+	struct NCR_ESP *esp = (struct NCR_ESP *) SCptr->host->hostdata;
+	struct ESP_regs *eregs = esp->eregs;
+	int don;
+	unsigned long flags;
+
+	ESPLOG(("esp%d: Aborting command\n", esp->esp_id));
+	esp_dump_state(esp, eregs);
+
+	/* Wheee, if this is the current command on the bus, the
+	 * best we can do is assert ATN and wait for msgout phase.
+	 * This should even fix a hung SCSI bus when we lose state
+	 * in the driver and timeout because the eventual phase change
+	 * will cause the ESP to (eventually) give an interrupt.
+	 */
+	save_and_cli(flags);
+	if(esp->current_SC == SCptr) {
+		esp->cur_msgout[0] = ABORT;
+		esp->msgout_len = 1;
+		esp->msgout_ctr = 0;
+		esp_cmd(esp, eregs, ESP_CMD_SATN);
+		restore_flags(flags);
+		return SCSI_ABORT_PENDING;
+	}
+	restore_flags(flags);
+
+	/* If it is still in the issue queue then we can safely
+	 * call the completion routine and report abort success.
+	 */
+	don = esp->dma_ports_p(esp);
+	if(don) {
+		esp->dma_ints_off(esp);
+		synchronize_irq();
+	}
+	if(esp->issue_SC) {
+		Scsi_Cmnd **prev, *this;
+		for(prev = (&esp->issue_SC), this = esp->issue_SC;
+		    this;
+		    prev = (Scsi_Cmnd **) &(this->host_scribble),
+		    this = (Scsi_Cmnd *) this->host_scribble) {
+			if(this == SCptr) {
+				*prev = (Scsi_Cmnd *) this->host_scribble;
+				this->host_scribble = NULL;
+				this->result = DID_ABORT << 16;
+				this->done(this);
+				if(don)
+					esp->dma_ints_on(esp);
+				return SCSI_ABORT_SUCCESS;
+			}
+		}
+	}
+
+	/* Yuck, the command to abort is disconnected, it is not
+	 * worth trying to abort it now if something else is live
+	 * on the bus at this time.  So, we let the SCSI code wait
+	 * a little bit and try again later.
+	 */
+	if(esp->current_SC)
+		return SCSI_ABORT_BUSY;
+
+	/* It's disconnected, we have to reconnect to re-establish
+	 * the nexus and tell the device to abort.  However, we really
+	 * cannot 'reconnect' per se, therefore we tell the upper layer
+	 * the safest thing we can.  This is, wait a bit, if nothing
+	 * happens, we are really hung so reset the bus.
+	 */
+
+	return SCSI_ABORT_SNOOZE;
+}
+
+/* Reset ESP chip, reset hanging bus, then kill active and
+ * disconnected commands for targets without soft reset.
+ */
+int esp_reset(Scsi_Cmnd *SCptr, unsigned int how)
+{
+	struct NCR_ESP *esp = (struct NCR_ESP *) SCptr->host->hostdata;
+	struct ESP_regs *eregs = esp->eregs;
+
+	ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id));
+	esp->resetting_bus = 1;
+	esp_cmd(esp, eregs, ESP_CMD_RS);
+	return SCSI_RESET_PENDING;
+}
+
+/* Internal ESP done function. */
+static void esp_done(struct NCR_ESP *esp, int error)
+{
+	Scsi_Cmnd *done_SC;
+
+	if(esp->current_SC) {
+		unsigned long flags;
+
+		done_SC = esp->current_SC;
+		esp->current_SC = NULL;
+
+		/* Free dvma entry. */
+		if(!done_SC->use_sg) {
+#ifdef CONFIG_SCSI_SUNESP
+			/* Sneaky. */
+			mmu_release_scsi_one(done_SC->SCp.have_data_in,
+					     done_SC->request_bufflen,
+					     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+#endif
+		} else {
+#ifdef DEBUG_ESP_SG
+			printk("esp%d: unmapping sg ", esp->esp_id);
+#endif
+#ifdef CONFIG_SCSI_SUNESP
+			mmu_release_scsi_sgl((struct mmu_sglist *) done_SC->buffer,
+					     done_SC->use_sg - 1,
+					     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+#endif
+#ifdef DEBUG_ESP_SG
+			printk("done.\n");
+#endif
+		}
+
+		done_SC->result = error;
+		done_SC->scsi_done(done_SC);
+
+		save_and_cli(flags);
+
+		/* Bus is free, issue any commands in the queue. */
+		if(esp->issue_SC && !esp->current_SC)
+			esp_exec_cmd(esp);
+
+		restore_flags(flags);
+	} else {
+		/* Panic is safe as current_SC is null so we may still
+		 * be able to accept more commands to sync disk buffers.
+		 */
+		ESPLOG(("panicing\n"));
+		panic("esp: done() called with NULL esp->current_SC");
+	}
+}
+
+/* Wheee, ESP interrupt engine. */  
+
+enum {
+	do_phase_determine, do_reset_bus, do_reset_complete,
+	do_work_bus, do_intr_end,
+};
+
+/* Forward declarations. */
+static int esp_do_phase_determine(struct NCR_ESP *esp, 
+				  struct ESP_regs *eregs);
+static int esp_do_data_finale(struct NCR_ESP *esp, struct ESP_regs *eregs);
+static int esp_select_complete(struct NCR_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_status(struct NCR_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_msgin(struct NCR_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_msgindone(struct NCR_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_msgout(struct NCR_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_cmdbegin(struct NCR_ESP *esp, struct ESP_regs *eregs);
+
+static inline int sreg_datainp(unchar sreg)
+{
+	return (sreg & ESP_STAT_PMASK) == ESP_DIP;
+}
+
+static inline int sreg_dataoutp(unchar sreg)
+{
+	return (sreg & ESP_STAT_PMASK) == ESP_DOP;
+}
+
+/* Did they drop these fabs on the floor or what?!?!! */
+static inline void hme_fifo_hwbug_workaround(struct NCR_ESP *esp,
+					     struct ESP_regs *eregs)
+{
+	unchar status = esp->sreg;
+
+	/* Cannot safely frob the fifo for these following cases. */
+	if(sreg_datainp(status) || sreg_dataoutp(status) ||
+	   (esp->current_SC && esp->current_SC->SCp.phase == in_data_done)) {
+		ESPHME(("<wkaround_skipped>"));
+		return;
+	} else {
+		unsigned long count = 0;
+		unsigned long fcnt = eregs->esp_fflags & ESP_FF_FBYTES;
+
+		/* The HME stores bytes in multiples of 2 in the fifo. */
+		ESPHME(("hme_fifo[fcnt=%d", (int)fcnt));
+		while(fcnt) {
+			esp->hme_fifo_workaround_buffer[count++] = eregs->esp_fdata;
+			esp->hme_fifo_workaround_buffer[count++] = eregs->esp_fdata;
+			ESPHME(("<%02x,%02x>", esp->hme_fifo_workaround_buffer[count-2], esp->hme_fifo_workaround_buffer[count-1]));
+			fcnt--;
+		}
+		if(eregs->esp_status2 & ESP_STAT2_F1BYTE) {
+			ESPHME(("<poke_byte>"));
+			eregs->esp_fdata = 0;
+			esp->hme_fifo_workaround_buffer[count++] = eregs->esp_fdata;
+			ESPHME(("<%02x,0x00>", esp->hme_fifo_workaround_buffer[count-1]));
+			ESPHME(("CMD_FLUSH"));
+			esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+		} else {
+			ESPHME(("no_xtra_byte"));
+		}
+		esp->hme_fifo_workaround_count = count;
+		ESPHME(("wkarnd_cnt=%d]", (int)count));
+	}
+}
+
+static inline void hme_fifo_push(struct NCR_ESP *esp, struct ESP_regs *eregs,
+				 unchar *bytes, unchar count)
+{
+	esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+	while(count) {
+		eregs->esp_fdata = *bytes++;
+		eregs->esp_fdata = 0;
+		count--;
+	}
+}
+
+/* We try to avoid some interrupts by jumping ahead and see if the ESP
+ * has gotten far enough yet.  Hence the following.
+ */
+static inline int skipahead1(struct NCR_ESP *esp, struct ESP_regs *eregs,
+			     Scsi_Cmnd *scp, int prev_phase, int new_phase)
+{
+	if(scp->SCp.sent_command != prev_phase)
+		return 0;
+
+	if(esp->dma_irq_p(esp)) {
+		/* Yes, we are able to save an interrupt. */
+		esp->sreg = eregs->esp_status;
+		if(esp->erev == fashme) {
+			/* This chip is really losing. */
+			ESPHME(("HME["));
+			/* Must latch fifo before reading the interrupt
+			 * register else garbage ends up in the FIFO
+			 * which confuses the driver utterly.
+			 * Happy Meal indeed....
+			 */
+			ESPHME(("fifo_workaround]"));
+			hme_fifo_hwbug_workaround(esp, eregs);
+		}
+		esp->ireg = eregs->esp_intrpt;
+		esp->sreg &= ~(ESP_STAT_INTR);
+		if(!(esp->ireg & ESP_INTR_SR))
+			return 0;
+		else
+			return do_reset_complete;
+	}
+	/* Ho hum, target is taking forever... */
+	scp->SCp.sent_command = new_phase; /* so we don't recurse... */
+	return do_intr_end;
+}
+
+static inline int skipahead2(struct NCR_ESP *esp,
+			     struct ESP_regs *eregs,
+			     Scsi_Cmnd *scp, int prev_phase1, int prev_phase2,
+			     int new_phase)
+{
+	if(scp->SCp.sent_command != prev_phase1 &&
+	   scp->SCp.sent_command != prev_phase2)
+		return 0;
+	if(esp->dma_irq_p(esp)) {
+		/* Yes, we are able to save an interrupt. */
+		esp->sreg = eregs->esp_status;
+		if(esp->erev == fashme) {
+			/* This chip is really losing. */
+			ESPHME(("HME["));
+
+			/* Must latch fifo before reading the interrupt
+			 * register else garbage ends up in the FIFO
+			 * which confuses the driver utterly.
+			 * Happy Meal indeed....
+			 */
+			ESPHME(("fifo_workaround]"));
+			hme_fifo_hwbug_workaround(esp, eregs);
+		}
+		esp->ireg = eregs->esp_intrpt;
+		esp->sreg &= ~(ESP_STAT_INTR);
+		if(!(esp->ireg & ESP_INTR_SR))
+			return 0;
+		else
+			return do_reset_complete;
+	}
+	/* Ho hum, target is taking forever... */
+	scp->SCp.sent_command = new_phase; /* so we don't recurse... */
+	return do_intr_end;
+}
+
+/* Misc. esp helper routines. */
+static inline void esp_setcount(struct ESP_regs *eregs, int cnt, int hme)
+{
+	eregs->esp_tclow = (cnt & 0xff);
+	eregs->esp_tcmed = ((cnt >> 8) & 0xff);
+	if(hme) {
+		eregs->fas_rlo = 0;
+		eregs->fas_rhi = 0;
+	}
+}
+
+static inline int esp_getcount(struct ESP_regs *eregs)
+{
+	return (((eregs->esp_tclow)&0xff) |
+		(((eregs->esp_tcmed)&0xff) << 8));
+}
+
+static inline int fcount(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	if(esp->erev == fashme)
+		return esp->hme_fifo_workaround_count;
+	else
+		return eregs->esp_fflags & ESP_FF_FBYTES;
+}
+
+static inline int fnzero(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	if(esp->erev == fashme)
+		return 0;
+	else
+		return eregs->esp_fflags & ESP_FF_ONOTZERO;
+}
+
+/* XXX speculative nops unnecessary when continuing amidst a data phase
+ * XXX even on esp100!!!  another case of flooding the bus with I/O reg
+ * XXX writes...
+ */
+static inline void esp_maybe_nop(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	if(esp->erev == esp100)
+		esp_cmd(esp, eregs, ESP_CMD_NULL);
+}
+
+static inline int sreg_to_dataphase(unchar sreg)
+{
+	if((sreg & ESP_STAT_PMASK) == ESP_DOP)
+		return in_dataout;
+	else
+		return in_datain;
+}
+
+/* The ESP100 when in synchronous data phase, can mistake a long final
+ * REQ pulse from the target as an extra byte, it places whatever is on
+ * the data lines into the fifo.  For now, we will assume when this
+ * happens that the target is a bit quirky and we don't want to
+ * be talking synchronously to it anyways.  Regardless, we need to
+ * tell the ESP to eat the extraneous byte so that we can proceed
+ * to the next phase.
+ */
+static inline int esp100_sync_hwbug(struct NCR_ESP *esp, struct ESP_regs *eregs,
+				    Scsi_Cmnd *sp, int fifocnt)
+{
+	/* Do not touch this piece of code. */
+	if((!(esp->erev == esp100)) ||
+	   (!(sreg_datainp((esp->sreg = eregs->esp_status)) && !fifocnt) &&
+	    !(sreg_dataoutp(esp->sreg) && !fnzero(esp, eregs)))) {
+		if(sp->SCp.phase == in_dataout)
+			esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+		return 0;
+	} else {
+		/* Async mode for this guy. */
+		build_sync_nego_msg(esp, 0, 0);
+
+		/* Ack the bogus byte, but set ATN first. */
+		esp_cmd(esp, eregs, ESP_CMD_SATN);
+		esp_cmd(esp, eregs, ESP_CMD_MOK);
+		return 1;
+	}
+}
+
+/* This closes the window during a selection with a reselect pending, because
+ * we use DMA for the selection process the FIFO should hold the correct
+ * contents if we get reselected during this process.  So we just need to
+ * ack the possible illegal cmd interrupt pending on the esp100.
+ */
+static inline int esp100_reconnect_hwbug(struct NCR_ESP *esp,
+					 struct ESP_regs *eregs)
+{
+	volatile unchar junk;
+
+	if(esp->erev != esp100)
+		return 0;
+	junk = eregs->esp_intrpt;
+
+	if(junk & ESP_INTR_SR)
+		return 1;
+	return 0;
+}
+
+/* This verifies the BUSID bits during a reselection so that we know which
+ * target is talking to us.
+ */
+static inline int reconnect_target(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	int it, me = esp->scsi_id_mask, targ = 0;
+
+	if(2 != fcount(esp, eregs))
+		return -1;
+	if(esp->erev == fashme) {
+		/* HME does not latch it's own BUS ID bits during
+		 * a reselection.  Also the target number is given
+		 * as an unsigned char, not as a sole bit number
+		 * like the other ESP's do.
+		 * Happy Meal indeed....
+		 */
+		targ = esp->hme_fifo_workaround_buffer[0];
+	} else {
+		it = eregs->esp_fdata;
+		if(!(it & me))
+			return -1;
+		it &= ~me;
+		if(it & (it - 1))
+			return -1;
+		while(!(it & 1))
+			targ++, it >>= 1;
+	}
+	return targ;
+}
+
+/* This verifies the identify from the target so that we know which lun is
+ * being reconnected.
+ */
+static inline int reconnect_lun(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	int lun;
+
+	if((esp->sreg & ESP_STAT_PMASK) != ESP_MIP)
+		return -1;
+	if(esp->erev == fashme)
+		lun = esp->hme_fifo_workaround_buffer[1];
+	else
+		lun = eregs->esp_fdata;
+	if(esp->sreg & ESP_STAT_PERR)
+		return 0;
+	if((lun & 0x40) || !(lun & 0x80))
+		return -1;
+	return lun & 7;
+}
+
+/* This puts the driver in a state where it can revitalize a command that
+ * is being continued due to reselection.
+ */
+static inline void esp_connect(struct NCR_ESP *esp, struct ESP_regs *eregs,
+			       Scsi_Cmnd *sp)
+{
+	Scsi_Device *dp = sp->device;
+	eregs->esp_soff = dp->sync_max_offset;
+	eregs->esp_stp  = dp->sync_min_period;
+	if(esp->erev > esp100a)
+		eregs->esp_cfg3 = esp->config3[sp->target];
+	if(esp->erev == fashme)
+		eregs->esp_busid = (sp->target & 0xf) |
+			(ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT);
+	esp->current_SC = sp;
+}
+
+/* This will place the current working command back into the issue queue
+ * if we are to receive a reselection amidst a selection attempt.
+ */
+static inline void esp_reconnect(struct NCR_ESP *esp, Scsi_Cmnd *sp)
+{
+	if(!esp->disconnected_SC)
+		printk("esp%d: Weird, being reselected but disconnected "
+		       "command queue is empty.\n", esp->esp_id);
+	esp->snip = 0;
+	esp->current_SC = 0;
+	sp->SCp.phase = not_issued;
+	append_SC(&esp->issue_SC, sp);
+}
+
+/* Begin message in phase. */
+static inline int esp_do_msgin(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	/* Must be very careful with the fifo on the HME */
+	if((esp->erev != fashme) || !(eregs->esp_status2 & ESP_STAT2_FEMPTY))
+		esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+	esp_maybe_nop(esp, eregs);
+	esp_cmd(esp, eregs, ESP_CMD_TI);
+	esp->msgin_len = 1;
+	esp->msgin_ctr = 0;
+	esp_advance_phase(esp->current_SC, in_msgindone);
+	return do_work_bus;
+}
+
+static inline void advance_sg(Scsi_Cmnd *sp)
+{
+	++sp->SCp.buffer;
+	--sp->SCp.buffers_residual;
+	sp->SCp.this_residual = sp->SCp.buffer->length;
+#ifdef CONFIG_SCSI_SUNESP
+	sp->SCp.ptr = (char *)((unsigned long)sp->SCp.buffer->dvma_address);
+#else
+	sp->SCp.ptr = (char *)VTOP((unsigned long) sp->SCp.buffer->address);
+#endif
+}
+
+/* Please note that the way I've coded these routines is that I _always_
+ * check for a disconnect during any and all information transfer
+ * phases.  The SCSI standard states that the target _can_ cause a BUS
+ * FREE condition by dropping all MSG/CD/IO/BSY signals.  Also note
+ * that during information transfer phases the target controls every
+ * change in phase, the only thing the initiator can do is "ask" for
+ * a message out phase by driving ATN true.  The target can, and sometimes
+ * will, completely ignore this request so we cannot assume anything when
+ * we try to force a message out phase to abort/reset a target.  Most of
+ * the time the target will eventually be nice and go to message out, so
+ * we may have to hold on to our state about what we want to tell the target
+ * for some period of time.
+ */
+
+/* I think I have things working here correctly.  Even partial transfers
+ * within a buffer or sub-buffer should not upset us at all no matter
+ * how bad the target and/or ESP fucks things up.
+ */
+
+static inline int esp_do_data(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int thisphase, hmuch;
+
+	ESPDATA(("esp_do_data: "));
+	esp_maybe_nop(esp, eregs);
+	thisphase = sreg_to_dataphase(esp->sreg);
+	esp_advance_phase(SCptr, thisphase);
+	ESPDATA(("newphase<%s> ", (thisphase == in_datain) ? "DATAIN" : "DATAOUT"));
+	hmuch = esp->dma_can_transfer(esp, SCptr);
+	ESPDATA(("hmuch<%d> ", hmuch));
+	esp->current_transfer_size = hmuch;
+	if(esp->erev == fashme) {
+		/* Touchy chip, this stupid HME scsi adapter... */
+		esp_setcount(eregs, hmuch, 1);
+		esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+
+		if(thisphase == in_datain)
+			esp->dma_init_read(esp, (__u32)((unsigned long)SCptr->SCp.ptr), hmuch);
+		else
+			esp->dma_init_write(esp, (__u32)((unsigned long)SCptr->SCp.ptr), hmuch);
+	} else {
+		esp_setcount(eregs, hmuch, 0);
+		esp->dma_setup(esp, 
+			       (__u32)((unsigned long)SCptr->SCp.ptr), 
+			       hmuch, (thisphase == in_datain));
+		ESPDATA(("DMA|TI --> do_intr_end\n"));
+		esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+	}
+	return do_intr_end;
+}
+
+/* See how successful the data transfer was. */
+static inline int esp_do_data_finale(struct NCR_ESP *esp,
+				     struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int bogus_data = 0, bytes_sent = 0, fifocnt, ecount = 0;
+
+	if(esp->dma_led_off)
+		esp->dma_led_off(esp);
+
+	ESPDATA(("esp_do_data_finale: "));
+
+	if(SCptr->SCp.phase == in_datain) {
+		if(esp->sreg & ESP_STAT_PERR) {
+			/* Yuck, parity error.  The ESP asserts ATN
+			 * so that we can go to message out phase
+			 * immediately and inform the target that
+			 * something bad happened.
+			 */
+			ESPLOG(("esp%d: data bad parity detected.\n",
+				esp->esp_id));
+			esp->cur_msgout[0] = INITIATOR_ERROR;
+			esp->msgout_len = 1;
+		}
+		if(esp->dma_drain)
+			esp->dma_drain(esp);
+	}
+	if(esp->dma_invalidate)
+		esp->dma_invalidate(esp);
+
+	/* This could happen for the above parity error case. */
+	if(!(esp->ireg == ESP_INTR_BSERV)) {
+		/* Please go to msgout phase, please please please... */
+		ESPLOG(("esp%d: !BSERV after data, probably to msgout\n",
+			esp->esp_id));
+		return esp_do_phase_determine(esp, eregs);
+	}	
+
+	/* Check for partial transfers and other horrible events.
+	 * Note, here we read the real fifo flags register even
+	 * on HME broken adapters because we skip the HME fifo
+	 * workaround code in esp_handle() if we are doing data
+	 * phase things.  We don't want to fuck directly with
+	 * the fifo like that, especially if doing syncronous
+	 * transfers!  Also, will need to double the count on
+	 * HME if we are doing wide transfers, as the HME fifo
+	 * will move and count 16-bit quantities during wide data.
+	 * SMCC _and_ Qlogic can both bite me.
+	 */
+	fifocnt = eregs->esp_fflags & ESP_FF_FBYTES;
+	if(esp->erev != fashme)
+		ecount = esp_getcount(eregs);
+	bytes_sent = esp->current_transfer_size;
+
+	/* Uhhh, might not want both of these conditionals to run
+	 * at once on HME due to the fifo problems it has.  Consider
+	 * changing it to:
+	 *
+	 * 	if(!(esp->sreg & ESP_STAT_TCNT)) {
+	 * 		bytes_sent -= ecount;
+	 * 	} else if(SCptr->SCp.phase == in_dataout) {
+	 * 		bytes_sent -= fifocnt;
+	 *	}
+	 *
+	 * But only for the HME case, leave the current code alone
+	 * for all other ESP revisions as we know the existing code
+	 * works just fine for them.
+	 */
+	ESPDATA(("trans_sz=%d, ", bytes_sent));
+	if(esp->erev == fashme) {
+		if(!(esp->sreg & ESP_STAT_TCNT)) {
+			bytes_sent -= esp_getcount(eregs);
+		} else if(SCptr->SCp.phase == in_dataout) {
+			bytes_sent -= fifocnt;
+		}
+	} else {
+		if(!(esp->sreg & ESP_STAT_TCNT))
+			bytes_sent -= ecount;
+		if(SCptr->SCp.phase == in_dataout)
+			bytes_sent -= fifocnt;
+	}
+
+	ESPDATA(("bytes_sent=%d, ", bytes_sent));
+
+	/* If we were in synchronous mode, check for peculiarities. */
+	if(esp->erev == fashme) {
+		if(SCptr->device->sync_max_offset) {
+			if(SCptr->SCp.phase == in_dataout)
+				esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+		} else {
+			esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+		}
+	} else {
+		if(SCptr->device->sync_max_offset)
+			bogus_data = esp100_sync_hwbug(esp, eregs, SCptr, fifocnt);
+		else
+			esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+	}
+
+	/* Until we are sure of what has happened, we are certainly
+	 * in the dark.
+	 */
+	esp_advance_phase(SCptr, in_the_dark);
+
+	if(bytes_sent < 0) {
+		/* I've seen this happen due to lost state in this
+		 * driver.  No idea why it happened, but allowing
+		 * this value to be negative caused things to
+		 * lock up.  This allows greater chance of recovery.
+		 */
+		ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id));
+		ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n",
+			esp->esp_id,
+			esp->current_transfer_size, fifocnt, ecount));
+		ESPLOG(("esp%d: use_sg=%d ptr=%p this_residual=%d\n",
+			esp->esp_id,
+			SCptr->use_sg, SCptr->SCp.ptr, SCptr->SCp.this_residual));
+		bytes_sent = 0;
+	}
+
+	/* Update the state of our transfer. */
+	SCptr->SCp.ptr += bytes_sent;
+	SCptr->SCp.this_residual -= bytes_sent;
+	if(SCptr->SCp.this_residual < 0) {
+		/* shit */
+		printk("esp%d: Data transfer overrun.\n", esp->esp_id);
+		SCptr->SCp.this_residual = 0;
+	}
+
+	/* Maybe continue. */
+	if(!bogus_data) {
+		ESPDATA(("!bogus_data, "));
+		/* NO MATTER WHAT, we advance the scatterlist,
+		 * if the target should decide to disconnect
+		 * in between scatter chunks (which is common)
+		 * we could die horribly!  I used to have the sg
+		 * advance occur only if we are going back into
+		 * (or are staying in) a data phase, you can
+		 * imagine the hell I went through trying to
+		 * figure this out.
+		 */
+		if(SCptr->use_sg && !SCptr->SCp.this_residual)
+			advance_sg(SCptr);
+		if(sreg_datainp(esp->sreg) || sreg_dataoutp(esp->sreg)) {
+			ESPDATA(("to more data\n"));
+			return esp_do_data(esp, eregs);
+		}
+		ESPDATA(("to new phase\n"));
+		return esp_do_phase_determine(esp, eregs);
+	}
+	/* Bogus data, just wait for next interrupt. */
+	ESPLOG(("esp%d: bogus_data during end of data phase\n",
+		esp->esp_id));
+	return do_intr_end;
+}
+
+/* Either a command is completing or a target is dropping off the bus
+ * to continue the command in the background so we can do other work.
+ */
+static inline int esp_do_freebus(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int rval;
+
+	rval = skipahead2(esp, eregs, SCptr, in_status, in_msgindone, in_freeing);
+	if(rval)
+		return rval;
+
+	if(esp->ireg != ESP_INTR_DC) {
+		ESPLOG(("esp%d: Target will not disconnect\n", esp->esp_id));
+		return do_reset_bus; /* target will not drop BSY... */
+	}
+	esp->msgout_len = 0;
+	esp->prevmsgout = NOP;
+	if(esp->prevmsgin == COMMAND_COMPLETE) {
+		/* Normal end of nexus. */
+		if(esp->disconnected_SC || (esp->erev == fashme))
+			esp_cmd(esp, eregs, ESP_CMD_ESEL);
+
+		if(SCptr->SCp.Status != GOOD && SCptr->SCp.Status != CONDITION_GOOD &&
+		   ((1<<SCptr->target) & esp->targets_present) &&
+		   SCptr->device->sync && SCptr->device->sync_max_offset) {
+			/* SCSI standard says that the synchronous capabilities
+			 * should be renegotiated at this point.  Most likely
+			 * we are about to request sense from this target
+			 * in which case we want to avoid using sync
+			 * transfers until we are sure of the current target
+			 * state.
+			 */
+			ESPMISC(("esp: Status <%d> for target %d lun %d\n",
+				 SCptr->SCp.Status, SCptr->target, SCptr->lun));
+
+			/* But don't do this when spinning up a disk at
+			 * boot time while we poll for completion as it
+			 * fills up the console with messages.  Also, tapes
+			 * can report not ready many times right after
+			 * loading up a tape.
+			 */
+			if(SCptr->cmnd[0] != START_STOP &&
+			   SCptr->data_cmnd[0] != START_STOP &&
+			   SCptr->cmnd[0] != TEST_UNIT_READY &&
+			   SCptr->data_cmnd[0] != TEST_UNIT_READY &&
+			   !(SCptr->device->type == TYPE_TAPE &&
+			     (SCptr->cmnd[0] == TEST_UNIT_READY ||
+			      SCptr->data_cmnd[0] == TEST_UNIT_READY ||
+			      SCptr->cmnd[0] == MODE_SENSE ||
+			      SCptr->data_cmnd[0] == MODE_SENSE)))
+				SCptr->device->sync = 0;
+		}
+		ESPDISC(("F<%02x,%02x>", SCptr->target, SCptr->lun));
+		esp_done(esp, ((SCptr->SCp.Status & 0xff) |
+			       ((SCptr->SCp.Message & 0xff)<<8) |
+			       (DID_OK << 16)));
+	} else if(esp->prevmsgin == DISCONNECT) {
+		/* Normal disconnect. */
+		esp_cmd(esp, eregs, ESP_CMD_ESEL);
+		ESPDISC(("D<%02x,%02x>", SCptr->target, SCptr->lun));
+		append_SC(&esp->disconnected_SC, SCptr);
+		esp->current_SC = NULL;
+		if(esp->issue_SC)
+			esp_exec_cmd(esp);
+	} else {
+		/* Driver bug, we do not expect a disconnect here
+		 * and should not have advanced the state engine
+		 * to in_freeing.
+		 */
+		ESPLOG(("esp%d: last msg not disc and not cmd cmplt.\n",
+			esp->esp_id));
+		return do_reset_bus;
+	}
+	return do_intr_end;
+}
+
+/* Do the needy when a target tries to reconnect to us. */
+static inline int esp_do_reconnect(struct NCR_ESP *esp, 
+				   struct ESP_regs *eregs)
+{
+	int lun, target;
+	Scsi_Cmnd *SCptr;
+
+	/* Check for all bogus conditions first. */
+	target = reconnect_target(esp, eregs);
+	if(target < 0) {
+		ESPDISC(("bad bus bits\n"));
+		return do_reset_bus;
+	}
+	lun = reconnect_lun(esp, eregs);
+	if(lun < 0) {
+		ESPDISC(("target=%2x, bad identify msg\n", target));
+		return do_reset_bus;
+	}
+
+	/* Things look ok... */
+	ESPDISC(("R<%02x,%02x>", target, lun));
+
+	/* Must flush both FIFO and the DVMA on HME. */
+	if(esp->erev == fashme) {
+		/* XXX this still doesn't fix the problem... */
+		esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+		if(esp->dma_invalidate)
+			esp->dma_invalidate(esp);
+	} else {
+		esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+		if(esp100_reconnect_hwbug(esp, eregs))
+			return do_reset_bus;
+		esp_cmd(esp, eregs, ESP_CMD_NULL);
+	}
+
+	SCptr = remove_SC(&esp->disconnected_SC, (unchar) target, (unchar) lun);
+	if(!SCptr) {
+		Scsi_Cmnd *sp;
+
+		ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n",
+			esp->esp_id));
+		ESPLOG(("QUEUE DUMP\n"));
+		sp = esp->issue_SC;
+		ESPLOG(("esp%d: issue_SC[", esp->esp_id));
+		while(sp) {
+			ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
+			sp = (Scsi_Cmnd *) sp->host_scribble;
+		}
+		ESPLOG(("]\n"));
+		sp = esp->current_SC;
+		ESPLOG(("esp%d: current_SC[", esp->esp_id));
+		while(sp) {
+			ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
+			sp = (Scsi_Cmnd *) sp->host_scribble;
+		}
+		ESPLOG(("]\n"));
+		sp = esp->disconnected_SC;
+		ESPLOG(("esp%d: disconnected_SC[", esp->esp_id));
+		while(sp) {
+			ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
+			sp = (Scsi_Cmnd *) sp->host_scribble;
+		}
+		ESPLOG(("]\n"));
+		return do_reset_bus;
+	}
+	esp_connect(esp, eregs, SCptr);
+	esp_cmd(esp, eregs, ESP_CMD_MOK);
+
+	/* No need for explicit restore pointers operation. */
+	esp->snip = 0;
+	esp_advance_phase(SCptr, in_the_dark);
+	return do_intr_end;
+}
+
+/* End of NEXUS (hopefully), pick up status + message byte then leave if
+ * all goes well.
+ */
+static int esp_do_status(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int intr, rval;
+
+	rval = skipahead1(esp, eregs, SCptr, in_the_dark, in_status);
+	if(rval)
+		return rval;
+
+	intr = esp->ireg;
+	ESPSTAT(("esp_do_status: "));
+	if(intr != ESP_INTR_DC) {
+		int message_out = 0; /* for parity problems */
+
+		/* Ack the message. */
+		ESPSTAT(("ack msg, "));
+		esp_cmd(esp, eregs, ESP_CMD_MOK);
+
+		if(esp->dma_poll)
+			esp->dma_poll(esp, (unsigned char *) esp->esp_command);
+
+		ESPSTAT(("got something, "));
+		/* ESP chimes in with one of
+		 *
+		 * 1) function done interrupt:
+		 *	both status and message in bytes
+		 *	are available
+		 *
+		 * 2) bus service interrupt:
+		 *	only status byte was acquired
+		 *
+		 * 3) Anything else:
+		 *	can't happen, but we test for it
+		 *	anyways
+		 *
+		 * ALSO: If bad parity was detected on either
+		 *       the status _or_ the message byte then
+		 *       the ESP has asserted ATN on the bus
+		 *       and we must therefore wait for the
+		 *       next phase change.
+		 */
+		if(intr & ESP_INTR_FDONE) {
+			/* We got it all, hallejulia. */
+			ESPSTAT(("got both, "));
+			SCptr->SCp.Status = esp->esp_command[0];
+			SCptr->SCp.Message = esp->esp_command[1];
+			esp->prevmsgin = SCptr->SCp.Message;
+			esp->cur_msgin[0] = SCptr->SCp.Message;
+			if(esp->sreg & ESP_STAT_PERR) {
+				/* There was bad parity for the
+				 * message byte, the status byte
+				 * was ok.
+				 */
+				message_out = MSG_PARITY_ERROR;
+			}
+		} else if(intr == ESP_INTR_BSERV) {
+			/* Only got status byte. */
+			ESPLOG(("esp%d: got status only, ", esp->esp_id));
+			if(!(esp->sreg & ESP_STAT_PERR)) {
+				SCptr->SCp.Status = esp->esp_command[0];
+				SCptr->SCp.Message = 0xff;
+			} else {
+				/* The status byte had bad parity.
+				 * we leave the scsi_pointer Status
+				 * field alone as we set it to a default
+				 * of CHECK_CONDITION in esp_queue.
+				 */
+				message_out = INITIATOR_ERROR;
+			}
+		} else {
+			/* This shouldn't happen ever. */
+			ESPSTAT(("got bolixed\n"));
+			esp_advance_phase(SCptr, in_the_dark);
+			return esp_do_phase_determine(esp, eregs);
+		}
+
+		if(!message_out) {
+			ESPSTAT(("status=%2x msg=%2x, ", SCptr->SCp.Status,
+				SCptr->SCp.Message));
+			if(SCptr->SCp.Message == COMMAND_COMPLETE) {
+				ESPSTAT(("and was COMMAND_COMPLETE\n"));
+				esp_advance_phase(SCptr, in_freeing);
+				return esp_do_freebus(esp, eregs);
+			} else {
+				ESPLOG(("esp%d: and _not_ COMMAND_COMPLETE\n",
+					esp->esp_id));
+				esp->msgin_len = esp->msgin_ctr = 1;
+				esp_advance_phase(SCptr, in_msgindone);
+				return esp_do_msgindone(esp, eregs);
+			}
+		} else {
+			/* With luck we'll be able to let the target
+			 * know that bad parity happened, it will know
+			 * which byte caused the problems and send it
+			 * again.  For the case where the status byte
+			 * receives bad parity, I do not believe most
+			 * targets recover very well.  We'll see.
+			 */
+			ESPLOG(("esp%d: bad parity somewhere mout=%2x\n",
+				esp->esp_id, message_out));
+			esp->cur_msgout[0] = message_out;
+			esp->msgout_len = esp->msgout_ctr = 1;
+			esp_advance_phase(SCptr, in_the_dark);
+			return esp_do_phase_determine(esp, eregs);
+		}
+	} else {
+		/* If we disconnect now, all hell breaks loose. */
+		ESPLOG(("esp%d: whoops, disconnect\n", esp->esp_id));
+		esp_advance_phase(SCptr, in_the_dark);
+		return esp_do_phase_determine(esp, eregs);
+	}
+}
+
+/* The target has control of the bus and we have to see where it has
+ * taken us.
+ */
+static int esp_do_phase_determine(struct NCR_ESP *esp,
+				  struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+
+	ESPPHASE(("esp_do_phase_determine: "));
+	if(!(esp->ireg & ESP_INTR_DC)) {
+		switch(esp->sreg & ESP_STAT_PMASK) {
+		case ESP_DOP:
+		case ESP_DIP:
+			ESPPHASE(("to data phase\n"));
+			return esp_do_data(esp, eregs);
+
+		case ESP_STATP:
+			/* Whee, status phase, finish up the command. */
+			ESPPHASE(("to status phase\n"));
+
+			esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+
+			if(esp->do_pio_cmds){
+				esp_advance_phase(SCptr, in_status);
+				esp_cmd(esp, eregs, ESP_CMD_ICCSEQ);
+				while(!(esp->eregs->esp_status & ESP_STAT_INTR));
+				esp->esp_command[0] = eregs->esp_fdata;
+				while(!(esp->eregs->esp_status & ESP_STAT_INTR));
+				esp->esp_command[1] = eregs->esp_fdata;
+			} else {				
+				if(esp->erev != fashme) {
+					esp->esp_command[0] = 0xff;
+					esp->esp_command[1] = 0xff;
+					eregs->esp_tclow = 2;
+					eregs->esp_tcmed = 0;
+					esp->dma_init_read(esp, esp->esp_command_dvma, 2);
+					esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_ICCSEQ);
+				} else {
+					/* Using DVMA for status/message bytes is
+					 * unreliable on HME, nice job QLogic.
+					 * Happy Meal indeed....
+					 */
+					esp_cmd(esp, eregs, ESP_CMD_ICCSEQ);
+				}
+				esp_advance_phase(SCptr, in_status);
+			}
+			return esp_do_status(esp, eregs);
+
+		case ESP_MOP:
+			ESPPHASE(("to msgout phase\n"));
+			esp_advance_phase(SCptr, in_msgout);
+			return esp_do_msgout(esp, eregs);
+
+		case ESP_MIP:
+			ESPPHASE(("to msgin phase\n"));
+			esp_advance_phase(SCptr, in_msgin);
+			return esp_do_msgin(esp, eregs);
+
+		case ESP_CMDP:
+			/* Ugh, we're running a non-standard command the
+			 * ESP doesn't understand, one byte at a time.
+			 */
+			ESPPHASE(("to cmd phase\n"));
+			esp_advance_phase(SCptr, in_cmdbegin);
+			return esp_do_cmdbegin(esp, eregs);
+		};
+	} else {
+		Scsi_Device *dp = SCptr->device;
+
+		/* This means real problems if we see this
+		 * here.  Unless we were actually trying
+		 * to force the device to abort/reset.
+		 */
+		ESPLOG(("esp%d Disconnect amidst phases, ", esp->esp_id));
+		ESPLOG(("pphase<%s> cphase<%s>, ",
+			phase_string(SCptr->SCp.phase),
+			phase_string(SCptr->SCp.sent_command)));
+		if(esp->disconnected_SC || (esp->erev == fashme))
+			esp_cmd(esp, eregs, ESP_CMD_ESEL);
+
+		switch(esp->cur_msgout[0]) {
+		default:
+			/* We didn't expect this to happen at all. */
+			ESPLOG(("device is bolixed\n"));
+			esp_advance_phase(SCptr, in_tgterror);
+			esp_done(esp, (DID_ERROR << 16));
+			break;
+
+		case BUS_DEVICE_RESET:
+			ESPLOG(("device reset successful\n"));
+			dp->sync_max_offset = 0;
+			dp->sync_min_period = 0;
+			dp->sync = 0;
+			esp_advance_phase(SCptr, in_resetdev);
+			esp_done(esp, (DID_RESET << 16));
+			break;
+
+		case ABORT:
+			ESPLOG(("device abort successful\n"));
+			esp_advance_phase(SCptr, in_abortone);
+			esp_done(esp, (DID_ABORT << 16));
+			break;
+
+		};
+		return do_intr_end;
+	}
+
+	ESPLOG(("esp%d: to unknown phase\n", esp->esp_id));
+	printk("esp%d: Bizarre bus phase %2x.\n", esp->esp_id,
+	       esp->sreg & ESP_STAT_PMASK);
+	return do_reset_bus;
+}
+
+/* First interrupt after exec'ing a cmd comes here. */
+static int esp_select_complete(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	Scsi_Device *SDptr = SCptr->device;
+	int cmd_bytes_sent, fcnt;
+
+	if(esp->erev != fashme)
+		esp->seqreg = (eregs->esp_sstep & ESP_STEP_VBITS);
+	if(esp->erev == fashme)
+		fcnt = esp->hme_fifo_workaround_count;
+	else
+		fcnt = (eregs->esp_fflags & ESP_FF_FBYTES);
+	cmd_bytes_sent = esp->dma_bytes_sent(esp, fcnt);
+	if(esp->dma_invalidate)
+		esp->dma_invalidate(esp);
+
+	/* Let's check to see if a reselect happened
+	 * while we we're trying to select.  This must
+	 * be checked first.
+	 */
+	if(esp->ireg == (ESP_INTR_RSEL | ESP_INTR_FDONE)) {
+		esp_reconnect(esp, SCptr);
+		return esp_do_reconnect(esp, eregs);
+	}
+
+	/* Looks like things worked, we should see a bus service &
+	 * a function complete interrupt at this point.  Note we
+	 * are doing a direct comparison because we don't want to
+	 * be fooled into thinking selection was successful if
+	 * ESP_INTR_DC is set, see below.
+	 */
+	if(esp->ireg == (ESP_INTR_FDONE | ESP_INTR_BSERV)) {
+		/* target speaks... */
+		esp->targets_present |= (1<<SCptr->target);
+
+		/* What if the target ignores the sdtr? */
+		if(esp->snip)
+			SDptr->sync = 1;
+
+		/* See how far, if at all, we got in getting
+		 * the information out to the target.
+		 */
+		switch(esp->seqreg) {
+		default:
+
+		case ESP_STEP_ASEL:
+			/* Arbitration won, target selected, but
+			 * we are in some phase which is not command
+			 * phase nor is it message out phase.
+			 *
+			 * XXX We've confused the target, obviously.
+			 * XXX So clear it's state, but we also end
+			 * XXX up clearing everyone elses.  That isn't
+			 * XXX so nice.  I'd like to just reset this
+			 * XXX target, but if I cannot even get it's
+			 * XXX attention and finish selection to talk
+			 * XXX to it, there is not much more I can do.
+			 * XXX If we have a loaded bus we're going to
+			 * XXX spend the next second or so renegotiating
+			 * XXX for synchronous transfers.
+			 */
+			ESPLOG(("esp%d: STEP_ASEL for tgt %d\n",
+				esp->esp_id, SCptr->target));
+
+		case ESP_STEP_SID:
+			/* Arbitration won, target selected, went
+			 * to message out phase, sent one message
+			 * byte, then we stopped.  ATN is asserted
+			 * on the SCSI bus and the target is still
+			 * there hanging on.  This is a legal
+			 * sequence step if we gave the ESP a select
+			 * and stop command.
+			 *
+			 * XXX See above, I could set the borken flag
+			 * XXX in the device struct and retry the
+			 * XXX command.  But would that help for
+			 * XXX tagged capable targets?
+			 */
+
+		case ESP_STEP_NCMD:
+			/* Arbitration won, target selected, maybe
+			 * sent the one message byte in message out
+			 * phase, but we did not go to command phase
+			 * in the end.  Actually, we could have sent
+			 * only some of the message bytes if we tried
+			 * to send out the entire identify and tag
+			 * message using ESP_CMD_SA3.
+			 */
+			cmd_bytes_sent = 0;
+			break;
+
+		case ESP_STEP_PPC:
+			/* No, not the powerPC pinhead.  Arbitration
+			 * won, all message bytes sent if we went to
+			 * message out phase, went to command phase
+			 * but only part of the command was sent.
+			 *
+			 * XXX I've seen this, but usually in conjunction
+			 * XXX with a gross error which appears to have
+			 * XXX occurred between the time I told the
+			 * XXX ESP to arbitrate and when I got the
+			 * XXX interrupt.  Could I have misloaded the
+			 * XXX command bytes into the fifo?  Actually,
+			 * XXX I most likely missed a phase, and therefore
+			 * XXX went into never never land and didn't even
+			 * XXX know it.  That was the old driver though.
+			 * XXX What is even more peculiar is that the ESP
+			 * XXX showed the proper function complete and
+			 * XXX bus service bits in the interrupt register.
+			 */
+
+		case ESP_STEP_FINI4:
+		case ESP_STEP_FINI5:
+		case ESP_STEP_FINI6:
+		case ESP_STEP_FINI7:
+			/* Account for the identify message */
+			if(SCptr->SCp.phase == in_slct_norm)
+				cmd_bytes_sent -= 1;
+		};
+		if(esp->erev != fashme)
+			esp_cmd(esp, eregs, ESP_CMD_NULL);
+
+		/* Be careful, we could really get fucked during synchronous
+		 * data transfers if we try to flush the fifo now.
+		 */
+		if((esp->erev != fashme) && /* not a Happy Meal and... */
+		   !fcnt && /* Fifo is empty and... */
+		   /* either we are not doing synchronous transfers or... */
+		   (!SDptr->sync_max_offset ||
+		    /* We are not going into data in phase. */
+		    ((esp->sreg & ESP_STAT_PMASK) != ESP_DIP)))
+			esp_cmd(esp, eregs, ESP_CMD_FLUSH); /* flush is safe */
+
+		/* See how far we got if this is not a slow command. */
+		if(!esp->esp_slowcmd) {
+			if(cmd_bytes_sent < 0)
+				cmd_bytes_sent = 0;
+			if(cmd_bytes_sent != SCptr->cmd_len) {
+				/* Crapola, mark it as a slowcmd
+				 * so that we have some chance of
+				 * keeping the command alive with
+				 * good luck.
+				 *
+				 * XXX Actually, if we didn't send it all
+				 * XXX this means either we didn't set things
+				 * XXX up properly (driver bug) or the target
+				 * XXX or the ESP detected parity on one of
+				 * XXX the command bytes.  This makes much
+				 * XXX more sense, and therefore this code
+				 * XXX should be changed to send out a
+				 * XXX parity error message or if the status
+				 * XXX register shows no parity error then
+				 * XXX just expect the target to bring the
+				 * XXX bus into message in phase so that it
+				 * XXX can send us the parity error message.
+				 * XXX SCSI sucks...
+				 */
+				esp->esp_slowcmd = 1;
+				esp->esp_scmdp = &(SCptr->cmnd[cmd_bytes_sent]);
+				esp->esp_scmdleft = (SCptr->cmd_len - cmd_bytes_sent);
+			}
+		}
+
+		/* Now figure out where we went. */
+		esp_advance_phase(SCptr, in_the_dark);
+		return esp_do_phase_determine(esp, eregs);
+	}
+
+	/* Did the target even make it? */
+	if(esp->ireg == ESP_INTR_DC) {
+		/* wheee... nobody there or they didn't like
+		 * what we told it to do, clean up.
+		 */
+
+		/* If anyone is off the bus, but working on
+		 * a command in the background for us, tell
+		 * the ESP to listen for them.
+		 */
+		if(esp->disconnected_SC)
+			esp_cmd(esp, eregs, ESP_CMD_ESEL);
+
+		if(((1<<SCptr->target) & esp->targets_present) &&
+		   esp->seqreg && esp->cur_msgout[0] == EXTENDED_MESSAGE &&
+		   (SCptr->SCp.phase == in_slct_msg ||
+		    SCptr->SCp.phase == in_slct_stop)) {
+			/* shit */
+			esp->snip = 0;
+			printk("esp%d: Failed synchronous negotiation for target %d "
+			       "lun %d\n",
+			       esp->esp_id, SCptr->target, SCptr->lun);
+			SDptr->sync_max_offset = 0;
+			SDptr->sync_min_period = 0;
+			SDptr->sync = 1; /* so we don't negotiate again */
+
+			/* Run the command again, this time though we
+			 * won't try to negotiate for synchronous transfers.
+			 *
+			 * XXX I'd like to do something like send an
+			 * XXX INITIATOR_ERROR or ABORT message to the
+			 * XXX target to tell it, "Sorry I confused you,
+			 * XXX please come back and I will be nicer next
+			 * XXX time".  But that requires having the target
+			 * XXX on the bus, and it has dropped BSY on us.
+			 */
+			esp->current_SC = NULL;
+			esp_advance_phase(SCptr, not_issued);
+			prepend_SC(&esp->issue_SC, SCptr);
+			esp_exec_cmd(esp);
+			return do_intr_end;
+		}
+
+		/* Ok, this is normal, this is what we see during boot
+		 * or whenever when we are scanning the bus for targets.
+		 * But first make sure that is really what is happening.
+		 */
+		if(((1<<SCptr->target) & esp->targets_present)) {
+			printk("esp%d: Warning, live target %d not responding to "
+			       "selection.\n", esp->esp_id, SCptr->target);
+
+			/* This _CAN_ happen.  The SCSI standard states that
+			 * the target is to _not_ respond to selection if
+			 * _it_ detects bad parity on the bus for any reason.
+			 * Therefore, we assume that if we've talked successfully
+			 * to this target before, bad parity is the problem.
+			 */
+			esp_done(esp, (DID_PARITY << 16));
+		} else {
+			/* Else, there really isn't anyone there. */
+			ESPMISC(("esp: selection failure, maybe nobody there?\n"));
+			ESPMISC(("esp: target %d lun %d\n",
+				 SCptr->target, SCptr->lun));
+			esp_done(esp, (DID_BAD_TARGET << 16));
+		}
+		return do_intr_end;
+	}
+
+
+	ESPLOG(("esp%d: Selection failure.\n", esp->esp_id));
+	printk("esp%d: Currently -- ", esp->esp_id);
+	esp_print_ireg(esp->ireg);
+	printk(" ");
+	esp_print_statreg(esp->sreg);
+	printk(" ");
+	esp_print_seqreg(esp->seqreg);
+	printk("\n");
+	printk("esp%d: New -- ", esp->esp_id);
+	esp->sreg = eregs->esp_status;
+	esp->seqreg = eregs->esp_sstep;
+	esp->ireg = eregs->esp_intrpt;
+	esp_print_ireg(esp->ireg);
+	printk(" ");
+	esp_print_statreg(esp->sreg);
+	printk(" ");
+	esp_print_seqreg(esp->seqreg);
+	printk("\n");
+	ESPLOG(("esp%d: resetting bus\n", esp->esp_id));
+	return do_reset_bus; /* ugh... */
+}
+
+/* Continue reading bytes for msgin phase. */
+static int esp_do_msgincont(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	if(esp->ireg & ESP_INTR_BSERV) {
+		/* in the right phase too? */
+		if((esp->sreg & ESP_STAT_PMASK) == ESP_MIP) {
+			/* phew... */
+			esp_cmd(esp, eregs, ESP_CMD_TI);
+			esp_advance_phase(esp->current_SC, in_msgindone);
+			return do_intr_end;
+		}
+
+		/* We changed phase but ESP shows bus service,
+		 * in this case it is most likely that we, the
+		 * hacker who has been up for 20hrs straight
+		 * staring at the screen, drowned in coffee
+		 * smelling like retched cigarette ashes
+		 * have miscoded something..... so, try to
+		 * recover as best we can.
+		 */
+		printk("esp%d: message in mis-carriage.\n", esp->esp_id);
+	}
+	esp_advance_phase(esp->current_SC, in_the_dark);
+	return do_phase_determine;
+}
+
+static inline int check_singlebyte_msg(struct NCR_ESP *esp,
+				       struct ESP_regs *eregs)
+{
+	esp->prevmsgin = esp->cur_msgin[0];
+	if(esp->cur_msgin[0] & 0x80) {
+		/* wheee... */
+		ESPLOG(("esp%d: target sends identify amidst phases\n",
+			esp->esp_id));
+		esp_advance_phase(esp->current_SC, in_the_dark);
+		return 0;
+	} else if(((esp->cur_msgin[0] & 0xf0) == 0x20) ||
+		  (esp->cur_msgin[0] == EXTENDED_MESSAGE)) {
+		esp->msgin_len = 2;
+		esp_advance_phase(esp->current_SC, in_msgincont);
+		return 0;
+	}
+	esp_advance_phase(esp->current_SC, in_the_dark);
+	switch(esp->cur_msgin[0]) {
+	default:
+		/* We don't want to hear about it. */
+		ESPLOG(("esp%d: msg %02x which we don't know about\n", esp->esp_id,
+			esp->cur_msgin[0]));
+		return MESSAGE_REJECT;
+
+	case NOP:
+		ESPLOG(("esp%d: target %d sends a nop\n", esp->esp_id,
+			esp->current_SC->target));
+		return 0;
+
+	case RESTORE_POINTERS:
+	case SAVE_POINTERS:
+		/* We handle this all automatically. */
+		return 0;
+
+	case COMMAND_COMPLETE:
+	case DISCONNECT:
+		/* Freeing the bus, let it go. */
+		esp->current_SC->SCp.phase = in_freeing;
+		return 0;
+
+	case MESSAGE_REJECT:
+		ESPMISC(("msg reject, "));
+		if(esp->prevmsgout == EXTENDED_MESSAGE) {
+			Scsi_Device *SDptr = esp->current_SC->device;
+
+			/* Doesn't look like this target can
+			 * do synchronous or WIDE transfers.
+			 */
+			ESPSDTR(("got reject, was trying nego, clearing sync/WIDE\n"));
+			SDptr->sync = 1;
+			SDptr->wide = 1;
+			SDptr->sync_min_period = 0;
+			SDptr->sync_max_offset = 0;
+			return 0;
+		} else {
+			ESPMISC(("not sync nego, sending ABORT\n"));
+			return ABORT;
+		}
+	};
+}
+
+/* Target negotiates for synchronous transfers before we do, this
+ * is legal although very strange.  What is even funnier is that
+ * the SCSI2 standard specifically recommends against targets doing
+ * this because so many initiators cannot cope with this occuring.
+ */
+static inline int target_with_ants_in_pants(struct NCR_ESP *esp,
+					    Scsi_Cmnd *SCptr,
+					    Scsi_Device *SDptr)
+{
+	if(SDptr->sync || SDptr->borken) {
+		/* sorry, no can do */
+		ESPSDTR(("forcing to async, "));
+		build_sync_nego_msg(esp, 0, 0);
+		SDptr->sync = 1;
+		esp->snip = 1;
+		ESPLOG(("esp%d: hoping for msgout\n", esp->esp_id));
+		esp_advance_phase(SCptr, in_the_dark);
+		return EXTENDED_MESSAGE;
+	}
+
+	/* Ok, we'll check them out... */
+	return 0;
+}
+
+static inline void sync_report(struct NCR_ESP *esp)
+{
+	int msg3, msg4;
+	char *type;
+
+	msg3 = esp->cur_msgin[3];
+	msg4 = esp->cur_msgin[4];
+	if(msg4) {
+		int hz = 1000000000 / (msg3 * 4);
+		int integer = hz / 1000000;
+		int fraction = (hz - (integer * 1000000)) / 10000;
+		if((esp->erev == fashme) &&
+		   (esp->config3[esp->current_SC->target] & ESP_CONFIG3_EWIDE)) {
+			type = "FAST-WIDE";
+			integer <<= 1;
+			fraction <<= 1;
+		} else if((msg3 * 4) < 200) {
+			type = "FAST";
+		} else {
+			type = "synchronous";
+		}
+		printk(KERN_INFO "esp%d: target %d [period %dns offset %d %d.%02dMHz %s SCSI%s]\n",
+		       esp->esp_id, esp->current_SC->target,
+		       (int) msg3 * 4,
+		       (int) msg4,
+		       integer, fraction, type,
+		       (((msg3 * 4) < 200) ? "-II" : ""));
+	} else {
+		printk(KERN_INFO "esp%d: target %d asynchronous\n",
+		       esp->esp_id, esp->current_SC->target);
+	}
+}
+
+static inline int check_multibyte_msg(struct NCR_ESP *esp,
+				       struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	Scsi_Device *SDptr = SCptr->device;
+	unchar regval = 0;
+	int message_out = 0;
+
+	ESPSDTR(("chk multibyte msg: "));
+	if(esp->cur_msgin[2] == EXTENDED_SDTR) {
+		int period = esp->cur_msgin[3];
+		int offset = esp->cur_msgin[4];
+
+		ESPSDTR(("is sync nego response, "));
+		if(!esp->snip) {
+			int rval;
+
+			/* Target negotiates first! */
+			ESPSDTR(("target jumps the gun, "));
+			message_out = EXTENDED_MESSAGE; /* we must respond */
+			rval = target_with_ants_in_pants(esp, SCptr, SDptr);
+			if(rval)
+				return rval;
+		}
+
+		ESPSDTR(("examining sdtr, "));
+
+		/* Offset cannot be larger than ESP fifo size. */
+		if(offset > 15) {
+			ESPSDTR(("offset too big %2x, ", offset));
+			offset = 15;
+			ESPSDTR(("sending back new offset\n"));
+			build_sync_nego_msg(esp, period, offset);
+			return EXTENDED_MESSAGE;
+		}
+
+		if(offset && period > esp->max_period) {
+			/* Yeee, async for this slow device. */
+			ESPSDTR(("period too long %2x, ", period));
+			build_sync_nego_msg(esp, 0, 0);
+			ESPSDTR(("hoping for msgout\n"));
+			esp_advance_phase(esp->current_SC, in_the_dark);
+			return EXTENDED_MESSAGE;
+		} else if (offset && period < esp->min_period) {
+			ESPSDTR(("period too short %2x, ", period));
+			period = esp->min_period;
+			if(esp->erev > esp236)
+				regval = 4;
+			else
+				regval = 5;
+		} else if(offset) {
+			int tmp;
+
+			ESPSDTR(("period is ok, "));
+			tmp = esp->ccycle / 1000;
+			regval = (((period << 2) + tmp - 1) / tmp);
+			if(regval && ((esp->erev == fas100a ||
+				       esp->erev == fas236 ||
+				       esp->erev == fashme))) {
+				if(period >= 50)
+					regval--;
+			}
+		}
+
+		if(offset) {
+			unchar bit;
+
+			SDptr->sync_min_period = (regval & 0x1f);
+			SDptr->sync_max_offset = (offset | esp->radelay);
+			if((esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme)) {
+				if((esp->erev == fas100a) || (esp->erev == fashme))
+					bit = ESP_CONFIG3_FAST;
+				else
+					bit = ESP_CONFIG3_FSCSI;
+				if(period < 50)
+					esp->config3[SCptr->target] |= bit;
+				else
+					esp->config3[SCptr->target] &= ~bit;
+				eregs->esp_cfg3 = esp->config3[SCptr->target];
+			}
+			eregs->esp_soff = SDptr->sync_min_period;
+			eregs->esp_stp  = SDptr->sync_max_offset;
+
+			ESPSDTR(("soff=%2x stp=%2x cfg3=%2x\n",
+				SDptr->sync_max_offset,
+				SDptr->sync_min_period,
+				esp->config3[SCptr->target]));
+
+			esp->snip = 0;
+		} else if(SDptr->sync_max_offset) {
+			unchar bit;
+
+			/* back to async mode */
+			ESPSDTR(("unaccaptable sync nego, forcing async\n"));
+			SDptr->sync_max_offset = 0;
+			SDptr->sync_min_period = 0;
+			eregs->esp_soff = 0;
+			eregs->esp_stp = 0;
+			if((esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme)) {
+				if((esp->erev == fas100a) || (esp->erev == fashme))
+					bit = ESP_CONFIG3_FAST;
+				else
+					bit = ESP_CONFIG3_FSCSI;
+				esp->config3[SCptr->target] &= ~bit;
+				eregs->esp_cfg3 = esp->config3[SCptr->target];
+			}
+		}
+
+		sync_report(esp);
+
+		ESPSDTR(("chk multibyte msg: sync is known, "));
+		SDptr->sync = 1;
+
+		if(message_out) {
+			ESPLOG(("esp%d: sending sdtr back, hoping for msgout\n",
+				esp->esp_id));
+			build_sync_nego_msg(esp, period, offset);
+			esp_advance_phase(SCptr, in_the_dark);
+			return EXTENDED_MESSAGE;
+		}
+
+		ESPSDTR(("returning zero\n"));
+		esp_advance_phase(SCptr, in_the_dark); /* ...or else! */
+		return 0;
+	} else if(esp->cur_msgin[2] == EXTENDED_WDTR) {
+		int size = 8 << esp->cur_msgin[3];
+
+		esp->wnip = 0;
+		if(esp->erev != fashme) {
+			printk("esp%d: AIEEE wide msg received and not HME.\n",
+			       esp->esp_id);
+			message_out = MESSAGE_REJECT;
+		} else if(size > 16) {
+			printk("esp%d: AIEEE wide transfer for %d size not supported.\n",
+			       esp->esp_id, size);
+			message_out = MESSAGE_REJECT;
+		} else {
+			/* Things look good, lets see what we got. */
+			if(size == 16) {
+				/* Set config 3 register for this target. */
+				printk("esp%d: 16 byte WIDE transfers enabled for target %d.\n",
+				       esp->esp_id, SCptr->target);
+				esp->config3[SCptr->target] |= ESP_CONFIG3_EWIDE;
+			} else {
+				/* Just make sure it was one byte sized. */
+				if(size != 8) {
+					printk("esp%d: Aieee, wide nego of %d size.\n",
+					       esp->esp_id, size);
+					message_out = MESSAGE_REJECT;
+					goto finish;
+				}
+				/* Pure paranoia. */
+				esp->config3[SCptr->target] &= ~(ESP_CONFIG3_EWIDE);
+			}
+			eregs->esp_cfg3 = esp->config3[SCptr->target];
+
+			/* Regardless, next try for sync transfers. */
+			build_sync_nego_msg(esp, esp->sync_defp, 15);
+			SDptr->sync = 1;
+			esp->snip = 1;
+			message_out = EXTENDED_MESSAGE;
+		}
+	} else if(esp->cur_msgin[2] == EXTENDED_MODIFY_DATA_POINTER) {
+		ESPLOG(("esp%d: rejecting modify data ptr msg\n", esp->esp_id));
+		message_out = MESSAGE_REJECT;
+	}
+finish:
+	esp_advance_phase(SCptr, in_the_dark);
+	return message_out;
+}
+
+static int esp_do_msgindone(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int message_out = 0, it = 0, rval;
+
+	rval = skipahead1(esp, eregs, SCptr, in_msgin, in_msgindone);
+	if(rval)
+		return rval;
+	if(SCptr->SCp.sent_command != in_status) {
+		if(!(esp->ireg & ESP_INTR_DC)) {
+			if(esp->msgin_len && (esp->sreg & ESP_STAT_PERR)) {
+				message_out = MSG_PARITY_ERROR;
+				esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+			} else if(esp->erev != fashme &&
+				  (it = (eregs->esp_fflags & ESP_FF_FBYTES))!=1) {
+				/* We certainly dropped the ball somewhere. */
+				message_out = INITIATOR_ERROR;
+				esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+			} else if(!esp->msgin_len) {
+				if(esp->erev == fashme)
+					it = esp->hme_fifo_workaround_buffer[0];
+				else
+					it = eregs->esp_fdata;
+				esp_advance_phase(SCptr, in_msgincont);
+			} else {
+				/* it is ok and we want it */
+				if(esp->erev == fashme)
+					it = esp->cur_msgin[esp->msgin_ctr] =
+						esp->hme_fifo_workaround_buffer[0];
+				else
+					it = esp->cur_msgin[esp->msgin_ctr] =
+						eregs->esp_fdata;
+				esp->msgin_ctr++;
+			}
+		} else {
+			esp_advance_phase(SCptr, in_the_dark);
+			return do_work_bus;
+		}
+	} else {
+		it = esp->cur_msgin[0];
+	}
+	if(!message_out && esp->msgin_len) {
+		if(esp->msgin_ctr < esp->msgin_len) {
+			esp_advance_phase(SCptr, in_msgincont);
+		} else if(esp->msgin_len == 1) {
+			message_out = check_singlebyte_msg(esp, eregs);
+		} else if(esp->msgin_len == 2) {
+			if(esp->cur_msgin[0] == EXTENDED_MESSAGE) {
+				if((it+2) >= 15) {
+					message_out = MESSAGE_REJECT;
+				} else {
+					esp->msgin_len = (it + 2);
+					esp_advance_phase(SCptr, in_msgincont);
+				}
+			} else {
+				message_out = MESSAGE_REJECT; /* foo on you */
+			}
+		} else {
+			message_out = check_multibyte_msg(esp, eregs);
+		}
+	}
+	if(message_out < 0) {
+		return -message_out;
+	} else if(message_out) {
+		if(((message_out != 1) &&
+		    ((message_out < 0x20) || (message_out & 0x80))))
+			esp->msgout_len = 1;
+		esp->cur_msgout[0] = message_out;
+		esp_cmd(esp, eregs, ESP_CMD_SATN);
+		esp_advance_phase(SCptr, in_the_dark);
+		esp->msgin_len = 0;
+	}
+	esp->sreg = eregs->esp_status;
+	esp->sreg &= ~(ESP_STAT_INTR);
+	if((esp->sreg & (ESP_STAT_PMSG|ESP_STAT_PCD)) == (ESP_STAT_PMSG|ESP_STAT_PCD))
+		esp_cmd(esp, eregs, ESP_CMD_MOK);
+	if((SCptr->SCp.sent_command == in_msgindone) &&
+	    (SCptr->SCp.phase == in_freeing))
+		return esp_do_freebus(esp, eregs);
+	return do_intr_end;
+}
+
+static int esp_do_cmdbegin(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+
+	esp_advance_phase(SCptr, in_cmdend);
+	if(esp->erev == fashme) {
+		int i;
+
+		for(i = 0; i < esp->esp_scmdleft; i++)
+			esp->esp_command[i] = *esp->esp_scmdp++;
+		esp->esp_scmdleft = 0;
+		esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+		esp_setcount(eregs, i, 1);
+		esp_cmd(esp, eregs, (ESP_CMD_DMA | ESP_CMD_TI));
+		esp->dma_init_write(esp, esp->esp_command_dvma, i);
+	} else {
+		esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+		eregs->esp_fdata = *esp->esp_scmdp++;
+		esp->esp_scmdleft--;
+		esp_cmd(esp, eregs, ESP_CMD_TI);
+	}
+	return do_intr_end;
+}
+
+static inline int esp_do_cmddone(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	if(esp->erev == fashme){
+		if(esp->dma_invalidate)
+			esp->dma_invalidate(esp);
+	} else
+		esp_cmd(esp, eregs, ESP_CMD_NULL);
+	if(esp->ireg & ESP_INTR_BSERV) {
+		esp_advance_phase(esp->current_SC, in_the_dark);
+		return esp_do_phase_determine(esp, eregs);
+	}
+	ESPLOG(("esp%d: in do_cmddone() but didn't get BSERV interrupt.\n",
+		esp->esp_id));
+	return do_reset_bus;
+}
+
+static int esp_do_msgout(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+	switch(esp->msgout_len) {
+	case 1:
+		if(esp->erev == fashme)
+			hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 1);
+		else
+			eregs->esp_fdata = esp->cur_msgout[0];
+		esp_cmd(esp, eregs, ESP_CMD_TI);
+		break;
+
+	case 2:
+		if(esp->do_pio_cmds){
+			eregs->esp_fdata = esp->cur_msgout[0];
+			eregs->esp_fdata = esp->cur_msgout[1];
+			esp_cmd(esp, eregs, ESP_CMD_TI);
+		} else {
+			esp->esp_command[0] = esp->cur_msgout[0];
+			esp->esp_command[1] = esp->cur_msgout[1];
+			if(esp->erev == fashme) {
+				hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 2);
+				esp_cmd(esp, eregs, ESP_CMD_TI);
+			} else {
+				esp->dma_setup(esp, esp->esp_command_dvma, 2, 0);
+				esp_setcount(eregs, 2, 0);
+				esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+			}
+		}
+		break;
+
+	case 4:
+		esp->snip = 1;
+		if(esp->do_pio_cmds){
+			eregs->esp_fdata = esp->cur_msgout[0];
+			eregs->esp_fdata = esp->cur_msgout[1];
+			eregs->esp_fdata = esp->cur_msgout[2];
+			eregs->esp_fdata = esp->cur_msgout[3];
+			esp_cmd(esp, eregs, ESP_CMD_TI);
+		} else {
+			esp->esp_command[0] = esp->cur_msgout[0];
+			esp->esp_command[1] = esp->cur_msgout[1];
+			esp->esp_command[2] = esp->cur_msgout[2];
+			esp->esp_command[3] = esp->cur_msgout[3];
+			if(esp->erev == fashme) {
+				hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 4);
+				esp_cmd(esp, eregs, ESP_CMD_TI);
+			} else {
+				esp->dma_setup(esp, esp->esp_command_dvma, 4, 0);
+				esp_setcount(eregs, 4, 0);
+				esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+			}
+		}
+		break;
+
+	case 5:
+		esp->snip = 1;
+		if(esp->do_pio_cmds){
+			eregs->esp_fdata = esp->cur_msgout[0];
+			eregs->esp_fdata = esp->cur_msgout[1];
+			eregs->esp_fdata = esp->cur_msgout[2];
+			eregs->esp_fdata = esp->cur_msgout[3];
+			eregs->esp_fdata = esp->cur_msgout[4];
+			esp_cmd(esp, eregs, ESP_CMD_TI);
+		} else {
+			esp->esp_command[0] = esp->cur_msgout[0];
+			esp->esp_command[1] = esp->cur_msgout[1];
+			esp->esp_command[2] = esp->cur_msgout[2];
+			esp->esp_command[3] = esp->cur_msgout[3];
+			esp->esp_command[4] = esp->cur_msgout[4];
+			if(esp->erev == fashme) {
+				hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 5);
+				esp_cmd(esp, eregs, ESP_CMD_TI);
+			} else {
+				esp->dma_setup(esp, esp->esp_command_dvma, 5, 0);
+				esp_setcount(eregs, 5, 0);
+				esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+			}
+		}
+		break;
+
+	default:
+		/* whoops */
+		ESPMISC(("bogus msgout sending NOP\n"));
+		esp->cur_msgout[0] = NOP;
+		if(esp->erev == fashme) {
+			hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 1);
+		} else {
+			eregs->esp_fdata = esp->cur_msgout[0];
+		}
+		esp->msgout_len = 1;
+		esp_cmd(esp, eregs, ESP_CMD_TI);
+		break;
+	}
+	esp_advance_phase(esp->current_SC, in_msgoutdone);
+	return do_intr_end;
+}
+
+static inline int esp_do_msgoutdone(struct NCR_ESP *esp, 
+				    struct ESP_regs *eregs)
+{
+	if((esp->msgout_len > 1) && esp->dma_barrier)
+		esp->dma_barrier(esp);
+
+	if(!(esp->ireg & ESP_INTR_DC)) {
+		if(esp->erev != fashme)
+			esp_cmd(esp, eregs, ESP_CMD_NULL);
+		switch(esp->sreg & ESP_STAT_PMASK) {
+		case ESP_MOP:
+			/* whoops, parity error */
+			ESPLOG(("esp%d: still in msgout, parity error assumed\n",
+				esp->esp_id));
+			if(esp->msgout_len > 1)
+				esp_cmd(esp, eregs, ESP_CMD_SATN);
+			esp_advance_phase(esp->current_SC, in_msgout);
+			return do_work_bus;
+
+		case ESP_DIP:
+			break;
+
+		default:
+			/* Happy Meal fifo is touchy... */
+			if((esp->erev != fashme) &&
+			   !fcount(esp, eregs) &&
+			   !(esp->current_SC->device->sync_max_offset))
+				esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+			break;
+
+		};
+	} else {
+		ESPLOG(("esp%d: disconnect, resetting bus\n", esp->esp_id));
+		return do_reset_bus;
+	}
+
+	/* If we sent out a synchronous negotiation message, update
+	 * our state.
+	 */
+	if(esp->cur_msgout[2] == EXTENDED_MESSAGE &&
+	   esp->cur_msgout[4] == EXTENDED_SDTR) {
+		esp->snip = 1; /* anal retentiveness... */
+	}
+
+	esp->prevmsgout = esp->cur_msgout[0];
+	esp->msgout_len = 0;
+	esp_advance_phase(esp->current_SC, in_the_dark);
+	return esp_do_phase_determine(esp, eregs);
+}
+
+/* This is the second tier in our dual-level SCSI state machine. */
+static inline int esp_work_bus(struct NCR_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+
+	ESPBUS(("esp_work_bus: "));
+	if(!SCptr) {
+		ESPBUS(("reconnect\n"));
+		return esp_do_reconnect(esp, eregs);
+	}
+
+	switch(SCptr->SCp.phase) {
+	case in_the_dark:
+		ESPBUS(("in the dark\n"));
+		return esp_do_phase_determine(esp, eregs);
+
+	case in_slct_norm:
+	case in_slct_stop:
+	case in_slct_msg:
+	case in_slct_tag:
+	case in_slct_sneg:
+		ESPBUS(("finish selection\n"));
+		return esp_select_complete(esp, eregs);
+
+	case in_datain:
+	case in_dataout:
+		ESPBUS(("finish data\n"));
+		return esp_do_data_finale(esp, eregs);
+
+	case in_msgout:
+		ESPBUS(("message out "));
+		return esp_do_msgout(esp, eregs);
+
+	case in_msgoutdone:
+		ESPBUS(("finish message out "));
+		return esp_do_msgoutdone(esp, eregs);
+
+	case in_msgin:
+		ESPBUS(("message in "));
+		return esp_do_msgin(esp, eregs);
+
+	case in_msgincont:
+		ESPBUS(("continue message in "));
+		return esp_do_msgincont(esp, eregs);
+
+	case in_msgindone:
+		ESPBUS(("finish message in "));
+		return esp_do_msgindone(esp, eregs);
+
+	case in_status:
+		ESPBUS(("status phase "));
+		return esp_do_status(esp, eregs);
+
+	case in_freeing:
+		ESPBUS(("freeing the bus "));
+		return esp_do_freebus(esp, eregs);
+
+	case in_cmdbegin:
+		ESPBUS(("begin slow cmd "));
+		return esp_do_cmdbegin(esp, eregs);
+
+	case in_cmdend:
+		ESPBUS(("end slow cmd "));
+		return esp_do_cmddone(esp, eregs);
+
+	default:
+		printk("esp%d: command in weird state %2x\n",
+		       esp->esp_id, esp->current_SC->SCp.phase);
+		return do_reset_bus;
+	};
+}
+
+/* Main interrupt handler for an esp adapter. */
+inline void esp_handle(struct NCR_ESP *esp)
+{
+	struct ESP_regs *eregs;
+	Scsi_Cmnd *SCptr;
+	int what_next = do_intr_end;
+#ifdef CONFIG_SCSI_SUNESP
+	struct sparc_dma_registers *dregs = 
+	  (struct sparc_dma_registers*) esp->dregs;
+#endif
+	eregs = esp->eregs;
+	SCptr = esp->current_SC;
+
+	if(esp->dma_irq_entry)
+		esp->dma_irq_entry(esp);
+
+	/* Check for errors. */
+	esp->sreg = eregs->esp_status;
+	esp->sreg &= (~ESP_STAT_INTR);
+	if(esp->erev == fashme) {
+		esp->sreg2 = eregs->esp_status2;
+		esp->seqreg = (eregs->esp_sstep & ESP_STEP_VBITS);
+	}
+	if(esp->sreg & (ESP_STAT_SPAM)) {
+		/* Gross error, could be due to one of:
+		 *
+		 * - top of fifo overwritten, could be because
+		 *   we tried to do a synchronous transfer with
+		 *   an offset greater than ESP fifo size
+		 *
+		 * - top of command register overwritten
+		 *
+		 * - DMA setup to go in one direction, SCSI
+		 *   bus points in the other, whoops
+		 *
+		 * - weird phase change during asynchronous
+		 *   data phase while we are initiator
+		 */
+		ESPLOG(("esp%d: Gross error sreg=%2x\n", esp->esp_id, esp->sreg));
+
+		/* If a command is live on the bus we cannot safely
+		 * reset the bus, so we'll just let the pieces fall
+		 * where they may.  Here we are hoping that the
+		 * target will be able to cleanly go away soon
+		 * so we can safely reset things.
+		 */
+		if(!SCptr) {
+			ESPLOG(("esp%d: No current cmd during gross error, "
+				"resetting bus\n", esp->esp_id));
+			what_next = do_reset_bus;
+			goto again;
+		}
+	}
+
+#ifdef CONFIG_SCSI_SUNESP
+	if(dregs->cond_reg & DMA_HNDL_ERROR) {
+		/* A DMA gate array error.  Here we must
+		 * be seeing one of two things.  Either the
+		 * virtual to physical address translation
+		 * on the SBUS could not occur, else the
+		 * translation it did get pointed to a bogus
+		 * page.  Ho hum...
+		 */
+		ESPLOG(("esp%d: DMA error %08x\n", esp->esp_id,
+			dregs->cond_reg));
+
+		/* DMA gate array itself must be reset to clear the
+		 * error condition.
+		 */
+		if(esp->dma_reset)
+			esp->dma_reset(esp);
+
+		what_next = do_reset_bus;
+		goto again;
+	}
+#endif /* CONFIG_SCSI_SUNESP */
+
+	if(esp->erev == fashme) {
+		/* This chip is really losing. */
+		ESPHME(("HME["));
+
+		ESPHME(("sreg2=%02x,", esp->sreg2));
+		/* Must latch fifo before reading the interrupt
+		 * register else garbage ends up in the FIFO
+		 * which confuses the driver utterly.
+		 */
+		if(!(esp->sreg2 & ESP_STAT2_FEMPTY) ||
+		   (esp->sreg2 & ESP_STAT2_F1BYTE)) {
+			ESPHME(("fifo_workaround]"));
+			hme_fifo_hwbug_workaround(esp, eregs);
+		} else {
+			ESPHME(("no_fifo_workaround]"));
+		}
+	}
+
+	esp->ireg = eregs->esp_intrpt;   /* Unlatch intr and stat regs */
+
+	/* This cannot be done until this very moment. -DaveM */
+	synchronize_irq();
+
+	/* No current cmd is only valid at this point when there are
+	 * commands off the bus or we are trying a reset.
+	 */
+	if(!SCptr && !esp->disconnected_SC && !(esp->ireg & ESP_INTR_SR)) {
+		/* Panic is safe, since current_SC is null. */
+		ESPLOG(("esp%d: no command in esp_handle()\n", esp->esp_id));
+		panic("esp_handle: current_SC == penguin within interrupt!");
+	}
+
+	if(esp->ireg & (ESP_INTR_IC)) {
+		/* Illegal command fed to ESP.  Outside of obvious
+		 * software bugs that could cause this, there is
+		 * a condition with esp100 where we can confuse the
+		 * ESP into an erroneous illegal command interrupt
+		 * because it does not scrape the FIFO properly
+		 * for reselection.  See esp100_reconnect_hwbug()
+		 * to see how we try very hard to avoid this.
+		 */
+		ESPLOG(("esp%d: illegal command\n", esp->esp_id));
+
+		esp_dump_state(esp, eregs);
+
+		if(SCptr) {
+			/* Devices with very buggy firmware can drop BSY
+			 * during a scatter list interrupt when using sync
+			 * mode transfers.  We continue the transfer as
+			 * expected, the target drops the bus, the ESP
+			 * gets confused, and we get a illegal command
+			 * interrupt because the bus is in the disconnected
+			 * state now and ESP_CMD_TI is only allowed when
+			 * a nexus is alive on the bus.
+			 */
+			ESPLOG(("esp%d: Forcing async and disabling disconnect for "
+				"target %d\n", esp->esp_id, SCptr->target));
+			SCptr->device->borken = 1; /* foo on you */
+		}
+
+		what_next = do_reset_bus;
+		goto again;
+	}
+
+	if(!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) {
+		int phase;
+
+		if(SCptr) {
+			phase = SCptr->SCp.phase;
+			if(phase & in_phases_mask) {
+				what_next = esp_work_bus(esp, eregs);
+			} else if(phase & in_slct_mask) {
+				what_next = esp_select_complete(esp, eregs);
+			} else {
+				ESPLOG(("esp%d: interrupt for no good reason...\n",
+					esp->esp_id));
+				goto esp_handle_done;
+			}
+		} else {
+			ESPLOG(("esp%d: BSERV or FDONE or DC while SCptr==NULL\n",
+				esp->esp_id));
+			what_next = do_reset_bus;
+			goto again;
+		}
+	} else if(esp->ireg & ESP_INTR_SR) {
+		ESPLOG(("esp%d: SCSI bus reset interrupt\n", esp->esp_id));
+		what_next = do_reset_complete;
+	} else if(esp->ireg & (ESP_INTR_S | ESP_INTR_SATN)) {
+		ESPLOG(("esp%d: AIEEE we have been selected by another initiator!\n",
+			esp->esp_id));
+		what_next = do_reset_bus;
+		goto again;
+	} else if(esp->ireg & ESP_INTR_RSEL) {
+		if(!SCptr) {
+			/* This is ok. */
+			what_next = esp_do_reconnect(esp, eregs);
+		} else if(SCptr->SCp.phase & in_slct_mask) {
+			/* Only selection code knows how to clean
+			 * up properly.
+			 */
+			ESPDISC(("Reselected during selection attempt\n"));
+			what_next = esp_select_complete(esp, eregs);
+		} else {
+			ESPLOG(("esp%d: Reselected while bus is busy\n",
+				esp->esp_id));
+			what_next = do_reset_bus;
+			goto again;
+		}
+	}
+
+	/* We're trying to fight stack problems, and inline as much as
+	 * possible without making this driver a mess. hate hate hate
+	 * This is tier-one in our dual level SCSI state machine.
+	 */
+again:
+	switch(what_next) {
+	case do_intr_end:
+		goto esp_handle_done;
+
+	case do_work_bus:
+		what_next = esp_work_bus(esp, eregs);
+		break;
+
+	case do_phase_determine:
+		what_next = esp_do_phase_determine(esp, eregs);
+		break;
+
+	case do_reset_bus:
+		ESPLOG(("esp%d: resetting bus...\n", esp->esp_id));
+		esp->resetting_bus = 1;
+		esp_cmd(esp, eregs, ESP_CMD_RS);
+		goto esp_handle_done;
+
+	case do_reset_complete:
+		/* Tricky, we don't want to cause any more commands to
+		 * go out until we clear all the live cmds by hand.
+		 */
+		if(esp->current_SC) {
+			Scsi_Cmnd *SCptr = esp->current_SC;
+
+#ifdef CONFIG_SCSI_SUNESP
+			if(!SCptr->use_sg)
+				mmu_release_scsi_one(SCptr->SCp.have_data_in,
+						     SCptr->request_bufflen,
+						     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+			else
+				mmu_release_scsi_sgl((struct mmu_sglist *)
+						     SCptr->buffer,
+						     SCptr->use_sg - 1,
+						     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+#endif
+			SCptr->result = (DID_RESET << 16);
+
+			SCptr->scsi_done(SCptr);
+		}
+		esp->current_SC = NULL;
+		if(esp->disconnected_SC) {
+			Scsi_Cmnd *SCptr;
+			while((SCptr = remove_first_SC(&esp->disconnected_SC))) {
+				if(!SCptr->use_sg)
+#ifdef CONFIG_SCSI_SUNESP
+					mmu_release_scsi_one(SCptr->SCp.have_data_in,
+							     SCptr->request_bufflen,
+							     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+				else
+					mmu_release_scsi_sgl((struct mmu_sglist *)
+							     SCptr->buffer,
+							     SCptr->use_sg - 1,
+							     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+#endif
+				SCptr->result = (DID_RESET << 16);
+
+				SCptr->scsi_done(SCptr);
+			}
+		}
+		esp->resetting_bus = 0;
+
+		if(esp->current_SC) {
+			printk("esp%d: weird weird weird, current_SC not NULL after "
+			       "SCSI bus reset.\n", esp->esp_id);
+			goto esp_handle_done;
+		}
+
+		/* Now it is safe to execute more things. */
+		if(esp->issue_SC)
+			esp_exec_cmd(esp);
+		goto esp_handle_done;
+
+	default:
+		/* state is completely lost ;-( */
+		ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n",
+			esp->esp_id));
+		what_next = do_reset_bus;
+		break;
+
+	};
+	goto again;
+
+esp_handle_done:
+	if(esp->dma_irq_exit)
+		esp->dma_irq_exit(esp);
+	return;
+}
+
+#ifndef __sparc_v9__
+
+#ifndef __SMP__
+void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
+{
+	struct NCR_ESP *esp;
+	int again;
+
+	/* Handle all ESP interrupts showing at this IRQ level. */
+repeat:
+	again = 0;
+	for_each_esp(esp) {
+		if(((esp)->irq & 0xf) == irq) {
+			if(esp->dma_irq_p(esp)) {
+				again = 1;
+
+				esp->dma_ints_off(esp);
+
+				ESPIRQ(("I%d(", esp->esp_id));
+				esp_handle(esp);
+				ESPIRQ((")"));
+
+				esp->dma_ints_on(esp);
+			}
+		}
+	}
+	if(again)
+		goto repeat;
+}
+#else
+/* For SMP we only service one ESP on the list list at our IRQ level! */
+static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
+{
+	struct NCR_ESP *esp;
+
+	/* Handle all ESP interrupts showing at this IRQ level. */
+	for_each_esp(esp) {
+		if(((esp)->irq & 0xf) == irq) {
+			if(esp->dma_irq_p(esp)) {
+				esp->dma_ints_off(esp);
+
+				ESPIRQ(("I[%d:%d](",
+					smp_processor_id(), esp->esp_id));
+				esp_handle(esp);
+				ESPIRQ((")"));
+
+				esp->dma_ints_on(esp);
+				return;
+			}
+		}
+	}
+}
+#endif
+
+#else /* __sparc_v9__ */
+
+static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
+{
+	struct NCR_ESP *esp = dev_id;
+
+	if(esp->dma_irq_p(esp)) {
+		esp->dma_ints_off(dregs);
+
+		ESPIRQ(("I[%d:%d](", smp_processor_id(), esp->esp_id));
+		esp_handle(esp);
+		ESPIRQ((")"));
+
+		esp->dma_ints_on(esp);
+	}
+}
+
+#endif
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/NCR53C9x.h linux/drivers/scsi/NCR53C9x.h
--- v2.1.90/linux/drivers/scsi/NCR53C9x.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/NCR53C9x.h	Sat Mar 21 11:09:47 1998
@@ -0,0 +1,529 @@
+/* NCR53C9x.c:  Defines and structures for the NCR53C9x generic driver.
+ *
+ * Originaly esp.h:  Defines and structures for the Sparc ESP 
+ *                   (Enhanced SCSI Processor) driver under Linux.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Generalization by Jesper Skov (jskov@cygnus.co.uk)
+ */
+
+#ifndef NCR53C9X_H
+#define NCR53C9X_H
+
+/* Macros for debugging messages */
+
+/* #define DEBUG_ESP */
+/* #define DEBUG_ESP_HME */
+/* #define DEBUG_ESP_DATA */
+/* #define DEBUG_ESP_QUEUE */
+/* #define DEBUG_ESP_DISCONNECT */
+/* #define DEBUG_ESP_STATUS */
+/* #define DEBUG_ESP_PHASES */
+/* #define DEBUG_ESP_WORKBUS */
+/* #define DEBUG_STATE_MACHINE */
+/* #define DEBUG_ESP_CMDS */
+/* #define DEBUG_ESP_IRQS */
+/* #define DEBUG_SDTR */
+/* #define DEBUG_ESP_SG */
+
+/* Use the following to sprinkle debugging messages in a way which
+ * suits you if combinations of the above become too verbose when
+ * trying to track down a specific problem.
+ */
+/* #define DEBUG_ESP_MISC */
+
+#if defined(DEBUG_ESP)
+#define ESPLOG(foo)  printk foo
+#else
+#define ESPLOG(foo)
+#endif /* (DEBUG_ESP) */
+
+#if defined(DEBUG_ESP_HME)
+#define ESPHME(foo)  printk foo
+#else
+#define ESPHME(foo)
+#endif
+
+#if defined(DEBUG_ESP_DATA)
+#define ESPDATA(foo)  printk foo
+#else
+#define ESPDATA(foo)
+#endif
+
+#if defined(DEBUG_ESP_QUEUE)
+#define ESPQUEUE(foo)  printk foo
+#else
+#define ESPQUEUE(foo)
+#endif
+
+#if defined(DEBUG_ESP_DISCONNECT)
+#define ESPDISC(foo)  printk foo
+#else
+#define ESPDISC(foo)
+#endif
+
+#if defined(DEBUG_ESP_STATUS)
+#define ESPSTAT(foo)  printk foo
+#else
+#define ESPSTAT(foo)
+#endif
+
+#if defined(DEBUG_ESP_PHASES)
+#define ESPPHASE(foo)  printk foo
+#else
+#define ESPPHASE(foo)
+#endif
+
+#if defined(DEBUG_ESP_WORKBUS)
+#define ESPBUS(foo)  printk foo
+#else
+#define ESPBUS(foo)
+#endif
+
+#if defined(DEBUG_ESP_IRQS)
+#define ESPIRQ(foo)  printk foo
+#else
+#define ESPIRQ(foo)
+#endif
+
+#if defined(DEBUG_SDTR)
+#define ESPSDTR(foo)  printk foo
+#else
+#define ESPSDTR(foo)
+#endif
+
+#if defined(DEBUG_ESP_MISC)
+#define ESPMISC(foo)  printk foo
+#else
+#define ESPMISC(foo)
+#endif
+
+#define INTERNAL_ESP_ERROR \
+        (panic ("Internal ESP driver error in file %s, line %d\n", \
+		__FILE__, __LINE__))
+
+#define INTERNAL_ESP_ERROR_NOPANIC \
+        (printk ("Internal ESP driver error in file %s, line %d\n", \
+		 __FILE__, __LINE__))
+
+/* The ESP SCSI controllers have their register sets in three
+ * "classes":
+ *
+ * 1) Registers which are both read and write.
+ * 2) Registers which are read only.
+ * 3) Registers which are write only.
+ *
+ * Yet, they all live within the same IO space.
+ */
+
+/* All the ESP registers are one byte each and are accessed longwords
+ * apart with a big-endian ordering to the bytes.
+ */
+
+struct ESP_regs {
+                                /* Access    Description              Offset */
+    volatile unchar esp_tclow;  /* rw  Low bits of the transfer count 0x00   */
+                                unchar tlpad1[3];
+    volatile unchar esp_tcmed;  /* rw  Mid bits of the transfer count 0x04   */
+                                unchar fdpad[3];
+    volatile unchar esp_fdata;  /* rw  FIFO data bits                 0x08   */
+                                unchar cbpad[3];
+    volatile unchar esp_cmd;    /* rw  SCSI command bits              0x0c   */
+                                unchar stpad[3];
+    volatile unchar esp_status; /* ro  ESP status register            0x10   */
+#define esp_busid   esp_status  /* wo  Bus ID for select/reselect     0x10   */
+                                unchar irqpd[3];
+    volatile unchar esp_intrpt; /* ro  Kind of interrupt              0x14   */
+#define esp_timeo   esp_intrpt  /* wo  Timeout value for select/resel 0x14   */
+                                unchar sspad[3];
+    volatile unchar esp_sstep;  /* ro  Sequence step register         0x18   */
+#define esp_stp     esp_sstep   /* wo  Transfer period per sync       0x18   */
+                                unchar ffpad[3];
+    volatile unchar esp_fflags; /* ro  Bits of current FIFO info      0x1c   */
+#define esp_soff    esp_fflags  /* wo  Sync offset                    0x1c   */
+                                unchar cf1pd[3];
+    volatile unchar esp_cfg1;   /* rw  First configuration register   0x20   */
+                                unchar cfpad[3];
+    volatile unchar esp_cfact;  /* wo  Clock conversion factor        0x24   */
+#define esp_status2 esp_cfact   /* ro  HME status2 register           0x24   */
+                                unchar ctpad[3];
+    volatile unchar esp_ctest;  /* wo  Chip test register             0x28   */
+                                unchar cf2pd[3];
+    volatile unchar esp_cfg2;   /* rw  Second configuration register  0x2c   */
+                                unchar cf3pd[3];
+
+    /* The following is only found on the 53C9X series SCSI chips */
+    volatile unchar esp_cfg3;   /* rw  Third configuration register   0x30  */
+                                unchar thpd[7];
+
+    /* The following is found on all chips except the NCR53C90 (ESP100) */
+    volatile unchar esp_tchi;   /* rw  High bits of transfer count    0x38  */
+#define esp_uid     esp_tchi    /* ro  Unique ID code                 0x38  */
+#define fas_rlo     esp_tchi    /* rw  HME extended counter           0x38  */
+                                unchar fgpad[3];
+    volatile unchar esp_fgrnd;  /* rw  Data base for fifo             0x3c  */
+#define fas_rhi     esp_fgrnd   /* rw  HME extended counter           0x3c  */
+};
+
+/* Various revisions of the ESP board. */
+enum esp_rev {
+  esp100     = 0x00,  /* NCR53C90 - very broken */
+  esp100a    = 0x01,  /* NCR53C90A */
+  esp236     = 0x02,
+  fas236     = 0x03,
+  fas100a    = 0x04,
+  fast       = 0x05,
+  fashme     = 0x06,
+  espunknown = 0x07
+};
+
+/* We get one of these for each ESP probed. */
+struct NCR_ESP {
+  struct NCR_ESP *next;                   /* Next ESP on probed or NULL */
+  struct ESP_regs *eregs;	          /* All esp registers */
+  struct Linux_DMA *dma;                  /* Who I do transfers with. */
+  void *dregs;		  		  /* And his registers. */
+  struct Scsi_Host *ehost;                /* Backpointer to SCSI Host */
+
+  void *edev;        		          /* Pointer to controller base/SBus */
+  char prom_name[64];                     /* Name of ESP device from prom */
+  int prom_node;                          /* Prom node where ESP found */
+  int esp_id;                             /* Unique per-ESP ID number */
+
+  /* ESP Configuration Registers */
+  unsigned char config1;                  /* Copy of the 1st config register */
+  unsigned char config2;                  /* Copy of the 2nd config register */
+  unsigned char config3[16];              /* Copy of the 3rd config register */
+
+  /* The current command we are sending to the ESP chip.  This esp_command
+   * ptr needs to be mapped in DVMA area so we can send commands and read
+   * from the ESP fifo without burning precious CPU cycles.  Programmed I/O
+   * sucks when we have the DVMA to do it for us.  The ESP is stupid and will
+   * only send out 6, 10, and 12 byte SCSI commands, others we need to send
+   * one byte at a time.  esp_slowcmd being set says that we are doing one
+   * of the command types ESP doesn't understand, esp_scmdp keeps track of
+   * which byte we are sending, esp_scmdleft says how many bytes to go.
+   */
+  volatile unchar *esp_command;           /* Location of command (CPU view)  */
+  __u32            esp_command_dvma;      /* Location of command (DVMA view) */
+  unsigned char esp_clen;                 /* Length of this command */
+  unsigned char esp_slowcmd;
+  unsigned char *esp_scmdp;
+  unsigned char esp_scmdleft;
+
+  /* The following are used to determine the cause of an IRQ. Upon every
+   * IRQ entry we synchronize these with the hardware registers.
+   */
+  unchar ireg;                            /* Copy of ESP interrupt register */
+  unchar sreg;                            /* Same for ESP status register */
+  unchar seqreg;                          /* The ESP sequence register */
+  unchar sreg2;                           /* Copy of HME status2 register */
+
+  /* The HME is the biggest piece of shit I have ever seen. */
+  unchar hme_fifo_workaround_buffer[16 * 2]; /* 16-bit/entry fifo for wide scsi */
+  unchar hme_fifo_workaround_count;
+
+  /* Clock periods, frequencies, synchronization, etc. */
+  unsigned int cfreq;                    /* Clock frequency in HZ */
+  unsigned int cfact;                    /* Clock conversion factor */
+  unsigned int ccycle;                   /* One ESP clock cycle */
+  unsigned int ctick;                    /* One ESP clock time */
+  unsigned int radelay;                  /* FAST chip req/ack delay */
+  unsigned int neg_defp;                 /* Default negotiation period */
+  unsigned int sync_defp;                /* Default sync transfer period */
+  unsigned int max_period;               /* longest our period can be */
+  unsigned int min_period;               /* shortest period we can withstand */
+  unsigned char ccf;			 /* Clock conversion factor */
+  /* For slow to medium speed input clock rates we shoot for 5mb/s,
+   * but for high input clock rates we try to do 10mb/s although I
+   * don't think a transfer can even run that fast with an ESP even
+   * with DMA2 scatter gather pipelining.
+   */
+#define SYNC_DEFP_SLOW            0x32   /* 5mb/s  */
+#define SYNC_DEFP_FAST            0x19   /* 10mb/s */
+
+  unsigned int snip;                      /* Sync. negotiation in progress */
+  unsigned int wnip;                      /* WIDE negotiation in progress */
+  unsigned int targets_present;           /* targets spoken to before */
+
+  int current_transfer_size;              /* Set at beginning of data dma */
+
+  unchar espcmdlog[32];                   /* Log of current esp cmds sent. */
+  unchar espcmdent;                       /* Current entry in esp cmd log. */
+
+  /* Misc. info about this ESP */
+  enum esp_rev erev;                      /* ESP revision */
+  int irq;                                /* SBus IRQ for this ESP */
+  int scsi_id;                            /* Who am I as initiator? */
+  int scsi_id_mask;                       /* Bitmask of 'me'. */
+  int diff;                               /* Differential SCSI bus? */
+  int bursts;                             /* Burst sizes our DVMA supports */
+
+  /* Our command queues, only one cmd lives in the current_SC queue. */
+  Scsi_Cmnd *issue_SC;           /* Commands to be issued */
+  Scsi_Cmnd *current_SC;         /* Who is currently working the bus */
+  Scsi_Cmnd *disconnected_SC;    /* Commands disconnected from the bus */
+
+  /* Message goo */
+  unchar cur_msgout[16];
+  unchar cur_msgin[16];
+  unchar prevmsgout, prevmsgin;
+  unchar msgout_len, msgin_len;
+  unchar msgout_ctr, msgin_ctr;
+
+  /* States that we cannot keep in the per cmd structure because they
+   * cannot be assosciated with any specific command.
+   */
+  unchar resetting_bus;
+
+  unchar do_pio_cmds;		/* Do command transfer with pio */
+
+  /* Functions handling DMA
+   */ 
+  /* Required functions */
+  int  (*dma_bytes_sent)(struct NCR_ESP *, int);
+  int  (*dma_can_transfer)(struct NCR_ESP *, Scsi_Cmnd *);
+  void (*dma_dump_state)(struct NCR_ESP *);
+  void (*dma_init_read)(struct NCR_ESP *, __u32, int);
+  void (*dma_init_write)(struct NCR_ESP *, __u32, int);
+  void (*dma_ints_off)(struct NCR_ESP *);
+  void (*dma_ints_on)(struct NCR_ESP *);
+  int  (*dma_irq_p)(struct NCR_ESP *);
+  int  (*dma_ports_p)(struct NCR_ESP *);
+  void (*dma_setup)(struct NCR_ESP *, __u32, int, int);
+
+  /* Optional functions (i.e. may be initialized to 0) */
+  void (*dma_barrier)(struct NCR_ESP *);
+  void (*dma_drain)(struct NCR_ESP *);
+  void (*dma_invalidate)(struct NCR_ESP *);
+  void (*dma_irq_entry)(struct NCR_ESP *);
+  void (*dma_irq_exit)(struct NCR_ESP *);
+  void (*dma_led_off)(struct NCR_ESP *);
+  void (*dma_led_on)(struct NCR_ESP *);
+  void (*dma_poll)(struct NCR_ESP *, unsigned char *);
+  void (*dma_reset)(struct NCR_ESP *);
+};
+
+/* Bitfield meanings for the above registers. */
+
+/* ESP config reg 1, read-write, found on all ESP chips */
+#define ESP_CONFIG1_ID        0x07             /* My BUS ID bits */
+#define ESP_CONFIG1_CHTEST    0x08             /* Enable ESP chip tests */
+#define ESP_CONFIG1_PENABLE   0x10             /* Enable parity checks */
+#define ESP_CONFIG1_PARTEST   0x20             /* Parity test mode enabled? */
+#define ESP_CONFIG1_SRRDISAB  0x40             /* Disable SCSI reset reports */
+#define ESP_CONFIG1_SLCABLE   0x80             /* Enable slow cable mode */
+
+/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236 chips */
+#define ESP_CONFIG2_DMAPARITY 0x01             /* enable DMA Parity (200,236) */
+#define ESP_CONFIG2_REGPARITY 0x02             /* enable reg Parity (200,236) */
+#define ESP_CONFIG2_BADPARITY 0x04             /* Bad parity target abort  */
+#define ESP_CONFIG2_SCSI2ENAB 0x08             /* Enable SCSI-2 features (tmode only) */
+#define ESP_CONFIG2_HI        0x10             /* High Impedance DREQ ???  */
+#define ESP_CONFIG2_HMEFENAB  0x10             /* HME features enable */
+#define ESP_CONFIG2_BCM       0x20             /* Enable byte-ctrl (236)   */
+#define ESP_CONFIG2_DISPINT   0x20             /* Disable pause irq (hme) */
+#define ESP_CONFIG2_FENAB     0x40             /* Enable features (fas100,esp216)      */
+#define ESP_CONFIG2_SPL       0x40             /* Enable status-phase latch (esp236)   */
+#define ESP_CONFIG2_MKDONE    0x40             /* HME magic feature */
+#define ESP_CONFIG2_HME32     0x80             /* HME 32 extended */
+#define ESP_CONFIG2_MAGIC     0xe0             /* Invalid bits... */
+
+/* ESP config register 3 read-write, found only esp236+fas236+fas100a+hme chips */
+#define ESP_CONFIG3_FCLOCK    0x01             /* FAST SCSI clock rate (esp100a/hme) */
+#define ESP_CONFIG3_TEM       0x01             /* Enable thresh-8 mode (esp/fas236)  */
+#define ESP_CONFIG3_FAST      0x02             /* Enable FAST SCSI     (esp100a/hme) */
+#define ESP_CONFIG3_ADMA      0x02             /* Enable alternate-dma (esp/fas236)  */
+#define ESP_CONFIG3_TENB      0x04             /* group2 SCSI2 support (esp100a/hme) */
+#define ESP_CONFIG3_SRB       0x04             /* Save residual byte   (esp/fas236)  */
+#define ESP_CONFIG3_TMS       0x08             /* Three-byte msg's ok  (esp100a/hme) */
+#define ESP_CONFIG3_FCLK      0x08             /* Fast SCSI clock rate (esp/fas236)  */
+#define ESP_CONFIG3_IDMSG     0x10             /* ID message checking  (esp100a/hme) */
+#define ESP_CONFIG3_FSCSI     0x10             /* Enable FAST SCSI     (esp/fas236)  */
+#define ESP_CONFIG3_GTM       0x20             /* group2 SCSI2 support (esp/fas236)  */
+#define ESP_CONFIG3_BIGID     0x20             /* SCSI-ID's are 4bits  (hme)         */
+#define ESP_CONFIG3_TBMS      0x40             /* Three-byte msg's ok  (esp/fas236)  */
+#define ESP_CONFIG3_EWIDE     0x40             /* Enable Wide-SCSI     (hme)         */
+#define ESP_CONFIG3_IMS       0x80             /* ID msg chk'ng        (esp/fas236)  */
+#define ESP_CONFIG3_OBPUSH    0x80             /* Push odd-byte to dma (hme)         */
+
+/* ESP command register read-write */
+/* Group 1 commands:  These may be sent at any point in time to the ESP
+ *                    chip.  None of them can generate interrupts 'cept
+ *                    the "SCSI bus reset" command if you have not disabled
+ *                    SCSI reset interrupts in the config1 ESP register.
+ */
+#define ESP_CMD_NULL          0x00             /* Null command, ie. a nop */
+#define ESP_CMD_FLUSH         0x01             /* FIFO Flush */
+#define ESP_CMD_RC            0x02             /* Chip reset */
+#define ESP_CMD_RS            0x03             /* SCSI bus reset */
+
+/* Group 2 commands:  ESP must be an initiator and connected to a target
+ *                    for these commands to work.
+ */
+#define ESP_CMD_TI            0x10             /* Transfer Information */
+#define ESP_CMD_ICCSEQ        0x11             /* Initiator cmd complete sequence */
+#define ESP_CMD_MOK           0x12             /* Message okie-dokie */
+#define ESP_CMD_TPAD          0x18             /* Transfer Pad */
+#define ESP_CMD_SATN          0x1a             /* Set ATN */
+#define ESP_CMD_RATN          0x1b             /* De-assert ATN */
+
+/* Group 3 commands:  ESP must be in the MSGOUT or MSGIN state and be connected
+ *                    to a target as the initiator for these commands to work.
+ */
+#define ESP_CMD_SMSG          0x20             /* Send message */
+#define ESP_CMD_SSTAT         0x21             /* Send status */
+#define ESP_CMD_SDATA         0x22             /* Send data */
+#define ESP_CMD_DSEQ          0x23             /* Discontinue Sequence */
+#define ESP_CMD_TSEQ          0x24             /* Terminate Sequence */
+#define ESP_CMD_TCCSEQ        0x25             /* Target cmd cmplt sequence */
+#define ESP_CMD_DCNCT         0x27             /* Disconnect */
+#define ESP_CMD_RMSG          0x28             /* Receive Message */
+#define ESP_CMD_RCMD          0x29             /* Receive Command */
+#define ESP_CMD_RDATA         0x2a             /* Receive Data */
+#define ESP_CMD_RCSEQ         0x2b             /* Receive cmd sequence */
+
+/* Group 4 commands:  The ESP must be in the disconnected state and must
+ *                    not be connected to any targets as initiator for
+ *                    these commands to work.
+ */
+#define ESP_CMD_RSEL          0x40             /* Reselect */
+#define ESP_CMD_SEL           0x41             /* Select w/o ATN */
+#define ESP_CMD_SELA          0x42             /* Select w/ATN */
+#define ESP_CMD_SELAS         0x43             /* Select w/ATN & STOP */
+#define ESP_CMD_ESEL          0x44             /* Enable selection */
+#define ESP_CMD_DSEL          0x45             /* Disable selections */
+#define ESP_CMD_SA3           0x46             /* Select w/ATN3 */
+#define ESP_CMD_RSEL3         0x47             /* Reselect3 */
+
+/* This bit enables the ESP's DMA on the SBus */
+#define ESP_CMD_DMA           0x80             /* Do DMA? */
+
+
+/* ESP status register read-only */
+#define ESP_STAT_PIO          0x01             /* IO phase bit */
+#define ESP_STAT_PCD          0x02             /* CD phase bit */
+#define ESP_STAT_PMSG         0x04             /* MSG phase bit */
+#define ESP_STAT_PMASK        0x07             /* Mask of phase bits */
+#define ESP_STAT_TDONE        0x08             /* Transfer Completed */
+#define ESP_STAT_TCNT         0x10             /* Transfer Counter Is Zero */
+#define ESP_STAT_PERR         0x20             /* Parity error */
+#define ESP_STAT_SPAM         0x40             /* Real bad error */
+/* This indicates the 'interrupt pending' condition on esp236, it is a reserved
+ * bit on other revs of the ESP.
+ */
+#define ESP_STAT_INTR         0x80             /* Interrupt */
+
+/* HME only: status 2 register */
+#define ESP_STAT2_SCHBIT      0x01 /* Upper bits 3-7 of sstep enabled */
+#define ESP_STAT2_FFLAGS      0x02 /* The fifo flags are now latched */
+#define ESP_STAT2_XCNT        0x04 /* The transfer counter is latched */
+#define ESP_STAT2_CREGA       0x08 /* The command reg is active now */
+#define ESP_STAT2_WIDE        0x10 /* Interface on this adapter is wide */
+#define ESP_STAT2_F1BYTE      0x20 /* There is one byte at top of fifo */
+#define ESP_STAT2_FMSB        0x40 /* Next byte in fifo is most significant */
+#define ESP_STAT2_FEMPTY      0x80 /* FIFO is empty */
+
+/* The status register can be masked with ESP_STAT_PMASK and compared
+ * with the following values to determine the current phase the ESP
+ * (at least thinks it) is in.  For our purposes we also add our own
+ * software 'done' bit for our phase management engine.
+ */
+#define ESP_DOP   (0)                                       /* Data Out  */
+#define ESP_DIP   (ESP_STAT_PIO)                            /* Data In   */
+#define ESP_CMDP  (ESP_STAT_PCD)                            /* Command   */
+#define ESP_STATP (ESP_STAT_PCD|ESP_STAT_PIO)               /* Status    */
+#define ESP_MOP   (ESP_STAT_PMSG|ESP_STAT_PCD)              /* Message Out */
+#define ESP_MIP   (ESP_STAT_PMSG|ESP_STAT_PCD|ESP_STAT_PIO) /* Message In */
+
+/* ESP interrupt register read-only */
+#define ESP_INTR_S            0x01             /* Select w/o ATN */
+#define ESP_INTR_SATN         0x02             /* Select w/ATN */
+#define ESP_INTR_RSEL         0x04             /* Reselected */
+#define ESP_INTR_FDONE        0x08             /* Function done */
+#define ESP_INTR_BSERV        0x10             /* Bus service */
+#define ESP_INTR_DC           0x20             /* Disconnect */
+#define ESP_INTR_IC           0x40             /* Illegal command given */
+#define ESP_INTR_SR           0x80             /* SCSI bus reset detected */
+
+/* Interrupt status macros */
+#define ESP_SRESET_IRQ(esp)  ((esp)->intreg & (ESP_INTR_SR))
+#define ESP_ILLCMD_IRQ(esp)  ((esp)->intreg & (ESP_INTR_IC))
+#define ESP_SELECT_WITH_ATN_IRQ(esp)     ((esp)->intreg & (ESP_INTR_SATN))
+#define ESP_SELECT_WITHOUT_ATN_IRQ(esp)  ((esp)->intreg & (ESP_INTR_S))
+#define ESP_SELECTION_IRQ(esp)  ((ESP_SELECT_WITH_ATN_IRQ(esp)) ||         \
+				 (ESP_SELECT_WITHOUT_ATN_IRQ(esp)))
+#define ESP_RESELECTION_IRQ(esp)         ((esp)->intreg & (ESP_INTR_RSEL))
+
+/* ESP sequence step register read-only */
+#define ESP_STEP_VBITS        0x07             /* Valid bits */
+#define ESP_STEP_ASEL         0x00             /* Selection&Arbitrate cmplt */
+#define ESP_STEP_SID          0x01             /* One msg byte sent */
+#define ESP_STEP_NCMD         0x02             /* Was not in command phase */
+#define ESP_STEP_PPC          0x03             /* Early phase chg caused cmnd
+                                                * bytes to be lost
+                                                */
+#define ESP_STEP_FINI4        0x04             /* Command was sent ok */
+
+/* Ho hum, some ESP's set the step register to this as well... */
+#define ESP_STEP_FINI5        0x05
+#define ESP_STEP_FINI6        0x06
+#define ESP_STEP_FINI7        0x07
+
+/* ESP chip-test register read-write */
+#define ESP_TEST_TARG         0x01             /* Target test mode */
+#define ESP_TEST_INI          0x02             /* Initiator test mode */
+#define ESP_TEST_TS           0x04             /* Tristate test mode */
+
+/* ESP unique ID register read-only, found on fas236+fas100a only */
+#define ESP_UID_F100A         0x00             /* ESP FAS100A  */
+#define ESP_UID_F236          0x02             /* ESP FAS236   */
+#define ESP_UID_REV           0x07             /* ESP revision */
+#define ESP_UID_FAM           0xf8             /* ESP family   */
+
+/* ESP fifo flags register read-only */
+/* Note that the following implies a 16 byte FIFO on the ESP. */
+#define ESP_FF_FBYTES         0x1f             /* Num bytes in FIFO */
+#define ESP_FF_ONOTZERO       0x20             /* offset ctr not zero (esp100) */
+#define ESP_FF_SSTEP          0xe0             /* Sequence step */
+
+/* ESP clock conversion factor register write-only */
+#define ESP_CCF_F0            0x00             /* 35.01MHz - 40MHz */
+#define ESP_CCF_NEVER         0x01             /* Set it to this and die */
+#define ESP_CCF_F2            0x02             /* 10MHz */
+#define ESP_CCF_F3            0x03             /* 10.01MHz - 15MHz */
+#define ESP_CCF_F4            0x04             /* 15.01MHz - 20MHz */
+#define ESP_CCF_F5            0x05             /* 20.01MHz - 25MHz */
+#define ESP_CCF_F6            0x06             /* 25.01MHz - 30MHz */
+#define ESP_CCF_F7            0x07             /* 30.01MHz - 35MHz */
+
+/* HME only... */
+#define ESP_BUSID_RESELID     0x10
+#define ESP_BUSID_CTR32BIT    0x40
+
+#define ESP_BUS_TIMEOUT        275             /* In milli-seconds */
+#define ESP_TIMEO_CONST       8192
+#define ESP_NEG_DEFP(mhz, cfact) \
+        ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact)))
+#define ESP_MHZ_TO_CYCLE(mhertz)  ((1000000000) / ((mhertz) / 1000))
+#define ESP_TICK(ccf, cycle)  ((7682 * (ccf) * (cycle) / 1000))
+
+
+extern struct proc_dir_entry proc_scsi_esp;
+
+/* UGLY, UGLY, UGLY! */
+extern int nesps, esps_in_use, esps_running;
+
+/* For our interrupt engine. */
+#define for_each_esp(esp) \
+        for((esp) = espchain; (esp); (esp) = (esp)->next)
+
+
+/* External functions */
+extern inline void esp_cmd(struct NCR_ESP *esp, struct ESP_regs *eregs,
+			   unchar cmd);
+extern struct NCR_ESP *esp_allocate(Scsi_Host_Template *, void *);
+extern void esp_initialize(struct NCR_ESP *);
+extern void esp_intr(int, void *, struct pt_regs *);
+#endif /* !(NCR53C9X_H) */
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/a2091.c linux/drivers/scsi/a2091.c
--- v2.1.90/linux/drivers/scsi/a2091.c	Tue May 13 22:41:13 1997
+++ linux/drivers/scsi/a2091.c	Sat Mar 21 11:09:47 1998
@@ -196,8 +196,8 @@
     static unsigned char called = 0;
     struct Scsi_Host *instance;
     caddr_t address;
-    int key;
-    struct ConfigDev *cd;
+    unsigned int key;
+    const struct ConfigDev *cd;
 
     if (!MACH_IS_AMIGA || called)
 	return 0;
@@ -206,8 +206,8 @@
     tpnt->proc_dir = &proc_scsi_a2091;
     tpnt->proc_info = &wd33c93_proc_info;
 
-    while ((key = zorro_find(MANUF_COMMODORE, PROD_A2091, 0, 0)) ||
-	   (key = zorro_find(MANUF_COMMODORE, PROD_A590, 0, 0))) {
+    while ((key = zorro_find(ZORRO_PROD_CBM_A590_A2091_1, 0, 0)) ||
+	   (key = zorro_find(ZORRO_PROD_CBM_A590_A2091_2, 0, 0))) {
 	cd = zorro_get_board(key);
 	address = cd->cd_BoardAddr;
 	instance = scsi_register (tpnt, sizeof (struct WD33C93_hostdata));
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/amiga7xx.c linux/drivers/scsi/amiga7xx.c
--- v2.1.90/linux/drivers/scsi/amiga7xx.c	Mon Jul  7 08:24:28 1997
+++ linux/drivers/scsi/amiga7xx.c	Sat Mar 21 11:09:47 1998
@@ -33,14 +33,17 @@
     S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
 
+extern ncr53c7xx_init (Scsi_Host_Template *tpnt, int board, int chip, 
+		       u32 base, int io_port, int irq, int dma,
+		       long long options, int clock);
+
 int amiga7xx_detect(Scsi_Host_Template *tpnt)
 {
     static unsigned char called = 0;
-    int key, clock;
-    int num = 0;
-    unsigned long address;
+    unsigned int key;
+    int num = 0, clock;
     long long options;
-    struct ConfigDev *cd;
+    const struct ConfigDev *cd;
 
     if (called || !MACH_IS_AMIGA)
 	return 0;
@@ -48,8 +51,9 @@
     tpnt->proc_dir = &proc_scsi_amiga7xx;
 
 #ifdef CONFIG_WARPENGINE_SCSI
-    if ((key = zorro_find(MANUF_MACROSYSTEMS, PROD_WARP_ENGINE, 0, 0)))
+    if ((key = zorro_find(ZORRO_PROD_MACROSYSTEMS_WARP_ENGINE_40xx, 0, 0)))
     {
+	unsigned long address;
 	cd = zorro_get_board(key);
 	address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr,
 		cd->cd_BoardSize, KERNELMAP_NOCACHE_SER, NULL);
@@ -59,7 +63,8 @@
 	clock = 50000000;	/* 50MHz SCSI Clock */
 
 	ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(address + 0x40000),
-			0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock);
+			0, IRQ_AMIGA_PORTS, DMA_NONE, 
+			options, clock);
 
 	zorro_config_board(key, 0);
 	num++;
@@ -73,17 +78,18 @@
 
 	clock = 50000000;	/* 50MHz SCSI Clock */
 
-    	ncr53c7xx_init(tpnt, 0, 710,
-		       (u32)(unsigned char *)ZTWO_VADDR(0xDD0040),
-		       0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock);
+    	ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)ZTWO_VADDR(0xDD0040),
+			0, IRQ_AMIGA_PORTS, DMA_NONE,
+			options, clock);
     	num++;
     }
 #endif
 
 #ifdef CONFIG_A4091_SCSI
-    while ( (key = zorro_find(MANUF_COMMODORE, PROD_A4091, 0, 0)) ||
-	    (key = zorro_find(MANUF_COMMODORE2, PROD_A4091_2, 0, 0)) )
+    while ( (key = zorro_find(ZORRO_PROD_CBM_A4091_1, 0, 0)) ||
+	    (key = zorro_find(ZORRO_PROD_CBM_A4091_2, 0, 0)) )
     {
+	unsigned long address;
 	cd = zorro_get_board(key);
 	address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr,
 		cd->cd_BoardSize, KERNELMAP_NOCACHE_SER, NULL);
@@ -93,11 +99,31 @@
 	clock = 50000000;	/* 50MHz SCSI Clock */
 
     	ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(address+0x800000),
-			0, IRQ_AMIGA_PORTS, DMA_NONE, 
-			options, clock);
+			0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock);
 
 	zorro_config_board(key, 0);
 	num++;
+    }
+#endif
+
+#ifdef CONFIG_GVP_TURBO_SCSI
+    if((key = zorro_find(ZORRO_PROD_GVP_GFORCE_040_060, 0, 0)))
+    {
+	    cd = zorro_get_board(key);
+	    address = ZTWO_VADDR((unsigned long)cd->cd_BoardAddr);
+
+	    options = OPTION_MEMORY_MAPPED | OPTION_DEBUG_TEST1 |
+		      OPTION_INTFLY | OPTION_SYNCHRONOUS |
+		      OPTION_ALWAYS_SYNCHRONOUS | OPTION_DISCONNECT;
+
+	    clock = 50000000;	/* 50MHz SCSI Clock */
+
+	    ncr53c7xx_init(tpnt, 0, 710,
+			   (u32)(unsigned char *)(address + 0x40000),
+			   0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock);
+
+	    zorro_config_board(key, 0);
+	    num++;
     }
 #endif
 
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/amiga7xx.h linux/drivers/scsi/amiga7xx.h
--- v2.1.90/linux/drivers/scsi/amiga7xx.h	Fri Feb  6 15:33:40 1998
+++ linux/drivers/scsi/amiga7xx.h	Sat Mar 21 11:09:47 1998
@@ -6,7 +6,7 @@
 const char *NCR53c7x0_info(void);
 int NCR53c7xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int NCR53c7xx_abort(Scsi_Cmnd *);
-int NCR53c7x0_release (Scsi_Host_Template *);
+int NCR53c7x0_release (struct Scsi_Host *);
 int NCR53c7xx_reset(Scsi_Cmnd *, unsigned int);
 void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
 
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/atari_dma_emul.c linux/drivers/scsi/atari_dma_emul.c
--- v2.1.90/linux/drivers/scsi/atari_dma_emul.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/atari_dma_emul.c	Sat Mar 21 11:09:47 1998
@@ -0,0 +1,466 @@
+/*
+ * atari_dma_emul.c -- TT SCSI DMA emulator for the Hades.
+ *
+ * Copyright 1997 Wout Klaren <W.Klaren@inter.nl.net>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ * This code was written using the Hades TOS source code as a
+ * reference. This source code can be found on the home page
+ * of Medusa Computer Systems.
+ *
+ * Version 0.1, 1997-09-24.
+ * 
+ * This code should be considered experimental. It has only been
+ * tested on a Hades with a 68060. It might not work on a Hades
+ * with a 68040. Make backups of your hard drives before using
+ * this code.
+ */
+
+#include <asm/uaccess.h>
+
+#define hades_dma_ctrl		(*(unsigned char *) 0xffff8717)
+#define hades_psdm_reg		(*(unsigned char *) 0xffff8741)
+
+#define TRANSFER_SIZE		16
+
+struct m68040_frame {
+	unsigned long  effaddr;  /* effective address */
+	unsigned short ssw;      /* special status word */
+	unsigned short wb3s;     /* write back 3 status */
+	unsigned short wb2s;     /* write back 2 status */
+	unsigned short wb1s;     /* write back 1 status */
+	unsigned long  faddr;    /* fault address */
+	unsigned long  wb3a;     /* write back 3 address */
+	unsigned long  wb3d;     /* write back 3 data */
+	unsigned long  wb2a;     /* write back 2 address */
+	unsigned long  wb2d;     /* write back 2 data */
+	unsigned long  wb1a;     /* write back 1 address */
+	unsigned long  wb1dpd0;  /* write back 1 data/push data 0*/
+	unsigned long  pd1;      /* push data 1*/
+	unsigned long  pd2;      /* push data 2*/
+	unsigned long  pd3;      /* push data 3*/
+};
+
+static void writeback (unsigned short wbs, unsigned long wba,
+		       unsigned long wbd, void *old_buserr)
+{
+	mm_segment_t fs = get_fs();
+	static void *save_buserr;
+
+	__asm__ __volatile__ ("movec.l	%%vbr,%%a0\n\t"
+			      "move.l	%0,8(%%a0)\n\t"
+			      :
+			      : "r" (&&bus_error)
+			      : "a0" );
+
+	save_buserr = old_buserr;
+
+	set_fs (MAKE_MM_SEG(wbs & WBTM_040));
+
+	switch (wbs & WBSIZ_040) {
+	    case BA_SIZE_BYTE:
+		put_user (wbd & 0xff, (char *)wba);
+		break;
+	    case BA_SIZE_WORD:
+		put_user (wbd & 0xffff, (short *)wba);
+		break;
+	    case BA_SIZE_LONG:
+		put_user (wbd, (int *)wba);
+		break;
+	}
+
+	set_fs (fs);
+	return;
+
+bus_error:
+	__asm__ __volatile__ ("cmp.l	%0,2(%%sp)\n\t"
+			      "bcs.s	.jump_old\n\t"
+			      "cmp.l	%1,2(%%sp)\n\t"
+			      "bls.s	.restore_old\n"
+			".jump_old:\n\t"
+			      "move.l	%2,-(%%sp)\n\t"
+			      "rts\n"
+			".restore_old:\n\t"
+			      "move.l	%%a0,-(%%sp)\n\t"
+			      "movec.l	%%vbr,%%a0\n\t"
+			      "move.l	%2,8(%%a0)\n\t"
+			      "move.l	(%%sp)+,%%a0\n\t"
+			      "rte\n\t"
+			      :
+			      : "i" (writeback), "i" (&&bus_error),
+			        "m" (save_buserr) );
+}
+
+/*
+ * static inline void set_restdata_reg(unsigned char *cur_addr)
+ *
+ * Set the rest data register if necessary.
+ */
+
+static inline void set_restdata_reg(unsigned char *cur_addr)
+{
+	if (((long) cur_addr & ~3) != 0)
+		tt_scsi_dma.dma_restdata =
+			*((unsigned long *) ((long) cur_addr & ~3));
+}
+
+/*
+ * void hades_dma_emulator(int irq, void *dummy, struct pt_regs *fp)
+ * 
+ * This code emulates TT SCSI DMA on the Hades.
+ * 
+ * Note the following:
+ * 
+ * 1. When there is no byte available to read from the SCSI bus, or
+ *    when a byte cannot yet bet written to the SCSI bus, a bus
+ *    error occurs when reading or writing the pseudo DMA data
+ *    register (hades_psdm_reg). We have to catch this bus error
+ *    and try again to read or write the byte. If after several tries
+ *    we still get a bus error, the interrupt handler is left. When
+ *    the byte can be read or written, the interrupt handler is
+ *    called again.
+ * 
+ * 2. The SCSI interrupt must be disabled in this interrupt handler.
+ * 
+ * 3. If we set the EOP signal, the SCSI controller still expects one
+ *    byte to be read or written. Therefore the last byte is transferred
+ *    separately, after setting the EOP signal.
+ * 
+ * 4. When this function is left, the address pointer (start_addr) is
+ *    converted to a physical address. Because it points one byte
+ *    further than the last transfered byte, it can point outside the
+ *    current page. If virt_to_phys() is called with this address we
+ *    might get an access error. Therefore virt_to_phys() is called with
+ *    start_addr - 1 if the count has reached zero. The result is
+ *    increased with one.
+ */
+
+static void hades_dma_emulator(int irq, void *dummy, struct pt_regs *fp)
+{
+	unsigned long dma_base;
+	register unsigned long dma_cnt asm ("d3");
+	static long save_buserr;
+	register unsigned long save_sp asm ("d4");
+	register int tries asm ("d5");
+	register unsigned char *start_addr asm ("a3"), *end_addr asm ("a4");
+	register unsigned char *eff_addr;
+	register unsigned char *psdm_reg;
+	unsigned long rem;
+
+	atari_disable_irq(IRQ_TT_MFP_SCSI);
+
+	/*
+	 * Read the dma address and count registers.
+	 */
+
+	dma_base = SCSI_DMA_READ_P(dma_addr);
+	dma_cnt = SCSI_DMA_READ_P(dma_cnt);
+
+	/*
+	 * Check if DMA is still enabled.
+	 */
+
+	if ((tt_scsi_dma.dma_ctrl & 2) == 0)
+	{
+		atari_enable_irq(IRQ_TT_MFP_SCSI);
+		return;
+	}
+
+	if (dma_cnt == 0)
+	{
+		printk(KERN_NOTICE "DMA emulation: count is zero.\n");
+		tt_scsi_dma.dma_ctrl &= 0xfd;	/* DMA ready. */
+		atari_enable_irq(IRQ_TT_MFP_SCSI);
+		return;
+	}
+
+	/*
+	 * Install new bus error routine.
+	 */
+
+	__asm__ __volatile__ ("movec.l	%%vbr,%%a0\n\t"
+			      "move.l	8(%%a0),%0\n\t"
+			      "move.l	%1,8(%%a0)\n\t"
+			      : "=&r" (save_buserr)
+			      : "r" (&&scsi_bus_error)
+			      : "a0" );
+
+	hades_dma_ctrl &= 0xfc;		/* Bus error and EOP off. */
+
+	/*
+	 * Save the stack pointer.
+	 */
+
+	__asm__ __volatile__ ("move.l	%%sp,%0\n\t"
+			      : "=&r" (save_sp) );
+
+	tries = 100;			/* Maximum number of bus errors. */
+	start_addr = phys_to_virt(dma_base);
+	end_addr = start_addr + dma_cnt;
+
+scsi_loop:
+	dma_cnt--;
+	rem = dma_cnt & (TRANSFER_SIZE - 1);
+	dma_cnt &= ~(TRANSFER_SIZE - 1);
+	psdm_reg = &hades_psdm_reg;
+
+	if (tt_scsi_dma.dma_ctrl & 1)	/* Read or write? */
+	{
+		/*
+		 * SCSI write. Abort when count is zero.
+		 */
+
+		switch (rem)
+		{
+		case 0:
+			while (dma_cnt > 0)
+			{
+				dma_cnt -= TRANSFER_SIZE;
+
+				*psdm_reg = *start_addr++;
+		case 15:
+				*psdm_reg = *start_addr++;
+		case 14:
+				*psdm_reg = *start_addr++;
+		case 13:
+				*psdm_reg = *start_addr++;
+		case 12:
+				*psdm_reg = *start_addr++;
+		case 11:
+				*psdm_reg = *start_addr++;
+		case 10:
+				*psdm_reg = *start_addr++;
+		case 9:
+				*psdm_reg = *start_addr++;
+		case 8:
+				*psdm_reg = *start_addr++;
+		case 7:
+				*psdm_reg = *start_addr++;
+		case 6:
+				*psdm_reg = *start_addr++;
+		case 5:
+				*psdm_reg = *start_addr++;
+		case 4:
+				*psdm_reg = *start_addr++;
+		case 3:
+				*psdm_reg = *start_addr++;
+		case 2:
+				*psdm_reg = *start_addr++;
+		case 1:
+				*psdm_reg = *start_addr++;
+			}
+		}
+
+		hades_dma_ctrl |= 1;	/* Set EOP. */
+		udelay(10);
+		*psdm_reg = *start_addr++;	/* Dummy byte. */
+		tt_scsi_dma.dma_ctrl &= 0xfd;	/* DMA ready. */
+	}
+	else
+	{
+		/*
+		 * SCSI read. Abort when count is zero.
+		 */
+
+		switch (rem)
+		{
+		case 0:
+			while (dma_cnt > 0)
+			{
+				dma_cnt -= TRANSFER_SIZE;
+
+				*start_addr++ = *psdm_reg;
+		case 15:
+				*start_addr++ = *psdm_reg;
+		case 14:
+				*start_addr++ = *psdm_reg;
+		case 13:
+				*start_addr++ = *psdm_reg;
+		case 12:
+				*start_addr++ = *psdm_reg;
+		case 11:
+				*start_addr++ = *psdm_reg;
+		case 10:
+				*start_addr++ = *psdm_reg;
+		case 9:
+				*start_addr++ = *psdm_reg;
+		case 8:
+				*start_addr++ = *psdm_reg;
+		case 7:
+				*start_addr++ = *psdm_reg;
+		case 6:
+				*start_addr++ = *psdm_reg;
+		case 5:
+				*start_addr++ = *psdm_reg;
+		case 4:
+				*start_addr++ = *psdm_reg;
+		case 3:
+				*start_addr++ = *psdm_reg;
+		case 2:
+				*start_addr++ = *psdm_reg;
+		case 1:
+				*start_addr++ = *psdm_reg;
+			}
+		}
+
+		hades_dma_ctrl |= 1;	/* Set EOP. */
+		udelay(10);
+		*start_addr++ = *psdm_reg;
+		tt_scsi_dma.dma_ctrl &= 0xfd;	/* DMA ready. */
+
+		set_restdata_reg(start_addr);
+	}
+
+	if (start_addr != end_addr)
+		printk(KERN_CRIT "DMA emulation: FATAL: Count is not zero at end of transfer.\n");
+
+	dma_cnt = end_addr - start_addr;
+
+scsi_end:
+	dma_base = (dma_cnt == 0) ? virt_to_phys(start_addr - 1) + 1 :  
+				    virt_to_phys(start_addr);
+
+	SCSI_DMA_WRITE_P(dma_addr, dma_base);
+	SCSI_DMA_WRITE_P(dma_cnt, dma_cnt);
+
+	/*
+	 * Restore old bus error routine.
+	 */
+
+	__asm__ __volatile__ ("movec.l	%%vbr,%%a0\n\t"
+			      "move.l	%0,8(%%a0)\n\t"
+			      :
+			      : "r" (save_buserr)
+			      : "a0" );
+
+	atari_enable_irq(IRQ_TT_MFP_SCSI);
+
+	return;
+
+scsi_bus_error:
+	/*
+	 * First check if the bus error is caused by our code.
+	 * If not, call the original handler.
+	 */
+
+	__asm__ __volatile__ ("cmp.l	%0,2(%%sp)\n\t"
+			      "bcs.s	.old_vector\n\t"
+			      "cmp.l	%1,2(%%sp)\n\t"
+			      "bls.s	.scsi_buserr\n"
+			".old_vector:\n\t"
+			      "move.l	%2,-(%%sp)\n\t"
+			      "rts\n"
+			".scsi_buserr:\n\t"
+			      :
+			      : "i" (&&scsi_loop), "i" (&&scsi_end),
+			        "m" (save_buserr) );
+
+	if (CPU_IS_060)
+	{
+		/*
+		 * Get effective address and restore the stack.
+		 */
+
+		__asm__ __volatile__ ("move.l	8(%%sp),%0\n\t"
+				      "move.l	%1,%%sp\n\t"
+				      : "=a&" (eff_addr)
+				      : "r" (save_sp) );
+	}
+	else
+	{
+		register struct m68040_frame *frame;
+
+		__asm__ __volatile__ ("lea	8(%%sp),%0\n\t"
+				      : "=a&" (frame) );
+
+		if (tt_scsi_dma.dma_ctrl & 1)
+		{
+			/*
+			 * Bus error while writing.
+			 */
+
+			if (frame->wb3s & WBV_040)
+			{
+				if (frame->wb3a == (long) &hades_psdm_reg)
+					start_addr--;
+				else
+					writeback(frame->wb3s, frame->wb3a,
+						  frame->wb3d, &&scsi_bus_error);
+			}
+
+			if (frame->wb2s & WBV_040)
+			{
+				if (frame->wb2a == (long) &hades_psdm_reg)
+					start_addr--;
+				else
+					writeback(frame->wb2s, frame->wb2a,
+						  frame->wb2d, &&scsi_bus_error);
+			}
+
+			if (frame->wb1s & WBV_040)
+			{
+				if (frame->wb1a == (long) &hades_psdm_reg)
+					start_addr--;
+			}
+		}
+		else
+		{
+			/*
+			 * Bus error while reading.
+			 */
+
+			if (frame->wb3s & WBV_040)
+				writeback(frame->wb3s, frame->wb3a,
+					  frame->wb3d, &&scsi_bus_error);
+		}
+
+		eff_addr = (unsigned char *) frame->faddr;
+
+		__asm__ __volatile__ ("move.l	%0,%%sp\n\t"
+				      :
+				      : "r" (save_sp) );
+	}
+
+	dma_cnt = end_addr - start_addr;
+
+	if (eff_addr == &hades_psdm_reg)
+	{
+		/*
+		 * Bus error occured while reading the pseudo
+		 * DMA register. Time out.
+		 */
+
+		tries--;
+
+		if (tries <= 0)
+		{
+			if ((tt_scsi_dma.dma_ctrl & 1) == 0)	/* Read or write? */
+				set_restdata_reg(start_addr);
+
+			if (dma_cnt <= 1)
+				printk(KERN_CRIT "DMA emulation: Fatal "
+				       "error while %s the last byte.\n",
+				       (tt_scsi_dma.dma_ctrl & 1)
+				       ? "writing" : "reading");
+
+			goto scsi_end;
+		}
+		else
+			goto scsi_loop;
+	}
+	else
+	{
+		/*
+		 * Bus error during pseudo DMA transfer.
+		 * Terminate the DMA transfer.
+		 */
+
+		hades_dma_ctrl |= 3;	/* Set EOP and bus error. */
+		if ((tt_scsi_dma.dma_ctrl & 1) == 0)	/* Read or write? */
+			set_restdata_reg(start_addr);
+		goto scsi_end;
+	}
+}
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/atari_scsi.c linux/drivers/scsi/atari_scsi.c
--- v2.1.90/linux/drivers/scsi/atari_scsi.c	Thu Jul 31 13:09:17 1997
+++ linux/drivers/scsi/atari_scsi.c	Sat Mar 21 11:09:47 1998
@@ -107,6 +107,7 @@
 #include "NCR5380.h"
 #include "constants.h"
 #include <asm/atari_stdma.h>
+#include <asm/atari_stram.h>
 #include <asm/io.h>
 
 #include <linux/stat.h>
@@ -255,6 +256,10 @@
 MODULE_PARM(setup_hostid, "i");
 
 
+#if defined(CONFIG_TT_DMA_EMUL)
+#include "atari_dma_emul.c"
+#endif
+
 #if defined(REAL_DMA)
 
 static int scsi_dma_is_ignored_buserr( unsigned char dma_stat )
@@ -652,8 +657,12 @@
 	 */
 	if (MACH_IS_ATARI && ATARIHW_PRESENT(ST_SCSI) &&
 	    !ATARIHW_PRESENT(EXTD_DMA) && m68k_num_memory > 1) {
-		atari_dma_buffer = scsi_init_malloc(STRAM_BUFFER_SIZE,
-						    GFP_ATOMIC | GFP_DMA);
+		atari_dma_buffer = atari_stram_alloc( STRAM_BUFFER_SIZE, NULL, "SCSI" );
+		if (!atari_dma_buffer) {
+			printk( KERN_ERR "atari_scsi_detect: can't allocate ST-RAM "
+					"double buffer\n" );
+			return( 0 );
+		}
 		atari_dma_phys_buffer = VTOP( atari_dma_buffer );
 		atari_dma_orig_addr = 0;
 	}
@@ -682,7 +691,14 @@
 		atari_dma_residual = 0;
 #endif /* REAL_DMA */
 #ifdef REAL_DMA
-		if (is_medusa || is_hades) {
+#ifdef CONFIG_TT_DMA_EMUL
+		if (MACH_IS_HADES) {
+			request_irq(IRQ_AUTO_2, hades_dma_emulator,
+				    IRQ_TYPE_PRIO, "Hades DMA emulator",
+				    hades_dma_emulator);
+		}
+#endif
+		if (MACH_IS_MEDUSA || MACH_IS_HADES) {
 			/* While the read overruns (described by Drew Eckhardt in
 			 * NCR5380.c) never happened on TTs, they do in fact on the Medusa
 			 * (This was the cause why SCSI didn't work right for so long
@@ -739,7 +755,7 @@
 	if (IS_A_TT())
 		free_irq(IRQ_TT_MFP_SCSI, scsi_tt_intr);
 	if (atari_dma_buffer)
-		scsi_init_free (atari_dma_buffer, STRAM_BUFFER_SIZE);
+		atari_stram_free (atari_dma_buffer);
 	return 1;
 }
 #endif
@@ -1003,11 +1019,11 @@
 					int write_flag )
 {
 	unsigned long	possible_len, limit;
-
-	if (is_hades)
+#ifndef CONFIG_TT_DMA_EMUL
+	if (MACH_IS_HADES)
 		/* Hades has no SCSI DMA at all :-( Always force use of PIO */
 		return( 0 );
-	
+#endif	
 	if (IS_A_TT())
 		/* TT SCSI DMA can transfer arbitrary #bytes */
 		return( wanted_len );
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/gvp11.c linux/drivers/scsi/gvp11.c
--- v2.1.90/linux/drivers/scsi/gvp11.c	Tue May 13 22:41:13 1997
+++ linux/drivers/scsi/gvp11.c	Sat Mar 21 11:09:47 1998
@@ -199,14 +199,22 @@
 
 static int num_gvp11 = 0;
 
+#define CHECK_WD33C93
+
 __initfunc(int gvp11_detect(Scsi_Host_Template *tpnt))
 {
     static unsigned char called = 0;
     struct Scsi_Host *instance;
     caddr_t address;
-    enum GVP_ident epc;
-    int key = 0;
-    struct ConfigDev *cd;
+    unsigned int epc;
+    unsigned int key = 0, skey;
+    const struct ConfigDev *cd;
+    unsigned int default_dma_xfer_mask;
+#ifdef CHECK_WD33C93
+    volatile unsigned char *sasr_3393, *scmd_3393;
+    unsigned char save_sasr;
+    unsigned char q, qq;
+#endif
 
     if (!MACH_IS_AMIGA || called)
 	return 0;
@@ -215,7 +223,27 @@
     tpnt->proc_dir = &proc_scsi_gvp11;
     tpnt->proc_info = &wd33c93_proc_info;
 
-    while ((key = zorro_find(MANUF_GVP, PROD_GVPIISCSI, 0, key))) {
+    while (1) {
+	/* 
+	 * This should (hopefully) be the correct way to identify
+	 * all the different GVP SCSI controllers (except for the
+	 * SERIES I though).
+	 */
+	skey = key;
+
+	if ((key = zorro_find(ZORRO_PROD_GVP_COMBO_030_R3_SCSI, 0, skey)) ||
+	    (key = zorro_find(ZORRO_PROD_GVP_SERIES_II, 0, skey)))
+	    default_dma_xfer_mask = ~0x00ffffff;
+	else if ((key = zorro_find(ZORRO_PROD_GVP_GFORCE_030_SCSI, 0, skey)) ||
+		 (key = zorro_find(ZORRO_PROD_GVP_A530_SCSI, 0, skey)) ||
+		 (key = zorro_find(ZORRO_PROD_GVP_COMBO_030_R4_SCSI, 0, skey)))
+	    default_dma_xfer_mask = ~0x01ffffff;
+	else if ((key = zorro_find(ZORRO_PROD_GVP_A1291, 0, skey)) ||
+		 (key = zorro_find(ZORRO_PROD_GVP_GFORCE_040_SCSI_1, 0, skey)))
+	    default_dma_xfer_mask = ~0x07ffffff;
+	else
+	    break;
+
 	cd = zorro_get_board(key);
 	address = cd->cd_BoardAddr;
 
@@ -227,23 +255,77 @@
 	if (cd->cd_BoardSize != 0x10000)
 		continue;
 
-	/* check extended product code */
-	epc = *(unsigned short *)(ZTWO_VADDR(address) + 0x8000);
-	epc = epc & GVP_PRODMASK;
+#ifdef CHECK_WD33C93
 
-	/* 
-	 * This should (hopefully) be the correct way to identify
-	 * all the different GVP SCSI controllers (except for the
-	 * SERIES I though).
+	/*
+	 * These darn GVP boards are a problem - it can be tough to tell
+	 * whether or not they include a SCSI controller. This is the
+	 * ultimate Yet-Another-GVP-Detection-Hack in that it actually
+	 * probes for a WD33c93 chip: If we find one, it's extremely
+	 * likely that this card supports SCSI, regardless of Product_
+	 * Code, Board_Size, etc. 
 	 */
-	if (!((epc == GVP_A1291_SCSI) || 
-	      (epc == GVP_GFORCE_040_SCSI) ||
-	      (epc == GVP_GFORCE_030_SCSI) ||
-	      (epc == GVP_A530_SCSI) ||
-	      (epc == GVP_COMBO_R4_SCSI) ||
-	      (epc == GVP_COMBO_R3_SCSI) ||
-	      (epc == GVP_SERIESII)))
-	    continue;
+
+    /* Get pointers to the presumed register locations and save contents */
+
+	sasr_3393 = &(((gvp11_scsiregs *)(ZTWO_VADDR(address)))->SASR);
+	scmd_3393 = &(((gvp11_scsiregs *)(ZTWO_VADDR(address)))->SCMD);
+	save_sasr = *sasr_3393;
+
+    /* First test the AuxStatus Reg */
+
+	q = *sasr_3393;		/* read it */
+	if (q & 0x08)		/* bit 3 should always be clear */
+		continue;
+	*sasr_3393 = WD_AUXILIARY_STATUS;	 /* setup indirect address */
+	if (*sasr_3393 == WD_AUXILIARY_STATUS) { /* shouldn't retain the write */
+		*sasr_3393 = save_sasr;	/* Oops - restore this byte */
+		continue;
+		}
+	if (*sasr_3393 != q) {	/* should still read the same */
+		*sasr_3393 = save_sasr;	/* Oops - restore this byte */
+		continue;
+		}
+	if (*scmd_3393 != q)	/* and so should the image at 0x1f */
+		continue;
+
+
+    /* Ok, we probably have a wd33c93, but let's check a few other places
+     * for good measure. Make sure that this works for both 'A and 'B    
+     * chip versions.
+     */
+
+	*sasr_3393 = WD_SCSI_STATUS;
+	q = *scmd_3393;
+	*sasr_3393 = WD_SCSI_STATUS;
+	*scmd_3393 = ~q;
+	*sasr_3393 = WD_SCSI_STATUS;
+	qq = *scmd_3393;
+	*sasr_3393 = WD_SCSI_STATUS;
+	*scmd_3393 = q;
+	if (qq != q)			/* should be read only */
+		continue;
+	*sasr_3393 = 0x1e;	/* this register is unimplemented */
+	q = *scmd_3393;
+	*sasr_3393 = 0x1e;
+	*scmd_3393 = ~q;
+	*sasr_3393 = 0x1e;
+	qq = *scmd_3393;
+	*sasr_3393 = 0x1e;
+	*scmd_3393 = q;
+	if (qq != q || qq != 0xff)	/* should be read only, all 1's */
+		continue;
+	*sasr_3393 = WD_TIMEOUT_PERIOD;
+	q = *scmd_3393;
+	*sasr_3393 = WD_TIMEOUT_PERIOD;
+	*scmd_3393 = ~q;
+	*sasr_3393 = WD_TIMEOUT_PERIOD;
+	qq = *scmd_3393;
+	*sasr_3393 = WD_TIMEOUT_PERIOD;
+	*scmd_3393 = q;
+	if (qq != (~q & 0xff))		/* should be read/write */
+		continue;
+#endif
 
 	instance = scsi_register (tpnt, sizeof (struct WD33C93_hostdata));
 	instance->base = (unsigned char *)ZTWO_VADDR(address);
@@ -252,22 +334,9 @@
 
 	if (gvp11_xfer_mask)
 		HDATA(instance)->dma_xfer_mask = gvp11_xfer_mask;
-	else{
-		switch (epc){
-		case GVP_COMBO_R3_SCSI:
-		case GVP_SERIESII:
-			HDATA(instance)->dma_xfer_mask = ~0x00ffffff;
-			break;
-		case GVP_GFORCE_030_SCSI:
-		case GVP_A530_SCSI:
-		case GVP_COMBO_R4_SCSI:
-			HDATA(instance)->dma_xfer_mask = ~0x01ffffff;
-			break;
-		default:
-			HDATA(instance)->dma_xfer_mask = ~0x07ffffff;
-			break;
-		}
-	}
+	else
+		HDATA(instance)->dma_xfer_mask = default_dma_xfer_mask;
+
 
 	DMA(instance)->secret2 = 1;
 	DMA(instance)->secret1 = 0;
@@ -283,16 +352,17 @@
 	 * Check for 14MHz SCSI clock
 	 */
 	if (epc & GVP_SCSICLKMASK)
-	  wd33c93_init(instance, (wd33c93_regs *)&(DMA(instance)->SASR),
-		       dma_setup, dma_stop, WD33C93_FS_8_10);
+		wd33c93_init(instance, (wd33c93_regs *)&(DMA(instance)->SASR),
+			     dma_setup, dma_stop, WD33C93_FS_8_10);
 	else
-	  wd33c93_init(instance, (wd33c93_regs *)&(DMA(instance)->SASR),
-		       dma_setup, dma_stop, WD33C93_FS_12_15);
+		wd33c93_init(instance, (wd33c93_regs *)&(DMA(instance)->SASR),
+			     dma_setup, dma_stop, WD33C93_FS_12_15);
 
 	if (num_gvp11++ == 0) {
-	    first_instance = instance;
-	    gvp11_template = instance->hostt;
-	    request_irq(IRQ_AMIGA_PORTS, gvp11_intr, 0, "GVP11 SCSI", gvp11_intr);
+		first_instance = instance;
+		gvp11_template = instance->hostt;
+		request_irq(IRQ_AMIGA_PORTS, gvp11_intr, 0,
+			    "GVP11 SCSI", gvp11_intr);
 	}
 	DMA(instance)->CNTR = GVP11_DMAC_INT_ENABLE;
 	zorro_config_board(key, 0);
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/mvme16x.c linux/drivers/scsi/mvme16x.c
--- v2.1.90/linux/drivers/scsi/mvme16x.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/mvme16x.c	Sat Mar 21 11:09:47 1998
@@ -0,0 +1,60 @@
+/*
+ * Detection routine for the NCR53c710 based MVME16x SCSI Controllers for Linux.
+ *
+ * Based on work by Alan Hourihane
+ */
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/blk.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/zorro.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/mvme16xhw.h>
+#include <asm/irq.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "53c7xx.h"
+#include "mvme16x.h"
+#include "asm/mvme16xhw.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_mvme16x = {
+    PROC_SCSI_MVME16x, 7, "MVME16x",
+    S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+extern ncr53c7xx_init (Scsi_Host_Template *tpnt, int board, int chip,
+			u32 base, int io_port, int irq, int dma,
+			long long options, int clock);
+
+int mvme16x_scsi_detect(Scsi_Host_Template *tpnt)
+{
+    static unsigned char called = 0;
+    int clock;
+    long long options;
+
+    if (mvme16x_config & MVME16x_CONFIG_NO_SCSICHIP) {
+	printk ("SCSI detection disabled, SCSI chip not present\n");
+	return 0;
+    }
+    if (called)
+	return 0;
+
+    tpnt->proc_dir = &proc_scsi_mvme16x;
+
+    options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT;
+
+    clock = 66000000;	/* 66MHz SCSI Clock */
+
+    ncr53c7xx_init(tpnt, 0, 710, (u32)0xfff47000,
+			0, 0x55, DMA_NONE,
+			options, clock);
+    called = 1;
+    return 1;
+}
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/mvme16x.h linux/drivers/scsi/mvme16x.h
--- v2.1.90/linux/drivers/scsi/mvme16x.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/mvme16x.h	Sat Mar 21 11:09:47 1998
@@ -0,0 +1,53 @@
+#ifndef MVME16x_SCSI_H
+#define MVME16x_SCSI_H
+
+#include <linux/types.h>
+
+int mvme16x_scsi_detect(Scsi_Host_Template *);
+const char *NCR53c7x0_info(void);
+int NCR53c7xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int NCR53c7xx_abort(Scsi_Cmnd *);
+int NCR53c7x0_release (Scsi_Host_Template *);
+int NCR53c7xx_reset(Scsi_Cmnd *, unsigned int);
+void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 3
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 24
+#endif
+
+#if defined(HOSTS_C) || defined(MODULE)
+#include <scsi/scsicam.h>
+
+extern struct proc_dir_entry proc_scsi_mvme16x;
+
+#define MVME16x_SCSI {/* next */                NULL,            \
+		      /* usage_count */         NULL,	         \
+		      /* proc_dir_entry */      NULL, \
+		      /* proc_info */           NULL,            \
+		      /* name */                "MVME16x SCSI", \
+		      /* detect */              mvme16x_scsi_detect,    \
+		      /* release */             NULL,   \
+		      /* info */                NULL,	         \
+		      /* command */             NULL,            \
+		      /* queuecommand */        NCR53c7xx_queue_command, \
+		      /* abort */               NCR53c7xx_abort,   \
+		      /* reset */               NCR53c7xx_reset,   \
+		      /* slave_attach */        NULL,            \
+		      /* bios_param */          NULL /*scsicam_bios_param*/, \
+		      /* can_queue */           24,       \
+		      /* this_id */             7,               \
+		      /* sg_tablesize */        127,          \
+		      /* cmd_per_lun */	        3,     \
+		      /* present */             0,               \
+		      /* unchecked_isa_dma */   0,               \
+		      /* use_clustering */      DISABLE_CLUSTERING }
+#endif
+#endif /* MVME16x_SCSI_H */
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v2.1.90/linux/drivers/scsi/scsi.h	Mon Feb 23 18:12:07 1998
+++ linux/drivers/scsi/scsi.h	Thu Mar 26 14:55:42 1998
@@ -312,7 +312,14 @@
 #define ASKED_FOR_SENSE 0x20
 
 
+#ifdef __mc68000__
+#include <asm/pgtable.h>
+#define CONTIGUOUS_BUFFERS(X,Y) \
+	(virt_to_phys((X)->b_data+(X)->b_size-1)+1==virt_to_phys((Y)->b_data))
+#else
 #define CONTIGUOUS_BUFFERS(X,Y) ((X->b_data+X->b_size) == Y->b_data)
+#endif
+
 
 /*
  * This is the crap from the old error handling code.  We have it in a special
@@ -454,14 +461,6 @@
                                      * because we did a bus reset. */
     unsigned device_blocked:1;      /* Device returned QUEUE_FULL. */
 };
-
-#ifdef __mc68000__
-#include <asm/pgtable.h>
-#define CONTIGUOUS_BUFFERS(X,Y) \
-	(virt_to_phys((X)->b_data+(X)->b_size-1)+1==virt_to_phys((Y)->b_data))
-#else
-#define CONTIGUOUS_BUFFERS(X,Y) ((X->b_data+X->b_size) == Y->b_data)
-#endif
 
 
 /*
diff -u --recursive --new-file v2.1.90/linux/drivers/scsi/wd33c93.c linux/drivers/scsi/wd33c93.c
--- v2.1.90/linux/drivers/scsi/wd33c93.c	Wed Jul 16 19:22:51 1997
+++ linux/drivers/scsi/wd33c93.c	Sat Mar 21 11:09:47 1998
@@ -1579,7 +1579,7 @@
 
 
 #define MAX_WD33C93_HOSTS 4
-#define MAX_SETUP_ARGS (sizeof(setup_args) / sizeof(char *))
+#define MAX_SETUP_ARGS ((int)(sizeof(setup_args) / sizeof(char *)))
 #define SETUP_BUFFER_SIZE 200
 static char setup_buffer[SETUP_BUFFER_SIZE];
 static char setup_used[MAX_SETUP_ARGS];
diff -u --recursive --new-file v2.1.90/linux/drivers/sound/pas2_card.c linux/drivers/sound/pas2_card.c
--- v2.1.90/linux/drivers/sound/pas2_card.c	Tue Mar 17 22:18:15 1998
+++ linux/drivers/sound/pas2_card.c	Sat Mar 21 10:57:42 1998
@@ -164,7 +164,7 @@
 		}
 		else
 		{
-			if (request_irq(pas_irq, pasintr, "PAS16", 0, NULL) < 0)
+			if (request_irq(pas_irq, pasintr, 0, "PAS16", NULL) < 0)
 				ok = 0;
 		}
 	}
diff -u --recursive --new-file v2.1.90/linux/drivers/video/Config.in linux/drivers/video/Config.in
--- v2.1.90/linux/drivers/video/Config.in	Mon Nov 24 01:18:47 1997
+++ linux/drivers/video/Config.in	Sat Mar 21 11:12:49 1998
@@ -7,43 +7,70 @@
   mainmenu_option next_comment
   comment 'Frame buffer devices'
 
- if [ "$CONFIG_AMIGA" = "y" ]; then
+  if [ "$CONFIG_APOLLO" = "y" ]; then
+    define_bool CONFIG_FB_APOLLO y
+  fi
+  if [ "$CONFIG_AMIGA" = "y" ]; then
     bool 'Amiga native chipset support' CONFIG_FB_AMIGA
     if [ "$CONFIG_FB_AMIGA" != "n" ]; then
       bool 'Amiga OCS chipset support' CONFIG_FB_AMIGA_OCS
       bool 'Amiga ECS chipset support' CONFIG_FB_AMIGA_ECS
       bool 'Amiga AGA chipset support' CONFIG_FB_AMIGA_AGA
     fi
-    tristate 'Amiga Cybervision support' CONFIG_FB_CYBER
+    tristate 'Amiga CyberVision support' CONFIG_FB_CYBER
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      bool 'Amiga CyberVision3D support (experimental)' CONFIG_FB_VIRGE
       tristate 'Amiga RetinaZ3 support' CONFIG_FB_RETINAZ3
     fi
   fi
   if [ "$CONFIG_ATARI" = "y" ]; then
     bool 'Atari native chipset support' CONFIG_FB_ATARI
-#    tristate 'Mach64 Frame Buffer support' CONFIG_FB_MACH64
+    bool 'ATI Mach64 display support' CONFIG_FB_ATY
+  fi
+  if [ "$CONFIG_PPC" = "y" ]; then
+    bool 'Open Firmware frame buffer device support' CONFIG_FB_OF
+    bool 'S3 Trio frame buffer device support' CONFIG_FB_S3TRIO
+    if [ "$CONFIG_FB_OF" = "y" ]; then
+#     bool 'Apple "control" display support' CONFIG_FB_CONTROL
+#     bool 'Apple "platinum" display support' CONFIG_FB_PLATINUM
+#     bool 'Apple "valkyrie" display support' CONFIG_FB_VALKYRIE
+      bool 'ATI Mach64 display support' CONFIG_FB_ATY
+#     bool 'IMS Twin Turbo display support' CONFIG_FB_IMSTT
+#     bool 'Chips 65550 display support' CONFIG_FB_CT65550
+#     bool 'S3 Trio display support' CONFIG_FB_S3TRIO
+    fi
+  fi
+  if [ "$CONFIG_MAC" = "y" ]; then
+    define_bool CONFIG_FB_MAC y
+  fi
+  if [ "$CONFIG_TGA_CONSOLE" = "y" ]; then
+    define_bool CONFIG_FB_TGA y
   fi
-  if [ "$CONFIG_CHRP" = "y" -o "$CONFIG_PMAC" = "y" ]; then
-    bool 'Open Firmware frame buffer device support' CONFIG_FB_OPEN_FIRMWARE
-  fi 
-  tristate 'Virtual Frame Buffer support' CONFIG_FB_VIRTUAL
+  tristate 'Virtual Frame Buffer support (ONLY FOR TESTING!)' CONFIG_FB_VIRTUAL
 
   bool 'Advanced low level driver options' CONFIG_FBCON_ADVANCED
   if [ "$CONFIG_FBCON_ADVANCED" = "y" ]; then
-    tristate 'Monochrome support' CONFIG_FBCON_MFB
-    tristate 'Interleaved bitplanes support' CONFIG_FBCON_ILBM
-    tristate 'Normal bitplanes support' CONFIG_FBCON_AFB
-    tristate 'Atari interleaved bitplanes (2 planes) support' CONFIG_FBCON_IPLAN2P2
-    tristate 'Atari interleaved bitplanes (4 planes) support' CONFIG_FBCON_IPLAN2P4
-    tristate 'Atari interleaved bitplanes (8 planes) support' CONFIG_FBCON_IPLAN2P8
-    tristate '8 bpp packed pixel support' CONFIG_FBCON_CFB8
-    tristate '16 bpp packed pixel support' CONFIG_FBCON_CFB16
-    tristate 'Cybervision support (accelerated)' CONFIG_FBCON_CYBER
-    tristate 'RetinaZ3 support (accelerated)' CONFIG_FBCON_RETINAZ3
+    bool 'Monochrome support' CONFIG_FBCON_MFB
+    bool '2 bpp packed pixels support' CONFIG_FBCON_CFB2
+    bool '4 bpp packed pixels support' CONFIG_FBCON_CFB4
+    bool '8 bpp packed pixels support' CONFIG_FBCON_CFB8
+    bool '16 bpp packed pixels support' CONFIG_FBCON_CFB16
+    bool '24 bpp packed pixels support' CONFIG_FBCON_CFB24
+    bool '32 bpp packed pixels support' CONFIG_FBCON_CFB32
+    bool 'Amiga bitplanes support' CONFIG_FBCON_AFB
+    bool 'Amiga interleaved bitplanes support' CONFIG_FBCON_ILBM
+    bool 'Atari interleaved bitplanes (2 planes) support' CONFIG_FBCON_IPLAN2P2
+    bool 'Atari interleaved bitplanes (4 planes) support' CONFIG_FBCON_IPLAN2P4
+    bool 'Atari interleaved bitplanes (8 planes) support' CONFIG_FBCON_IPLAN2P8
+    bool 'Mac variable bpp packed pixels support' CONFIG_FBCON_MAC
   else
-    if [ "$CONFIG_FB_AMIGA" != "n" -o "$CONFIG_FB_ATARI" != "n" -o \
-	 "$CONFIG_FB_CYBER" != "n" -o "$CONFIG_FB_RETINAZ3" != "n" -o \
-	 "$CONFIG_FB_VIRTUAL" != "n" ]; then
+    if [ "$CONFIG_FB_AMIGA" = "y" -o "$CONFIG_FB_AMIGA" = "m" -o \
+	 "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" -o \
+	 "$CONFIG_FB_CYBER" = "y" -o "$CONFIG_FB_CYBER" = "m" -o \
+	 "$CONFIG_FB_RETINAZ3" = "y" -o "$CONFIG_FB_RETINAZ3" = "m" -o \
+	 "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_VIRGE" = "m" -o \
+	 "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_MAC" = "m" -o \
+	 "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
       define_bool CONFIG_FBCON_MFB y
     fi
     if [ "$CONFIG_FB_AMIGA" = "y" -o "$CONFIG_FB_AMIGA" = "m" ]; then
@@ -55,20 +82,32 @@
       define_bool CONFIG_FBCON_IPLAN2P4 y
       define_bool CONFIG_FBCON_IPLAN2P8 y
     fi
+    if [ "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_MAC" = "m" -o \
+	 "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
+      define_bool CONFIG_FBCON_MAC  y
+      define_bool CONFIG_FBCON_CFB2 y
+      define_bool CONFIG_FBCON_CFB4 y
+    fi
     if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" -o \
-	 "$CONFIG_FB_OPEN_FIRMWARE" = "y" -o \
+	 "$CONFIG_FB_OF" = "y" -o "$CONFIG_FB_OF" = "m" -o \
+	 "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_MAC" = "m" -o  \
+	 "$CONFIG_FB_TGA" = "y" -o "$CONFIG_FB_TGA" = "m" -o \
 	 "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
       define_bool CONFIG_FBCON_CFB8 y
     fi
     if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" -o \
+	 "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_ATY" = "m" -o \
+	 "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_MAC" = "m" -o \
 	 "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
       define_bool CONFIG_FBCON_CFB16 y
     fi
-    if [ "$CONFIG_FB_CYBER" = "y" -o "$CONFIG_FB_CYBER" = "m" ]; then
-      define_bool CONFIG_FBCON_CYBER y
+    if [ "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
+      define_bool CONFIG_FBCON_CFB24 y
     fi
-    if [ "$CONFIG_FB_RETINAZ3" = "y" -o "$CONFIG_FB_RETINAZ3" = "m" ]; then
-      define_bool CONFIG_FBCON_RETINAZ3 y
+    if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" -o \
+	 "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_ATY" = "m" -o \
+	 "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
+      define_bool CONFIG_FBCON_CFB32 y
     fi
   fi
 
diff -u --recursive --new-file v2.1.90/linux/drivers/video/Makefile linux/drivers/video/Makefile
--- v2.1.90/linux/drivers/video/Makefile	Tue Nov 25 02:37:04 1997
+++ linux/drivers/video/Makefile	Sat Mar 21 11:12:49 1998
@@ -22,15 +22,11 @@
 # Frame Buffer Console
 
 ifeq ($(CONFIG_FB),y)
-  L_OBJS += fonts.o font_8x8.o font_8x16.o pearl_8x8.o
-  LX_OBJS += fbcon.o fbcmap.o
+  L_OBJS += fonts.o font_8x8.o font_8x16.o pearl_8x8.o font_6x11.o
+  LX_OBJS += fbcon.o fbcmap.o fbgen.o
 endif
 
-# Frame buffer devices
-
-ifeq ($(CONFIG_APOLLO),y)
-L_OBJS += dn_fb.o
-endif
+# Frame Buffer Devices
 
 ifeq ($(CONFIG_FB_AMIGA),y)
 L_OBJS += amifb.o
@@ -40,6 +36,10 @@
   endif
 endif
 
+ifeq ($(CONFIG_FB_APOLLO),y)
+L_OBJS += dnfb.o
+endif
+
 ifeq ($(CONFIG_FB_ATARI),y)
 L_OBJS += atafb.o
 else
@@ -48,134 +48,114 @@
   endif
 endif
 
+ifeq ($(CONFIG_FB_ATY),y)
+L_OBJS += atyfb.o
+endif
+
 ifeq ($(CONFIG_FB_CYBER),y)
-LX_OBJS += cyberfb.o
+L_OBJS += cyberfb.o
 else
   ifeq ($(CONFIG_FB_CYBER),m)
-  MX_OBJS += cyberfb.o
+  M_OBJS += cyberfb.o
   endif
 endif
 
+ifeq ($(CONFIG_FB_MAC),y)
+L_OBJS += macfb.o
+endif
+
+ifeq ($(CONFIG_FB_OF),y)
+L_OBJS += offb.o
+endif
+
 ifeq ($(CONFIG_FB_RETINAZ3),y)
-LX_OBJS += retz3fb.o
+L_OBJS += retz3fb.o
 else
   ifeq ($(CONFIG_FB_RETINAZ3),m)
-  MX_OBJS += retz3fb.o
+  M_OBJS += retz3fb.o
   endif
 endif
 
-ifeq ($(CONFIG_FB_VIRTUAL),y)
-L_OBJS += vfb.o
+ifeq ($(CONFIG_FB_S3TRIO),y)
+L_OBJS += S3triofb.o
 else
-  ifeq ($(CONFIG_FB_VIRTUAL),m)
-  M_OBJS += vfb.o
+  ifeq ($(CONFIG_FB_S3TRIO),m)
+  M_OBJS += S3triofb.o
   endif
 endif
 
-ifeq ($(CONFIG_FB_OPEN_FIRMWARE),y)
-L_OBJS += offb.o
+ifeq ($(CONFIG_FB_TGA),y)
+L_OBJS += tgafb.o
 endif
 
-ifeq ($(CONFIG_FB_MACH64),y)
-L_OBJS += mach64fb.o
+ifeq ($(CONFIG_FB_VIRGE),y)
+L_OBJS += virgefb.o
 else
-  ifeq ($(CONFIG_FB_MACH64),m)
-  M_OBJS += mach64fb.o
+  ifeq ($(CONFIG_FB_VIRGE),m)
+  M_OBJS += virgefb.o
   endif
 endif
 
-ifeq ($(CONFIG_FB_TGA),y)
-L_OBJS += tgafb.o
+ifeq ($(CONFIG_FB_VIRTUAL),y)
+L_OBJS += vfb.o
+else
+  ifeq ($(CONFIG_FB_VIRTUAL),m)
+  M_OBJS += vfb.o
+  endif
 endif
 
-# Low level drivers
+# Generic Low Level Drivers
 
-ifeq ($(CONFIG_FBCON_AFB),y)
-L_OBJS += fbcon-afb.o
-else
-  ifeq ($(CONFIG_FBCON_AFB),m)
-  M_OBJS += fbcon-afb.o
-  endif
+ifdef CONFIG_FBCON_AFB
+LX_OBJS += fbcon-afb.o
 endif
 
-ifeq ($(CONFIG_FBCON_CFB8),y)
-L_OBJS += fbcon-cfb8.o
-else
-  ifeq ($(CONFIG_FBCON_CFB8),m)
-  M_OBJS += fbcon-cfb8.o
-  endif
+ifdef CONFIG_FBCON_CFB2
+LX_OBJS += fbcon-cfb2.o
 endif
 
-ifeq ($(CONFIG_FBCON_CFB16),y)
+ifdef CONFIG_FBCON_CFB4
+LX_OBJS += fbcon-cfb4.o
+endif
+
+ifdef CONFIG_FBCON_CFB8
+LX_OBJS += fbcon-cfb8.o
+endif
+
+ifdef CONFIG_FBCON_CFB16
 LX_OBJS += fbcon-cfb16.o
-else
-  ifeq ($(CONFIG_FBCON_CFB16),m)
-  MX_OBJS += fbcon-cfb16.o
-  endif
 endif
 
-ifeq ($(CONFIG_FBCON_ILBM),y)
-L_OBJS += fbcon-ilbm.o
-else
-  ifeq ($(CONFIG_FBCON_ILBM),m)
-  M_OBJS += fbcon-ilbm.o
-  endif
+ifdef CONFIG_FBCON_CFB24
+LX_OBJS += fbcon-cfb24.o
 endif
 
-ifeq ($(CONFIG_FBCON_IPLAN2P2),y)
-L_OBJS += fbcon-iplan2p2.o
-else
-  ifeq ($(CONFIG_FBCON_IPLAN2P2),m)
-  M_OBJS += fbcon-iplan2p2.o
-  endif
+ifdef CONFIG_FBCON_CFB32
+LX_OBJS += fbcon-cfb32.o
 endif
 
-ifeq ($(CONFIG_FBCON_IPLAN2P4),y)
-L_OBJS += fbcon-iplan2p4.o
-else
-  ifeq ($(CONFIG_FBCON_IPLAN2P4),m)
-  M_OBJS += fbcon-iplan2p4.o
-  endif
+ifdef CONFIG_FBCON_ILBM
+LX_OBJS += fbcon-ilbm.o
 endif
 
-ifeq ($(CONFIG_FBCON_IPLAN2P8),y)
-L_OBJS += fbcon-iplan2p8.o
-else
-  ifeq ($(CONFIG_FBCON_IPLAN2P8),m)
-  M_OBJS += fbcon-iplan2p8.o
-  endif
+ifdef CONFIG_FBCON_IPLAN2P2
+LX_OBJS += fbcon-iplan2p2.o
 endif
 
-ifeq ($(CONFIG_FBCON_MFB),y)
-L_OBJS += fbcon-mfb.o
-else
-  ifeq ($(CONFIG_FBCON_MFB),m)
-  M_OBJS += fbcon-mfb.o
-  endif
+ifdef CONFIG_FBCON_IPLAN2P4
+LX_OBJS += fbcon-iplan2p4.o
 endif
 
-ifeq ($(CONFIG_FBCON_CYBER),y)
-L_OBJS += fbcon-cyber.o
-else
-  ifeq ($(CONFIG_FBCON_CYBER),m)
-  M_OBJS += fbcon-cyber.o
-  endif
+ifdef CONFIG_FBCON_IPLAN2P8
+LX_OBJS += fbcon-iplan2p8.o
 endif
 
-ifeq ($(CONFIG_FBCON_RETINAZ3),y)
-L_OBJS += fbcon-retz3.o
-else
-  ifeq ($(CONFIG_FBCON_RETINAZ3),m)
-  M_OBJS += fbcon-retz3.o
-  endif
+ifdef CONFIG_FBCON_MAC
+LX_OBJS += fbcon-mac.o
 endif
 
-ifeq ($(CONFIG_FBCON_MACH64),y)
-L_OBJS += fbcon-mach64.o
-else
-  ifeq ($(CONFIG_FBCON_MACH64),m)
-  M_OBJS += fbcon-mach64.o
-  endif
+ifdef CONFIG_FBCON_MFB
+LX_OBJS += fbcon-mfb.o
 endif
 
 # GSP Console
@@ -184,7 +164,7 @@
 L_OBJS := $(L_OBJS) gspcon.o gspcore.o
 endif
 
-# VGA Console
+# VGA Text Console
 
 ifdef CONFIG_ABSTRACT_CONSOLE
 ifdef CONFIG_VGA_CONSOLE
diff -u --recursive --new-file v2.1.90/linux/drivers/video/S3triofb.c linux/drivers/video/S3triofb.c
--- v2.1.90/linux/drivers/video/S3triofb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/S3triofb.c	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,885 @@
+/*
+ *  linux/drivers/video/S3Triofb.c -- Open Firmware based frame buffer device
+ *
+ *	Copyright (C) 1997 Peter De Schrijver
+ *
+ *  This driver is partly based on the PowerMac console driver:
+ *
+ *	Copyright (C) 1996 Paul Mackerras
+ *
+ *  and on the Open Firmware based frame buffer device:
+ *
+ *	Copyright (C) 1997 Geert Uytterhoeven
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+/*
+	Bugs : + OF dependencies should be removed.
+               + This driver should be merged with the CyberVision driver. The
+                 CyberVision is a Zorro III implementation of the S3Trio64 chip.
+
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <linux/pci.h>
+
+#include "fbcon-cfb8.h"
+
+
+#define mem_in8(addr)           in_8((void *)(addr))
+#define mem_in16(addr)          in_le16((void *)(addr))
+#define mem_in32(addr)          in_le32((void *)(addr))
+
+#define mem_out8(val, addr)     out_8((void *)(addr), val)
+#define mem_out16(val, addr)    out_le16((void *)(addr), val)
+#define mem_out32(val, addr)    out_le32((void *)(addr), val)
+
+#define IO_OUT16VAL(v, r)       (((v) << 8) | (r))
+
+#define arraysize(x)	(sizeof(x)/sizeof(*(x)))
+
+static int currcon = 0;
+static struct display disp;
+static struct fb_info fb_info;
+static struct { u_char red, green, blue, pad; } palette[256];
+static char s3trio_name[16] = "S3Trio ";
+
+
+static struct fb_fix_screeninfo fb_fix;
+static struct fb_var_screeninfo fb_var = { 0, };
+
+
+    /*
+     *  Interface used by the world
+     */
+
+void of_video_setup(char *options, int *ints);
+
+static int s3trio_open(struct fb_info *info);
+static int s3trio_release(struct fb_info *info);
+static int s3trio_get_fix(struct fb_fix_screeninfo *fix, int con,
+			  struct fb_info *info);
+static int s3trio_get_var(struct fb_var_screeninfo *var, int con,
+			  struct fb_info *info);
+static int s3trio_set_var(struct fb_var_screeninfo *var, int con,
+			  struct fb_info *info);
+static int s3trio_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			   struct fb_info *info);
+static int s3trio_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			   struct fb_info *info);
+static int s3trio_pan_display(struct fb_var_screeninfo *var, int con,
+			      struct fb_info *info);
+static int s3trio_ioctl(struct inode *inode, struct file *file, u_int cmd,
+			u_long arg, int con, struct fb_info *info);
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+extern struct vc_mode display_info;
+extern struct fb_info *console_fb_info;
+extern int (*console_setmode_ptr)(struct vc_mode *, int);
+extern int (*console_set_cmap_ptr)(struct fb_cmap *, int, int,
+				   struct fb_info *);
+static int s3trio_console_setmode(struct vc_mode *mode, int doit);
+#endif /* CONFIG_FB_COMPAT_XPMAC */
+
+    /*
+     *  Interface to the low level console driver
+     */
+
+unsigned long s3trio_fb_init(unsigned long mem_start);
+static int s3triofbcon_switch(int con, struct fb_info *info);
+static int s3triofbcon_updatevar(int con, struct fb_info *info);
+static void s3triofbcon_blank(int blank, struct fb_info *info);
+static int s3triofbcon_setcmap(struct fb_cmap *cmap, int con);
+
+    /*
+     *  Text console acceleration
+     */
+
+#ifdef CONFIG_FBCON_CFB8
+static struct display_switch fbcon_trio8;
+#endif
+
+    /*
+     *    Accelerated Functions used by the low level console driver
+     */
+
+static void Trio_WaitQueue(u_short fifo);
+static void Trio_WaitBlit(void);
+static void Trio_BitBLT(u_short curx, u_short cury, u_short destx,
+			u_short desty, u_short width, u_short height,
+			u_short mode);
+static void Trio_RectFill(u_short x, u_short y, u_short width, u_short height,
+			  u_short mode, u_short color);
+static void Trio_MoveCursor(u_short x, u_short y);
+
+
+    /*
+     *  Internal routines
+     */
+
+static int s3trio_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+                         u_int *transp, struct fb_info *info);
+static int s3trio_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                         u_int transp, struct fb_info *info);
+static void do_install_cmap(int con);
+
+
+static struct fb_ops s3trio_ops = {
+    s3trio_open, s3trio_release, s3trio_get_fix, s3trio_get_var, s3trio_set_var,
+    s3trio_get_cmap, s3trio_set_cmap, s3trio_pan_display, NULL, s3trio_ioctl
+};
+
+
+    /*
+     *  Open/Release the frame buffer device
+     */
+
+static int s3trio_open(struct fb_info *info)
+{
+    /*
+     *  Nothing, only a usage count for the moment
+     */
+
+    MOD_INC_USE_COUNT;
+    return(0);
+}
+
+static int s3trio_release(struct fb_info *info)
+{
+    MOD_DEC_USE_COUNT;
+    return(0);
+}
+
+
+    /*
+     *  Get the Fixed Part of the Display
+     */
+
+static int s3trio_get_fix(struct fb_fix_screeninfo *fix, int con,
+			  struct fb_info *info)
+{
+    memcpy(fix, &fb_fix, sizeof(fb_fix));
+    return 0;
+}
+
+
+    /*
+     *  Get the User Defined Part of the Display
+     */
+
+static int s3trio_get_var(struct fb_var_screeninfo *var, int con,
+			  struct fb_info *info)
+{
+    memcpy(var, &fb_var, sizeof(fb_var));
+    return 0;
+}
+
+
+    /*
+     *  Set the User Defined Part of the Display
+     */
+
+static int s3trio_set_var(struct fb_var_screeninfo *var, int con,
+			  struct fb_info *info)
+{
+    if (var->xres > fb_var.xres || var->yres > fb_var.yres ||
+	var->xres_virtual > fb_var.xres_virtual ||
+	var->yres_virtual > fb_var.yres_virtual ||
+	var->bits_per_pixel > fb_var.bits_per_pixel ||
+	var->nonstd || var->vmode != FB_VMODE_NONINTERLACED)
+	return -EINVAL;
+    memcpy(var, &fb_var, sizeof(fb_var));
+    return 0;
+}
+
+
+    /*
+     *  Pan or Wrap the Display
+     *
+     *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+     */
+
+static int s3trio_pan_display(struct fb_var_screeninfo *var, int con,
+			      struct fb_info *info)
+{
+    if (var->xoffset || var->yoffset)
+	return -EINVAL;
+    else
+	return 0;
+}
+
+    /*
+     *  Get the Colormap
+     */
+
+static int s3trio_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			   struct fb_info *info)
+{
+    if (con == currcon) /* current console? */
+	return fb_get_cmap(cmap, &fb_display[con].var, kspc, s3trio_getcolreg,
+			   info);
+    else if (fb_display[con].cmap.len) /* non default colormap? */
+	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+    else
+	fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+		     cmap, kspc ? 0 : 2);
+    return 0;
+}
+
+    /*
+     *  Set the Colormap
+     */
+
+static int s3trio_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			   struct fb_info *info)
+{
+    int err;
+
+
+    if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
+	if ((err = fb_alloc_cmap(&fb_display[con].cmap,
+				 1<<fb_display[con].var.bits_per_pixel, 0)))
+	    return err;
+    }
+    if (con == currcon)			/* current console? */
+	return fb_set_cmap(cmap, &fb_display[con].var, kspc, s3trio_setcolreg,
+			   info);
+    else
+	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+    return 0;
+}
+
+
+static int s3trio_ioctl(struct inode *inode, struct file *file, u_int cmd,
+			u_long arg, int con, struct fb_info *info)
+{
+    return -EINVAL;
+}
+
+__initfunc(int s3trio_resetaccel(void)) {
+
+
+#define EC01_ENH_ENB    0x0005
+#define EC01_LAW_ENB    0x0010
+#define EC01_MMIO_ENB   0x0020
+
+#define EC00_RESET      0x8000
+#define EC00_ENABLE     0x4000
+#define MF_MULT_MISC    0xE000
+#define SRC_FOREGROUND  0x0020
+#define SRC_BACKGROUND  0x0000
+#define MIX_SRC                 0x0007
+#define MF_T_CLIP       0x1000
+#define MF_L_CLIP       0x2000
+#define MF_B_CLIP       0x3000
+#define MF_R_CLIP       0x4000
+#define MF_PIX_CONTROL  0xA000
+#define MFA_SRC_FOREGR_MIX      0x0000
+#define MF_PIX_CONTROL  0xA000
+
+	outw(EC00_RESET,  0x42e8);
+	inw(  0x42e8);
+	outw(EC00_ENABLE,  0x42e8);
+	inw(  0x42e8);
+	outw(EC01_ENH_ENB | EC01_LAW_ENB,
+		   0x4ae8);
+	outw(MF_MULT_MISC,  0xbee8); /* 16 bit I/O registers */
+
+	/* Now set some basic accelerator registers */
+	Trio_WaitQueue(0x0400);
+	outw(SRC_FOREGROUND | MIX_SRC, 0xbae8);
+	outw(SRC_BACKGROUND | MIX_SRC,  0xb6e8);/* direct color*/
+	outw(MF_T_CLIP | 0, 0xbee8 );     /* clip virtual area  */
+	outw(MF_L_CLIP | 0, 0xbee8 );
+	outw(MF_R_CLIP | (640 - 1), 0xbee8);
+	outw(MF_B_CLIP | (480 - 1),  0xbee8);
+	Trio_WaitQueue(0x0400);
+	outw(0xffff,  0xaae8);       /* Enable all planes */
+	outw(0xffff, 0xaae8);       /* Enable all planes */
+	outw( MF_PIX_CONTROL | MFA_SRC_FOREGR_MIX,  0xbee8);
+
+}
+
+__initfunc(int s3trio_init(void)) {
+
+    u_char bus, dev;
+    unsigned int t32;
+    unsigned short cmd;
+    int i;
+
+	bus=0;
+	dev=(3<<3);
+                pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32);
+                if(t32 == (PCI_DEVICE_ID_S3_TRIO << 16) + PCI_VENDOR_ID_S3) {
+                        pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, &t32);
+                        pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_1, &t32);
+			pcibios_read_config_word(bus, dev, PCI_COMMAND,&cmd);
+
+			pcibios_write_config_word(bus, dev, PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+
+			pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0,0xffffffff);
+                        pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, &t32);
+
+/* This is a gross hack as OF only maps enough memory for the framebuffer and
+   we want to use MMIO too. We should find out which chunk of address space
+   we can use here */
+			pcibios_write_config_dword(bus,dev,PCI_BASE_ADDRESS_0,0xc6000000);
+
+			/* unlock s3 */
+
+			outb(0x01, 0x3C3);
+
+			outb(inb(0x03CC) | 1, 0x3c2);
+
+			outw(IO_OUT16VAL(0x48, 0x38),0x03D4);
+			outw(IO_OUT16VAL(0xA0, 0x39),0x03D4);
+			outb(0x33,0x3d4);
+			outw(IO_OUT16VAL( inb(0x3d5) & ~(0x2 |
+			 0x10 | 0x40) , 0x33),0x3d4);
+
+			outw(IO_OUT16VAL(0x6,0x8), 0x3c4);
+
+			/* switch to MMIO only mode */
+
+			outb(0x58,0x3d4);
+			outw(IO_OUT16VAL(inb(0x3d5) | 3 | 0x10,0x58),0x3d4);
+			outw(IO_OUT16VAL(8,0x53),0x3d4);
+
+			/* switch off I/O accesses */
+
+#if 0
+			pcibios_write_config_word(bus, dev, PCI_COMMAND,
+				        PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+#endif
+                }
+
+	return 0;
+}
+
+
+    /*
+     *  Initialisation
+     *  We heavily rely on OF for the moment. This needs fixing.
+     */
+
+__initfunc(unsigned long s3trio_fb_init(unsigned long mem_start))
+{
+    struct device_node *dp;
+    int i, err, *pp, len;
+    unsigned *up, address;
+    u_long *CursorBase;
+
+    if (!prom_display_paths[0])
+	return mem_start;
+    if (!(dp = find_path_device(prom_display_paths[0])))
+	return mem_start;
+
+    strncat(s3trio_name, dp->name, sizeof(s3trio_name));
+    s3trio_name[sizeof(s3trio_name)-1] = '\0';
+    strcpy(fb_fix.id, s3trio_name);
+
+    if((pp = (int *)get_property(dp, "vendor-id", &len)) != NULL
+	&& *pp!=PCI_VENDOR_ID_S3) {
+	printk("%s: can't find S3 Trio board\n", dp->full_name);
+	return mem_start;
+    }
+
+    if((pp = (int *)get_property(dp, "device-id", &len)) != NULL
+	&& *pp!=PCI_DEVICE_ID_S3_TRIO) {
+	printk("%s: can't find S3 Trio board\n", dp->full_name);
+	return mem_start;
+    }
+
+    if ((pp = (int *)get_property(dp, "depth", &len)) != NULL
+	&& len == sizeof(int) && *pp != 8) {
+	printk("%s: can't use depth = %d\n", dp->full_name, *pp);
+	return mem_start;
+    }
+    if ((pp = (int *)get_property(dp, "width", &len)) != NULL
+	&& len == sizeof(int))
+	fb_var.xres = fb_var.xres_virtual = *pp;
+    if ((pp = (int *)get_property(dp, "height", &len)) != NULL
+	&& len == sizeof(int))
+	fb_var.yres = fb_var.yres_virtual = *pp;
+    if ((pp = (int *)get_property(dp, "linebytes", &len)) != NULL
+	&& len == sizeof(int))
+	fb_fix.line_length = *pp;
+    else
+	fb_fix.line_length = fb_var.xres_virtual;
+    fb_fix.smem_len = fb_fix.line_length*fb_var.yres;
+
+    s3trio_init();
+    address=0xc6000000;
+    fb_fix.smem_start = ioremap(address,64*1024*1024);
+    fb_fix.type = FB_TYPE_PACKED_PIXELS;
+    fb_fix.type_aux = 0;
+
+
+    s3trio_resetaccel();
+
+	mem_out8(0x30,fb_fix.smem_start+0x1008000 + 0x03D4);
+	mem_out8(0x2d,fb_fix.smem_start+0x1008000 + 0x03D4);
+	mem_out8(0x2e,fb_fix.smem_start+0x1008000 + 0x03D4);
+
+	mem_out8(0x50,fb_fix.smem_start+0x1008000 + 0x03D4);
+
+    /* disable HW cursor */
+
+    mem_out8(0x39,fb_fix.smem_start+0x1008000 + 0x03D4);
+    mem_out8(0xa0,fb_fix.smem_start+0x1008000 + 0x03D5);
+
+    mem_out8(0x45,fb_fix.smem_start+0x1008000 + 0x03D4);
+    mem_out8(0,fb_fix.smem_start+0x1008000 + 0x03D5);
+
+    mem_out8(0x4e,fb_fix.smem_start+0x1008000 + 0x03D4);
+    mem_out8(0,fb_fix.smem_start+0x1008000 + 0x03D5);
+
+    mem_out8(0x4f,fb_fix.smem_start+0x1008000 + 0x03D4);
+    mem_out8(0,fb_fix.smem_start+0x1008000 + 0x03D5);
+
+    /* init HW cursor */
+
+    CursorBase=(u_long *)(fb_fix.smem_start + 2*1024*1024 - 0x400);
+	for (i=0; i < 8; i++) {
+		*(CursorBase  +(i*4)) = 0xffffff00;
+		*(CursorBase+1+(i*4)) = 0xffff0000;
+		*(CursorBase+2+(i*4)) = 0xffff0000;
+		*(CursorBase+3+(i*4)) = 0xffff0000;
+	}
+	for (i=8; i < 64; i++) {
+		*(CursorBase  +(i*4)) = 0xffff0000;
+		*(CursorBase+1+(i*4)) = 0xffff0000;
+		*(CursorBase+2+(i*4)) = 0xffff0000;
+		*(CursorBase+3+(i*4)) = 0xffff0000;
+	}
+
+
+    mem_out8(0x4c,fb_fix.smem_start+0x1008000 + 0x03D4);
+    mem_out8(((2*1024 - 1)&0xf00)>>8,fb_fix.smem_start+0x1008000 + 0x03D5);
+
+    mem_out8(0x4d,fb_fix.smem_start+0x1008000 + 0x03D4);
+    mem_out8((2*1024 - 1) & 0xff,fb_fix.smem_start+0x1008000 + 0x03D5);
+
+    mem_out8(0x45,fb_fix.smem_start+0x1008000 + 0x03D4);
+	mem_in8(fb_fix.smem_start+0x1008000 + 0x03D4);
+
+    mem_out8(0x4a,fb_fix.smem_start+0x1008000 + 0x03D4);
+    mem_out8(0x80,fb_fix.smem_start+0x1008000 + 0x03D5);
+    mem_out8(0x80,fb_fix.smem_start+0x1008000 + 0x03D5);
+    mem_out8(0x80,fb_fix.smem_start+0x1008000 + 0x03D5);
+
+    mem_out8(0x4b,fb_fix.smem_start+0x1008000 + 0x03D4);
+    mem_out8(0x00,fb_fix.smem_start+0x1008000 + 0x03D5);
+    mem_out8(0x00,fb_fix.smem_start+0x1008000 + 0x03D5);
+    mem_out8(0x00,fb_fix.smem_start+0x1008000 + 0x03D5);
+
+    mem_out8(0x45,fb_fix.smem_start+0x1008000 + 0x03D4);
+    mem_out8(0,fb_fix.smem_start+0x1008000 + 0x03D5);
+
+    s3trio_setcolreg(255, 56, 100, 160, 0, NULL /* not used */);
+    s3trio_setcolreg(254, 0, 0, 0, 0, NULL /* not used */);
+    memset((char *)fb_fix.smem_start,0,640*480);
+
+#if 0
+    Trio_RectFill(0,0,90,90,7,1);
+#endif
+
+    fb_fix.visual = FB_VISUAL_PSEUDOCOLOR ;
+    fb_var.xoffset = fb_var.yoffset = 0;
+    fb_var.bits_per_pixel = 8;
+    fb_var.grayscale = 0;
+    fb_var.red.offset = fb_var.green.offset = fb_var.blue.offset = 0;
+    fb_var.red.length = fb_var.green.length = fb_var.blue.length = 8;
+    fb_var.red.msb_right = fb_var.green.msb_right = fb_var.blue.msb_right = 0;
+    fb_var.transp.offset = fb_var.transp.length = fb_var.transp.msb_right = 0;
+    fb_var.nonstd = 0;
+    fb_var.activate = 0;
+    fb_var.height = fb_var.width = -1;
+    fb_var.accel = 5;
+    fb_var.pixclock = 1;
+    fb_var.left_margin = fb_var.right_margin = 0;
+    fb_var.upper_margin = fb_var.lower_margin = 0;
+    fb_var.hsync_len = fb_var.vsync_len = 0;
+    fb_var.sync = 0;
+    fb_var.vmode = FB_VMODE_NONINTERLACED;
+
+    disp.var = fb_var;
+    disp.cmap.start = 0;
+    disp.cmap.len = 0;
+    disp.cmap.red = disp.cmap.green = disp.cmap.blue = disp.cmap.transp = NULL;
+    disp.screen_base = fb_fix.smem_start;
+    disp.visual = fb_fix.visual;
+    disp.type = fb_fix.type;
+    disp.type_aux = fb_fix.type_aux;
+    disp.ypanstep = 0;
+    disp.ywrapstep = 0;
+    disp.line_length = fb_fix.line_length;
+    disp.can_soft_blank = 1;
+    disp.inverse = 0;
+#ifdef CONFIG_FBCON_CFB8
+    disp.dispsw = &fbcon_trio8;
+#else
+    disp.dispsw = NULL;
+#endif
+
+    strcpy(fb_info.modename, "Trio64 ");
+    strncat(fb_info.modename, dp->full_name, sizeof(fb_info.modename));
+    fb_info.node = -1;
+    fb_info.fbops = &s3trio_ops;
+#if 0
+    fb_info.fbvar_num = 1;
+    fb_info.fbvar = &fb_var;
+#endif
+    fb_info.disp = &disp;
+    fb_info.fontname[0] = '\0';
+    fb_info.changevar = NULL;
+    fb_info.switch_con = &s3triofbcon_switch;
+    fb_info.updatevar = &s3triofbcon_updatevar;
+    fb_info.blank = &s3triofbcon_blank;
+#if 0
+    fb_info.setcmap = &s3triofbcon_setcmap;
+#endif
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+    if (!console_fb_info) {
+	display_info.height = fb_var.yres;
+	display_info.width = fb_var.xres;
+	display_info.depth = 8;
+	display_info.pitch = fb_fix.line_length;
+	display_info.mode = 0;
+	strncpy(display_info.name, dp->name, sizeof(display_info.name));
+	display_info.fb_address = (unsigned long)fb_fix.smem_start;
+	display_info.disp_reg_address = address + 0x1008000;
+	display_info.cmap_adr_address = address + 0x1008000 + 0x3c8;
+	display_info.cmap_data_address = address + 0x1008000 + 0x3c9;
+	console_fb_info = &fb_info;
+	console_setmode_ptr = s3trio_console_setmode;
+	console_set_cmap_ptr = s3trio_set_cmap;
+    }
+#endif /* CONFIG_FB_COMPAT_XPMAC) */
+
+    err = register_framebuffer(&fb_info);
+    if (err < 0)
+	return mem_start;
+
+    printk("fb%d: S3 Trio frame buffer device on %s\n",
+	   GET_FB_IDX(fb_info.node), dp->full_name);
+
+    return mem_start;
+}
+
+
+static int s3triofbcon_switch(int con, struct fb_info *info)
+{
+    /* Do we have to save the colormap? */
+    if (fb_display[currcon].cmap.len)
+	fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
+		    s3trio_getcolreg, info);
+
+    currcon = con;
+    /* Install new colormap */
+    do_install_cmap(con);
+    return 0;
+}
+
+    /*
+     *  Update the `var' structure (called by fbcon.c)
+     */
+
+static int s3triofbcon_updatevar(int con, struct fb_info *info)
+{
+    /* Nothing */
+    return 0;
+}
+
+    /*
+     *  Blank the display.
+     */
+
+static void s3triofbcon_blank(int blank, struct fb_info *info)
+{
+    /* Nothing */
+}
+
+    /*
+     *  Set the colormap
+     */
+
+static int s3triofbcon_setcmap(struct fb_cmap *cmap, int con)
+{
+    return(s3trio_set_cmap(cmap, 1, con, &fb_info));
+}
+
+
+    /*
+     *  Read a single color register and split it into
+     *  colors/transparent. Return != 0 for invalid regno.
+     */
+
+static int s3trio_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+                         u_int *transp, struct fb_info *info)
+{
+    if (regno > 255)
+	return 1;
+    *red = palette[regno].red;
+    *green = palette[regno].green;
+    *blue = palette[regno].blue;
+    return 0;
+}
+
+
+    /*
+     *  Set a single color register. The values supplied are already
+     *  rounded down to the hardware's capabilities (according to the
+     *  entries in the var structure). Return != 0 for invalid regno.
+     */
+
+static int s3trio_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                            u_int transp, struct fb_info *info)
+{
+    if (regno > 255)
+	return 1;
+    palette[regno].red = red;
+    palette[regno].green = green;
+    palette[regno].blue = blue;
+
+    mem_out8(regno,fb_fix.smem_start+0x1008000 + 0x3c8);
+    mem_out8((red & 0xff) >> 2,fb_fix.smem_start+0x1008000 + 0x3c9);
+    mem_out8((green & 0xff) >> 2,fb_fix.smem_start+0x1008000 + 0x3c9);
+    mem_out8((blue & 0xff) >> 2,fb_fix.smem_start+0x1008000 + 0x3c9);
+
+    return 0;
+}
+
+
+static void do_install_cmap(int con)
+{
+    if (con != currcon)
+	return;
+    if (fb_display[con].cmap.len)
+	fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
+		    s3trio_setcolreg, &fb_info);
+    else
+	fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+		    &fb_display[con].var, 1, s3trio_setcolreg, &fb_info);
+}
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+
+    /*
+     *  Backward compatibility mode for Xpmac
+     */
+
+static int s3trio_console_setmode(struct vc_mode *mode, int doit)
+{
+    int err;
+    struct fb_var_screeninfo var;
+    struct s3trio_par par;
+
+    if (mode->mode <= 0 || mode->mode > VMODE_MAX )
+        return -EINVAL;
+    par.video_mode = mode->mode;
+
+    switch (mode->depth) {
+        case 24:
+        case 32:
+            par.color_mode = CMODE_32;
+            break;
+        case 16:
+            par.color_mode = CMODE_16;
+            break;
+        case 8:
+        case 0:			/* (default) */
+            par.color_mode = CMODE_8;
+            break;
+        default:
+            return -EINVAL;
+    }
+    encode_var(&var, &par);
+    if ((err = decode_var(&var, &par)))
+        return err;
+    if (doit)
+        s3trio_set_var(&var, currcon, 0);
+    return 0;
+}
+
+#endif /* CONFIG_FB_COMPAT_XPMAC */
+
+void s3trio_video_setup(char *options, int *ints) {
+
+        return;
+
+}
+
+static void Trio_WaitQueue(u_short fifo) {
+
+	u_short status;
+
+        do
+        {
+		status = mem_in16(fb_fix.smem_start + 0x1000000 + 0x9AE8);
+	}  while (!(status & fifo));
+
+}
+
+static void Trio_WaitBlit(void) {
+
+	u_short status;
+
+        do
+        {
+		status = mem_in16(fb_fix.smem_start + 0x1000000 + 0x9AE8);
+	}  while (status & 0x200);
+
+}
+
+static void Trio_BitBLT(u_short curx, u_short cury, u_short destx,
+			u_short desty, u_short width, u_short height,
+			u_short mode) {
+
+	u_short blitcmd = 0xc011;
+
+	/* Set drawing direction */
+        /* -Y, X maj, -X (default) */
+
+	if (curx > destx)
+		blitcmd |= 0x0020;  /* Drawing direction +X */
+	else {
+		curx  += (width - 1);
+		destx += (width - 1);
+	}
+
+	if (cury > desty)
+		blitcmd |= 0x0080;  /* Drawing direction +Y */
+	else {
+		cury  += (height - 1);
+		desty += (height - 1);
+	}
+
+	Trio_WaitQueue(0x0400);
+
+	outw(0xa000,  0xBEE8);
+	outw(0x60 | mode,  0xBAE8);
+
+	outw(curx,  0x86E8);
+	outw(cury,  0x82E8);
+
+	outw(destx,  0x8EE8);
+	outw(desty,  0x8AE8);
+
+	outw(height - 1,  0xBEE8);
+	outw(width - 1,  0x96E8);
+
+	outw(blitcmd,  0x9AE8);
+
+}
+
+static void Trio_RectFill(u_short x, u_short y, u_short width, u_short height,
+			  u_short mode, u_short color) {
+
+	u_short blitcmd = 0x40b1;
+
+	Trio_WaitQueue(0x0400);
+
+	outw(0xa000,  0xBEE8);
+	outw((0x20 | mode),  0xBAE8);
+	outw(0xe000,  0xBEE8);
+	outw(color,  0xA6E8);
+	outw(x,  0x86E8);
+	outw(y,  0x82E8);
+	outw((height - 1), 0xBEE8);
+	outw((width - 1), 0x96E8);
+	outw(blitcmd,  0x9AE8);
+
+}
+
+
+static void Trio_MoveCursor(u_short x, u_short y) {
+
+	mem_out8(0x39, fb_fix.smem_start + 0x1008000 + 0x3d4);
+	mem_out8(0xa0, fb_fix.smem_start + 0x1008000 + 0x3d5);
+
+	mem_out8(0x46, fb_fix.smem_start + 0x1008000 + 0x3d4);
+	mem_out8((x & 0x0700) >> 8, fb_fix.smem_start + 0x1008000 + 0x3d5);
+	mem_out8(0x47, fb_fix.smem_start + 0x1008000 + 0x3d4);
+	mem_out8(x & 0x00ff, fb_fix.smem_start + 0x1008000 + 0x3d5);
+
+	mem_out8(0x48, fb_fix.smem_start + 0x1008000 + 0x3d4);
+	mem_out8((y & 0x0700) >> 8, fb_fix.smem_start + 0x1008000 + 0x3d5);
+	mem_out8(0x49, fb_fix.smem_start + 0x1008000 + 0x3d4);
+	mem_out8(y & 0x00ff, fb_fix.smem_start + 0x1008000 + 0x3d5);
+
+}
+
+
+    /*
+     *  Text console acceleration
+     */
+
+#ifdef CONFIG_FBCON_CFB8
+static void fbcon_trio8_bmove(struct display *p, int sy, int sx, int dy,
+			      int dx, int height, int width)
+{
+    sx *= 8; dx *= 8; width *= 8;
+    Trio_BitBLT((u_short)sx, (u_short)(sy*p->fontheight), (u_short)dx,
+		 (u_short)(dy*p->fontheight), (u_short)width,
+		 (u_short)(height*p->fontheight), (u_short)S3_NEW);
+}
+
+static void fbcon_trio8_clear(struct vc_data *conp, struct display *p, int sy,
+			      int sx, int height, int width)
+{
+    unsigned char bg;
+
+    sx *= 8; width *= 8;
+    bg = attr_bgcol_ec(p,conp);
+    Trio_RectFill((u_short)sx,
+		   (u_short)(sy*p->fontheight),
+		   (u_short)width,
+		   (u_short)(height*p->fontheight),
+		   (u_short)S3_NEW,
+		   (u_short)bg);
+}
+
+static void fbcon_trio8_putc(struct vc_data *conp, struct display *p, int c,
+			     int yy, int xx)
+{
+    Trio_WaitBlit();
+    fbcon_cfb8_putc(conp, p, c, yy, xx);
+}
+
+static void fbcon_trio8_putcs(struct vc_data *conp, struct display *p,
+			      const char *s, int count, int yy, int xx)
+{
+    Trio_WaitBlit();
+    fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
+}
+
+static void fbcon_trio8_revc(struct display *p, int xx, int yy)
+{
+    Trio_WaitBlit();
+    fbcon_cfb8_revc(p, xx, yy);
+}
+
+static struct display_switch fbcon_trio8 = {
+   fbcon_cfb8_setup, fbcon_trio8_bmove, fbcon_trio8_clear, fbcon_trio8_putc,
+   fbcon_trio8_putcs, fbcon_trio8_revc
+};
+#endif
diff -u --recursive --new-file v2.1.90/linux/drivers/video/amifb.c linux/drivers/video/amifb.c
--- v2.1.90/linux/drivers/video/amifb.c	Mon Nov 24 01:18:49 1997
+++ linux/drivers/video/amifb.c	Sat Mar 21 11:12:49 1998
@@ -60,6 +60,11 @@
 #include <asm/amigaints.h>
 #include <asm/setup.h>
 
+#include "fbcon-afb.h"
+#include "fbcon-ilbm.h"
+#include "fbcon-mfb.h"
+
+
 #define DEBUG
 
 #if !defined(CONFIG_FB_AMIGA_OCS) && !defined(CONFIG_FB_AMIGA_ECS) && !defined(CONFIG_FB_AMIGA_AGA)
@@ -535,15 +540,6 @@
 
 
 	/*
-	 * Monitor Specifications
-	 *
-	 * These are typical for a `generic' Amiga monitor (e.g. A1960)
-	 */
-
-static long vfmin = 50, vfmax = 90, hfmin = 15000, hfmax = 38000;
-
-
-	/*
 	 * Various macros
 	 */
 
@@ -672,7 +668,7 @@
 	 * Current Video Mode
 	 */
 
-static struct amiga_fb_par {
+static struct amifb_par {
 
 	/* General Values */
 
@@ -740,6 +736,7 @@
 static int currcon = 0;
 
 static struct display disp;
+
 static struct fb_info fb_info;
 
 
@@ -789,229 +786,199 @@
 	 * The rest of the name is filled in during initialization
 	 */
 
-static char amiga_fb_name[16] = "Amiga ";
-
-	/*
-	 * Predefined Video Mode Names
-	 *
-	 * The a2024-?? modes don't work yet because there's no A2024 driver.
-	 */
-
-static char *amiga_fb_modenames[] = {
-
-	/*
-	 * Autodetect (Default) Video Mode
-	 */
-
-	"default",
-
-	/*
-	 * AmigaOS Video Modes
-	 */
-
-	"ntsc",			/* 640x200, 15 kHz, 60 Hz (NTSC) */
-	"ntsc-lace",		/* 640x400, 15 kHz, 60 Hz interlaced (NTSC) */
-	"pal",			/* 640x256, 15 kHz, 50 Hz (PAL) */
-	"pal-lace",		/* 640x512, 15 kHz, 50 Hz interlaced (PAL) */
-	"multiscan",		/* 640x480, 29 kHz, 57 Hz */
-	"multiscan-lace",	/* 640x960, 29 kHz, 57 Hz interlaced */
-	"a2024-10",		/* 1024x800, 10 Hz (Not yet supported) */
-	"a2024-15",		/* 1024x800, 15 Hz (Not yet supported) */
-	"euro36",		/* 640x200, 15 kHz, 72 Hz */
-	"euro36-lace",		/* 640x400, 15 kHz, 72 Hz interlaced */
-	"euro72",		/* 640x400, 29 kHz, 68 Hz */
-	"euro72-lace",		/* 640x800, 29 kHz, 68 Hz interlaced */
-	"super72",		/* 800x300, 23 kHz, 70 Hz */
-	"super72-lace",		/* 800x600, 23 kHz, 70 Hz interlaced */
-	"dblntsc",		/* 640x200, 27 kHz, 57 Hz doublescan */
-	"dblntsc-ff",		/* 640x400, 27 kHz, 57 Hz */
-	"dblntsc-lace",		/* 640x800, 27 kHz, 57 Hz interlaced */
-	"dblpal",		/* 640x256, 27 kHz, 47 Hz doublescan */
-	"dblpal-ff",		/* 640x512, 27 kHz, 47 Hz */
-	"dblpal-lace",		/* 640x1024, 27 kHz, 47 Hz interlaced */
-
-	/*
-	 * VGA Video Modes
-	 */
-
-	"vga",			/* 640x480, 31 kHz, 60 Hz (VGA) */
-	"vga70",		/* 640x400, 31 kHz, 70 Hz (VGA) */
-
-	/*
-	 * User Defined Video Modes: to be set after boot up using e.g. fbset
-	 */
-
-	"user0", "user1", "user2", "user3", "user4", "user5", "user6", "user7"
-};
-
-static struct fb_var_screeninfo amiga_fb_predefined[] = {
-
-	/*
-	 * Autodetect (Default) Video Mode
-	 */
+static char amifb_name[16] = "Amiga ";
 
-	{ 0, },
 
 	/*
-	 * AmigaOS Video Modes
+	 * Predefined Video Modes
+	 *
 	 */
 
-	{
-		/* ntsc */
-		640, 200, 640, 200, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 44, 16, 76, 2,
-		FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* ntsc-lace */
-		640, 400, 640, 400, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 88, 33, 76, 4,
-		FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* pal */
-		640, 256, 640, 256, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 40, 14, 76, 2,
-		FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* pal-lace */
-		640, 512, 640, 512, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 80, 29, 76, 4,
-		FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* multiscan */
-		640, 480, 640, 480, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 96, 112, 29, 8, 72, 8,
-		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
-
-	}, {
-		/* multiscan-lace */
-		640, 960, 640, 960, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 96, 112, 58, 16, 72, 16,
-		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* a2024-10 (Not yet supported) */
-		1024, 800, 1024, 800, 0, 0, 2, 0,
-		{0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 0, 0, 0, 0, 0, 0,
-		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* a2024-15 (Not yet supported) */
-		1024, 800, 1024, 800, 0, 0, 2, 0,
-		{0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 0, 0, 0, 0, 0, 0,
-		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* euro36 */
-		640, 200, 640, 200, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 92, 124, 6, 6, 52, 5,
-		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* euro36-lace */
-		640, 400, 640, 400, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 92, 124, 12, 12, 52, 10,
-		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* euro72 */
-		640, 400, 640, 400, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 164, 92, 9, 9, 80, 8,
-		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* euro72-lace */
-		640, 800, 640, 800, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 164, 92, 18, 18, 80, 16,
-		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* super72 */
-		800, 300, 800, 300, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 212, 140, 10, 11, 80, 7,
-		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* super72-lace */
-		800, 600, 800, 600, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 212, 140, 20, 22, 80, 14,
-		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* dblntsc */
-		640, 200, 640, 200, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 18, 17, 80, 4,
-		0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
-	}, {
-		/* dblntsc-ff */
-		640, 400, 640, 400, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 36, 35, 80, 7,
-		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* dblntsc-lace */
-		640, 800, 640, 800, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 72, 70, 80, 14,
-		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* dblpal */
-		640, 256, 640, 256, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 14, 13, 80, 4,
-		0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
-	}, {
-		/* dblpal-ff */
-		640, 512, 640, 512, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 28, 27, 80, 7,
-		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* dblpal-lace */
-		640, 1024, 640, 1024, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 56, 54, 80, 14,
-		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
-	},
+static struct fb_videomode amifb_predefined[] __initdata = {
 
-	/*
-	 * VGA Video Modes
-	 */
-
-	{
-		/* vga */
-		640, 480, 640, 480, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 64, 96, 30, 9, 112, 2,
-		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
-	}, {
-		/* vga70 */
-		640, 400, 640, 400, 0, 0, 4, 0,
-		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 64, 96, 35, 12, 112, 2,
-		FB_SYNC_VERT_HIGH_ACT | FB_SYNC_COMP_HIGH_ACT, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
-	},
+    /*
+     *  AmigaOS Video Modes
+     */
+
+    {
+	"ntsc", {		/* 640x200, 15 kHz, 60 Hz (NTSC) */
+	    640, 200, 640, 200, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_HIRES, 106, 86, 44, 16, 76, 2,
+	    FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"ntsc-lace", {		/* 640x400, 15 kHz, 60 Hz interlaced (NTSC) */
+	    640, 400, 640, 400, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_HIRES, 106, 86, 88, 33, 76, 4,
+	    FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"pal", {		/* 640x256, 15 kHz, 50 Hz (PAL) */
+	    640, 256, 640, 256, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_HIRES, 106, 86, 40, 14, 76, 2,
+	    FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"pal-lace", {		/* 640x512, 15 kHz, 50 Hz interlaced (PAL) */
+	    640, 512, 640, 512, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_HIRES, 106, 86, 80, 29, 76, 4,
+	    FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"multiscan", {		/* 640x480, 29 kHz, 57 Hz */
+	    640, 480, 640, 480, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_SHRES, 96, 112, 29, 8, 72, 8,
+	    0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"multiscan-lace", {	/* 640x960, 29 kHz, 57 Hz interlaced */
+	    640, 960, 640, 960, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_SHRES, 96, 112, 58, 16, 72, 16,
+	    0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"euro36", {		/* 640x200, 15 kHz, 72 Hz */
+	    640, 200, 640, 200, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_HIRES, 92, 124, 6, 6, 52, 5,
+	    0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"euro36-lace", {	/* 640x400, 15 kHz, 72 Hz interlaced */
+	    640, 400, 640, 400, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_HIRES, 92, 124, 12, 12, 52, 10,
+	    0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"euro72", {		/* 640x400, 29 kHz, 68 Hz */
+	    640, 400, 640, 400, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_SHRES, 164, 92, 9, 9, 80, 8,
+	    0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"euro72-lace", {	/* 640x800, 29 kHz, 68 Hz interlaced */
+	    640, 800, 640, 800, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_SHRES, 164, 92, 18, 18, 80, 16,
+	    0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"super72", {		/* 800x300, 23 kHz, 70 Hz */
+	    800, 300, 800, 300, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_SHRES, 212, 140, 10, 11, 80, 7,
+	    0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"super72-lace", {	/* 800x600, 23 kHz, 70 Hz interlaced */
+	    800, 600, 800, 600, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_SHRES, 212, 140, 20, 22, 80, 14,
+	    0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"dblntsc", {		/* 640x200, 27 kHz, 57 Hz doublescan */
+	    640, 200, 640, 200, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_SHRES, 196, 124, 18, 17, 80, 4,
+	    0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
+	}
+    }, {
+	"dblntsc-ff", {		/* 640x400, 27 kHz, 57 Hz */
+	    640, 400, 640, 400, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_SHRES, 196, 124, 36, 35, 80, 7,
+	    0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"dblntsc-lace", {	/* 640x800, 27 kHz, 57 Hz interlaced */
+	    640, 800, 640, 800, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_SHRES, 196, 124, 72, 70, 80, 14,
+	    0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"dblpal", {		/* 640x256, 27 kHz, 47 Hz doublescan */
+	    640, 256, 640, 256, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_SHRES, 196, 124, 14, 13, 80, 4,
+	    0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
+	}
+    }, {
+	"dblpal-ff", {		/* 640x512, 27 kHz, 47 Hz */
+	    640, 512, 640, 512, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_SHRES, 196, 124, 28, 27, 80, 7,
+	    0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"dblpal-lace", {	/* 640x1024, 27 kHz, 47 Hz interlaced */
+	    640, 1024, 640, 1024, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_SHRES, 196, 124, 56, 54, 80, 14,
+	    0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}
+    },
+
+    /*
+     *  VGA Video Modes
+     */
+
+    {
+	"vga", {		/* 640x480, 31 kHz, 60 Hz (VGA) */
+	    640, 480, 640, 480, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_SHRES, 64, 96, 30, 9, 112, 2,
+	    0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"vga70", {		/* 640x400, 31 kHz, 70 Hz (VGA) */
+	    640, 400, 640, 400, 0, 0, 4, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_SHRES, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_VERT_HIGH_ACT | FB_SYNC_COMP_HIGH_ACT, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}
+    },
 
-	/*
-	 * User Defined Video Modes
-	 */
+#if 0
 
-	{ 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }
+    /*
+     *  A2024 video modes
+     *  These modes don't work yet because there's no A2024 driver.
+     */
+
+    {
+	"a2024-10", {		/* 1024x800, 10 Hz */
+	    1024, 800, 1024, 800, 0, 0, 2, 0,
+	    {0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_HIRES, 0, 0, 0, 0, 0, 0,
+	    0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}
+    }, {
+	"a2024-15", {		/* 1024x800, 15 Hz */
+	    1024, 800, 1024, 800, 0, 0, 2, 0,
+	    {0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0},
+	    0, 0, -1, -1, 0, TAG_HIRES, 0, 0, 0, 0, 0, 0,
+	    0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}
+    }
+#endif
 };
 
-#define NUM_USER_MODES   (8)
-#define NUM_TOTAL_MODES  arraysize(amiga_fb_predefined)
-#define NUM_PREDEF_MODES (NUM_TOTAL_MODES-NUM_USER_MODES)
+#define NUM_TOTAL_MODES  arraysize(amifb_predefined)
 
 static int amifb_ilbm = 0;	/* interleaved or normal bitplanes */
-
 static int amifb_inverse = 0;
-static int amifb_usermode = 0;
+static int amifb_usermode __initdata = 0;
+static int amifb_userdepth __initdata = -1;
 
 	/*
 	 * Some default modes
@@ -1023,6 +990,9 @@
 #define DEFMODE_AMBER_NTSC "ntsc-lace"	/* for flicker fixed NTSC (A3000) */
 #define DEFMODE_AGA        "vga70"	/* for AGA */
 
+static struct fb_var_screeninfo amifb_default;
+
+
 	/*
 	 * Macros for the conversion from real world values to hardware register
 	 * values
@@ -1181,40 +1151,47 @@
 	 * Interface used by the world
 	 */
 
-void amiga_video_setup(char *options, int *ints);
+void amifb_setup(char *options, int *ints);
 
-static int amiga_fb_open(int fbidx);
-static int amiga_fb_release(int fbidx);
-static int amiga_fb_get_fix(struct fb_fix_screeninfo *fix, int con);
-static int amiga_fb_get_var(struct fb_var_screeninfo *var, int con);
-static int amiga_fb_set_var(struct fb_var_screeninfo *var, int con);
-static int amiga_fb_pan_display(struct fb_var_screeninfo *var, int con);
-static int amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con);
-static int amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con);
-static int amiga_fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
-                          u_long arg, int con);
-
-static int amiga_fb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con);
-static int amiga_fb_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con);
-static int amiga_fb_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con);
-static int amiga_fb_get_cursorstate(struct fb_cursorstate *state, int con);
-static int amiga_fb_set_cursorstate(struct fb_cursorstate *state, int con);
+static int amifb_open(struct fb_info *info);
+static int amifb_release(struct fb_info *info);
+static int amifb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			 struct fb_info *info);
+static int amifb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info);
+static int amifb_set_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info);
+static int amifb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info);
+static int amifb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info);
+static int amifb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info);
+static int amifb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg, int con, struct fb_info *info);
+
+static int amifb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con);
+static int amifb_get_var_cursorinfo(struct fb_var_cursorinfo *var,
+				       u_char *data, int con);
+static int amifb_set_var_cursorinfo(struct fb_var_cursorinfo *var,
+				       u_char *data, int con);
+static int amifb_get_cursorstate(struct fb_cursorstate *state, int con);
+static int amifb_set_cursorstate(struct fb_cursorstate *state, int con);
 
 	/*
 	 * Interface to the low level console driver
 	 */
 
-unsigned long amiga_fb_init(unsigned long mem_start);
-static int amifbcon_switch(int con);
-static int amifbcon_updatevar(int con);
-static void amifbcon_blank(int blank);
-static int amifbcon_setcmap(struct fb_cmap *cmap, int con);
+unsigned long amifb_init(unsigned long mem_start);
+static int amifbcon_switch(int con, struct fb_info *info);
+static int amifbcon_updatevar(int con, struct fb_info *info);
+static void amifbcon_blank(int blank, struct fb_info *info);
 
 	/*
 	 * Internal routines
 	 */
 
-static void do_install_cmap(int con);
+static void do_install_cmap(int con, struct fb_info *info);
 static int flash_cursor(void);
 static void amifb_interrupt(int irq, void *dev_id, struct pt_regs *fp);
 static void get_video_mode(const char *name);
@@ -1227,22 +1204,22 @@
 	 */
 
 static int ami_encode_fix(struct fb_fix_screeninfo *fix,
-                          struct amiga_fb_par *par);
+                          struct amifb_par *par);
 static int ami_decode_var(struct fb_var_screeninfo *var,
-                          struct amiga_fb_par *par);
+                          struct amifb_par *par);
 static int ami_encode_var(struct fb_var_screeninfo *var,
-                          struct amiga_fb_par *par);
-static void ami_get_par(struct amiga_fb_par *par);
+                          struct amifb_par *par);
+static void ami_get_par(struct amifb_par *par);
 static void ami_set_var(struct fb_var_screeninfo *var);
 #ifdef DEBUG
-static void ami_set_par(struct amiga_fb_par *par);
+static void ami_set_par(struct amifb_par *par);
 #endif
 static void ami_pan_var(struct fb_var_screeninfo *var);
 static int ami_update_par(void);
 static int ami_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
-                         u_int *transp);
+                         u_int *transp, struct fb_info *info);
 static int ami_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp);
+                         u_int transp, struct fb_info *info);
 static void ami_update_display(void);
 static void ami_init_display(void);
 static void ami_do_blank(void);
@@ -1265,29 +1242,14 @@
 extern unsigned short ami_intena_vals[];
 
 
-static struct fb_ops amiga_fb_ops = {
-	amiga_fb_open, amiga_fb_release, amiga_fb_get_fix, amiga_fb_get_var,
-	amiga_fb_set_var, amiga_fb_get_cmap, amiga_fb_set_cmap,
-	amiga_fb_pan_display, amiga_fb_ioctl
+static struct fb_ops amifb_ops = {
+	amifb_open, amifb_release, amifb_get_fix, amifb_get_var,
+	amifb_set_var, amifb_get_cmap, amifb_set_cmap,
+	amifb_pan_display, NULL, amifb_ioctl
 };
 
-struct useropts {
-	long xres;
-	long yres;
-	long xres_virtual;
-	long yres_virtual;
-	long bits_per_pixel;
-	long left_margin;
-	long right_margin;
-	long upper_margin;
-	long lower_margin;
-	long hsync_len;
-	long vsync_len;
-} useropts __initdata = {
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-};
 
-__initfunc(void amiga_video_setup(char *options, int *ints))
+__initfunc(void amifb_setup(char *options, int *ints))
 {
 	char *this_opt;
 	char mcap_spec[80];
@@ -1313,51 +1275,51 @@
 		else if (!strncmp(this_opt, "fstart:", 7))
 			min_fstrt = simple_strtoul(this_opt+7, NULL, 0);
 		else if (!strncmp(this_opt, "depth:", 6))
-			useropts.bits_per_pixel = simple_strtoul(this_opt+6, NULL, 0);
+			amifb_userdepth = simple_strtoul(this_opt+6, NULL, 0);
 		else if (!strncmp(this_opt, "size:", 5)) {
 			p = this_opt + 5;
 			if (*p != ';')
-				useropts.xres = simple_strtoul(p, NULL, 0);
+				amifb_default.xres = simple_strtoul(p, NULL, 0);
 			if (!(p = strchr(p, ';')))
 				continue;
 			if (*++p != ';')
-				useropts.yres = simple_strtoul(p, NULL, 0);
+				amifb_default.yres = simple_strtoul(p, NULL, 0);
 			if (!(p = strchr(p, ';')))
 				continue;
 			if (*++p != ';')
-				useropts.xres_virtual = simple_strtoul(p, NULL, 0);
+				amifb_default.xres_virtual = simple_strtoul(p, NULL, 0);
 			if (!(p = strchr(p, ';')))
 				continue;
 			if (*++p != ';')
-				useropts.yres_virtual = simple_strtoul(p, NULL, 0);
+				amifb_default.yres_virtual = simple_strtoul(p, NULL, 0);
 			if (!(p = strchr(p, ';')))
 				continue;
 			if (*++p)
-				useropts.bits_per_pixel = simple_strtoul(p, NULL, 0);
+				amifb_default.bits_per_pixel = simple_strtoul(p, NULL, 0);
 		} else if (!strncmp(this_opt, "timing:", 7)) {
 			p = this_opt + 7;
 			if (*p != ';')
-				useropts.left_margin = simple_strtoul(p, NULL, 0);
+				amifb_default.left_margin = simple_strtoul(p, NULL, 0);
 			if (!(p = strchr(p, ';')))
 				continue;
 			if (*++p != ';')
-				useropts.right_margin = simple_strtoul(p, NULL, 0);
+				amifb_default.right_margin = simple_strtoul(p, NULL, 0);
 			if (!(p = strchr(p, ';')))
 				continue;
 			if (*++p != ';')
-				useropts.upper_margin = simple_strtoul(p, NULL, 0);
+				amifb_default.upper_margin = simple_strtoul(p, NULL, 0);
 			if (!(p = strchr(p, ';')))
 				continue;
 			if (*++p)
-				useropts.lower_margin = simple_strtoul(p, NULL, 0);
+				amifb_default.lower_margin = simple_strtoul(p, NULL, 0);
 		} else if (!strncmp(this_opt, "sync:", 5)) {
 			p = this_opt + 5;
 			if (*p != ';')
-				useropts.hsync_len = simple_strtoul(p, NULL, 0);
+				amifb_default.hsync_len = simple_strtoul(p, NULL, 0);
 			if (!(p = strchr(p, ';')))
 				continue;
 			if (*++p)
-				useropts.vsync_len = simple_strtoul(p, NULL, 0);
+				amifb_default.vsync_len = simple_strtoul(p, NULL, 0);
 		} else
 			get_video_mode(this_opt);
 	}
@@ -1395,10 +1357,10 @@
 		if (hmax <= 0 || hmax <= hmin)
 			goto cap_invalid;
 
-		vfmin = vmin;
-		vfmax = vmax;
-		hfmin = hmin;
-		hfmax = hmax;
+		fb_info.monspecs.vfmin = vmin;
+		fb_info.monspecs.vfmax = vmax;
+		fb_info.monspecs.hfmin = hmin;
+		fb_info.monspecs.hfmax = hmax;
 cap_invalid:
 		;
 	}
@@ -1408,7 +1370,7 @@
 	 * Open/Release the frame buffer device
 	 */
 
-static int amiga_fb_open(int fbidx)
+static int amifb_open(struct fb_info *info)
 {
 	/*
 	 * Nothing, only a usage count for the moment
@@ -1418,7 +1380,7 @@
 	return(0);
 }
 
-static int amiga_fb_release(int fbidx)
+static int amifb_release(struct fb_info *info)
 {
 	MOD_DEC_USE_COUNT;
 	return(0);
@@ -1429,9 +1391,10 @@
 	 * Get the Fixed Part of the Display
 	 */
 
-static int amiga_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
+static int amifb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			 struct fb_info *info)
 {
-	struct amiga_fb_par par;
+	struct amifb_par par;
 
 	if (con == -1)
 		ami_get_par(&par);
@@ -1448,12 +1411,13 @@
 	 * Get the User Defined Part of the Display
 	 */
 
-static int amiga_fb_get_var(struct fb_var_screeninfo *var, int con)
+static int amifb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info)
 {
 	int err = 0;
 
 	if (con == -1) {
-		struct amiga_fb_par par;
+		struct amifb_par par;
 
 		ami_get_par(&par);
 		err = ami_encode_var(var, &par);
@@ -1466,11 +1430,12 @@
 	 * Set the User Defined Part of the Display
 	 */
 
-static int amiga_fb_set_var(struct fb_var_screeninfo *var, int con)
+static int amifb_set_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info)
 {
 	int err, activate = var->activate;
 	int oldxres, oldyres, oldvxres, oldvyres, oldbpp;
-	struct amiga_fb_par par;
+	struct amifb_par par;
 
 	struct display *display;
 	if (con >= 0)
@@ -1513,13 +1478,32 @@
 			display->line_length = fix.line_length;
 			display->can_soft_blank = 1;
 			display->inverse = amifb_inverse;
+			switch (fix.type) {
+#ifdef CONFIG_FBCON_ILBM
+			    case FB_TYPE_INTERLEAVED_PLANES:
+				display->dispsw = &fbcon_ilbm;
+				break;
+#endif
+#ifdef CONFIG_FBCON_AFB
+			    case FB_TYPE_PLANES:
+				display->dispsw = &fbcon_afb;
+				break;
+#endif
+#ifdef CONFIG_FBCON_MFB
+			    case FB_TYPE_PACKED_PIXELS:	/* depth == 1 */
+				display->dispsw = &fbcon_mfb;
+				break;
+#endif
+			    default:
+				display->dispsw = NULL;
+			}
 			if (fb_info.changevar)
 				(*fb_info.changevar)(con);
 		}
 		if (oldbpp != var->bits_per_pixel) {
 			if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
 				return err;
-			do_install_cmap(con);
+			do_install_cmap(con, info);
 		}
 		if (con == currcon)
 			ami_set_var(&display->var);
@@ -1533,7 +1517,8 @@
 	 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
 	 */
 
-static int amiga_fb_pan_display(struct fb_var_screeninfo *var, int con)
+static int amifb_pan_display(struct fb_var_screeninfo *var, int con,
+				struct fb_info *info)
 {
 	if (var->vmode & FB_VMODE_YWRAP) {
 		if (var->yoffset<0 || var->yoffset >= fb_display[con].var.yres_virtual || var->xoffset)
@@ -1562,15 +1547,16 @@
 	 * Get the Colormap
 	 */
 
-static int amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+static int amifb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info)
 {
 	if (con == currcon) /* current console? */
 		return fb_get_cmap(cmap, &fb_display[con].var, kspc,
-				   ami_getcolreg);
+				   ami_getcolreg, info);
 	else if (fb_display[con].cmap.len) /* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
 	else
-		fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+		fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
 			     cmap, kspc ? 0 : 2);
 	return 0;
 }
@@ -1579,7 +1565,8 @@
 	 * Set the Colormap
 	 */
 
-static int amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+static int amifb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info)
 {
 	int err;
 
@@ -1591,7 +1578,7 @@
 	}
 	if (con == currcon)			/* current console? */
 		return fb_set_cmap(cmap, &fb_display[con].var, kspc,
-				   ami_setcolreg);
+				   ami_setcolreg, info);
 	else
 		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
 	return 0;
@@ -1601,8 +1588,8 @@
 	 * Amiga Frame Buffer Specific ioctls
 	 */
 
-static int amiga_fb_ioctl(struct inode *inode, struct file *file,
-                          u_int cmd, u_long arg, int con)
+static int amifb_ioctl(struct inode *inode, struct file *file,
+                       u_int cmd, u_long arg, int con, struct fb_info *info)
 {
 	int i;
 
@@ -1612,7 +1599,7 @@
 			
 			i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrfix));
 			if (!i) {
-				i = amiga_fb_get_fix_cursorinfo(&crsrfix, con);
+				i = amifb_get_fix_cursorinfo(&crsrfix, con);
 				copy_to_user((void *)arg, &crsrfix, sizeof(crsrfix));
 			}
 			return i;
@@ -1622,7 +1609,7 @@
 
 			i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrvar));
 			if (!i) {
-				i = amiga_fb_get_var_cursorinfo(&crsrvar,
+				i = amifb_get_var_cursorinfo(&crsrvar,
 					((struct fb_var_cursorinfo *)arg)->data, con);
 				copy_to_user((void *)arg, &crsrvar, sizeof(crsrvar));
 			}
@@ -1634,7 +1621,7 @@
 			i = verify_area(VERIFY_READ, (void *)arg, sizeof(crsrvar));
 			if (!i) {
 				copy_from_user(&crsrvar, (void *)arg, sizeof(crsrvar));
-				i = amiga_fb_set_var_cursorinfo(&crsrvar,
+				i = amifb_set_var_cursorinfo(&crsrvar,
 					((struct fb_var_cursorinfo *)arg)->data, con);
 			}
 			return i;
@@ -1644,7 +1631,7 @@
 
 			i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrstate));
 			if (!i) {
-				i = amiga_fb_get_cursorstate(&crsrstate, con);
+				i = amifb_get_cursorstate(&crsrstate, con);
 				copy_to_user((void *)arg, &crsrstate, sizeof(crsrstate));
 			}
 			return i;
@@ -1655,27 +1642,27 @@
 			i = verify_area(VERIFY_READ, (void *)arg, sizeof(crsrstate));
 			if (!i) {
 				copy_from_user(&crsrstate, (void *)arg, sizeof(crsrstate));
-				i = amiga_fb_set_cursorstate(&crsrstate, con);
+				i = amifb_set_cursorstate(&crsrstate, con);
 			}
 			return i;
 		}
 #ifdef DEBUG
 		case FBCMD_GET_CURRENTPAR : {
-			struct amiga_fb_par par;
+			struct amifb_par par;
 
-			i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct amiga_fb_par));
+			i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct amifb_par));
 			if (!i) {
 				ami_get_par(&par);
-				copy_to_user((void *)arg, &par, sizeof(struct amiga_fb_par));
+				copy_to_user((void *)arg, &par, sizeof(struct amifb_par));
 			}
 			return i;
 		}
 		case FBCMD_SET_CURRENTPAR : {
-			struct amiga_fb_par par;
+			struct amifb_par par;
 
-			i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct amiga_fb_par));
+			i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct amifb_par));
 			if (!i) {
-				copy_from_user(&par, (void *)arg, sizeof(struct amiga_fb_par));
+				copy_from_user(&par, (void *)arg, sizeof(struct amifb_par));
 				ami_set_par(&par);
 			}
 			return i;
@@ -1689,27 +1676,27 @@
 	 * Hardware Cursor
 	 */
 
-static int amiga_fb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con)
+static int amifb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con)
 {
 	return ami_get_fix_cursorinfo(fix, con);
 }
 
-static int amiga_fb_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con)
+static int amifb_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con)
 {
 	return ami_get_var_cursorinfo(var, data, con);
 }
 
-static int amiga_fb_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con)
+static int amifb_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con)
 {
 	return ami_set_var_cursorinfo(var, data, con);
 }
 
-static int amiga_fb_get_cursorstate(struct fb_cursorstate *state, int con)
+static int amifb_get_cursorstate(struct fb_cursorstate *state, int con)
 {
 	return ami_get_cursorstate(state, con);
 }
 
-static int amiga_fb_set_cursorstate(struct fb_cursorstate *state, int con)
+static int amifb_set_cursorstate(struct fb_cursorstate *state, int con)
 {
 	return ami_set_cursorstate(state, con);
 }
@@ -1719,7 +1706,7 @@
 	 * Initialisation
 	 */
 
-__initfunc(unsigned long amiga_fb_init(unsigned long mem_start))
+__initfunc(unsigned long amifb_init(unsigned long mem_start))
 {
 	int err, tag, i;
 	u_long chipptr;
@@ -1745,7 +1732,7 @@
 	switch (amiga_chipset) {
 #ifdef CONFIG_FB_AMIGA_OCS
 		case CS_OCS:
-			strcat(amiga_fb_name, "OCS");
+			strcat(amifb_name, "OCS");
 default_chipset:
 			chipset = TAG_OCS;
 			maxdepth[TAG_SHRES] = 0;	/* OCS means no SHRES */
@@ -1761,7 +1748,7 @@
 
 #ifdef CONFIG_FB_AMIGA_ECS
 		case CS_ECS:
-			strcat(amiga_fb_name, "ECS");
+			strcat(amifb_name, "ECS");
 			chipset = TAG_ECS;
 			maxdepth[TAG_SHRES] = 2;
 			maxdepth[TAG_HIRES] = 4;
@@ -1785,7 +1772,7 @@
 
 #ifdef CONFIG_FB_AMIGA_AGA
 		case CS_AGA:
-			strcat(amiga_fb_name, "AGA");
+			strcat(amifb_name, "AGA");
 			chipset = TAG_AGA;
 			maxdepth[TAG_SHRES] = 8;
 			maxdepth[TAG_HIRES] = 8;
@@ -1804,7 +1791,7 @@
 		default:
 #ifdef CONFIG_FB_AMIGA_OCS
 			printk("Unknown graphics chipset, defaulting to OCS\n");
-			strcat(amiga_fb_name, "Unknown");
+			strcat(amifb_name, "Unknown");
 			goto default_chipset;
 #else /* CONFIG_FB_AMIGA_OCS */
 			return mem_start;
@@ -1824,26 +1811,42 @@
 	 * Replace the Tag Values with the Real Pixel Clock Values
 	 */
 
-	for (i = 0; i < NUM_PREDEF_MODES; i++) {
-		tag = amiga_fb_predefined[i].pixclock;
+	if (amifb_userdepth != -1)
+		amifb_default.bits_per_pixel = amifb_userdepth;
+	for (i = 0; i < NUM_TOTAL_MODES; i++) {
+		struct fb_var_screeninfo *var = &amifb_predefined[i].var;
+		tag = var->pixclock;
 		if (tag == TAG_SHRES || tag == TAG_HIRES || tag == TAG_LORES) {
-			amiga_fb_predefined[i].pixclock = pixclock[tag];
-			if (amiga_fb_predefined[i].bits_per_pixel > maxdepth[tag])
-				amiga_fb_predefined[i].bits_per_pixel = maxdepth[tag];
+			var->pixclock = pixclock[tag];
+			if (var->bits_per_pixel > maxdepth[tag])
+				var->bits_per_pixel = maxdepth[tag];
 		}
 	}
+	tag = amifb_default.pixclock;
+	if (tag == TAG_SHRES || tag == TAG_HIRES || tag == TAG_LORES) {
+		amifb_default.pixclock = pixclock[tag];
+		if (amifb_default.bits_per_pixel > maxdepth[tag])
+			amifb_default.bits_per_pixel = maxdepth[tag];
+	}
 
-	strcpy(fb_info.modename, amiga_fb_name);
+	/*
+	 *  These monitor specs are for a typical Amiga monitor (e.g. A1960)
+	 */
+	if (fb_info.monspecs.hfmin == 0) {
+	    fb_info.monspecs.hfmin = 15000;
+	    fb_info.monspecs.hfmax = 38000;
+	    fb_info.monspecs.vfmin = 49;
+	    fb_info.monspecs.vfmax = 90;
+	}
+
+	strcpy(fb_info.modename, amifb_name);
 	fb_info.changevar = NULL;
 	fb_info.node = -1;
-	fb_info.fbops = &amiga_fb_ops;
-	fb_info.fbvar_num = NUM_TOTAL_MODES;
-	fb_info.fbvar = amiga_fb_predefined;
+	fb_info.fbops = &amifb_ops;
 	fb_info.disp = &disp;
 	fb_info.switch_con = &amifbcon_switch;
 	fb_info.updatevar = &amifbcon_updatevar;
 	fb_info.blank = &amifbcon_blank;
-	fb_info.setcmap = &amifbcon_setcmap;
 
 	err = register_framebuffer(&fb_info);
 	if (err < 0)
@@ -1889,10 +1892,11 @@
 	custom.intena = IF_VERTB;
 	custom.intena = IF_SETCLR | IF_COPER;
 
-	amiga_fb_set_var(&amiga_fb_predefined[0], -1);
+	amifb_set_var(&amifb_default, -1, &fb_info);
 
-	printk("%s frame buffer device, using %ldK of video memory\n",
-	       fb_info.modename, videomemorysize>>10);
+	printk("fb%d: %s frame buffer device, using %ldK of video memory\n",
+	       GET_FB_IDX(fb_info.node), fb_info.modename,
+	       videomemorysize>>10);
 
 	/* TODO: This driver cannot be unloaded yet */
 	MOD_INC_USE_COUNT;
@@ -1900,17 +1904,17 @@
 	return mem_start;
 }
 
-static int amifbcon_switch(int con)
+static int amifbcon_switch(int con, struct fb_info *info)
 {
 	/* Do we have to save the colormap? */
 	if (fb_display[currcon].cmap.len)
 		fb_get_cmap(&fb_display[currcon].cmap,
-			    &fb_display[currcon].var, 1, ami_getcolreg);
+			    &fb_display[currcon].var, 1, ami_getcolreg, info);
 
 	currcon = con;
 	ami_set_var(&fb_display[con].var);
 	/* Install new colormap */
-	do_install_cmap(con);
+	do_install_cmap(con, info);
 	return 0;
 }
 
@@ -1918,7 +1922,7 @@
 	 * Update the `var' structure (called by fbcon.c)
 	 */
 
-static int amifbcon_updatevar(int con)
+static int amifbcon_updatevar(int con, struct fb_info *info)
 {
 	ami_pan_var(&fb_display[con].var);
 	return 0;
@@ -1928,7 +1932,7 @@
 	 * Blank the display.
 	 */
 
-static void amifbcon_blank(int blank)
+static void amifbcon_blank(int blank, struct fb_info *info)
 {
 	do_blank = blank ? blank : -1;
 }
@@ -1937,23 +1941,17 @@
 	 * Set the colormap
 	 */
 
-static int amifbcon_setcmap(struct fb_cmap *cmap, int con)
-{
-	return(amiga_fb_set_cmap(cmap, 1, con));
-}
-
-
-static void do_install_cmap(int con)
+static void do_install_cmap(int con, struct fb_info *info)
 {
 	if (con != currcon)
 		return;
 	if (fb_display[con].cmap.len)
 		fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
-			    ami_setcolreg);
+			    ami_setcolreg, info);
 	else
-		fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
 					    &fb_display[con].var, 1,
-					    ami_setcolreg);
+					    ami_setcolreg, info);
 }
 
 static int flash_cursor(void)
@@ -2039,33 +2037,9 @@
 {
 	int i;
 
-	for (i = 1; i < NUM_PREDEF_MODES; i++) {
-		if (!strcmp(name, amiga_fb_modenames[i])) {
-			amiga_fb_predefined[0] = amiga_fb_predefined[i];
-
-			if (useropts.xres != -1)
-				amiga_fb_predefined[0].xres = useropts.xres;
-			if (useropts.yres != -1)
-				amiga_fb_predefined[0].yres = useropts.yres;
-			if (useropts.xres_virtual != -1)
-				amiga_fb_predefined[0].xres_virtual = useropts.xres_virtual;
-			if (useropts.yres_virtual != -1)
-				amiga_fb_predefined[0].yres_virtual = useropts.yres_virtual;
-			if (useropts.bits_per_pixel != -1)
-				amiga_fb_predefined[0].bits_per_pixel = useropts.bits_per_pixel;
-			if (useropts.left_margin != -1)
-				amiga_fb_predefined[0].left_margin = useropts.left_margin;
-			if (useropts.right_margin != -1)
-				amiga_fb_predefined[0].right_margin = useropts.right_margin;
-			if (useropts.upper_margin != -1)
-				amiga_fb_predefined[0].upper_margin = useropts.upper_margin;
-			if (useropts.lower_margin != -1)
-				amiga_fb_predefined[0].lower_margin = useropts.lower_margin;
-			if (useropts.hsync_len != -1)
-				amiga_fb_predefined[0].hsync_len = useropts.hsync_len;
-			if (useropts.vsync_len != -1)
-				amiga_fb_predefined[0].vsync_len = useropts.vsync_len;
-
+	for (i = 0; i < NUM_TOTAL_MODES; i++) {
+		if (!strcmp(name, amifb_predefined[i].name)) {
+			amifb_default = amifb_predefined[i].var;
 			amifb_usermode = i;
 			return;
 		}
@@ -2073,23 +2047,22 @@
 }
 
 	/*
-	 * Probe the  Video Modes
+	 *  Probe the Video Modes
 	 */
 
 __initfunc(static void check_default_mode(void))
 {
-	struct amiga_fb_par par;
+	struct amifb_par par;
 	int mode;
 
-	for (mode = 0; mode < NUM_PREDEF_MODES; mode++) {
-		if (!ami_decode_var(&amiga_fb_predefined[mode], &par)) {
-			if (mode)
-				amiga_fb_predefined[0] = amiga_fb_predefined[mode];
+	if (!ami_decode_var(&amifb_default, &par))
+		return;
+	printk("Can't use default video mode. Probing video modes...\n");
+	for (mode = 0; mode < NUM_TOTAL_MODES; mode++)
+		if (!ami_decode_var(&amifb_predefined[mode].var, &par)) {
+			amifb_default = amifb_predefined[mode].var;
 			return;
 		}
-		if (!mode)
-			printk("Can't use default video mode. Probing video modes...\n");
-	}
 	panic("Can't find any usable video mode");
 }
 
@@ -2141,13 +2114,19 @@
 	 */
 
 static int ami_encode_fix(struct fb_fix_screeninfo *fix,
-                          struct amiga_fb_par *par)
+                          struct amifb_par *par)
 {
 	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
-	strcpy(fix->id, amiga_fb_name);
+	strcpy(fix->id, amifb_name);
 	fix->smem_start = (char *)videomemory;
 	fix->smem_len = videomemorysize;
 
+#ifdef CONFIG_FBCON_MFB
+	if (par->bpp == 1) {
+		fix->type = FB_TYPE_PACKED_PIXELS;
+		fix->type_aux = 0;
+	} else
+#endif
 	if (amifb_ilbm) {
 		fix->type = FB_TYPE_INTERLEAVED_PLANES;
 		fix->type_aux = par->next_line;
@@ -2169,6 +2148,7 @@
 			fix->xpanstep = 16<<maxfmode;
 		fix->ypanstep = 1;
 	}
+	fix->accel = FB_ACCEL_AMIGABLITT;
 	return 0;
 }
 
@@ -2178,11 +2158,11 @@
 	 */
 
 static int ami_decode_var(struct fb_var_screeninfo *var,
-                          struct amiga_fb_par *par)
+                          struct amifb_par *par)
 {
 	u_short clk_shift, line_shift;
 	u_long maxfetchstop, fstrt, fsize, fconst, xres_n, yres_n;
-	u_long hrate = 0, vrate = 0;
+	u_int htotal, vtotal;
 
 	/*
 	 * Find a matching Pixel Clock
@@ -2319,8 +2299,8 @@
 				DPRINTK("diwstrt_v too low for pal\n");
 				return -EINVAL;
 			}
-			hrate = 15625;
-			vrate = 50;
+			htotal = PAL_HTOTAL>>clk_shift;
+			vtotal = PAL_VTOTAL>>1;
 			if (!IS_OCS) {
 				par->beamcon0 = BMC0_PAL;
 				par->bplcon3 |= BPC3_BRDRBLNK;
@@ -2349,8 +2329,8 @@
 				DPRINTK("diwstrt_v too low for ntsc\n");
 				return -EINVAL;
 			}
-			hrate = 15750;
-			vrate = 60;
+			htotal = NTSC_HTOTAL>>clk_shift;
+			vtotal = NTSC_VTOTAL>>1;
 			if (!IS_OCS) {
 				par->beamcon0 = 0;
 				par->bplcon3 |= BPC3_BRDRBLNK;
@@ -2409,9 +2389,8 @@
 			par->beamcon0 |= BMC0_VSYTRUE;
 		if (var->sync & FB_SYNC_COMP_HIGH_ACT)
 			par->beamcon0 |= BMC0_CSYTRUE;
-		hrate = (amiga_masterclock+par->htotal/2)/par->htotal;
-		vrate = div2(par->vtotal) * par->htotal;
-		vrate = (amiga_masterclock+vrate/2)/vrate;
+		htotal = par->htotal>>clk_shift;
+		vtotal = par->vtotal>>1;
 	} else {
 		DPRINTK("only broadcast modes possible for ocs\n");
 		return -EINVAL;
@@ -2546,7 +2525,8 @@
 	par->crsr.spot_x = par->crsr.spot_y = 0;
 	par->crsr.height = par->crsr.width = 0;
 
-	if (hrate < hfmin || hrate > hfmax || vrate < vfmin || vrate > vfmax) {
+	if (!fbmon_valid_timings(pixclock[clk_shift], htotal, vtotal,
+				 &fb_info)) {
 		DPRINTK("mode doesn't fit for monitor\n");
 		return -EINVAL;
 	}
@@ -2560,7 +2540,7 @@
 	 */
 
 static int ami_encode_var(struct fb_var_screeninfo *var,
-                          struct amiga_fb_par *par)
+                          struct amifb_par *par)
 {
 	u_short clk_shift, line_shift;
 	int i;
@@ -2606,7 +2586,6 @@
 
 	var->height = -1;
 	var->width = -1;
-	var->accel = 0;
 
 	var->pixclock = pixclock[clk_shift];
 
@@ -2657,7 +2636,7 @@
 	 * Get current hardware setting
 	 */
 
-static void ami_get_par(struct amiga_fb_par *par)
+static void ami_get_par(struct amifb_par *par)
 {
 	*par = currentpar;
 }
@@ -2676,7 +2655,7 @@
 }
 
 #ifdef DEBUG
-static void ami_set_par(struct amiga_fb_par *par)
+static void ami_set_par(struct amifb_par *par)
 {
 	do_vmode_pan = 0;
 	do_vmode_full = 0;
@@ -2695,7 +2674,7 @@
 
 static void ami_pan_var(struct fb_var_screeninfo *var)
 {
-	struct amiga_fb_par *par = &currentpar;
+	struct amifb_par *par = &currentpar;
 
 	par->xoffset = var->xoffset;
 	par->yoffset = var->yoffset;
@@ -2715,7 +2694,7 @@
 
 static int ami_update_par(void)
 {
-	struct amiga_fb_par *par = &currentpar;
+	struct amifb_par *par = &currentpar;
 	short clk_shift, vshift, fstrt, fsize, fstop, fconst,  shift, move, mod;
 
 	clk_shift = par->clk_shift;
@@ -2780,7 +2759,7 @@
 	 */
 
 static int ami_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
-                         u_int *transp)
+                         u_int *transp, struct fb_info *info)
 {
 	if (IS_AGA) {
 		if (regno > 255)
@@ -2804,7 +2783,7 @@
 	 */
 
 static int ami_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp)
+                         u_int transp, struct fb_info *info)
 {
 #if defined(CONFIG_FB_AMIGA_AGA)
 	u_short bplcon3 = currentpar.bplcon3;
@@ -2867,7 +2846,7 @@
 
 static void ami_update_display(void)
 {
-	struct amiga_fb_par *par = &currentpar;
+	struct amifb_par *par = &currentpar;
 
 	custom.bplcon1 = par->bplcon1;
 	custom.bpl1mod = par->bpl1mod;
@@ -2882,7 +2861,7 @@
 
 static void ami_init_display(void)
 {
-	struct amiga_fb_par *par = &currentpar;
+	struct amifb_par *par = &currentpar;
 
 	custom.bplcon0 = par->bplcon0 & ~BPC0_LACE;
 	custom.bplcon2 = (IS_OCS ? 0 : BPC2_KILLEHB) | BPC2_PF2P2 | BPC2_PF1P2;
@@ -2938,7 +2917,7 @@
 
 static void ami_do_blank(void)
 {
-	struct amiga_fb_par *par = &currentpar;
+	struct amifb_par *par = &currentpar;
 #if defined(CONFIG_FB_AMIGA_AGA)
 	u_short bplcon3 = par->bplcon3;
 #endif
@@ -3023,7 +3002,7 @@
 
 static int ami_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con)
 {
-	struct amiga_fb_par *par = &currentpar;
+	struct amifb_par *par = &currentpar;
 
 	fix->crsr_width = fix->crsr_xsize = par->crsr.width;
 	fix->crsr_height = fix->crsr_ysize = par->crsr.height;
@@ -3034,7 +3013,7 @@
 
 static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con)
 {
-	struct amiga_fb_par *par = &currentpar;
+	struct amifb_par *par = &currentpar;
 	register u_short *lspr, *sspr;
 #ifdef __mc68000__
 	register u_long datawords asm ("d2");
@@ -3109,7 +3088,7 @@
 
 static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con)
 {
-	struct amiga_fb_par *par = &currentpar;
+	struct amifb_par *par = &currentpar;
 	register u_short *lspr, *sspr;
 #ifdef __mc68000__
 	register u_long datawords asm ("d2");
@@ -3228,7 +3207,7 @@
 
 static int ami_get_cursorstate(struct fb_cursorstate *state, int con)
 {
-	struct amiga_fb_par *par = &currentpar;
+	struct amifb_par *par = &currentpar;
 
 	state->xoffset = par->crsr.crsr_x;
 	state->yoffset = par->crsr.crsr_y;
@@ -3238,7 +3217,7 @@
 
 static int ami_set_cursorstate(struct fb_cursorstate *state, int con)
 {
-	struct amiga_fb_par *par = &currentpar;
+	struct amifb_par *par = &currentpar;
 
 	par->crsr.crsr_x = state->xoffset;
 	par->crsr.crsr_y = state->yoffset;
@@ -3250,7 +3229,7 @@
 
 static void ami_set_sprite(void)
 {
-	struct amiga_fb_par *par = &currentpar;
+	struct amifb_par *par = &currentpar;
 	copins *copl, *cops;
 	u_short hs, vs, ve;
 	u_long pl, ps, pt;
@@ -3333,7 +3312,7 @@
 
 static void ami_reinit_copper(void)
 {
-	struct amiga_fb_par *par = &currentpar;
+	struct amifb_par *par = &currentpar;
 
 	copdisplay.init[cip_bplcon0].w[1] = ~(BPC0_BPU3 | BPC0_BPU2 | BPC0_BPU1 | BPC0_BPU0) & par->bplcon0;
 	copdisplay.wait->l = CWAIT(32, par->diwstrt_v-4);
@@ -3345,7 +3324,7 @@
 
 static void ami_build_copper(void)
 {
-	struct amiga_fb_par *par = &currentpar;
+	struct amifb_par *par = &currentpar;
 	copins *copl, *cops;
 	u_long p;
 
@@ -3422,7 +3401,7 @@
 
 static void ami_rebuild_copper(void)
 {
-	struct amiga_fb_par *par = &currentpar;
+	struct amifb_par *par = &currentpar;
 	copins *copl, *cops;
 	u_short line, h_end1, h_end2;
 	short i;
@@ -3507,7 +3486,7 @@
 #ifdef MODULE
 int init_module(void)
 {
-	return(amiga_fb_init(NULL));
+	return(amifb_init(NULL));
 }
 
 void cleanup_module(void)
diff -u --recursive --new-file v2.1.90/linux/drivers/video/atafb.c linux/drivers/video/atafb.c
--- v2.1.90/linux/drivers/video/atafb.c	Mon Nov 24 01:18:51 1997
+++ linux/drivers/video/atafb.c	Sat Mar 21 11:12:49 1998
@@ -66,10 +66,31 @@
 
 #include <asm/atarihw.h>
 #include <asm/atariints.h>
+#include <asm/atari_stram.h>
 
 #include <linux/fb.h>
 #include <asm/atarikb.h>
 
+#ifdef CONFIG_FBCON_CFB8
+#include "fbcon-cfb8.h"
+#endif
+#ifdef CONFIG_FBCON_CFB16
+#include "fbcon-cfb16.h"
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P2
+#include "fbcon-iplan2p2.h"
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P4
+#include "fbcon-iplan2p4.h"
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P8
+#include "fbcon-iplan2p8.h"
+#endif
+#ifdef CONFIG_FBCON_MFB
+#include "fbcon-mfb.h"
+#endif
+
+
 #define SWITCH_ACIA 0x01		/* modes for switch on OverScan */
 #define SWITCH_SND6 0x40
 #define SWITCH_SND7 0x80
@@ -92,16 +113,18 @@
 static int sttt_xres=640,st_yres=400,tt_yres=480;
 static int sttt_xres_virtual=640,sttt_yres_virtual=400;
 static int ovsc_offset=0, ovsc_addlen=0;
-int        ovsc_switchmode=0;
 
-static struct atari_fb_par {
+static struct atafb_par {
 	unsigned long screen_base;
 	int yres_virtual;
+#if defined ATAFB_TT || defined ATAFB_STE
 	union {
 		struct {
 			int mode;
 			int sync;
 		} tt, st;
+#endif
+#ifdef ATAFB_FALCON
 		struct falcon_hw {
 			/* Here are fields for storing a video mode, as direct
 			 * parameters for the hardware.
@@ -121,6 +144,7 @@
 			short ste_mode;
 			short bpp;
 		} falcon;
+#endif
 		/* Nothing needed for external mode */
 	} hw;
 } current_par;
@@ -130,6 +154,7 @@
  * hardware extensions (e.g. ScreenBlaster)  */
 static int DontCalcRes = 0; 
 
+#ifdef ATAFB_FALCON
 #define HHT hw.falcon.hht
 #define HBB hw.falcon.hbb
 #define HBE hw.falcon.hbe
@@ -150,6 +175,7 @@
 #define VMO_DOUBLE		0x01
 #define VMO_INTER		0x02
 #define VMO_PREMASK		0x0c
+#endif
 
 static struct fb_info fb_info;
 
@@ -239,26 +265,22 @@
 extern int fontwidth_8x16;
 extern unsigned char fontdata_8x16[];
 
-/* import first 16 colors from fbcon.c */
-extern unsigned short packed16_cmap[16];
-
-
 /* ++roman: This structure abstracts from the underlying hardware (ST(e),
  * TT, or Falcon.
  *
  * int (*detect)( void )
  *   This function should detect the current video mode settings and
- *   store them in atari_fb_predefined[0] for later reference by the
+ *   store them in atafb_predefined[0] for later reference by the
  *   user. Return the index+1 of an equivalent predefined mode or 0
  *   if there is no such.
  * 
  * int (*encode_fix)( struct fb_fix_screeninfo *fix,
- *                    struct atari_fb_par *par )
+ *                    struct atafb_par *par )
  *   This function should fill in the 'fix' structure based on the
  *   values in the 'par' structure.
  *   
  * int (*decode_var)( struct fb_var_screeninfo *var,
- *                    struct atari_fb_par *par )
+ *                    struct atafb_par *par )
  *   Get the video params out of 'var'. If a value doesn't fit, round
  *   it up, if it's too big, return EINVAL.
  *   Round up in the following order: bits_per_pixel, xres, yres, 
@@ -266,26 +288,26 @@
  *   horizontal timing, vertical timing.
  *
  * int (*encode_var)( struct fb_var_screeninfo *var,
- *                    struct atari_fb_par *par );
+ *                    struct atafb_par *par );
  *   Fill the 'var' structure based on the values in 'par' and maybe
  *   other values read out of the hardware.
  *   
- * void (*get_par)( struct atari_fb_par *par )
+ * void (*get_par)( struct atafb_par *par )
  *   Fill the hardware's 'par' structure.
  *   
- * void (*set_par)( struct atari_fb_par *par )
+ * void (*set_par)( struct atafb_par *par )
  *   Set the hardware according to 'par'.
  *   
  * int (*setcolreg)( unsigned regno, unsigned red,
  *                   unsigned green, unsigned blue,
- *                   unsigned transp )
+ *                   unsigned transp, struct fb_info *info )
  *   Set a single color register. The values supplied are already
  *   rounded down to the hardware's capabilities (according to the
  *   entries in the var structure). Return != 0 for invalid regno.
  *
  * int (*getcolreg)( unsigned regno, unsigned *red,
  *                   unsigned *green, unsigned *blue,
- *                   unsigned *transp )
+ *                   unsigned *transp, struct fb_info *info )
  *   Read a single color register and split it into
  *   colors/transparent. Return != 0 for invalid regno.
  *
@@ -305,23 +327,23 @@
 static struct fb_hwswitch {
 	int  (*detect)( void );
 	int  (*encode_fix)( struct fb_fix_screeninfo *fix,
-						struct atari_fb_par *par );
+						struct atafb_par *par );
 	int  (*decode_var)( struct fb_var_screeninfo *var,
-						struct atari_fb_par *par );
+						struct atafb_par *par );
 	int  (*encode_var)( struct fb_var_screeninfo *var,
-						struct atari_fb_par *par );
-	void (*get_par)( struct atari_fb_par *par );
-	void (*set_par)( struct atari_fb_par *par );
+						struct atafb_par *par );
+	void (*get_par)( struct atafb_par *par );
+	void (*set_par)( struct atafb_par *par );
 	int  (*getcolreg)( unsigned regno, unsigned *red,
 					   unsigned *green, unsigned *blue,
-					   unsigned *transp );
+					   unsigned *transp, struct fb_info *info );
 	int  (*setcolreg)( unsigned regno, unsigned red,
 					   unsigned green, unsigned blue,
-					   unsigned transp );
+					   unsigned transp, struct fb_info *info );
 	void (*set_screen_base)( unsigned long s_base );
 	int  (*blank)( int blank_mode );
 	int  (*pan_display)( struct fb_var_screeninfo *var,
-						 struct atari_fb_par *par);
+						 struct atafb_par *par);
 } *fbhw;
 
 static char *autodetect_names[] = {"autodetect", NULL};
@@ -337,15 +359,6 @@
 static char *vga256_names[] = {"vga256", NULL};
 static char *falh2_names[] = {"falh2", NULL};
 static char *falh16_names[] = {"falh16", NULL};
-static char *user0_names[] = {"user0", NULL};
-static char *user1_names[] = {"user1", NULL};
-static char *user2_names[] = {"user2", NULL};
-static char *user3_names[] = {"user3", NULL};
-static char *user4_names[] = {"user4", NULL};
-static char *user5_names[] = {"user5", NULL};
-static char *user6_names[] = {"user6", NULL};
-static char *user7_names[] = {"user7", NULL};
-static char *dummy_names[] = {"dummy", NULL};
 
 static char **fb_var_names[] = {
 	/* Writing the name arrays directly in this array (via "(char *[]){...}")
@@ -365,22 +378,11 @@
 	vga256_names,
 	falh2_names,
 	falh16_names,
-	dummy_names, dummy_names, dummy_names, dummy_names,
-	dummy_names, dummy_names, dummy_names, dummy_names,
-	dummy_names, dummy_names,
-	user0_names,
-	user1_names,
-	user2_names,
-	user3_names,
-	user4_names,
-	user5_names,
-	user6_names,
-	user7_names,
 	NULL
 	/* ,NULL */ /* this causes a sigsegv on my gcc-2.5.8 */
 };
 
-static struct fb_var_screeninfo atari_fb_predefined[] = {
+static struct fb_var_screeninfo atafb_predefined[] = {
  	/*
  	 * yres_virtual==0 means use hw-scrolling if possible, else yres
  	 */
@@ -436,53 +438,9 @@
 	  896, 608, 896, 0, 0, 0, 4, 0,
 	  {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
 	  0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
-	/* Minor 14..23 free for more standard video modes */
-	{ 0, },
-	{ 0, },
-	{ 0, },
-	{ 0, },
-	{ 0, },
-	{ 0, },
-	{ 0, },
-	{ 0, },
-	{ 0, },
-	{ 0, },
-	/* Minor 24..31 reserved for user defined video modes */
-	{ /* user0, initialized to Rx;y;d from commandline, if supplied */
-	  0, 0, 0, 0, 0, 0, 0, 0,
-	  {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
-	  0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
-	{ /* user1 */
-	  0, 0, 0, 0, 0, 0, 0, 0,
-	  {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
-	  0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
-	{ /* user2 */
-	  0, 0, 0, 0, 0, 0, 0, 0,
-	  {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
-	  0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
-	{ /* user3 */
-	  0, 0, 0, 0, 0, 0, 0, 0,
-	  {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
-	  0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
-	{ /* user4 */
-	  0, 0, 0, 0, 0, 0, 0, 0,
-	  {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
-	  0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
-	{ /* user5 */
-	  0, 0, 0, 0, 0, 0, 0, 0,
-	  {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
-	  0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
-	{ /* user6 */
-	  0, 0, 0, 0, 0, 0, 0, 0,
-	  {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
-	  0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
-	{ /* user7 */
-	  0, 0, 0, 0, 0, 0, 0, 0,
-	  {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
-	  0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }
 };
 
-static int num_atari_fb_predefined=arraysize(atari_fb_predefined);
+static int num_atafb_predefined=arraysize(atafb_predefined);
 
 
 static int
@@ -492,7 +450,7 @@
     char **name;
     int i;
     name_list=fb_var_names;
-    for (i = 0 ; i < num_atari_fb_predefined ; i++) {
+    for (i = 0 ; i < num_atafb_predefined ; i++) {
 	name=*(name_list++);
 	if (! name || ! *name)
 	    break;
@@ -512,7 +470,7 @@
 #ifdef ATAFB_TT
 
 static int tt_encode_fix( struct fb_fix_screeninfo *fix,
-						  struct atari_fb_par *par )
+						  struct atafb_par *par )
 
 {
 	int mode;
@@ -539,7 +497,7 @@
 
 
 static int tt_decode_var( struct fb_var_screeninfo *var,
-						  struct atari_fb_par *par )
+						  struct atafb_par *par )
 {
 	int xres=var->xres;
 	int yres=var->yres;
@@ -620,7 +578,7 @@
 }
 
 static int tt_encode_var( struct fb_var_screeninfo *var,
-						  struct atari_fb_par *par )
+						  struct atafb_par *par )
 {
 	int linelen, i;
 	var->red.offset=0;
@@ -716,7 +674,7 @@
 }
 
 
-static void tt_get_par( struct atari_fb_par *par )
+static void tt_get_par( struct atafb_par *par )
 {
 	unsigned long addr;
 	par->hw.tt.mode=shifter_tt.tt_shiftmode;
@@ -727,7 +685,7 @@
 	par->screen_base = PTOV(addr);
 }
 
-static void tt_set_par( struct atari_fb_par *par )
+static void tt_set_par( struct atafb_par *par )
 {
 	shifter_tt.tt_shiftmode=par->hw.tt.mode;
 	shifter.syncmode=par->hw.tt.sync;
@@ -739,7 +697,7 @@
 
 static int tt_getcolreg( unsigned regno, unsigned *red,
 						 unsigned *green, unsigned *blue,
-						 unsigned *transp )
+						 unsigned *transp, struct fb_info *info )
 {
 	if ((shifter_tt.tt_shiftmode & TT_SHIFTER_MODEMASK) == TT_SHIFTER_STHIGH)
 		regno += 254;
@@ -756,7 +714,7 @@
 
 static int tt_setcolreg( unsigned regno, unsigned red,
 						 unsigned green, unsigned blue,
-						 unsigned transp )
+						 unsigned transp, struct fb_info *info )
 {
 	if ((shifter_tt.tt_shiftmode & TT_SHIFTER_MODEMASK) == TT_SHIFTER_STHIGH)
 		regno += 254;
@@ -772,7 +730,7 @@
 						  
 static int tt_detect( void )
 
-{	struct atari_fb_par par;
+{	struct atafb_par par;
 
 	/* Determine the connected monitor: The DMA sound must be
 	 * disabled before reading the MFP GPIP, because the Sound
@@ -789,7 +747,7 @@
 	mono_moni = (mfp.par_dt_reg & 0x80) == 0;
 
 	tt_get_par(&par);
-	tt_encode_var(&atari_fb_predefined[0], &par);
+	tt_encode_var(&atafb_predefined[0], &par);
 
 	return 1;
 }
@@ -807,10 +765,6 @@
 #define F_MON_VGA	2
 #define F_MON_TV	3
 
-/* Multisync monitor capabilities */
-/* Atari-TOS defaults if no boot option present */
-static long vfmin=58, vfmax=62, hfmin=31000, hfmax=32000;
-
 static struct pixel_clock {
 	unsigned long f;	/* f/[Hz] */
 	unsigned long t;	/* t/[ps] (=1/f) */
@@ -837,7 +791,7 @@
 }
 
 static int falcon_encode_fix( struct fb_fix_screeninfo *fix,
-							  struct atari_fb_par *par )
+							  struct atafb_par *par )
 {
 	strcpy(fix->id, "Atari Builtin");
 	fix->smem_start = (char *)real_screen_base;
@@ -867,7 +821,7 @@
 
 
 static int falcon_decode_var( struct fb_var_screeninfo *var,
-							  struct atari_fb_par *par )
+							  struct atafb_par *par )
 {
 	int bpp = var->bits_per_pixel;
 	int xres = var->xres;
@@ -943,7 +897,7 @@
 
 	if (mon_type == F_MON_SM || DontCalcRes) {
 		/* Skip all calculations. VGA/TV/SC1224 only supported. */
-		struct fb_var_screeninfo *myvar = &atari_fb_predefined[0];
+		struct fb_var_screeninfo *myvar = &atafb_predefined[0];
 		
 		if (bpp > myvar->bits_per_pixel ||
 			var->xres > myvar->xres ||
@@ -1069,11 +1023,14 @@
 
 			/* Choose master pixelclock depending on hor. timing */
 			plen = 1 * xstretch;
-			if ((plen * xres + f25.right+f25.hsync+f25.left) * hfmin < f25.f)
+			if ((plen * xres + f25.right+f25.hsync+f25.left) *
+			    fb_info.monspecs.hfmin < f25.f)
 				pclock = &f25;
-			else if ((plen * xres + f32.right+f32.hsync+f32.left) * hfmin < f32.f)
+			else if ((plen * xres + f32.right+f32.hsync+f32.left) * 
+			    fb_info.monspecs.hfmin < f32.f)
 				pclock = &f32;
-			else if ((plen * xres + fext.right+fext.hsync+fext.left) * hfmin < fext.f
+			else if ((plen * xres + fext.right+fext.hsync+fext.left) * 
+			    fb_info.monspecs.hfmin < fext.f
 			         && fext.f)
 				pclock = &fext;
 			else
@@ -1246,14 +1203,14 @@
 
 	/*  check hor. frequency */
 	hfreq = pclock->f / ((par->HHT+2)*prescale*2);
-	if (hfreq > hfmax && mon_type!=F_MON_VGA) {
+	if (hfreq > fb_info.monspecs.hfmax && mon_type!=F_MON_VGA) {
 		/* ++guenther:   ^^^^^^^^^^^^^^^^^^^ can't remember why I did this */
 		/* Too high -> enlarge margin */
 		left_margin += 1;
 		right_margin += 1;
 		goto again;
 	}
-	if (hfreq > hfmax || hfreq < hfmin)
+	if (hfreq > fb_info.monspecs.hfmax || hfreq < fb_info.monspecs.hfmin)
 		return -EINVAL;
 
 	/* Vxx-registers */
@@ -1282,45 +1239,52 @@
 	/* V-frequency check, hope I didn't create any loop here. */
 	/* Interlace and doubleline are mutually exclusive. */
 	vfreq = (hfreq * 2) / (par->VFT + 1);
-	if      (vfreq > vfmax && !doubleline && !interlace) {
+	if      (vfreq > fb_info.monspecs.vfmax && !doubleline && !interlace) {
 		/* Too high -> try again with doubleline */
 		doubleline = 1;
 		goto again;
 	}
-	else if (vfreq < vfmin && !interlace && !doubleline) {
+	else if (vfreq < fb_info.monspecs.vfmin && !interlace && !doubleline) {
 		/* Too low -> try again with interlace */
 		interlace = 1;
 		goto again;
 	}
-	else if (vfreq < vfmin && doubleline) {
+	else if (vfreq < fb_info.monspecs.vfmin && doubleline) {
 		/* Doubleline too low -> clear doubleline and enlarge margins */
 		int lines;
 		doubleline = 0;
-		for (lines=0; (hfreq*2)/(par->VFT+1+4*lines-2*yres)>vfmax; lines++)
+		for (lines=0;
+		     (hfreq*2)/(par->VFT+1+4*lines-2*yres)>fb_info.monspecs.vfmax;
+		     lines++)
 			;
 		upper_margin += lines;
 		lower_margin += lines;
 		goto again;
 	}
-	else if (vfreq > vfmax && doubleline) {
+	else if (vfreq > fb_info.monspecs.vfmax && doubleline) {
 		/* Doubleline too high -> enlarge margins */
 		int lines;
-		for (lines=0; (hfreq*2)/(par->VFT+1+4*lines)>vfmax; lines+=2)
+		for (lines=0;
+		     (hfreq*2)/(par->VFT+1+4*lines)>fb_info.monspecs.vfmax;
+		     lines+=2)
 			;
 		upper_margin += lines;
 		lower_margin += lines;
 		goto again;
 	}
-	else if (vfreq > vfmax && interlace) {
+	else if (vfreq > fb_info.monspecs.vfmax && interlace) {
 		/* Interlace, too high -> enlarge margins */
 		int lines;
-		for (lines=0; (hfreq*2)/(par->VFT+1+4*lines)>vfmax; lines++)
+		for (lines=0;
+		     (hfreq*2)/(par->VFT+1+4*lines)>fb_info.monspecs.vfmax;
+		     lines++)
 			;
 		upper_margin += lines;
 		lower_margin += lines;
 		goto again;
 	}
-	else if (vfreq < vfmin || vfreq > vfmax)
+	else if (vfreq < fb_info.monspecs.vfmin ||
+		 vfreq > fb_info.monspecs.vfmax)
 		return -EINVAL;
 
   set_screen_base:
@@ -1339,7 +1303,7 @@
 }
 
 static int falcon_encode_var( struct fb_var_screeninfo *var,
-							  struct atari_fb_par *par )
+							  struct atafb_par *par )
 {
 /* !!! only for VGA !!! */
 	int linelen, i;
@@ -1425,12 +1389,13 @@
 	var->transp.msb_right=0;
 
 	linelen = var->xres_virtual * var->bits_per_pixel / 8;
-	if (screen_len)
+	if (screen_len) {
 		if (par->yres_virtual)
 			var->yres_virtual = par->yres_virtual;
 		else
 			/* yres_virtual==0 means use maximum */
 			var->yres_virtual = screen_len / linelen;
+	}
 	else {
 		if (hwscroll < 0)
 			var->yres_virtual = 2 * var->yres;
@@ -1506,7 +1471,7 @@
 static struct falcon_hw f_new_mode;
 static int f_pan_display = 0;
 
-static void falcon_get_par( struct atari_fb_par *par )
+static void falcon_get_par( struct atafb_par *par )
 {
 	unsigned long addr;
 	struct falcon_hw *hw = &par->hw.falcon;
@@ -1543,7 +1508,7 @@
 	           ((hw->f_shift & 0x510)==0 && hw->st_shift==0x200);
 }
 
-static void falcon_set_par( struct atari_fb_par *par )
+static void falcon_set_par( struct atafb_par *par )
 {
 	f_change_mode = 0;
 
@@ -1627,7 +1592,7 @@
 
 
 static int falcon_pan_display( struct fb_var_screeninfo *var,
-							   struct atari_fb_par *par )
+							   struct atafb_par *par )
 {
 	int xoffset;
 	int bpp = fb_display[currcon].var.bits_per_pixel;
@@ -1659,7 +1624,7 @@
 
 static int falcon_getcolreg( unsigned regno, unsigned *red,
 				 unsigned *green, unsigned *blue,
-				 unsigned *transp )
+				 unsigned *transp, struct fb_info *info )
 {	unsigned long col;
 	
 	if (regno > 255)
@@ -1679,7 +1644,7 @@
 
 static int falcon_setcolreg( unsigned regno, unsigned red,
 							 unsigned green, unsigned blue,
-							 unsigned transp )
+							 unsigned transp, struct fb_info *info )
 {
 	if (regno > 255)
 		return 1;
@@ -1690,7 +1655,7 @@
 			(((green & 0xe) >> 1) | ((green & 1) << 3) << 4) |
 			((blue & 0xe) >> 1) | ((blue & 1) << 3);
 #ifdef CONFIG_FBCON_CFB16
-		packed16_cmap[regno] = (red << 11) | (green << 5) | blue;
+		fbcon_cfb16_cmap[regno] = (red << 11) | (green << 5) | blue;
 #endif
 	}
 	return 0;
@@ -1738,7 +1703,7 @@
  
 static int falcon_detect( void )
 {
-	struct atari_fb_par par;
+	struct atafb_par par;
 	unsigned char fhw;
 
 	/* Determine connected monitor and set monitor parameters */
@@ -1748,18 +1713,18 @@
 	f030_bus_width = fhw << 6 & 0x80;
 	switch (mon_type) {
 	case F_MON_SM:
-		vfmin = 70;
-		vfmax = 72;
-		hfmin = 35713;
-		hfmax = 35715;
+		fb_info.monspecs.vfmin = 70;
+		fb_info.monspecs.vfmax = 72;
+		fb_info.monspecs.hfmin = 35713;
+		fb_info.monspecs.hfmax = 35715;
 		break;
 	case F_MON_SC:
 	case F_MON_TV:
 		/* PAL...NTSC */
-		vfmin = 49; /* not 50, since TOS defaults to 49.9x Hz */
-		vfmax = 60;
-		hfmin = 15620;
-		hfmax = 15755;
+		fb_info.monspecs.vfmin = 49; /* not 50, since TOS defaults to 49.9x Hz */
+		fb_info.monspecs.vfmax = 60;
+		fb_info.monspecs.hfmin = 15620;
+		fb_info.monspecs.hfmax = 15755;
 		break;
 	}
 	/* initialize hsync-len */
@@ -1769,7 +1734,7 @@
 		fext.hsync = h_syncs[mon_type] / fext.t;
 
 	falcon_get_par(&par);
-	falcon_encode_var(&atari_fb_predefined[0], &par);
+	falcon_encode_var(&atafb_predefined[0], &par);
 
 	/* Detected mode is always the "autodetect" slot */
 	return 1;
@@ -1782,7 +1747,7 @@
 #ifdef ATAFB_STE
 
 static int stste_encode_fix( struct fb_fix_screeninfo *fix,
-							 struct atari_fb_par *par )
+							 struct atafb_par *par )
 
 {
 	int mode;
@@ -1813,7 +1778,7 @@
 
 
 static int stste_decode_var( struct fb_var_screeninfo *var,
-						  struct atari_fb_par *par )
+						  struct atafb_par *par )
 {
 	int xres=var->xres;
 	int yres=var->yres;
@@ -1871,7 +1836,7 @@
 }
 
 static int stste_encode_var( struct fb_var_screeninfo *var,
-						  struct atari_fb_par *par )
+						  struct atafb_par *par )
 {
 	int linelen, i;
 	var->red.offset=0;
@@ -1922,12 +1887,13 @@
 	
 	if (! use_hwscroll)
 		var->yres_virtual=var->yres;
-	else if (screen_len)
+	else if (screen_len) {
 		if (par->yres_virtual)
 			var->yres_virtual = par->yres_virtual;
 		else
 			/* yres_virtual==0 means use maximum */
 			var->yres_virtual = screen_len / linelen;
+	}
 	else {
 		if (hwscroll < 0)
 			var->yres_virtual = 2 * var->yres;
@@ -1948,7 +1914,7 @@
 }
 
 
-static void stste_get_par( struct atari_fb_par *par )
+static void stste_get_par( struct atafb_par *par )
 {
 	unsigned long addr;
 	par->hw.st.mode=shifter_tt.st_shiftmode;
@@ -1960,7 +1926,7 @@
 	par->screen_base = PTOV(addr);
 }
 
-static void stste_set_par( struct atari_fb_par *par )
+static void stste_set_par( struct atafb_par *par )
 {
 	shifter_tt.st_shiftmode=par->hw.st.mode;
 	shifter.syncmode=par->hw.st.sync;
@@ -1972,7 +1938,7 @@
 
 static int stste_getcolreg( unsigned regno, unsigned *red,
 							unsigned *green, unsigned *blue,
-							unsigned *transp )
+							unsigned *transp, struct fb_info *info )
 {	unsigned col;
 	
 	if (regno > 15)
@@ -1995,7 +1961,7 @@
 
 static int stste_setcolreg( unsigned regno, unsigned red,
 						 unsigned green, unsigned blue,
-						 unsigned transp )
+						 unsigned transp, struct fb_info *info )
 {
 	if (regno > 15)
 		return 1;
@@ -2015,7 +1981,7 @@
 						  
 static int stste_detect( void )
 
-{	struct atari_fb_par par;
+{	struct atafb_par par;
 
 	/* Determine the connected monitor: The DMA sound must be
 	 * disabled before reading the MFP GPIP, because the Sound
@@ -2028,7 +1994,7 @@
 	mono_moni = (mfp.par_dt_reg & 0x80) == 0;
 
 	stste_get_par(&par);
-	stste_encode_var(&atari_fb_predefined[0], &par);
+	stste_encode_var(&atafb_predefined[0], &par);
 
 	if (!ATARIHW_PRESENT(EXTD_SHIFTER))
 		use_hwscroll = 0;
@@ -2067,12 +2033,12 @@
 #define SYNC_DELAY  (mono_moni ? 1500 : 2000)
 
 /* SWITCH_ACIA may be used for Falcon (ScreenBlaster III internal!) */
-static void st_ovsc_switch(int switchmode)
+static void st_ovsc_switch(void)
 {
     unsigned long flags;
     register unsigned char old, new;
 
-    if ((switchmode & (SWITCH_ACIA | SWITCH_SND6 | SWITCH_SND7)) == 0)
+    if (!(atari_switches & ATARI_SWITCH_OVSC_MASK))
 	return;
     save_flags(flags);
     cli();
@@ -2093,11 +2059,15 @@
     mfp.tim_ct_b = 0x10;
     udelay(SYNC_DELAY);
 
-    if (switchmode == SWITCH_ACIA)
-	acia.key_ctrl = (ACIA_DIV64|ACIA_D8N1S|ACIA_RHTID|ACIA_RIE);
-    else {
+    if (atari_switches & ATARI_SWITCH_OVSC_IKBD)
+	acia.key_ctrl = ACIA_DIV64 | ACIA_D8N1S | ACIA_RHTID | ACIA_RIE;
+    if (atari_switches & ATARI_SWITCH_OVSC_MIDI)
+	acia.mid_ctrl = ACIA_DIV16 | ACIA_D8N1S | ACIA_RHTID;
+    if (atari_switches & (ATARI_SWITCH_OVSC_SND6|ATARI_SWITCH_OVSC_SND7)) {
 	sound_ym.rd_data_reg_sel = 14;
-	sound_ym.wd_data = sound_ym.rd_data_reg_sel | switchmode;
+	sound_ym.wd_data = sound_ym.rd_data_reg_sel |
+			   ((atari_switches&ATARI_SWITCH_OVSC_SND6) ? 0x40:0) |
+			   ((atari_switches&ATARI_SWITCH_OVSC_SND7) ? 0x80:0);
     }
     restore_flags(flags);
 }
@@ -2107,7 +2077,7 @@
 #ifdef ATAFB_EXT
 
 static int ext_encode_fix( struct fb_fix_screeninfo *fix,
-						   struct atari_fb_par *par )
+						   struct atafb_par *par )
 
 {
 	strcpy(fix->id,"Unknown Extern");
@@ -2157,9 +2127,9 @@
 
 
 static int ext_decode_var( struct fb_var_screeninfo *var,
-						   struct atari_fb_par *par )
+						   struct atafb_par *par )
 {
-	struct fb_var_screeninfo *myvar = &atari_fb_predefined[0];
+	struct fb_var_screeninfo *myvar = &atafb_predefined[0];
 	
 	if (var->bits_per_pixel > myvar->bits_per_pixel ||
 		var->xres > myvar->xres ||
@@ -2173,7 +2143,7 @@
 
 
 static int ext_encode_var( struct fb_var_screeninfo *var,
-						   struct atari_fb_par *par )
+						   struct atafb_par *par )
 {
 	int i;
 
@@ -2217,12 +2187,12 @@
 }
 
 
-static void ext_get_par( struct atari_fb_par *par )
+static void ext_get_par( struct atafb_par *par )
 {
 	par->screen_base = external_addr;
 }
 
-static void ext_set_par( struct atari_fb_par *par )
+static void ext_set_par( struct atafb_par *par )
 {
 }
 
@@ -2238,10 +2208,8 @@
 
 static int ext_getcolreg( unsigned regno, unsigned *red,
 						  unsigned *green, unsigned *blue,
-						  unsigned *transp )
-
-{	unsigned char colmask = (1 << external_bitspercol) - 1;
-		
+						  unsigned *transp, struct fb_info *info )
+{
 	if (! external_vgaiobase)
 		return 1;
 
@@ -2254,7 +2222,7 @@
 	
 static int ext_setcolreg( unsigned regno, unsigned red,
 						  unsigned green, unsigned blue,
-						  unsigned transp )
+						  unsigned transp, struct fb_info *info )
 
 {	unsigned char colmask = (1 << external_bitspercol) - 1;
 
@@ -2292,8 +2260,8 @@
 static int ext_detect( void )
 
 {
-	struct fb_var_screeninfo *myvar = &atari_fb_predefined[0];
-	struct atari_fb_par dummy_par;
+	struct fb_var_screeninfo *myvar = &atafb_predefined[0];
+	struct atafb_par dummy_par;
 
 	myvar->xres = external_xres;
 	myvar->xres_virtual = external_xres_virtual;
@@ -2319,7 +2287,7 @@
 
 
 static int pan_display( struct fb_var_screeninfo *var,
-                        struct atari_fb_par *par )
+                        struct atafb_par *par )
 {
 	if (!fbhw->set_screen_base ||
 		(!ATARIHW_PRESENT(EXTD_SHIFTER) && var->xoffset))
@@ -2369,7 +2337,7 @@
 
 
 
-static void atari_fb_get_par( struct atari_fb_par *par )
+static void atafb_get_par( struct atafb_par *par )
 {
 	if (current_par_valid) {
 		*par=current_par;
@@ -2379,7 +2347,7 @@
 }
 
 
-static void atari_fb_set_par( struct atari_fb_par *par )
+static void atafb_set_par( struct atafb_par *par )
 {
 	fbhw->set_par(par);
 	current_par=*par;
@@ -2396,7 +2364,7 @@
 /* used for hardware scrolling */
 
 static int
-fb_update_var(int con)
+fb_update_var(int con, struct fb_info *info)
 {
 	int off=fb_display[con].var.yoffset*fb_display[con].var.xres_virtual*
 			fb_display[con].var.bits_per_pixel>>3;
@@ -2412,12 +2380,12 @@
 do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
 {
 	int err,activate;
-	struct atari_fb_par par;
+	struct atafb_par par;
 	if ((err=fbhw->decode_var(var, &par)))
 		return err;
 	activate=var->activate;
 	if (((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive)
-		atari_fb_set_par(&par);
+		atafb_set_par(&par);
 	fbhw->encode_var(var, &par);
 	var->activate=activate;
 	return 0;
@@ -2426,17 +2394,17 @@
 /* Functions for handling colormap */
 
 static void
-do_install_cmap(int con)
+do_install_cmap(int con, struct fb_info *info)
 {
 	if (con != currcon)
 		return;
 	if (fb_display[con].cmap.len)
 		fb_set_cmap(&fb_display[con].cmap, &(fb_display[con].var), 1,
-			    fbhw->setcolreg);
+			    fbhw->setcolreg, info);
 	else
-		fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
 					    &(fb_display[con].var), 1,
-					    fbhw->setcolreg);		
+					    fbhw->setcolreg, info);		
 }
 
 
@@ -2444,7 +2412,7 @@
 	 * Open/Release the frame buffer device
 	 */
 
-static int atari_fb_open(int fbidx)
+static int atafb_open(struct fb_info *info)
 {
 	/*
 	 * Nothing, only a usage count for the moment
@@ -2454,7 +2422,7 @@
 	return(0);
 }
 
-static int atari_fb_release(int fbidx)
+static int atafb_release(struct fb_info *info)
 {
 	MOD_DEC_USE_COUNT;
 	return(0);
@@ -2462,11 +2430,11 @@
 
 
 static int
-atari_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
+atafb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
 {
-	struct atari_fb_par par;
+	struct atafb_par par;
 	if (con == -1)
-		atari_fb_get_par(&par);
+		atafb_get_par(&par);
 	else
 		fbhw->decode_var(&fb_display[con].var,&par);
 	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
@@ -2474,11 +2442,11 @@
 }
 	
 static int
-atari_fb_get_var(struct fb_var_screeninfo *var, int con)
+atafb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
 {
-	struct atari_fb_par par;
+	struct atafb_par par;
 	if (con == -1) {
-		atari_fb_get_par(&par);
+		atafb_get_par(&par);
 		fbhw->encode_var(var, &par);
 	}
 	else
@@ -2487,9 +2455,10 @@
 }
 
 static void
-atari_fb_set_disp(int con)
+atafb_set_disp(int con, struct fb_info *info)
 {
 	struct fb_fix_screeninfo fix;
+	struct fb_var_screeninfo var;
 	struct display *display;
 
 	if (con >= 0)
@@ -2497,7 +2466,8 @@
 	else
 		display = &disp;	/* used during initialization */
 
-	atari_fb_get_fix(&fix, con);
+	atafb_get_fix(&fix, con, info);
+	atafb_get_var(&var, con, info);
 	if (con == -1)
 		con=0;
 	display->screen_base = (u_char *)fix.smem_start;
@@ -2514,10 +2484,50 @@
 		display->can_soft_blank = 1;
 	display->inverse =
 	    (fix.visual == FB_VISUAL_MONO01 ? !inverse : inverse);
+	switch (fix.type) {
+	    case FB_TYPE_INTERLEAVED_PLANES:
+		switch (var.bits_per_pixel) {
+#ifdef CONFIG_FBCON_IPLAN2P2
+		    case 2:
+			display->dispsw = &fbcon_iplan2p2;
+			break;
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P4
+		    case 4:
+			display->dispsw = &fbcon_iplan2p4;
+			break;
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P8
+		    case 8:
+			display->dispsw = &fbcon_iplan2p8;
+			break;
+#endif
+		}
+		break;
+	    case FB_TYPE_PACKED_PIXELS:
+		switch (var.bits_per_pixel) {
+#ifdef CONFIG_FBCON_MFB
+		    case 1:
+			display->dispsw = &fbcon_mfb;
+			break;
+#endif
+#ifdef CONFIG_FBCON_CFB8
+		    case 8:
+			display->dispsw = &fbcon_cfb8;
+			break;
+#endif
+#ifdef CONFIG_FBCON_CFB16
+		    case 16:
+			display->dispsw = &fbcon_cfb16;
+			break;
+#endif
+		}
+		break;
+	}
 }
 
 static int
-atari_fb_set_var(struct fb_var_screeninfo *var, int con)
+atafb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
 {
 	int err,oldxres,oldyres,oldbpp,oldxres_virtual,
 	    oldyres_virtual,oldyoffset;
@@ -2536,10 +2546,10 @@
 		    || oldyres_virtual != var->yres_virtual
 		    || oldbpp != var->bits_per_pixel
 		    || oldyoffset != var->yoffset) {
-			atari_fb_set_disp(con);
+			atafb_set_disp(con, info);
 			(*fb_info.changevar)(con);
 			fb_alloc_cmap(&fb_display[con].cmap, 0, 0);
-			do_install_cmap(con);
+			do_install_cmap(con, info);
 		}
 	}
 	var->activate=0;
@@ -2549,22 +2559,22 @@
 
 
 static int
-atari_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+atafb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
 {
 	if (con == currcon) /* current console ? */
 		return fb_get_cmap(cmap, &(fb_display[con].var), kspc,
-				   fbhw->getcolreg);
+				   fbhw->getcolreg, info);
 	else
 		if (fb_display[con].cmap.len) /* non default colormap ? */
 			fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
 		else
-			fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+			fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
 				     cmap, kspc ? 0 : 2);
 	return 0;
 }
 
 static int
-atari_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+atafb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
 {
 	int err;
 	if (! fb_display[con].cmap.len) { /* no colormap allocated ? */
@@ -2575,14 +2585,14 @@
 	}
 	if (con == currcon) /* current console ? */
 		return fb_set_cmap(cmap, &(fb_display[con].var), kspc,
-				   fbhw->setcolreg);
+				   fbhw->setcolreg, info);
 	else
 		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
 	return 0;
 }
 
 static int
-atari_fb_pan_display(struct fb_var_screeninfo *var, int con)
+atafb_pan_display(struct fb_var_screeninfo *var, int con, struct fb_info *info)
 {
 	int xoffset = var->xoffset;
 	int yoffset = var->yoffset;
@@ -2606,33 +2616,33 @@
 }
 
 static int
-atari_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
-	       unsigned long arg, int con)
+atafb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+	       unsigned long arg, int con, struct fb_info *info)
 {
 	switch (cmd) {
 #ifdef FBCMD_GET_CURRENTPAR
 	case FBCMD_GET_CURRENTPAR:
 		if (copy_to_user((void *)arg, (void *)&current_par,
-				 sizeof(struct atari_fb_par)))
+				 sizeof(struct atafb_par)))
 			return -EFAULT;
 		return 0;
 #endif
 #ifdef FBCMD_SET_CURRENTPAR
 	case FBCMD_SET_CURRENTPAR:
 		if (copy_from_user((void *)&current_par, (void *)arg,
-				   sizeof(struct atari_fb_par)))
+				   sizeof(struct atafb_par)))
 			return -EFAULT;
-		atari_fb_set_par(&current_par);
+		atafb_set_par(&current_par);
 		return 0;
 #endif
 	}
 	return -EINVAL;
 }
 
-static struct fb_ops atari_fb_ops = {
-	atari_fb_open, atari_fb_release, atari_fb_get_fix, atari_fb_get_var,
-	atari_fb_set_var, atari_fb_get_cmap, atari_fb_set_cmap,
-	atari_fb_pan_display, atari_fb_ioctl	
+static struct fb_ops atafb_ops = {
+	atafb_open, atafb_release, atafb_get_fix, atafb_get_var,
+	atafb_set_var, atafb_get_cmap, atafb_set_cmap,
+	atafb_pan_display, NULL, atafb_ioctl	
 };
 
 static void
@@ -2645,14 +2655,14 @@
 
 	/* First try the user supplied mode */
 	if (default_par) {
-		var=atari_fb_predefined[default_par-1];
+		var=atafb_predefined[default_par-1];
 		var.activate = FB_ACTIVATE_TEST;
 		if (do_fb_set_var(&var,1))
 			default_par=0;		/* failed */
 	}
 	/* Next is the autodetected one */
 	if (! default_par) {
-		var=atari_fb_predefined[detected_mode-1]; /* autodetect */
+		var=atafb_predefined[detected_mode-1]; /* autodetect */
 		var.activate = FB_ACTIVATE_TEST;
 		if (!do_fb_set_var(&var,1))
 			default_par=detected_mode;
@@ -2665,7 +2675,7 @@
 			default_par=get_video_mode(default_name);
 			if (! default_par)
 				panic("can't set default video mode\n");
-			var=atari_fb_predefined[default_par-1];
+			var=atafb_predefined[default_par-1];
 			var.activate = FB_ACTIVATE_TEST;
 			if (! do_fb_set_var(&var,1))
 				break;	/* ok */
@@ -2677,16 +2687,17 @@
 }
 
 static int
-atafb_switch(int con)
+atafb_switch(int con, struct fb_info *info)
 {
 	/* Do we have to save the colormap ? */
 	if (fb_display[currcon].cmap.len)
 		fb_get_cmap(&fb_display[currcon].cmap,
-			    &(fb_display[currcon].var), 1, fbhw->getcolreg);
+			    &(fb_display[currcon].var), 1, fbhw->getcolreg,
+			    info);
 	do_fb_set_var(&fb_display[con].var,1);
 	currcon=con;
 	/* Install new colormap */
-	do_install_cmap(con);
+	do_install_cmap(con, info);
 	return 0;
 }
 
@@ -2698,7 +2709,7 @@
  * 4 = off
  */
 static void
-atafb_blank(int blank)
+atafb_blank(int blank, struct fb_info *info)
 {
 	unsigned short black[16];
 	struct fb_cmap cmap;
@@ -2713,19 +2724,13 @@
 		cmap.start=0;
 		cmap.len=16;
 		fb_set_cmap(&cmap, &(fb_display[currcon].var), 1,
-			    fbhw->setcolreg);
+			    fbhw->setcolreg, info);
 	}
 	else
-		do_install_cmap(currcon);
-}
-
-static int
-atafb_setcmap(struct fb_cmap *cmap, int con)
-{
-	return(atari_fb_set_cmap(cmap, 1, con));
+		do_install_cmap(currcon, info);
 }
 
-__initfunc(unsigned long atari_fb_init(unsigned long mem_start))
+__initfunc(unsigned long atafb_init(unsigned long mem_start))
 {
 	int err;
 	int pad;
@@ -2770,6 +2775,16 @@
 		panic("Cannot initialize video hardware\n");
 #endif
 	} while (0);
+
+	/* Multisync monitor capabilities */
+	/* Atari-TOS defaults if no boot option present */
+	if (fb_info.monspecs.hfmin == 0) {
+	    fb_info.monspecs.hfmin = 31000;
+	    fb_info.monspecs.hfmax = 32000;
+	    fb_info.monspecs.vfmin = 58;
+	    fb_info.monspecs.vfmax = 62;
+	}
+
 	detected_mode = fbhw->detect();
 	check_default_par(detected_mode);
 #ifdef ATAFB_EXT
@@ -2778,13 +2793,14 @@
 		mem_req = default_mem_req + ovsc_offset +
 			ovsc_addlen;
 		mem_req = ((mem_req + PAGE_SIZE - 1) & PAGE_MASK) + PAGE_SIZE;
-		screen_base = (unsigned long) atari_stram_alloc(mem_req, &mem_start);
+		screen_base = (unsigned long)atari_stram_alloc(mem_req, &mem_start,
+													   "atafb");
 		memset((char *) screen_base, 0, mem_req);
 		pad = ((screen_base + PAGE_SIZE-1) & PAGE_MASK) - screen_base;
 		screen_base+=pad;
 		real_screen_base=screen_base+ovsc_offset;
 		screen_len = (mem_req - pad - ovsc_offset) & PAGE_MASK;
-		st_ovsc_switch(ovsc_switchmode);
+		st_ovsc_switch();
 		if (CPU_IS_040_OR_060) {
 			/* On a '040+, the cache mode of video RAM must be set to
 			 * write-through also for internal video hardware! */
@@ -2815,32 +2831,29 @@
 	strcpy(fb_info.modename, "Atari Builtin ");
 	fb_info.changevar = NULL;
 	fb_info.node = -1;
-	fb_info.fbops = &atari_fb_ops;
-	fb_info.fbvar_num = num_atari_fb_predefined;
-	fb_info.fbvar = atari_fb_predefined;
+	fb_info.fbops = &atafb_ops;
 	fb_info.disp = &disp;
 	fb_info.switch_con = &atafb_switch;
 	fb_info.updatevar = &fb_update_var;
 	fb_info.blank = &atafb_blank;
-	fb_info.setcmap = &atafb_setcmap;
-	do_fb_set_var(&atari_fb_predefined[default_par-1], 1);
+	do_fb_set_var(&atafb_predefined[default_par-1], 1);
 	strcat(fb_info.modename, fb_var_names[default_par-1][0]);
 
 	err=register_framebuffer(&fb_info);
 	if (err < 0)
 		return(err);
 
-	atari_fb_get_var(&disp.var, -1);
-	atari_fb_set_disp(-1);
+	atafb_get_var(&disp.var, -1, &fb_info);
+	atafb_set_disp(-1, &fb_info);
 	printk("Determined %dx%d, depth %d\n",
 	       disp.var.xres, disp.var.yres, disp.var.bits_per_pixel);
 	if ((disp.var.xres != disp.var.xres_virtual) ||
 	    (disp.var.yres != disp.var.yres_virtual))
 	   printk("   virtual %dx%d\n",
 			  disp.var.xres_virtual, disp.var.yres_virtual);
-	do_install_cmap(0);
-	printk("%s frame buffer device, using %dK of video memory\n",
-	       fb_info.modename, screen_len>>10);
+	do_install_cmap(0, &fb_info);
+	printk("fb%d: %s frame buffer device, using %dK of video memory\n",
+	       GET_FB_IDX(fb_info.node), fb_info.modename, screen_len>>10);
 
 	/* TODO: This driver cannot be unloaded yet */
 	MOD_INC_USE_COUNT;
@@ -2870,7 +2883,7 @@
   return sbegin;
 }
 
-__initfunc(void atari_video_setup( char *options, int *ints ))
+__initfunc(void atafb_setup( char *options, int *ints ))
 {
     char *this_opt;
     int temp;
@@ -2902,15 +2915,6 @@
 		if (hwscroll > 200)
 			hwscroll = 200;
 	}
-	else if (! strncmp(this_opt, "sw_",3)) {
-		if (! strcmp(this_opt+3, "acia"))
-			ovsc_switchmode = SWITCH_ACIA;
-		else if (! strcmp(this_opt+3, "snd6"))
-			ovsc_switchmode = SWITCH_SND6;
-		else if (! strcmp(this_opt+3, "snd7"))
-			ovsc_switchmode = SWITCH_SND7;
-		else ovsc_switchmode = SWITCH_NONE;
-	}
 #ifdef ATAFB_EXT
 	else if (!strcmp(this_opt,"mv300")) {
 		external_bitspercol = 8;
@@ -2939,12 +2943,8 @@
 
     if (*int_str) {
 	/* Format to config extended internal video hardware like OverScan:
-	"<switch-type>,internal:<xres>;<yres>;<xres_max>;<yres_max>;<offset>"
+	"internal:<xres>;<yres>;<xres_max>;<yres_max>;<offset>"
 	Explanation:
-	<switch-type> type to switch on higher resolution
-			sw_acia : via keyboard ACIA
-			sw_snd6 : via bit 6 of the soundchip port
-			sw_snd7 : via bit 7 of the soundchip port
 	<xres>: x-resolution 
 	<yres>: y-resolution
 	The following are only needed if you have an overscan which
@@ -2974,9 +2974,9 @@
 
 	if (ovsc_offset || (sttt_yres_virtual != st_yres))
 		use_hwscroll=0;
+      int_invalid:
+	;
     }
-    else 
-      int_invalid:	ovsc_switchmode = SWITCH_NONE;
 
 #ifdef ATAFB_EXT
     if (*ext_str) {
@@ -3103,10 +3103,10 @@
 	hmax = 1000 * simple_strtoul(p, NULL, 10);
 	if (hmax <= 0 || hmax <= hmin) goto cap_invalid;
 
-	vfmin = vmin;
-	vfmax = vmax;
-	hfmin = hmin;
-	hfmax = hmax;
+	fb_info.monspecs.vfmin = vmin;
+	fb_info.monspecs.vfmax = vmax;
+	fb_info.monspecs.hfmin = hmin;
+	fb_info.monspecs.hfmax = hmax;
       cap_invalid:
 	;
     }
@@ -3126,9 +3126,9 @@
 		depth = simple_strtoul(p, NULL, 10);
 		if ((temp=get_video_mode("user0"))) {
 			default_par=temp;
-			atari_fb_predefined[default_par-1].xres = xres;
-			atari_fb_predefined[default_par-1].yres = yres;
-			atari_fb_predefined[default_par-1].bits_per_pixel = depth;
+			atafb_predefined[default_par-1].xres = xres;
+			atafb_predefined[default_par-1].yres = yres;
+			atafb_predefined[default_par-1].bits_per_pixel = depth;
 		}
 
 	  user_invalid:
@@ -3139,7 +3139,7 @@
 #ifdef MODULE
 int init_module(void)
 {
-	return(atari_fb_init(NULL));
+	return(atafb_init(NULL));
 }
 
 void cleanup_module(void)
@@ -3147,6 +3147,7 @@
 	/* Not reached because the usecount will never
 	   be decremented to zero */
 	unregister_framebuffer(&fb_info);
-	/* TODO: clean up ... */
+	/* atari_stram_free( screen_base ); */
+	/* TODO: further clean up ... */
 }
 #endif /* MODULE */
diff -u --recursive --new-file v2.1.90/linux/drivers/video/ati-gt.h linux/drivers/video/ati-gt.h
--- v2.1.90/linux/drivers/video/ati-gt.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/ati-gt.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,203 @@
+/* the usage for the following structs vary from the gx and vt:
+and sdram and sgram gt's
+	pll registers (sdram) 6,7,11;
+	crtc_h_sync_strt_wid[3];
+	dsp1[3] (sdram,sgram,unused)
+	dsp2[3] (offset regbase+24, depends on colour mode);
+	crtc_h_tot_disp,crtc_v_tot_disp,crtc_v_sync_strt_wid,unused;
+	pll registers (sgram) 7,11;
+*/
+
+/* Register values for 1280x1024, 75Hz mode (20). no 16/32 */
+static struct aty_regvals aty_gt_reg_init_20 = {
+	{ 0x41, 0xf9, 0x04 },
+	{ 0xe02a7, 0x1401a6, 0 },
+	{ 0x260957, 0x2806d6, 0 },
+	{ 0x10006b6, 0x20006b6, 0x30006b6 },
+
+	0x9f00d2, 0x03ff0429, 0x30400, 0,
+	{ 0xb5, 0x04 }
+};
+
+#if 0
+/* Register values for 1280x960, 75Hz mode (19) */
+static struct aty_regvals aty_gt_reg_init_19 = {
+};
+#endif
+
+/* Register values for 1152x870, 75Hz mode (18) */
+static struct aty_regvals aty_gt_reg_init_18 = {
+	{ 0x41, 0xe6, 0x04 },
+	{ 0x300295, 0x300194, 0x300593 },
+	{ 0x260a1c, 0x380561, 0},
+	{ 0x1000744, 0x2000744, 0x3000744 },
+
+	0x8f00b5, 0x3650392, 0x230368, 0,
+	{ 0xe6, 0x04 }
+};
+
+/* Register values for 1024x768, 75Hz mode (17), 32 bpp untested */
+static struct aty_regvals aty_gt_reg_init_17 = {
+	{ 0x41, 0xb5, 0x04 },
+	{ 0xc0283, 0xc0182, 0xc0581 },
+	{ 0x36066d, 0x3806d6, 0},
+	{ 0xa0049e, 0x100049e, 0x200049e },
+
+	0x7f00a3, 0x2ff031f, 0x30300, 0,
+	{ 0xb8, 0x04 }
+};
+
+#if 0
+/* Register values for x, Hz mode (16) */
+static struct aty_regvals aty_gt_reg_init_16 = {
+};
+#endif
+
+/* Register values for 1024x768, 70Hz mode (15) */
+static struct aty_regvals aty_gt_reg_init_15 = {
+	{ 0x41, 0xad, 0x04 },
+	{ 0x310284, 0x310183, 0x310582 },
+	{ 0x0, 0x380727 },
+	{ 0x0 },
+	0x7f00a5, 0x2ff0325, 0x260302,
+};    
+
+/* Register values for 1024x768, 60Hz mode (14) */
+static struct aty_regvals aty_gt_reg_init_14 = {
+	{ 0x40, 0xe1, 0x14 },
+	{ 0x310284, 0x310183, 0x310582 },
+	{ 0x3607c0, 0x380840, 0x0 },
+	{ 0xa80592, 0x1000592, 0x0 },
+
+	0x7f00a7, 0x2ff0325, 0x260302, 0,
+	{ 0xe1, 0x14 }
+};  
+
+/* Register values for 832x624, 75Hz mode (13) */
+static struct aty_regvals aty_gt_reg_init_13 = {
+	{ 0x40, 0xc6, 0x14 },
+	{ 0x28026d, 0x28016c, 0x28056b },
+	{ 0x3608cf, 0x380960, 0 },
+	{ 0xb00655, 0x1000655, 0x2000655 },
+
+	0x67008f, 0x26f029a, 0x230270, 0,
+	{ 0xc6, 0x14 }
+};
+
+/* Register values for 800x600, 75Hz mode (12) */
+static struct aty_regvals aty_gt_reg_init_12 = {
+	{ 0x42, 0xe4, 0x04 },
+	{ 0xa0267, 0xa0166, 0x0a0565},
+	{ 0x360a33, 0x48056d, 0},
+	{ 0xc00755, 0x1000755, 0x02000755},
+
+	0x630083, 0x2570270, 0x30258, 0,
+	{ 0xe4, 0x4 }
+};
+
+/* Register values for 800x600, 72Hz mode (11) */
+static struct aty_regvals aty_gt_reg_init_11 = {
+	{ 0x42, 0xe6, 0x04 },
+	{ 0xf026c, 0xf016b, 0xf056a },
+	{ 0x360a1d, 0x480561, 0},
+	{ 0xc00745, 0x1000745, 0x2000745 },
+	
+	0x630081, 0x02570299, 0x6027c
+};
+
+/* Register values for 800x600, 60Hz mode (10) */
+static struct aty_regvals aty_gt_reg_init_10 = {
+	{ 0x42, 0xb8, 0x04 },
+	{ 0x10026a, 0x100169, 0x100568 },
+	{ 0x460652, 0x4806ba, 0},
+	{ 0x68048b, 0xa0048b, 0x100048b },
+
+	0x630083, 0x02570273, 0x40258, 0,
+	{ 0xb8, 0x4 }
+};
+
+/* Register values for 800x600, 56Hz mode (9) */
+static struct aty_regvals aty_gt_reg_init_9 = {
+	{ 0x42, 0xf9, 0x14 },
+	{ 0x90268, 0x90167, 0x090566 },
+	{ 0x460701, 0x480774, 0},
+	{ 0x700509, 0xa80509, 0x1000509 },
+
+	0x63007f, 0x2570270, 0x20258
+};
+
+#if 0
+/* Register values for 768x576, 50Hz mode (8) */
+static struct aty_regvals aty_gt_reg_init_8 = {
+};
+
+/* Register values for 640x870, 75Hz Full Page Display (7) */
+static struct aty_regvals aty_gt_reg_init_7 = {
+};
+#endif
+
+/* Register values for 640x480, 67Hz mode (6) */
+static struct aty_regvals aty_gt_reg_init_6 = {
+	{ 0x42, 0xd1, 0x14 },
+	{ 0x280259, 0x280158, 0x280557 },
+	{ 0x460858, 0x4808e2, 0},
+	{ 0x780600, 0xb00600, 0x1000600 },
+
+	0x4f006b, 0x1df020c, 0x2301e2, 0,
+	{ 0x8b, 0x4 }
+};
+
+/* Register values for 640x480, 60Hz mode (5) */
+static struct aty_regvals aty_gt_reg_init_5 = {
+	{ 0x43, 0xe8, 0x04 },
+	{ 0x2c0253, 0x2c0152, 0x2c0551 },
+	{ 0x460a06, 0x580555, 0},
+	{ 0x880734, 0xc00734, 0x1000734 },
+
+	0x4f0063, 0x1df020c, 0x2201e9, 0,
+	{ 0xe8, 0x04 }
+};
+
+#if 0
+/* Register values for x, Hz mode (4) */
+static struct aty_regvals aty_gt_reg_init_4 = {
+};
+
+/* Register values for x, Hz mode (3) */
+static struct aty_regvals aty_gt_reg_init_3 = {
+};
+
+/* Register values for x, Hz mode (2) */
+static struct aty_regvals aty_gt_reg_init_2 = {
+};
+
+/* Register values for x, Hz mode (1) */
+static struct aty_regvals aty_gt_reg_init_1 = {
+};
+#endif
+
+/* yikes, more data structures (dsp2)
+ * XXX kludge for sgram
+ */
+static int sgram_dsp[20][3] = {
+	{0,0,0},
+	{0,0,0},
+	{0,0,0},
+	{0,0,0},
+	{0x5203d7,0x7803d9,0xb803dd}, //5
+	{0x940666,0xe0066a,0x1700672}, //6
+	{0,0,0},
+	{0,0,0},
+	{0x88055f,0xd80563,0x170056b}, //9
+	{0x8404d9,0xb804dd,0x17004e5}, //10
+	{0x7803e2,0xb803e6,0x17003ee}, //11
+	{0x7803eb,0xb803ef,0x17003f7}, //12
+	{0xe806c5,0x17006cd,0x2e006dd}, //13
+	{0xe005f6,0x17005fe,0x2e0060e}, //14
+	{0xd8052c,0x1700534,0x2e00544}, //15
+	{0,0,0},
+	{0xb804f2,0x17004e5,0x2e0050a}, //17
+	{0xb803e6,0x17003ee,0x2e003fe}, //18
+	{0,0,0},
+	{0,0,0},
+};
diff -u --recursive --new-file v2.1.90/linux/drivers/video/ati-gx.h linux/drivers/video/ati-gx.h
--- v2.1.90/linux/drivers/video/ati-gx.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/ati-gx.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,122 @@
+/* Register values for 1280x1024, 75Hz (WAS 60) mode (20) */
+static struct aty_regvals aty_gx_reg_init_20 = {
+   { 0x200, 0x200, 0x200 },
+
+    { 0x1200a5, 0x1200a3, 0x1200a3 },
+    { 0x30c0200, 0x30e0300, 0x30e0300 },
+    { 0x2, 0x3, 0x3 },
+
+    0x9f00d2, 0x3ff0429, 0x30400, 0x28100040,
+    { 0xd4, 0x9 }
+};     
+
+/* Register values for 1152x870, 75Hz mode (18) */
+static struct aty_regvals aty_gx_reg_init_18 = {
+    { 0x200, 0x200, 0x200 },
+
+    { 0x300097, 0x300095, 0x300094 },
+    { 0x3090200, 0x30e0300, 0x30e0600 },
+    { 0x2, 0x3, 0x6 },
+
+    0x8f00b5, 0x3650392, 0x230368, 0x24100040,
+    { 0x53, 0x3 }
+};
+
+/* Register values for 1024x768, 75Hz mode (17) */
+static struct aty_regvals aty_gx_reg_init_17 = {
+    { 0x200, 0x200, 0x200 },
+
+    { 0x2c0087, 0x2c0085, 0x2c0084 },
+    { 0x3070200, 0x30e0300, 0x30e0600 },
+    { 0x2, 0x3, 0x6 },
+
+    0x7f00a5, 0x2ff0323, 0x230302, 0x20100000,
+    { 0x42, 0x3 }
+};
+
+/* Register values for 1024x768, 72Hz mode (15) */
+static struct aty_regvals aty_gx_reg_init_15 = {
+    { 0, 0, 0 },
+
+    { 0x310086, 0x310084, 0x310084 },
+    { 0x3070200, 0x30e0300, 0x30e0300 },
+    { 0x2002312, 0x3002312, 0x3002312 },
+
+    0x7f00a5, 0x2ff0325, 0x260302, 0x20100000,
+    { 0x88, 0x7 }
+};    
+
+/* Register values for 1024x768, 60Hz mode (14) */
+static struct aty_regvals aty_gx_reg_init_14 = {
+	{ 0, 0, 0 },
+
+	{ 0x310086, 0x310084, 0x310084 },
+	{ 0x3060200, 0x30d0300, 0x30d0300 },
+	{ 0x2002312, 0x3002312, 0x3002312 },
+
+	0x7f00a7, 0x2ff0325, 0x260302, 0x20100000,
+	{ 0x6c, 0x6 }
+};  
+
+/* Register values for 832x624, 75Hz mode (13) */
+static struct aty_regvals aty_gx_reg_init_13 = {
+    { 0x200, 0x200, 0x200 },
+
+    { 0x28006f, 0x28006d, 0x28006c },
+    { 0x3050200, 0x30b0300, 0x30e0600 },
+    { 0x2, 0x3, 0x6 },
+
+    0x67008f, 0x26f029a, 0x230270, 0x1a100040,
+    { 0x4f, 0x5 }
+};
+
+#if 0		/* not filled in yet */
+/* Register values for 800x600, 75Hz mode (12) */
+static struct aty_regvals aty_gx_reg_init_12 = {
+	{ 0x10, 0x28, 0x50 },
+	{ },
+	{ }	/* pixel clock = 49.11MHz for V=74.40Hz */
+};
+
+/* Register values for 800x600, 72Hz mode (11) */
+static struct aty_regvals aty_gx_reg_init_11 = {
+	{ 0x10, 0x28, 0x50 },
+	{ },
+	{ }	/* pixel clock = 49.63MHz for V=71.66Hz */
+};
+
+/* Register values for 800x600, 60Hz mode (10) */
+static struct aty_regvals aty_gx_reg_init_10 = {
+	{ 0x10, 0x28, 0x50 },
+	{ },
+	{ }	/* pixel clock = 41.41MHz for V=59.78Hz */
+};
+
+/* Register values for 640x870, 75Hz Full Page Display (7) */
+static struct aty_regvals aty_gx_reg_init_7 = {
+	{ 0x10, 0x30, 0x68 },
+	{ },
+	{ }	/* pixel clock = 57.29MHz for V=75.01Hz */
+};
+#endif
+
+/* Register values for 640x480, 67Hz mode (6) */
+static struct aty_regvals aty_gx_reg_init_6 = {
+	{ 0x200, 0x200, 0x200 },
+
+	{ 0x28005b, 0x280059, 0x280058 },
+	{ 0x3040200, 0x3060300, 0x30c0600 },
+	{ 0x2002312, 0x3002312, 0x6002312 },
+
+	0x4f006b, 0x1df020c, 0x2301e2, 0x14100040,
+        { 0x35, 0x07 }
+};
+
+#if 0		/* not filled in yet */
+/* Register values for 640x480, 60Hz mode (5) */
+static struct aty_regvals aty_gx_reg_init_5 = {
+	{ 0x200, 0x200, 0x200 },
+	{ },
+        { 0x35, 0x07 }
+};
+#endif
diff -u --recursive --new-file v2.1.90/linux/drivers/video/ati-vt.h linux/drivers/video/ati-vt.h
--- v2.1.90/linux/drivers/video/ati-vt.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/ati-vt.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,147 @@
+/* Register values for 1280x1024, 60Hz mode (20) */
+static struct aty_regvals aty_vt_reg_init_20 = {
+  { 0, 0, 0 },
+     
+  { 0x002e02a7, 0x002e02a7, 0 },
+  { 0x03070200, 0x03070200, 0 },
+  { 0x0a00cb22, 0x0b00cb23, 0 },
+     
+    0x009f00d2, 0x03ff0429, 0x00030400, 0x28000000,
+  { 0x00, 0xaa }
+};
+
+/* Register values for 1280x960, 75Hz mode (19) */
+static struct aty_regvals aty_vt_reg_init_19 = {
+  { 0, 0, 0 },
+  { 0x003202a3, 0x003201a2, 0 },
+  { 0x030b0200, 0x030b0300, 0 },
+  { 0x0a00cb22, 0x0b00cb23, 0 },
+
+    0x009f00d1, 0x03bf03e7, 0x000303c0, 0x28000000,
+  { 0x00, 0xc6 }
+};
+
+/* Register values for 1152x870, 75Hz mode (18) */
+static struct aty_regvals aty_vt_reg_init_18 = {
+  { 0, 0, 0 },
+     
+  { 0x00300295, 0x00300194, 0 },
+  { 0x03080200, 0x03080300, 0 },
+  { 0x0a00cb21, 0x0b00cb22, 0 },
+     
+    0x008f00b5, 0x03650392, 0x00230368, 0x24000000,
+  { 0x00, 0x9d }
+};
+
+/* Register values for 1024x768, 75Hz mode (17) */
+static struct aty_regvals aty_vt_reg_init_17 = {
+  { 0, 0, 0 },
+
+  { 0x002c0283, 0x002c0182, 0 },
+  { 0x03080200, 0x03080300, 0 },
+  { 0x0a00cb21, 0x0b00cb22, 0 },
+     
+    0x007f00a3, 0x02ff031f, 0x00030300, 0x20000000,
+  { 0x01, 0xf7 }
+};
+
+/* Register values for 1024x768, 70Hz mode (15) */
+static struct aty_regvals aty_vt_reg_init_15 = {
+  { 0, 0, 0 },
+  { 0x00310284, 0x00310183, 0 },
+  { 0x03080200, 0x03080300, 0 },
+  { 0x0a00cb21, 0x0b00cb22, 0 },
+
+    0x007f00a5, 0x02ff0325, 0x00260302, 0x20000000,
+  { 0x01, 0xeb }
+};    
+
+/* Register values for 1024x768, 60Hz mode (14) */
+static struct aty_regvals aty_vt_reg_init_14 = {
+  { 0, 0, 0 },
+     
+  { 0x00310284, 0x00310183, 0x00310582 }, /* 32 bit 0x00310582 */
+  { 0x03080200, 0x03080300, 0x03070600 }, /* 32 bit 0x03070600 */
+  { 0x0a00cb21, 0x0b00cb22, 0x0e00cb23 },
+    
+    0x007f00a7, 0x02ff0325, 0x00260302, 0x20000000,
+  { 0x01, 0xcc }
+};  
+
+/* Register values for 832x624, 75Hz mode (13) */
+static struct aty_regvals aty_vt_reg_init_13 = {
+  { 0, 0, 0 },
+
+  { 0x0028026d, 0x0028016c, 0x0028056b },
+  { 0x03080200, 0x03070300, 0x03090600 },
+  { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 },
+
+    0x0067008f, 0x026f029a, 0x00230270, 0x1a000000,
+  { 0x01, 0xb4 }
+};
+
+/* Register values for 800x600, 75Hz mode (12) */
+static struct aty_regvals aty_vt_reg_init_12 = {
+  { 0, 0, 0 },
+     
+  { 0x002a0267, 0x002a0166, 0x002a0565 },
+  { 0x03040200, 0x03060300, 0x03070600 },
+  { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 },
+     
+    0x00630083, 0x02570270, 0x00030258, 0x19000000,
+  { 0x01, 0x9c }
+};
+
+/* Register values for 800x600, 72Hz mode (11) */
+static struct aty_regvals aty_vt_reg_init_11 = {
+  { 0, 0, 0 },
+     
+  { 0x002f026c, 0x002f016b, 0x002f056a },
+  { 0x03050200, 0x03070300, 0x03090600 },
+  { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 },
+
+    0x00630081, 0x02570299, 0x0006027c, 0x19000000,
+  { 0x01, 0x9d }
+};
+
+/* Register values for 800x600, 60Hz mode (10) */
+static struct aty_regvals aty_vt_reg_init_10 = {
+  { 0, 0, 0 },
+     
+  { 0x0030026a, 0x00300169, 0x00300568 },
+  { 0x03050200, 0x03070300, 0x03090600 },
+  { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 },
+     
+    0x00630083, 0x02570273, 0x00040258, 0x19000000,
+  { 0x02, 0xfb }
+};
+
+/* Register values for 640x480, 67Hz mode (6) */
+static struct aty_regvals aty_vt_reg_init_6 = {
+  { 0, 0, 0 },
+      
+  { 0x00280259, 0x00280158, 0x00280557 },
+  { 0x03050200, 0x03070300, 0x030a0600 },
+  { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 },
+      
+    0x004f006b, 0x01df020c, 0x002301e2, 0x14000000,
+  { 0x02, 0xbe }
+};
+
+/* Register values for 640x480, 60Hz mode (5) */
+static struct aty_regvals aty_vt_reg_init_5 = {
+  { 0, 0, 0 },
+      
+  { 0x002c0253, 0x002c0152, 0x002c0551 },
+  { 0x03050200, 0x03070300, 0x03090600 },
+  { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 },
+
+    0x004f0063, 0x01df020c, 0x002201e9, 0x14000000,
+  { 0x02, 0x9e }
+};
+                               /*     8 bit       15 bit      32 bit   */
+static int vt_mem_cntl[3][3] = { { 0x0A00CB21, 0x0B00CB21, 0x0E00CB21 },  /* 1 MB VRAM */
+                                 { 0x0A00CB22, 0x0B00CB22, 0x0E00CB22 },  /* 2 MB VRAM */
+                                { 0x0200053B, 0x0300053B, 0x0600053B }   /* 4 M B VRAM */
+ };
+
diff -u --recursive --new-file v2.1.90/linux/drivers/video/aty.h linux/drivers/video/aty.h
--- v2.1.90/linux/drivers/video/aty.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/aty.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,923 @@
+/*
+ * Exported procedures for the ATI/mach64 display driver on PowerMacs.
+ *
+ * Copyright (C) 1997 Michael AK Tesch
+ *  written with much help from Jon Howell
+ *
+ * Updated for 3D RAGE PRO by Geert Uytterhoeven
+ *	
+ * 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.
+ */
+
+/*
+ * most of the rest of this file comes from ATI sample code
+ */
+#ifndef REGMACH64_H
+#define REGMACH64_H
+
+/* NON-GUI MEMORY MAPPED Registers - expressed in BYTE offsets */
+
+#define CRTC_H_TOTAL_DISP	0x0000	/* Dword offset 0_00 */
+#define CRTC_H_SYNC_STRT_WID	0x0004	/* Dword offset 0_01 */
+#define CRTC_H_SYNC_STRT	0x0004
+#define CRTC_H_SYNC_DLY		0x0005
+#define CRTC_H_SYNC_WID		0x0006
+
+#define CRTC_V_TOTAL_DISP	0x0008	/* Dword offset 0_02 */
+#define CRTC_V_TOTAL		0x0008
+#define CRTC_V_DISP		0x000A
+#define CRTC_V_SYNC_STRT_WID	0x000C	/* Dword offset 0_03 */
+#define CRTC_V_SYNC_STRT	0x000C
+#define CRTC_V_SYNC_WID		0x000E
+
+#define CRTC_VLINE_CRNT_VLINE	0x0010	/* Dword offset 0_04 */
+#define CRTC_OFF_PITCH		0x0014	/* Dword offset 0_05 */
+#define CRTC_OFFSET		0x0014
+#define CRTC_PITCH		0x0016
+
+#define CRTC_INT_CNTL		0x0018	/* Dword offset 0_06 */
+#define CRTC_GEN_CNTL		0x001C	/* Dword offset 0_07 */
+#define CRTC_PIX_WIDTH		0x001D
+#define CRTC_FIFO		0x001E
+#define CRTC_EXT_DISP		0x001F
+
+#define DSP_CONFIG		0x0020	/* Dword offset 0_08 */
+#define DSP_ON_OFF		0x0024	/* Dword offset 0_09 */
+#define TIMER_CONFIG		0x0028	/* Dword offset 0_0A */
+#define MEM_BUF_CNTL		0x002C	/* Dword offset 0_0B */
+#define MEM_ADDR_CONFIG		0x0034	/* Dword offset 0_0D */
+
+#define CRT_TRAP		0x0038	/* Dword offset 0_0E */
+
+#define I2C_CNTL_0		0x003C	/* Dword offset 0_0F */
+
+#define OVR_CLR			0x0040	/* Dword offset 0_10 */
+#define OVR_WID_LEFT_RIGHT	0x0044	/* Dword offset 0_11 */
+#define OVR_WID_TOP_BOTTOM	0x0048	/* Dword offset 0_12 */
+
+#define VGA_DSP_CONFIG		0x004C	/* Dword offset 0_13 */
+#define VGA_DSP_ON_OFF		0x0050	/* Dword offset 0_14 */
+
+#define CUR_CLR0		0x0060	/* Dword offset 0_18 */
+#define CUR_CLR1		0x0064	/* Dword offset 0_19 */
+#define CUR_OFFSET		0x0068	/* Dword offset 0_1A */
+#define CUR_HORZ_VERT_POSN	0x006C	/* Dword offset 0_1B */
+#define CUR_HORZ_VERT_OFF	0x0070	/* Dword offset 0_1C */
+
+#define GP_IO			0x0078	/* Dword offset 0_1E */
+
+#define HW_DEBUG		0x007C	/* Dword offset 0_1F */
+
+#define SCRATCH_REG0		0x0080	/* Dword offset 0_20 */
+#define SCRATCH_REG1		0x0084	/* Dword offset 0_21 */
+
+#define CLOCK_CNTL		0x0090	/* Dword offset 0_24 */
+#define CLOCK_SEL_CNTL		0x0090	/* Dword offset 0_24 */
+
+#define CONFIG_STAT1		0x0094	/* Dword offset 0_25 */
+#define CONFIG_STAT2		0x0098	/* Dword offset 0_26 */
+
+#define BUS_CNTL		0x00A0	/* Dword offset 0_28 */
+
+#define EXT_MEM_CNTL		0x00AC	/* Dword offset 0_2B */
+#define MEM_CNTL		0x00B0	/* Dword offset 0_2C */
+
+#define MEM_VGA_WP_SEL		0x00B4	/* Dword offset 0_2D */
+#define MEM_VGA_RP_SEL		0x00B8	/* Dword offset 0_2E */
+
+#define DAC_REGS		0x00C0	/* Dword offset 0_30 */
+#define DAC_W_INDEX		0x00C0	/* Dword offset 0_30 */
+#define DAC_DATA		0x00C1	/* Dword offset 0_30 */
+#define DAC_MASK		0x00C2	/* Dword offset 0_30 */
+#define DAC_R_INDEX		0x00C3	/* Dword offset 0_30 */
+#define DAC_CNTL		0x00C4	/* Dword offset 0_31 */
+
+#define EXT_DAC_REGS		0x00C8	/* Dword offset 0_32 */
+
+#define GEN_TEST_CNTL		0x00D0	/* Dword offset 0_34 */
+
+#define CUSTOM_MACRO_CNTL	0x00D4	/* Dword offset 0_35 */
+
+#define CONFIG_CNTL		0x00DC	/* Dword offset 0_37 (CT, ET, VT) */
+#define CONFIG_CHIP_ID		0x00E0	/* Dword offset 0_38 */
+#define CONFIG_STAT0		0x00E4	/* Dword offset 0_39 */
+#define CRC_SIG			0x00E8	/* Dword offset 0_3A */
+
+
+/* GUI MEMORY MAPPED Registers */
+
+#define DST_OFF_PITCH		0x0100	/* Dword offset 0_40 */
+#define DST_X			0x0104	/* Dword offset 0_41 */
+#define DST_Y			0x0108	/* Dword offset 0_42 */
+#define DST_Y_X			0x010C	/* Dword offset 0_43 */
+#define DST_WIDTH		0x0110	/* Dword offset 0_44 */
+#define DST_HEIGHT		0x0114	/* Dword offset 0_45 */
+#define DST_HEIGHT_WIDTH	0x0118	/* Dword offset 0_46 */
+#define DST_X_WIDTH		0x011C	/* Dword offset 0_47 */
+#define DST_BRES_LNTH		0x0120	/* Dword offset 0_48 */
+#define DST_BRES_ERR		0x0124	/* Dword offset 0_49 */
+#define DST_BRES_INC		0x0128	/* Dword offset 0_4A */
+#define DST_BRES_DEC		0x012C	/* Dword offset 0_4B */
+#define DST_CNTL		0x0130	/* Dword offset 0_4C */
+#define DST_Y_X__ALIAS__	0x0134	/* Dword offset 0_4D */
+#define TRAIL_BRES_ERR		0x0138	/* Dword offset 0_4E */
+#define TRAIL_BRES_INC		0x013C	/* Dword offset 0_4F */
+#define TRAIL_BRES_DEC		0x0140	/* Dword offset 0_50 */
+#define LEAD_BRES_LNTH		0x0144	/* Dword offset 0_51 */
+#define Z_OFF_PITCH		0x0148	/* Dword offset 0_52 */
+#define Z_CNTL			0x014C	/* Dword offset 0_53 */
+#define ALPHA_TST_CNTL		0x0150	/* Dword offset 0_54 */
+#define SECONDARY_STW_EXP	0x0158	/* Dword offset 0_56 */
+#define SECONDARY_S_X_INC	0x015C	/* Dword offset 0_57 */
+#define SECONDARY_S_Y_INC	0x0160	/* Dword offset 0_58 */
+#define SECONDARY_S_START	0x0164	/* Dword offset 0_59 */
+#define SECONDARY_W_X_INC	0x0168	/* Dword offset 0_5A */
+#define SECONDARY_W_Y_INC	0x016C	/* Dword offset 0_5B */
+#define SECONDARY_W_START	0x0170	/* Dword offset 0_5C */
+#define SECONDARY_T_X_INC	0x0174	/* Dword offset 0_5D */
+#define SECONDARY_T_Y_INC	0x0178	/* Dword offset 0_5E */
+#define SECONDARY_T_START	0x017C	/* Dword offset 0_5F */
+
+#define SRC_OFF_PITCH		0x0180	/* Dword offset 0_60 */
+#define SRC_X			0x0184	/* Dword offset 0_61 */
+#define SRC_Y			0x0188	/* Dword offset 0_62 */
+#define SRC_Y_X			0x018C	/* Dword offset 0_63 */
+#define SRC_WIDTH1		0x0190	/* Dword offset 0_64 */
+#define SRC_HEIGHT1		0x0194	/* Dword offset 0_65 */
+#define SRC_HEIGHT1_WIDTH1	0x0198	/* Dword offset 0_66 */
+#define SRC_X_START		0x019C	/* Dword offset 0_67 */
+#define SRC_Y_START		0x01A0	/* Dword offset 0_68 */
+#define SRC_Y_X_START		0x01A4	/* Dword offset 0_69 */
+#define SRC_WIDTH2		0x01A8	/* Dword offset 0_6A */
+#define SRC_HEIGHT2		0x01AC	/* Dword offset 0_6B */
+#define SRC_HEIGHT2_WIDTH2	0x01B0	/* Dword offset 0_6C */
+#define SRC_CNTL		0x01B4	/* Dword offset 0_6D */
+
+#define SCALE_OFF		0x01C0	/* Dword offset 0_70 */
+#define SECONDARY_SCALE_OFF	0x01C4	/* Dword offset 0_71 */
+
+#define TEX_0_OFF		0x01C0	/* Dword offset 0_70 */
+#define TEX_1_OFF		0x01C4	/* Dword offset 0_71 */
+#define TEX_2_OFF		0x01C8	/* Dword offset 0_72 */
+#define TEX_3_OFF		0x01CC	/* Dword offset 0_73 */
+#define TEX_4_OFF		0x01D0	/* Dword offset 0_74 */
+#define TEX_5_OFF		0x01D4	/* Dword offset 0_75 */
+#define TEX_6_OFF		0x01D8	/* Dword offset 0_76 */
+#define TEX_7_OFF		0x01DC	/* Dword offset 0_77 */
+
+#define SCALE_WIDTH		0x01DC	/* Dword offset 0_77 */
+#define SCALE_HEIGHT		0x01E0	/* Dword offset 0_78 */
+
+#define TEX_8_OFF		0x01E0	/* Dword offset 0_78 */
+#define TEX_9_OFF		0x01E4	/* Dword offset 0_79 */
+#define TEX_10_OFF		0x01E8	/* Dword offset 0_7A */
+#define S_Y_INC			0x01EC	/* Dword offset 0_7B */
+
+#define SCALE_PITCH		0x01EC	/* Dword offset 0_7B */
+#define SCALE_X_INC		0x01F0	/* Dword offset 0_7C */
+
+#define RED_X_INC		0x01F0	/* Dword offset 0_7C */
+#define GREEN_X_INC		0x01F4	/* Dword offset 0_7D */
+
+#define SCALE_Y_INC		0x01F4	/* Dword offset 0_7D */
+#define SCALE_VACC		0x01F8	/* Dword offset 0_7E */
+#define SCALE_3D_CNTL		0x01FC	/* Dword offset 0_7F */
+
+#define HOST_DATA0		0x0200	/* Dword offset 0_80 */
+#define HOST_DATA1		0x0204	/* Dword offset 0_81 */
+#define HOST_DATA2		0x0208	/* Dword offset 0_82 */
+#define HOST_DATA3		0x020C	/* Dword offset 0_83 */
+#define HOST_DATA4		0x0210	/* Dword offset 0_84 */
+#define HOST_DATA5		0x0214	/* Dword offset 0_85 */
+#define HOST_DATA6		0x0218	/* Dword offset 0_86 */
+#define HOST_DATA7		0x021C	/* Dword offset 0_87 */
+#define HOST_DATA8		0x0220	/* Dword offset 0_88 */
+#define HOST_DATA9		0x0224	/* Dword offset 0_89 */
+#define HOST_DATAA		0x0228	/* Dword offset 0_8A */
+#define HOST_DATAB		0x022C	/* Dword offset 0_8B */
+#define HOST_DATAC		0x0230	/* Dword offset 0_8C */
+#define HOST_DATAD		0x0234	/* Dword offset 0_8D */
+#define HOST_DATAE		0x0238	/* Dword offset 0_8E */
+#define HOST_DATAF		0x023C	/* Dword offset 0_8F */
+#define HOST_CNTL		0x0240	/* Dword offset 0_90 */
+
+#define BM_HOSTDATA		0x0244	/* Dword offset 0_91 */
+#define BM_ADDR			0x0248	/* Dword offset 0_92 */
+#define BM_DATA			0x0248	/* Dword offset 0_92 */
+#define BM_GUI_TABLE_CMD	0x024C	/* Dword offset 0_93 */
+
+#define PAT_REG0		0x0280	/* Dword offset 0_A0 */
+#define PAT_REG1		0x0284	/* Dword offset 0_A1 */
+#define PAT_CNTL		0x0288	/* Dword offset 0_A2 */
+
+#define SC_LEFT			0x02A0	/* Dword offset 0_A8 */
+#define SC_RIGHT		0x02A4	/* Dword offset 0_A9 */
+#define SC_LEFT_RIGHT		0x02A8	/* Dword offset 0_AA */
+#define SC_TOP			0x02AC	/* Dword offset 0_AB */
+#define SC_BOTTOM		0x02B0	/* Dword offset 0_AC */
+#define SC_TOP_BOTTOM		0x02B4	/* Dword offset 0_AD */
+
+#define DP_BKGD_CLR		0x02C0	/* Dword offset 0_B0 */
+#define DP_FOG_CLR		0x02C4	/* Dword offset 0_B1 */
+#define DP_FRGD_CLR		0x02C4	/* Dword offset 0_B1 */
+#define DP_WRITE_MSK		0x02C8	/* Dword offset 0_B2 */
+#define DP_CHAIN_MSK		0x02CC	/* Dword offset 0_B3 */
+#define DP_PIX_WIDTH		0x02D0	/* Dword offset 0_B4 */
+#define DP_MIX			0x02D4	/* Dword offset 0_B5 */
+#define DP_SRC			0x02D8	/* Dword offset 0_B6 */
+#define DP_FRGD_CLR_MIX		0x02DC	/* Dword offset 0_B7 */
+#define DP_FRGD_BLGD_CLR	0x02E0	/* Dword offset 0_B8 */
+
+#define DST_X_Y			0x02E8	/* Dword offset 0_BA */
+#define DST_WIDTH_HEIGHT	0x02EC	/* Dword offset 0_BB */
+#define USR_DST_PICTH		0x02F0	/* Dword offset 0_BC */
+#define DP_SET_GUI_ENGINE2	0x02F8	/* Dword offset 0_BE */
+#define DP_SET_GUI_ENGINE	0x02FC	/* Dword offset 0_BF */
+
+#define CLR_CMP_CLR		0x0300	/* Dword offset 0_C0 */
+#define CLR_CMP_MSK		0x0304	/* Dword offset 0_C1 */
+#define CLR_CMP_CNTL		0x0308	/* Dword offset 0_C2 */
+
+#define FIFO_STAT		0x0310	/* Dword offset 0_C4 */
+
+#define CONTEXT_MASK		0x0320	/* Dword offset 0_C8 */
+#define CONTEXT_LOAD_CNTL	0x032C	/* Dword offset 0_CB */
+
+#define GUI_TRAJ_CNTL		0x0330	/* Dword offset 0_CC */
+#define GUI_STAT		0x0338	/* Dword offset 0_CE */
+
+#define TEX_PALETTE_INDEX	0x0340	/* Dword offset 0_D0 */
+#define STW_EXP			0x0344	/* Dword offset 0_D1 */
+#define LOG_MAX_INC		0x0348	/* Dword offset 0_D2 */
+#define S_X_INC			0x034C	/* Dword offset 0_D3 */
+#define S_Y_INC__ALIAS__	0x0350	/* Dword offset 0_D4 */
+
+#define SCALE_PITCH__ALIAS__	0x0350	/* Dword offset 0_D4 */
+
+#define S_START			0x0354	/* Dword offset 0_D5 */
+#define W_X_INC			0x0358	/* Dword offset 0_D6 */
+#define W_Y_INC			0x035C	/* Dword offset 0_D7 */
+#define W_START			0x0360	/* Dword offset 0_D8 */
+#define T_X_INC			0x0364	/* Dword offset 0_D9 */
+#define T_Y_INC			0x0368	/* Dword offset 0_DA */
+
+#define SECONDARY_SCALE_PITCH	0x0368	/* Dword offset 0_DA */
+
+#define T_START			0x036C	/* Dword offset 0_DB */
+#define TEX_SIZE_PITCH		0x0370	/* Dword offset 0_DC */
+#define TEX_CNTL		0x0374	/* Dword offset 0_DD */
+#define SECONDARY_TEX_OFFSET	0x0378	/* Dword offset 0_DE */
+#define TEX_PALETTE		0x037C	/* Dword offset 0_DF */
+
+#define SCALE_PITCH_BOTH	0x0380	/* Dword offset 0_E0 */
+#define SECONDARY_SCALE_OFF_ACC	0x0384	/* Dword offset 0_E1 */
+#define SCALE_OFF_ACC		0x0388	/* Dword offset 0_E2 */
+#define SCALE_DST_Y_X		0x038C	/* Dword offset 0_E3 */
+
+#define COMPOSITE_SHADOW_ID	0x0398	/* Dword offset 0_E6 */
+
+#define SECONDARY_SCALE_X_INC	0x039C	/* Dword offset 0_E7 */
+
+#define SPECULAR_RED_X_INC	0x039C	/* Dword offset 0_E7 */
+#define SPECULAR_RED_Y_INC	0x03A0	/* Dword offset 0_E8 */
+#define SPECULAR_RED_START	0x03A4	/* Dword offset 0_E9 */
+
+#define SECONDARY_SCALE_HACC	0x03A4	/* Dword offset 0_E9 */
+
+#define SPECULAR_GREEN_X_INC	0x03A8	/* Dword offset 0_EA */
+#define SPECULAR_GREEN_Y_INC	0x03AC	/* Dword offset 0_EB */
+#define SPECULAR_GREEN_START	0x03B0	/* Dword offset 0_EC */
+#define SPECULAR_BLUE_X_INC	0x03B4	/* Dword offset 0_ED */
+#define SPECULAR_BLUE_Y_INC	0x03B8	/* Dword offset 0_EE */
+#define SPECULAR_BLUE_START	0x03BC	/* Dword offset 0_EF */
+
+#define SCALE_X_INC__ALIAS__	0x03C0	/* Dword offset 0_F0 */
+
+#define RED_X_INC__ALIAS__	0x03C0	/* Dword offset 0_F0 */
+#define RED_Y_INC		0x03C4	/* Dword offset 0_F1 */
+#define RED_START		0x03C8	/* Dword offset 0_F2 */
+
+#define SCALE_HACC		0x03C8	/* Dword offset 0_F2 */
+#define SCALE_Y_INC__ALIAS__	0x03CC	/* Dword offset 0_F3 */
+
+#define GREEN_X_INC__ALIAS__	0x03CC	/* Dword offset 0_F3 */
+#define GREEN_Y_INC		0x03D0	/* Dword offset 0_F4 */
+
+#define SECONDARY_SCALE_Y_INC	0x03D0	/* Dword offset 0_F4 */
+#define SECONDARY_SCALE_VACC	0x03D4	/* Dword offset 0_F5 */
+
+#define GREEN_START		0x03D4	/* Dword offset 0_F5 */
+#define BLUE_X_INC		0x03D8	/* Dword offset 0_F6 */
+#define BLUE_Y_INC		0x03DC	/* Dword offset 0_F7 */
+#define BLUE_START		0x03E0	/* Dword offset 0_F8 */
+#define Z_X_INC			0x03E4	/* Dword offset 0_F9 */
+#define Z_Y_INC			0x03E8	/* Dword offset 0_FA */
+#define Z_START			0x03EC	/* Dword offset 0_FB */
+#define ALPHA_X_INC		0x03F0	/* Dword offset 0_FC */
+#define FOG_X_INC		0x03F0	/* Dword offset 0_FC */
+#define ALPHA_Y_INC		0x03F4	/* Dword offset 0_FD */
+#define FOG_Y_INC		0x03F4	/* Dword offset 0_FD */
+#define ALPHA_START		0x03F8	/* Dword offset 0_FE */
+#define FOG_START		0x03F8	/* Dword offset 0_FE */
+
+#define OVERLAY_Y_X_START		0x0400	/* Dword offset 1_00 */
+#define OVERLAY_Y_X_END			0x0404	/* Dword offset 1_01 */
+#define OVERLAY_VIDEO_KEY_CLR		0x0408	/* Dword offset 1_02 */
+#define OVERLAY_VIDEO_KEY_MSK		0x040C	/* Dword offset 1_03 */
+#define OVERLAY_GRAPHICS_KEY_CLR	0x0410	/* Dword offset 1_04 */
+#define OVERLAY_GRAPHICS_KEY_MSK	0x0414	/* Dword offset 1_05 */
+#define OVERLAY_KEY_CNTL		0x0418	/* Dword offset 1_06 */
+
+#define OVERLAY_SCALE_INC	0x0420	/* Dword offset 1_08 */
+#define OVERLAY_SCALE_CNTL	0x0424	/* Dword offset 1_09 */
+#define SCALER_HEIGHT_WIDTH	0x0428	/* Dword offset 1_0A */
+#define SCALER_TEST		0x042C	/* Dword offset 1_0B */
+#define SCALER_BUF0_OFFSET	0x0434	/* Dword offset 1_0D */
+#define SCALER_BUF1_OFFSET	0x0438	/* Dword offset 1_0E */
+#define SCALE_BUF_PITCH		0x043C	/* Dword offset 1_0F */
+
+#define CAPTURE_START_END	0x0440	/* Dword offset 1_10 */
+#define CAPTURE_X_WIDTH		0x0444	/* Dword offset 1_11 */
+#define VIDEO_FORMAT		0x0448	/* Dword offset 1_12 */
+#define VBI_START_END		0x044C	/* Dword offset 1_13 */
+#define CAPTURE_CONFIG		0x0450	/* Dword offset 1_14 */
+#define TRIG_CNTL		0x0454	/* Dword offset 1_15 */
+
+#define OVERLAY_EXCLUSIVE_HORZ	0x0458	/* Dword offset 1_16 */
+#define OVERLAY_EXCLUSIVE_VERT	0x045C	/* Dword offset 1_17 */
+
+#define VAL_WIDTH		0x0460	/* Dword offset 1_18 */
+#define CAPTURE_DEBUG		0x0464	/* Dword offset 1_19 */
+#define VIDEO_SYNC_TEST		0x0468	/* Dword offset 1_1A */
+
+#define SNAPSHOT_VH_COUNTS	0x0470	/* Dword offset 1_1C */
+#define SNAPSHOT_F_COUNT	0x0474	/* Dword offset 1_1D */
+#define N_VIF_COUNT		0x0478	/* Dword offset 1_1E */
+#define SNAPSHOT_VIF_COUNT	0x047C	/* Dword offset 1_1F */
+
+#define CAPTURE_BUF0_OFFSET	0x0480	/* Dword offset 1_20 */
+#define CAPTURE_BUF1_OFFSET	0x0484	/* Dword offset 1_21 */
+#define CAPTURE_BUF_PITCH	0x0488	/* Dword offset 1_22 */
+
+#define MPP_CONFIG		0x04C0	/* Dword offset 1_30 */
+#define MPP_STROBE_SEQ		0x04C4	/* Dword offset 1_31 */
+#define MPP_ADDR		0x04C8	/* Dword offset 1_32 */
+#define MPP_DATA		0x04CC	/* Dword offset 1_33 */
+#define TVO_CNTL		0x0500	/* Dword offset 1_40 */
+
+#define CRT_HORZ_VERT_LOAD	0x0544	/* Dword offset 1_51 */
+
+#define AGP_BASE		0x0548	/* Dword offset 1_52 */
+#define AGP_CNTL		0x054C	/* Dword offset 1_53 */
+
+#define SCALER_COLOUR_CNTL	0x0550	/* Dword offset 1_54 */
+#define SCALER_H_COEFF0		0x0554	/* Dword offset 1_55 */
+#define SCALER_H_COEFF1		0x0558	/* Dword offset 1_56 */
+#define SCALER_H_COEFF2		0x055C	/* Dword offset 1_57 */
+#define SCALER_H_COEFF3		0x0560	/* Dword offset 1_58 */
+#define SCALER_H_COEFF4		0x0564	/* Dword offset 1_59 */
+
+#define GUI_CNTL		0x0578	/* Dword offset 1_5E */
+
+#define BM_FRAME_BUF_OFFSET	0x0580	/* Dword offset 1_60 */
+#define BM_SYSTEM_MEM_ADDR	0x0584	/* Dword offset 1_61 */
+#define BM_COMMAND		0x0588	/* Dword offset 1_62 */
+#define BM_STATUS		0x058C	/* Dword offset 1_63 */
+#define BM_GUI_TABLE		0x05B8	/* Dword offset 1_6E */
+#define BM_SYSTEM_TABLE		0x05BC	/* Dword offset 1_6F */
+
+#define SCALER_BUF0_OFFSET_U	0x05D4	/* Dword offset 1_75 */
+#define SCALER_BUF0_OFFSET_V	0x05D8	/* Dword offset 1_76 */
+#define SCALER_BUF1_OFFSET_U	0x05DC	/* Dword offset 1_77 */
+#define SCALER_BUF1_OFFSET_V	0x05E0	/* Dword offset 1_78 */
+
+#define VERTEX_1_S		0x0640	/* Dword offset 1_90 */
+#define VERTEX_1_T		0x0644	/* Dword offset 1_91 */
+#define VERTEX_1_W		0x0648	/* Dword offset 1_92 */
+#define VERTEX_1_SPEC_ARGB	0x064C	/* Dword offset 1_93 */
+#define VERTEX_1_Z		0x0650	/* Dword offset 1_94 */
+#define VERTEX_1_ARGB		0x0654	/* Dword offset 1_95 */
+#define VERTEX_1_X_Y		0x0658	/* Dword offset 1_96 */
+#define ONE_OVER_AREA		0x065C	/* Dword offset 1_97 */
+#define VERTEX_2_S		0x0660	/* Dword offset 1_98 */
+#define VERTEX_2_T		0x0664	/* Dword offset 1_99 */
+#define VERTEX_2_W		0x0668	/* Dword offset 1_9A */
+#define VERTEX_2_SPEC_ARGB	0x066C	/* Dword offset 1_9B */
+#define VERTEX_2_Z		0x0670	/* Dword offset 1_9C */
+#define VERTEX_2_ARGB		0x0674	/* Dword offset 1_9D */
+#define VERTEX_2_X_Y		0x0678	/* Dword offset 1_9E */
+#define ONE_OVER_AREA		0x065C	/* Dword offset 1_9F */
+#define VERTEX_3_S		0x0680	/* Dword offset 1_A0 */
+#define VERTEX_3_T		0x0684	/* Dword offset 1_A1 */
+#define VERTEX_3_W		0x0688	/* Dword offset 1_A2 */
+#define VERTEX_3_SPEC_ARGB	0x068C	/* Dword offset 1_A3 */
+#define VERTEX_3_Z		0x0690	/* Dword offset 1_A4 */
+#define VERTEX_3_ARGB		0x0694	/* Dword offset 1_A5 */
+#define VERTEX_3_X_Y		0x0698	/* Dword offset 1_A6 */
+#define ONE_OVER_AREA		0x065C	/* Dword offset 1_A7 */
+#define VERTEX_1_S		0x0640	/* Dword offset 1_AB */
+#define VERTEX_1_T		0x0644	/* Dword offset 1_AC */
+#define VERTEX_1_W		0x0648	/* Dword offset 1_AD */
+#define VERTEX_2_S		0x0660	/* Dword offset 1_AE */
+#define VERTEX_2_T		0x0664	/* Dword offset 1_AF */
+#define VERTEX_2_W		0x0668	/* Dword offset 1_B0 */
+#define VERTEX_3_SECONDARY_S	0x06C0	/* Dword offset 1_B0 */
+#define VERTEX_3_S		0x0680	/* Dword offset 1_B1 */
+#define VERTEX_3_SECONDARY_T	0x06C4	/* Dword offset 1_B1 */
+#define VERTEX_3_T		0x0684	/* Dword offset 1_B2 */
+#define VERTEX_3_SECONDARY_W	0x06C8	/* Dword offset 1_B2 */
+#define VERTEX_3_W		0x0688	/* Dword offset 1_B3 */
+#define VERTEX_1_SPEC_ARGB	0x064C	/* Dword offset 1_B4 */
+#define VERTEX_2_SPEC_ARGB	0x066C	/* Dword offset 1_B5 */
+#define VERTEX_3_SPEC_ARGB	0x068C	/* Dword offset 1_B6 */
+#define VERTEX_1_Z		0x0650	/* Dword offset 1_B7 */
+#define VERTEX_2_Z		0x0670	/* Dword offset 1_B8 */
+#define VERTEX_3_Z		0x0690	/* Dword offset 1_B9 */
+#define VERTEX_1_ARGB		0x0654	/* Dword offset 1_BA */
+#define VERTEX_2_ARGB		0x0674	/* Dword offset 1_BB */
+#define VERTEX_3_ARGB		0x0694	/* Dword offset 1_BC */
+#define VERTEX_1_X_Y		0x0658	/* Dword offset 1_BD */
+#define VERTEX_2_X_Y		0x0678	/* Dword offset 1_BE */
+#define VERTEX_3_X_Y		0x0698	/* Dword offset 1_BF */
+#define ONE_OVER_AREA_UC	0x0700	/* Dword offset 1_C0 */
+#define SETUP_CNTL		0x0704	/* Dword offset 1_C1 */
+#define VERTEX_1_SECONDARY_S	0x0728	/* Dword offset 1_CA */
+#define VERTEX_1_SECONDARY_T	0x072C	/* Dword offset 1_CB */
+#define VERTEX_1_SECONDARY_W	0x0730	/* Dword offset 1_CC */
+#define VERTEX_2_SECONDARY_S	0x0734	/* Dword offset 1_CD */
+#define VERTEX_2_SECONDARY_T	0x0738	/* Dword offset 1_CE */
+#define VERTEX_2_SECONDARY_W	0x073C	/* Dword offset 1_CF */
+
+
+/* CRTC control values (mostly CRTC_GEN_CNTL) */
+
+#define CRTC_H_SYNC_NEG		0x00200000
+#define CRTC_V_SYNC_NEG		0x00200000
+
+#define CRTC_DBL_SCAN_EN	0x00000001
+#define CRTC_INTERLACE_EN	0x00000002
+#define CRTC_HSYNC_DIS		0x00000004
+#define CRTC_VSYNC_DIS		0x00000008
+#define CRTC_CSYNC_EN		0x00000010
+#define CRTC_PIX_BY_2_EN	0x00000020	/* unused on RAGE */
+#define CRTC_DISPLAY_DIS	0x00000040
+#define CRTC_VGA_XOVERSCAN	0x00000040
+
+#define CRTC_PIX_WIDTH_MASK	0x00000700
+#define CRTC_PIX_WIDTH_4BPP	0x00000100
+#define CRTC_PIX_WIDTH_8BPP	0x00000200
+#define CRTC_PIX_WIDTH_15BPP	0x00000300
+#define CRTC_PIX_WIDTH_16BPP	0x00000400
+#define CRTC_PIX_WIDTH_24BPP	0x00000500
+#define CRTC_PIX_WIDTH_32BPP	0x00000600
+
+#define CRTC_BYTE_PIX_ORDER	0x00000800
+#define CRTC_PIX_ORDER_MSN_LSN	0x00000000
+#define CRTC_PIX_ORDER_LSN_MSN	0x00000800
+
+#define CRTC_FIFO_LWM		0x000f0000
+
+#define VGA_128KAP_PAGING	0x00100000
+#define VFC_SYNC_TRISTATE	0x00200000
+#define CRTC_LOCK_REGS		0x00400000
+#define CRTC_SYNC_TRISTATE	0x00800000
+
+#define CRTC_EXT_DISP_EN	0x01000000
+#define CRTC_ENABLE		0x02000000
+#define CRTC_DISP_REQ_ENB	0x04000000
+#define VGA_ATI_LINEAR		0x08000000
+#define CRTC_VSYNC_FALL_EDGE	0x10000000
+#define VGA_TEXT_132		0x20000000
+#define VGA_XCRT_CNT_EN		0x40000000
+#define VGA_CUR_B_TEST		0x80000000
+
+#define CRTC_CRNT_VLINE		0x07f00000
+#define CRTC_VBLANK		0x00000001
+
+
+/* DAC control values */
+
+#define DAC_EXT_SEL_RS2		0x01
+#define DAC_EXT_SEL_RS3		0x02
+#define DAC_8BIT_EN		0x00000100
+#define DAC_PIX_DLY_MASK	0x00000600
+#define DAC_PIX_DLY_0NS		0x00000000
+#define DAC_PIX_DLY_2NS		0x00000200
+#define DAC_PIX_DLY_4NS		0x00000400
+#define DAC_BLANK_ADJ_MASK	0x00001800
+#define DAC_BLANK_ADJ_0		0x00000000
+#define DAC_BLANK_ADJ_1		0x00000800
+#define DAC_BLANK_ADJ_2		0x00001000
+
+
+/* Mix control values */
+
+#define MIX_NOT_DST		0x0000
+#define MIX_0			0x0001
+#define MIX_1			0x0002
+#define MIX_DST			0x0003
+#define MIX_NOT_SRC		0x0004
+#define MIX_XOR			0x0005
+#define MIX_XNOR		0x0006
+#define MIX_SRC			0x0007
+#define MIX_NAND		0x0008
+#define MIX_NOT_SRC_OR_DST	0x0009
+#define MIX_SRC_OR_NOT_DST	0x000a
+#define MIX_OR			0x000b
+#define MIX_AND			0x000c
+#define MIX_SRC_AND_NOT_DST	0x000d
+#define MIX_NOT_SRC_AND_DST	0x000e
+#define MIX_NOR			0x000f
+
+/* Maximum engine dimensions */
+#define ENGINE_MIN_X		0
+#define ENGINE_MIN_Y		0
+#define ENGINE_MAX_X		4095
+#define ENGINE_MAX_Y		16383
+
+/* Mach64 engine bit constants - these are typically ORed together */
+
+/* BUS_CNTL register constants */
+#define BUS_FIFO_ERR_ACK	0x00200000
+#define BUS_HOST_ERR_ACK	0x00800000
+
+/* GEN_TEST_CNTL register constants */
+#define GEN_OVR_OUTPUT_EN	0x20
+#define HWCURSOR_ENABLE		0x80
+#define GUI_ENGINE_ENABLE	0x100
+#define BLOCK_WRITE_ENABLE	0x200
+
+/* DSP_CONFIG register constants */
+#define DSP_XCLKS_PER_QW	0x00003fff
+#define DSP_LOOP_LATENCY	0x000f0000
+#define DSP_PRECISION		0x00700000
+
+/* DSP_ON_OFF register constants */
+#define DSP_OFF			0x000007ff
+#define DSP_ON			0x07ff0000
+
+/* CLOCK_CNTL register constants */
+#define CLOCK_SEL		0x0f
+#define CLOCK_DIV		0x30
+#define CLOCK_DIV1		0x00
+#define CLOCK_DIV2		0x10
+#define CLOCK_DIV4		0x20
+#define CLOCK_STROBE		0x40
+#define PLL_WR_EN		0x02
+
+/* PLL registers */
+#define PLL_MACRO_CNTL		0x01
+#define PLL_REF_DIV		0x02
+#define PLL_GEN_CNTL		0x03
+#define MCLK_FB_DIV		0x04
+#define PLL_VCLK_CNTL		0x05
+#define VCLK_POST_DIV		0x06
+#define VCLK0_FB_DIV		0x07
+#define VCLK1_FB_DIV		0x08
+#define VCLK2_FB_DIV		0x09
+#define VCLK3_FB_DIV		0x0A
+#define PLL_XCLK_CNTL		0x0B
+#define PLL_TEST_CTRL		0x0E
+#define PLL_TEST_COUNT		0x0F
+
+/* Fields in PLL registers */
+#define PLL_PC_GAIN		0x07
+#define PLL_VC_GAIN		0x18
+#define PLL_DUTY_CYC		0xE0
+#define PLL_OVERRIDE		0x01
+#define PLL_MCLK_RST		0x02
+#define OSC_EN			0x04
+#define EXT_CLK_EN		0x08
+#define MCLK_SRC_SEL		0x70
+#define EXT_CLK_CNTL		0x80
+#define VCLK_SRC_SEL		0x03
+#define PLL_VCLK_RST		0x04
+#define VCLK_INVERT		0x08
+#define VCLK0_POST		0x03
+#define VCLK1_POST		0x0C
+#define VCLK2_POST		0x30
+#define VCLK3_POST		0xC0
+
+/* CONFIG_CNTL register constants */
+#define APERTURE_4M_ENABLE	1
+#define APERTURE_8M_ENABLE	2
+#define VGA_APERTURE_ENABLE	4
+
+/* CONFIG_STAT0 register constants (GX, CX) */
+#define CFG_BUS_TYPE		0x00000007
+#define CFG_MEM_TYPE		0x00000038
+#define CFG_INIT_DAC_TYPE	0x00000e00
+
+/* CONFIG_STAT0 register constants (CT, ET, VT) */
+#define CFG_MEM_TYPE_xT		0x00000007
+
+#define ISA			0
+#define EISA			1
+#define LOCAL_BUS		6
+#define PCI			7
+
+/* Memory types for GX, CX */
+#define DRAMx4			0
+#define VRAMx16			1
+#define VRAMx16ssr		2
+#define DRAMx16			3
+#define GraphicsDRAMx16		4
+#define EnhancedVRAMx16		5
+#define EnhancedVRAMx16ssr	6
+
+/* Memory types for CT, ET, VT, GT */
+#define DRAM			1
+#define EDO			2
+#define PSEUDO_EDO		3
+#define SDRAM			4
+#define SGRAM			5
+#define WRAM			6
+
+#define DAC_INTERNAL		0x00
+#define DAC_IBMRGB514		0x01
+#define DAC_ATI68875		0x02
+#define DAC_TVP3026_A		0x72
+#define DAC_BT476		0x03
+#define DAC_BT481		0x04
+#define DAC_ATT20C491		0x14
+#define DAC_SC15026		0x24
+#define DAC_MU9C1880		0x34
+#define DAC_IMSG174		0x44
+#define DAC_ATI68860_B		0x05
+#define DAC_ATI68860_C		0x15
+#define DAC_TVP3026_B		0x75
+#define DAC_STG1700		0x06
+#define DAC_ATT498		0x16
+#define DAC_STG1702		0x07
+#define DAC_SC15021		0x17
+#define DAC_ATT21C498		0x27
+#define DAC_STG1703		0x37
+#define DAC_CH8398		0x47
+#define DAC_ATT20C408		0x57
+
+#define CLK_ATI18818_0		0
+#define CLK_ATI18818_1		1
+#define CLK_STG1703		2
+#define CLK_CH8398		3
+#define CLK_INTERNAL		4
+#define CLK_ATT20C408		5
+#define CLK_IBMRGB514		6
+
+/* MEM_CNTL register constants */
+#define MEM_SIZE_ALIAS		0x00000007
+#define MEM_SIZE_512K		0x00000000
+#define MEM_SIZE_1M		0x00000001
+#define MEM_SIZE_2M		0x00000002
+#define MEM_SIZE_4M		0x00000003
+#define MEM_SIZE_6M		0x00000004
+#define MEM_SIZE_8M		0x00000005
+#define MEM_SIZE_ALIAS_GTB	0x0000000F
+#define MEM_SIZE_2M_GTB		0x00000003
+#define MEM_SIZE_4M_GTB		0x00000007
+#define MEM_SIZE_6M_GTB		0x00000009
+#define MEM_SIZE_8M_GTB		0x0000000B
+#define MEM_BNDRY		0x00030000
+#define MEM_BNDRY_0K		0x00000000
+#define MEM_BNDRY_256K		0x00010000
+#define MEM_BNDRY_512K		0x00020000
+#define MEM_BNDRY_1M		0x00030000
+#define MEM_BNDRY_EN		0x00040000
+
+/* ATI PCI constants */
+#define PCI_ATI_VENDOR_ID	0x1002
+#define PCI_MACH64_GX		0x4758
+#define PCI_MACH64_CX		0x4358
+#define PCI_MACH64_CT		0x4354
+#define PCI_MACH64_ET		0x4554
+#define PCI_MACH64_VT		0x5654
+#define PCI_MACH64_GT		0x4754
+
+/* CONFIG_CHIP_ID register constants */
+#define CFG_CHIP_TYPE		0x0000FFFF
+#define CFG_CHIP_CLASS		0x00FF0000
+#define CFG_CHIP_REV		0xFF000000
+#define CFG_CHIP_VERSION	0x07000000
+#define CFG_CHIP_FOUNDRY	0x38000000
+#define CFG_CHIP_REVISION	0xC0000000
+
+/* Chip IDs read from CONFIG_CHIP_ID */
+#define MACH64_GX_ID		0xD7
+#define MACH64_CX_ID		0x57
+#define MACH64_CT_ID		0x4354
+#define MACH64_ET_ID		0x4554
+#define MACH64_VT_ID		0x5654
+#define MACH64_GT_ID		0x4754
+
+/* Mach64 chip types */
+#define MACH64_UNKNOWN		0
+#define MACH64_GX		1
+#define MACH64_CX		2
+#define MACH64_CT		3
+#define MACH64_ET		4
+#define MACH64_VT		5
+#define MACH64_GT		6
+
+/* DST_CNTL register constants */
+#define DST_X_RIGHT_TO_LEFT	0
+#define DST_X_LEFT_TO_RIGHT	1
+#define DST_Y_BOTTOM_TO_TOP	0
+#define DST_Y_TOP_TO_BOTTOM	2
+#define DST_X_MAJOR		0
+#define DST_Y_MAJOR		4
+#define DST_X_TILE		8
+#define DST_Y_TILE		0x10
+#define DST_LAST_PEL		0x20
+#define DST_POLYGON_ENABLE	0x40
+#define DST_24_ROTATION_ENABLE	0x80
+
+/* SRC_CNTL register constants */
+#define SRC_PATTERN_ENABLE		1
+#define SRC_ROTATION_ENABLE		2
+#define SRC_LINEAR_ENABLE		4
+#define SRC_BYTE_ALIGN			8
+#define SRC_LINE_X_RIGHT_TO_LEFT	0
+#define SRC_LINE_X_LEFT_TO_RIGHT	0x10
+
+/* HOST_CNTL register constants */
+#define HOST_BYTE_ALIGN		1
+
+/* GUI_TRAJ_CNTL register constants */
+#define PAT_MONO_8x8_ENABLE	0x01000000
+#define PAT_CLR_4x2_ENABLE	0x02000000
+#define PAT_CLR_8x1_ENABLE	0x04000000
+
+/* DP_CHAIN_MASK register constants */
+#define DP_CHAIN_4BPP		0x8888
+#define DP_CHAIN_7BPP		0xD2D2
+#define DP_CHAIN_8BPP		0x8080
+#define DP_CHAIN_8BPP_RGB	0x9292
+#define DP_CHAIN_15BPP		0x4210
+#define DP_CHAIN_16BPP		0x8410
+#define DP_CHAIN_24BPP		0x8080
+#define DP_CHAIN_32BPP		0x8080
+
+/* DP_PIX_WIDTH register constants */
+#define DST_1BPP		0
+#define DST_4BPP		1
+#define DST_8BPP		2
+#define DST_15BPP		3
+#define DST_16BPP		4
+#define DST_32BPP		6
+#define SRC_1BPP		0
+#define SRC_4BPP		0x100
+#define SRC_8BPP		0x200
+#define SRC_15BPP		0x300
+#define SRC_16BPP		0x400
+#define SRC_32BPP		0x600
+#define HOST_1BPP		0
+#define HOST_4BPP		0x10000
+#define HOST_8BPP		0x20000
+#define HOST_15BPP		0x30000
+#define HOST_16BPP		0x40000
+#define HOST_32BPP		0x60000
+#define BYTE_ORDER_MSB_TO_LSB	0
+#define BYTE_ORDER_LSB_TO_MSB	0x1000000
+
+/* DP_MIX register constants */
+#define BKGD_MIX_NOT_D			0
+#define BKGD_MIX_ZERO			1
+#define BKGD_MIX_ONE			2
+#define BKGD_MIX_D			3
+#define BKGD_MIX_NOT_S			4
+#define BKGD_MIX_D_XOR_S		5
+#define BKGD_MIX_NOT_D_XOR_S		6
+#define BKGD_MIX_S			7
+#define BKGD_MIX_NOT_D_OR_NOT_S		8
+#define BKGD_MIX_D_OR_NOT_S		9
+#define BKGD_MIX_NOT_D_OR_S		10
+#define BKGD_MIX_D_OR_S			11
+#define BKGD_MIX_D_AND_S		12
+#define BKGD_MIX_NOT_D_AND_S		13
+#define BKGD_MIX_D_AND_NOT_S		14
+#define BKGD_MIX_NOT_D_AND_NOT_S	15
+#define BKGD_MIX_D_PLUS_S_DIV2		0x17
+#define FRGD_MIX_NOT_D			0
+#define FRGD_MIX_ZERO			0x10000
+#define FRGD_MIX_ONE			0x20000
+#define FRGD_MIX_D			0x30000
+#define FRGD_MIX_NOT_S			0x40000
+#define FRGD_MIX_D_XOR_S		0x50000
+#define FRGD_MIX_NOT_D_XOR_S		0x60000
+#define FRGD_MIX_S			0x70000
+#define FRGD_MIX_NOT_D_OR_NOT_S		0x80000
+#define FRGD_MIX_D_OR_NOT_S		0x90000
+#define FRGD_MIX_NOT_D_OR_S		0xa0000
+#define FRGD_MIX_D_OR_S			0xb0000
+#define FRGD_MIX_D_AND_S		0xc0000
+#define FRGD_MIX_NOT_D_AND_S		0xd0000
+#define FRGD_MIX_D_AND_NOT_S		0xe0000
+#define FRGD_MIX_NOT_D_AND_NOT_S	0xf0000
+#define FRGD_MIX_D_PLUS_S_DIV2		0x170000
+
+/* DP_SRC register constants */
+#define BKGD_SRC_BKGD_CLR	0
+#define BKGD_SRC_FRGD_CLR	1
+#define BKGD_SRC_HOST		2
+#define BKGD_SRC_BLIT		3
+#define BKGD_SRC_PATTERN	4
+#define FRGD_SRC_BKGD_CLR	0
+#define FRGD_SRC_FRGD_CLR	0x100
+#define FRGD_SRC_HOST		0x200
+#define FRGD_SRC_BLIT		0x300
+#define FRGD_SRC_PATTERN	0x400
+#define MONO_SRC_ONE		0
+#define MONO_SRC_PATTERN	0x10000
+#define MONO_SRC_HOST		0x20000
+#define MONO_SRC_BLIT		0x30000
+
+/* CLR_CMP_CNTL register constants */
+#define COMPARE_FALSE		0
+#define COMPARE_TRUE		1
+#define COMPARE_NOT_EQUAL	4
+#define COMPARE_EQUAL		5
+#define COMPARE_DESTINATION	0
+#define COMPARE_SOURCE		0x1000000
+
+/* FIFO_STAT register constants */
+#define FIFO_ERR		0x80000000
+
+/* CONTEXT_LOAD_CNTL constants */
+#define CONTEXT_NO_LOAD			0
+#define CONTEXT_LOAD			0x10000
+#define CONTEXT_LOAD_AND_DO_FILL	0x20000
+#define CONTEXT_LOAD_AND_DO_LINE	0x30000
+#define CONTEXT_EXECUTE			0
+#define CONTEXT_CMD_DISABLE		0x80000000
+
+/* GUI_STAT register constants */
+#define ENGINE_IDLE		0
+#define ENGINE_BUSY		1
+#define SCISSOR_LEFT_FLAG	0x10
+#define SCISSOR_RIGHT_FLAG	0x20
+#define SCISSOR_TOP_FLAG	0x40
+#define SCISSOR_BOTTOM_FLAG	0x80
+
+/* ATI VGA Extended Regsiters */
+#define sioATIEXT		0x1ce
+#define bioATIEXT		0x3ce
+
+#define ATI2E			0xae
+#define ATI32			0xb2
+#define ATI36			0xb6
+
+/* VGA Graphics Controller Registers */
+#define VGAGRA			0x3ce
+#define GRA06			0x06
+
+/* VGA Seququencer Registers */
+#define VGASEQ			0x3c4
+#define SEQ02			0x02
+#define SEQ04			0x04
+
+#define MACH64_MAX_X		ENGINE_MAX_X
+#define MACH64_MAX_Y		ENGINE_MAX_Y
+
+#define INC_X			0x0020
+#define INC_Y			0x0080
+
+#define RGB16_555		0x0000
+#define RGB16_565		0x0040
+#define RGB16_655		0x0080
+#define RGB16_664		0x00c0
+
+#define POLY_TEXT_TYPE		0x0001
+#define IMAGE_TEXT_TYPE		0x0002
+#define TEXT_TYPE_8_BIT		0x0004
+#define TEXT_TYPE_16_BIT	0x0008
+#define POLY_TEXT_TYPE_8	(POLY_TEXT_TYPE | TEXT_TYPE_8_BIT)
+#define IMAGE_TEXT_TYPE_8	(IMAGE_TEXT_TYPE | TEXT_TYPE_8_BIT)
+#define POLY_TEXT_TYPE_16	(POLY_TEXT_TYPE | TEXT_TYPE_16_BIT)
+#define IMAGE_TEXT_TYPE_16	(IMAGE_TEXT_TYPE | TEXT_TYPE_16_BIT)
+
+#define MACH64_NUM_CLOCKS	16
+#define MACH64_NUM_FREQS	50
+
+/* Wait until "v" queue entries are free */
+#define aty_WaitQueue(v)    { while ((aty_ld_le32(FIFO_STAT) & 0xffff) > \
+			 ((unsigned short)(0x8000 >> (v)))); }
+
+/* Wait until GP is idle and queue is empty */
+#define aty_WaitIdleEmpty() { aty_WaitQueue(16); \
+			  while ((aty_ld_le32(GUI_STAT) & 1) != 0); }
+
+#define SKIP_2(_v) ((((_v)<<1)&0xfff8)|((_v)&0x3)|(((_v)&0x80)>>5))
+
+#define MACH64_BIT_BLT(_srcx, _srcy, _dstx, _dsty, _w, _h, _dir) \
+{ \
+    aty_WaitQueue(5); \
+    aty_st_le32(SRC_Y_X, (((_srcx) << 16) | ((_srcy) & 0x0000ffff))); \
+    aty_st_le32(SRC_WIDTH1, (_w)); \
+    aty_st_le32(DST_CNTL, (_dir)); \
+    aty_st_le32(DST_Y_X, (((_dstx) << 16) | ((_dsty) & 0x0000ffff))); \
+    aty_st_le32(DST_HEIGHT_WIDTH, (((_w) << 16) | ((_h) & 0x0000ffff))); \
+}
+#endif /* REGMACH64_H */
+
diff -u --recursive --new-file v2.1.90/linux/drivers/video/atyfb.c linux/drivers/video/atyfb.c
--- v2.1.90/linux/drivers/video/atyfb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/atyfb.c	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,1685 @@
+/*
+ *  linux/drivers/video/atyfb.c -- Frame buffer device for ATI/Open Firmware
+ *
+ *	Copyright (C) 1997 Geert Uytterhoeven
+ *
+ *  This driver is partly based on the PowerMac console driver:
+ *
+ *	Copyright (C) 1996 Paul Mackerras
+ *
+ *  and on the PowerMac ATI/mach64 display driver:
+ *
+ *	Copyright (C) 1997 Michael AK Tesch
+ *
+ *	      with work by Jon Howell
+ *			   Harry AC Eaton
+ *			   Anthony Tong <atong@uiuc.edu>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/selection.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/nvram.h>
+#include <linux/vc_ioctl.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+
+#include "aty.h"
+#include "fbcon.h"
+#include "fbcon-cfb8.h"
+#include "fbcon-cfb16.h"
+#include "fbcon-cfb32.h"
+
+
+static int currcon = 0;
+static struct display fb_disp;
+static struct fb_info fb_info;
+static struct { u_char red, green, blue, pad; } palette[256];
+
+static char atyfb_name[16] = "ATY Mach64";
+
+struct atyfb_par {
+    int vmode;
+    int cmode;
+    u_int vxres;	/* virtual screen size */
+    u_int vyres;
+    int xoffset;	/* virtual screen position */
+    int yoffset;
+};
+
+
+/*
+ * Video mode values.
+ * These are supposed to be the same as the values that
+ * Apple uses in MacOS.
+ */
+#define VMODE_NVRAM		0	/* use value stored in nvram */
+#define VMODE_512_384_60I	1	/* 512x384, 60Hz interlaced (NTSC) */
+#define VMODE_512_384_60	2	/* 512x384, 60Hz */
+#define VMODE_640_480_50I	3	/* 640x480, 50Hz interlaced (PAL) */
+#define VMODE_640_480_60I	4	/* 640x480, 60Hz interlaced (NTSC) */
+#define VMODE_640_480_60	5	/* 640x480, 60Hz (VGA) */
+#define VMODE_640_480_67	6	/* 640x480, 67Hz */
+#define VMODE_640_870_75P	7	/* 640x870, 75Hz (portrait) */
+#define VMODE_768_576_50I	8	/* 768x576, 50Hz (PAL full frame) */
+#define VMODE_800_600_56	9	/* 800x600, 56Hz */
+#define VMODE_800_600_60	10	/* 800x600, 60Hz */
+#define VMODE_800_600_72	11	/* 800x600, 72Hz */
+#define VMODE_800_600_75	12	/* 800x600, 75Hz */
+#define VMODE_832_624_75	13	/* 832x624, 75Hz */
+#define VMODE_1024_768_60	14	/* 1024x768, 60Hz */
+#define VMODE_1024_768_70	15	/* 1024x768, 70Hz (or 72Hz?) */
+#define VMODE_1024_768_75V	16	/* 1024x768, 75Hz (VESA) */
+#define VMODE_1024_768_75	17	/* 1024x768, 75Hz */
+#define VMODE_1152_870_75	18	/* 1152x870, 75Hz */
+#define VMODE_1280_960_75	19	/* 1280x960, 75Hz */
+#define VMODE_1280_1024_75	20	/* 1280x1024, 75Hz */
+#define VMODE_MAX		20
+#define VMODE_CHOOSE		99	/* choose based on monitor sense */
+
+/*
+ * Color mode values, used to select number of bits/pixel.
+ */
+#define CMODE_NVRAM		-1	/* use value stored in nvram */
+#define CMODE_8			0	/* 8 bits/pixel */
+#define CMODE_16		1	/* 16 (actually 15) bits/pixel */
+#define CMODE_32		2	/* 32 (actually 24) bits/pixel */
+
+
+static int default_video_mode = VMODE_NVRAM;
+static int default_color_mode = CMODE_NVRAM;
+
+static struct atyfb_par default_par;
+static struct atyfb_par current_par;
+
+
+/*
+ * Addresses in NVRAM where video mode and pixel size are stored.
+ */
+#define NV_VMODE	0x140f
+#define NV_CMODE	0x1410
+
+/*
+ * Horizontal and vertical resolution information.
+ */
+extern struct vmode_attr {
+	int	hres;
+	int	vres;
+	int	vfreq;
+	int	interlaced;
+} vmode_attrs[VMODE_MAX];
+
+
+/*
+ * Horizontal and vertical resolution for each mode.
+ */
+static struct vmode_attr vmode_attrs[VMODE_MAX] = {
+    {512, 384, 60, 1},
+    {512, 384, 60},
+    {640, 480, 50, 1},
+    {640, 480, 60, 1},
+    {640, 480, 60},
+    {640, 480, 67},
+    {640, 870, 75},
+    {768, 576, 50, 1},
+    {800, 600, 56},
+    {800, 600, 60},
+    {800, 600, 72},
+    {800, 600, 75},
+    {832, 624, 75},
+    {1024, 768, 60},
+    {1024, 768, 72},
+    {1024, 768, 75},
+    {1024, 768, 75},
+    {1152, 870, 75},
+    {1280, 960, 75},
+    {1280, 1024, 75}
+};
+
+
+/*
+ * We get a sense value from the monitor and use it to choose
+ * what resolution to use.  This structure maps sense values
+ * to display mode values (which determine the resolution and
+ * frequencies).
+ */
+static struct mon_map {
+	int	sense;
+	int	vmode;
+} monitor_map [] = {
+	{0x000, VMODE_1280_1024_75},	/* 21" RGB */
+	{0x114, VMODE_640_870_75P},	/* Portrait Monochrome */
+	{0x221, VMODE_512_384_60},	/* 12" RGB*/
+	{0x331, VMODE_1280_1024_75},	/* 21" RGB (Radius) */
+	{0x334, VMODE_1280_1024_75},	/* 21" mono (Radius) */
+	{0x335, VMODE_1280_1024_75},	/* 21" mono */
+	{0x40A, VMODE_640_480_60I},	/* NTSC */
+	{0x51E, VMODE_640_870_75P},	/* Portrait RGB */
+	{0x603, VMODE_832_624_75},	/* 12"-16" multiscan */
+	{0x60b, VMODE_1024_768_70},	/* 13"-19" multiscan */
+	{0x623, VMODE_1152_870_75},	/* 13"-21" multiscan */
+	{0x62b, VMODE_640_480_67},	/* 13"/14" RGB */
+	{0x700, VMODE_640_480_50I},	/* PAL */
+	{0x714, VMODE_640_480_60I},	/* NTSC */
+	{0x717, VMODE_800_600_75},	/* VGA */
+	{0x72d, VMODE_832_624_75},	/* 16" RGB (Goldfish) */
+	{0x730, VMODE_768_576_50I},	/* PAL (Alternate) */
+	{0x73a, VMODE_1152_870_75},	/* 3rd party 19" */
+	{-1,	VMODE_640_480_60},	/* catch-all, must be last */
+};
+
+static int map_monitor_sense(int sense)
+{
+	struct mon_map *map;
+
+	for (map = monitor_map; map->sense >= 0; ++map)
+		if (map->sense == sense)
+			break;
+	return map->vmode;
+}
+
+struct aty_cmap_regs {
+    unsigned char windex;
+    unsigned char lut;
+    unsigned char mask;
+    unsigned char rindex;
+    unsigned char cntl;
+};
+
+typedef struct aty_regvals {
+    int offset[3];		/* first pixel address */
+
+    int crtc_h_sync_strt_wid[3];	/* depth dependant */
+    int crtc_gen_cntl[3];
+    int mem_cntl[3];
+
+    int crtc_h_tot_disp;	/* mode dependant */
+    int crtc_v_tot_disp;
+    int crtc_v_sync_strt_wid;
+    int crtc_off_pitch;
+
+    unsigned char clock_val[2];	/* vals for 20 and 21 */
+} aty_regvals;
+
+struct rage_regvals {
+    int h_total, h_sync_start, h_sync_width;
+    int v_total, v_sync_start, v_sync_width;
+    int h_sync_neg, v_sync_neg;
+};
+
+static int aty_vram_reqd(const struct atyfb_par *par);
+static struct aty_regvals *get_aty_struct(int vmode);
+
+static unsigned long frame_buffer;
+
+static int total_vram;		/* total amount of video memory, bytes */
+static int chip_type;		/* what chip type was detected */
+
+static unsigned long ati_regbase;
+static struct aty_cmap_regs *aty_cmap_regs;
+
+#include "ati-gx.h"
+#include "ati-gt.h"
+#include "ati-vt.h"
+
+static struct aty_regvals *aty_gt_reg_init[20] = {
+	NULL, NULL, NULL, NULL,
+	&aty_gt_reg_init_5,
+	&aty_gt_reg_init_6,
+	NULL, NULL,
+	&aty_gt_reg_init_9,
+	&aty_gt_reg_init_10,
+	&aty_gt_reg_init_11,
+	&aty_gt_reg_init_12,
+	&aty_gt_reg_init_13,
+	&aty_gt_reg_init_14,
+	&aty_gt_reg_init_15,
+	NULL,
+	&aty_gt_reg_init_17,
+	&aty_gt_reg_init_18,
+	NULL,
+	&aty_gt_reg_init_20
+};
+
+static struct aty_regvals *aty_gx_reg_init[20] = {
+	NULL, NULL, NULL, NULL,
+	&aty_gx_reg_init_6,
+	&aty_gx_reg_init_6,
+	NULL, NULL, NULL, NULL, NULL, NULL,
+	&aty_gx_reg_init_13,
+	&aty_gx_reg_init_14,
+	&aty_gx_reg_init_15,
+	NULL,
+	&aty_gx_reg_init_17,
+	&aty_gx_reg_init_18,
+	NULL,
+	&aty_gx_reg_init_20
+};
+
+static struct aty_regvals *aty_vt_reg_init[21] = {
+	NULL, NULL, NULL, NULL,
+	&aty_vt_reg_init_5,
+	&aty_vt_reg_init_6,
+	NULL, NULL, NULL,
+	&aty_vt_reg_init_10,
+	&aty_vt_reg_init_11,
+	&aty_vt_reg_init_12,
+	&aty_vt_reg_init_13,
+	&aty_vt_reg_init_14,
+	&aty_vt_reg_init_15,
+	NULL,
+	&aty_vt_reg_init_17,
+	&aty_vt_reg_init_18,
+	&aty_vt_reg_init_19,
+	&aty_vt_reg_init_20
+};
+
+    /*
+     *  Interface used by the world
+     */
+
+unsigned long atyfb_init(unsigned long mem_start);
+void atyfb_setup(char *options, int *ints);
+
+static int atyfb_open(struct fb_info *info);
+static int atyfb_release(struct fb_info *info);
+static int atyfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			 struct fb_info *info);
+static int atyfb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info);
+static int atyfb_set_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info);
+static int atyfb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info);
+static int atyfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info);
+static int atyfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info);
+static int atyfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg, int con, struct fb_info *info);
+
+
+    /*
+     *  Interface to the low level console driver
+     */
+
+static int atyfbcon_switch(int con, struct fb_info *info);
+static int atyfbcon_updatevar(int con, struct fb_info *info);
+static void atyfbcon_blank(int blank, struct fb_info *info);
+
+
+    /*
+     *  Text console acceleration
+     */
+
+#ifdef CONFIG_FBCON_CFB8
+static struct display_switch fbcon_aty8;
+#endif
+
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+extern struct vc_mode display_info;
+extern struct fb_info *console_fb_info;
+extern int (*console_setmode_ptr)(struct vc_mode *, int);
+extern int (*console_set_cmap_ptr)(struct fb_cmap *, int, int,
+				   struct fb_info *);
+static int atyfb_console_setmode(struct vc_mode *, int);
+#endif
+
+
+    /*
+     *  Internal routines
+     */
+
+static int atyfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+			 u_int *transp, struct fb_info *info);
+static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			 u_int transp, struct fb_info *info);
+static void do_install_cmap(int con, struct fb_info *info);
+
+
+static struct fb_ops atyfb_ops = {
+    atyfb_open, atyfb_release, atyfb_get_fix, atyfb_get_var, atyfb_set_var,
+    atyfb_get_cmap, atyfb_set_cmap, atyfb_pan_display, NULL, atyfb_ioctl
+};
+
+
+static inline int aty_vram_reqd(const struct atyfb_par *par)
+{
+    return (par->vxres*par->vyres) << par->cmode;
+}
+
+extern inline unsigned aty_ld_le32(volatile unsigned long addr)
+{
+    register unsigned long temp = ati_regbase,val;
+
+    asm("lwbrx %0,%1,%2": "=r"(val):"r"(addr), "r"(temp));
+    return val;
+}
+
+extern inline void aty_st_le32(volatile unsigned long addr, unsigned val)
+{
+    register unsigned long temp = ati_regbase;
+
+    asm("stwbrx %0,%1,%2": : "r"(val), "r"(addr), "r"(temp):"memory");
+}
+
+extern inline unsigned char aty_ld_8(volatile unsigned long addr)
+{
+    return *(char *) ((long) addr + (long) ati_regbase);
+}
+
+extern inline void aty_st_8(volatile unsigned long addr, unsigned char val)
+{
+    *(unsigned char *) (addr + (unsigned long) ati_regbase) = val;
+}
+
+static void aty_st_514(int offset, char val)
+{
+    aty_WaitQueue(5);
+    aty_st_8(DAC_CNTL, 1);
+    aty_st_8(DAC_W_INDEX, offset & 0xff);	/* right addr byte */
+    aty_st_8(DAC_DATA, (offset >> 8) & 0xff);	/* left addr byte */
+    eieio();
+    aty_st_8(DAC_MASK, val);
+    eieio();
+    aty_st_8(DAC_CNTL, 0);
+}
+
+static void aty_st_pll(int offset, char val)
+{
+    aty_WaitQueue(3);
+    aty_st_8(CLOCK_CNTL + 1, (offset << 2) | PLL_WR_EN);	/* write addr byte */
+    eieio();
+    aty_st_8(CLOCK_CNTL + 2, val);	/* write the register value */
+    eieio();
+    aty_st_8(CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN);
+}
+
+static struct aty_regvals *get_aty_struct(int vmode)
+{
+    int v = vmode - 1;
+
+    switch (chip_type) {
+	case MACH64_GT_ID:
+	    return aty_gt_reg_init[v];
+	    break;
+	case MACH64_VT_ID:
+	    return aty_vt_reg_init[v];
+	    break;
+	default: /* default to MACH64_GX_ID */
+	    return aty_gx_reg_init[v];
+	    break;
+    }
+}
+
+static int read_aty_sense(void)
+{
+    int sense, i;
+
+    aty_st_le32(GP_IO, 0x31003100);	/* drive outputs high */
+    __delay(200);
+    aty_st_le32(GP_IO, 0);		/* turn off outputs */
+    __delay(2000);
+    i = aty_ld_le32(GP_IO);		/* get primary sense value */
+    sense = ((i & 0x3000) >> 3) | (i & 0x100);
+
+    /* drive each sense line low in turn and collect the other 2 */
+    aty_st_le32(GP_IO, 0x20000000);	/* drive A low */
+    __delay(2000);
+    i = aty_ld_le32(GP_IO);
+    sense |= ((i & 0x1000) >> 7) | ((i & 0x100) >> 4);
+    aty_st_le32(GP_IO, 0x20002000);	/* drive A high again */
+    __delay(200);
+
+    aty_st_le32(GP_IO, 0x10000000);	/* drive B low */
+    __delay(2000);
+    i = aty_ld_le32(GP_IO);
+    sense |= ((i & 0x2000) >> 10) | ((i & 0x100) >> 6);
+    aty_st_le32(GP_IO, 0x10001000);	/* drive B high again */
+    __delay(200);
+
+    aty_st_le32(GP_IO, 0x01000000);	/* drive C low */
+    __delay(2000);
+    sense |= (aty_ld_le32(GP_IO) & 0x3000) >> 12;
+    aty_st_le32(GP_IO, 0);		/* turn off outputs */
+
+    return sense;
+}
+
+static void RGB514_Program(int cmode)
+{
+    typedef struct {
+	char pixel_dly;
+	char misc2_cntl;
+	char pixel_rep;
+	char pixel_cntl_index;
+	char pixel_cntl_v1;
+    } RGB514_DAC_Table;
+
+    static RGB514_DAC_Table RGB514DAC_Tab[8] = {
+	{0, 0x41, 0x03, 0x71, 0x45},	// 8bpp
+	{0, 0x45, 0x04, 0x0c, 0x01},	// 555
+	{0, 0x45, 0x06, 0x0e, 0x00},	// XRGB
+    };
+    RGB514_DAC_Table *pDacProgTab;
+
+    pDacProgTab = &RGB514DAC_Tab[cmode];
+
+    aty_st_514(0x90, 0x00);
+    aty_st_514(0x04, pDacProgTab->pixel_dly);
+    aty_st_514(0x05, 0x00);
+
+    aty_st_514(0x2, 0x1);
+    aty_st_514(0x71, pDacProgTab->misc2_cntl);
+    aty_st_514(0x0a, pDacProgTab->pixel_rep);
+
+    aty_st_514(pDacProgTab->pixel_cntl_index, pDacProgTab->pixel_cntl_v1);
+}
+
+static void set_off_pitch(const struct atyfb_par *par)
+{
+    u32 pitch, offset;
+
+    pitch = par->vxres>>3;
+    offset = ((par->yoffset*par->vxres+par->xoffset)>>3)<<par->cmode;
+    aty_st_le32(CRTC_OFF_PITCH, pitch<<22 | offset);
+    if (chip_type == MACH64_GT_ID) {
+	/* Is this OK for other chips? */
+	aty_st_le32(DST_OFF_PITCH, pitch<<22 | offset);
+	aty_st_le32(SRC_OFF_PITCH, pitch<<22 | offset);
+    }
+}
+
+static void atyfb_set_par(struct atyfb_par *par)
+{
+    int i, hres;
+    struct aty_regvals *init = get_aty_struct(par->vmode);
+    int vram_type = aty_ld_le32(CONFIG_STAT0) & 7;
+
+    if (init == 0)	/* paranoia, shouldn't get here */
+	panic("aty: display mode %d not supported", par->vmode);
+
+    current_par = *par;
+    hres = vmode_attrs[par->vmode-1].hres;
+
+    /* clear FIFO errors */
+    aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL) | BUS_HOST_ERR_ACK
+			  | BUS_FIFO_ERR_ACK);
+
+    /* Reset engine */
+    i = aty_ld_le32(GEN_TEST_CNTL);
+    aty_st_le32(GEN_TEST_CNTL, i & ~GUI_ENGINE_ENABLE);
+    eieio();
+    aty_WaitIdleEmpty();
+    aty_st_le32(GEN_TEST_CNTL, i | GUI_ENGINE_ENABLE);
+    aty_WaitIdleEmpty();
+
+    if ( chip_type != MACH64_GT_ID ) {
+	i = aty_ld_le32(CRTC_GEN_CNTL);
+	aty_st_le32(CRTC_GEN_CNTL, i | CRTC_EXT_DISP_EN);
+    }
+
+    if ( chip_type == MACH64_GX_ID ) {
+	i = aty_ld_le32(GEN_TEST_CNTL);
+	aty_st_le32(GEN_TEST_CNTL, i | GEN_OVR_OUTPUT_EN );
+    }
+
+    switch (chip_type) {
+	case MACH64_VT_ID:
+	    aty_st_pll(PLL_MACRO_CNTL, 0xb5);
+	    aty_st_pll(PLL_REF_DIV, 0x2d);
+	    aty_st_pll(PLL_GEN_CNTL, 0x14);
+	    aty_st_pll(MCLK_FB_DIV, 0xbd);
+	    aty_st_pll(PLL_VCLK_CNTL, 0x0b);
+	    aty_st_pll(VCLK_POST_DIV, init->clock_val[0]);
+	    aty_st_pll(VCLK0_FB_DIV, init->clock_val[1]);
+	    aty_st_pll(VCLK1_FB_DIV, 0xd6);
+	    aty_st_pll(VCLK2_FB_DIV, 0xee);
+	    aty_st_pll(VCLK3_FB_DIV, 0xf8);
+	    aty_st_pll(PLL_XCLK_CNTL, 0x0);
+	    aty_st_pll(PLL_TEST_CTRL, 0x0);
+	    aty_st_pll(PLL_TEST_COUNT, 0x0);
+	    break;
+	case MACH64_GT_ID:
+	    if (vram_type == 5) {
+		aty_st_pll(0, 0xcd);
+		aty_st_pll(PLL_MACRO_CNTL,
+			   par->vmode >= VMODE_1024_768_60 ? 0xd3: 0xd5);
+		aty_st_pll(PLL_REF_DIV, 0x21);
+		aty_st_pll(PLL_GEN_CNTL, 0x44);
+		aty_st_pll(MCLK_FB_DIV, 0xe8);
+		aty_st_pll(PLL_VCLK_CNTL, 0x03);
+		aty_st_pll(VCLK_POST_DIV, init->offset[0]);
+		aty_st_pll(VCLK0_FB_DIV, init->offset[1]);
+		aty_st_pll(VCLK1_FB_DIV, 0x8e);
+		aty_st_pll(VCLK2_FB_DIV, 0x9e);
+		aty_st_pll(VCLK3_FB_DIV, 0xc6);
+		aty_st_pll(PLL_XCLK_CNTL, init->offset[2]);
+		aty_st_pll(12, 0xa6);
+		aty_st_pll(13, 0x1b);
+	    } else {
+		aty_st_pll(PLL_MACRO_CNTL, 0xd5);
+		aty_st_pll(PLL_REF_DIV, 0x21);
+		aty_st_pll(PLL_GEN_CNTL, 0xc4);
+		aty_st_pll(MCLK_FB_DIV, 0xda);
+		aty_st_pll(PLL_VCLK_CNTL, 0x03);
+		/* offset actually holds clock values */
+		aty_st_pll(VCLK_POST_DIV, init->offset[0]);
+		aty_st_pll(VCLK0_FB_DIV, init->offset[1]);
+		aty_st_pll(VCLK1_FB_DIV, 0x8e);
+		aty_st_pll(VCLK2_FB_DIV, 0x9e);
+		aty_st_pll(VCLK3_FB_DIV, 0xc6);
+		aty_st_pll(PLL_TEST_CTRL, 0x0);
+		aty_st_pll(PLL_XCLK_CNTL, init->offset[2]);
+		aty_st_pll(12, 0xa0);
+		aty_st_pll(13, 0x1b);
+	    }
+	    break;
+	default:
+	    RGB514_Program(par->cmode);
+	    aty_WaitIdleEmpty();
+	    aty_st_514(0x06, 0x02);
+	    aty_st_514(0x10, 0x01);
+	    aty_st_514(0x70, 0x01);
+	    aty_st_514(0x8f, 0x1f);
+	    aty_st_514(0x03, 0x00);
+	    aty_st_514(0x05, 0x00);
+	    aty_st_514(0x20, init->clock_val[0]);
+	    aty_st_514(0x21, init->clock_val[1]);
+	    break;
+    }
+
+    aty_ld_8(DAC_REGS);	/* clear counter */
+    aty_WaitIdleEmpty();
+
+    aty_st_le32(CRTC_H_TOTAL_DISP, init->crtc_h_tot_disp);
+    aty_st_le32(CRTC_H_SYNC_STRT_WID, init->crtc_h_sync_strt_wid[par->cmode]);
+    aty_st_le32(CRTC_V_TOTAL_DISP, init->crtc_v_tot_disp);
+    aty_st_le32(CRTC_V_SYNC_STRT_WID, init->crtc_v_sync_strt_wid);
+
+    aty_st_8(CLOCK_CNTL, 0);
+    aty_st_8(CLOCK_CNTL, CLOCK_STROBE);
+
+    aty_st_le32(CRTC_VLINE_CRNT_VLINE, 0);
+
+    set_off_pitch(par);
+
+    if (chip_type == MACH64_GT_ID) {
+	aty_st_le32(BUS_CNTL, 0x7b23a040);
+
+	/* need to set DSP values !! assume sdram */
+	i = init->crtc_gen_cntl[0] - (0x100000 * par->cmode);
+	if ( vram_type == 5 )
+	    i = init->crtc_gen_cntl[1] - (0x100000 * par->cmode);
+	aty_st_le32(DSP_CONFIG, i);
+
+	i = aty_ld_le32(MEM_CNTL) & MEM_SIZE_ALIAS;
+	if ( vram_type == 5 ) {
+	    i |= ((1 * par->cmode) << 26) | 0x4215b0;
+	    aty_st_le32(DSP_ON_OFF,sgram_dsp[par->vmode-1][par->cmode]);
+
+	//aty_st_le32(CLOCK_CNTL,8192);
+	} else {
+	    i |= ((1 * par->cmode) << 26) | 0x300090;
+	    aty_st_le32(DSP_ON_OFF, init->mem_cntl[par->cmode]);
+	}
+
+	aty_st_le32(MEM_CNTL, i);
+	aty_st_le32(EXT_MEM_CNTL, 0x5000001);
+
+	/* if (total_vram > 0x400000)	
+	    i |= 0x538; this not been verified on > 4Megs!! */
+    } else {
+
+/* The magic constant below translates into:
+* 5   = No RDY delay, 1 wait st for mem write, increment during burst transfer
+* 9   = DAC access delayed, 1 wait state for DAC
+* 0   = Disables interupts for FIFO errors
+* e   = Allows FIFO to generate 14 wait states before generating error
+* 1   = DAC snooping disabled, ROM disabled
+* 0   = ROM page at 0 (disabled so doesn't matter)
+* f   = 15 ROM wait states (disabled so doesn't matter)
+* f   = 15 BUS wait states (I'm not sure this applies to PCI bus types)
+* at some point it would be good to experiment with bench marks to see if
+* we can gain some speed by fooling with the wait states etc.
+*/
+	if (chip_type == MACH64_VT_ID)
+	    aty_st_le32(BUS_CNTL, 0x680000f9);
+	else
+	    aty_st_le32(BUS_CNTL, 0x590e10ff);
+
+	switch (total_vram) {
+	    case 0x00100000:
+		aty_st_le32(MEM_CNTL, vt_mem_cntl[0][par->cmode]);
+		break;
+	    case 0x00200000:
+		aty_st_le32(MEM_CNTL, vt_mem_cntl[1][par->cmode]);
+		break;
+	    case 0x00400000:
+		aty_st_le32(MEM_CNTL, vt_mem_cntl[2][par->cmode]);
+		break;
+	    default:
+		i = aty_ld_le32(MEM_CNTL) & 0x000F;
+		aty_st_le32(MEM_CNTL,
+			    (init->mem_cntl[par->cmode] & 0xFFFFFFF0) | i);
+	}
+    }
+/* These magic constants are harder to figure out
+* on the vt chipset bit 2 set makes the screen brighter
+* and bit 15 makes the screen black! But nothing else
+* seems to matter for the vt DAC_CNTL
+*/
+    switch (chip_type) {
+	case MACH64_GT_ID:
+	    i = 0x86010102;
+	    break;
+	case MACH64_VT_ID:
+	    i = 0x87010184;
+	    break;
+	default:
+	    i = 0x47012100;
+	    break;
+    }
+
+    aty_st_le32(DAC_CNTL, i);
+    aty_st_8(DAC_MASK, 0xff);
+
+    switch (par->cmode) {
+	case CMODE_16:
+	    i = CRTC_PIX_WIDTH_15BPP; break;
+	/*case CMODE_24: */
+	case CMODE_32:
+	    i = CRTC_PIX_WIDTH_32BPP; break;
+	case CMODE_8:
+	default:
+	    i = CRTC_PIX_WIDTH_8BPP; break;
+    }
+
+    if (chip_type != MACH64_GT_ID) {
+	aty_st_le32(CRTC_INT_CNTL, 0x00000002);
+	aty_st_le32(GEN_TEST_CNTL, GUI_ENGINE_ENABLE | BLOCK_WRITE_ENABLE);	/* gui_en block_en */
+	i |= init->crtc_gen_cntl[par->cmode];
+    }
+    /* Gentlemen, start your crtc engine */
+    aty_st_le32(CRTC_GEN_CNTL, CRTC_EXT_DISP_EN | CRTC_ENABLE | i);
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+    display_info.height = vmode_attrs[par->vmode-1].vres;
+    display_info.width = vmode_attrs[par->vmode-1].hres;
+    display_info.depth = 8<<par->cmode;
+    display_info.pitch = par->vxres<<par->cmode;
+    display_info.mode = par->vmode;
+    strcpy(display_info.name, atyfb_name);
+    display_info.fb_address =
+	iopa(((chip_type != MACH64_GT_ID) ?
+	     frame_buffer + init->offset[par->cmode] : frame_buffer));
+    display_info.cmap_adr_address = iopa((unsigned long)&aty_cmap_regs->windex);
+    display_info.cmap_data_address = iopa((unsigned long)&aty_cmap_regs->lut);
+    display_info.disp_reg_address = iopa(ati_regbase);
+#endif /* CONFIG_FB_COMPAT_XPMAC) */
+}
+
+
+    /*
+     *  Open/Release the frame buffer device
+     */
+
+static int atyfb_open(struct fb_info *info)
+
+{
+    /*
+     *  Nothing, only a usage count for the moment
+     */
+
+    MOD_INC_USE_COUNT;
+    return(0);
+}
+
+static int atyfb_release(struct fb_info *info)
+{
+    MOD_DEC_USE_COUNT;
+    return(0);
+}
+
+
+static int encode_fix(struct fb_fix_screeninfo *fix,
+		      const struct atyfb_par *par)
+{
+    struct aty_regvals *init;
+
+    memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+
+    strcpy(fix->id, atyfb_name);
+    init = get_aty_struct(par->vmode);
+    /*
+     *  FIXME: This will cause problems on non-GT chips, because the frame
+     *  buffer must be aligned to a page
+     */
+    fix->smem_start = (char *)((chip_type != MACH64_GT_ID)
+	    ? frame_buffer + init->offset[par->cmode] : frame_buffer);
+    fix->smem_len = (u32)total_vram;
+    if (fix->smem_len > 0x7ff000)
+	fix->smem_len = 0x7ff000;	/* last page is MMIO */
+    fix->mmio_start = (char *)(ati_regbase & ~0xfff);
+    fix->mmio_len = 4096;
+    fix->type = FB_TYPE_PACKED_PIXELS;
+    fix->type_aux = 0;
+    fix->line_length = par->vxres<<par->cmode;
+    fix->visual = par->cmode == CMODE_8 ? FB_VISUAL_PSEUDOCOLOR
+					: FB_VISUAL_TRUECOLOR;
+    fix->ywrapstep = 0;
+    fix->xpanstep = 8;
+    fix->ypanstep = 1;
+
+    return 0;
+}
+
+
+static int decode_var(struct fb_var_screeninfo *var,
+		      struct atyfb_par *par)
+{
+    int xres = var->xres;
+    int yres = var->yres;
+    int bpp = var->bits_per_pixel;
+    struct aty_regvals *init;
+
+    /* This should support more video modes */
+
+    if (xres <= 512 && yres <= 384)
+	par->vmode = VMODE_512_384_60;		/* 512x384, 60Hz */
+    else if (xres <= 640 && yres <= 480)
+	par->vmode = VMODE_640_480_67;		/* 640x480, 67Hz */
+    else if (xres <= 640 && yres <= 870)
+	par->vmode = VMODE_640_870_75P;		/* 640x870, 75Hz (portrait) */
+    else if (xres <= 768 && yres <= 576)
+	par->vmode = VMODE_768_576_50I;		/* 768x576, 50Hz (PAL full frame) */
+    else if (xres <= 800 && yres <= 600)
+	par->vmode = VMODE_800_600_75;		/* 800x600, 75Hz */
+    else if (xres <= 832 && yres <= 624)
+	par->vmode = VMODE_832_624_75;		/* 832x624, 75Hz */
+    else if (xres <= 1024 && yres <= 768)
+	par->vmode = VMODE_1024_768_75;		/* 1024x768, 75Hz */
+    else if (xres <= 1152 && yres <= 870)
+	par->vmode = VMODE_1152_870_75;		/* 1152x870, 75Hz */
+    else if (xres <= 1280 && yres <= 960)
+	par->vmode = VMODE_1280_960_75;		/* 1280x960, 75Hz */
+    else if (xres <= 1280 && yres <= 1024)
+	par->vmode = VMODE_1280_1024_75;	/* 1280x1024, 75Hz */
+    else
+	return -EINVAL;
+
+    xres = vmode_attrs[par->vmode-1].hres;
+    yres = vmode_attrs[par->vmode-1].vres;
+
+    if (var->xres_virtual <= xres)
+	par->vxres = xres;
+    else
+	par->vxres = (var->xres_virtual+7) & ~7;
+    if (var->yres_virtual <= yres)
+	par->vyres = yres;
+    else
+	par->vyres = var->yres_virtual;
+
+    par->xoffset = (var->xoffset+7) & ~7;
+    par->yoffset = var->yoffset;
+    if (par->xoffset+xres > par->vxres || par->yoffset+yres > par->vyres)
+	return -EINVAL;
+
+    if (bpp <= 8)
+	par->cmode = CMODE_8;
+    else if (bpp <= 16)
+	par->cmode = CMODE_16;
+    else if (bpp <= 32)
+	par->cmode = CMODE_32;
+    else
+	return -EINVAL;
+
+    if (aty_vram_reqd(par) > total_vram)
+	return -EINVAL;
+
+    /* Check if we know about the wanted video mode */
+    init = get_aty_struct(par->vmode);
+    if (init == NULL || init->crtc_h_sync_strt_wid[par->cmode] == 0 ||
+	(chip_type != MACH64_GT_ID &&
+	 init->crtc_gen_cntl[par->cmode] == 0) ||
+	(chip_type == MACH64_GT_ID && (aty_ld_le32(CONFIG_STAT0) & 7) == 5 &&
+	 init->crtc_gen_cntl[1] == 0))
+	return -EINVAL;
+
+#if 0
+    if (!fbmon_valid_timings(pixclock, htotal, vtotal, info))
+	return -EINVAL;
+#endif
+
+    return 0;
+}
+
+static int encode_var(struct fb_var_screeninfo *var,
+		      const struct atyfb_par *par)
+{
+    memset(var, 0, sizeof(struct fb_var_screeninfo));
+
+    var->xres = vmode_attrs[par->vmode-1].hres;
+    var->yres = vmode_attrs[par->vmode-1].vres;
+    var->xres_virtual = par->vxres;
+    var->yres_virtual = par->vyres;
+    var->xoffset = par->xoffset;
+    var->yoffset = par->yoffset;
+    var->grayscale = 0;
+    switch (par->cmode) {
+	case CMODE_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 CMODE_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 CMODE_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 = 0;
+    var->green.msb_right = 0;
+    var->blue.msb_right = 0;
+    var->transp.msb_right = 0;
+    var->nonstd = 0;
+    var->activate = 0;
+    var->height = -1;
+    var->width = -1;
+    var->accel = /* FB_ACCEL_ATY */ 0;
+    var->vmode = FB_VMODE_NONINTERLACED;
+    var->left_margin = var->right_margin = 64;	/* guesses */
+    var->upper_margin = var->lower_margin = 32;
+    var->hsync_len = 64;
+    var->vsync_len = 2;
+
+    /* no long long support in the kernel :-( */
+    /* this splittig trick will work if xres > 232 */
+    var->pixclock = 1000000000/
+	(var->left_margin+var->xres+var->right_margin+var->hsync_len);
+    var->pixclock *= 1000;
+    var->pixclock /= vmode_attrs[par->vmode-1].vfreq*
+	 (var->upper_margin+var->yres+var->lower_margin+var->vsync_len);
+    var->sync = 0;
+
+    return 0;
+}
+
+
+static void init_par(struct atyfb_par *par, int vmode, int cmode)
+{
+    par->vmode = vmode;
+    par->cmode = cmode;
+    par->vxres = vmode_attrs[vmode-1].hres;
+    par->vyres = vmode_attrs[vmode-1].vres;
+    par->xoffset = 0;
+    par->yoffset = 0;
+}
+
+
+    /*
+     *  Get the Fixed Part of the Display
+     */
+
+static int atyfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			 struct fb_info *info)
+{
+    struct atyfb_par par;
+
+    if (con == -1)
+	par = default_par;
+    else
+	decode_var(&fb_display[con].var, &par);
+    encode_fix(fix, &par);
+    return 0;
+}
+
+
+    /*
+     *  Get the User Defined Part of the Display
+     */
+
+static int atyfb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info)
+{
+    if (con == -1)
+	encode_var(var, &default_par);
+    else
+	*var=fb_display[con].var;
+    return 0;
+}
+
+
+    /*
+     *  Set the User Defined Part of the Display
+     */
+
+static int atyfb_set_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info)
+{
+    struct atyfb_par par;
+    struct display *display;
+    int oldxres, oldyres, oldvxres, oldvyres, oldbpp;
+    int err;
+    int activate = var->activate;
+
+    if (con >= 0)
+	display = &fb_display[con];
+    else
+	display = &fb_disp;	/* used during initialization */
+
+    if ((err = decode_var(var, &par)))
+	return err;
+
+    encode_var(var, &par);
+
+    if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+	oldxres = display->var.xres;
+	oldyres = display->var.yres;
+	oldvxres = display->var.xres_virtual;
+	oldvyres = display->var.yres_virtual;
+	oldbpp = display->var.bits_per_pixel;
+	display->var = *var;
+	if (oldxres != var->xres || oldyres != var->yres ||
+	    oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
+	    oldbpp != var->bits_per_pixel) {
+	    struct fb_fix_screeninfo fix;
+
+	    encode_fix(&fix, &par);
+	    display->screen_base = (u_char *)fix.smem_start;
+	    display->visual = fix.visual;
+	    display->type = fix.type;
+	    display->type_aux = fix.type_aux;
+	    display->ypanstep = fix.ypanstep;
+	    display->ywrapstep = fix.ywrapstep;
+	    display->line_length = fix.line_length;
+	    display->can_soft_blank = 1;
+	    display->inverse = 0;
+	    switch (par.cmode) {
+		case CMODE_8:
+#if 1
+		    display->dispsw = &fbcon_cfb8;
+#else
+		    display->dispsw = &fbcon_aty8;
+#endif
+		    break;
+		case CMODE_16:
+		    display->dispsw = &fbcon_cfb16;
+		    break;
+		case CMODE_32:
+		    display->dispsw = &fbcon_cfb32;
+		    break;
+		default:
+		    display->dispsw = NULL;
+		    break;
+	    }
+	    if (fb_info.changevar)
+		(*fb_info.changevar)(con);
+	}
+	if (con == currcon)
+	    atyfb_set_par(&par);
+	if (oldbpp != var->bits_per_pixel) {
+	    if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
+		return err;
+	    do_install_cmap(con, info);
+	}
+    }
+
+    return 0;
+}
+
+
+    /*
+     *  Pan or Wrap the Display
+     *
+     *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+     */
+
+static int atyfb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info)
+{
+    u32 xres, yres, xoffset, yoffset;
+    struct atyfb_par *par = &current_par;
+
+    xres = vmode_attrs[par->vmode-1].hres;
+    yres = vmode_attrs[par->vmode-1].vres;
+    xoffset = (var->xoffset+7) & ~7;
+    yoffset = var->yoffset;
+    if (xoffset+xres > par->vxres || yoffset+yres > par->vyres)
+	return -EINVAL;
+    par->xoffset = xoffset;
+    par->yoffset = yoffset;
+    set_off_pitch(par);
+    return 0;
+}
+
+    /*
+     *  Get the Colormap
+     */
+
+static int atyfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info)
+{
+    if (con == currcon) /* current console? */
+	return fb_get_cmap(cmap, &fb_display[con].var, kspc, atyfb_getcolreg,
+			   info);
+    else if (fb_display[con].cmap.len) /* non default colormap? */
+	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+    else
+	fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+		     cmap, kspc ? 0 : 2);
+    return 0;
+}
+
+    /*
+     *  Set the Colormap
+     */
+
+static int atyfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info)
+{
+    int err;
+
+    if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
+	if ((err = fb_alloc_cmap(&fb_display[con].cmap,
+				 1<<fb_display[con].var.bits_per_pixel, 0)))
+	    return err;
+    }
+    if (con == currcon)			/* current console? */
+	return fb_set_cmap(cmap, &fb_display[con].var, kspc, atyfb_setcolreg,
+			   info);
+    else
+	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+    return 0;
+}
+
+
+static int atyfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg, int con, struct fb_info *info)
+{
+    return -EINVAL;
+}
+
+
+    /*
+     *  Initialisation
+     */
+
+__initfunc(unsigned long atyfb_init(unsigned long mem_start))
+{
+#ifdef __powerpc__
+    /* We don't want to be called like this. */
+    /* We rely on Open Firmware (offb) instead. */
+    return mem_start;
+#else /* !__powerpc__ */
+    /* To be merged with Bernd's mach64fb */
+    return mem_start;
+#endif /* !__powerpc__ */
+}
+
+
+unsigned long atyfb_of_init(unsigned long mem_start, struct device_node *dp)
+{
+    int i, err, sense;
+    struct fb_var_screeninfo var;
+    struct aty_regvals *init;
+    unsigned long addr;
+    unsigned char bus, devfn;
+    unsigned short cmd;
+
+    if (dp->next)
+	printk("Warning: only using first ATI card detected\n");
+    if (dp->n_addrs != 1 && dp->n_addrs != 3)
+	printk("Warning: expecting 1 or 3 addresses for ATY (got %d)",
+	       dp->n_addrs);
+
+    ati_regbase = (int)ioremap((0x7ffc00 + dp->addrs[0].address), 0x1000);
+    aty_cmap_regs = (struct aty_cmap_regs *)(ati_regbase + 0xC0);
+
+    /* enable memory-space accesses using config-space command register */
+    if (pci_device_loc(dp, &bus, &devfn) == 0) {
+	pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
+	if (cmd != 0xffff) {
+	    cmd |= PCI_COMMAND_MEMORY;
+	    pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
+	}
+    }
+    chip_type = (aty_ld_le32(CONFIG_CHIP_ID) & CFG_CHIP_TYPE);
+
+    i = aty_ld_le32(MEM_CNTL);
+    if (chip_type != MACH64_GT_ID)
+	switch (i & MEM_SIZE_ALIAS) {
+	    case MEM_SIZE_512K:
+		total_vram = 0x80000;
+		break;
+	    case MEM_SIZE_1M:
+		total_vram = 0x100000;
+		break;
+	    case MEM_SIZE_2M:
+		total_vram = 0x200000;
+		break;
+	    case MEM_SIZE_4M:
+		total_vram = 0x400000;
+		break;
+	    case MEM_SIZE_6M:
+		total_vram = 0x600000;
+		break;
+	    case MEM_SIZE_8M:
+		total_vram = 0x800000;
+		break;
+	    default:
+		total_vram = 0x80000;
+	}
+    else
+	switch (i & 0xF) {	/* 0xF used instead of MEM_SIZE_ALIAS */
+	    case MEM_SIZE_512K:
+		total_vram = 0x80000;
+		break;
+	    case MEM_SIZE_1M:
+		total_vram = 0x100000;
+		break;
+	    case MEM_SIZE_2M_GTB:
+		total_vram = 0x200000;
+		break;
+	    case MEM_SIZE_4M_GTB:
+		total_vram = 0x400000;
+		break;
+	    case MEM_SIZE_6M_GTB:
+		total_vram = 0x600000;
+		break;
+	    case MEM_SIZE_8M_GTB:
+		total_vram = 0x800000;
+		break;
+	    default:
+		total_vram = 0x80000;
+	}
+
+#if 1
+    printk("aty_display_init: node = %p, addrs = ", dp->node);
+    printk(" %x(%x)", dp->addrs[0].address, dp->addrs[0].size);
+    printk(", intrs =");
+    for (i = 0; i < dp->n_intrs; ++i)
+    printk(" %x", dp->intrs[i].line);
+    printk("\nregbase: %x pci loc: %x:%x total_vram: %x cregs: %p\n",
+	   (int)ati_regbase, bus, devfn, total_vram, aty_cmap_regs);
+#endif
+
+    /* Map in frame buffer */
+    addr = dp->addrs[0].address;
+
+    /* use the big-endian aperture (??) */
+    addr += 0x800000;
+    frame_buffer = (unsigned long)__ioremap(addr, 0x800000, _PAGE_WRITETHRU);
+
+    if (default_video_mode != -1) {
+	sense = read_aty_sense();
+	printk("monitor sense = %x\n", sense);
+	if (default_video_mode == VMODE_NVRAM) {
+	    default_video_mode = nvram_read_byte(NV_VMODE);
+	    init = get_aty_struct(default_video_mode);
+	    if (default_video_mode <= 0 ||
+		default_video_mode > VMODE_MAX || init == 0)
+		default_video_mode = VMODE_CHOOSE;
+	}
+	if (default_video_mode == VMODE_CHOOSE)
+	    default_video_mode = map_monitor_sense(sense);
+
+	init = get_aty_struct(default_video_mode);
+	if (!init)
+	    default_video_mode = VMODE_640_480_60;
+    }
+
+    /*
+     * Reduce the pixel size if we don't have enough VRAM.
+     */
+
+    if (default_color_mode == CMODE_NVRAM)
+	default_color_mode = nvram_read_byte(NV_CMODE);
+    if (default_color_mode < CMODE_8 ||
+	default_color_mode > CMODE_32)
+	default_color_mode = CMODE_8;
+
+    init_par(&default_par, default_video_mode, default_color_mode);
+    while (aty_vram_reqd(&default_par) > total_vram) {
+	while (default_color_mode > CMODE_8 &&
+	       aty_vram_reqd(&default_par) > total_vram) {
+	    --default_color_mode;
+	    init_par(&default_par, default_video_mode, default_color_mode);
+	}
+	/*
+	 * adjust the video mode smaller if there still is not enough VRAM
+	 */
+	if (aty_vram_reqd(&default_par) > total_vram)
+	    do {
+		default_video_mode--;
+		init_par(&default_par, default_video_mode, default_color_mode);
+		init = get_aty_struct(default_video_mode);
+	    } while ((init == 0) &&
+		     (default_video_mode > VMODE_640_480_60));
+    }
+
+    if (chip_type == MACH64_GT_ID && (aty_ld_le32(CONFIG_STAT0) & 7) == 5
+	&& init->crtc_gen_cntl[1] == 0) {
+	    default_video_mode = VMODE_640_480_67;
+	    default_color_mode = CMODE_8;
+	    init_par(&default_par, default_video_mode, default_color_mode);
+    }
+
+    switch (chip_type) {
+	case MACH64_GX_ID:
+	    strcat(atyfb_name, "GX");
+	    break;
+	case MACH64_VT_ID:
+	    strcat(atyfb_name, "VT");
+	    break;
+	case MACH64_GT_ID:
+	    strcat(atyfb_name, "GT");
+	    break;
+	default:
+	    break;
+    }
+    strcpy(fb_info.modename, atyfb_name);
+    fb_info.node = -1;
+    fb_info.fbops = &atyfb_ops;
+    fb_info.disp = &fb_disp;
+    fb_info.fontname[0] = '\0';
+    fb_info.changevar = NULL;
+    fb_info.switch_con = &atyfbcon_switch;
+    fb_info.updatevar = &atyfbcon_updatevar;
+    fb_info.blank = &atyfbcon_blank;
+
+    err = register_framebuffer(&fb_info);
+    if (err < 0)
+	return mem_start;
+
+    for (i = 0; i < 16; i++) {
+	int j = color_table[i];
+	palette[i].red = default_red[j];
+	palette[i].green = default_grn[j];
+	palette[i].blue = default_blu[j];
+    }
+    atyfb_set_par(&default_par);
+    encode_var(&var, &default_par);
+    atyfb_set_var(&var, -1, &fb_info);
+
+    printk("fb%d: %s frame buffer device on %s\n", GET_FB_IDX(fb_info.node),
+	   atyfb_name, dp->full_name);
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+    if (!console_fb_info) {
+	console_fb_info = &fb_info;
+	console_setmode_ptr = atyfb_console_setmode;
+	console_set_cmap_ptr = atyfb_set_cmap;
+    }
+#endif /* CONFIG_FB_COMPAT_XPMAC) */
+
+    return mem_start;
+}
+
+
+/* XXX: doesn't work yet */
+void atyfb_setup(char *options, int *ints)
+{
+    char *this_opt;
+    int vmode;
+    int depth;
+
+    if (!options || !*options)
+	return;
+
+    for (this_opt = strtok(options, ","); this_opt;
+	 this_opt = strtok(NULL, ",")) {
+	if (!strncmp(this_opt, "vmode:", 6)) {
+	    vmode = simple_strtoul(this_opt+6, NULL, 0);
+	    if (vmode > 0 && vmode <= VMODE_MAX)
+		default_video_mode = vmode;
+	} else if (!strncmp(this_opt, "cmode:", 6)) {
+	    depth = simple_strtoul(this_opt+6, NULL, 0);
+	    switch (depth) {
+		case 8:
+		    default_color_mode = CMODE_8;
+		    break;
+		case 15:
+		case 16:
+		    default_color_mode = CMODE_16;
+		    break;
+		case 24:
+		case 32:
+		    default_color_mode = CMODE_32;
+		    break;
+	    };
+	}
+    }
+}
+
+
+static int atyfbcon_switch(int con, struct fb_info *info)
+{
+    struct atyfb_par par;
+
+    /* Do we have to save the colormap? */
+    if (fb_display[currcon].cmap.len)
+	fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
+		    atyfb_getcolreg, info);
+    currcon = con;
+    decode_var(&fb_display[con].var, &par);
+    atyfb_set_par(&par);
+    /* Install new colormap */
+    do_install_cmap(con, info);
+    return 0;
+}
+
+    /*
+     *  Update the `var' structure (called by fbcon.c)
+     */
+
+static int atyfbcon_updatevar(int con, struct fb_info *info)
+{
+    current_par.yoffset = fb_display[con].var.yoffset;
+    set_off_pitch(&current_par);
+    return 0;
+}
+
+    /*
+     *  Blank the display.
+     */
+
+static void atyfbcon_blank(int blank, struct fb_info *info)
+{
+    char gen_cntl;
+
+    gen_cntl = aty_ld_8(CRTC_GEN_CNTL);
+    if (blank & VESA_VSYNC_SUSPEND)
+	    gen_cntl |= 0x8;
+    if (blank & VESA_HSYNC_SUSPEND)
+	    gen_cntl |= 0x4;
+    if ((blank & VESA_POWERDOWN) == VESA_POWERDOWN)
+	    gen_cntl |= 0x40;
+    if (blank == VESA_NO_BLANKING)
+	    gen_cntl &= ~(0x4c);
+    aty_st_8(CRTC_GEN_CNTL, gen_cntl);
+}
+
+
+    /*
+     *  Read a single color register and split it into
+     *  colors/transparent. Return != 0 for invalid regno.
+     */
+
+static int atyfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+			 u_int *transp, struct fb_info *info)
+{
+    if (regno > 255)
+	return 1;
+    *red = palette[regno].red;
+    *green = palette[regno].green;
+    *blue = palette[regno].blue;
+    return 0;
+}
+
+
+    /*
+     *  Set a single color register. The values supplied are already
+     *  rounded down to the hardware's capabilities (according to the
+     *  entries in the var structure). Return != 0 for invalid regno.
+     */
+
+static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			 u_int transp, struct fb_info *info)
+{
+    int i, scale;
+
+    if (regno > 255)
+	return 1;
+    palette[regno].red = red;
+    palette[regno].green = green;
+    palette[regno].blue = blue;
+    aty_WaitQueue(2);
+    i = aty_ld_8(DAC_CNTL) & 0xfc;
+    if (chip_type == MACH64_GT_ID)
+	    i |= 0x2;	/*DAC_CNTL|0x2 turns off the extra brightness for gt*/
+    aty_st_8(DAC_CNTL, i);
+    aty_st_8(DAC_REGS + DAC_MASK, 0xff);
+    eieio();
+    scale = ((chip_type != MACH64_GX_ID) &&
+	     (current_par.cmode == CMODE_16)) ? 3 : 0;
+    aty_WaitQueue(4);
+    aty_cmap_regs->windex = regno << scale;
+    eieio();
+    aty_cmap_regs->lut = red << scale;
+    eieio();
+    aty_cmap_regs->lut = green << scale;
+    eieio();
+    aty_cmap_regs->lut = blue << scale;
+    eieio();
+    if (regno < 16) {
+#ifdef CONFIG_FBCON_CFB16
+	fbcon_cfb16_cmap[regno] = (regno << 10) | (regno << 5) | regno;
+#endif
+#ifdef CONFIG_FBCON_CFB32
+	fbcon_cfb32_cmap[regno] = (regno << 24) | (regno << 16) |
+				  (regno << 8) | regno;
+#endif
+    }
+    return 0;
+}
+
+
+static void do_install_cmap(int con, struct fb_info *info)
+{
+    if (con != currcon)
+	return;
+    if (fb_display[con].cmap.len)
+	fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
+		    atyfb_setcolreg, info);
+    else
+	fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+				    &fb_display[con].var, 1, atyfb_setcolreg,
+		    info);
+}
+
+
+    /*
+     *  Accelerated functions
+     */
+
+void aty_waitblit(void)
+{
+    aty_WaitIdleEmpty();	/* Make sure that all commands have finished */
+}
+
+void aty_rectcopy(int srcx, int srcy, int dstx, int dsty, u_int width,
+		  u_int height)
+{
+    u_int direction = 0;
+
+    if (srcy < dsty) {
+	dsty += height - 1;
+	srcy += height - 1;
+    } else
+	direction |= DST_Y_TOP_TO_BOTTOM;
+
+    if (srcx < dstx) {
+	dstx += width - 1;
+	srcx += width - 1;
+    } else
+	direction |= DST_X_LEFT_TO_RIGHT;
+
+    aty_WaitQueue(4);
+    aty_st_le32(DP_WRITE_MSK, 0x000000FF /* pGC->planemask */ );
+    aty_st_le32(DP_MIX, (MIX_SRC << 16) |  MIX_DST);
+    aty_st_le32(DP_SRC, FRGD_SRC_BLIT);
+
+    aty_WaitQueue(5);
+    aty_st_le32(SRC_Y_X, (srcx << 16) | (srcy & 0x0000ffff));
+    aty_st_le32(SRC_WIDTH1, width);
+    aty_st_le32(DST_CNTL, direction);
+    aty_st_le32(DST_Y_X, (dstx << 16) | (dsty & 0x0000ffff));
+    aty_st_le32(DST_HEIGHT_WIDTH, (width << 16) | (height & 0x0000ffff));
+
+    aty_WaitIdleEmpty();	/* Make sure that all commands have finished */
+
+    /*
+     * Make sure that the destination trajectory is correctly set
+     * for subsequent calls.  MACH64_BIT_BLT is the only function that
+     * currently changes the destination trajectory from L->R and T->B.
+     */
+    aty_st_le32(DST_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM));
+}
+
+void aty_rectfill(int dstx, int dsty, u_int width, u_int height, u_int color)
+{
+    if (!width || !height)
+	return;
+
+    aty_WaitQueue(5);
+    aty_st_le32(DP_FRGD_CLR, color /* pGC->fgPixel */ );
+    aty_st_le32(DP_WRITE_MSK, 0x000000FF /* pGC->planemask */ );
+    aty_st_le32(DP_MIX, (MIX_SRC << 16) | MIX_DST);
+    aty_st_le32(DP_SRC, FRGD_SRC_FRGD_CLR);
+
+    aty_st_le32(DST_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
+
+    aty_WaitQueue(2);
+    aty_st_le32(DST_Y_X, (((u_int)dstx << 16) | ((u_int)dsty & 0x0000ffff)));
+    aty_st_le32(DST_HEIGHT_WIDTH, (((u_int)width << 16) | height));
+
+    aty_WaitIdleEmpty();	/* Make sure that all commands have finished */
+}
+
+
+    /*
+     *  Text console acceleration
+     */
+
+static void fbcon_aty8_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			     int height, int width)
+{
+    sx *= p->fontwidth;
+    sy *= p->fontheight;
+    dx *= p->fontwidth;
+    dy *= p->fontheight;
+    width *= p->fontwidth;
+    height *= p->fontheight;
+
+    aty_rectcopy(sx, sy, dx, dy, width, height);
+}
+
+static void fbcon_aty8_clear(struct vc_data *conp, struct display *p, int sy,
+			     int sx, int height, int width)
+{
+    u32 bgx = attr_bgcol_ec(p, conp);
+    bgx |= (bgx << 8);
+    bgx |= (bgx << 16);
+
+    sx *= p->fontwidth;
+    sy *= p->fontheight;
+    width *= p->fontwidth;
+    height *= p->fontheight;
+
+    aty_rectfill(sx, sy, width, height, bgx);
+}
+
+static void fbcon_aty8_putc(struct vc_data *conp, struct display *p, int c,
+			    int yy, int xx)
+{
+    aty_waitblit();
+    fbcon_cfb8_putc(conp, p, c, yy, xx);
+}
+
+static void fbcon_aty8_putcs(struct vc_data *conp, struct display *p,
+			     const char *s, int count, int yy, int xx)
+{
+    aty_waitblit();
+    fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
+}
+
+static struct display_switch fbcon_aty8 = {
+    fbcon_cfb8_setup, fbcon_aty8_bmove, fbcon_aty8_clear, fbcon_aty8_putc,
+    fbcon_aty8_putcs, fbcon_cfb8_revc
+};
+
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+
+    /*
+     *  Backward compatibility mode for Xpmac
+     */
+
+static int atyfb_console_setmode(struct vc_mode *mode, int doit)
+{
+    int err;
+    struct fb_var_screeninfo var;
+    struct atyfb_par par;
+    int vmode, cmode;
+
+    if (mode->mode <= 0 || mode->mode > VMODE_MAX )
+	return -EINVAL;
+    vmode = mode->mode;
+
+    switch (mode->depth) {
+	case 24:
+	case 32:
+	    cmode = CMODE_32;
+	    break;
+	case 16:
+	    cmode = CMODE_16;
+	    break;
+	case 8:
+	case 0:			/* (default) */
+	    cmode = CMODE_8;
+	    break;
+	default:
+	    return -EINVAL;
+    }
+    init_par(&par, vmode, cmode);
+    encode_var(&var, &par);
+    if ((err = decode_var(&var, &par)))
+	return err;
+    if (doit)
+	atyfb_set_var(&var, currcon, 0);
+    return 0;
+}
+
+#endif /* CONFIG_FB_COMPAT_XPMAC */
diff -u --recursive --new-file v2.1.90/linux/drivers/video/cyberfb.c linux/drivers/video/cyberfb.c
--- v2.1.90/linux/drivers/video/cyberfb.c	Fri Oct 31 07:59:40 1997
+++ linux/drivers/video/cyberfb.c	Sat Mar 21 11:12:49 1998
@@ -36,18 +36,34 @@
 #include <asm/system.h>
 #include <asm/irq.h>
 #include <asm/pgtable.h>
+#include <asm/amigahw.h>
+
 #include "s3blit.h"
+#include "fbcon.h"
+#include "fbcon-cfb8.h"
+#include "fbcon-cfb16.h"
+
 
+#ifdef CYBERFBDEBUG
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
 
 #define arraysize(x)    (sizeof(x)/sizeof(*(x)))
 
-struct Cyber_fb_par {
+
+#define wb_64(reg,dat) (*((unsigned char volatile *)CyberRegs + reg) = dat)
+
+
+
+struct cyberfb_par {
    int xres;
    int yres;
    int bpp;
 };
 
-static struct Cyber_fb_par current_par;
+static struct cyberfb_par current_par;
 
 static int current_par_valid = 0;
 static int currcon = 0;
@@ -68,13 +84,13 @@
 
    /* Display Control */
 
-   int (*encode_fix)(struct fb_fix_screeninfo *fix, struct Cyber_fb_par *par);
-   int (*decode_var)(struct fb_var_screeninfo *var, struct Cyber_fb_par *par);
-   int (*encode_var)(struct fb_var_screeninfo *var, struct Cyber_fb_par *par);
+   int (*encode_fix)(struct fb_fix_screeninfo *fix, struct cyberfb_par *par);
+   int (*decode_var)(struct fb_var_screeninfo *var, struct cyberfb_par *par);
+   int (*encode_var)(struct fb_var_screeninfo *var, struct cyberfb_par *par);
    int (*getcolreg)(u_int regno, u_int *red, u_int *green, u_int *blue,
-                    u_int *transp);
+                    u_int *transp, struct fb_info *info);
    int (*setcolreg)(u_int regno, u_int red, u_int green, u_int blue,
-                    u_int transp);
+                    u_int transp, struct fb_info *info);
    void (*blank)(int blank);
 } *fbhw;
 
@@ -83,7 +99,7 @@
  *    Frame Buffer Name
  */
 
-static char Cyber_fb_name[16] = "Cybervision";
+static char cyberfb_name[16] = "Cybervision";
 
 
 /*
@@ -106,105 +122,67 @@
 static unsigned long CyberMem;
 static unsigned long CyberSize;
 static volatile char *CyberRegs;
-
-
-/*
- *    Predefined Video Mode Names
- */
-
-static char *Cyber_fb_modenames[] = {
-
-   /*
-    *    Autodetect (Default) Video Mode
-    */
-
-   "default",
-
-   /*
-    *    Predefined Video Modes
-    */
-    
-   "cyber8",            /* Cybervision 8 bpp */
-   "cyber16",           /* Cybervision 16 bpp */
-   "800x600x8",
-   "640x480x8",
-
-   /*
-    *    Dummy Video Modes
-    */
-
-   "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy",
-   "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy",
-   "dummy", "dummy",
-
-   /*
-    *    User Defined Video Modes
-    *
-    *    This doesn't work yet!!
-    */
-
-   "user0", "user1", "user2", "user3", "user4", "user5", "user6", "user7"
-};
-
+ 
 
 /*
- *    Predefined Video Mode Definitions
+ *    Predefined Video Modes
  */
 
-static struct fb_var_screeninfo cyber_fb_predefined[] = {
-
-   /*
-    *    Autodetect (Default) Video Mode
-    */
-
-   { 0, },
-
-   /*
-    *    Predefined Video Modes
-    */
-    
-   {
-      /* Cybervision 8 bpp */
-      CYBER8_WIDTH, CYBER8_HEIGHT, CYBER8_WIDTH, CYBER8_HEIGHT, 0, 0, 8, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
-      FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
-   }, {
-      /* Cybervision 16 bpp */
-      800, 600, 800, 600, 0, 0, 16, 0,
-      {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, CYBER16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
-      FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
-   }, {
-      /* Cybervision 8 bpp */
-      800, 600, 800, 600, 0, 0, 8, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
-      FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
-   }, {
-      /* Cybervision 8 bpp */
-      640, 480, 640, 480, 0, 0, 8, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
-      FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
-   },
-   /*
-    *    Dummy Video Modes
-    */
-
-   { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, 
-   { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, },
-
-   /*
-    *    User Defined Video Modes
-    */
-
-   { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }
+static struct fb_videomode cyberfb_predefined[] __initdata = {
+    {
+	"640x480-8", {		/* Cybervision 8 bpp */
+	    640, 480, 640, 480, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }, {
+	"800x600-8", {		/* Cybervision 8 bpp */
+	    800, 600, 800, 600, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }, {
+	"1024x768-8", {		/* Cybervision 8 bpp */
+	    1024, 768, 1024, 768, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }, {
+	"1152x886-8", {		/* Cybervision 8 bpp */
+	    1152, 886, 1152, 886, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }, {
+	"1280x1024-8", {	/* Cybervision 8 bpp */
+	    1280, 1024, 1280, 1024, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }, {
+	"1600x1200-8", {	/* Cybervision 8 bpp */
+	    1600, 1200, 1600, 1200, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }, {
+	"800x600-16", {		/* Cybervision 16 bpp */
+	    800, 600, 800, 600, 0, 0, 16, 0,
+	    {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NONE, CYBER16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }
 };
 
 
-#define NUM_TOTAL_MODES    arraysize(cyber_fb_predefined)
-#define NUM_PREDEF_MODES   (5)
+#define NUM_TOTAL_MODES    arraysize(cyberfb_predefined)
 
 
 static int Cyberfb_inverse = 0;
@@ -212,57 +190,72 @@
 static int Cyberfb_Cyber8 = 0;        /* Use Cybervision board */
 static int Cyberfb_Cyber16 = 0;       /* Use Cybervision board */
 #endif
-static int Cyberfb_mode = 0;
-
 
 /*
  *    Some default modes
  */
 
-#define CYBER8_DEFMODE     (1)
-#define CYBER16_DEFMODE    (2)
+#define CYBER8_DEFMODE     (0)
+#define CYBER16_DEFMODE    (6)
+
+static struct fb_var_screeninfo cyberfb_default;
 
 
 /*
  *    Interface used by the world
  */
 
-void Cyber_video_setup(char *options, int *ints);
+void cyberfb_setup(char *options, int *ints);
 
-static int Cyber_fb_open(int fbidx);
-static int Cyber_fb_release(int fbidx);
-static int Cyber_fb_get_fix(struct fb_fix_screeninfo *fix, int con);
-static int Cyber_fb_get_var(struct fb_var_screeninfo *var, int con);
-static int Cyber_fb_set_var(struct fb_var_screeninfo *var, int con);
-static int Cyber_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con);
-static int Cyber_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con);
-static int Cyber_fb_pan_display(struct fb_var_screeninfo *var, int con);
-static int Cyber_fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
-                          u_long arg, int con);
+static int cyberfb_open(struct fb_info *info);
+static int cyberfb_release(struct fb_info *info);
+static int cyberfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct
+fb_info *info);
+static int cyberfb_get_var(struct fb_var_screeninfo *var, int con, struct
+fb_info *info);
+static int cyberfb_set_var(struct fb_var_screeninfo *var, int con, struct
+fb_info *info);
+static int cyberfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info);
+static int cyberfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info);
+static int cyberfb_pan_display(struct fb_var_screeninfo *var, int con,
+			       struct fb_info *info);
+static int cyberfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+                         u_long arg, int con, struct fb_info *info);
 
 
 /*
  *    Interface to the low level console driver
  */
 
-unsigned long Cyber_fb_init(unsigned long mem_start);
-static int Cyberfb_switch(int con);
-static int Cyberfb_updatevar(int con);
-static void Cyberfb_blank(int blank);
-static int Cyberfb_setcmap(struct fb_cmap *cmap, int con);
+unsigned long cyberfb_init(unsigned long mem_start);
+static int Cyberfb_switch(int con, struct fb_info *info);
+static int Cyberfb_updatevar(int con, struct fb_info *info);
+static void Cyberfb_blank(int blank, struct fb_info *info);
+
+
+/*
+ *    Text console acceleration
+ */
+
+#ifdef CONFIG_FBCON_CFB8
+static struct display_switch fbcon_cyber8;
+#endif
 
 
 /*
  *    Accelerated Functions used by the low level console driver
  */
 
-void Cyber_WaitQueue(u_short fifo);
-void Cyber_WaitBlit(void);
-void Cyber_BitBLT(u_short curx, u_short cury, u_short destx, u_short desty,
-                  u_short width, u_short height, u_short mode);
-void Cyber_RectFill(u_short x, u_short y, u_short width, u_short height,
-                    u_short mode, u_short color);
-void Cyber_MoveCursor(u_short x, u_short y);
+static void Cyber_WaitQueue(u_short fifo);
+static void Cyber_WaitBlit(void);
+static void Cyber_BitBLT(u_short curx, u_short cury, u_short destx,
+			 u_short desty, u_short width, u_short height,
+			 u_short mode);
+static void Cyber_RectFill(u_short x, u_short y, u_short width, u_short height,
+			   u_short mode, u_short color);
+static void Cyber_MoveCursor(u_short x, u_short y);
 
 
 /*
@@ -271,15 +264,15 @@
 
 static int Cyber_init(void);
 static int Cyber_encode_fix(struct fb_fix_screeninfo *fix,
-                          struct Cyber_fb_par *par);
+                          struct cyberfb_par *par);
 static int Cyber_decode_var(struct fb_var_screeninfo *var,
-                          struct Cyber_fb_par *par);
+                          struct cyberfb_par *par);
 static int Cyber_encode_var(struct fb_var_screeninfo *var,
-                          struct Cyber_fb_par *par);
+                          struct cyberfb_par *par);
 static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
-                         u_int *transp);
+                         u_int *transp, struct fb_info *info);
 static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp);
+                         u_int transp, struct fb_info *info);
 static void Cyber_blank(int blank);
 
 
@@ -287,11 +280,11 @@
  *    Internal routines
  */
 
-static void Cyber_fb_get_par(struct Cyber_fb_par *par);
-static void Cyber_fb_set_par(struct Cyber_fb_par *par);
+static void cyberfb_get_par(struct cyberfb_par *par);
+static void cyberfb_set_par(struct cyberfb_par *par);
 static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive);
-static void do_install_cmap(int con);
-static void Cyber_fb_set_disp(int con);
+static void do_install_cmap(int con, struct fb_info *info);
+static void cyberfb_set_disp(int con, struct fb_info *info);
 static int get_video_mode(const char *name);
 
 
@@ -311,41 +304,32 @@
 	char size;
 	volatile u_long *CursorBase;
 
-#if 0
-	if (Cyberfb_mode == -1)
+	for (i = 0; i < 256; i++)
 	{
-		if (Cyberfb_Cyber8)
-			Cyberfb_mode = CYBER8_DEFMODE;
-		else
-			Cyberfb_mode = CYBER16_DEFMODE;
+		Cyber_colour_table [i][0] = i;
+		Cyber_colour_table [i][1] = i;
+		Cyber_colour_table [i][2] = i;
+		Cyber_colour_table [i][3] = 0;
 	}
-#endif
-
-	for (i = 0; i < 256; i++)
-
-		for (i = 0; i < 256; i++)
-		{
-			Cyber_colour_table [i][0] = i;
-			Cyber_colour_table [i][1] = i;
-			Cyber_colour_table [i][2] = i;
-			Cyber_colour_table [i][3] = 0;
-		}
 
 	/*
 	 * Just clear the thing for the biggest mode.
+	 *
+	 * ++Andre, TODO: determine size first, then clear all memory
+	 *                (the 3D penguin might need texture memory :-) )
 	 */
 
-	memset ((char*)CyberMem, 0, CYBER8_WIDTH * CYBER8_HEIGHT);
+	memset ((char*)CyberMem, 0, 1600 * 1200);
 
 	/* Disable hardware cursor */
-	*(CyberRegs + S3_CRTC_ADR)  = S3_REG_LOCK2;
-	*(CyberRegs + S3_CRTC_DATA) = 0xa0;
-	*(CyberRegs + S3_CRTC_ADR)  = S3_HGC_MODE;
-	*(CyberRegs + S3_CRTC_DATA) = 0x00;
-	*(CyberRegs + S3_CRTC_ADR)  = S3_HWGC_DX;
-	*(CyberRegs + S3_CRTC_DATA) = 0x00;
-	*(CyberRegs + S3_CRTC_ADR)  = S3_HWGC_DY;
-	*(CyberRegs + S3_CRTC_DATA) = 0x00;
+	wb_64(S3_CRTC_ADR, S3_REG_LOCK2);
+	wb_64(S3_CRTC_DATA, 0xa0);
+	wb_64(S3_CRTC_ADR, S3_HGC_MODE);
+	wb_64(S3_CRTC_DATA, 0x00);
+	wb_64(S3_CRTC_ADR, S3_HWGC_DX);
+	wb_64(S3_CRTC_DATA, 0x00);
+	wb_64(S3_CRTC_ADR, S3_HWGC_DY);
+	wb_64(S3_CRTC_DATA, 0x00);
 
 	/* Get memory size (if not 2MB it is 4MB) */
 	*(CyberRegs + S3_CRTC_ADR) = S3_LAW_CTL;
@@ -372,8 +356,8 @@
 		*(CursorBase+3+(i*4)) = 0xffff0000;
 	}
 
-	Cyber_setcolreg (255, 56, 100, 160, 0);
-	Cyber_setcolreg (254, 0, 0, 0, 0);
+	Cyber_setcolreg (255, 56, 100, 160, 0, NULL /* unused */);
+	Cyber_setcolreg (254, 0, 0, 0, 0, NULL /* unused */);
 
 	return 0;
 }
@@ -385,11 +369,11 @@
  */
 
 static int Cyber_encode_fix(struct fb_fix_screeninfo *fix,
-			    struct Cyber_fb_par *par)
+			    struct cyberfb_par *par)
 {
 	int i;
 
-	strcpy(fix->id, Cyber_fb_name);
+	strcpy(fix->id, cyberfb_name);
 	fix->smem_start = (caddr_t)CyberMem;
 	fix->smem_len = CyberSize;
 	fix->mmio_start = (unsigned char *)CyberRegs;
@@ -420,7 +404,7 @@
  */
 
 static int Cyber_decode_var(struct fb_var_screeninfo *var,
-			    struct Cyber_fb_par *par)
+			    struct cyberfb_par *par)
 {
 #if 1
 	par->xres = var->xres;
@@ -447,7 +431,7 @@
  */
 
 static int Cyber_encode_var(struct fb_var_screeninfo *var,
-			    struct Cyber_fb_par *par)
+			    struct cyberfb_par *par)
 {
 	int i;
 
@@ -486,7 +470,10 @@
 
 	var->height = -1;
 	var->width = -1;
+
 	var->accel = FB_ACCEL_CYBERVISION;
+	DPRINTK("accel CV64\n");
+
 	var->vmode = FB_VMODE_NONINTERLACED;
 
 	/* Dummy values */
@@ -517,20 +504,22 @@
  */
 
 static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			   u_int transp)
+			   u_int transp, struct fb_info *info)
 {
 	if (regno > 255)
+	{
 		return (1);
+	}
 
-	*(CyberRegs + 0x3c8) = (char)regno;
+	wb_64(0x3c8, (unsigned char) regno);
 	Cyber_colour_table [regno][0] = red & 0xff;
 	Cyber_colour_table [regno][1] = green & 0xff;
 	Cyber_colour_table [regno][2] = blue & 0xff;
 	Cyber_colour_table [regno][3] = transp;
 
-	*(CyberRegs + 0x3c9) = (red & 0xff) >> 2;
-	*(CyberRegs + 0x3c9) = (green & 0xff) >> 2;
-	*(CyberRegs + 0x3c9) = (blue & 0xff) >> 2;
+	wb_64(0x3c9, (red & 0xff) >> 2);
+	wb_64(0x3c9, (green & 0xff) >> 2);
+	wb_64(0x3c9, (blue & 0xff) >> 2);
 
 	return (0);
 }
@@ -542,13 +531,13 @@
  */
 
 static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
-			   u_int *transp)
+			   u_int *transp, struct fb_info *info)
 {
 	if (regno >= 256)
 		return (1);
-	*red    = Cyber_colour_table [regno][0];
-	*green  = Cyber_colour_table [regno][1];
-	*blue   = Cyber_colour_table [regno][2];
+	*red	= Cyber_colour_table [regno][0];
+	*green	= Cyber_colour_table [regno][1];
+	*blue	= Cyber_colour_table [regno][2];
 	*transp = Cyber_colour_table [regno][3];
 	return (0);
 }
@@ -563,28 +552,32 @@
 	int i;
 
 	if (blank)
+	{
 		for (i = 0; i < 256; i++)
 		{
-			*(CyberRegs + 0x3c8) = i;
-			*(CyberRegs + 0x3c9) = 0;
-			*(CyberRegs + 0x3c9) = 0;
-			*(CyberRegs + 0x3c9) = 0;
+			wb_64(0x3c8, (unsigned char) i);
+			wb_64(0x3c9, 0);
+			wb_64(0x3c9, 0);
+			wb_64(0x3c9, 0);
 		}
+	}
 	else
+	{
 		for (i = 0; i < 256; i++)
 		{
-			*(CyberRegs + 0x3c8) = i;
-			*(CyberRegs + 0x3c9) = Cyber_colour_table [i][0] >> 2;
-			*(CyberRegs + 0x3c9) = Cyber_colour_table [i][1] >> 2;
-			*(CyberRegs + 0x3c9) = Cyber_colour_table [i][2] >> 2;
+			wb_64(0x3c8, (unsigned char) i);
+			wb_64(0x3c9, Cyber_colour_table[i][0] >> 2);
+			wb_64(0x3c9, Cyber_colour_table[i][1] >> 2);
+			wb_64(0x3c9, Cyber_colour_table[i][2] >> 2);
 		}
+	}
 }
 
 
 /**************************************************************
  * We are waiting for "fifo" FIFO-slots empty
  */
-void Cyber_WaitQueue (u_short fifo)
+static void Cyber_WaitQueue (u_short fifo)
 {
 	u_short status;
 
@@ -598,7 +591,7 @@
 /**************************************************************
  * We are waiting for Hardware (Graphics Engine) not busy
  */
-void Cyber_WaitBlit (void)
+static void Cyber_WaitBlit (void)
 {
 	u_short status;
 
@@ -612,8 +605,9 @@
 /**************************************************************
  * BitBLT - Through the Plane
  */
-void Cyber_BitBLT (u_short curx, u_short cury, u_short destx, u_short desty,
-                   u_short width, u_short height, u_short mode)
+static void Cyber_BitBLT (u_short curx, u_short cury, u_short destx,
+			  u_short desty, u_short width, u_short height,
+			  u_short mode)
 {
 	u_short blitcmd = S3_BITBLT;
 
@@ -655,8 +649,8 @@
 /**************************************************************
  * Rectangle Fill Solid
  */
-void Cyber_RectFill (u_short x, u_short y, u_short width, u_short height,
-                     u_short mode, u_short color)
+static void Cyber_RectFill (u_short x, u_short y, u_short width,
+			    u_short height, u_short mode, u_short color)
 {
 	u_short blitcmd = S3_FILLEDRECT;
 
@@ -677,11 +671,10 @@
 	*((u_short volatile *)(CyberRegs + S3_CMD)) = blitcmd;
 }
 
-
 /**************************************************************
  * Move cursor to x, y
  */
-void Cyber_MoveCursor (u_short x, u_short y)
+static void Cyber_MoveCursor (u_short x, u_short y)
 {
 	*(CyberRegs + S3_CRTC_ADR)  = 0x39;
 	*(CyberRegs + S3_CRTC_DATA) = 0xa0;
@@ -714,16 +707,20 @@
  *    Fill the hardware's `par' structure.
  */
 
-static void Cyber_fb_get_par(struct Cyber_fb_par *par)
+static void cyberfb_get_par(struct cyberfb_par *par)
 {
 	if (current_par_valid)
+	{
 		*par = current_par;
+	}
 	else
-		fbhw->decode_var(&cyber_fb_predefined[Cyberfb_mode], par);
+	{
+		fbhw->decode_var(&cyberfb_default, par);
+	}
 }
 
 
-static void Cyber_fb_set_par(struct Cyber_fb_par *par)
+static void cyberfb_set_par(struct cyberfb_par *par)
 {
 	current_par = *par;
 	current_par_valid = 1;
@@ -733,6 +730,7 @@
 static void cyber_set_video(struct fb_var_screeninfo *var)
 {
 	/* Set clipping rectangle to current screen size */
+ 
 	*((u_short volatile *)(CyberRegs + 0xbee8)) = 0x1000;
 	*((u_short volatile *)(CyberRegs + 0xbee8)) = 0x2000;
 
@@ -744,13 +742,13 @@
 static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
 {
 	int err, activate;
-	struct Cyber_fb_par par;
+	struct cyberfb_par par;
 
 	if ((err = fbhw->decode_var(var, &par)))
 		return(err);
 	activate = var->activate;
 	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive)
-		Cyber_fb_set_par(&par);
+		cyberfb_set_par(&par);
 	fbhw->encode_var(var, &par);
 	var->activate = activate;
 
@@ -759,16 +757,16 @@
 }
 
 
-static void do_install_cmap(int con)
+static void do_install_cmap(int con, struct fb_info *info)
 {
 	if (con != currcon)
 		return;
 	if (fb_display[con].cmap.len)
 		fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
-			    fbhw->setcolreg);
+			    fbhw->setcolreg, info);
 	else
-		fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
-			    &fb_display[con].var, 1, fbhw->setcolreg);
+		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+			    &fb_display[con].var, 1, fbhw->setcolreg, info);
 }
 
 
@@ -776,7 +774,7 @@
  *  Open/Release the frame buffer device
  */
 
-static int Cyber_fb_open(int fbidx)
+static int cyberfb_open(struct fb_info *info)
 {
 	/*
 	 * Nothing, only a usage count for the moment
@@ -786,7 +784,7 @@
 	return(0);
 }
 
-static int Cyber_fb_release(int fbidx)
+static int cyberfb_release(struct fb_info *info)
 {
 	MOD_DEC_USE_COUNT;
 	return(0);
@@ -797,13 +795,14 @@
  *    Get the Fixed Part of the Display
  */
 
-static int Cyber_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
+static int cyberfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			   struct fb_info *info)
 {
-	struct Cyber_fb_par par;
+	struct cyberfb_par par;
 	int error = 0;
 
 	if (con == -1)
-		Cyber_fb_get_par(&par);
+		cyberfb_get_par(&par);
 	else
 		error = fbhw->decode_var(&fb_display[con].var, &par);
 	return(error ? error : fbhw->encode_fix(fix, &par));
@@ -814,21 +813,28 @@
  *    Get the User Defined Part of the Display
  */
 
-static int Cyber_fb_get_var(struct fb_var_screeninfo *var, int con)
+static int cyberfb_get_var(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info)
 {
-	struct Cyber_fb_par par;
+	struct cyberfb_par par;
 	int error = 0;
 
-	if (con == -1) {
-		Cyber_fb_get_par(&par);
+	if (con == -1)
+	{
+		cyberfb_get_par(&par);
 		error = fbhw->encode_var(var, &par);
-	} else
+		disp.var = *var;   /* ++Andre: don't know if this is the right place */
+	}
+	else
+	{
 		*var = fb_display[con].var;
+	}
+
 	return(error);
 }
 
 
-static void Cyber_fb_set_disp(int con)
+static void cyberfb_set_disp(int con, struct fb_info *info)
 {
 	struct fb_fix_screeninfo fix;
 	struct display *display;
@@ -838,7 +844,7 @@
 	else
 		display = &disp;	/* used during initialization */
 
-	Cyber_fb_get_fix(&fix, con);
+	cyberfb_get_fix(&fix, con, info);
 	if (con == -1)
 		con = 0;
 	display->screen_base = (u_char *)fix.smem_start;
@@ -849,6 +855,21 @@
 	display->ywrapstep = fix.ywrapstep;
 	display->can_soft_blank = 1;
 	display->inverse = Cyberfb_inverse;
+	switch (display->var.bits_per_pixel) {
+#ifdef CONFIG_FBCON_CFB8
+	    case 8:
+		display->dispsw = &fbcon_cyber8;
+		break;
+#endif
+#ifdef CONFIG_FBCON_CFB16
+	    case 16:
+		display->dispsw = &fbcon_cfb16;
+		break;
+#endif
+	    default:
+		display->dispsw = NULL;
+		break;
+	}
 }
 
 
@@ -856,7 +877,8 @@
  *    Set the User Defined Part of the Display
  */
 
-static int Cyber_fb_set_var(struct fb_var_screeninfo *var, int con)
+static int cyberfb_set_var(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info)
 {
 	int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp;
 
@@ -873,10 +895,10 @@
 		    oldvxres != var->xres_virtual ||
 		    oldvyres != var->yres_virtual ||
 		    oldbpp != var->bits_per_pixel) {
-			Cyber_fb_set_disp(con);
+			cyberfb_set_disp(con, info);
 			(*fb_info.changevar)(con);
 			fb_alloc_cmap(&fb_display[con].cmap, 0, 0);
-			do_install_cmap(con);
+			do_install_cmap(con, info);
 		}
 	}
 	var->activate = 0;
@@ -888,15 +910,16 @@
  *    Get the Colormap
  */
 
-static int Cyber_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+static int cyberfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info)
 {
 	if (con == currcon) /* current console? */
 		return(fb_get_cmap(cmap, &fb_display[con].var,
-				   kspc, fbhw->getcolreg));
+				   kspc, fbhw->getcolreg, info));
 	else if (fb_display[con].cmap.len) /* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
 	else
-		fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+		fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
 			     cmap, kspc ? 0 : 2);
 	return(0);
 }
@@ -906,7 +929,8 @@
  *    Set the Colormap
  */
 
-static int Cyber_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+static int cyberfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info)
 {
 	int err;
 
@@ -915,9 +939,9 @@
 				 1<<fb_display[con].var.bits_per_pixel, 0)))
 			return(err);
 	}
-	if (con == currcon)              /* current console? */
+	if (con == currcon)		 /* current console? */
 		return(fb_set_cmap(cmap, &fb_display[con].var,
-				   kspc, fbhw->setcolreg));
+				   kspc, fbhw->setcolreg, info));
 	else
 		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
 	return(0);
@@ -930,31 +954,32 @@
  *    This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
  */
 
-static int Cyber_fb_pan_display(struct fb_var_screeninfo *var, int con)
+static int cyberfb_pan_display(struct fb_var_screeninfo *var, int con,
+			       struct fb_info *info)
 {
 	return(-EINVAL);
 }
 
 
 /*
-    *    Cybervision Frame Buffer Specific ioctls
+    *	 Cybervision Frame Buffer Specific ioctls
     */
 
-static int Cyber_fb_ioctl(struct inode *inode, struct file *file,
-                          u_int cmd, u_long arg, int con)
+static int cyberfb_ioctl(struct inode *inode, struct file *file,
+			 u_int cmd, u_long arg, int con, struct fb_info *info)
 {
 	return(-EINVAL);
 }
 
 
-static struct fb_ops Cyber_fb_ops = {
-	Cyber_fb_open, Cyber_fb_release, Cyber_fb_get_fix, Cyber_fb_get_var,
-	Cyber_fb_set_var, Cyber_fb_get_cmap, Cyber_fb_set_cmap,
-	Cyber_fb_pan_display, Cyber_fb_ioctl
+static struct fb_ops cyberfb_ops = {
+	cyberfb_open, cyberfb_release, cyberfb_get_fix, cyberfb_get_var,
+	cyberfb_set_var, cyberfb_get_cmap, cyberfb_set_cmap,
+	cyberfb_pan_display, NULL, cyberfb_ioctl
 };
 
 
-__initfunc(void Cyber_video_setup(char *options, int *ints))
+__initfunc(void cyberfb_setup(char *options, int *ints))
 {
 	char *this_opt;
 
@@ -969,14 +994,18 @@
 			fb_invert_cmaps();
 		} else if (!strncmp(this_opt, "font:", 5))
 			strcpy(fb_info.fontname, this_opt+5);
-#if 0
-		else if (!strcmp (this_opt, "cyber8"))
-			Cyberfb_Cyber8 = 1;
-		else if (!strcmp (this_opt, "cyber16"))
-			Cyberfb_Cyber16 = 1;
-#endif
+		else if (!strcmp (this_opt, "cyber8")){
+			cyberfb_default = cyberfb_predefined[CYBER8_DEFMODE].var;
+		}
+		else if (!strcmp (this_opt, "cyber16")){
+			cyberfb_default = cyberfb_predefined[CYBER16_DEFMODE].var;
+		}
 		else
-			Cyberfb_mode = get_video_mode(this_opt);
+			get_video_mode(this_opt);
+
+	DPRINTK("default mode: xres=%d, yres=%d, bpp=%d\n",cyberfb_default.xres,
+                                                           cyberfb_default.yres,
+		                                           cyberfb_default.bits_per_pixel);
 }
 
 
@@ -984,10 +1013,10 @@
  *    Initialization
  */
 
-__initfunc(unsigned long Cyber_fb_init(unsigned long mem_start))
+__initfunc(unsigned long cyberfb_init(unsigned long mem_start))
 {
 	int err;
-	struct Cyber_fb_par par;
+	struct cyberfb_par par;
 	unsigned long board_addr;
 	const struct ConfigDev *cd;
 
@@ -1005,33 +1034,30 @@
 
 	fbhw = &Cyber_switch;
 
-	strcpy(fb_info.modename, Cyber_fb_name);
+	strcpy(fb_info.modename, cyberfb_name);
 	fb_info.changevar = NULL;
 	fb_info.node = -1;
-	fb_info.fbops = &Cyber_fb_ops;
-	fb_info.fbvar_num = NUM_TOTAL_MODES;
-	fb_info.fbvar = cyber_fb_predefined;
+	fb_info.fbops = &cyberfb_ops;
 	fb_info.disp = &disp;
 	fb_info.switch_con = &Cyberfb_switch;
 	fb_info.updatevar = &Cyberfb_updatevar;
 	fb_info.blank = &Cyberfb_blank;
-	fb_info.setcmap = &Cyberfb_setcmap;
 
 	err = register_framebuffer(&fb_info);
 	if (err < 0)
 		return mem_start;
 
 	fbhw->init();
-	fbhw->decode_var(&cyber_fb_predefined[Cyberfb_mode], &par);
-	fbhw->encode_var(&cyber_fb_predefined[0], &par);
+	fbhw->decode_var(&cyberfb_default, &par);
+	fbhw->encode_var(&cyberfb_default, &par);
 
-	do_fb_set_var(&cyber_fb_predefined[0], 1);
-	Cyber_fb_get_var(&fb_display[0].var, -1);
-	Cyber_fb_set_disp(-1);
-	do_install_cmap(0);
+	do_fb_set_var(&cyberfb_default, 1);
+	cyberfb_get_var(&fb_display[0].var, -1, &fb_info);
+	cyberfb_set_disp(-1, &fb_info);
+	do_install_cmap(0, &fb_info);
 
-	printk("%s frame buffer device, using %ldK of video memory\n",
-	       fb_info.modename, CyberSize>>10);
+	printk("fb%d: %s frame buffer device, using %ldK of video memory\n",
+	       GET_FB_IDX(fb_info.node), fb_info.modename, CyberSize>>10);
 
 	/* TODO: This driver cannot be unloaded yet */
 	MOD_INC_USE_COUNT;
@@ -1040,17 +1066,17 @@
 }
 
 
-static int Cyberfb_switch(int con)
+static int Cyberfb_switch(int con, struct fb_info *info)
 {
 	/* Do we have to save the colormap? */
 	if (fb_display[currcon].cmap.len)
 		fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
-			    fbhw->getcolreg);
+			    fbhw->getcolreg, info);
 
 	do_fb_set_var(&fb_display[con].var, 1);
 	currcon = con;
 	/* Install new colormap */
-	do_install_cmap(con);
+	do_install_cmap(con, info);
 	return(0);
 }
 
@@ -1062,7 +1088,7 @@
  *    Since it's called by a kernel driver, no range checking is done.
  */
 
-static int Cyberfb_updatevar(int con)
+static int Cyberfb_updatevar(int con, struct fb_info *info)
 {
 	return(0);
 }
@@ -1072,42 +1098,92 @@
     *    Blank the display.
     */
 
-static void Cyberfb_blank(int blank)
+static void Cyberfb_blank(int blank, struct fb_info *info)
 {
 	fbhw->blank(blank);
 }
 
 
 /*
- *    Set the colormap
+ *    Get a Video Mode
  */
 
-static int Cyberfb_setcmap(struct fb_cmap *cmap, int con)
+__initfunc(static int get_video_mode(const char *name))
 {
-	return(Cyber_fb_set_cmap(cmap, 1, con));
+	int i;
+
+	for (i = 0; i < NUM_TOTAL_MODES; i++) {
+		if (!strcmp(name, cyberfb_predefined[i].name)) {
+			cyberfb_default = cyberfb_predefined[i].var;
+			return(i);
+		}
+	}
+	/* ++Andre: set cyberfb default mode */
+	cyberfb_default = cyberfb_predefined[CYBER8_DEFMODE].var;
+	return(0);
 }
 
 
 /*
- *    Get a Video Mode
+ *    Text console acceleration
  */
 
-static int get_video_mode(const char *name)
+#ifdef CONFIG_FBCON_CFB8
+static void fbcon_cyber8_bmove(struct display *p, int sy, int sx, int dy,
+			       int dx, int height, int width)
 {
-	int i;
+    sx *= 8; dx *= 8; width *= 8;
+    Cyber_BitBLT((u_short)sx, (u_short)(sy*p->fontheight), (u_short)dx,
+		 (u_short)(dy*p->fontheight), (u_short)width,
+		 (u_short)(height*p->fontheight), (u_short)S3_NEW);
+}
 
-	for (i = 1; i < NUM_PREDEF_MODES; i++)
-		if (!strcmp(name, Cyber_fb_modenames[i]))
-			cyber_fb_predefined[0] = cyber_fb_predefined[i];
-			return(i);
-	return(0);
+static void fbcon_cyber8_clear(struct vc_data *conp, struct display *p, int sy,
+			       int sx, int height, int width)
+{
+    unsigned char bg;
+
+    sx *= 8; width *= 8;
+    bg = attr_bgcol_ec(p,conp);
+    Cyber_RectFill((u_short)sx,
+		   (u_short)(sy*p->fontheight),
+		   (u_short)width,
+		   (u_short)(height*p->fontheight),
+		   (u_short)S3_NEW,
+		   (u_short)bg);
+}
+
+static void fbcon_cyber8_putc(struct vc_data *conp, struct display *p, int c,
+			      int yy, int xx)
+{
+    Cyber_WaitBlit();
+    fbcon_cfb8_putc(conp, p, c, yy, xx);
+}
+
+static void fbcon_cyber8_putcs(struct vc_data *conp, struct display *p,
+			       const char *s, int count, int yy, int xx)
+{
+    Cyber_WaitBlit();
+    fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
 }
 
+static void fbcon_cyber8_revc(struct display *p, int xx, int yy)
+{
+    Cyber_WaitBlit();
+    fbcon_cfb8_revc(p, xx, yy);
+}
+
+static struct display_switch fbcon_cyber8 = {
+   fbcon_cfb8_setup, fbcon_cyber8_bmove, fbcon_cyber8_clear, fbcon_cyber8_putc,
+   fbcon_cyber8_putcs, fbcon_cyber8_revc
+};
+#endif
+
 
 #ifdef MODULE
 int init_module(void)
 {
-	return(Cyber_fb_init(NULL));
+	return(cyberfb_init(NULL));
 }
 
 void cleanup_module(void)
@@ -1118,12 +1194,3 @@
 	/* TODO: clean up ... */
 }
 #endif /* MODULE */
-
-
-/*
- *  Visible symbols for modules
- */
-
-EXPORT_SYMBOL(Cyber_BitBLT);
-EXPORT_SYMBOL(Cyber_RectFill);
-EXPORT_SYMBOL(Cyber_WaitBlit);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/dn_fb.c linux/drivers/video/dn_fb.c
--- v2.1.90/linux/drivers/video/dn_fb.c	Mon Feb 23 18:12:09 1998
+++ linux/drivers/video/dn_fb.c	Wed Dec 31 16:00:00 1969
@@ -1,388 +0,0 @@
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/tty.h>
-#include <linux/malloc.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <asm/setup.h>
-#include <asm/segment.h>
-#include <asm/system.h>
-#include <asm/irq.h>
-#include <asm/apollohw.h>
-#include <linux/fb.h>
-#include <linux/module.h>
-
-/* apollo video HW definitions */
-
-/*
- * Control Registers.   IOBASE + $x
- *
- * Note: these are the Memory/IO BASE definitions for a mono card set to the
- * alternate address
- *
- * Control 3A and 3B serve identical functions except that 3A
- * deals with control 1 and 3b deals with Color LUT reg.
- */
-
-#define AP_IOBASE       0x5d80          /* Base address of 1 plane board. */
-#define AP_STATUS       0x5d80          /* Status register.  Read */
-#define AP_WRITE_ENABLE 0x5d80          /* Write Enable Register Write */
-#define AP_DEVICE_ID    0x5d81          /* Device ID Register. Read */
-#define AP_ROP_1        0x5d82          /* Raster Operation reg. Write Word */
-#define AP_DIAG_MEM_REQ 0x5d84          /* Diagnostic Memory Request. Write Word */
-#define AP_CONTROL_0    0x5d88          /* Control Register 0.  Read/Write */
-#define AP_CONTROL_1    0x5d8a          /* Control Register 1.  Read/Write */
-#define AP_CONTROL_3A   0x5d8e          /* Control Register 3a. Read/Write */
-#define AP_CONTROL_2    0x5d8c          /* Control Register 2. Read/Write */
-
-
-#define FRAME_BUFFER_START 0x0FA0000
-#define FRAME_BUFFER_LEN 0x40000
-
-/* CREG 0 */
-#define VECTOR_MODE 0x40 /* 010x.xxxx */
-#define DBLT_MODE   0x80 /* 100x.xxxx */
-#define NORMAL_MODE 0xE0 /* 111x.xxxx */
-#define SHIFT_BITS  0x1F /* xxx1.1111 */
-        /* other bits are Shift value */
-
-/* CREG 1 */
-#define AD_BLT      0x80 /* 1xxx.xxxx */
-#define NORMAL      0x80 /* 1xxx.xxxx */   /* What is happening here ?? */
-#define INVERSE     0x00 /* 0xxx.xxxx */   /* Clearing this reverses the screen */
-#define PIX_BLT     0x00 /* 0xxx.xxxx */
-
-#define AD_HIBIT        0x40 /* xIxx.xxxx */
-
-#define ROP_EN          0x10 /* xxx1.xxxx */
-#define DST_EQ_SRC      0x00 /* xxx0.xxxx */
-#define nRESET_SYNC     0x08 /* xxxx.1xxx */
-#define SYNC_ENAB       0x02 /* xxxx.xx1x */
-
-#define BLANK_DISP      0x00 /* xxxx.xxx0 */
-#define ENAB_DISP       0x01 /* xxxx.xxx1 */
-
-#define NORM_CREG1      (nRESET_SYNC | SYNC_ENAB | ENAB_DISP) /* no reset sync */
-
-/* CREG 2 */
-
-/*
- * Following 3 defines are common to 1, 4 and 8 plane.
- */
-
-#define S_DATA_1s   0x00 /* 00xx.xxxx */ /* set source to all 1's -- vector drawing */
-#define S_DATA_PIX  0x40 /* 01xx.xxxx */ /* takes source from ls-bits and replicates over 16 bits */
-#define S_DATA_PLN  0xC0 /* 11xx.xxxx */ /* normal, each data access =16-bits in
- one plane of image mem */
-
-/* CREG 3A/CREG 3B */
-#       define RESET_CREG 0x80 /* 1000.0000 */
-
-/* ROP REG  -  all one nibble */
-/*      ********* NOTE : this is used r0,r1,r2,r3 *********** */
-#define ROP(r2,r3,r0,r1) ( (U_SHORT)((r0)|((r1)<<4)|((r2)<<8)|((r3)<<12)) )
-#define DEST_ZERO               0x0
-#define SRC_AND_DEST    0x1
-#define SRC_AND_nDEST   0x2
-#define SRC                             0x3
-#define nSRC_AND_DEST   0x4
-#define DEST                    0x5
-#define SRC_XOR_DEST    0x6
-#define SRC_OR_DEST             0x7
-#define SRC_NOR_DEST    0x8
-#define SRC_XNOR_DEST   0x9
-#define nDEST                   0xA
-#define SRC_OR_nDEST    0xB
-#define nSRC                    0xC
-#define nSRC_OR_DEST    0xD
-#define SRC_NAND_DEST   0xE
-#define DEST_ONE                0xF
-
-#define SWAP(A) ((A>>8) | ((A&0xff) <<8))
-
-#if 0
-#define outb(a,d) *(char *)(a)=(d)
-#define outw(a,d) *(unsigned short *)a=d
-#endif
-
-
-void dn_video_setup(char *options, int *ints);
-
-/* frame buffer operations */
-
-static int dn_fb_open(int fbidx);
-static int dn_fb_release(int fbidx);
-static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con);
-static int dn_fb_get_var(struct fb_var_screeninfo *var, int con);
-static int dn_fb_set_var(struct fb_var_screeninfo *var, int isactive);
-static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con);
-static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con);
-static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con);
-static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
-		       unsigned long arg, int con);
-
-static int dnfbcon_switch(int con);
-static int dnfbcon_updatevar(int con);
-static void dnfbcon_blank(int blank);
-
-static void dn_fb_set_disp(int con);
-
-static struct display disp[MAX_NR_CONSOLES];
-static struct fb_info fb_info;
-static struct fb_ops dn_fb_ops = { 
-	dn_fb_open,dn_fb_release, dn_fb_get_fix, dn_fb_get_var, dn_fb_set_var,
-	dn_fb_get_cmap, dn_fb_set_cmap, dn_fb_pan_display, dn_fb_ioctl
-};
-
-static int currcon=0;
-
-#define NUM_TOTAL_MODES 1
-struct fb_var_screeninfo dn_fb_predefined[] = {
-
-	{ 0, },
-
-};
-
-static char dn_fb_name[]="Apollo ";
-
-static int dn_fb_open(int fbidx)
-{
-        /*
-         * Nothing, only a usage count for the moment
-         */
-
-        MOD_INC_USE_COUNT;
-        return(0);
-}
-
-static int dn_fb_release(int fbidx)
-{
-        MOD_DEC_USE_COUNT;
-        return(0);
-}
-
-static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con) {
-
-	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
-	strcpy(fix->id,"Apollo Mono");
-	fix->smem_start=(char*)(FRAME_BUFFER_START+IO_BASE);
-	fix->smem_len=FRAME_BUFFER_LEN;
-	fix->type=FB_TYPE_PACKED_PIXELS;
-	fix->type_aux=0;
-	fix->visual=FB_VISUAL_MONO10;
-	fix->xpanstep=0;
-	fix->ypanstep=0;
-	fix->ywrapstep=0;
-        fix->line_length=256;
-
-	return 0;
-
-}
-        
-static int dn_fb_get_var(struct fb_var_screeninfo *var, int con) {
-		
-	var->xres=1280;
-	var->yres=1024;
-	var->xres_virtual=2048;
-	var->yres_virtual=1024;
-	var->xoffset=0;
-	var->yoffset=0;
-	var->bits_per_pixel=1;
-	var->grayscale=0;
-	var->nonstd=0;
-	var->activate=0;
-	var->height=-1;
-	var->width=-1;
-	var->pixclock=0;
-	var->left_margin=0;
-	var->right_margin=0;
-	var->hsync_len=0;
-	var->vsync_len=0;
-	var->sync=0;
-	var->vmode=FB_VMODE_NONINTERLACED;
-	var->accel=FB_ACCEL_NONE;
-
-	return 0;
-
-}
-
-static int dn_fb_set_var(struct fb_var_screeninfo *var, int isactive) {
-
-        printk("fb_set_var\n");
-	if(var->xres!=1280) 
-		return -EINVAL;
-	if(var->yres!=1024)
-		return -EINVAL;
-	if(var->xres_virtual!=2048)
-		return -EINVAL;
-	if(var->yres_virtual!=1024)
-		return -EINVAL;
-	if(var->xoffset!=0)
-		return -EINVAL;
-	if(var->yoffset!=0)
-		return -EINVAL;
-	if(var->bits_per_pixel!=1)
-		return -EINVAL;
-	if(var->grayscale!=0)
-		return -EINVAL;
-	if(var->nonstd!=0)
-		return -EINVAL;
-	if(var->activate!=0)
-		return -EINVAL;
-	if(var->pixclock!=0)
-		return -EINVAL;
-	if(var->left_margin!=0)
-		return -EINVAL;
-	if(var->right_margin!=0)
-		return -EINVAL;
-	if(var->hsync_len!=0)
-		return -EINVAL;
-	if(var->vsync_len!=0)
-		return -EINVAL;
-	if(var->sync!=0)
-		return -EINVAL;
-	if(var->vmode!=FB_VMODE_NONINTERLACED)
-		return -EINVAL;
-	if(var->accel!=FB_ACCEL_NONE)
-		return -EINVAL;
-
-	return 0;
-
-}
-
-static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con) {
-
-	printk("get cmap not supported\n");
-
-	return -EINVAL;
-}
-
-static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con) {
-
-	printk("set cmap not supported\n");
-
-	return -EINVAL;
-
-}
-
-static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con) {
-
-	printk("panning not supported\n");
-
-	return -EINVAL;
-
-}
-
-static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
-		    unsigned long arg, int con) {
-
-	printk("no IOCTLs as of yet.\n");
-
-	return -EINVAL;
-
-}
-
-static void dn_fb_set_disp(int con) {
-
-  struct fb_fix_screeninfo fix;
-
-  dn_fb_get_fix(&fix,con);
-  if(con==-1) 
-    con=0;
-
-   disp[con].screen_base = (u_char *)fix.smem_start;
-printk("screenbase: %p\n",fix.smem_start);
-   disp[con].visual = fix.visual;
-   disp[con].type = fix.type;
-   disp[con].type_aux = fix.type_aux;
-   disp[con].ypanstep = fix.ypanstep;
-   disp[con].ywrapstep = fix.ywrapstep;
-   disp[con].can_soft_blank = 1;
-   disp[con].inverse = 0;
-   disp[con].line_length = fix.line_length;
-}
-  
-unsigned long dn_fb_init(unsigned long mem_start) {
-
-	int err;
-       
-printk("dn_fb_init\n");
-
-	fb_info.changevar=NULL;
-	strcpy(&fb_info.modename[0],dn_fb_name);
-	fb_info.fontname[0]=0;
-	fb_info.disp=disp;
-	fb_info.switch_con=&dnfbcon_switch;
-	fb_info.updatevar=&dnfbcon_updatevar;
-	fb_info.blank=&dnfbcon_blank;	
-	fb_info.node = -1;
-	fb_info.fbops = &dn_fb_ops;
-	fb_info.fbvar = dn_fb_predefined;
-	fb_info.fbvar_num = NUM_TOTAL_MODES;
-	
-printk("dn_fb_init: register\n");
-	err=register_framebuffer(&fb_info);
-	if(err < 0) {
-		panic("unable to register apollo frame buffer\n");
-	}
- 
-	/* now we have registered we can safely setup the hardware */
-
-        outb(RESET_CREG, AP_CONTROL_3A);
-        outw(0x0, AP_WRITE_ENABLE);
-        outb(NORMAL_MODE,AP_CONTROL_0); 
-        outb((AD_BLT | DST_EQ_SRC | NORM_CREG1), AP_CONTROL_1);
-        outb(S_DATA_PLN, AP_CONTROL_2);
-        outw(SWAP(0x3),AP_ROP_1);
-
-        printk("apollo frame buffer alive and kicking !\n");
-
-	
-        dn_fb_get_var(&disp[0].var,0);
-
-	dn_fb_set_disp(-1);
-
-	return mem_start;
-
-}	
-
-	
-static int dnfbcon_switch(int con) { 
-
-	currcon=con;
-	
-	return 0;
-
-}
-
-static int dnfbcon_updatevar(int con) {
-
-	return 0;
-
-}
-
-static void dnfbcon_blank(int blank) {
-
-	printk("dnfbcon_blank: %d\n",blank);
-	if(blank)  {
-        	outb(0, AP_CONTROL_3A);
-		outb((AD_BLT | DST_EQ_SRC | NORM_CREG1) & ~ENAB_DISP,
-		     AP_CONTROL_1);
-	}
-	else {
-	        outb(1, AP_CONTROL_3A);
-        	outb((AD_BLT | DST_EQ_SRC | NORM_CREG1), AP_CONTROL_1);
-	}
-
-	return ;
-
-}
-
-void dn_video_setup(char *options, int *ints) {
-	
-	return;
-
-}
-
diff -u --recursive --new-file v2.1.90/linux/drivers/video/dnfb.c linux/drivers/video/dnfb.c
--- v2.1.90/linux/drivers/video/dnfb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/dnfb.c	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,381 @@
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/config.h>
+#include <linux/interrupt.h>
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/apollohw.h>
+#include <linux/fb.h>
+#include <linux/module.h>
+
+#include "fbcon-mfb.h"
+
+
+/* apollo video HW definitions */
+
+/*
+ * Control Registers.   IOBASE + $x
+ *
+ * Note: these are the Memory/IO BASE definitions for a mono card set to the
+ * alternate address
+ *
+ * Control 3A and 3B serve identical functions except that 3A
+ * deals with control 1 and 3b deals with Color LUT reg.
+ */
+
+#define AP_IOBASE       0x5d80          /* Base address of 1 plane board. */
+#define AP_STATUS       0x5d80          /* Status register.  Read */
+#define AP_WRITE_ENABLE 0x5d80          /* Write Enable Register Write */
+#define AP_DEVICE_ID    0x5d81          /* Device ID Register. Read */
+#define AP_ROP_1        0x5d82          /* Raster Operation reg. Write Word */
+#define AP_DIAG_MEM_REQ 0x5d84          /* Diagnostic Memory Request. Write Word */
+#define AP_CONTROL_0    0x5d88          /* Control Register 0.  Read/Write */
+#define AP_CONTROL_1    0x5d8a          /* Control Register 1.  Read/Write */
+#define AP_CONTROL_3A   0x5d8e          /* Control Register 3a. Read/Write */
+#define AP_CONTROL_2    0x5d8c          /* Control Register 2. Read/Write */
+
+
+#define FRAME_BUFFER_START 0x0FA0000
+#define FRAME_BUFFER_LEN 0x40000
+
+/* CREG 0 */
+#define VECTOR_MODE 0x40 /* 010x.xxxx */
+#define DBLT_MODE   0x80 /* 100x.xxxx */
+#define NORMAL_MODE 0xE0 /* 111x.xxxx */
+#define SHIFT_BITS  0x1F /* xxx1.1111 */
+        /* other bits are Shift value */
+
+/* CREG 1 */
+#define AD_BLT      0x80 /* 1xxx.xxxx */
+#define NORMAL      0x80 /* 1xxx.xxxx */   /* What is happening here ?? */
+#define INVERSE     0x00 /* 0xxx.xxxx */   /* Clearing this reverses the screen */
+#define PIX_BLT     0x00 /* 0xxx.xxxx */
+
+#define AD_HIBIT        0x40 /* xIxx.xxxx */
+
+#define ROP_EN          0x10 /* xxx1.xxxx */
+#define DST_EQ_SRC      0x00 /* xxx0.xxxx */
+#define nRESET_SYNC     0x08 /* xxxx.1xxx */
+#define SYNC_ENAB       0x02 /* xxxx.xx1x */
+
+#define BLANK_DISP      0x00 /* xxxx.xxx0 */
+#define ENAB_DISP       0x01 /* xxxx.xxx1 */
+
+#define NORM_CREG1      (nRESET_SYNC | SYNC_ENAB | ENAB_DISP) /* no reset sync */
+
+/* CREG 2 */
+
+/*
+ * Following 3 defines are common to 1, 4 and 8 plane.
+ */
+
+#define S_DATA_1s   0x00 /* 00xx.xxxx */ /* set source to all 1's -- vector drawing */
+#define S_DATA_PIX  0x40 /* 01xx.xxxx */ /* takes source from ls-bits and replicates over 16 bits */
+#define S_DATA_PLN  0xC0 /* 11xx.xxxx */ /* normal, each data access =16-bits in
+ one plane of image mem */
+
+/* CREG 3A/CREG 3B */
+#       define RESET_CREG 0x80 /* 1000.0000 */
+
+/* ROP REG  -  all one nibble */
+/*      ********* NOTE : this is used r0,r1,r2,r3 *********** */
+#define ROP(r2,r3,r0,r1) ( (U_SHORT)((r0)|((r1)<<4)|((r2)<<8)|((r3)<<12)) )
+#define DEST_ZERO               0x0
+#define SRC_AND_DEST    0x1
+#define SRC_AND_nDEST   0x2
+#define SRC                             0x3
+#define nSRC_AND_DEST   0x4
+#define DEST                    0x5
+#define SRC_XOR_DEST    0x6
+#define SRC_OR_DEST             0x7
+#define SRC_NOR_DEST    0x8
+#define SRC_XNOR_DEST   0x9
+#define nDEST                   0xA
+#define SRC_OR_nDEST    0xB
+#define nSRC                    0xC
+#define nSRC_OR_DEST    0xD
+#define SRC_NAND_DEST   0xE
+#define DEST_ONE                0xF
+
+#define SWAP(A) ((A>>8) | ((A&0xff) <<8))
+
+#if 0
+#define outb(a,d) *(char *)(a)=(d)
+#define outw(a,d) *(unsigned short *)a=d
+#endif
+
+
+/* frame buffer operations */
+
+static int dnfb_open(struct fb_info *info);
+static int dnfb_release(struct fb_info *info);
+static int dnfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			struct fb_info *info);
+static int dnfb_get_var(struct fb_var_screeninfo *var, int con,
+			struct fb_info *info);
+static int dnfb_set_var(struct fb_var_screeninfo *var, int con,
+			struct fb_info *info);
+static int dnfb_get_cmap(struct fb_cmap *cmap,int kspc,int con,
+			 struct fb_info *info);
+static int dnfb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
+			 struct fb_info *info);
+static int dnfb_pan_display(struct fb_var_screeninfo *var, int con,
+			    struct fb_info *info);
+static int dnfb_ioctl(struct inode *inode, struct file *file,
+		      unsigned int cmd, unsigned long arg, int con,
+		      struct fb_info *info);
+
+static int dnfbcon_switch(int con, struct fb_info *info);
+static int dnfbcon_updatevar(int con, struct fb_info *info);
+static void dnfbcon_blank(int blank, struct fb_info *info);
+
+static void dnfb_set_disp(int con, struct fb_info *info);
+
+static struct display disp[MAX_NR_CONSOLES];
+static struct fb_info fb_info;
+static struct fb_ops dnfb_ops = { 
+	dnfb_open,dnfb_release, dnfb_get_fix, dnfb_get_var, dnfb_set_var,
+	dnfb_get_cmap, dnfb_set_cmap, dnfb_pan_display, NULL, dnfb_ioctl
+};
+
+static int currcon=0;
+
+static char dnfb_name[]="Apollo";
+
+static int dnfb_open(struct fb_info *info)
+{
+        /*
+         * Nothing, only a usage count for the moment
+         */
+
+        MOD_INC_USE_COUNT;
+        return(0);
+}
+
+static int dnfb_release(struct fb_info *info)
+{
+        MOD_DEC_USE_COUNT;
+        return(0);
+}
+
+static int dnfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			struct fb_info *info)
+{
+	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+	strcpy(fix->id,"Apollo Mono");
+	fix->smem_start=(char*)(FRAME_BUFFER_START+IO_BASE);
+	fix->smem_len=FRAME_BUFFER_LEN;
+	fix->type=FB_TYPE_PACKED_PIXELS;
+	fix->type_aux=0;
+	fix->visual=FB_VISUAL_MONO10;
+	fix->xpanstep=0;
+	fix->ypanstep=0;
+	fix->ywrapstep=0;
+        fix->line_length=256;
+
+	return 0;
+
+}
+        
+static int dnfb_get_var(struct fb_var_screeninfo *var, int con,
+			struct fb_info *info)
+{
+	var->xres=1280;
+	var->yres=1024;
+	var->xres_virtual=2048;
+	var->yres_virtual=1024;
+	var->xoffset=0;
+	var->yoffset=0;
+	var->bits_per_pixel=1;
+	var->grayscale=0;
+	var->nonstd=0;
+	var->activate=0;
+	var->height=-1;
+	var->width=-1;
+	var->pixclock=0;
+	var->left_margin=0;
+	var->right_margin=0;
+	var->hsync_len=0;
+	var->vsync_len=0;
+	var->sync=0;
+	var->vmode=FB_VMODE_NONINTERLACED;
+	var->accel=FB_ACCEL_NONE;
+
+	return 0;
+
+}
+
+static int dnfb_set_var(struct fb_var_screeninfo *var, int con,
+			struct fb_info *info)
+{
+	if(var->xres!=1280) 
+		return -EINVAL;
+	if(var->yres!=1024)
+		return -EINVAL;
+	if(var->xres_virtual!=2048)
+		return -EINVAL;
+	if(var->yres_virtual!=1024)
+		return -EINVAL;
+	if(var->xoffset!=0)
+		return -EINVAL;
+	if(var->yoffset!=0)
+		return -EINVAL;
+	if(var->bits_per_pixel!=1)
+		return -EINVAL;
+	if(var->grayscale!=0)
+		return -EINVAL;
+	if(var->nonstd!=0)
+		return -EINVAL;
+	if(var->activate!=0)
+		return -EINVAL;
+	if(var->pixclock!=0)
+		return -EINVAL;
+	if(var->left_margin!=0)
+		return -EINVAL;
+	if(var->right_margin!=0)
+		return -EINVAL;
+	if(var->hsync_len!=0)
+		return -EINVAL;
+	if(var->vsync_len!=0)
+		return -EINVAL;
+	if(var->sync!=0)
+		return -EINVAL;
+	if(var->vmode!=FB_VMODE_NONINTERLACED)
+		return -EINVAL;
+	if(var->accel!=FB_ACCEL_NONE)
+		return -EINVAL;
+
+	return 0;
+
+}
+
+static int dnfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			 struct fb_info *info)
+{
+	printk("get cmap not supported\n");
+
+	return -EINVAL;
+}
+
+static int dnfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			 struct fb_info *info)
+{
+	printk("set cmap not supported\n");
+
+	return -EINVAL;
+
+}
+
+static int dnfb_pan_display(struct fb_var_screeninfo *var, int con,
+			    struct fb_info *info)
+{
+	printk("panning not supported\n");
+
+	return -EINVAL;
+
+}
+
+static int dnfb_ioctl(struct inode *inode, struct file *file,
+		      unsigned int cmd, unsigned long arg, int con,
+		      struct fb_info *info)
+{
+	return -EINVAL;
+}
+
+static void dnfb_set_disp(int con, struct fb_info *info)
+{
+  struct fb_fix_screeninfo fix;
+
+  dnfb_get_fix(&fix, con, info);
+  if(con==-1) 
+    con=0;
+
+   disp[con].screen_base = (u_char *)fix.smem_start;
+   disp[con].visual = fix.visual;
+   disp[con].type = fix.type;
+   disp[con].type_aux = fix.type_aux;
+   disp[con].ypanstep = fix.ypanstep;
+   disp[con].ywrapstep = fix.ywrapstep;
+   disp[con].can_soft_blank = 1;
+   disp[con].inverse = 0;
+   disp[con].line_length = fix.line_length;
+#ifdef CONFIG_FBCON_MFB
+   disp[con].dispsw = &fbcon_mfb;
+#else
+   disp[con].dispsw = NULL;
+#endif
+}
+  
+unsigned long dnfb_init(unsigned long mem_start)
+{
+	int err;
+       
+	fb_info.changevar=NULL;
+	strcpy(&fb_info.modename[0],dnfb_name);
+	fb_info.fontname[0]=0;
+	fb_info.disp=disp;
+	fb_info.switch_con=&dnfbcon_switch;
+	fb_info.updatevar=&dnfbcon_updatevar;
+	fb_info.blank=&dnfbcon_blank;	
+	fb_info.node = -1;
+	fb_info.fbops = &dnfb_ops;
+	
+	err=register_framebuffer(&fb_info);
+	if(err < 0) {
+		panic("unable to register apollo frame buffer\n");
+	}
+ 
+	/* now we have registered we can safely setup the hardware */
+
+        outb(RESET_CREG, AP_CONTROL_3A);
+        outw(0x0, AP_WRITE_ENABLE);
+        outb(NORMAL_MODE,AP_CONTROL_0); 
+        outb((AD_BLT | DST_EQ_SRC | NORM_CREG1), AP_CONTROL_1);
+        outb(S_DATA_PLN, AP_CONTROL_2);
+        outw(SWAP(0x3),AP_ROP_1);
+
+        printk("fb%d: apollo frame buffer alive and kicking !\n",
+	       GET_FB_IDX(fb_info.node));
+
+	
+        dnfb_get_var(&disp[0].var, 0, &fb_info);
+
+	dnfb_set_disp(-1, &fb_info);
+
+	return mem_start;
+
+}	
+
+	
+static int dnfbcon_switch(int con, struct fb_info *info)
+{ 
+	currcon=con;
+	
+	return 0;
+
+}
+
+static int dnfbcon_updatevar(int con, struct fb_info *info)
+{
+	return 0;
+}
+
+static void dnfbcon_blank(int blank, struct fb_info *info)
+{
+	if(blank)  {
+        	outb(0, AP_CONTROL_3A);
+		outb((AD_BLT | DST_EQ_SRC | NORM_CREG1) & ~ENAB_DISP,
+		     AP_CONTROL_1);
+	}
+	else {
+	        outb(1, AP_CONTROL_3A);
+        	outb((AD_BLT | DST_EQ_SRC | NORM_CREG1), AP_CONTROL_1);
+	}
+}
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcmap.c linux/drivers/video/fbcmap.c
--- v2.1.90/linux/drivers/video/fbcmap.c	Tue Sep 16 02:29:26 1997
+++ linux/drivers/video/fbcmap.c	Sat Mar 21 11:12:49 1998
@@ -35,45 +35,45 @@
 #define CNVT_FROMHW(val,width)	(((width) ? ((((val)<<16)-(val)) / \
 					((1<<(width))-1)) : 0))
 
-static u_short red2[] = {
+static u16 red2[] = {
     0x0000, 0xaaaa
 };
-static u_short green2[] = {
+static u16 green2[] = {
     0x0000, 0xaaaa
 };
-static u_short blue2[] = {
+static u16 blue2[] = {
     0x0000, 0xaaaa
-};                                                  
- 
-static u_short red4[] = {
+};
+
+static u16 red4[] = {
     0x0000, 0xaaaa, 0x5555, 0xffff
 };
-static u_short green4[] = {
+static u16 green4[] = {
     0x0000, 0xaaaa, 0x5555, 0xffff
 };
-static u_short blue4[] = {
+static u16 blue4[] = {
     0x0000, 0xaaaa, 0x5555, 0xffff
 };
- 
-static u_short red8[] = {
+
+static u16 red8[] = {
     0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa
 };
-static u_short green8[] = {
+static u16 green8[] = {
     0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0xaaaa, 0xaaaa
 };
-static u_short blue8[] = {
+static u16 blue8[] = {
     0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa
 };
- 
-static u_short red16[] = {
+
+static u16 red16[] = {
     0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa,
     0x5555, 0x5555, 0x5555, 0x5555, 0xffff, 0xffff, 0xffff, 0xffff
 };
-static u_short green16[] = {
+static u16 green16[] = {
     0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0xaaaa, 0xaaaa,
     0x5555, 0x5555, 0xffff, 0xffff, 0x5555, 0x5555, 0xffff, 0xffff
 };
-static u_short blue16[] = {
+static u16 blue16[] = {
     0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa,
     0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff
 };
@@ -83,10 +83,10 @@
 };
 static struct fb_cmap default_8_colors = {
     0, 8, red8, green8, blue8, NULL
-};                                  
+};
 static struct fb_cmap default_4_colors = {
     0, 4, red4, green4, blue4, NULL
-};             
+};
 static struct fb_cmap default_16_colors = {
     0, 16, red16, green16, blue16, NULL
 };
@@ -98,32 +98,32 @@
 
 int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp)
 {
-    int size = len*sizeof(u_short);
-		    
+    int size = len*sizeof(u16);
+
     if (cmap->len != len) {
-	if (cmap->red)                                         
+	if (cmap->red)
 	    kfree(cmap->red);
 	if (cmap->green)
 	    kfree(cmap->green);
 	if (cmap->blue)
 	    kfree(cmap->blue);
 	if (cmap->transp)
-	    kfree(cmap->transp);            
-	cmap->red = cmap->green = cmap->blue = cmap->transp = NULL;    
-	cmap->len = 0;                          
-	if (!len) 
-	    return 0;                                      
+	    kfree(cmap->transp);
+	cmap->red = cmap->green = cmap->blue = cmap->transp = NULL;
+	cmap->len = 0;
+	if (!len)
+	    return 0;
 	if (!(cmap->red = kmalloc(size, GFP_ATOMIC)))
 	    return -1;
-	if (!(cmap->green = kmalloc(size, GFP_ATOMIC)))                 
+	if (!(cmap->green = kmalloc(size, GFP_ATOMIC)))
 	    return -1;
 	if (!(cmap->blue = kmalloc(size, GFP_ATOMIC)))
-	    return -1;                  
-	if (transp) {                            
+	    return -1;
+	if (transp) {
 	    if (!(cmap->transp = kmalloc(size, GFP_ATOMIC)))
-		return -1;                                      
-	} else                  
-	    cmap->transp = NULL;                                    
+		return -1;
+	} else
+	    cmap->transp = NULL;
     }
     cmap->start = 0;
     cmap->len = len;
@@ -150,12 +150,12 @@
 	size = from->len-fromoff;
     if (size < 0)
 	return;
-    size *= sizeof(u_short);                         
+    size *= sizeof(u16);
     memcpy_fs(fsfromto, to->red+tooff, from->red+fromoff, size);
     memcpy_fs(fsfromto, to->green+tooff, from->green+fromoff, size);
     memcpy_fs(fsfromto, to->blue+tooff, from->blue+fromoff, size);
     if (from->transp && to->transp)
-	memcpy_fs(fsfromto, to->transp+tooff, from->transp+fromoff, size);                              
+	memcpy_fs(fsfromto, to->transp+tooff, from->transp+fromoff, size);
 }
 
 
@@ -164,10 +164,11 @@
      */
 
 int fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, int kspc,
-    	    	int (*getcolreg)(u_int, u_int *, u_int *, u_int *, u_int *))
+    	    	int (*getcolreg)(u_int, u_int *, u_int *, u_int *, u_int *,
+				 struct fb_info *), struct fb_info *info)
 {
     int i, start;
-    u_short *red, *green, *blue, *transp;
+    u16 *red, *green, *blue, *transp;
     u_int hred, hgreen, hblue, htransp;
 
     red = cmap->red;
@@ -178,7 +179,7 @@
     if (start < 0)
 	return -EINVAL;
     for (i = 0; i < cmap->len; i++) {
-	if (getcolreg(start++, &hred, &hgreen, &hblue, &htransp))
+	if (getcolreg(start++, &hred, &hgreen, &hblue, &htransp, info))
 	    return 0;
 	hred = CNVT_FROMHW(hred, var->red.length);
 	hgreen = CNVT_FROMHW(hgreen, var->green.length);
@@ -212,10 +213,11 @@
      */
 
 int fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, int kspc,
-    	    	int (*setcolreg)(u_int, u_int, u_int, u_int, u_int))
+    	    	int (*setcolreg)(u_int, u_int, u_int, u_int, u_int,
+				 struct fb_info *), struct fb_info *info)
 {
     int i, start;
-    u_short *red, *green, *blue, *transp;
+    u16 *red, *green, *blue, *transp;
     u_int hred, hgreen, hblue, htransp;
 
     red = cmap->red;
@@ -250,7 +252,7 @@
 	blue++;
 	if (transp)
 	    transp++;
-	if (setcolreg(start++, hred, hgreen, hblue, htransp))
+	if (setcolreg(start++, hred, hgreen, hblue, htransp, info))
 	    return 0;
     }
     return 0;
@@ -261,22 +263,15 @@
      *  Get the default colormap for a specific screen depth
      */
 
-struct fb_cmap *fb_default_cmap(int bpp)
+struct fb_cmap *fb_default_cmap(int len)
 {
-    switch (bpp) {                                                          
-	case 1:                                                       
-	    return &default_2_colors;
-	    break;
-	case 2:                                            
-	    return &default_4_colors;                             
-	    break;                                   
-	case 3:
-	    return &default_8_colors;                              
-	    break;
-	default:
-	    return &default_16_colors;
-	    break;                                                  
-    }
+    if (len <= 2)
+	return &default_2_colors;
+    if (len <= 4)
+	return &default_4_colors;
+    if (len <= 8)
+	return &default_8_colors;
+    return &default_16_colors;
 }
 
 
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-afb.c linux/drivers/video/fbcon-afb.c
--- v2.1.90/linux/drivers/video/fbcon-afb.c	Mon Feb 23 18:12:10 1998
+++ linux/drivers/video/fbcon-afb.c	Sat Mar 21 11:12:49 1998
@@ -13,132 +13,242 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
+#include <linux/config.h>
 #include <linux/fb.h>
 
 #include "fbcon.h"
+#include "fbcon-afb.h"
 
 
     /*
-     *  Prototypes
-     */
-
-static int open_afb(struct display *p);
-static void release_afb(void);
-static void bmove_afb(struct display *p, int sy, int sx, int dy, int dx,
-	              int height, int width);
-static void clear_afb(struct vc_data *conp, struct display *p, int sy, int sx,
-	              int height, int width);
-static void putc_afb(struct vc_data *conp, struct display *p, int c, int yy,
-	             int xx);
-static void putcs_afb(struct vc_data *conp, struct display *p, const char *s,
-	              int count, int yy, int xx);
-static void rev_char_afb(struct display *p, int xx, int yy);
-
-
-    /*
-     *  `switch' for the low level operations
+     *  Bitplanes à la Amiga
      */
 
-static struct display_switch dispsw_afb = {
-    open_afb, release_afb, bmove_afb, clear_afb, putc_afb, putcs_afb,
-    rev_char_afb
+static u8 expand_table[1024] = {
+    /*  bg = fg = 0 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    /* bg = 0, fg = 1 */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+    /* bg = 1, fg = 0 */
+    0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
+    0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
+    0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8,
+    0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0,
+    0xdf, 0xde, 0xdd, 0xdc, 0xdb, 0xda, 0xd9, 0xd8,
+    0xd7, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xd0,
+    0xcf, 0xce, 0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc8,
+    0xc7, 0xc6, 0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0,
+    0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8,
+    0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0,
+    0xaf, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9, 0xa8,
+    0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1, 0xa0,
+    0x9f, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98,
+    0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90,
+    0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88,
+    0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80,
+    0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78,
+    0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70,
+    0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68,
+    0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61, 0x60,
+    0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58,
+    0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50,
+    0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48,
+    0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40,
+    0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38,
+    0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30,
+    0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28,
+    0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20,
+    0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
+    0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    /* bg = fg = 1 */
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 };
 
-    /*
-     *  Bitplanes à la Amiga
-     */
-
-static int open_afb(struct display *p)
+void fbcon_afb_setup(struct display *p)
 {
-    if (p->type != FB_TYPE_PLANES)
-	return -EINVAL;
-
     if (p->line_length)
 	p->next_line = p->line_length;
     else
 	p->next_line = p->var.xres_virtual>>3;
     p->next_plane = p->var.yres_virtual*p->next_line;
-    MOD_INC_USE_COUNT;
-    return 0;
 }
 
-static void release_afb(void)
+void fbcon_afb_bmove(struct display *p, int sy, int sx, int dy, int dx,
+		     int height, int width)
 {
-    MOD_DEC_USE_COUNT;
-}
-
-static void bmove_afb(struct display *p, int sy, int sx, int dy, int dx,
-	              int height, int width)
-{
-    u_char *src, *dest, *src0, *dest0;
-    u_int i, rows;
+    u8 *src, *dest, *src0, *dest0;
+    u_short i, j;
 
     if (sx == 0 && dx == 0 && width == p->next_line) {
 	src = p->screen_base+sy*p->fontheight*width;
 	dest = p->screen_base+dy*p->fontheight*width;
-	for (i = p->var.bits_per_pixel; i--;) {
+	i = p->var.bits_per_pixel;
+	do {
 	    mymemmove(dest, src, height*p->fontheight*width);
 	    src += p->next_plane;
 	    dest += p->next_plane;
-	}
+	} while (--i);
     } else if (dy <= sy) {
 	src0 = p->screen_base+sy*p->fontheight*p->next_line+sx;
 	dest0 = p->screen_base+dy*p->fontheight*p->next_line+dx;
-	for (i = p->var.bits_per_pixel; i--;) {
+	i = p->var.bits_per_pixel;
+	do {
 	    src = src0;
 	    dest = dest0;
-	    for (rows = height*p->fontheight; rows--;) {
+	    j = height*p->fontheight;
+	    do {
 	        mymemmove(dest, src, width);
 	        src += p->next_line;
 	        dest += p->next_line;
-	    }
+	    } while (--j);
 	    src0 += p->next_plane;
 	    dest0 += p->next_plane;
-	}
+	} while (--i);
     } else {
 	src0 = p->screen_base+(sy+height)*p->fontheight*p->next_line+sx;
 	dest0 = p->screen_base+(dy+height)*p->fontheight*p->next_line+dx;
-	for (i = p->var.bits_per_pixel; i--;) {
+	i = p->var.bits_per_pixel;
+	do {
 	    src = src0;
 	    dest = dest0;
-	    for (rows = height*p->fontheight; rows--;) {
+	    j = height*p->fontheight;
+	    do {
 	        src -= p->next_line;
 	        dest -= p->next_line;
 	        mymemmove(dest, src, width);
-	    }
+	    } while (--j);
 	    src0 += p->next_plane;
 	    dest0 += p->next_plane;
-	}
+	} while (--i);
     }
 }
 
-static void clear_afb(struct vc_data *conp, struct display *p, int sy, int sx,
-	              int height, int width)
+void fbcon_afb_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+		     int height, int width)
 {
-    u_char *dest, *dest0;
-    u_int i, rows;
+    u8 *dest, *dest0;
+    u_short i, j;
     int bg;
 
     dest0 = p->screen_base+sy*p->fontheight*p->next_line+sx;
 
     bg = attr_bgcol_ec(p,conp);
-    for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) {
+    i = p->var.bits_per_pixel;
+    do {
 	dest = dest0;
-	for (rows = height*p->fontheight; rows--; dest += p->next_line)
+	j = height*p->fontheight;
+	do {
 	    if (bg & 1)
 	        mymemset(dest, width);
 	    else
 	        mymemclear(dest, width);
+	    dest += p->next_line;
+	} while (--j);
 	bg >>= 1;
-    }
+	dest0 += p->next_plane;
+    } while (--i);
 }
 
-static void putc_afb(struct vc_data *conp, struct display *p, int c, int yy,
-	             int xx)
+void fbcon_afb_putc(struct vc_data *conp, struct display *p, int c, int yy,
+		    int xx)
 {
-    u_char *dest, *dest0, *cdat, *cdat0;
-    u_int rows, i;
-    u_char d;
+    u8 *dest, *dest0, *cdat, *cdat0, *expand;
+    u_short i, j;
     int fg, bg;
 
     c &= 0xff;
@@ -148,25 +258,24 @@
     fg = attr_fgcol(p,conp);
     bg = attr_bgcol(p,conp);
 
-    for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) {
+    i = p->var.bits_per_pixel;
+    do {
 	dest = dest0;
 	cdat = cdat0;
-	for (rows = p->fontheight; rows--; dest += p->next_line) {
-	    d = *cdat++;
-	    if (bg & 1)
-	        if (fg & 1)
-	            *dest = 0xff;
-	        else
-	            *dest = ~d;
-	    else
-	        if (fg & 1)
-	            *dest = d;
-	        else
-	            *dest = 0x00;
-	}
+	expand = expand_table;
+	if (bg & 1)
+	    expand += 512;
+	if (fg & 1)
+	    expand += 256;
+	j = p->fontheight;
+	do {
+	    *dest = expand[*cdat++];
+	    dest += p->next_line;
+	} while (--j);
 	bg >>= 1;
 	fg >>= 1;
-    }
+	dest0 += p->next_plane;
+    } while (--i);
 }
 
     /*
@@ -174,14 +283,13 @@
      *  (cfr. fbcon_putcs_ilbm())
      */
 
-static void putcs_afb(struct vc_data *conp, struct display *p, const char *s,
-	              int count, int yy, int xx)
+void fbcon_afb_putcs(struct vc_data *conp, struct display *p, const char *s,
+		     int count, int yy, int xx)
 {
-    u_char *dest, *dest0, *dest1;
-    u_char *cdat1, *cdat2, *cdat3, *cdat4, *cdat10, *cdat20, *cdat30, *cdat40;
-    u_int rows, i;
-    u_char c1, c2, c3, c4;
-    u_long d;
+    u8 *dest, *dest0, *dest1, *expand;
+    u8 *cdat1, *cdat2, *cdat3, *cdat4, *cdat10, *cdat20, *cdat30, *cdat40;
+    u_short i, j;
+    u8 c1, c2, c3, c4;
     int fg0, bg0, fg, bg;
 
     dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
@@ -198,26 +306,25 @@
 	    fg = fg0;
 	    bg = bg0;
 
-	    for (i = p->var.bits_per_pixel; i--; dest1 += p->next_plane) {
+	    i = p->var.bits_per_pixel;
+	    do {
 	        dest = dest1;
 	        cdat1 = cdat10;
-	        for (rows = p->fontheight; rows--; dest += p->next_line) {
-	            d = *cdat1++;
-	            if (bg & 1)
-	                if (fg & 1)
-	                    *dest = 0xff;
-	                else
-	                    *dest = ~d;
-	            else
-	                if (fg & 1)
-	                    *dest = d;
-	                else
-	                    *dest = 0x00;
-	        }
+		expand = expand_table;
+		if (bg & 1)
+		    expand += 512;
+		if (fg & 1)
+		    expand += 256;
+		j = p->fontheight;
+		do {
+		    *dest = expand[*cdat1++];
+		    dest += p->next_line;
+	        } while (--j);
 	        bg >>= 1;
 	        fg >>= 1;
-	    }
-	} else {	/* Fast version */
+		dest1 += p->next_plane;
+	    } while (--i);
+	} else {			/* Fast version */
 	    c1 = s[0];
 	    c2 = s[1];
 	    c3 = s[2];
@@ -231,28 +338,39 @@
 	    fg = fg0;
 	    bg = bg0;
 
-	    for (i = p->var.bits_per_pixel; i--; dest1 += p->next_plane) {
+	    i = p->var.bits_per_pixel;
+	    do {
 	        dest = dest1;
 	        cdat1 = cdat10;
 	        cdat2 = cdat20;
 	        cdat3 = cdat30;
 	        cdat4 = cdat40;
-	        for (rows = p->fontheight; rows--; dest += p->next_line) {
-	            d = *cdat1++<<24 | *cdat2++<<16 | *cdat3++<<8 | *cdat4++;
-	            if (bg & 1)
-	                if (fg & 1)
-	                    *(u_long *)dest = 0xffffffff;
-	                else
-	                    *(u_long *)dest = ~d;
-	            else
-	                if (fg & 1)
-	                    *(u_long *)dest = d;
-	                else
-	                    *(u_long *)dest = 0x00000000;
-	        }
+		expand = expand_table;
+		if (bg & 1)
+		    expand += 512;
+		if (fg & 1)
+		    expand += 256;
+		j = p->fontheight;
+	        do {
+#if defined(__BIG_ENDIAN)
+		    *(u32 *)dest = expand[*cdat1++]<<24 |
+				   expand[*cdat2++]<<16 |
+				   expand[*cdat3++]<<8 |
+				   expand[*cdat4++];
+#elif defined(__LITTLE_ENDIAN)
+		    *(u32 *)dest = expand[*cdat1++] |
+				   expand[*cdat2++]<<8 |
+				   expand[*cdat3++]<<16 |
+				   expand[*cdat4++]<<24;
+#else
+#error FIXME: No endianness??
+#endif
+		    dest += p->next_line;
+	        } while (--j);
 	        bg >>= 1;
 	        fg >>= 1;
-	    }
+		dest1 += p->next_plane;
+	    } while (--i);
 	    s += 4;
 	    dest0 += 4;
 	    xx += 4;
@@ -260,10 +378,10 @@
 	}
 }
 
-static void rev_char_afb(struct display *p, int xx, int yy)
+void fbcon_afb_revc(struct display *p, int xx, int yy)
 {
-    u_char *dest, *dest0;
-    u_int rows, i;
+    u8 *dest, *dest0;
+    u_short i, j;
     int mask;
 
     dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
@@ -275,29 +393,40 @@
      *  inverting.
      */
 
-    for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) {
+    i = p->var.bits_per_pixel;
+    do {
 	if (mask & 1) {
 	    dest = dest0;
-	    for (rows = p->fontheight; rows--; dest += p->next_line)
+	    j = p->fontheight;
+	    do {
 	        *dest = ~*dest;
+		dest += p->next_line;
+	    } while (--j);
 	}
 	mask >>= 1;
-    }
+	dest0 += p->next_plane;
+    } while (--i);
 }
 
 
-#ifdef MODULE
-int init_module(void)
-#else
-int fbcon_init_afb(void)
-#endif
-{
-    return(fbcon_register_driver(&dispsw_afb, 0));
-}
+    /*
+     *  `switch' for the low level operations
+     */
 
-#ifdef MODULE
-void cleanup_module(void)
-{
-    fbcon_unregister_driver(&dispsw_afb);
-}
-#endif /* MODULE */
+struct display_switch fbcon_afb = {
+    fbcon_afb_setup, fbcon_afb_bmove, fbcon_afb_clear, fbcon_afb_putc,
+    fbcon_afb_putcs, fbcon_afb_revc
+};
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(fbcon_afb);
+EXPORT_SYMBOL(fbcon_afb_setup);
+EXPORT_SYMBOL(fbcon_afb_bmove);
+EXPORT_SYMBOL(fbcon_afb_clear);
+EXPORT_SYMBOL(fbcon_afb_putc);
+EXPORT_SYMBOL(fbcon_afb_putcs);
+EXPORT_SYMBOL(fbcon_afb_revc);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-afb.h linux/drivers/video/fbcon-afb.h
--- v2.1.90/linux/drivers/video/fbcon-afb.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-afb.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,15 @@
+    /*
+     *  Amiga bitplanes (afb)
+     */
+
+extern struct display_switch fbcon_afb;
+extern void fbcon_afb_setup(struct display *p);
+extern void fbcon_afb_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			    int height, int width);
+extern void fbcon_afb_clear(struct vc_data *conp, struct display *p, int sy,
+			    int sx, int height, int width);
+extern void fbcon_afb_putc(struct vc_data *conp, struct display *p, int c,
+			   int yy, int xx);
+extern void fbcon_afb_putcs(struct vc_data *conp, struct display *p,
+			    const char *s, int count, int yy, int xx);
+extern void fbcon_afb_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-cfb16.c linux/drivers/video/fbcon-cfb16.c
--- v2.1.90/linux/drivers/video/fbcon-cfb16.c	Mon Feb 23 18:12:10 1998
+++ linux/drivers/video/fbcon-cfb16.c	Sat Mar 21 11:12:49 1998
@@ -1,6 +1,6 @@
 /*
  *  linux/drivers/video/cfb16.c -- Low level frame buffer operations for 16 bpp
- *				   packed pixels
+ *				   truecolor packed pixels
  *
  *	Created 5 Apr 1997 by Geert Uytterhoeven
  *
@@ -13,69 +13,40 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
+#include <linux/config.h>
 #include <linux/fb.h>
 
 #include "fbcon.h"
-
-
-    /*
-     *  Prototypes
-     */
-
-static int open_cfb16(struct display *p);
-static void release_cfb16(void);
-static void bmove_cfb16(struct display *p, int sy, int sx, int dy, int dx,
-			int height, int width);
-static void clear_cfb16(struct vc_data *conp, struct display *p, int sy,
-			int sx, int height, int width);
-static void putc_cfb16(struct vc_data *conp, struct display *p, int c,
-		       int yy, int xx);
-static void putcs_cfb16(struct vc_data *conp, struct display *p,
-			const char *s, int count, int yy, int xx);
-static void rev_char_cfb16(struct display *p, int xx, int yy);
-
-
-    /*
-     *  `switch' for the low level operations
-     */
-
-static struct display_switch dispsw_cfb16 = {
-    open_cfb16, release_cfb16, bmove_cfb16, clear_cfb16, putc_cfb16,
-    putcs_cfb16, rev_char_cfb16
-};
+#include "fbcon-cfb16.h"
 
 
     /*
      *  16 bpp packed pixels
      */
 
-u_short packed16_cmap[16];
+u16 fbcon_cfb16_cmap[16];
 
-static u_long tab_cfb16[] = {
-    0x00000000,0x0000ffff,0xffff0000,0xffffffff
+static u32 tab_cfb16[] = {
+#if defined(__BIG_ENDIAN)
+    0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
+#elif defined(__LITTLE_ENDIAN)
+    0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
+#else
+#error FIXME: No endianness??
+#endif
 };
 
-static int open_cfb16(struct display *p)
+void fbcon_cfb16_setup(struct display *p)
 {
-    if (p->type != FB_TYPE_PACKED_PIXELS || p->var.bits_per_pixel != 16)
-	return -EINVAL;
-
     p->next_line = p->var.xres_virtual<<1;
     p->next_plane = 0;
-    MOD_INC_USE_COUNT;
-    return 0;
-}
-
-static void release_cfb16(void)
-{
-    MOD_DEC_USE_COUNT;
 }
 
-static void bmove_cfb16(struct display *p, int sy, int sx, int dy, int dx,
-			int height, int width)
+void fbcon_cfb16_bmove(struct display *p, int sy, int sx, int dy, int dx,
+		       int height, int width)
 {
     int bytes = p->next_line, linesize = bytes * p->fontheight, rows;
-    u_char *src,*dst;
+    u8 *src, *dst;
 
     if (sx == 0 && dx == 0 && width * 16 == bytes)
 	mymemmove(p->screen_base + dy * linesize,
@@ -100,82 +71,78 @@
     }
 }
 
-static void clear_cfb16(struct vc_data *conp, struct display *p, int sy,
-			int sx, int height, int width)
+void fbcon_cfb16_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+		       int height, int width)
 {
-    u_char *dest0,*dest;
-    int bytes=p->next_line,lines=height * p->fontheight, rows, i;
-    u_long bgx;
+    u8 *dest0, *dest;
+    int bytes = p->next_line, lines = height * p->fontheight, rows, i;
+    u32 bgx;
 
     dest = p->screen_base + sy * p->fontheight * bytes + sx * 16;
 
-    bgx = attr_bgcol_ec(p,conp);
-    bgx = packed16_cmap[bgx];
+    bgx = fbcon_cfb16_cmap[attr_bgcol_ec(p, conp)];
     bgx |= (bgx << 16);
 
     if (sx == 0 && width * 16 == bytes)
 	for (i = 0 ; i < lines * width ; i++) {
-	    ((u_long *)dest)[0]=bgx;
-	    ((u_long *)dest)[1]=bgx;
-	    ((u_long *)dest)[2]=bgx;
-	    ((u_long *)dest)[3]=bgx;
-	    dest+=16;
+	    ((u32 *)dest)[0] = bgx;
+	    ((u32 *)dest)[1] = bgx;
+	    ((u32 *)dest)[2] = bgx;
+	    ((u32 *)dest)[3] = bgx;
+	    dest += 16;
 	}
     else {
-	dest0=dest;
+	dest0 = dest;
 	for (rows = lines; rows-- ; dest0 += bytes) {
-	    dest=dest0;
+	    dest = dest0;
 	    for (i = 0 ; i < width ; i++) {
-		((u_long *)dest)[0]=bgx;
-		((u_long *)dest)[1]=bgx;
-		((u_long *)dest)[2]=bgx;
-		((u_long *)dest)[3]=bgx;
-		dest+=16;
+		((u32 *)dest)[0] = bgx;
+		((u32 *)dest)[1] = bgx;
+		((u32 *)dest)[2] = bgx;
+		((u32 *)dest)[3] = bgx;
+		dest += 16;
 	    }
 	}
     }
 }
 
-static void putc_cfb16(struct vc_data *conp, struct display *p, int c, int yy,
-		       int xx)
+void fbcon_cfb16_putc(struct vc_data *conp, struct display *p, int c, int yy,
+		      int xx)
 {
-    u_char *dest,*cdat;
-    int bytes=p->next_line,rows;
-    ulong eorx,fgx,bgx;
+    u8 *dest, *cdat;
+    int bytes = p->next_line, rows;
+    u32 eorx, fgx, bgx;
 
     c &= 0xff;
 
     dest = p->screen_base + yy * p->fontheight * bytes + xx * 16;
     cdat = p->fontdata + c * p->fontheight;
 
-    fgx = attr_fgcol(p,conp);
-    fgx = packed16_cmap[fgx];
-    bgx = attr_bgcol(p,conp);
-    bgx = packed16_cmap[bgx];
+    fgx = fbcon_cfb16_cmap[attr_fgcol(p, conp)];
+    bgx = fbcon_cfb16_cmap[attr_bgcol(p, conp)];
     fgx |= (fgx << 16);
     bgx |= (bgx << 16);
     eorx = fgx ^ bgx;
 
     for (rows = p->fontheight ; rows-- ; dest += bytes) {
-	((u_long *)dest)[0]= (tab_cfb16[*cdat >> 6] & eorx) ^ bgx;
-	((u_long *)dest)[1]= (tab_cfb16[*cdat >> 4 & 0x3] & eorx) ^ bgx;
-	((u_long *)dest)[2]= (tab_cfb16[*cdat >> 2 & 0x3] & eorx) ^ bgx;
-	((u_long *)dest)[3]= (tab_cfb16[*cdat++ & 0x3] & eorx) ^ bgx;
+	u8 bits = *cdat++;
+	((u32 *)dest)[0] = (tab_cfb16[bits >> 6] & eorx) ^ bgx;
+	((u32 *)dest)[1] = (tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx;
+	((u32 *)dest)[2] = (tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx;
+	((u32 *)dest)[3] = (tab_cfb16[bits & 3] & eorx) ^ bgx;
     }
 }
 
-static void putcs_cfb16(struct vc_data *conp, struct display *p, const char *s,
-			int count, int yy, int xx)
+void fbcon_cfb16_putcs(struct vc_data *conp, struct display *p, const char *s,
+		       int count, int yy, int xx)
 {
-    u_char *cdat, c, *dest, *dest0;
-    int rows,bytes=p->next_line;
-    u_long eorx, fgx, bgx;
+    u8 *cdat, c, *dest, *dest0;
+    int rows, bytes = p->next_line;
+    u32 eorx, fgx, bgx;
 
     dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 16;
-    fgx = attr_fgcol(p,conp);
-    fgx = packed16_cmap[fgx];
-    bgx = attr_bgcol(p,conp);
-    bgx = packed16_cmap[bgx];
+    fgx = fbcon_cfb16_cmap[attr_fgcol(p, conp)];
+    bgx = fbcon_cfb16_cmap[attr_bgcol(p, conp)];
     fgx |= (fgx << 16);
     bgx |= (bgx << 16);
     eorx = fgx ^ bgx;
@@ -184,49 +151,50 @@
 	cdat = p->fontdata + c * p->fontheight;
 
 	for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
-	    ((u_long *)dest)[0]= (tab_cfb16[*cdat >> 6] & eorx) ^ bgx;
-	    ((u_long *)dest)[1]= (tab_cfb16[*cdat >> 4 & 0x3] & eorx) ^ bgx;
-	    ((u_long *)dest)[2]= (tab_cfb16[*cdat >> 2 & 0x3] & eorx) ^ bgx;
-	    ((u_long *)dest)[3]= (tab_cfb16[*cdat++ & 0x3] & eorx) ^ bgx;
+	    u8 bits = *cdat++;
+	    ((u32 *)dest)[0] = (tab_cfb16[bits >> 6] & eorx) ^ bgx;
+	    ((u32 *)dest)[1] = (tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx;
+	    ((u32 *)dest)[2] = (tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx;
+	    ((u32 *)dest)[3] = (tab_cfb16[bits & 3] & eorx) ^ bgx;
 	}
-	dest0+=16;
+	dest0 += 16;
     }
 }
 
-static void rev_char_cfb16(struct display *p, int xx, int yy)
+void fbcon_cfb16_revc(struct display *p, int xx, int yy)
 {
-    u_char *dest;
-    int bytes=p->next_line, rows;
+    u8 *dest;
+    int bytes = p->next_line, rows;
 
     dest = p->screen_base + yy * p->fontheight * bytes + xx * 16;
     for (rows = p->fontheight ; rows-- ; dest += bytes) {
-	((u_long *)dest)[0] ^= 0xffffffff;
-	((u_long *)dest)[1] ^= 0xffffffff;
-	((u_long *)dest)[2] ^= 0xffffffff;
-	((u_long *)dest)[3] ^= 0xffffffff;
+	((u32 *)dest)[0] ^= 0xffffffff;
+	((u32 *)dest)[1] ^= 0xffffffff;
+	((u32 *)dest)[2] ^= 0xffffffff;
+	((u32 *)dest)[3] ^= 0xffffffff;
     }
 }
 
 
-#ifdef MODULE
-int init_module(void)
-#else
-int fbcon_init_cfb16(void)
-#endif
-{
-    return(fbcon_register_driver(&dispsw_cfb16, 0));
-}
+    /*
+     *  `switch' for the low level operations
+     */
 
-#ifdef MODULE
-void cleanup_module(void)
-{
-    fbcon_unregister_driver(&dispsw_cfb16);
-}
-#endif /* MODULE */
+struct display_switch fbcon_cfb16 = {
+    fbcon_cfb16_setup, fbcon_cfb16_bmove, fbcon_cfb16_clear, fbcon_cfb16_putc,
+    fbcon_cfb16_putcs, fbcon_cfb16_revc
+};
 
 
     /*
      *  Visible symbols for modules
      */
 
-EXPORT_SYMBOL(packed16_cmap);
+EXPORT_SYMBOL(fbcon_cfb16);
+EXPORT_SYMBOL(fbcon_cfb16_setup);
+EXPORT_SYMBOL(fbcon_cfb16_bmove);
+EXPORT_SYMBOL(fbcon_cfb16_clear);
+EXPORT_SYMBOL(fbcon_cfb16_putc);
+EXPORT_SYMBOL(fbcon_cfb16_putcs);
+EXPORT_SYMBOL(fbcon_cfb16_revc);
+EXPORT_SYMBOL(fbcon_cfb16_cmap);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-cfb16.h linux/drivers/video/fbcon-cfb16.h
--- v2.1.90/linux/drivers/video/fbcon-cfb16.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-cfb16.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,16 @@
+    /*
+     *  16 bpp packed pixel (cfb16)
+     */
+
+extern struct display_switch fbcon_cfb16;
+extern u16 fbcon_cfb16_cmap[16];
+extern void fbcon_cfb16_setup(struct display *p);
+extern void fbcon_cfb16_bmove(struct display *p, int sy, int sx, int dy,
+			      int dx, int height, int width);
+extern void fbcon_cfb16_clear(struct vc_data *conp, struct display *p, int sy,
+			      int sx, int height, int width);
+extern void fbcon_cfb16_putc(struct vc_data *conp, struct display *p, int c,
+			     int yy, int xx);
+extern void fbcon_cfb16_putcs(struct vc_data *conp, struct display *p,
+			      const char *s, int count, int yy, int xx);
+extern void fbcon_cfb16_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-cfb2.c linux/drivers/video/fbcon-cfb2.c
--- v2.1.90/linux/drivers/video/fbcon-cfb2.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-cfb2.c	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,205 @@
+/*
+ *  linux/drivers/video/cfb2.c -- Low level frame buffer operations for 2 bpp
+ *				  packed pixels
+ *
+ *	Created 26 Dec 1997 by Michael Schmitz
+ *	Based on cfb4.c
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+#include "fbcon-cfb2.h"
+
+
+    /*
+     *  2 bpp packed pixels
+     */
+
+    /*
+     *  IFF the font is even pixel aligned (that is to say each
+     *  character start is a byte start in the pixel pairs). That
+     *  avoids us having to mask bytes and means we won't be here
+     *  all week. On a MacII that matters _lots_
+     */
+
+static u_char nibbletab_cfb2[]={
+	0x00,0x03,0x0c,0x0f,
+	0x30,0x33,0x3c,0x3f,
+	0xc0,0xc3,0xcc,0xcf,
+	0xf0,0xf3,0xfc,0xff
+};
+ 
+
+void fbcon_cfb2_setup(struct display *p)
+{
+    p->next_line = p->var.xres_virtual>>2;
+    p->next_plane = 0;
+}
+
+void fbcon_cfb2_bmove(struct display *p, int sy, int sx, int dy, int dx,
+		      int height, int width)
+{
+	int bytes = p->next_line, linesize = bytes * p->fontheight, rows;
+	u8 *src,*dst;
+
+	if (sx == 0 && dx == 0 && width * 2 == bytes) {
+		mymemmove(p->screen_base + dy * linesize,
+			  p->screen_base + sy * linesize,
+			  height * linesize);
+	}
+	else {
+		if (dy < sy || (dy == sy && dx < sx)) {
+			src = p->screen_base + sy * linesize + sx * 2;
+			dst = p->screen_base + dy * linesize + dx * 2;
+			for (rows = height * p->fontheight ; rows-- ;) {
+				mymemmove(dst, src, width * 2);
+				src += bytes;
+				dst += bytes;
+			}
+		}
+		else {
+			src = p->screen_base + (sy+height) * linesize + sx * 2
+				- bytes;
+			dst = p->screen_base + (dy+height) * linesize + dx * 2
+				- bytes;
+			for (rows = height * p->fontheight ; rows-- ;) {
+				mymemmove(dst, src, width * 2);
+				src -= bytes;
+				dst -= bytes;
+			}
+		}
+	}
+}
+
+void fbcon_cfb2_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+		      int height, int width)
+{
+	u8 *dest0,*dest;
+	int bytes=p->next_line,lines=height * p->fontheight, rows, i;
+	u32 bgx;
+
+	dest = p->screen_base + sy * p->fontheight * bytes + sx * 2;
+
+	bgx=attr_bgcol_ec(p,conp);
+	bgx |= (bgx << 2);	/* expand the colour to 16 bits */
+	bgx |= (bgx << 4);
+	bgx |= (bgx << 8);
+
+	if (sx == 0 && width * 2 == bytes) {
+		for (i = 0 ; i < lines * width ; i++) {
+			((u16 *)dest)[0]=bgx;
+			dest+=2;
+		}
+	} else {
+		dest0=dest;
+		for (rows = lines; rows-- ; dest0 += bytes) {
+			dest=dest0;
+			for (i = 0 ; i < width ; i++) {
+				/* memset ?? */
+				((u16 *)dest)[0]=bgx;
+				dest+=2;
+			}
+		}
+	}
+}
+
+void fbcon_cfb2_putc(struct vc_data *conp, struct display *p, int c, int yy,
+		     int xx)
+{
+	u8 *dest,*cdat;
+	int bytes=p->next_line,rows;
+	u32 eorx,fgx,bgx;
+
+	c &= 0xff;
+
+	dest = p->screen_base + yy * p->fontheight * bytes + xx * 2;
+	cdat = p->fontdata + c * p->fontheight;
+
+	fgx=3;/*attr_fgcol(p,conp)&0x0F;*/
+	bgx=attr_bgcol(p,conp)&0x0F;
+	fgx |= (fgx << 2);	/* expand color to 8 bits */
+	fgx |= (fgx << 4);
+	bgx |= (bgx << 2);
+	bgx |= (bgx << 4);
+	eorx = fgx ^ bgx;
+
+	for (rows = p->fontheight ; rows-- ; dest += bytes) {
+		((u8 *)dest)[0]=
+			(nibbletab_cfb2[*cdat >> 4] & eorx) ^ bgx;
+		((u8 *)dest)[1]=
+			(nibbletab_cfb2[*cdat++ & 0xf] & eorx) ^ bgx;
+	}
+}
+
+void fbcon_cfb2_putcs(struct vc_data *conp, struct display *p, const char *s,
+		      int count, int yy, int xx)
+{
+	u8 *cdat, c, *dest, *dest0;
+	int rows,bytes=p->next_line;
+	u32 eorx, fgx, bgx;
+
+	dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 2;
+	fgx=3/*attr_fgcol(p,conp)*/;
+	bgx=attr_bgcol(p,conp);
+	fgx |= (fgx << 2);
+	fgx |= (fgx << 4);
+	bgx |= (bgx << 2);
+	bgx |= (bgx << 4);
+	eorx = fgx ^ bgx;
+	while (count--) {
+		c = *s++;
+		cdat = p->fontdata + c * p->fontheight;
+
+		for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
+			((u8 *)dest)[0]=
+				(nibbletab_cfb2[*cdat >> 4] & eorx) ^ bgx;
+			((u8 *)dest)[1]=
+				(nibbletab_cfb2[*cdat++ & 0xf] & eorx) ^ bgx;
+		}
+		dest0+=2;
+	}
+}
+
+void fbcon_cfb2_revc(struct display *p, int xx, int yy)
+{
+	u8 *dest;
+	int bytes=p->next_line, rows;
+
+	dest = p->screen_base + yy * p->fontheight * bytes + xx * 2;
+	for (rows = p->fontheight ; rows-- ; dest += bytes) {
+		((u16 *)dest)[0] ^= 0xffff;
+	}
+}
+
+
+    /*
+     *  `switch' for the low level operations
+     */
+
+struct display_switch fbcon_cfb2 = {
+    fbcon_cfb2_setup, fbcon_cfb2_bmove, fbcon_cfb2_clear, fbcon_cfb2_putc,
+    fbcon_cfb2_putcs, fbcon_cfb2_revc
+};
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(fbcon_cfb2);
+EXPORT_SYMBOL(fbcon_cfb2_setup);
+EXPORT_SYMBOL(fbcon_cfb2_bmove);
+EXPORT_SYMBOL(fbcon_cfb2_clear);
+EXPORT_SYMBOL(fbcon_cfb2_putc);
+EXPORT_SYMBOL(fbcon_cfb2_putcs);
+EXPORT_SYMBOL(fbcon_cfb2_revc);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-cfb2.h linux/drivers/video/fbcon-cfb2.h
--- v2.1.90/linux/drivers/video/fbcon-cfb2.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-cfb2.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,15 @@
+    /*
+     *  2 bpp packed pixel (cfb2)
+     */
+
+extern struct display_switch fbcon_cfb2;
+extern void fbcon_cfb2_setup(struct display *p);
+extern void fbcon_cfb2_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			     int height, int width);
+extern void fbcon_cfb2_clear(struct vc_data *conp, struct display *p, int sy,
+			     int sx, int height, int width);
+extern void fbcon_cfb2_putc(struct vc_data *conp, struct display *p, int c,
+			    int yy, int xx);
+extern void fbcon_cfb2_putcs(struct vc_data *conp, struct display *p,
+			     const char *s, int count, int yy, int xx);
+extern void fbcon_cfb2_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-cfb24.c linux/drivers/video/fbcon-cfb24.c
--- v2.1.90/linux/drivers/video/fbcon-cfb24.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-cfb24.c	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,221 @@
+/*
+ *  linux/drivers/video/cfb24.c -- Low level frame buffer operations for 24 bpp
+ *				   truecolor packed pixels
+ *
+ *	Created 7 Mar 1998 by Geert Uytterhoeven
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+#include "fbcon-cfb24.h"
+
+
+#warning Remove this warning after the test cycle was finalized
+
+
+    /*
+     *  24 bpp packed pixels
+     */
+
+u32 fbcon_cfb24_cmap[16];
+
+void fbcon_cfb24_setup(struct display *p)
+{
+    p->next_line = p->line_length ? p->line_length : p->var.xres_virtual*3;
+    p->next_plane = 0;
+}
+
+void fbcon_cfb24_bmove(struct display *p, int sy, int sx, int dy, int dx,
+		       int height, int width)
+{
+    int bytes = p->next_line, linesize = bytes * p->fontheight, rows;
+    u8 *src, *dst;
+
+    if (sx == 0 && dx == 0 && width * 24 == bytes)
+	mymemmove(p->screen_base + dy * linesize,
+		  p->screen_base + sy * linesize,
+		  height * linesize);
+    else if (dy < sy || (dy == sy && dx < sx)) {
+	src = p->screen_base + sy * linesize + sx * 24;
+	dst = p->screen_base + dy * linesize + dx * 24;
+	for (rows = height * p->fontheight ; rows-- ;) {
+	    mymemmove(dst, src, width * 24);
+	    src += bytes;
+	    dst += bytes;
+	}
+    } else {
+	src = p->screen_base + (sy+height) * linesize + sx * 24 - bytes;
+	dst = p->screen_base + (dy+height) * linesize + dx * 24 - bytes;
+	for (rows = height * p->fontheight ; rows-- ;) {
+	    mymemmove(dst, src, width * 24);
+	    src -= bytes;
+	    dst -= bytes;
+	}
+    }
+}
+
+void fbcon_cfb24_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+		       int height, int width)
+{
+    u8 *dest0, *dest;
+    int bytes = p->next_line, lines = height * p->fontheight, rows, i;
+    u32 bgx;
+
+    dest = p->screen_base + sy * p->fontheight * bytes + sx * 24;
+
+    bgx = fbcon_cfb24_cmap[attr_bgcol_ec(p, conp)];
+
+    if (sx == 0 && width * 24 == bytes)
+	for (i = 0 ; i < lines * width ; i++) {
+	    ((u32 *)dest)[0] = bgx;
+	    ((u32 *)dest)[1] = bgx;
+	    ((u32 *)dest)[2] = bgx;
+	    ((u32 *)dest)[3] = bgx;
+	    ((u32 *)dest)[4] = bgx;
+	    ((u32 *)dest)[5] = bgx;
+	    dest += 24;
+	}
+    else {
+	dest0 = dest;
+	for (rows = lines; rows-- ; dest0 += bytes) {
+	    dest = dest0;
+	    for (i = 0 ; i < width ; i++) {
+		((u32 *)dest)[0] = bgx;
+		((u32 *)dest)[1] = bgx;
+		((u32 *)dest)[2] = bgx;
+		((u32 *)dest)[3] = bgx;
+		((u32 *)dest)[4] = bgx;
+		((u32 *)dest)[5] = bgx;
+		dest += 24;
+	    }
+	}
+    }
+}
+
+static inline void store4pixels(u32 d1, u32 d2, u32 d3, u32 d4, u32 *dest)
+{
+#if defined(__BIG_ENDIAN)
+    *dest++ = (d1<<8) | (d2>>16);
+    *dest++ = (d2<<16) | (d3>>8);
+    *dest++ = (d3<<24) | d4;
+#elif defined(__LITTLE_ENDIAN)
+#error Please add support for little endian byteorder
+#else
+#error FIXME: No endianness??
+#endif
+}
+
+void fbcon_cfb24_putc(struct vc_data *conp, struct display *p, int c, int yy,
+		      int xx)
+{
+    u8 *dest, *cdat;
+    int bytes = p->next_line, rows;
+    u32 eorx, fgx, bgx;
+
+    c &= 0xff;
+
+    dest = p->screen_base + yy * p->fontheight * bytes + xx * 24;
+    cdat = p->fontdata + c * p->fontheight;
+
+    fgx = fbcon_cfb24_cmap[attr_fgcol(p, conp)];
+    bgx = fbcon_cfb24_cmap[attr_bgcol(p, conp)];
+    eorx = fgx ^ bgx;
+
+    for (rows = p->fontheight ; rows-- ; dest += bytes) {
+	u8 bits = *cdat++;
+	u32 d1, d2, d3, d4;
+	d1 = (-(bits >> 7) & eorx) ^ bgx;
+	d2 = (-(bits >> 6 & 1) & eorx) ^ bgx;
+	d3 = (-(bits >> 5 & 1) & eorx) ^ bgx;
+	d4 = (-(bits >> 4 & 1) & eorx) ^ bgx;
+	store4pixels(d1, d2, d3, d4, (u32 *)dest);
+	d1 = (-(bits >> 3 & 1) & eorx) ^ bgx;
+	d2 = (-(bits >> 2 & 1) & eorx) ^ bgx;
+	d3 = (-(bits >> 1 & 1) & eorx) ^ bgx;
+	d4 = (-(bits & 1) & eorx) ^ bgx;
+	store4pixels(d1, d2, d3, d4, (u32 *)(dest+12));
+    }
+}
+
+void fbcon_cfb24_putcs(struct vc_data *conp, struct display *p, const char *s,
+		       int count, int yy, int xx)
+{
+    u8 *cdat, c, *dest, *dest0;
+    int rows, bytes = p->next_line;
+    u32 eorx, fgx, bgx;
+
+    dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 24;
+    fgx = fbcon_cfb24_cmap[attr_fgcol(p, conp)];
+    bgx = fbcon_cfb24_cmap[attr_bgcol(p, conp)];
+    eorx = fgx ^ bgx;
+    while (count--) {
+	c = *s++;
+	cdat = p->fontdata + c * p->fontheight;
+
+	for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
+	    u8 bits = *cdat++;
+	    u32 d1, d2, d3, d4;
+	    d1 = (-(bits >> 7) & eorx) ^ bgx;
+	    d2 = (-(bits >> 6 & 1) & eorx) ^ bgx;
+	    d3 = (-(bits >> 5 & 1) & eorx) ^ bgx;
+	    d4 = (-(bits >> 4 & 1) & eorx) ^ bgx;
+	    store4pixels(d1, d2, d3, d4, (u32 *)dest);
+	    d1 = (-(bits >> 3 & 1) & eorx) ^ bgx;
+	    d2 = (-(bits >> 2 & 1) & eorx) ^ bgx;
+	    d3 = (-(bits >> 1 & 1) & eorx) ^ bgx;
+	    d4 = (-(bits & 1) & eorx) ^ bgx;
+	    store4pixels(d1, d2, d3, d4, (u32 *)(dest+12));
+	}
+	dest0 += 24;
+    }
+}
+
+void fbcon_cfb24_revc(struct display *p, int xx, int yy)
+{
+    u8 *dest;
+    int bytes = p->next_line, rows;
+
+    dest = p->screen_base + yy * p->fontheight * bytes + xx * 24;
+    for (rows = p->fontheight ; rows-- ; dest += bytes) {
+	((u32 *)dest)[0] ^= 0xffffffff;
+	((u32 *)dest)[1] ^= 0xffffffff;
+	((u32 *)dest)[2] ^= 0xffffffff;
+	((u32 *)dest)[3] ^= 0xffffffff;
+	((u32 *)dest)[4] ^= 0xffffffff;
+	((u32 *)dest)[5] ^= 0xffffffff;
+    }
+}
+
+
+    /*
+     *  `switch' for the low level operations
+     */
+
+struct display_switch fbcon_cfb24 = {
+    fbcon_cfb24_setup, fbcon_cfb24_bmove, fbcon_cfb24_clear, fbcon_cfb24_putc,
+    fbcon_cfb24_putcs, fbcon_cfb24_revc
+};
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(fbcon_cfb24);
+EXPORT_SYMBOL(fbcon_cfb24_setup);
+EXPORT_SYMBOL(fbcon_cfb24_bmove);
+EXPORT_SYMBOL(fbcon_cfb24_clear);
+EXPORT_SYMBOL(fbcon_cfb24_putc);
+EXPORT_SYMBOL(fbcon_cfb24_putcs);
+EXPORT_SYMBOL(fbcon_cfb24_revc);
+EXPORT_SYMBOL(fbcon_cfb24_cmap);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-cfb24.h linux/drivers/video/fbcon-cfb24.h
--- v2.1.90/linux/drivers/video/fbcon-cfb24.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-cfb24.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,16 @@
+    /*
+     *  24 bpp packed pixel (cfb24)
+     */
+
+extern struct display_switch fbcon_cfb24;
+extern u32 fbcon_cfb24_cmap[16];
+extern void fbcon_cfb24_setup(struct display *p);
+extern void fbcon_cfb24_bmove(struct display *p, int sy, int sx, int dy,
+			      int dx, int height, int width);
+extern void fbcon_cfb24_clear(struct vc_data *conp, struct display *p, int sy,
+			      int sx, int height, int width);
+extern void fbcon_cfb24_putc(struct vc_data *conp, struct display *p, int c,
+			     int yy, int xx);
+extern void fbcon_cfb24_putcs(struct vc_data *conp, struct display *p,
+			      const char *s, int count, int yy, int xx);
+extern void fbcon_cfb24_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-cfb32.c linux/drivers/video/fbcon-cfb32.c
--- v2.1.90/linux/drivers/video/fbcon-cfb32.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-cfb32.c	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,205 @@
+/*
+ *  linux/drivers/video/cfb32.c -- Low level frame buffer operations for 32 bpp
+ *				   truecolor packed pixels
+ *
+ *	Created 28 Dec 1997 by Geert Uytterhoeven
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+#include "fbcon-cfb32.h"
+
+
+    /*
+     *  32 bpp packed pixels
+     */
+
+u32 fbcon_cfb32_cmap[16];
+
+void fbcon_cfb32_setup(struct display *p)
+{
+    p->next_line = p->line_length ? p->line_length : p->var.xres_virtual<<2;
+    p->next_plane = 0;
+}
+
+void fbcon_cfb32_bmove(struct display *p, int sy, int sx, int dy, int dx,
+		       int height, int width)
+{
+    int bytes = p->next_line, linesize = bytes * p->fontheight, rows;
+    u8 *src, *dst;
+
+    if (sx == 0 && dx == 0 && width * 32 == bytes)
+	mymemmove(p->screen_base + dy * linesize,
+		  p->screen_base + sy * linesize,
+		  height * linesize);
+    else if (dy < sy || (dy == sy && dx < sx)) {
+	src = p->screen_base + sy * linesize + sx * 32;
+	dst = p->screen_base + dy * linesize + dx * 32;
+	for (rows = height * p->fontheight ; rows-- ;) {
+	    mymemmove(dst, src, width * 32);
+	    src += bytes;
+	    dst += bytes;
+	}
+    } else {
+	src = p->screen_base + (sy+height) * linesize + sx * 32 - bytes;
+	dst = p->screen_base + (dy+height) * linesize + dx * 32 - bytes;
+	for (rows = height * p->fontheight ; rows-- ;) {
+	    mymemmove(dst, src, width * 32);
+	    src -= bytes;
+	    dst -= bytes;
+	}
+    }
+}
+
+void fbcon_cfb32_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+		       int height, int width)
+{
+    u8 *dest0, *dest;
+    int bytes = p->next_line, lines = height * p->fontheight, rows, i;
+    u32 bgx;
+
+    dest = p->screen_base + sy * p->fontheight * bytes + sx * 32;
+
+    bgx = fbcon_cfb32_cmap[attr_bgcol_ec(p, conp)];
+
+    if (sx == 0 && width * 32 == bytes)
+	for (i = 0 ; i < lines * width ; i++) {
+	    ((u32 *)dest)[0] = bgx;
+	    ((u32 *)dest)[1] = bgx;
+	    ((u32 *)dest)[2] = bgx;
+	    ((u32 *)dest)[3] = bgx;
+	    ((u32 *)dest)[4] = bgx;
+	    ((u32 *)dest)[5] = bgx;
+	    ((u32 *)dest)[6] = bgx;
+	    ((u32 *)dest)[7] = bgx;
+	    dest += 32;
+	}
+    else {
+	dest0 = dest;
+	for (rows = lines; rows-- ; dest0 += bytes) {
+	    dest = dest0;
+	    for (i = 0 ; i < width ; i++) {
+		((u32 *)dest)[0] = bgx;
+		((u32 *)dest)[1] = bgx;
+		((u32 *)dest)[2] = bgx;
+		((u32 *)dest)[3] = bgx;
+		((u32 *)dest)[4] = bgx;
+		((u32 *)dest)[5] = bgx;
+		((u32 *)dest)[6] = bgx;
+		((u32 *)dest)[7] = bgx;
+		dest += 32;
+	    }
+	}
+    }
+}
+
+void fbcon_cfb32_putc(struct vc_data *conp, struct display *p, int c, int yy,
+		      int xx)
+{
+    u8 *dest, *cdat;
+    int bytes = p->next_line, rows;
+    u32 eorx, fgx, bgx;
+
+    c &= 0xff;
+
+    dest = p->screen_base + yy * p->fontheight * bytes + xx * 32;
+    cdat = p->fontdata + c * p->fontheight;
+
+    fgx = fbcon_cfb32_cmap[attr_fgcol(p, conp)];
+    bgx = fbcon_cfb32_cmap[attr_bgcol(p, conp)];
+    eorx = fgx ^ bgx;
+
+    for (rows = p->fontheight ; rows-- ; dest += bytes) {
+	u8 bits = *cdat++;
+	((u32 *)dest)[0] = (-(bits >> 7) & eorx) ^ bgx;
+	((u32 *)dest)[1] = (-(bits >> 6 & 1) & eorx) ^ bgx;
+	((u32 *)dest)[2] = (-(bits >> 5 & 1) & eorx) ^ bgx;
+	((u32 *)dest)[3] = (-(bits >> 4 & 1) & eorx) ^ bgx;
+	((u32 *)dest)[4] = (-(bits >> 3 & 1) & eorx) ^ bgx;
+	((u32 *)dest)[5] = (-(bits >> 2 & 1) & eorx) ^ bgx;
+	((u32 *)dest)[6] = (-(bits >> 1 & 1) & eorx) ^ bgx;
+	((u32 *)dest)[7] = (-(bits & 1) & eorx) ^ bgx;
+    }
+}
+
+void fbcon_cfb32_putcs(struct vc_data *conp, struct display *p, const char *s,
+		       int count, int yy, int xx)
+{
+    u8 *cdat, c, *dest, *dest0;
+    int rows, bytes = p->next_line;
+    u32 eorx, fgx, bgx;
+
+    dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 32;
+    fgx = fbcon_cfb32_cmap[attr_fgcol(p, conp)];
+    bgx = fbcon_cfb32_cmap[attr_bgcol(p, conp)];
+    eorx = fgx ^ bgx;
+    while (count--) {
+	c = *s++;
+	cdat = p->fontdata + c * p->fontheight;
+
+	for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
+	    u8 bits = *cdat++;
+	    ((u32 *)dest)[0] = (-(bits >> 7) & eorx) ^ bgx;
+	    ((u32 *)dest)[1] = (-(bits >> 6 & 1) & eorx) ^ bgx;
+	    ((u32 *)dest)[2] = (-(bits >> 5 & 1) & eorx) ^ bgx;
+	    ((u32 *)dest)[3] = (-(bits >> 4 & 1) & eorx) ^ bgx;
+	    ((u32 *)dest)[4] = (-(bits >> 3 & 1) & eorx) ^ bgx;
+	    ((u32 *)dest)[5] = (-(bits >> 2 & 1) & eorx) ^ bgx;
+	    ((u32 *)dest)[6] = (-(bits >> 1 & 1) & eorx) ^ bgx;
+	    ((u32 *)dest)[7] = (-(bits & 1) & eorx) ^ bgx;
+	}
+	dest0 += 32;
+    }
+}
+
+void fbcon_cfb32_revc(struct display *p, int xx, int yy)
+{
+    u8 *dest;
+    int bytes = p->next_line, rows;
+
+    dest = p->screen_base + yy * p->fontheight * bytes + xx * 32;
+    for (rows = p->fontheight ; rows-- ; dest += bytes) {
+	((u32 *)dest)[0] ^= 0xffffffff;
+	((u32 *)dest)[1] ^= 0xffffffff;
+	((u32 *)dest)[2] ^= 0xffffffff;
+	((u32 *)dest)[3] ^= 0xffffffff;
+	((u32 *)dest)[4] ^= 0xffffffff;
+	((u32 *)dest)[5] ^= 0xffffffff;
+	((u32 *)dest)[6] ^= 0xffffffff;
+	((u32 *)dest)[7] ^= 0xffffffff;
+    }
+}
+
+
+    /*
+     *  `switch' for the low level operations
+     */
+
+struct display_switch fbcon_cfb32 = {
+    fbcon_cfb32_setup, fbcon_cfb32_bmove, fbcon_cfb32_clear, fbcon_cfb32_putc,
+    fbcon_cfb32_putcs, fbcon_cfb32_revc
+};
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(fbcon_cfb32);
+EXPORT_SYMBOL(fbcon_cfb32_setup);
+EXPORT_SYMBOL(fbcon_cfb32_bmove);
+EXPORT_SYMBOL(fbcon_cfb32_clear);
+EXPORT_SYMBOL(fbcon_cfb32_putc);
+EXPORT_SYMBOL(fbcon_cfb32_putcs);
+EXPORT_SYMBOL(fbcon_cfb32_revc);
+EXPORT_SYMBOL(fbcon_cfb32_cmap);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-cfb32.h linux/drivers/video/fbcon-cfb32.h
--- v2.1.90/linux/drivers/video/fbcon-cfb32.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-cfb32.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,16 @@
+    /*
+     *  32 bpp packed pixel (cfb32)
+     */
+
+extern struct display_switch fbcon_cfb32;
+extern u32 fbcon_cfb32_cmap[16];
+extern void fbcon_cfb32_setup(struct display *p);
+extern void fbcon_cfb32_bmove(struct display *p, int sy, int sx, int dy,
+			      int dx, int height, int width);
+extern void fbcon_cfb32_clear(struct vc_data *conp, struct display *p, int sy,
+			      int sx, int height, int width);
+extern void fbcon_cfb32_putc(struct vc_data *conp, struct display *p, int c,
+			     int yy, int xx);
+extern void fbcon_cfb32_putcs(struct vc_data *conp, struct display *p,
+			      const char *s, int count, int yy, int xx);
+extern void fbcon_cfb32_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-cfb4.c linux/drivers/video/fbcon-cfb4.c
--- v2.1.90/linux/drivers/video/fbcon-cfb4.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-cfb4.c	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,208 @@
+/*
+ *  linux/drivers/video/cfb4.c -- Low level frame buffer operations for 4 bpp
+ *				  packed pixels
+ *
+ *	Created 26 Dec 1997 by Michael Schmitz
+ *	Based on the old macfb.c 4bpp code by Alan Cox
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+#include "fbcon-cfb4.h"
+
+
+    /*
+     *  4 bpp packed pixels
+     */
+
+    /*
+     *  IFF the font is even pixel aligned (that is to say each
+     *  character start is a byte start in the pixel pairs). That
+     *  avoids us having to mask bytes and means we won't be here
+     *  all week. On a MacII that matters _lots_
+     */
+
+static u16 nibbletab_cfb4[] = {
+    0x0000,0x000f,0x00f0,0x00ff,
+    0x0f00,0x0f0f,0x0ff0,0x0fff,
+    0xf000,0xf00f,0xf0f0,0xf0ff,
+    0xff00,0xff0f,0xfff0,0xffff
+};
+
+void fbcon_cfb4_setup(struct display *p)
+{
+    p->next_line = p->var.xres_virtual>>1;
+    p->next_plane = 0;
+}
+
+void fbcon_cfb4_bmove(struct display *p, int sy, int sx, int dy, int dx,
+		      int height, int width)
+{
+	int bytes = p->next_line, linesize = bytes * p->fontheight, rows;
+	u8 *src,*dst;
+
+	if (sx == 0 && dx == 0 && width * 4 == bytes) {
+		mymemmove(p->screen_base + dy * linesize,
+			  p->screen_base + sy * linesize,
+			  height * linesize);
+	}
+	else {
+		if (dy < sy || (dy == sy && dx < sx)) {
+			src = p->screen_base + sy * linesize + sx * 4;
+			dst = p->screen_base + dy * linesize + dx * 4;
+			for (rows = height * p->fontheight ; rows-- ;) {
+				mymemmove(dst, src, width * 4);
+				src += bytes;
+				dst += bytes;
+			}
+		}
+		else {
+			src = p->screen_base + (sy+height) * linesize + sx * 4
+				- bytes;
+			dst = p->screen_base + (dy+height) * linesize + dx * 4
+				- bytes;
+			for (rows = height * p->fontheight ; rows-- ;) {
+				mymemmove(dst, src, width * 4);
+				src -= bytes;
+				dst -= bytes;
+			}
+		}
+	}
+}
+
+void fbcon_cfb4_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+		      int height, int width)
+{
+	u8 *dest0,*dest;
+	int bytes=p->next_line,lines=height * p->fontheight, rows, i;
+	u32 bgx;
+
+/*	if(p->screen_base!=0xFDD00020)
+		mac_boom(1);*/
+	dest = p->screen_base + sy * p->fontheight * bytes + sx * 4;
+
+	bgx=attr_bgcol_ec(p,conp);
+	bgx |= (bgx << 4);	/* expand the colour to 32bits */
+	bgx |= (bgx << 8);
+	bgx |= (bgx << 16);
+
+	if (sx == 0 && width * 4 == bytes) {
+		for (i = 0 ; i < lines * width ; i++) {
+			((u32 *)dest)[0]=bgx;
+			dest+=4;
+		}
+	} else {
+		dest0=dest;
+		for (rows = lines; rows-- ; dest0 += bytes) {
+			dest=dest0;
+			for (i = 0 ; i < width ; i++) {
+				/* memset ?? */
+				((u32 *)dest)[0]=bgx;
+				dest+=4;
+			}
+		}
+	}
+}
+
+void fbcon_cfb4_putc(struct vc_data *conp, struct display *p, int c, int yy,
+		     int xx)
+{
+	u8 *dest,*cdat;
+	int bytes=p->next_line,rows;
+	u32 eorx,fgx,bgx;
+
+	c &= 0xff;
+
+	dest = p->screen_base + yy * p->fontheight * bytes + xx * 4;
+	cdat = p->fontdata + c * p->fontheight;
+
+	fgx=15;/*attr_fgcol(p,conp)&0x0F;*/
+	bgx=attr_bgcol(p,conp)&0x0F;
+	fgx |= (fgx << 4);
+	fgx |= (fgx << 8);
+	bgx |= (bgx << 4);
+	bgx |= (bgx << 8);
+	eorx = fgx ^ bgx;
+
+	for (rows = p->fontheight ; rows-- ; dest += bytes) {
+		((u16 *)dest)[0]=
+			(nibbletab_cfb4[*cdat >> 4] & eorx) ^ bgx;
+		((u16 *)dest)[1]=
+			(nibbletab_cfb4[*cdat++ & 0xf] & eorx) ^ bgx;
+	}
+}
+
+void fbcon_cfb4_putcs(struct vc_data *conp, struct display *p, const char *s,
+		      int count, int yy, int xx)
+{
+	u8 *cdat, c, *dest, *dest0;
+	int rows,bytes=p->next_line;
+	u32 eorx, fgx, bgx;
+
+	dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 4;
+	fgx=15/*attr_fgcol(p,conp)*/;
+	bgx=attr_bgcol(p,conp);
+	fgx |= (fgx << 4);
+	fgx |= (fgx << 8);
+	fgx |= (fgx << 16);
+	bgx |= (bgx << 4);
+	bgx |= (bgx << 8);
+	bgx |= (bgx << 16);
+	eorx = fgx ^ bgx;
+	while (count--) {
+		c = *s++;
+		cdat = p->fontdata + c * p->fontheight;
+
+		for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
+			((u16 *)dest)[0]=
+			(nibbletab_cfb4[*cdat >> 4] & eorx) ^ bgx;
+			((u16 *)dest)[1]=
+			(nibbletab_cfb4[*cdat++ & 0xf] & eorx) ^ bgx;
+		}
+		dest0+=4;
+	}
+}
+
+void fbcon_cfb4_revc(struct display *p, int xx, int yy)
+{
+	u8 *dest;
+	int bytes=p->next_line, rows;
+
+	dest = p->screen_base + yy * p->fontheight * bytes + xx * 4;
+	for (rows = p->fontheight ; rows-- ; dest += bytes) {
+		((u32 *)dest)[0] ^= 0x0f0f0f0f;
+	}
+}
+
+
+    /*
+     *  `switch' for the low level operations
+     */
+
+struct display_switch fbcon_cfb4 = {
+    fbcon_cfb4_setup, fbcon_cfb4_bmove, fbcon_cfb4_clear, fbcon_cfb4_putc,
+    fbcon_cfb4_putcs, fbcon_cfb4_revc
+};
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(fbcon_cfb4);
+EXPORT_SYMBOL(fbcon_cfb4_setup);
+EXPORT_SYMBOL(fbcon_cfb4_bmove);
+EXPORT_SYMBOL(fbcon_cfb4_clear);
+EXPORT_SYMBOL(fbcon_cfb4_putc);
+EXPORT_SYMBOL(fbcon_cfb4_putcs);
+EXPORT_SYMBOL(fbcon_cfb4_revc);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-cfb4.h linux/drivers/video/fbcon-cfb4.h
--- v2.1.90/linux/drivers/video/fbcon-cfb4.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-cfb4.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,15 @@
+    /*
+     *  4 bpp packed pixel (cfb4)
+     */
+
+extern struct display_switch fbcon_cfb4;
+extern void fbcon_cfb4_setup(struct display *p);
+extern void fbcon_cfb4_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			     int height, int width);
+extern void fbcon_cfb4_clear(struct vc_data *conp, struct display *p, int sy,
+			     int sx, int height, int width);
+extern void fbcon_cfb4_putc(struct vc_data *conp, struct display *p, int c,
+			    int yy, int xx);
+extern void fbcon_cfb4_putcs(struct vc_data *conp, struct display *p,
+			     const char *s, int count, int yy, int xx);
+extern void fbcon_cfb4_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-cfb8.c linux/drivers/video/fbcon-cfb8.c
--- v2.1.90/linux/drivers/video/fbcon-cfb8.c	Mon Feb 23 18:12:10 1998
+++ linux/drivers/video/fbcon-cfb8.c	Sat Mar 21 11:12:49 1998
@@ -13,70 +13,44 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
+#include <linux/config.h>
 #include <linux/fb.h>
 
 #include "fbcon.h"
-
-
-    /*
-     *  Prototypes
-     */
-
-static int open_cfb8(struct display *p);
-static void release_cfb8(void);
-static void bmove_cfb8(struct display *p, int sy, int sx, int dy, int dx,
-		       int height, int width);
-static void clear_cfb8(struct vc_data *conp, struct display *p, int sy,
-		       int sx, int height, int width);
-static void putc_cfb8(struct vc_data *conp, struct display *p, int c, int yy,
-		      int xx);
-static void putcs_cfb8(struct vc_data *conp, struct display *p,
-		       const char *s, int count, int yy, int xx);
-static void rev_char_cfb8(struct display *p, int xx, int yy);
-
-
-    /*
-     *  `switch' for the low level operations
-     */
-
-static struct display_switch dispsw_cfb8 = {
-    open_cfb8, release_cfb8, bmove_cfb8, clear_cfb8, putc_cfb8, putcs_cfb8,
-    rev_char_cfb8
-};
+#include "fbcon-cfb8.h"
 
 
     /*
      *  8 bpp packed pixels
      */
 
-static u_long nibbletab_cfb8[] = {
+static u32 nibbletab_cfb8[] = {
+#if defined(__BIG_ENDIAN)
     0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
     0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
     0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
     0xffff0000,0xffff00ff,0xffffff00,0xffffffff
+#elif defined(__LITTLE_ENDIAN)
+    0x00000000,0xff000000,0x00ff0000,0xffff0000,
+    0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
+    0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
+    0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
+#else
+#error FIXME: No endianness??
+#endif
 };
 
-static int open_cfb8(struct display *p)
+void fbcon_cfb8_setup(struct display *p)
 {
-    if (p->type != FB_TYPE_PACKED_PIXELS || p->var.bits_per_pixel != 8)
-	return -EINVAL;
-
     p->next_line = p->var.xres_virtual;
     p->next_plane = 0;
-    MOD_INC_USE_COUNT;
-    return 0;
 }
 
-static void release_cfb8(void)
-{
-    MOD_DEC_USE_COUNT;
-}
-
-static void bmove_cfb8(struct display *p, int sy, int sx, int dy, int dx,
-		       int height, int width)
+void fbcon_cfb8_bmove(struct display *p, int sy, int sx, int dy, int dx,
+		      int height, int width)
 {
     int bytes = p->next_line, linesize = bytes * p->fontheight, rows;
-    u_char *src,*dst;
+    u8 *src,*dst;
 
     if (sx == 0 && dx == 0 && width * 8 == bytes)
 	mymemmove(p->screen_base + dy * linesize,
@@ -101,12 +75,12 @@
     }
 }
 
-static void clear_cfb8(struct vc_data *conp, struct display *p, int sy, int sx,
-		       int height, int width)
+void fbcon_cfb8_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+		      int height, int width)
 {
-    u_char *dest0,*dest;
+    u8 *dest0,*dest;
     int bytes=p->next_line,lines=height * p->fontheight, rows, i;
-    u_long bgx;
+    u32 bgx;
 
     dest = p->screen_base + sy * p->fontheight * bytes + sx * 8;
 
@@ -116,8 +90,8 @@
 
     if (sx == 0 && width * 8 == bytes)
 	for (i = 0 ; i < lines * width ; i++) {
-	    ((u_long *)dest)[0]=bgx;
-	    ((u_long *)dest)[1]=bgx;
+	    ((u32 *)dest)[0]=bgx;
+	    ((u32 *)dest)[1]=bgx;
 	    dest+=8;
 	}
     else {
@@ -125,20 +99,20 @@
 	for (rows = lines; rows-- ; dest0 += bytes) {
 	    dest=dest0;
 	    for (i = 0 ; i < width ; i++) {
-		((u_long *)dest)[0]=bgx;
-		((u_long *)dest)[1]=bgx;
+		((u32 *)dest)[0]=bgx;
+		((u32 *)dest)[1]=bgx;
 		dest+=8;
 	    }
 	}
     }
 }
 
-static void putc_cfb8(struct vc_data *conp, struct display *p, int c, int yy,
-		      int xx)
+void fbcon_cfb8_putc(struct vc_data *conp, struct display *p, int c, int yy,
+		     int xx)
 {
-    u_char *dest,*cdat;
+    u8 *dest,*cdat;
     int bytes=p->next_line,rows;
-    ulong eorx,fgx,bgx;
+    u32 eorx,fgx,bgx;
 
     c &= 0xff;
 
@@ -154,17 +128,17 @@
     eorx = fgx ^ bgx;
 
     for (rows = p->fontheight ; rows-- ; dest += bytes) {
-	((u_long *)dest)[0]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx;
-	((u_long *)dest)[1]= (nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx;
+	((u32 *)dest)[0]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx;
+	((u32 *)dest)[1]= (nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx;
     }
 }
 
-static void putcs_cfb8(struct vc_data *conp, struct display *p, const char *s,
-		       int count, int yy, int xx)
+void fbcon_cfb8_putcs(struct vc_data *conp, struct display *p, const char *s,
+		      int count, int yy, int xx)
 {
-    u_char *cdat, c, *dest, *dest0;
+    u8 *cdat, c, *dest, *dest0;
     int rows,bytes=p->next_line;
-    u_long eorx, fgx, bgx;
+    u32 eorx, fgx, bgx;
 
     dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 8;
     fgx=attr_fgcol(p,conp);
@@ -179,39 +153,44 @@
 	cdat = p->fontdata + c * p->fontheight;
 
 	for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
-	    ((u_long *)dest)[0]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx;
-	    ((u_long *)dest)[1]= (nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^
-				 bgx;
+	    ((u32 *)dest)[0]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx;
+	    ((u32 *)dest)[1]= (nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx;
 	}
 	dest0+=8;
     }
 }
 
-static void rev_char_cfb8(struct display *p, int xx, int yy)
+void fbcon_cfb8_revc(struct display *p, int xx, int yy)
 {
-    u_char *dest;
+    u8 *dest;
     int bytes=p->next_line, rows;
 
     dest = p->screen_base + yy * p->fontheight * bytes + xx * 8;
     for (rows = p->fontheight ; rows-- ; dest += bytes) {
-	((u_long *)dest)[0] ^= 0x0f0f0f0f;
-	((u_long *)dest)[1] ^= 0x0f0f0f0f;
+	((u32 *)dest)[0] ^= 0x0f0f0f0f;
+	((u32 *)dest)[1] ^= 0x0f0f0f0f;
     }
 }
 
 
-#ifdef MODULE
-int init_module(void)
-#else
-int fbcon_init_cfb8(void)
-#endif
-{
-    return(fbcon_register_driver(&dispsw_cfb8, 0));
-}
+    /*
+     *  `switch' for the low level operations
+     */
 
-#ifdef MODULE
-void cleanup_module(void)
-{
-    fbcon_unregister_driver(&dispsw_cfb8);
-}
-#endif /* MODULE */
+struct display_switch fbcon_cfb8 = {
+    fbcon_cfb8_setup, fbcon_cfb8_bmove, fbcon_cfb8_clear, fbcon_cfb8_putc,
+    fbcon_cfb8_putcs, fbcon_cfb8_revc
+};
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(fbcon_cfb8);
+EXPORT_SYMBOL(fbcon_cfb8_setup);
+EXPORT_SYMBOL(fbcon_cfb8_bmove);
+EXPORT_SYMBOL(fbcon_cfb8_clear);
+EXPORT_SYMBOL(fbcon_cfb8_putc);
+EXPORT_SYMBOL(fbcon_cfb8_putcs);
+EXPORT_SYMBOL(fbcon_cfb8_revc);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-cfb8.h linux/drivers/video/fbcon-cfb8.h
--- v2.1.90/linux/drivers/video/fbcon-cfb8.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-cfb8.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,15 @@
+    /*
+     *  8 bpp packed pixel (cfb8)
+     */
+
+extern struct display_switch fbcon_cfb8;
+extern void fbcon_cfb8_setup(struct display *p);
+extern void fbcon_cfb8_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			     int height, int width);
+extern void fbcon_cfb8_clear(struct vc_data *conp, struct display *p, int sy,
+			     int sx, int height, int width);
+extern void fbcon_cfb8_putc(struct vc_data *conp, struct display *p, int c,
+			    int yy, int xx);
+extern void fbcon_cfb8_putcs(struct vc_data *conp, struct display *p,
+			     const char *s, int count, int yy, int xx);
+extern void fbcon_cfb8_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-cyber.c linux/drivers/video/fbcon-cyber.c
--- v2.1.90/linux/drivers/video/fbcon-cyber.c	Mon Feb 23 18:12:10 1998
+++ linux/drivers/video/fbcon-cyber.c	Wed Dec 31 16:00:00 1969
@@ -1,229 +0,0 @@
-/*
- *  linux/drivers/video/cyber.c -- Low level frame buffer operations for the
- *				   CyberVision64 (accelerated)
- *
- *	Created 5 Apr 1997 by Geert Uytterhoeven
- *
- *  This file is subject to the terms and conditions of the GNU General Public
- *  License.  See the file COPYING in the main directory of this archive for
- *  more details.
- */
-
-#include <linux/module.h>
-#include <linux/tty.h>
-#include <linux/console.h>
-#include <linux/string.h>
-#include <linux/fb.h>
-
-#include "fbcon.h"
-#include "s3blit.h"
-
-
-    /*
-     *  Prototypes
-     */
-
-static int open_cyber(struct display *p);
-static void release_cyber(void);
-static void bmove_cyber(struct display *p, int sy, int sx, int dy, int dx,
-	                int height, int width);
-static void clear_cyber(struct vc_data *conp, struct display *p, int sy, int sx,
-	                int height, int width);
-static void putc_cyber(struct vc_data *conp, struct display *p, int c, int yy,
-	               int xx);
-static void putcs_cyber(struct vc_data *conp, struct display *p, const char *s,
-	                int count, int yy, int xx);
-static void rev_char_cyber(struct display *p, int xx, int yy);
-
-
-    /*
-     *  Acceleration functions in cyberfb.c
-     */
-
-extern void Cyber_WaitQueue(unsigned short fifo);
-extern void Cyber_WaitBlit(void);
-extern void Cyber_BitBLT(unsigned short curx, unsigned short cury,
-			 unsigned short destx, unsigned short desty,
-			 unsigned short width, unsigned short height,
-			 unsigned short mode);
-extern void Cyber_RectFill(unsigned short xx, unsigned short yy,
-			   unsigned short width, unsigned short height,
-			   unsigned short mode, unsigned short fillcolor);
-extern void Cyber_MoveCursor(unsigned short xx, unsigned short yy);
-
-
-    /*
-     *  `switch' for the low level operations
-     */
-
-static struct display_switch dispsw_cyber = {
-   open_cyber, release_cyber, bmove_cyber, clear_cyber, putc_cyber,
-   putcs_cyber, rev_char_cyber
-};
-
-
-    /*
-     *  CyberVision64 (accelerated)
-     */
-
-static int open_cyber(struct display *p)
-{
-    if (p->type != FB_TYPE_PACKED_PIXELS ||
-	p->var.accel != FB_ACCEL_CYBERVISION)
-	return -EINVAL;
-
-    p->next_line = p->var.xres_virtual*p->var.bits_per_pixel>>3;
-    p->next_plane = 0;
-    MOD_INC_USE_COUNT;
-    return 0;
-}
-
-static void release_cyber(void)
-{
-    MOD_DEC_USE_COUNT;
-}
-
-static void bmove_cyber(struct display *p, int sy, int sx, int dy, int dx,
-	                int height, int width)
-{
-    sx *= 8; dx *= 8; width *= 8;
-    Cyber_BitBLT((u_short)sx, (u_short)(sy*p->fontheight), (u_short)dx,
-		 (u_short)(dy*p->fontheight), (u_short)width,
-		 (u_short)(height*p->fontheight), (u_short)S3_NEW);
-}
-
-static void clear_cyber(struct vc_data *conp, struct display *p, int
-			sy, int sx, int height, int width)
-{
-    unsigned char bg;
-
-    sx *= 8; width *= 8;
-    bg = attr_bgcol_ec(p,conp);
-    Cyber_RectFill((u_short)sx,
-		   (u_short)(sy*p->fontheight),
-		   (u_short)width,
-		   (u_short)(height*p->fontheight),
-		   (u_short)S3_NEW,
-		   (u_short)bg);
-}
-
-static void putc_cyber(struct vc_data *conp, struct display *p, int c, int yy,
-	               int xx)
-{
-    u_char *dest, *cdat;
-    u_long tmp;
-    u_int rows, revs, underl;
-    u_char d;
-    u_char fg, bg;
-
-    c &= 0xff;
-
-    dest = p->screen_base+yy*p->fontheight*p->next_line+8*xx;
-    cdat = p->fontdata+(c*p->fontheight);
-    fg = p->fgcol;
-    bg = p->bgcol;
-    revs = conp->vc_reverse;
-    underl = conp->vc_underline;
-
-    Cyber_WaitBlit();
-    for (rows = p->fontheight; rows--; dest += p->next_line) {
-	d = *cdat++;
-
-	if (underl && !rows)
-	    d = 0xff;
-	if (revs)
-	    d = ~d;
-
-	tmp =  ((d & 0x80) ? fg : bg) << 24;
-	tmp |= ((d & 0x40) ? fg : bg) << 16;
-	tmp |= ((d & 0x20) ? fg : bg) << 8;
-	tmp |= ((d & 0x10) ? fg : bg);
-	*((u_long*) dest) = tmp;
-	tmp =  ((d & 0x8) ? fg : bg) << 24;
-	tmp |= ((d & 0x4) ? fg : bg) << 16;
-	tmp |= ((d & 0x2) ? fg : bg) << 8;
-	tmp |= ((d & 0x1) ? fg : bg);
-	*((u_long*) dest + 1) = tmp;
-    }
-}
-
-static void putcs_cyber(struct vc_data *conp, struct display *p, const char *s,
-	                int count, int yy, int xx)
-{
-    u_char *dest, *dest0, *cdat;
-    u_long tmp;
-    u_int rows, underl;
-    u_char c, d;
-    u_char fg, bg;
-
-    dest0 = p->screen_base+yy*p->fontheight*p->next_line+8*xx;
-    fg = p->fgcol;
-    bg = p->bgcol;
-    underl = conp->vc_underline;
-
-    Cyber_WaitBlit();
-    while (count--) {
-	c = *s++;
-	dest = dest0;
-	dest0 += 8;
-	cdat = p->fontdata+(c*p->fontheight);
-	for (rows = p->fontheight; rows--; dest += p->next_line) {
-	    d = *cdat++;
-
-	    if (underl && !rows)
-		d = 0xff;
-
-	    tmp =  ((d & 0x80) ? fg : bg) << 24;
-	    tmp |= ((d & 0x40) ? fg : bg) << 16;
-	    tmp |= ((d & 0x20) ? fg : bg) << 8;
-	    tmp |= ((d & 0x10) ? fg : bg);
-	    *((u_long*) dest) = tmp;
-	    tmp =  ((d & 0x8) ? fg : bg) << 24;
-	    tmp |= ((d & 0x4) ? fg : bg) << 16;
-	    tmp |= ((d & 0x2) ? fg : bg) << 8;
-	    tmp |= ((d & 0x1) ? fg : bg);
-	    *((u_long*) dest + 1) = tmp;
-	}
-    }
-}
-
-
-static void rev_char_cyber(struct display *p, int xx, int yy)
-{
-    unsigned char *dest;
-    unsigned int rows;
-    unsigned char fg, bg;
-
-    fg = p->fgcol;
-    bg = p->bgcol;
-
-    dest = p->screen_base+yy*p->fontheight*p->next_line+8*xx;
-    Cyber_WaitBlit();
-    for (rows = p->fontheight; rows--; dest += p->next_line) {
-	*dest = (*dest == fg) ? bg : fg;
-	*(dest+1) = (*(dest + 1) == fg) ? bg : fg;
-	*(dest+2) = (*(dest + 2) == fg) ? bg : fg;
-	*(dest+3) = (*(dest + 3) == fg) ? bg : fg;
-	*(dest+4) = (*(dest + 4) == fg) ? bg : fg;
-	*(dest+5) = (*(dest + 5) == fg) ? bg : fg;
-	*(dest+6) = (*(dest + 6) == fg) ? bg : fg;
-	*(dest+7) = (*(dest + 7) == fg) ? bg : fg;
-    }
-}
-
-
-#ifdef MODULE
-int init_module(void)
-#else
-int fbcon_init_cyber(void)
-#endif
-{
-    return(fbcon_register_driver(&dispsw_cyber, 1));
-}
-
-#ifdef MODULE
-void cleanup_module(void)
-{
-    fbcon_unregister_driver(&dispsw_cyber);
-}
-#endif /* MODULE */
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-ilbm.c linux/drivers/video/fbcon-ilbm.c
--- v2.1.90/linux/drivers/video/fbcon-ilbm.c	Mon Feb 23 18:12:10 1998
+++ linux/drivers/video/fbcon-ilbm.c	Sat Mar 21 11:12:49 1998
@@ -16,33 +16,7 @@
 #include <linux/fb.h>
 
 #include "fbcon.h"
-
-
-    /*
-     *  Prototypes
-     */
-
-static int open_ilbm(struct display *p);
-static void release_ilbm(void);
-static void bmove_ilbm(struct display *p, int sy, int sx, int dy, int dx,
-		       int height, int width);
-static void clear_ilbm(struct vc_data *conp, struct display *p, int sy, int sx,
-		       int height, int width);
-static void putc_ilbm(struct vc_data *conp, struct display *p, int c, int yy,
-		      int xx);
-static void putcs_ilbm(struct vc_data *conp, struct display *p, const char *s,
-		       int count, int yy, int xx);
-static void rev_char_ilbm(struct display *p, int xx, int yy);
-
-
-    /*
-     *  `switch' for the low level operations
-     */
-
-static struct display_switch dispsw_ilbm = {
-    open_ilbm, release_ilbm, bmove_ilbm, clear_ilbm, putc_ilbm, putcs_ilbm,
-    rev_char_ilbm
-};
+#include "fbcon-ilbm.h"
 
 
     /*
@@ -56,11 +30,8 @@
      *  much performance loss?
      */
 
-static int open_ilbm(struct display *p)
+void fbcon_ilbm_setup(struct display *p)
 {
-    if (p->type != FB_TYPE_INTERLEAVED_PLANES || p->type_aux == 2)
-	return -EINVAL;
-
     if (p->line_length) {
 	p->next_line = p->line_length*p->var.bits_per_pixel;
 	p->next_plane = p->line_length;
@@ -68,24 +39,17 @@
 	p->next_line = p->type_aux;
 	p->next_plane = p->type_aux/p->var.bits_per_pixel;
     }
-    MOD_INC_USE_COUNT;
-    return 0;
 }
 
-static void release_ilbm(void)
-{
-    MOD_DEC_USE_COUNT;
-}
-
-static void bmove_ilbm(struct display *p, int sy, int sx, int dy, int dx,
-		       int height, int width)
+void fbcon_ilbm_bmove(struct display *p, int sy, int sx, int dy, int dx,
+		      int height, int width)
 {
     if (sx == 0 && dx == 0 && width == p->next_plane)
 	mymemmove(p->screen_base+dy*p->fontheight*p->next_line,
 		  p->screen_base+sy*p->fontheight*p->next_line,
 		  height*p->fontheight*p->next_line);
     else {
-	u_char *src, *dest;
+	u8 *src, *dest;
 	u_int i;
 
 	if (dy <= sy) {
@@ -108,10 +72,10 @@
     }
 }
 
-static void clear_ilbm(struct vc_data *conp, struct display *p, int sy, int sx,
-		       int height, int width)
+void fbcon_ilbm_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+		      int height, int width)
 {
-    u_char *dest;
+    u8 *dest;
     u_int i, rows;
     int bg, bg0;
 
@@ -130,12 +94,12 @@
     }
 }
 
-static void putc_ilbm(struct vc_data *conp, struct display *p, int c, int yy,
-			     int xx)
+void fbcon_ilbm_putc(struct vc_data *conp, struct display *p, int c, int yy,
+		     int xx)
 {
-    u_char *dest, *cdat;
+    u8 *dest, *cdat;
     u_int rows, i;
-    u_char d;
+    u8 d;
     int fg0, bg0, fg, bg;
 
     c &= 0xff;
@@ -150,16 +114,17 @@
 	fg = fg0;
 	bg = bg0;
 	for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) {
-	    if (bg & 1)
+	    if (bg & 1){
 		if (fg & 1)
 		    *dest = 0xff;
 		else
 		    *dest = ~d;
-	    else
+	    }else{
 		if (fg & 1)
 		    *dest = d;
 		else
 		    *dest = 0x00;
+	    }
 	    bg >>= 1;
 	    fg >>= 1;
 	}
@@ -181,13 +146,13 @@
      *  -- Geert
      */
 
-static void putcs_ilbm(struct vc_data *conp, struct display *p, const char *s,
-		       int count, int yy, int xx)
+void fbcon_ilbm_putcs(struct vc_data *conp, struct display *p, const char *s,
+		      int count, int yy, int xx)
 {
-    u_char *dest0, *dest, *cdat1, *cdat2, *cdat3, *cdat4;
+    u8 *dest0, *dest, *cdat1, *cdat2, *cdat3, *cdat4;
     u_int rows, i;
-    u_char c1, c2, c3, c4;
-    u_long d;
+    u8 c1, c2, c3, c4;
+    u32 d;
     int fg0, bg0, fg, bg;
 
     dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
@@ -206,16 +171,17 @@
 		fg = fg0;
 		bg = bg0;
 		for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) {
-		    if (bg & 1)
+		    if (bg & 1){
 			if (fg & 1)
 			    *dest = 0xff;
 			else
 			    *dest = ~d;
-		    else
+		    }else{
 			if (fg & 1)
 			    *dest = d;
 			else
 			    *dest = 0x00;
+		    }
 		    bg >>= 1;
 		    fg >>= 1;
 		}
@@ -232,20 +198,27 @@
 	    cdat3 = p->fontdata+c3*p->fontheight;
 	    cdat4 = p->fontdata+c4*p->fontheight;
 	    for (rows = p->fontheight; rows--;) {
+#if defined(__BIG_ENDIAN)
 		d = *cdat1++<<24 | *cdat2++<<16 | *cdat3++<<8 | *cdat4++;
+#elif defined(__LITTLE_ENDIAN)
+		d = *cdat1++ | *cdat2++<<8 | *cdat3++<<16 | *cdat4++<<32);
+#else
+#error FIXME: No endianness??
+#endif
 		fg = fg0;
 		bg = bg0;
 		for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) {
-		    if (bg & 1)
+		    if (bg & 1){
 			if (fg & 1)
-			    *(u_long *)dest = 0xffffffff;
+			    *(u32 *)dest = 0xffffffff;
 			else
-			    *(u_long *)dest = ~d;
-		    else
+			    *(u32 *)dest = ~d;
+		    }else{
 			if (fg & 1)
-			    *(u_long *)dest = d;
+			    *(u32 *)dest = d;
 			else
-			    *(u_long *)dest = 0x00000000;
+			    *(u32 *)dest = 0x00000000;
+		    }
 		    bg >>= 1;
 		    fg >>= 1;
 		}
@@ -257,9 +230,9 @@
 	}
 }
 
-static void rev_char_ilbm(struct display *p, int xx, int yy)
+void fbcon_ilbm_revc(struct display *p, int xx, int yy)
 {
-    u_char *dest, *dest0;
+    u8 *dest, *dest0;
     u_int rows, i;
     int mask;
 
@@ -283,18 +256,24 @@
 }
 
 
-#ifdef MODULE
-int init_module(void)
-#else
-int fbcon_init_ilbm(void)
-#endif
-{
-    return(fbcon_register_driver(&dispsw_ilbm, 0));
-}
+    /*
+     *  `switch' for the low level operations
+     */
 
-#ifdef MODULE
-void cleanup_module(void)
-{
-    fbcon_unregister_driver(&dispsw_ilbm);
-}
-#endif /* MODULE */
+struct display_switch fbcon_ilbm = {
+    fbcon_ilbm_setup, fbcon_ilbm_bmove, fbcon_ilbm_clear, fbcon_ilbm_putc,
+    fbcon_ilbm_putcs, fbcon_ilbm_revc
+};
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(fbcon_ilbm);
+EXPORT_SYMBOL(fbcon_ilbm_setup);
+EXPORT_SYMBOL(fbcon_ilbm_bmove);
+EXPORT_SYMBOL(fbcon_ilbm_clear);
+EXPORT_SYMBOL(fbcon_ilbm_putc);
+EXPORT_SYMBOL(fbcon_ilbm_putcs);
+EXPORT_SYMBOL(fbcon_ilbm_revc);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-ilbm.h linux/drivers/video/fbcon-ilbm.h
--- v2.1.90/linux/drivers/video/fbcon-ilbm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-ilbm.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,15 @@
+    /*
+     *  Amiga interleaved bitplanes (ilbm)
+     */
+
+extern struct display_switch fbcon_ilbm;
+extern void fbcon_ilbm_setup(struct display *p);
+extern void fbcon_ilbm_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			     int height, int width);
+extern void fbcon_ilbm_clear(struct vc_data *conp, struct display *p, int sy,
+			     int sx, int height, int width);
+extern void fbcon_ilbm_putc(struct vc_data *conp, struct display *p, int c,
+			    int yy, int xx);
+extern void fbcon_ilbm_putcs(struct vc_data *conp, struct display *p,
+			     const char *s, int count, int yy, int xx);
+extern void fbcon_ilbm_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-iplan2p2.c linux/drivers/video/fbcon-iplan2p2.c
--- v2.1.90/linux/drivers/video/fbcon-iplan2p2.c	Mon Feb 23 18:12:10 1998
+++ linux/drivers/video/fbcon-iplan2p2.c	Sat Mar 21 11:12:49 1998
@@ -14,41 +14,13 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
+#include <linux/config.h>
 #include <linux/fb.h>
 
-#include "fbcon.h"
-
-
-#ifndef __mc68000__
-#error No support for non-m68k yet
-#endif
-
-
-    /*
-     *  Prototypes
-     */
-
-static int open_iplan2p2(struct display *p);
-static void release_iplan2p2(void);
-static void bmove_iplan2p2(struct display *p, int sy, int sx, int dy, int dx,
-			   int height, int width);
-static void clear_iplan2p2(struct vc_data *conp, struct display *p, int sy,
-			   int sx, int height, int width);
-static void putc_iplan2p2(struct vc_data *conp, struct display *p, int c,
-			  int yy, int xx);
-static void putcs_iplan2p2(struct vc_data *conp, struct display *p,
-			   const char *s, int count, int yy, int xx);
-static void rev_char_iplan2p2(struct display *display, int xx, int yy);
-
-
-    /*
-     *  `switch' for the low level operations
-     */
+#include <asm/byteorder.h>
 
-static struct display_switch dispsw_iplan2p2 = {
-    open_iplan2p2, release_iplan2p2, bmove_iplan2p2, clear_iplan2p2,
-    putc_iplan2p2, putcs_iplan2p2, rev_char_iplan2p2
-};
+#include "fbcon.h"
+#include "fbcon-iplan2p2.h"
 
 
     /*
@@ -65,8 +37,19 @@
      *  The intensity bit (b3) is shifted into b1.
      */
 
-#define	COLOR_2P(c)	(((c & 7) >= 3 && (c & 7) != 4) | (c & 8) >> 2)
+static const u8 color_2p[] = { 0, 0, 0, 1, 0, 1, 1, 1, 2, 2, 2, 3, 2, 3, 3, 3 };
+#define	COLOR_2P(c)	color_2p[c]
 
+/* Perform the m68k movepw operation.  */
+static inline void movepw(u8 *d, u16 val)
+{
+#if defined __mc68000__ && !defined CONFIG_OPTIMIZE_060
+    asm volatile ("movepw %1,%0@(0)" : : "a" (d), "d" (val));
+#else
+    d[0] = (val >> 16) & 0xff;
+    d[2] = val & 0xff;
+#endif
+}
 
 /* Sets the bytes in the visible column at d, height h, to the value
  * val for a 2 plane screen. The the bis of the color in 'color' are
@@ -77,16 +60,13 @@
  *   *(d+2) = (color & 2) ? 0xff : 0;
  */
 
-static __inline__ void memclear_2p_col(void *d, size_t h, u_short val, int bpr)
+static __inline__ void memclear_2p_col(void *d, size_t h, u16 val, int bpr)
 {
-#ifdef __mc68000__
-    __asm__ __volatile__ ("1: movepw %4,%0@(0)\n\t"
-			  "addal  %5,%0\n\t"
-			  "dbra	  %1,1b"
-			  : "=a" (d), "=d" (h)
-			  : "0" (d), "1" (h - 1), "d" (val), "r" (bpr));
-#else /* !m68k */
-#endif /* !m68k */
+    u8 *dd = d;
+    do {
+	movepw(dd, val);
+	dd += bpr;
+    } while (--h);
 }
 
 /* Sets a 2 plane region from 'd', length 'count' bytes, to the color
@@ -98,9 +78,9 @@
  *   *(d+2) = *(d+3) = (color & 2) ? 0xff : 0;
  */
 
-static __inline__ void memset_even_2p(void *d, size_t count, u_long val)
+static __inline__ void memset_even_2p(void *d, size_t count, u32 val)
 {
-    u_long *dd = d;
+    u32 *dd = d;
 
     count /= 4;
     while (count--)
@@ -111,7 +91,7 @@
 
 static __inline__ void memmove_2p_col (void *d, void *s, int h, int bpr)
 {
-    u_char *dd = d, *ss = s;
+    u8 *dd = d, *ss = s;
 
     while (h--) {
 	dd[0] = ss[0];
@@ -124,81 +104,54 @@
 
 /* This expands a 2 bit color into a short for movepw (2 plane) operations. */
 
-static __inline__ u_short expand2w(u_char c)
-{
-    u_short	rv;
+static const u16 two2byte[] = {
+    0x0000, 0xff00, 0x00ff, 0xffff
+};
 
-#ifdef __mc68000__
-    __asm__ __volatile__ ("lsrb	 #1,%2\n\t"
-			  "scs	 %0\n\t"
-			  "lsll	 #8,%0\n\t"
-			  "lsrb	 #1,%2\n\t"
-			  "scs	 %0\n\t"
-			  : "=&d" (rv), "=d" (c)
-			  : "1" (c));
-#endif /* !m68k */
-    return(rv);
+static __inline__ u16 expand2w(u8 c)
+{
+    return two2byte[c];
 }
 
+
 /* This expands a 2 bit color into one long for a movel operation
  * (2 planes).
  */
 
-static __inline__ u_long expand2l(u_char c)
-{
-    u_long	rv;
+static const u32 two2word[] = {
+#ifndef __LITTLE_ENDIAN
+    0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
+#else
+    0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
+#endif
+};
 
-#ifdef __mc68000__
-    __asm__ __volatile__ ("lsrb	 #1,%2\n\t"
-			  "scs	 %0\n\t"
-			  "extw	 %0\n\t"
-			  "swap	 %0\n\t"
-			  "lsrb	 #1,%2\n\t"
-			  "scs	 %0\n\t"
-			  "extw	 %0\n\t"
-			  : "=&d" (rv), "=d" (c)
-			  : "1" (c));
-#endif /* !m68k */
-    return rv;
+static __inline__ u32 expand2l(u8 c)
+{
+    return two2word[c];
 }
 
 
 /* This duplicates a byte 2 times into a short. */
 
-static __inline__ u_short dup2w(u_char c)
+static __inline__ u16 dup2w(u8 c)
 {
-    ushort  rv;
+    u16 rv;
 
-#ifdef __mc68000__
-    __asm__ __volatile__ ("moveb  %1,%0\n\t"
-			  "lslw   #8,%0\n\t"
-			  "moveb  %1,%0\n\t"
-			  : "=&d" (rv)
-			  : "d" (c));
-#endif /* !m68k */
-    return( rv );
+    rv = c;
+    rv |= c << 8;
+    return rv;
 }
 
 
-static int open_iplan2p2(struct display *p)
+void fbcon_iplan2p2_setup(struct display *p)
 {
-    if (p->type != FB_TYPE_INTERLEAVED_PLANES || p->type_aux != 2 ||
-	p->var.bits_per_pixel != 2)
-	return -EINVAL;
-
     p->next_line = p->var.xres_virtual>>2;
     p->next_plane = 2;
-    MOD_INC_USE_COUNT;
-    return 0;
 }
 
-static void release_iplan2p2(void)
-{
-    MOD_DEC_USE_COUNT;
-}
-
-static void bmove_iplan2p2(struct display *p, int sy, int sx, int dy, int dx,
-			   int height, int width)
+void fbcon_iplan2p2_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			  int height, int width)
 {
     /*  bmove() has to distinguish two major cases: If both, source and
      *  destination, start at even addresses or both are at odd
@@ -212,7 +165,7 @@
      *  all movements by memmove_col().
      */
 
-    if (sx == 0 && dx == 0 && width == p->next_line/2) {
+    if (sx == 0 && dx == 0 && width * 2 == p->next_line) {
 	/*  Special (but often used) case: Moving whole lines can be
 	 *  done with memmove()
 	 */
@@ -221,8 +174,8 @@
 		  p->next_line * height * p->fontheight);
     } else {
 	int rows, cols;
-	u_char *src;
-	u_char *dst;
+	u8 *src;
+	u8 *dst;
 	int bytes = p->next_line;
 	int linesize = bytes * p->fontheight;
 	u_int colsize  = height * p->fontheight;
@@ -298,21 +251,21 @@
     }
 }
 
-static void clear_iplan2p2(struct vc_data *conp, struct display *p, int sy,
-			   int sx, int height, int width)
+void fbcon_iplan2p2_clear(struct vc_data *conp, struct display *p, int sy,
+			  int sx, int height, int width)
 {
-    ulong offset;
-    u_char *start;
+    u32 offset;
+    u8 *start;
     int rows;
     int bytes = p->next_line;
     int lines = height * p->fontheight;
-    ulong size;
-    u_long cval;
-    u_short pcval;
+    u32 size;
+    u32 cval;
+    u16 pcval;
 
     cval = expand2l (COLOR_2P (attr_bgcol_ec(p,conp)));
 
-    if (sx == 0 && width == bytes/2) {
+    if (sx == 0 && width * 2 == bytes) {
 	offset = sy * bytes * p->fontheight;
 	size = lines * bytes;
 	memset_even_2p(p->screen_base+offset, size, cval);
@@ -344,14 +297,14 @@
     }
 }
 
-static void putc_iplan2p2(struct vc_data *conp, struct display *p, int c,
-			  int yy, int xx)
+void fbcon_iplan2p2_putc(struct vc_data *conp, struct display *p, int c,
+			 int yy, int xx)
 {
-    u_char *dest;
-    u_char *cdat;
+    u8 *dest;
+    u8 *cdat;
     int rows;
     int bytes = p->next_line;
-    ulong eorx, fgx, bgx, fdx;
+    u16 eorx, fgx, bgx, fdx;
 
     c &= 0xff;
 
@@ -364,22 +317,18 @@
 
     for (rows = p->fontheight ; rows-- ; dest += bytes) {
 	fdx = dup2w(*cdat++);
-#ifdef __mc68000__
-	__asm__ __volatile__ ("movepw %1,%0@(0)"
-			      : /* no outputs */
-			      : "a" (dest), "d" ((fdx & eorx) ^ bgx));
-#endif /* !m68k */
+	movepw(dest, (fdx & eorx) ^ bgx);
     }
 }
 
-static void putcs_iplan2p2(struct vc_data *conp, struct display *p,
-			   const char *s, int count, int yy, int xx)
+void fbcon_iplan2p2_putcs(struct vc_data *conp, struct display *p,
+			  const char *s, int count, int yy, int xx)
 {
-    u_char *dest, *dest0;
-    u_char *cdat, c;
+    u8 *dest, *dest0;
+    u8 *cdat, c;
     int rows;
     int bytes;
-    ulong eorx, fgx, bgx, fdx;
+    u16 eorx, fgx, bgx, fdx;
 
     bytes = p->next_line;
     dest0 = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*4 + (xx & 1);
@@ -393,19 +342,15 @@
 
 	for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
 	    fdx = dup2w(*cdat++);
-#ifdef __mc68000__
-	    __asm__ __volatile__ ("movepw %1,%0@(0)"
-				  : /* no outputs */
-				  : "a" (dest), "d" ((fdx & eorx) ^ bgx));
-#endif /* !m68k */
+	    movepw(dest, (fdx & eorx) ^ bgx);
 	}
 	INC_2P(dest0);
     }
 }
 
-static void rev_char_iplan2p2(struct display *p, int xx, int yy)
+void fbcon_iplan2p2_revc(struct display *p, int xx, int yy)
 {
-    u_char *dest;
+    u8 *dest;
     int j;
     int bytes;
 
@@ -425,18 +370,24 @@
 }
 
 
-#ifdef MODULE
-int init_module(void)
-#else
-int fbcon_init_iplan2p2(void)
-#endif
-{
-    return(fbcon_register_driver(&dispsw_iplan2p2, 0));
-}
+    /*
+     *  `switch' for the low level operations
+     */
 
-#ifdef MODULE
-void cleanup_module(void)
-{
-    fbcon_unregister_driver(&dispsw_iplan2p2);
-}
-#endif /* MODULE */
+struct display_switch fbcon_iplan2p2 = {
+    fbcon_iplan2p2_setup, fbcon_iplan2p2_bmove, fbcon_iplan2p2_clear,
+    fbcon_iplan2p2_putc, fbcon_iplan2p2_putcs, fbcon_iplan2p2_revc
+};
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(fbcon_iplan2p2);
+EXPORT_SYMBOL(fbcon_iplan2p2_setup);
+EXPORT_SYMBOL(fbcon_iplan2p2_bmove);
+EXPORT_SYMBOL(fbcon_iplan2p2_clear);
+EXPORT_SYMBOL(fbcon_iplan2p2_putc);
+EXPORT_SYMBOL(fbcon_iplan2p2_putcs);
+EXPORT_SYMBOL(fbcon_iplan2p2_revc);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-iplan2p2.h linux/drivers/video/fbcon-iplan2p2.h
--- v2.1.90/linux/drivers/video/fbcon-iplan2p2.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-iplan2p2.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,15 @@
+    /*
+     *  Atari interleaved bitplanes (2 planes) (iplan2p2)
+     */
+
+extern struct display_switch fbcon_iplan2p2;
+extern void fbcon_iplan2p2_setup(struct display *p);
+extern void fbcon_iplan2p2_bmove(struct display *p, int sy, int sx, int dy,
+				 int dx, int height, int width);
+extern void fbcon_iplan2p2_clear(struct vc_data *conp, struct display *p,
+				 int sy, int sx, int height, int width);
+extern void fbcon_iplan2p2_putc(struct vc_data *conp, struct display *p, int c,
+				int yy, int xx);
+extern void fbcon_iplan2p2_putcs(struct vc_data *conp, struct display *p,
+				 const char *s, int count, int yy, int xx);
+extern void fbcon_iplan2p2_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-iplan2p4.c linux/drivers/video/fbcon-iplan2p4.c
--- v2.1.90/linux/drivers/video/fbcon-iplan2p4.c	Mon Feb 23 18:12:10 1998
+++ linux/drivers/video/fbcon-iplan2p4.c	Sat Mar 21 11:12:49 1998
@@ -14,41 +14,13 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
+#include <linux/config.h>
 #include <linux/fb.h>
 
-#include "fbcon.h"
-
-
-#ifndef __mc68000__
-#error No support for non-m68k yet
-#endif
+#include <asm/byteorder.h>
 
-
-    /*
-     *  Prototypes
-     */
-
-static int open_iplan2p4(struct display *p);
-static void release_iplan2p4(void);
-static void bmove_iplan2p4(struct display *p, int sy, int sx, int dy, int dx,
-			   int height, int width);
-static void clear_iplan2p4(struct vc_data *conp, struct display *p, int sy,
-			   int sx, int height, int width);
-static void putc_iplan2p4(struct vc_data *conp, struct display *p, int c,
-			  int yy, int xx);
-static void putcs_iplan2p4(struct vc_data *conp, struct display *p,
-			   const char *s, int count, int yy, int xx);
-static void rev_char_iplan2p4(struct display *p, int xx, int yy);
-
-
-    /*
-     *  `switch' for the low level operations
-     */
-
-static struct display_switch dispsw_iplan2p4 = {
-    open_iplan2p4, release_iplan2p4, bmove_iplan2p4, clear_iplan2p4,
-    putc_iplan2p4, putcs_iplan2p4, rev_char_iplan2p4
-};
+#include "fbcon.h"
+#include "fbcon-iplan2p4.h"
 
 
     /*
@@ -60,6 +32,18 @@
 #define	INC_4P(p)	do { if (!((long)(++(p)) & 1)) (p) += 6; } while(0)
 #define	DEC_4P(p)	do { if ((long)(--(p)) & 1) (p) -= 6; } while(0)
 
+/* Perform the m68k movepl operation.  */
+static inline void movepl(u8 *d, u32 val)
+{
+#if defined __mc68000__ && !defined CONFIG_OPTIMIZE_060
+    asm volatile ("movepl %1,%0@(0)" : : "a" (d), "d" (val));
+#else
+    d[0] = (val >> 24) & 0xff;
+    d[2] = (val >> 16) & 0xff;
+    d[4] = (val >> 8) & 0xff;
+    d[6] = val & 0xff;
+#endif
+}
 
 /* Sets the bytes in the visible column at d, height h, to the value
  * val for a 4 plane screen. The the bis of the color in 'color' are
@@ -72,15 +56,13 @@
  *   *(d+6) = (color & 8) ? 0xff : 0;
  */
 
-static __inline__ void memclear_4p_col(void *d, size_t h, u_long val, int bpr)
+static __inline__ void memclear_4p_col(void *d, size_t h, u32 val, int bpr)
 {
-#ifdef __mc68000__
-    __asm__ __volatile__ ("1: movepl %4,%0@(0)\n\t"
-			  "addal  %5,%0\n\t"
-			  "dbra	  %1,1b"
-			  : "=a" (d), "=d" (h)
-			  : "0" (d), "1" (h - 1), "d" (val), "r" (bpr));
-#endif /* !m68k */
+    u8 *dd = d;
+    do {
+	movepl(dd, val);
+	dd += bpr;
+    } while (--h);
 }
 
 /* Sets a 4 plane region from 'd', length 'count' bytes, to the color
@@ -94,10 +76,10 @@
  *   *(d+6) = *(d+7) = (color & 8) ? 0xff : 0;
  */
 
-static __inline__ void memset_even_4p(void *d, size_t count, u_long val1,
-                                      u_long val2)
+static __inline__ void memset_even_4p(void *d, size_t count, u32 val1,
+                                      u32 val2)
 {
-    u_long *dd = d;
+    u32 *dd = d;
 
     count /= 8;
     while (count--) {
@@ -110,7 +92,7 @@
 
 static __inline__ void memmove_4p_col (void *d, void *s, int h, int bpr)
 {
-    u_char *dd = d, *ss = s;
+    u8 *dd = d, *ss = s;
 
     while (h--) {
 	dd[0] = ss[0];
@@ -125,99 +107,59 @@
 
 /* This expands a 4 bit color into a long for movepl (4 plane) operations. */
 
-static __inline__ u_long expand4l(u_char c)
-{
-    u_long rv;
+static const u32 four2byte[] = {
+    0x00000000, 0xff000000, 0x00ff0000, 0xffff0000,
+    0x0000ff00, 0xff00ff00, 0x00ffff00, 0xffffff00,
+    0x000000ff, 0xff0000ff, 0x00ff00ff, 0xffff00ff,
+    0x0000ffff, 0xff00ffff, 0x00ffffff, 0xffffffff
+};
 
-#ifdef __mc68000__
-    __asm__ __volatile__ ("lsrb	 #1,%2\n\t"
-			  "scs	 %0\n\t"
-			  "lsll	 #8,%0\n\t"
-			  "lsrb	 #1,%2\n\t"
-			  "scs	 %0\n\t"
-			  "lsll	 #8,%0\n\t"
-			  "lsrb	 #1,%2\n\t"
-			  "scs	 %0\n\t"
-			  "lsll	 #8,%0\n\t"
-			  "lsrb	 #1,%2\n\t"
-			  "scs	 %0\n\t"
-			  : "=&d" (rv), "=d" (c)
-			  : "1" (c));
-#endif /* !m68k */
-    return(rv);
+static __inline__ u32 expand4l(u8 c)
+{
+    return four2byte[c];
 }
 
+
 /* This expands a 4 bit color into two longs for two movel operations
  * (4 planes).
  */
 
-static __inline__ void expand4dl(u_char c, u_long *ret1, u_long *ret2)
-{
-    u_long	rv1, rv2;
+static const u32 two2word[] = {
+#ifndef __LITTLE_ENDIAN
+    0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff,
+#else
+    0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff,
+#endif
+};
 
-#ifdef __mc68000__
-    __asm__ __volatile__ ("lsrb	 #1,%3\n\t"
-			  "scs	 %0\n\t"
-			  "extw	 %0\n\t"
-			  "swap	 %0\n\t"
-			  "lsrb	 #1,%3\n\t"
-			  "scs	 %0\n\t"
-			  "extw	 %0\n\t"
-			  "lsrb	 #1,%3\n\t"
-			  "scs	 %1\n\t"
-			  "extw	 %1\n\t"
-			  "swap	 %1\n\t"
-			  "lsrb	 #1,%3\n\t"
-			  "scs   %1\n\t"
-			  "extw	 %1"
-			  : "=&d" (rv1), "=&d" (rv2), "=d" (c)
-			  : "2" (c));
-#endif /* !m68k */
-    *ret1 = rv1;
-    *ret2 = rv2;
+static __inline__ void expand4dl(u8 c, u32 *ret1, u32 *ret2)
+{
+    *ret1 = two2word[c & 3];
+    *ret2 = two2word[c >> 2];
 }
 
 
 /* This duplicates a byte 4 times into a long. */
 
-static __inline__ u_long dup4l(u_char c)
+static __inline__ u32 dup4l(u8 c)
 {
-    ushort	tmp;
-    ulong	rv;
+    u32 rv;
 
-#ifdef __mc68000__
-    __asm__ __volatile__ ("moveb  %2,%0\n\t"
-			  "lslw   #8,%0\n\t"
-			  "moveb  %2,%0\n\t"
-			  "movew  %0,%1\n\t"
-			  "swap   %0\n\t"
-			  "movew  %1,%0"
-			  : "=&d" (rv), "=d" (tmp)
-			  : "d" (c));
-#endif /* !m68k */
-    return(rv);
+    rv = c;
+    rv |= rv << 8;
+    rv |= rv << 16;
+    return rv;
 }
 
 
-static int open_iplan2p4(struct display *p)
-{
-    if (p->type != FB_TYPE_INTERLEAVED_PLANES || p->type_aux != 2 ||
-	p->var.bits_per_pixel != 4)
-	return -EINVAL;
-
+void fbcon_iplan2p4_setup(struct display *p)
+{
     p->next_line = p->var.xres_virtual>>1;
     p->next_plane = 2;
-    MOD_INC_USE_COUNT;
-    return 0;
 }
 
-static void release_iplan2p4(void)
-{
-    MOD_DEC_USE_COUNT;
-}
-
-static void bmove_iplan2p4(struct display *p, int sy, int sx, int dy, int dx,
-			   int height, int width)
+void fbcon_iplan2p4_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			  int height, int width)
 {
     /*  bmove() has to distinguish two major cases: If both, source and
      *  destination, start at even addresses or both are at odd
@@ -231,7 +173,7 @@
      *  all movements by memmove_col().
      */
 
-    if (sx == 0 && dx == 0 && width == p->next_line/4) {
+    if (sx == 0 && dx == 0 && width * 4 == p->next_line) {
 	/*  Special (but often used) case: Moving whole lines can be
 	 *done with memmove()
 	 */
@@ -240,8 +182,8 @@
 		  p->next_line * height * p->fontheight);
     } else {
 	int rows, cols;
-	u_char *src;
-	u_char *dst;
+	u8 *src;
+	u8 *dst;
 	int bytes = p->next_line;
 	int linesize = bytes * p->fontheight;
 	u_int colsize  = height * p->fontheight;
@@ -320,20 +262,20 @@
     }
 }
 
-static void clear_iplan2p4(struct vc_data *conp, struct display *p, int sy,
-			   int sx, int height, int width)
+void fbcon_iplan2p4_clear(struct vc_data *conp, struct display *p, int sy,
+			  int sx, int height, int width)
 {
-    ulong offset;
-    u_char *start;
+    u32 offset;
+    u8 *start;
     int rows;
     int bytes = p->next_line;
     int lines = height * p->fontheight;
-    ulong size;
-    u_long cval1, cval2, pcval;
+    u32 size;
+    u32 cval1, cval2, pcval;
 
     expand4dl(attr_bgcol_ec(p,conp), &cval1, &cval2);
 
-    if (sx == 0 && width == bytes/4) {
+    if (sx == 0 && width * 4 == bytes) {
 	offset = sy * bytes * p->fontheight;
 	size = lines * bytes;
 	memset_even_4p(p->screen_base+offset, size, cval1, cval2);
@@ -365,14 +307,14 @@
     }
 }
 
-static void putc_iplan2p4(struct vc_data *conp, struct display *p, int c,
-			  int yy, int xx)
+void fbcon_iplan2p4_putc(struct vc_data *conp, struct display *p, int c,
+			 int yy, int xx)
 {
-    u_char *dest;
-    u_char *cdat;
+    u8 *dest;
+    u8 *cdat;
     int rows;
     int bytes = p->next_line;
-    ulong eorx, fgx, bgx, fdx;
+    u32 eorx, fgx, bgx, fdx;
 
     c &= 0xff;
 
@@ -385,22 +327,18 @@
 
     for(rows = p->fontheight ; rows-- ; dest += bytes) {
 	fdx = dup4l(*cdat++);
-#ifdef __mc68000__
-	__asm__ __volatile__ ("movepl %1,%0@(0)"
-			      : /* no outputs */
-			      : "a" (dest), "d" ((fdx & eorx) ^ bgx));
-#endif /* !m68k */
+	movepl(dest, (fdx & eorx) ^ bgx);
     }
 }
 
-static void putcs_iplan2p4(struct vc_data *conp, struct display *p,
-			   const char *s, int count, int yy, int xx)
+void fbcon_iplan2p4_putcs(struct vc_data *conp, struct display *p,
+			  const char *s, int count, int yy, int xx)
 {
-    u_char *dest, *dest0;
-    u_char *cdat, c;
+    u8 *dest, *dest0;
+    u8 *cdat, c;
     int rows;
     int bytes;
-    ulong eorx, fgx, bgx, fdx;
+    u32 eorx, fgx, bgx, fdx;
 
     bytes = p->next_line;
     dest0 = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*8 + (xx & 1);
@@ -421,19 +359,15 @@
 
 	for(rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
 	    fdx = dup4l(*cdat++);
-#ifdef __mc68000__
-	    __asm__ __volatile__ ("movepl %1,%0@(0)"
-				  : /* no outputs */
-				  : "a" (dest), "d" ((fdx & eorx) ^ bgx));
-#endif /* !m68k */
+	    movepl(dest, (fdx & eorx) ^ bgx);
 	}
 	INC_4P(dest0);
     }
 }
 
-static void rev_char_iplan2p4(struct display *p, int xx, int yy)
+void fbcon_iplan2p4_revc(struct display *p, int xx, int yy)
 {
-    u_char *dest;
+    u8 *dest;
     int j;
     int bytes;
 
@@ -456,18 +390,24 @@
 }
 
 
-#ifdef MODULE
-int init_module(void)
-#else
-int fbcon_init_iplan2p4(void)
-#endif
-{
-    return(fbcon_register_driver(&dispsw_iplan2p4, 0));
-}
+    /*
+     *  `switch' for the low level operations
+     */
 
-#ifdef MODULE
-void cleanup_module(void)
-{
-    fbcon_unregister_driver(&dispsw_iplan2p4);
-}
-#endif /* MODULE */
+struct display_switch fbcon_iplan2p4 = {
+    fbcon_iplan2p4_setup, fbcon_iplan2p4_bmove, fbcon_iplan2p4_clear,
+    fbcon_iplan2p4_putc, fbcon_iplan2p4_putcs, fbcon_iplan2p4_revc
+};
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(fbcon_iplan2p4);
+EXPORT_SYMBOL(fbcon_iplan2p4_setup);
+EXPORT_SYMBOL(fbcon_iplan2p4_bmove);
+EXPORT_SYMBOL(fbcon_iplan2p4_clear);
+EXPORT_SYMBOL(fbcon_iplan2p4_putc);
+EXPORT_SYMBOL(fbcon_iplan2p4_putcs);
+EXPORT_SYMBOL(fbcon_iplan2p4_revc);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-iplan2p4.h linux/drivers/video/fbcon-iplan2p4.h
--- v2.1.90/linux/drivers/video/fbcon-iplan2p4.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-iplan2p4.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,15 @@
+    /*
+     *  Atari interleaved bitplanes (4 planes) (iplan2p4)
+     */
+
+extern struct display_switch fbcon_iplan2p4;
+extern void fbcon_iplan2p4_setup(struct display *p);
+extern void fbcon_iplan2p4_bmove(struct display *p, int sy, int sx, int dy,
+				 int dx, int height, int width);
+extern void fbcon_iplan2p4_clear(struct vc_data *conp, struct display *p,
+				 int sy, int sx, int height, int width);
+extern void fbcon_iplan2p4_putc(struct vc_data *conp, struct display *p, int c,
+				int yy, int xx);
+extern void fbcon_iplan2p4_putcs(struct vc_data *conp, struct display *p,
+				 const char *s, int count, int yy, int xx);
+extern void fbcon_iplan2p4_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-iplan2p8.c linux/drivers/video/fbcon-iplan2p8.c
--- v2.1.90/linux/drivers/video/fbcon-iplan2p8.c	Mon Feb 23 18:12:10 1998
+++ linux/drivers/video/fbcon-iplan2p8.c	Sat Mar 21 11:12:49 1998
@@ -14,41 +14,13 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
+#include <linux/config.h>
 #include <linux/fb.h>
 
-#include "fbcon.h"
-
-
-#ifndef __mc68000__
-#error No support for non-m68k yet
-#endif
-
-
-    /*
-     *  Prototypes
-     */
-
-static int open_iplan2p8(struct display *p);
-static void release_iplan2p8(void);
-static void bmove_iplan2p8(struct display *p, int sy, int sx, int dy, int dx,
-			   int height, int width);
-static void clear_iplan2p8(struct vc_data *conp, struct display *p, int sy,
-			   int sx, int height, int width);
-static void putc_iplan2p8(struct vc_data *conp, struct display *p, int c,
-			  int yy, int xx);
-static void putcs_iplan2p8(struct vc_data *conp, struct display *p,
-			   const char *s, int count, int yy, int xx);
-static void rev_char_iplan2p8(struct display *display, int xx, int yy);
-
-
-    /*
-     *  `switch' for the low level operations
-     */
+#include <asm/byteorder.h>
 
-static struct display_switch dispsw_iplan2p8 = {
-    open_iplan2p8, release_iplan2p8, bmove_iplan2p8, clear_iplan2p8,
-    putc_iplan2p8, putcs_iplan2p8, rev_char_iplan2p8
-};
+#include "fbcon.h"
+#include "fbcon-iplan2p8.h"
 
 
     /*
@@ -65,9 +37,26 @@
 #define	INC_8P(p)	do { if (!((long)(++(p)) & 1)) (p) += 14; } while(0)
 #define	DEC_8P(p)	do { if ((long)(--(p)) & 1) (p) -= 14; } while(0)
 
+/* Perform the m68k movepl operation extended to 64 bits.  */
+static inline void movepl2(u8 *d, u32 val1, u32 val2)
+{
+#if defined __mc68000__ && !defined CONFIG_OPTIMIZE_060
+    asm volatile ("movepl %1,%0@(0); movepl %2,%0@(8)"
+		  : : "a" (d), "d" (val1), "d" (val2));
+#else
+    d[0] = (val1 >> 24) & 0xff;
+    d[2] = (val1 >> 16) & 0xff;
+    d[4] = (val1 >> 8) & 0xff;
+    d[6] = val1 & 0xff;
+    d[8] = (val2 >> 24) & 0xff;
+    d[10] = (val2 >> 16) & 0xff;
+    d[12] = (val2 >> 8) & 0xff;
+    d[14] = val2 & 0xff;
+#endif
+}
 
 /* Sets the bytes in the visible column at d, height h, to the value
- * val1,val2 for a 8 plane screen. The the bis of the color in 'color' are
+ * val1,val2 for a 8 plane screen. The bits of the color in 'color' are
  * moved (8 times) to the respective bytes. This means:
  *
  * for(h times; d += bpr)
@@ -81,18 +70,14 @@
  *   *(d+14) = (color & 128) ? 0xff : 0;
  */
 
-static __inline__ void memclear_8p_col(void *d, size_t h, u_long val1,
-                                       u_long val2, int bpr)
+static __inline__ void memclear_8p_col(void *d, size_t h, u32 val1,
+                                       u32 val2, int bpr)
 {
-#ifdef __mc68000__
-    __asm__ __volatile__ ("1: movepl %4,%0@(0)\n\t"
-			  "movepl %5,%0@(8)\n\t"
-			  "addal  %6,%0\n\t"
-			  "dbra	  %1,1b"
-			  : "=a" (d), "=d" (h)
-			  : "0" (d), "1" (h - 1), "d" (val1), "d" (val2),
-			    "r" (bpr));
-#endif /* !m68k */
+    u8 *dd = d;
+    do {
+	movepl2(dd, val1, val2);
+	dd += bpr;
+    } while (--h);
 }
 
 /* Sets a 8 plane region from 'd', length 'count' bytes, to the color
@@ -110,10 +95,10 @@
  *   *(d+14) = *(d+15) = (color & 128) ? 0xff : 0;
  */
 
-static __inline__ void memset_even_8p(void *d, size_t count, u_long val1,
-                                      u_long val2, u_long val3, u_long val4)
+static __inline__ void memset_even_8p(void *d, size_t count, u32 val1,
+                                      u32 val2, u32 val3, u32 val4)
 {
-    u_long *dd = d;
+    u32 *dd = d;
 
     count /= 16;
     while (count--) {
@@ -128,7 +113,7 @@
 
 static __inline__ void memmove_8p_col (void *d, void *s, int h, int bpr)
 {
-    u_char *dd = d, *ss = s;
+    u8 *dd = d, *ss = s;
 
     while (h--) {
 	dd[0] = ss[0];
@@ -149,124 +134,64 @@
  * operations.
  */
 
-static __inline__ void expand8dl(u_char c, u_long *ret1, u_long *ret2)
+static const u32 four2long[] =
 {
-    u_long	rv1, rv2;
+    0x00000000, 0xff000000, 0x00ff0000, 0xffff0000,
+    0x0000ff00, 0xff00ff00, 0x00ffff00, 0xffffff00,
+    0x000000ff, 0xff0000ff, 0x00ff00ff, 0xffff00ff,
+    0x0000ffff, 0xff00ffff, 0x00ffffff, 0xffffffff,
+};
 
-#ifdef __mc68000__
-    __asm__ __volatile__ ("lsrb	 #1,%3\n\t"
-			  "scs	 %0\n\t"
-			  "lsll	 #8,%0\n\t"
-			  "lsrb	 #1,%3\n\t"
-			  "scs	 %0\n\t"
-			  "lsll	 #8,%0\n\t"
-			  "lsrb	 #1,%3\n\t"
-			  "scs	 %0\n\t"
-			  "lsll	 #8,%0\n\t"
-			  "lsrb	 #1,%3\n\t"
-			  "scs	 %0\n\t"
-			  "lsrb	 #1,%3\n\t"
-			  "scs	 %1\n\t"
-			  "lsll	 #8,%1\n\t"
-			  "lsrb	 #1,%3\n\t"
-			  "scs	 %1\n\t"
-			  "lsll	 #8,%1\n\t"
-			  "lsrb	 #1,%3\n\t"
-			  "scs	 %1\n\t"
-			  "lsll	 #8,%1\n\t"
-			  "lsrb	 #1,%3\n\t"
-			  "scs	 %1"
-			  : "=&d" (rv1), "=&d" (rv2),"=d" (c)
-			  : "2" (c));
-#endif /* !m68k */
-    *ret1 = rv1;
-    *ret2 = rv2;
+static __inline__ void expand8dl(u8 c, u32 *ret1, u32 *ret2)
+{
+    *ret1 = four2long[c & 15];
+    *ret2 = four2long[c >> 4];
 }
 
+
 /* This expands a 8 bit color into four longs for four movel operations
  * (8 planes).
  */
 
-#ifdef __mc68000__
-/* ++andreas: use macro to avoid taking address of return values */
-#define expand8ql(c, rv1, rv2, rv3, rv4) \
-    do {								\
-	u_char tmp = c;							\
-	__asm__ __volatile__ ("lsrb  #1,%5\n\t"				\
-			      "scs   %0\n\t"				\
-			      "extw  %0\n\t"				\
-			      "swap  %0\n\t"				\
-			      "lsrb  #1,%5\n\t"				\
-			      "scs   %0\n\t"				\
-			      "extw  %0\n\t"				\
-			      "lsrb  #1,%5\n\t"				\
-			      "scs   %1\n\t"				\
-			      "extw  %1\n\t"				\
-			      "swap  %1\n\t"				\
-			      "lsrb  #1,%5\n\t"				\
-			      "scs   %1\n\t"				\
-			      "extw  %1\n\t"				\
-			      "lsrb  #1,%5\n\t"				\
-			      "scs   %2\n\t"				\
-			      "extw  %2\n\t"				\
-			      "swap  %2\n\t"				\
-			      "lsrb  #1,%5\n\t"				\
-			      "scs   %2\n\t"				\
-			      "extw  %2\n\t"				\
-			      "lsrb  #1,%5\n\t"				\
-			      "scs   %3\n\t"				\
-			      "extw  %3\n\t"				\
-			      "swap  %3\n\t"				\
-			      "lsrb  #1,%5\n\t"				\
-			      "scs   %3\n\t"				\
-			      "extw  %3"				\
-			      : "=&d" (rv1), "=&d" (rv2), "=&d" (rv3),	\
-				"=&d" (rv4), "=d" (tmp)			\
-			      : "4" (tmp));				\
-    } while (0)
-#endif /* !m68k */
+static const u32 two2word[] =
+{
+#ifndef __LITTLE_ENDIAN
+    0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
+#else
+    0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
+#endif
+};
+
+static inline void expand8ql(u8 c, u32 *rv1, u32 *rv2, u32 *rv3, u32 *rv4)
+{
+    *rv1 = two2word[c & 4];
+    *rv2 = two2word[(c >> 2) & 4];
+    *rv3 = two2word[(c >> 4) & 4];
+    *rv4 = two2word[c >> 6];
+}
 
 
 /* This duplicates a byte 4 times into a long. */
 
-static __inline__ u_long dup4l(u_char c)
+static __inline__ u32 dup4l(u8 c)
 {
-    ushort	tmp;
-    ulong	rv;
+    u32 rv;
 
-#ifdef __mc68000__
-    __asm__ __volatile__ ("moveb  %2,%0\n\t"
-			  "lslw   #8,%0\n\t"
-			  "moveb  %2,%0\n\t"
-			  "movew  %0,%1\n\t"
-			  "swap   %0\n\t"
-			  "movew  %1,%0"
-			  : "=&d" (rv), "=d" (tmp)
-			  : "d" (c));
-#endif /* !m68k */
-    return(rv);
+    rv = c;
+    rv |= rv << 8;
+    rv |= rv << 16;
+    return rv;
 }
 
 
-static int open_iplan2p8(struct display *p)
-{
-    if (p->type != FB_TYPE_INTERLEAVED_PLANES || p->type_aux != 2 ||
-	p->var.bits_per_pixel != 8)
-	return -EINVAL;
-
+void fbcon_iplan2p8_setup(struct display *p)
+{
     p->next_line = p->var.xres_virtual;
     p->next_plane = 2;
-    MOD_INC_USE_COUNT;
-    return 0;
 }
 
-static void release_iplan2p8(void)
-{
-    MOD_DEC_USE_COUNT;
-}
-
-static void bmove_iplan2p8(struct display *p, int sy, int sx, int dy, int dx,
-			   int height, int width)
+void fbcon_iplan2p8_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			  int height, int width)
 {
     /*  bmove() has to distinguish two major cases: If both, source and
      *  destination, start at even addresses or both are at odd
@@ -280,7 +205,7 @@
      *  all movements by memmove_col().
      */
 
-     if (sx == 0 && dx == 0 && width == p->next_line/8) {
+     if (sx == 0 && dx == 0 && width * 8 == p->next_line) {
 	/*  Special (but often used) case: Moving whole lines can be
 	 *  done with memmove()
 	 */
@@ -289,8 +214,8 @@
 		     p->next_line * height * p->fontheight);
      } else {
 	int rows, cols;
-	u_char *src;
-	u_char *dst;
+	u8 *src;
+	u8 *dst;
 	int bytes = p->next_line;
 	int linesize = bytes * p->fontheight;
 	u_int colsize = height * p->fontheight;
@@ -369,20 +294,20 @@
     }
 }
 
-static void clear_iplan2p8(struct vc_data *conp, struct display *p, int sy,
-			   int sx, int height, int width)
+void fbcon_iplan2p8_clear(struct vc_data *conp, struct display *p, int sy,
+			  int sx, int height, int width)
 {
-    ulong offset;
-    u_char *start;
+    u32 offset;
+    u8 *start;
     int rows;
     int bytes = p->next_line;
     int lines = height * p->fontheight;
-    ulong size;
-    u_long cval1, cval2, cval3, cval4, pcval1, pcval2;
+    u32 size;
+    u32 cval1, cval2, cval3, cval4, pcval1, pcval2;
 
-    expand8ql(attr_bgcol_ec(p,conp), cval1, cval2, cval3, cval4);
+    expand8ql(attr_bgcol_ec(p,conp), &cval1, &cval2, &cval3, &cval4);
 
-    if (sx == 0 && width == bytes/8) {
+    if (sx == 0 && width * 8 == bytes) {
 	offset = sy * bytes * p->fontheight;
 	size    = lines * bytes;
 	memset_even_8p(p->screen_base+offset, size, cval1, cval2, cval3, cval4);
@@ -414,14 +339,14 @@
 	}
 }
 
-static void putc_iplan2p8(struct vc_data *conp, struct display *p, int c,
-			  int yy, int xx)
+void fbcon_iplan2p8_putc(struct vc_data *conp, struct display *p, int c,
+			 int yy, int xx)
 {
-    u_char *dest;
-    u_char *cdat;
+    u8 *dest;
+    u8 *cdat;
     int rows;
     int bytes = p->next_line;
-    ulong eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx;
+    u32 eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx;
 
     c &= 0xff;
 
@@ -434,24 +359,18 @@
 
     for(rows = p->fontheight ; rows-- ; dest += bytes) {
 	fdx = dup4l(*cdat++);
-#ifdef __mc68000__
-	__asm__ __volatile__ ("movepl %1,%0@(0)\n\t"
-			      "movepl %2,%0@(8)"
-			      : /* no outputs */
-			      : "a" (dest), "d" ((fdx & eorx1) ^ bgx1),
-				"d" ((fdx & eorx2) ^ bgx2) );
-#endif /* !m68k */
+	movepl2(dest, (fdx & eorx1) ^ bgx1, (fdx & eorx2) ^ bgx2);
     }
 }
 
-static void putcs_iplan2p8(struct vc_data *conp, struct display *p,
-			   const char *s, int count, int yy, int xx)
+void fbcon_iplan2p8_putcs(struct vc_data *conp, struct display *p,
+			  const char *s, int count, int yy, int xx)
 {
-    u_char *dest, *dest0;
-    u_char *cdat, c;
+    u8 *dest, *dest0;
+    u8 *cdat, c;
     int rows;
     int bytes;
-    ulong eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx;
+    u32 eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx;
 
     bytes = p->next_line;
     dest0 = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*16 +
@@ -475,21 +394,15 @@
 
 	for(rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
 	    fdx = dup4l(*cdat++);
-#ifdef __mc68000__
-	    __asm__ __volatile__ ("movepl %1,%0@(0)\n\t"
-				  "movepl %2,%0@(8)"
-				  : /* no outputs */
-				  : "a" (dest), "d" ((fdx & eorx1) ^ bgx1),
-				    "d" ((fdx & eorx2) ^ bgx2));
-#endif /* !m68k */
+	    movepl2(dest, (fdx & eorx1) ^ bgx1, (fdx & eorx2) ^ bgx2);
 	}
 	INC_8P(dest0);
     }
 }
 
-static void rev_char_iplan2p8(struct display *p, int xx, int yy)
+void fbcon_iplan2p8_revc(struct display *p, int xx, int yy)
 {
-    u_char *dest;
+    u8 *dest;
     int j;
     int bytes;
 
@@ -502,7 +415,7 @@
 	/*  This should really obey the individual character's
 	 *  background and foreground colors instead of simply
 	 *  inverting. For 8 plane mode, only the lower 4 bits of the
-	 *  color are inverted, because only that color registers have
+	 *  color are inverted, because only these color registers have
 	 *  been set up.
 	 */
 	dest[0] = ~dest[0];
@@ -514,18 +427,24 @@
 }
 
 
-#ifdef MODULE
-int init_module(void)
-#else
-int fbcon_init_iplan2p8(void)
-#endif
-{
-    return(fbcon_register_driver(&dispsw_iplan2p8, 0));
-}
+    /*
+     *  `switch' for the low level operations
+     */
 
-#ifdef MODULE
-void cleanup_module(void)
-{
-    fbcon_unregister_driver(&dispsw_iplan2p8);
-}
-#endif /* MODULE */
+struct display_switch fbcon_iplan2p8 = {
+    fbcon_iplan2p8_setup, fbcon_iplan2p8_bmove, fbcon_iplan2p8_clear,
+    fbcon_iplan2p8_putc, fbcon_iplan2p8_putcs, fbcon_iplan2p8_revc
+};
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(fbcon_iplan2p8);
+EXPORT_SYMBOL(fbcon_iplan2p8_setup);
+EXPORT_SYMBOL(fbcon_iplan2p8_bmove);
+EXPORT_SYMBOL(fbcon_iplan2p8_clear);
+EXPORT_SYMBOL(fbcon_iplan2p8_putc);
+EXPORT_SYMBOL(fbcon_iplan2p8_putcs);
+EXPORT_SYMBOL(fbcon_iplan2p8_revc);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-iplan2p8.h linux/drivers/video/fbcon-iplan2p8.h
--- v2.1.90/linux/drivers/video/fbcon-iplan2p8.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-iplan2p8.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,15 @@
+    /*
+     *  Atari interleaved bitplanes (8 planes) (iplan2p8)
+     */
+
+extern struct display_switch fbcon_iplan2p8;
+extern void fbcon_iplan2p8_setup(struct display *p);
+extern void fbcon_iplan2p8_bmove(struct display *p, int sy, int sx, int dy,
+				 int dx, int height, int width);
+extern void fbcon_iplan2p8_clear(struct vc_data *conp, struct display *p,
+				 int sy, int sx, int height, int width);
+extern void fbcon_iplan2p8_putc(struct vc_data *conp, struct display *p, int c,
+				int yy, int xx);
+extern void fbcon_iplan2p8_putcs(struct vc_data *conp, struct display *p,
+				 const char *s, int count, int yy, int xx);
+extern void fbcon_iplan2p8_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-mac.c linux/drivers/video/fbcon-mac.c
--- v2.1.90/linux/drivers/video/fbcon-mac.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-mac.c	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,513 @@
+/*
+ *  linux/drivers/video/fbcon-mac.c -- Low level frame buffer operations for 
+ *				       x bpp packed pixels, font width != 8
+ *
+ *	Created 26 Dec 1997 by Michael Schmitz
+ *	Based on the old macfb.c 6x11 code by Randy Thelen
+ *
+ *	This driver is significantly slower than the 8bit font drivers 
+ *	and would probably benefit from splitting into drivers for each depth.
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+
+#include "fbcon.h"
+#include "fbcon-mac.h"
+
+
+    /*
+     *  variable bpp packed pixels
+     */
+
+static void plot_pixel_mac(struct display *p, int bw, int pixel_x,
+			   int pixel_y);
+static int get_pixel_mac(struct display *p, int pixel_x, int pixel_y);
+
+void fbcon_mac_setup(struct display *p)
+{
+    if (p->line_length)
+	p->next_line = p->line_length;
+    else
+    	p->next_line = p->var.xres_virtual>>3;
+    p->next_plane = 0;
+}
+
+
+   /*
+    *    Macintosh
+    */
+#define PIXEL_BLACK_MAC          0
+#define PIXEL_WHITE_MAC          1
+#define PIXEL_INVERT_MAC         2
+
+void fbcon_mac_bmove(struct display *p, int sy, int sx, int dy, int dx,
+		     int height, int width)
+{
+   int i, j;
+   u8 *dest, *src;
+   int l,r,t,b,w,lo,s;
+   int dl,dr,dt,db,dw,dlo;
+   int move_up;
+
+   src = (u8 *) (p->screen_base + sy * p->fontheight * p->next_line);
+   dest = (u8 *) (p->screen_base + dy * p->fontheight * p->next_line);
+
+   if( sx == 0 && width == p->conp->vc_cols) {
+     s = height * p->fontheight * p->next_line;
+     mymemmove(dest, src, s);
+     return;
+   }
+   
+   l = sx * p->fontwidth;
+   r = l + width * p->fontwidth;
+   t = sy * p->fontheight;
+   b = t + height * p->fontheight;
+
+   dl = dx * p->fontwidth;
+   dr = dl + width * p->fontwidth;
+   dt = dy * p->fontheight;
+   db = dt + height * p->fontheight;
+
+   /* w is the # pixels between two long-aligned points, left and right */
+   w = (r&~31) - ((l+31)&~31);
+   dw = (dr&~31) - ((dl+31)&~31);
+   /* lo is the # pixels between the left edge and a long-aligned left pixel */
+   lo = ((l+31)&~31) - l;
+   dlo = ((dl+31)&~31) - dl;
+   
+   /* if dx != sx then, logic has to align the left and right edges for fast moves */
+   if (lo != dlo) {
+     lo = ((l+7)&~7) - l;
+     dlo = ((dl+7)&~7) - dl;
+     w = (r&~7) - ((l+7)&~7);
+     dw = (dr&~7) - ((dl+7)&~7);
+     if (lo != dlo) {
+       char err_str[256];
+       unsigned long cnt;
+       sprintf( err_str, "ERROR: Shift algorithm: sx=%d,sy=%d,dx=%d,dy=%d,w=%d,h=%d,bpp=%d",
+		sx,sy,dx,dy,width,height,p->var.bits_per_pixel);
+       fbcon_mac_putcs(p->conp, p, err_str, strlen(err_str), 0, 0);
+       /* pause for the user */
+       for(cnt = 0; cnt < 50000; cnt++)
+		udelay(100);
+       return;
+     }
+   }
+
+   s = 0;
+   switch (p->var.bits_per_pixel) {
+   case 1:
+     s = w >> 3;
+     src += lo >> 3;
+     dest += lo >> 3;
+     break;
+   case 2:
+     s = w >> 2;
+     src += lo >> 2;
+     dest += lo >> 2;
+     break;
+   case 4:
+     s = w >> 1;
+     src += lo >> 1;
+     dest += lo >> 1;
+     break;
+   case 8:
+     s = w;
+     src += lo;
+     dest += lo;
+     break;
+   case 16:
+     s = w << 1;
+     src += lo << 1;
+     dest += lo << 1;
+     break;
+   case 32:
+     s = w << 2;
+     src += lo << 2;
+     dest += lo << 2;
+     break;
+   }
+
+   if (sy <= sx) {
+     i = b;
+     move_up = 0;
+     src += height * p->fontheight;
+     dest += height * p->fontheight;
+   } else {
+     i = t;
+     move_up = 1;
+   }
+
+   while (1) {
+     for (i = t; i < b; i++) {
+       j = l;
+
+       for (; j & 31 && j < r; j++)
+	 plot_pixel_mac(p, get_pixel_mac(p, j+(dx-sx), i+(dy-sy)), j, i);
+
+       if (j < r) {
+	 mymemmove(dest, src, s);
+	 if (move_up) {
+	   dest += p->next_line;
+	   src += p->next_line;
+	 } else {
+	   dest -= p->next_line;
+	   src -= p->next_line;
+	 }
+	 j += w;
+       }
+     
+       for (; j < r; j++)
+	 plot_pixel_mac(p, get_pixel_mac(p, j+(dx-sx), i+(dy-sy)), j, i);
+     }
+
+     if (move_up) {
+       i++;
+       if (i >= b)
+	 break;
+     } else {
+       i--;
+       if (i < t)
+	 break;
+     }
+   }
+}
+
+
+void fbcon_mac_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+		     int height, int width)
+{
+   int pixel;
+   int i, j;
+   int inverse;
+   u8 *dest;
+   int l,r,t,b,w,lo,s;
+
+   inverse = attr_reverse(p,conp);
+   pixel = inverse ? PIXEL_WHITE_MAC : PIXEL_BLACK_MAC;
+   dest = (u8 *) (p->screen_base + sy * p->fontheight * p->next_line);
+
+   if( sx == 0 && width == p->conp->vc_cols) {
+     s = height * p->fontheight * p->next_line;
+     if (inverse)
+       mymemclear(dest, s);
+     else
+       mymemset(dest, s);
+   }
+   
+   l = sx * p->fontwidth;
+   r = l + width * p->fontwidth;
+   t = sy * p->fontheight;
+   b = t + height * p->fontheight;
+   /* w is the # pixels between two long-aligned points, left and right */
+   w = (r&~31) - ((l+31)&~31);
+   /* lo is the # pixels between the left edge and a long-aligned left pixel */
+   lo = ((l+31)&~31) - l;
+   s = 0;
+   switch (p->var.bits_per_pixel) {
+   case 1:
+     s = w >> 3;
+     dest += lo >> 3;
+     break;
+   case 2:
+     s = w >> 2;
+     dest += lo >> 2;
+     break;
+   case 4:
+     s = w >> 1;
+     dest += lo >> 1;
+     break;
+   case 8:
+     s = w;
+     dest += lo;
+     break;
+   case 16:
+     s = w << 1;
+     dest += lo << 1;
+     break;
+   case 32:
+     s = w << 2;
+     dest += lo << 2;
+     break;
+   }
+
+   for (i = t; i < b; i++) {
+     j = l;
+
+     for (; j & 31 && j < r; j++)
+       plot_pixel_mac(p, pixel, j, i);
+
+     if (j < r) {
+       if (PIXEL_WHITE_MAC == pixel)
+	 mymemclear(dest, s);
+       else
+	 mymemset(dest, s);
+       dest += p->next_line;
+       j += w;
+     }
+     
+     for (; j < r; j++)
+       plot_pixel_mac(p, pixel, j, i);
+   }
+}
+
+
+void fbcon_mac_putc(struct vc_data *conp, struct display *p, int c, int yy,
+		    int xx)
+{
+   u8 *cdat;
+   u_int rows, bold, ch_reverse, ch_underline;
+   u8 d;
+   int j;
+
+   c &= 0xff;
+
+   cdat = p->fontdata+c*p->fontheight;
+   bold = attr_bold(p,conp);
+   ch_reverse = attr_reverse(p,conp);
+   ch_underline = attr_underline(p,conp);
+
+   for (rows = 0; rows < p->fontheight; rows++) {
+      d = *cdat++;
+      if (!conp->vc_can_do_color) {
+	if (ch_underline && rows == (p->fontheight-2))
+	  d = 0xff;
+	else if (bold)
+	  d |= d>>1;
+	if (ch_reverse)
+	  d = ~d;
+      }
+      for (j = 0; j < p->fontwidth; j++) {
+	plot_pixel_mac(p, (d & 0x80) >> 7, (xx*p->fontwidth) + j, (yy*p->fontheight) + rows);
+	d <<= 1;
+      }
+   }
+}
+
+
+void fbcon_mac_putcs(struct vc_data *conp, struct display *p, const char *s,
+		     int count, int yy, int xx)
+{
+   u8 c;
+
+   while (count--) {
+      c = *s++;
+      fbcon_mac_putc(conp, p, c, yy, xx++);
+   }
+}
+
+
+void fbcon_mac_revc(struct display *p, int xx, int yy)
+{
+   u_int rows, j;
+
+   for (rows = 0; rows < p->fontheight; rows++) {
+     for (j = 0; j < p->fontwidth; j++) {
+       plot_pixel_mac (p, PIXEL_INVERT_MAC, (xx*p->fontwidth)+j, (yy*p->fontheight)+rows);
+     }
+   }
+}
+
+/*
+ * plot_pixel_mac
+ *
+ * bw == 0 = black
+ *       1 = white
+ *       2 = invert
+ */
+static void plot_pixel_mac(struct display *p, int bw, int pixel_x, int pixel_y)
+{
+  u8 *dest, bit;
+  u16 *dest16, pix16;
+  u32 *dest32, pix32;
+
+  if (pixel_x < 0 || pixel_y < 0 || pixel_x >= 832 || pixel_y >= 624) {
+    int cnt;
+    printk ("ERROR: pixel_x == %d, pixel_y == %d", pixel_x, pixel_y);
+    for(cnt = 0; cnt < 100000; cnt++)
+        udelay(100);
+    return;
+  }
+
+  switch (p->var.bits_per_pixel) {
+  case 1:
+    dest = (u8 *) ((pixel_x >> 3) + p->screen_base + pixel_y * p->next_line);
+    bit = 0x80 >> (pixel_x & 7);
+    switch (bw) {
+    case PIXEL_BLACK_MAC:
+      *dest |= bit;
+      break;
+    case PIXEL_WHITE_MAC:
+      *dest &= ~bit;
+      break;
+    case PIXEL_INVERT_MAC:
+      *dest ^= bit;
+      break;
+    default:
+      printk( "ERROR: Unknown pixel value in plot_pixel_mac\n");
+    }
+    break;
+
+  case 2:
+    dest = (u8 *) ((pixel_x >> 2) + p->screen_base + pixel_y * p->next_line);
+    bit = 0xC0 >> ((pixel_x & 3) << 1);
+    switch (bw) {
+    case PIXEL_BLACK_MAC:
+      *dest |= bit;
+      break;
+    case PIXEL_WHITE_MAC:
+      *dest &= ~bit;
+      break;
+    case PIXEL_INVERT_MAC:
+      *dest ^= bit;
+      break;
+    default:
+      printk( "ERROR: Unknown pixel value in plot_pixel_mac\n");
+    }
+    break;
+
+  case 4:
+    dest = (u8 *) ((pixel_x / 2) + p->screen_base + pixel_y * p->next_line);
+    bit = 0xF0 >> ((pixel_x & 1) << 2);
+    switch (bw) {
+    case PIXEL_BLACK_MAC:
+      *dest |= bit;
+      break;
+    case PIXEL_WHITE_MAC:
+      *dest &= ~bit;
+      break;
+    case PIXEL_INVERT_MAC:
+      *dest ^= bit;
+      break;
+    default:
+      printk( "ERROR: Unknown pixel value in plot_pixel_mac\n");
+    }
+    break;
+
+  case 8:
+    dest = (u8 *) (pixel_x + p->screen_base + pixel_y * p->next_line);
+    bit = 0xFF;
+    switch (bw) {
+    case PIXEL_BLACK_MAC:
+      *dest |= bit;
+      break;
+    case PIXEL_WHITE_MAC:
+      *dest &= ~bit;
+      break;
+    case PIXEL_INVERT_MAC:
+      *dest ^= bit;
+      break;
+    default:
+      printk( "ERROR: Unknown pixel value in plot_pixel_mac\n");
+    }
+    break;
+
+  case 16:
+    dest16 = (u16 *) ((pixel_x *2) + p->screen_base + pixel_y * p->next_line);
+    pix16 = 0xFFFF;
+    switch (bw) {
+    case PIXEL_BLACK_MAC:
+      *dest16 = ~pix16;
+      break;
+    case PIXEL_WHITE_MAC:
+      *dest16 = pix16;
+      break;
+    case PIXEL_INVERT_MAC:
+      *dest16 ^= pix16;
+      break;
+    default:
+      printk( "ERROR: Unknown pixel value in plot_pixel_mac\n");
+    }
+    break;
+
+  case 32:
+    dest32 = (u32 *) ((pixel_x *4) + p->screen_base + pixel_y * p->next_line);
+    pix32 = 0xFFFFFFFF;
+    switch (bw) {
+    case PIXEL_BLACK_MAC:
+      *dest32 = ~pix32;
+      break;
+    case PIXEL_WHITE_MAC:
+      *dest32 = pix32;
+      break;
+    case PIXEL_INVERT_MAC:
+      *dest32 ^= pix32;
+      break;
+    default:
+      printk( "ERROR: Unknown pixel value in plot_pixel_mac\n");
+    }
+    break;
+  }
+}
+
+static int get_pixel_mac(struct display *p, int pixel_x, int pixel_y)
+{
+  u8 *dest, bit;
+  u16 *dest16;
+  u32 *dest32;
+  u8 pixel;
+
+  switch (p->var.bits_per_pixel) {
+  case 1:
+    dest = (u8 *) ((pixel_x / 8) + p->screen_base + pixel_y * p->next_line);
+    bit = 0x80 >> (pixel_x & 7);
+    pixel = *dest & bit;
+    break;
+  case 2:
+    dest = (u8 *) ((pixel_x / 4) + p->screen_base + pixel_y * p->next_line);
+    bit = 0xC0 >> (pixel_x & 3);
+    pixel = *dest & bit;
+    break;
+  case 4:
+    dest = (u8 *) ((pixel_x / 2) + p->screen_base + pixel_y * p->next_line);
+    bit = 0xF0 >> (pixel_x & 1);
+    pixel = *dest & bit;
+    break;
+  case 8:
+    dest = (u8 *) (pixel_x + p->screen_base + pixel_y * p->next_line);
+    pixel = *dest;
+    break;
+  case 16:
+    dest16 = (u16 *) ((pixel_x *2) + p->screen_base + pixel_y * p->next_line);
+    pixel = *dest16 ? 1 : 0;
+    break;
+  case 32:
+    dest32 = (u32 *) ((pixel_x *4) + p->screen_base + pixel_y * p->next_line);
+    pixel = *dest32 ? 1 : 0;
+    break;
+  }
+
+  return pixel ? PIXEL_BLACK_MAC : PIXEL_WHITE_MAC;
+}
+
+
+    /*
+     *  `switch' for the low level operations
+     */
+
+struct display_switch fbcon_mac = {
+    fbcon_mac_setup, fbcon_mac_bmove, fbcon_mac_clear, fbcon_mac_putc,
+    fbcon_mac_putcs, fbcon_mac_revc
+};
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(fbcon_mac);
+EXPORT_SYMBOL(fbcon_mac_setup);
+EXPORT_SYMBOL(fbcon_mac_bmove);
+EXPORT_SYMBOL(fbcon_mac_clear);
+EXPORT_SYMBOL(fbcon_mac_putc);
+EXPORT_SYMBOL(fbcon_mac_putcs);
+EXPORT_SYMBOL(fbcon_mac_revc);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-mac.h linux/drivers/video/fbcon-mac.h
--- v2.1.90/linux/drivers/video/fbcon-mac.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-mac.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,15 @@
+    /*
+     *  Mac variable bpp packed pixels (mac)
+     */
+
+extern struct display_switch fbcon_mac;
+extern void fbcon_mac_setup(struct display *p);
+extern void fbcon_mac_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			    int height, int width);
+extern void fbcon_mac_clear(struct vc_data *conp, struct display *p, int sy,
+			    int sx, int height, int width);
+extern void fbcon_mac_putc(struct vc_data *conp, struct display *p, int c,
+			   int yy, int xx);
+extern void fbcon_mac_putcs(struct vc_data *conp, struct display *p,
+			    const char *s, int count, int yy, int xx);
+extern void fbcon_mac_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-mfb.c linux/drivers/video/fbcon-mfb.c
--- v2.1.90/linux/drivers/video/fbcon-mfb.c	Mon Feb 23 18:12:10 1998
+++ linux/drivers/video/fbcon-mfb.c	Sat Mar 21 11:12:49 1998
@@ -13,65 +13,30 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
+#include <linux/config.h>
 #include <linux/fb.h>
 
 #include "fbcon.h"
-
-
-    /*
-     *  Prototypes
-     */
-
-static int open_mfb(struct display *p);
-static void release_mfb(void);
-static void bmove_mfb(struct display *p, int sy, int sx, int dy, int dx,
-		      int height, int width);
-static void clear_mfb(struct vc_data *conp, struct display *p, int sy, int sx,
-		      int height, int width);
-static void putc_mfb(struct vc_data *conp, struct display *p, int c, int yy,
-		      int xx);
-static void putcs_mfb(struct vc_data *conp, struct display *p, const char *s,
-		      int count, int yy, int xx);
-static void rev_char_mfb(struct display *p, int xx, int yy);
-
-
-    /*
-     *  `switch' for the low level operations
-     */
-
-static struct display_switch dispsw_mfb = {
-    open_mfb, release_mfb, bmove_mfb, clear_mfb, putc_mfb, putcs_mfb,
-    rev_char_mfb
-};
+#include "fbcon-mfb.h"
 
 
     /*
      *  Monochrome
      */
 
-static int open_mfb(struct display *p)
+void fbcon_mfb_setup(struct display *p)
 {
-    if (p->var.bits_per_pixel != 1)
-	return -EINVAL;
-
     if (p->line_length)
 	p->next_line = p->line_length;
     else
 	p->next_line = p->var.xres_virtual>>3;
     p->next_plane = 0;
-    MOD_INC_USE_COUNT;
-    return 0;
 }
 
-static void release_mfb(void)
+void fbcon_mfb_bmove(struct display *p, int sy, int sx, int dy, int dx,
+		     int height, int width)
 {
-    MOD_DEC_USE_COUNT;
-}
-
-static void bmove_mfb(struct display *p, int sy, int sx, int dy, int dx,
-		      int height, int width)
-{
-    u_char *src, *dest;
+    u8 *src, *dest;
     u_int rows;
 
     if (sx == 0 && dx == 0 && width == p->next_line) {
@@ -97,10 +62,10 @@
     }
 }
 
-static void clear_mfb(struct vc_data *conp, struct display *p, int sy, int sx,
-		      int height, int width)
+void fbcon_mfb_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+		     int height, int width)
 {
-    u_char *dest;
+    u8 *dest;
     u_int rows;
 
     dest = p->screen_base+sy*p->fontheight*p->next_line+sx;
@@ -118,12 +83,12 @@
 		mymemclear_small(dest, width);
 }
 
-static void putc_mfb(struct vc_data *conp, struct display *p, int c, int yy,
-		     int xx)
+void fbcon_mfb_putc(struct vc_data *conp, struct display *p, int c, int yy,
+		    int xx)
 {
-    u_char *dest, *cdat;
+    u8 *dest, *cdat;
     u_int rows, bold, revs, underl;
-    u_char d;
+    u8 d;
 
     c &= 0xff;
 
@@ -145,12 +110,12 @@
     }
 }
 
-static void putcs_mfb(struct vc_data *conp, struct display *p, const char *s,
-		      int count, int yy, int xx)
+void fbcon_mfb_putcs(struct vc_data *conp, struct display *p, const char *s,
+		     int count, int yy, int xx)
 {
-    u_char *dest, *dest0, *cdat;
+    u8 *dest, *dest0, *cdat;
     u_int rows, bold, revs, underl;
-    u_char c, d;
+    u8 c, d;
 
     dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
     bold = attr_bold(p,conp);
@@ -174,9 +139,9 @@
     }
 }
 
-static void rev_char_mfb(struct display *p, int xx, int yy)
+void fbcon_mfb_revc(struct display *p, int xx, int yy)
 {
-    u_char *dest;
+    u8 *dest;
     u_int rows;
 
     dest = p->screen_base+yy*p->fontheight*p->next_line+xx;
@@ -185,18 +150,24 @@
 }
 
 
-#ifdef MODULE
-int init_module(void)
-#else
-int fbcon_init_mfb(void)
-#endif
-{
-    return(fbcon_register_driver(&dispsw_mfb, 0));
-}
+    /*
+     *  `switch' for the low level operations
+     */
 
-#ifdef MODULE
-void cleanup_module(void)
-{
-    fbcon_unregister_driver(&dispsw_mfb);
-}
-#endif /* MODULE */
+struct display_switch fbcon_mfb = {
+    fbcon_mfb_setup, fbcon_mfb_bmove, fbcon_mfb_clear, fbcon_mfb_putc,
+    fbcon_mfb_putcs, fbcon_mfb_revc
+};
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(fbcon_mfb);
+EXPORT_SYMBOL(fbcon_mfb_setup);
+EXPORT_SYMBOL(fbcon_mfb_bmove);
+EXPORT_SYMBOL(fbcon_mfb_clear);
+EXPORT_SYMBOL(fbcon_mfb_putc);
+EXPORT_SYMBOL(fbcon_mfb_putcs);
+EXPORT_SYMBOL(fbcon_mfb_revc);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-mfb.h linux/drivers/video/fbcon-mfb.h
--- v2.1.90/linux/drivers/video/fbcon-mfb.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-mfb.h	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,15 @@
+    /*
+     *  Monochrome (mfb)
+     */
+
+extern struct display_switch fbcon_mfb;
+extern void fbcon_mfb_setup(struct display *p);
+extern void fbcon_mfb_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			    int height, int width);
+extern void fbcon_mfb_clear(struct vc_data *conp, struct display *p, int sy,
+			    int sx, int height, int width);
+extern void fbcon_mfb_putc(struct vc_data *conp, struct display *p, int c,
+			   int yy, int xx);
+extern void fbcon_mfb_putcs(struct vc_data *conp, struct display *p,
+			    const char *s, int count, int yy, int xx);
+extern void fbcon_mfb_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon-retz3.c linux/drivers/video/fbcon-retz3.c
--- v2.1.90/linux/drivers/video/fbcon-retz3.c	Mon Feb 23 18:12:10 1998
+++ linux/drivers/video/fbcon-retz3.c	Wed Dec 31 16:00:00 1969
@@ -1,260 +0,0 @@
-/*
- *  linux/drivers/video/retz3.c -- Low level frame buffer operations for the
- *				   RetinaZ3 (accelerated)
- *
- *	Created 5 Apr 1997 by Geert Uytterhoeven
- *
- *  This file is subject to the terms and conditions of the GNU General Public
- *  License.  See the file COPYING in the main directory of this archive for
- *  more details.
- */
-
-#include <linux/module.h>
-#include <linux/tty.h>
-#include <linux/console.h>
-#include <linux/string.h>
-#include <linux/fb.h>
-
-#include "fbcon.h"
-
-
-/*
- *  Prototypes
- */
-
-static int open_retz3(struct display *p);
-static void release_retz3(void);
-static void bmove_retz3(struct display *p, int sy, int sx, int dy, int dx,
-	                int height, int width);
-static void clear_retz3(struct vc_data *conp, struct display *p, int
-			sy, int sx, int height, int width);
-static void putc_retz3(struct vc_data *conp, struct display *p, int c,
-		       int ypos, int xpos);
-static void putcs_retz3(struct vc_data *conp, struct display *p, const
-			char *s, int count, int ypos, int xpos);
-static void rev_char_retz3(struct display *p, int xpos, int ypos);
-
-
-    /*
-     *  Acceleration functions in retz3fb.c
-     */
-
-extern void retz3_bitblt(struct fb_var_screeninfo *scr,
-			 unsigned short srcx, unsigned short srcy, unsigned
-			 short destx, unsigned short desty, unsigned short
-			 width, unsigned short height, unsigned short cmd,
-			 unsigned short mask);
-
-#define Z3BLTcopy		0xc0
-#define Z3BLTset		0xf0
-
-
-/*
-     *  `switch' for the low level operations
-     */
-
-static struct display_switch dispsw_retz3 = {
-	open_retz3, release_retz3, bmove_retz3, clear_retz3, putc_retz3,
-	putcs_retz3, rev_char_retz3
-};
-
-
-/*
-     *  RetinaZ3 (accelerated)
-     */
-
-static int open_retz3(struct display *p)
-{
-	if (p->type != FB_TYPE_PACKED_PIXELS ||
-	    p->var.accel != FB_ACCEL_RETINAZ3)
-		return -EINVAL;
-
-	p->next_line = p->var.xres_virtual*p->var.bits_per_pixel>>3;
-	p->next_plane = 0;
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
-static void release_retz3(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
-static void bmove_retz3(struct display *p, int sy, int sx, int dy, int dx,
-	                int height, int width)
-{
-	int fontwidth = p->fontwidth;
-
-	sx *= fontwidth;
-	dx *= fontwidth;
-	width *= fontwidth;
-
-	retz3_bitblt(&p->var,
-		     (unsigned short)sx,
-		     (unsigned short)(sy*p->fontheight),
-		     (unsigned short)dx,
-		     (unsigned short)(dy*p->fontheight),
-		     (unsigned short)width,
-		     (unsigned short)(height*p->fontheight),
-		     Z3BLTcopy,
-		     0xffff);
-}
-
-static void clear_retz3(struct vc_data *conp, struct display *p, int
-			sy, int sx, int height, int width)
-{
-	unsigned short col;
-	int fontwidth = p->fontwidth;
-
-	sx *= fontwidth;
-	width *= fontwidth;
-
-	col = attr_bgcol_ec(p, conp);
-	col &= 0xff;
-	col |= (col << 8);
-
-	retz3_bitblt(&p->var,
-		     (unsigned short)sx,
-		     (unsigned short)(sy*p->fontheight),
-		     (unsigned short)sx,
-		     (unsigned short)(sy*p->fontheight),
-		     (unsigned short)width,
-		     (unsigned short)(height*p->fontheight),
-		     Z3BLTset,
-		     col);
-}
-
-static void putc_retz3(struct vc_data *conp, struct display *p,
-		       int c, int ypos, int xpos)
-{
-	unsigned char *dest, *cdat;
-	unsigned long tmp;
-	unsigned int rows, revs, underl;
-	unsigned char d;
-	unsigned char fg, bg;
-
-	c &= 0xff;
-
-	dest = p->screen_base + ypos * p->fontheight * p->next_line +
-	        xpos*p->fontwidth;
-	cdat = p->fontdata + c * p->fontheight;
-
-	fg = p->fgcol;
-	bg = p->bgcol;
-	revs = conp->vc_reverse;
-	underl = conp->vc_underline;
-
-	for (rows = p->fontheight; rows--; dest += p->next_line) {
-		d = *cdat++;
-
-		if (underl && !rows)
-			d = 0xff;
-		if (revs)
-			d = ~d;
-
-		tmp =  ((d & 0x80) ? fg : bg) << 24;
-		tmp |= ((d & 0x40) ? fg : bg) << 16;
-		tmp |= ((d & 0x20) ? fg : bg) << 8;
-		tmp |= ((d & 0x10) ? fg : bg);
-		*((unsigned long*) dest) = tmp;
-		tmp =  ((d & 0x8) ? fg : bg) << 24;
-		tmp |= ((d & 0x4) ? fg : bg) << 16;
-		tmp |= ((d & 0x2) ? fg : bg) << 8;
-		tmp |= ((d & 0x1) ? fg : bg);
-		*((unsigned long*) dest + 1) = tmp;
-	}
-}
-
-static void putcs_retz3(struct vc_data *conp, struct display *p,
-			const char *s, int count, int ypos, int xpos)
-{
-	unsigned char *dest, *dest0, *cdat;
-	unsigned long tmp;
-	unsigned int rows, revs, underl;
-	unsigned char c, d;
-	unsigned char fg, bg;
-
-	dest0 = p->screen_base + ypos * p->fontheight * p->next_line
-		+ xpos * p->fontwidth;
-
-	fg = p->fgcol;
-	bg = p->bgcol;
-	revs = conp->vc_reverse;
-	underl = conp->vc_underline;
-
-	while (count--) {
-		c = *s++;
-		dest = dest0;
-		dest0 += 8;
-
-		cdat = p->fontdata + c * p->fontheight;
-		for (rows = p->fontheight; rows--; dest += p->next_line) {
-			d = *cdat++;
-
-			if (underl && !rows)
-				d = 0xff;
-			if (revs)
-				d = ~d;
-
-			tmp =  ((d & 0x80) ? fg : bg) << 24;
-			tmp |= ((d & 0x40) ? fg : bg) << 16;
-			tmp |= ((d & 0x20) ? fg : bg) << 8;
-			tmp |= ((d & 0x10) ? fg : bg);
-			*((unsigned long*) dest) = tmp;
-			tmp =  ((d & 0x8) ? fg : bg) << 24;
-			tmp |= ((d & 0x4) ? fg : bg) << 16;
-			tmp |= ((d & 0x2) ? fg : bg) << 8;
-			tmp |= ((d & 0x1) ? fg : bg);
-			*((unsigned long*) dest + 1) = tmp;
-		}
-	}
-}
-
-static void rev_char_retz3(struct display *p, int xpos, int ypos)
-{
-	unsigned char *dest;
-	int bytes=p->next_line, rows;
-	unsigned int bpp, mask;
-
-	bpp = p->var.bits_per_pixel;
-
-	switch (bpp){
-	case 8:
-		mask = 0x0f0f0f0f;
-		break;
-	case 16:
-		mask = 0xffffffff;
-		break;
-	case 24:
-		mask = 0xffffffff; /* ??? */
-		break;
-	default:
-		printk("illegal depth for rev_char_retz3(), bpp = %i\n", bpp);
-		return;
-	}
-
-	dest = p->screen_base + ypos * p->fontheight * bytes +
-		xpos * p->fontwidth;
-
-	for (rows = p->fontheight ; rows-- ; dest += bytes) {
-		((unsigned long *)dest)[0] ^= mask;
-		((unsigned long *)dest)[1] ^= mask;
-	}
-}
-
-
-#ifdef MODULE
-int init_module(void)
-#else
-int fbcon_init_retz3(void)
-#endif
-{
-	return(fbcon_register_driver(&dispsw_retz3, 1));
-}
-
-#ifdef MODULE
-void cleanup_module(void)
-{
-	fbcon_unregister_driver(&dispsw_retz3);
-}
-#endif /* MODULE */
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon.c linux/drivers/video/fbcon.c
--- v2.1.90/linux/drivers/video/fbcon.c	Tue Mar 17 22:18:15 1998
+++ linux/drivers/video/fbcon.c	Sat Mar 21 11:12:49 1998
@@ -28,26 +28,17 @@
  *  The low level operations for the various display memory organizations are
  *  now in separate source files.
  *
- *  Currently only the following organizations are supported:
+ *  Currently the following organizations are supported:
  *
- *    - non-accelerated:
- *
- *	  o mfb		 Monochrome
- *	  o ilbm	 Interleaved bitplanes à la Amiga
- *	  o afb		 Bitplanes à la Amiga
- *	  o iplan2p[248] Interleaved bitplanes à la Atari (2, 4 and 8 planes)
- *	  o cfb{8,16}    Packed pixels (8 and 16 bpp)
- *
- *    - accelerated:
- *
- *	  o cyber	 CyberVision64 packed pixels (accelerated)
- *	  o retz3	 Retina Z3 packed pixels (accelerated)
- *	  o mach64	 ATI Mach 64 packed pixels (accelerated)
+ *    o afb			Amiga bitplanes
+ *    o cfb{2,4,8,16,24,32}	Packed pixels
+ *    o ilbm			Amiga interleaved bitplanes
+ *    o iplan2p[248]		Atari interleaved bitplanes
+ *    o mfb			Monochrome
  *
  *  To do:
  *
  *    - Implement 16 plane mode (iplan2p16)
- *    - Add support for 24/32 bit packed pixels (cfb{24,32})
  *    - Hardware cursor
  *
  *
@@ -56,6 +47,8 @@
  *  more details.
  */
 
+#undef FBCONDEBUG
+
 #define SUPPORT_SCROLLBACK	0
 #define FLASHING_CURSOR		1
 
@@ -65,6 +58,7 @@
 #include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
+#include <linux/delay.h>	/* MSch: for IRQ probe */
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
@@ -74,9 +68,6 @@
 #include <linux/vt_kern.h>
 #include <linux/selection.h>
 #include <linux/init.h>
-#ifdef CONFIG_KMOD
-#include <linux/kmod.h>
-#endif
 
 #include <asm/irq.h>
 #include <asm/system.h>
@@ -88,6 +79,9 @@
 #ifdef CONFIG_ATARI
 #include <asm/atariints.h>
 #endif
+#ifdef CONFIG_MAC
+#include <asm/macints.h>
+#endif
 #ifdef __mc68000__
 #include <asm/machdep.h>
 #include <asm/setup.h>
@@ -95,8 +89,14 @@
 #include <asm/linux_logo.h>
 
 #include "fbcon.h"
+#include "fbcon-mac.h"	/* for 6x11 font on mac */
 #include "font.h"
 
+#ifdef FBCONDEBUG
+#  define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+#  define DPRINTK(fmt, args...)
+#endif
 
 struct display fb_display[MAX_NR_CONSOLES];
 
@@ -112,6 +112,7 @@
 /* # VBL ints between cursor state changes */
 #define AMIGA_CURSOR_BLINK_RATE		(20)
 #define ATARI_CURSOR_BLINK_RATE		(42)
+#define MAC_CURSOR_BLINK_RATE		(32)
 #define DEFAULT_CURSOR_BLINK_RATE	(20)
 
 static int vbl_cursor_cnt = 0;
@@ -146,26 +147,25 @@
 static unsigned long fbcon_startup(unsigned long kmem_start,
 				   const char **display_desc);
 static void fbcon_init(struct vc_data *conp);
-static int fbcon_deinit(struct vc_data *conp);
+static void fbcon_deinit(struct vc_data *conp);
 static int fbcon_changevar(int con);
-static int fbcon_clear(struct vc_data *conp, int sy, int sx, int height,
+static void fbcon_clear(struct vc_data *conp, int sy, int sx, int height,
 		       int width);
-static int fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos);
-static int fbcon_putcs(struct vc_data *conp, const char *s, int count,
-		       int ypos, int xpos);
-static int fbcon_cursor(struct vc_data *conp, int mode);
-static int fbcon_scroll(struct vc_data *conp, int t, int b,
-			int dir, int count);
-static int fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
-		       int height, int width);
+static void fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos);
+static void fbcon_putcs(struct vc_data *conp, const char *s, int count,
+			int ypos, int xpos);
+static void fbcon_cursor(struct vc_data *conp, int mode);
+static void fbcon_scroll(struct vc_data *conp, int t, int b, int dir,
+			 int count);
+static void fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+			int height, int width);
 static int fbcon_switch(struct vc_data *conp);
 static int fbcon_blank(int blank);
 static int fbcon_get_font(struct vc_data *conp, int *w, int *h, char *data);
 static int fbcon_set_font(struct vc_data *conp, int w, int h, char *data);
 static int fbcon_set_palette(struct vc_data *conp, unsigned char *table);
 static int fbcon_scrolldelta(int lines);
-int fbcon_register_driver(struct display_switch *dispsw, int is_accel);
-int fbcon_unregister_driver(struct display_switch *dispsw);
+static int fbcon_set_mode(struct vc_data *conp, int mode);
 
 
 /*
@@ -193,16 +193,23 @@
 				 struct display *p, int count);
 static void fbcon_bmove_rec(struct display *p, int sy, int sx, int dy, int dx,
 			    int height, int width, u_int y_break);
-static struct display_switch *probe_list(struct display_switch *dispsw,
-					 struct display *disp);
 
-#ifdef CONFIG_KMOD
-static void request_driver(struct display *disp, int is_accel);
-#endif
-static struct display_switch *fbcon_get_driver(struct display *disp);
 static int fbcon_show_logo(void);
 
 #if FLASHING_CURSOR
+
+#ifdef CONFIG_MAC
+/*
+ * On the Macintoy, there may or may not be a working VBL int. We need to prob
+ */
+static int vbl_detected = 0;
+
+static void fbcon_vbl_detect(int irq, void *dummy, struct pt_regs *fp)
+{
+      vbl_detected++;
+}
+#endif
+
 static void cursor_timer_handler(unsigned long dev_addr);
 
 static struct timer_list cursor_timer = {
@@ -212,7 +219,7 @@
 static void cursor_timer_handler(unsigned long dev_addr)
 {
       fbcon_vbl_handler(0, NULL, NULL);
-      cursor_timer.expires = jiffies+2;
+      cursor_timer.expires = jiffies+HZ/50;
       cursor_timer.data = 0;
       cursor_timer.next = cursor_timer.next = NULL;
       add_timer(&cursor_timer);
@@ -223,49 +230,8 @@
  *  Low Level Operations
  */
 
-static struct display_switch dispsw_dummy;
-
-#ifdef CONFIG_FBCON_MFB
-extern int fbcon_init_mfb(void);
-#endif
-#ifdef CONFIG_FBCON_ILBM
-extern int fbcon_init_ilbm(void);
-#endif
-#ifdef CONFIG_FBCON_AFB
-extern int fbcon_init_afb(void);
-#endif
-#ifdef CONFIG_FBCON_IPLAN2P2
-extern int fbcon_init_iplan2p2(void);
-#endif
-#ifdef CONFIG_FBCON_IPLAN2P4
-extern int fbcon_init_iplan2p4(void);
-#endif
-#ifdef CONFIG_FBCON_IPLAN2P8
-extern int fbcon_init_iplan2p8(void);
-#endif
-#ifdef CONFIG_FBCON_CFB8
-extern int fbcon_init_cfb8(void);
-#endif
-#ifdef CONFIG_FBCON_CFB16
-extern int fbcon_init_cfb16(void);
-#endif
-#ifdef CONFIG_FBCON_CFB24
-extern int fbcon_init_cfb24(void);
-#endif
-#ifdef CONFIG_FBCON_CFB32
-extern int fbcon_init_cfb32(void);
-#endif
-#ifdef CONFIG_FBCON_CYBER
-extern int fbcon_init_cyber(void);
-#endif
-#ifdef CONFIG_FBCON_RETINAZ3
-extern int fbcon_init_retz3(void);
-#endif
-#ifdef CONFIG_FBCON_MACH64
-extern int fbcon_init_mach64(void);
-#endif
+static struct display_switch fbcon_dummy;
 
-extern int num_registered_fb;
 
 __initfunc(static unsigned long fbcon_startup(unsigned long kmem_start,
 					      const char **display_desc))
@@ -275,49 +241,10 @@
     /* Probe all frame buffer devices */
     kmem_start = probe_framebuffers(kmem_start);
 
-    if (!num_registered_fb)
+    if (!num_registered_fb) {
+            DPRINTK("no framebuffer registered\n");
 	    return kmem_start;
-
-    /* Initialize all built-in low level drivers */
-#ifdef CONFIG_FBCON_RETINAZ3
-    fbcon_init_retz3();
-#endif
-#ifdef CONFIG_FBCON_MFB
-    fbcon_init_mfb();
-#endif
-#ifdef CONFIG_FBCON_IPLAN2P2
-    fbcon_init_iplan2p2();
-#endif
-#ifdef CONFIG_FBCON_IPLAN2P4
-    fbcon_init_iplan2p4();
-#endif
-#ifdef CONFIG_FBCON_IPLAN2P8
-    fbcon_init_iplan2p8();
-#endif
-#ifdef CONFIG_FBCON_ILBM
-    fbcon_init_ilbm();
-#endif
-#ifdef CONFIG_FBCON_AFB
-    fbcon_init_afb();
-#endif
-#ifdef CONFIG_FBCON_CFB8
-    fbcon_init_cfb8();
-#endif
-#ifdef CONFIG_FBCON_CFB16
-    fbcon_init_cfb16();
-#endif
-#ifdef CONFIG_FBCON_CFB24
-    fbcon_init_cfb24();
-#endif
-#ifdef CONFIG_FBCON_CFB32
-    fbcon_init_cfb32();
-#endif
-#ifdef CONFIG_FBCON_CYBER
-    fbcon_init_cyber();
-#endif
-#ifdef CONFIG_FBCON_MACH64
-    fbcon_init_mach64();
-#endif
+    }
 
     *display_desc = "frame buffer device";
 
@@ -335,9 +262,49 @@
 			     "console/cursor", fbcon_vbl_handler);
     }
 #endif /* CONFIG_ATARI */
+
+#ifdef CONFIG_MAC
+    /*
+     * On a Macintoy, the VBL interrupt may or may not be active. 
+     * As interrupt based cursor is more reliable and race free, we 
+     * probe for VBL interrupts.
+     */
+    if (MACH_IS_MAC) {
+       int ct = 0;
+       /*
+        * Probe for VBL: set temp. handler ...
+        */
+       irqres = request_irq(IRQ_MAC_VBL, fbcon_vbl_detect, 0,
+                            "console/cursor", fbcon_vbl_detect);
+       /*
+        * ... and spin for 20 ms ...
+        */
+       while (!vbl_detected && ++ct<1000)
+          udelay(20);
+ 
+       if(ct==1000)
+          printk("fbcon_startup: No VBL detected, using timer based cursor.\n");
+ 
+       if (vbl_detected) {
+         /*
+          * interrupt based cursor ok
+          */
+          cursor_blink_rate = MAC_CURSOR_BLINK_RATE;
+          irqres = request_irq(IRQ_MAC_VBL, fbcon_vbl_handler, 0,
+                               "console/cursor", fbcon_vbl_handler);
+       } else {
+          /*
+           * VBL not detected: fall through, use timer based cursor
+           */
+           irqres = 1;
+	   /* free interrupt here ?? */
+       }
+    }
+#endif /* CONFIG_MAC */
+
     if (irqres) {
 	cursor_blink_rate = DEFAULT_CURSOR_BLINK_RATE;
-	cursor_timer.expires = jiffies+2;
+	cursor_timer.expires = jiffies+HZ/50;
 	cursor_timer.data = 0;
 	cursor_timer.next = cursor_timer.prev = NULL;
 	add_timer(&cursor_timer);
@@ -360,22 +327,24 @@
 
     info->changevar = &fbcon_changevar;
     fb_display[unit] = *(info->disp);	/* copy from default */
+    DPRINTK("mode:   %s\n",info->modename);
+    DPRINTK("visual: %d\n",fb_display[unit].visual);
+    DPRINTK("res:    %dx%d-%d\n",fb_display[unit].var.xres,
+	                     fb_display[unit].var.yres,
+	                     fb_display[unit].var.bits_per_pixel);
     fb_display[unit].conp = conp;
     fb_display[unit].fb_info = info;
     fbcon_setup(unit, 1, 1);
 }
 
 
-static int fbcon_deinit(struct vc_data *conp)
+static void fbcon_deinit(struct vc_data *conp)
 {
     int unit = conp->vc_num;
     struct display *p = &fb_display[unit];
 
-    if (p->dispsw)
-	    p->dispsw->release();
-    p->dispsw = 0;
+    p->dispsw = NULL;
     p->conp = 0;
-    return(0);
 }
 
 
@@ -405,7 +374,6 @@
     struct display *p = &fb_display[con];
     struct vc_data *conp = p->conp;
     int nr_rows, nr_cols;
-    struct display_switch *old_dispsw, *new_dispsw;
 
     p->var.xoffset = p->var.yoffset = p->yscroll = 0;  /* reset wrap/pan */
 
@@ -415,9 +383,17 @@
 	getdefaultfont(p->var.xres, p->var.yres, NULL, &p->fontwidth,
 		       &p->fontheight, &p->fontdata);
     if (p->fontwidth != 8) {
-	/* ++Geert: changed from panic() to `correct and continue' */
-	printk(KERN_ERR "fbcon_setup: No support for fontwidth != 8");
-	p->fontwidth = 8;
+#ifdef CONFIG_MAC
+	if (MACH_IS_MAC)
+	    /* ++Geert: hack to make 6x11 fonts work on mac */
+	    p->dispsw = &fbcon_mac;
+	else
+#endif
+	{
+	    /* ++Geert: changed from panic() to `correct and continue' */
+	    printk(KERN_ERR "fbcon_setup: No support for fontwidth != 8");
+	    p->dispsw = &fbcon_dummy;
+	}
     }
     updatescrollmode(p);
 
@@ -435,18 +411,12 @@
     p->vrows = p->var.yres_virtual/p->fontheight;
     conp->vc_can_do_color = p->var.bits_per_pixel != 1;
 
-    new_dispsw = fbcon_get_driver(p);
-    if (!new_dispsw) {
+    if (!p->dispsw) {
 	printk(KERN_WARNING "fbcon_setup: type %d (aux %d, depth %d) not "
 	       "supported\n", p->type, p->type_aux, p->var.bits_per_pixel);
-	dispsw_dummy.open(p);
-	new_dispsw = &dispsw_dummy;
+	p->dispsw = &fbcon_dummy;
     }
-    /* Be careful when changing dispsw, it might be the current console.  */
-    old_dispsw = p->dispsw;
-    p->dispsw = new_dispsw;
-    if (old_dispsw)
-	old_dispsw->release();
+    p->dispsw->setup(p);
 
     if (setcol) {
 	p->fgcol = p->var.bits_per_pixel > 2 ? 7 : (1<<p->var.bits_per_pixel)-1;
@@ -492,15 +462,18 @@
 }
 
 
-static int fbcon_clear(struct vc_data *conp, int sy, int sx, int height,
-			      int width)
+static void fbcon_clear(struct vc_data *conp, int sy, int sx, int height,
+			int width)
 {
     int unit = conp->vc_num;
     struct display *p = &fb_display[unit];
     u_int y_break;
 
     if (!p->can_soft_blank && console_blanked)
-	return(0);
+	return;
+
+    if (!height || !width)
+	return;
 
     if ((sy <= p->cursor_y) && (p->cursor_y < sy+height) &&
 	(sx <= p->cursor_x) && (p->cursor_x < sx+width))
@@ -515,47 +488,41 @@
 	p->dispsw->clear(conp, p, real_y(p, sy+b), sx, height-b, width);
     } else
 	p->dispsw->clear(conp, p, real_y(p, sy), sx, height, width);
-
-    return(0);
 }
 
 
-static int fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos)
+static void fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos)
 {
     int unit = conp->vc_num;
     struct display *p = &fb_display[unit];
 
     if (!p->can_soft_blank && console_blanked)
-	    return 0;
+	    return;
 
     if ((p->cursor_x == xpos) && (p->cursor_y == ypos))
 	    CURSOR_UNDRAWN();
 
     p->dispsw->putc(conp, p, c, real_y(p, ypos), xpos);
-
-    return 0;
 }
 
 
-static int fbcon_putcs(struct vc_data *conp, const char *s, int count,
+static void fbcon_putcs(struct vc_data *conp, const char *s, int count,
 		       int ypos, int xpos)
 {
     int unit = conp->vc_num;
     struct display *p = &fb_display[unit];
 
     if (!p->can_soft_blank && console_blanked)
-	    return 0;
+	    return;
 
     if ((p->cursor_y == ypos) && (xpos <= p->cursor_x) &&
 	(p->cursor_x < (xpos + count)))
 	    CURSOR_UNDRAWN();
     p->dispsw->putcs(conp, p, s, count, real_y(p, ypos), xpos);
-
-    return(0);
 }
 
 
-static int fbcon_cursor(struct vc_data *conp, int mode)
+static void fbcon_cursor(struct vc_data *conp, int mode)
 {
     int unit = conp->vc_num;
     struct display *p = &fb_display[unit];
@@ -563,9 +530,9 @@
     /* Avoid flickering if there's no real change. */
     if (p->cursor_x == conp->vc_x && p->cursor_y == conp->vc_y &&
 	(mode == CM_ERASE) == !cursor_on)
-	return 0;
+	return;
     if (CURSOR_UNDRAWN ())
-	p->dispsw->rev_char(p, p->cursor_x, real_y(p, p->cursor_y));
+	p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y));
     p->cursor_x = conp->vc_x;
     p->cursor_y = conp->vc_y;
 
@@ -580,8 +547,6 @@
 	    cursor_on = 1;
 	    break;
     }
-
-    return(0);
 }
 
 
@@ -598,7 +563,7 @@
 	 * switching code should set vbl_cursor_cnt to an appropriate value.
 	 */
 	p = &fb_display[fg_console];
-	p->dispsw->rev_char(p, p->cursor_x, real_y(p, p->cursor_y));
+	p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y));
 	cursor_drawn ^= 1;
 	vbl_cursor_cnt = cursor_blink_rate;
     }
@@ -623,7 +588,7 @@
     p->var.xoffset = 0;
     p->var.yoffset = p->yscroll*p->fontheight;
     p->var.vmode |= FB_VMODE_YWRAP;
-    p->fb_info->updatevar(unit);
+    p->fb_info->updatevar(unit, p->fb_info);
 #if SUPPORT_SCROLLBACK
     scrollback_max += count;
     if (scrollback_max > p->vrows-conp->vc_rows)
@@ -646,7 +611,7 @@
     p->var.xoffset = 0;
     p->var.yoffset = p->yscroll*p->fontheight;
     p->var.vmode |= FB_VMODE_YWRAP;
-    p->fb_info->updatevar(unit);
+    p->fb_info->updatevar(unit, p->fb_info);
 #if SUPPORT_SCROLLBACK
     scrollback_max -= count;
     if (scrollback_max < 0)
@@ -668,7 +633,7 @@
     p->var.xoffset = 0;
     p->var.yoffset = p->yscroll*p->fontheight;
     p->var.vmode &= ~FB_VMODE_YWRAP;
-    p->fb_info->updatevar(unit);
+    p->fb_info->updatevar(unit, p->fb_info);
 }
 
 
@@ -684,17 +649,21 @@
     p->var.xoffset = 0;
     p->var.yoffset = p->yscroll*p->fontheight;
     p->var.vmode &= ~FB_VMODE_YWRAP;
-    p->fb_info->updatevar(unit);
+    p->fb_info->updatevar(unit, p->fb_info);
 }
 
 
-static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir, int count)
+static void fbcon_scroll(struct vc_data *conp, int t, int b, int dir,
+			 int count)
 {
     int unit = conp->vc_num;
     struct display *p = &fb_display[unit];
 
     if (!p->can_soft_blank && console_blanked)
-	return(0);
+	return;
+
+    if (!count)
+	return;
 
     fbcon_cursor(conp, CM_ERASE);
 
@@ -822,19 +791,20 @@
 	    fbcon_clear(conp, 0, t, conp->vc_rows, count);
 	    break;
     }
-
-    return(0);
 }
 
 
-static int fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
-		       int height, int width)
+static void fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+			int height, int width)
 {
     int unit = conp->vc_num;
     struct display *p = &fb_display[unit];
 
     if (!p->can_soft_blank && console_blanked)
-	return(0);
+	return;
+
+    if (!width || !height)
+	return;
 
     if (((sy <= p->cursor_y) && (p->cursor_y < sy+height) &&
 	 (sx <= p->cursor_x) && (p->cursor_x < sx+width)) ||
@@ -850,8 +820,6 @@
      *  over again, so we use fbcon_bmove_rec()
      */
     fbcon_bmove_rec(p, sy, sx, dy, dx, height, width, p->vrows-p->yscroll);
-
-    return(0);
 }
 
 
@@ -894,7 +862,7 @@
     struct fb_info *info = p->fb_info;
 
     if (info && info->switch_con)
-	(*info->switch_con)(conp->vc_num);
+	(*info->switch_con)(conp->vc_num, info);
 #if SUPPORT_SCROLLBACK
     scrollback_max = 0;
     scrollback_current = 0;
@@ -906,11 +874,19 @@
 static int fbcon_blank(int blank)
 {
     struct display *p = &fb_display[fg_console];
+    struct fb_info *info = p->fb_info;
 
     fbcon_cursor(p->conp, blank ? CM_ERASE : CM_DRAW);
 
     if (!p->can_soft_blank) {
 	if (blank) {
+#ifdef CONFIG_MAC
+	    if (MACH_IS_MAC)
+		mymemset(p->screen_base,
+			 p->var.xres_virtual*p->var.yres_virtual*
+			 p->var.bits_per_pixel>>3);
+	    else
+#endif
 	    if (p->visual == FB_VISUAL_MONO01)
 		mymemset(p->screen_base,
 			 p->var.xres_virtual*p->var.yres_virtual*
@@ -925,7 +901,7 @@
 	    return(1);
 	}
     }
-    (*p->fb_info->blank)(blank);
+    (*info->blank)(blank, info);
     return(0);
 }
 
@@ -975,7 +951,7 @@
 	copy_from_user( name, data, MAX_FONT_NAME );
 	name[sizeof(name)-1] = 0;
 
-	if (!findsoftfont( name, &w, &h, (u_char **)&data ))
+	if (!findsoftfont( name, &w, &h, (u8 **)&data ))
 	    return( -ENOENT );
 	userspace = 0;
     } else if (w == 1) {
@@ -1049,9 +1025,9 @@
     return( 0 );
 }
 
-static unsigned short palette_red[16];
-static unsigned short palette_green[16];
-static unsigned short palette_blue[16];
+static u16 palette_red[16];
+static u16 palette_green[16];
+static u16 palette_blue[16];
 
 static struct fb_cmap palette_cmap  = {
     0, 16, palette_red, palette_green, palette_blue, NULL
@@ -1062,7 +1038,7 @@
     int unit = conp->vc_num;
     struct display *p = &fb_display[unit];
     int i, j, k;
-    u_char val;
+    u8 val;
 
     if (!conp->vc_can_do_color || (!p->can_soft_blank && console_blanked))
 	return(-EINVAL);
@@ -1078,7 +1054,7 @@
     palette_cmap.len = 1<<p->var.bits_per_pixel;
     if (palette_cmap.len > 16)
 	palette_cmap.len = 16;
-    return(p->fb_info->setcmap(&palette_cmap, unit));
+    return p->fb_info->fbops->fb_set_cmap(&palette_cmap, 1, unit, p->fb_info);
 }
 
 static int fbcon_scrolldelta(int lines)
@@ -1109,13 +1085,27 @@
     p->var.vmode |= FB_VMODE_YWRAP;
     p->var.xoffset = 0;
     p->var.yoffset = offset*p->fontheight;
-    p->fb_info->updatevar(unit);
+    p->fb_info->updatevar(unit, p->fb_info);
 #else
     return -ENOSYS;
 #endif
 }
 
 
+    /*
+     *  Switch between `text' (emulated and accelerated) and `graphics'
+     *  (unaccelerated text) mode
+     */
+
+static int fbcon_set_mode(struct vc_data *conp, int mode)
+{
+    struct display *p = &fb_display[conp->vc_num];
+    struct fb_ops *ops = p->fb_info->fbops;
+
+    return ops->fb_set_mode ? ops->fb_set_mode(mode, p->fb_info) : 0;
+}
+
+
 #define LOGO_H			80
 #define LOGO_W			80
 #define LOGO_LINE	(LOGO_W/8)
@@ -1166,7 +1156,8 @@
 		palette_cmap.green[j] = (green[i+j] << 8) | green[i+j];
 		palette_cmap.blue[j]  = (blue[i+j] << 8) | blue[i+j];
 	    }
-	    p->fb_info->setcmap( &palette_cmap, fg_console );
+	    p->fb_info->fbops->fb_set_cmap(&palette_cmap, 1, fg_console,
+					   p->fb_info);
 	}
 	fb_display[fg_console].cmap.len = old_cmap_len;
     }
@@ -1184,12 +1175,65 @@
 	logo_depth = 1;
     }
 
-#if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CYBER) || \
-    defined(CONFIG_FBCON_RETINAZ3)
-    if ((depth % 8 == 0) && (p->visual == FB_VISUAL_TRUECOLOR ||
-			     p->visual == FB_VISUAL_DIRECTCOLOR)) {
+#if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CFB24) || \
+    defined(CONFIG_FBCON_CFB32)
+    if (p->visual == FB_VISUAL_TRUECOLOR) {
+	unsigned int val;		/* max. depth 32! */
+	int bdepth;
+	int redshift, greenshift, blueshift;
+		
+	/* Bug: Doesn't obey msb_right ... (who needs that?) */
+	redshift   = p->var.red.offset;
+	greenshift = p->var.green.offset;
+	blueshift  = p->var.blue.offset;
+
+	if (depth >= 24 && (depth % 8) == 0) {
+	    /* have at least 8 bits per color */
+	    src = logo;
+	    bdepth = depth/8;
+	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		dst = fb + y1*line;
+		for( x1 = 0; x1 < LOGO_W; x1++, src++ ) {
+		val = ((linux_logo_red[*src]   & redmask)   << redshift) |
+		      ((linux_logo_green[*src] & greenmask) << greenshift) |
+		      ((linux_logo_blue[*src]  & bluemask)  << blueshift);
+		    for( i = bdepth-1; i >= 0; --i )
+			*dst++ = val >> (i*8);
+		}
+	    }
+	}
+	else if (depth >= 15 && depth <= 23) {
+	    /* have 5..7 bits per color, using 16 color image */
+	    unsigned int pix;
+	    src = linux_logo16;
+	    bdepth = (depth+7)/8;
+	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		dst = fb + y1*line;
+		for( x1 = 0; x1 < LOGO_W/2; x1++, src++ ) {
+		    pix = (*src >> 4) | 0x10; /* upper nibble */
+		    val = (pix << redshift) |
+			  (pix << greenshift) |
+			  (pix << blueshift);
+		    for( i = 0; i < bdepth; ++i )
+			*dst++ = val >> (i*8);
+		    pix = (*src & 0x0f) | 0x10; /* lower nibble */
+		    val = (pix << redshift) |
+			  (pix << greenshift) |
+			  (pix << blueshift);
+		    for( i = bdepth-1; i >= 0; --i )
+			*dst++ = val >> (i*8);
+		}
+	    }
+	}
+	
+	done = 1;
+    }
+#endif
+#if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CFB24) || \
+    defined(CONFIG_FBCON_CFB32)
+    if ((depth % 8 == 0) && (p->visual == FB_VISUAL_DIRECTCOLOR)) {
 	/* Modes without color mapping, needs special data transformation... */
-	unsigned long val;		/* max. depth 32! */
+	unsigned int val;		/* max. depth 32! */
 	int bdepth = depth/8;
 	unsigned char mask[9] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff };
 	unsigned char redmask, greenmask, bluemask;
@@ -1218,8 +1262,7 @@
 	done = 1;
     }
 #endif
-#if defined(CONFIG_FBCON_CFB8) || defined(CONFIG_FBCON_CYBER) || \
-    defined(CONFIG_FBCON_RETINAZ3)
+#if defined(CONFIG_FBCON_CFB8)
     if (depth == 8 && p->type == FB_TYPE_PACKED_PIXELS) {
 	/* depth 8 or more, packed, with color registers */
 		
@@ -1316,153 +1359,23 @@
     fbcon_startup, fbcon_init, fbcon_deinit, fbcon_clear, fbcon_putc,
     fbcon_putcs, fbcon_cursor, fbcon_scroll, fbcon_bmove, fbcon_switch,
     fbcon_blank, fbcon_get_font, fbcon_set_font, fbcon_set_palette,
-    fbcon_scrolldelta
+    fbcon_scrolldelta, fbcon_set_mode
 };
 
 
 /*
- *  Driver registration
- */
-
-static struct display_switch *drivers = NULL, *accel_drivers = NULL;
-
-int fbcon_register_driver(struct display_switch *dispsw, int is_accel)
-{
-    struct display_switch **list;
-
-    list = is_accel ? &accel_drivers : &drivers;
-    dispsw->next = *list;
-    *list = dispsw;
-    return 0;
-}
-
-int fbcon_unregister_driver(struct display_switch *dispsw)
-{
-    struct display_switch **list;
-
-    for (list = &accel_drivers; *list; list = &(*list)->next)
-	if (*list == dispsw) {
-	    *list = dispsw->next;
-	    dispsw->next = NULL;
-	    return 0;
-	}
-    for (list = &drivers; *list; list = &(*list)->next)
-	if (*list == dispsw) {
-	    *list = dispsw->next;
-	    dispsw->next = NULL;
-	    return 0;
-	}
-    return -EINVAL;
-}
-
-
-static struct display_switch *probe_list(struct display_switch *dispsw,
-					 struct display *disp)
-{
-    while (dispsw) {
-	if (!dispsw->open(disp))
-	    return(dispsw);
-	dispsw = dispsw->next;
-    }
-    return(NULL);
-}
-
-
-#ifdef CONFIG_KMOD
-static void request_driver(struct display *disp, int is_accel)
-{
-    char modname[30];
-    int len;
-    const char *type;
-
-    if (disp->var.bits_per_pixel == 1)
-	type = "mfb";
-    else
-	switch (disp->type) {
-	    case FB_TYPE_INTERLEAVED_PLANES:
-		if (disp->type_aux == 2)
-		    type = "iplan2p%d";
-		else
-		    type = "ilbm";
-		break;
-	    case FB_TYPE_PLANES:
-		type = "afb";
-		break;
-	    case FB_TYPE_PACKED_PIXELS:
-		type = "cfb%d";
-		break;
-	    default:
-		return;
-	}
-    len = sprintf(modname, "fbcon-");
-    len += sprintf(modname+len, type, disp->var.bits_per_pixel);
-    if (is_accel)
-	len += sprintf(modname+len, "-%d", disp->var.accel);
-    request_module(modname);
-}
-#endif /* CONFIG_KMOD */
-
-
-static struct display_switch *fbcon_get_driver(struct display *disp)
-{
-    struct display_switch *dispsw;
-
-    if (disp->var.accel != FB_ACCEL_NONE) {
-	/* First try an accelerated driver */
-	dispsw = probe_list(accel_drivers, disp);
-#ifdef CONFIG_KMOD
-	if (!dispsw) {
-	    request_driver(disp, 1);
-	    dispsw = probe_list(accel_drivers, disp);
-	}
-#endif
-	if (dispsw)
-	    return(dispsw);
-    }
-
-    /* Then try an unaccelerated driver */
-    dispsw = probe_list(drivers, disp);
-#ifdef CONFIG_KMOD
-    if (!dispsw) {
-	request_driver(disp, 0);
-	dispsw = probe_list(drivers, disp);
-    }
-#endif
-    return(dispsw);
-}
-
-
-/*
  *  Dummy Low Level Operations
  */
 
-static int open_dummy(struct display *p)
-{
-    if (p->line_length)
-	p->next_line = p->line_length;
-    else
-	p->next_line = p->var.xres_virtual>>3;
-    p->next_plane = 0;
-    p->var.bits_per_pixel = 1;
-    return 0;
-}
-
-static void misc_dummy(void) {}
-
-static struct display_switch dispsw_dummy = {
-    open_dummy,
-    /* release_dummy */
-    misc_dummy,
-    /* bmove_dummy */
-    (void (*)(struct display *, int, int, int, int, int, int))misc_dummy,
-    /* clear_dummy */
-    (void (*)(struct vc_data *, struct display *, int, int, int, int))misc_dummy,
-    /* putc_dummy */
-    (void (*)(struct vc_data *, struct display *, int, int, int))misc_dummy,
-    /* putcs_dummy */
-    (void (*)(struct vc_data *, struct display *, const char *, int, int, int))misc_dummy,
-    /* rev_char_dummy */
-    (void (*)(struct display *, int, int))misc_dummy,
+static void fbcon_dummy_op(void) {}
+
+static struct display_switch fbcon_dummy = {
+    (void *)fbcon_dummy_op,	/* fbcon_dummy_setup */
+    (void *)fbcon_dummy_op,	/* fbcon_dummy_bmove */
+    (void *)fbcon_dummy_op,	/* fbcon_dummy_clear */
+    (void *)fbcon_dummy_op,	/* fbcon_dummy_putc */
+    (void *)fbcon_dummy_op,	/* fbcon_dummy_putcs */
+    (void *)fbcon_dummy_op,	/* fbcon_dummy_revc */
 };
 
 
@@ -1471,5 +1384,3 @@
  */
 
 EXPORT_SYMBOL(fb_display);
-EXPORT_SYMBOL(fbcon_register_driver);
-EXPORT_SYMBOL(fbcon_unregister_driver);
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbcon.h linux/drivers/video/fbcon.h
--- v2.1.90/linux/drivers/video/fbcon.h	Fri Oct 31 07:59:41 1997
+++ linux/drivers/video/fbcon.h	Sat Mar 21 11:12:49 1998
@@ -8,6 +8,9 @@
  *  for more details.
  */
 
+#ifndef __VIDEO_FBCON_H
+#define __VIDEO_FBCON_H
+
 #include <linux/console_struct.h>
 
 
@@ -16,8 +19,7 @@
      */
  
 struct display_switch {                                                
-    int (*open)(struct display *p);
-    void (*release)(void);
+    void (*setup)(struct display *p);
     void (*bmove)(struct display *p, int sy, int sx, int dy, int dx,
 		  int height, int width);
     void (*clear)(struct vc_data *conp, struct display *p, int sy, int sx,
@@ -26,20 +28,11 @@
     		 int xx);
     void (*putcs)(struct vc_data *conp, struct display *p, const char *s,
 		  int count, int yy, int xx);     
-    void (*rev_char)(struct display *p, int xx, int yy);
-    struct display_switch *next;
+    void (*revc)(struct display *p, int xx, int yy);
 }; 
 
 
     /*
-     *  Driver registration
-     */
-
-extern int fbcon_register_driver(struct display_switch *dispsw, int is_accel);
-int fbcon_unregister_driver(struct display_switch *dispsw);
-
-
-    /*
      *  Attribute Decoding
      */
 
@@ -335,3 +328,5 @@
 }
 
 #endif /* !m68k */
+
+#endif /* __VIDEO_FBCON_H */
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fbgen.c linux/drivers/video/fbgen.c
--- v2.1.90/linux/drivers/video/fbgen.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbgen.c	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,386 @@
+/*
+ * linux/drivers/video/fbgen.c -- Generic routines for frame buffer devices
+ *
+ *  Created 3 Jan 1998 by Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file README.legal in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/tty.h>
+#include <linux/fb.h>
+#include <linux/slab.h>
+
+#include <asm/uaccess.h>
+
+
+
+static int currcon = 0;
+
+static struct display disp;
+
+
+    /*
+     *  `Generic' versions of the frame buffer device operations
+     */
+
+extern int fbgen_get_fix(struct fb_fix_screeninfo *fix, int con,
+			 struct fb_info *info);
+extern int fbgen_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info);
+extern int fbgen_set_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info);
+extern int fbgen_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info);
+extern int fbgen_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info);
+extern int fbgen_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info);
+extern int fbgen_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg, int con,
+		       struct fb_info *info);
+
+
+    /*
+     *  Helper functions
+     */
+
+int fbgen_do_set_var(struct fb_var_screeninfo *var, int isactive,
+		     struct fb_info_gen *info);
+void fbgen_set_disp(int con, struct fb_info_gen *info);
+void fbgen_install_cmap(int con, struct fb_info_gen *info);
+int fbgen_update_var(int con, struct fb_info *info);
+int fbgen_switch(int con, struct fb_info *info);
+void fbgen_blank(int blank, struct fb_info *info);
+
+
+/* ---- `Generic' versions of the frame buffer device operations ----------- */
+
+
+    /*
+     *  Get the Fixed Part of the Display
+     */
+
+int fbgen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
+{
+    struct fb_info_gen *info2 = (struct fb_info_gen *)info;
+    struct fbgen_hwswitch *fbhw = info2->fbhw;
+    char par[info2->parsize];
+
+    if (con == -1)
+	fbhw->get_par(&par, info2);
+    else {
+	int err;
+
+	if ((err = fbhw->decode_var(&fb_display[con].var, &par, info2)))
+	    return err;
+    }
+    memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+    return fbhw->encode_fix(fix, &par, info2);
+}
+
+
+    /*
+     *  Get the User Defined Part of the Display
+     */
+
+int fbgen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+{
+    struct fb_info_gen *info2 = (struct fb_info_gen *)info;
+    struct fbgen_hwswitch *fbhw = info2->fbhw;
+    char par[info2->parsize];
+
+    if (con == -1) {
+	fbhw->get_par(&par, info2);
+	fbhw->encode_var(var, &par, info2);
+    } else
+	*var = fb_display[con].var;
+    return 0;
+}
+
+
+    /*
+     *  Set the User Defined Part of the Display
+     */
+
+int fbgen_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+{
+    struct fb_info_gen *info2 = (struct fb_info_gen *)info;
+    int err;
+    int oldxres, oldyres, oldbpp, oldxres_virtual, oldyres_virtual, oldyoffset;
+
+    if ((err = fbgen_do_set_var(var, con == currcon, info2)))
+	return err;
+    if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+	oldxres = fb_display[con].var.xres;
+	oldyres = fb_display[con].var.yres;
+	oldxres_virtual = fb_display[con].var.xres_virtual;
+	oldyres_virtual = fb_display[con].var.yres_virtual;
+	oldbpp = fb_display[con].var.bits_per_pixel;
+	oldyoffset = fb_display[con].var.yoffset;
+	fb_display[con].var = *var;
+	if (oldxres != var->xres || oldyres != var->yres ||
+	    oldxres_virtual != var->xres_virtual ||
+	    oldyres_virtual != var->yres_virtual ||
+	    oldbpp != var->bits_per_pixel ||
+	    oldyoffset != var->yoffset) {
+	    fbgen_set_disp(con, info2);
+	    if (info->changevar)
+		(*info->changevar)(con);
+	    if ((err = fb_alloc_cmap(&fb_display[con].cmap, 0, 0)))
+		return err;
+	    fbgen_install_cmap(con, info2);
+	}
+    }
+    var->activate = 0;
+    return 0;
+}
+
+
+    /*
+     *  Get the Colormap
+     */
+
+int fbgen_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+		   struct fb_info *info)
+{
+    struct fb_info_gen *info2 = (struct fb_info_gen *)info;
+    struct fbgen_hwswitch *fbhw = info2->fbhw;
+
+    if (con == currcon)			/* current console ? */
+	return fb_get_cmap(cmap, &fb_display[con].var, kspc, fbhw->getcolreg,
+			   info);
+    else
+	if (fb_display[con].cmap.len)	/* non default colormap ? */
+	    fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+	else
+	    fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+			 cmap, kspc ? 0 : 2);
+    return 0;
+}
+
+
+    /*
+     *  Set the Colormap
+     */
+
+int fbgen_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+		   struct fb_info *info)
+{
+    struct fb_info_gen *info2 = (struct fb_info_gen *)info;
+    struct fbgen_hwswitch *fbhw = info2->fbhw;
+    int err;
+
+    if (!fb_display[con].cmap.len) {	/* no colormap allocated ? */
+	if ((err = fb_alloc_cmap(&fb_display[con].cmap,
+				 1 << fb_display[con].var.bits_per_pixel, 0)))
+	    return err;
+    }
+    if (con == currcon)			/* current console ? */
+	return fb_set_cmap(cmap, &fb_display[con].var, kspc, fbhw->setcolreg,
+			   info);
+    else
+	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+    return 0;
+}
+
+
+    /*
+     *  Pan or Wrap the Display
+     *
+     *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+     */
+
+int fbgen_pan_display(struct fb_var_screeninfo *var, int con,
+		      struct fb_info *info)
+{
+    struct fb_info_gen *info2 = (struct fb_info_gen *)info;
+    struct fbgen_hwswitch *fbhw = info2->fbhw;
+    int xoffset = var->xoffset;
+    int yoffset = var->yoffset;
+    int err;
+
+    if (xoffset < 0 ||
+	xoffset+fb_display[con].var.xres > fb_display[con].var.xres_virtual ||
+	yoffset < 0 ||
+	yoffset+fb_display[con].var.yres > fb_display[con].var.yres_virtual)
+	return -EINVAL;
+    if (con == currcon) {
+	if (fbhw->pan_display) {
+	    if ((err = fbhw->pan_display(var, info2)))
+		return err;
+	} else
+	    return -EINVAL;
+    }
+    fb_display[con].var.xoffset = var->xoffset;
+    fb_display[con].var.yoffset = var->yoffset;
+    if (var->vmode & FB_VMODE_YWRAP)
+	fb_display[con].var.vmode |= FB_VMODE_YWRAP;
+    else
+	fb_display[con].var.vmode &= ~FB_VMODE_YWRAP;
+
+    return 0;
+}
+
+
+    /*
+     *  Frame Buffer Specific ioctls
+     */
+
+int fbgen_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		unsigned long arg, int con, struct fb_info *info)
+{
+    return -EINVAL;
+}
+
+
+/* ---- Helper functions --------------------------------------------------- */
+
+
+    /*
+     *  Change the video mode
+     */
+
+int fbgen_do_set_var(struct fb_var_screeninfo *var, int isactive,
+		     struct fb_info_gen *info)
+{
+    struct fbgen_hwswitch *fbhw = info->fbhw;
+    int err, activate;
+    char par[info->parsize];
+
+    if ((err = fbhw->decode_var(var, &par, info)))
+	return err;
+    activate = var->activate;
+    if (((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive)
+	fbhw->set_par(&par, info);
+    fbhw->encode_var(var, &par, info);
+    var->activate = activate;
+    return 0;
+}
+
+
+void fbgen_set_disp(int con, struct fb_info_gen *info)
+{
+    struct fbgen_hwswitch *fbhw = info->fbhw;
+    struct fb_fix_screeninfo fix;
+    char par[info->parsize];
+    struct display *display;
+
+    if (con >= 0)
+	display = &fb_display[con];
+    else
+	display = &disp;	/* used during initialization */
+
+    if (con == -1)
+	fbhw->get_par(&par, info);
+    else
+	fbhw->decode_var(&fb_display[con].var, &par, info);
+    memset(&fix, 0, sizeof(struct fb_fix_screeninfo));
+    fbhw->encode_fix(&fix, &par, info);
+
+    display->screen_base = fix.smem_start;
+    display->visual = fix.visual;
+    display->type = fix.type;
+    display->type_aux = fix.type_aux;
+    display->ypanstep = fix.ypanstep;
+    display->ywrapstep = fix.ywrapstep;
+    display->line_length = fix.line_length;
+    if (info->fbhw->blank || fix.visual == FB_VISUAL_PSEUDOCOLOR ||
+	fix.visual == FB_VISUAL_DIRECTCOLOR)
+	display->can_soft_blank = 1;
+    else
+	display->can_soft_blank = 0;
+    display->dispsw = fbhw->get_dispsw(&par, info);
+#if 0 /* FIXME: generic inverse is not supported yet */
+    display->inverse = (fix.visual == FB_VISUAL_MONO01 ? !inverse : inverse);
+#else
+    display->inverse = fix.visual == FB_VISUAL_MONO01;
+#endif
+}
+
+
+    /*
+     *  Install the current colormap
+     */
+
+void fbgen_install_cmap(int con, struct fb_info_gen *info)
+{
+    struct fbgen_hwswitch *fbhw = info->fbhw;
+    if (con != currcon)
+	return;
+    if (fb_display[con].cmap.len)
+	fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
+		    fbhw->setcolreg, &info->info);
+    else
+	fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+		    &fb_display[con].var, 1, fbhw->setcolreg, &info->info);
+}
+
+
+    /*
+     *  Update the `var' structure (called by fbcon.c)
+     */
+
+int fbgen_update_var(int con, struct fb_info *info)
+{
+    struct fb_info_gen *info2 = (struct fb_info_gen *)info;
+    struct fbgen_hwswitch *fbhw = info2->fbhw;
+    int err;
+
+    if (fbhw->pan_display) {
+        if ((err = fbhw->pan_display(&fb_display[con].var, info2)))
+            return err;
+    }
+    return 0;
+}
+
+
+    /*
+     *  Switch to a different virtual console
+     */
+
+int fbgen_switch(int con, struct fb_info *info)
+{
+    struct fb_info_gen *info2 = (struct fb_info_gen *)info;
+    struct fbgen_hwswitch *fbhw = info2->fbhw;
+
+    /* Do we have to save the colormap ? */
+    if (fb_display[currcon].cmap.len)
+	fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
+		    fbhw->getcolreg, &info2->info);
+    fbgen_do_set_var(&fb_display[con].var, 1, info2);
+    currcon = con;
+    /* Install new colormap */
+    fbgen_install_cmap(con, info2);
+    return 0;
+}
+
+
+    /*
+     *  Blank the screen
+     */
+
+void fbgen_blank(int blank, struct fb_info *info)
+{
+    struct fb_info_gen *info2 = (struct fb_info_gen *)info;
+    struct fbgen_hwswitch *fbhw = info2->fbhw;
+    u16 black[16];
+    struct fb_cmap cmap;
+
+    if (fbhw->blank && !fbhw->blank(blank, info2))
+	return;
+    if (blank) {
+	memset(black, 0, 16*sizeof(u16));
+	cmap.red = black;
+	cmap.green = black;
+	cmap.blue = black;
+	cmap.transp = NULL;
+	cmap.start = 0;
+	cmap.len = 16;
+	fb_set_cmap(&cmap, &fb_display[currcon].var, 1, fbhw->setcolreg, info);
+    } else
+	fbgen_install_cmap(currcon, info2);
+}
diff -u --recursive --new-file v2.1.90/linux/drivers/video/font_6x11.c linux/drivers/video/font_6x11.c
--- v2.1.90/linux/drivers/video/font_6x11.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/font_6x11.c	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,3345 @@
+/**********************************************/
+/*                                            */
+/*       Font file generated by rthelen       */
+/*                                            */
+/**********************************************/
+
+#define FONTDATAMAX (11*256)
+
+char fontname_6x11[] = "ProFont6x11";
+
+int  fontheight_6x11 = 11;
+int  fontwidth_6x11  = 6;
+
+unsigned char fontdata_6x11[FONTDATAMAX] = {
+
+	/* 0 0x00 '^A' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 1 0x01 '^B' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 2 0x02 '^C' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 3 0x03 '^D' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 4 0x04 '^E' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 5 0x05 '^F' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 6 0x06 '^G' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 7 0x07 '^H' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 8 0x08 '^I' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 9 0x09 '^J' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 10 0x0a '^K' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 11 0x0b '^L' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 12 0x0c '^M' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 13 0x0d '^N' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 14 0x0e '^O' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 15 0x0f '^P' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 16 0x10 '^Q' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 17 0x11 '^R' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x28, /* 00 0 000 */
+	0x54, /* 0 0 0 00 */
+	0x38, /* 00   000 */
+	0x54, /* 0 0 0 00 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 18 0x12 '^S' */
+	0x04, /* 00000 00 */
+	0x04, /* 00000 00 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x50, /* 0 0 0000 */
+	0x50, /* 0 0 0000 */
+	0x20, /* 00 00000 */
+	0x20, /* 00 00000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 19 0x13 '^T' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x38, /* 00   000 */
+	0x7c, /* 0     00 */
+	0x38, /* 00   000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 20 0x14 '^U' */
+	0x18, /* 000  000 */
+	0x10, /* 000 0000 */
+	0x28, /* 00 0 000 */
+	0x7c, /* 0     00 */
+	0x78, /* 0    000 */
+	0x78, /* 0    000 */
+	0x7c, /* 0     00 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 21 0x15 '^V' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 22 0x16 '^W' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 23 0x17 '^X' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 24 0x18 '^Y' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 25 0x19 '^Z' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 26 0x1a '^[' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 27 0x1b '^\' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 28 0x1c '^]' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 29 0x1d '^^' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 30 0x1e '^_' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 31 0x1f '^`' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 32 0x20 ' ' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 33 0x21 '!' */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 34 0x22 '"' */
+	0x28, /* 00 0 000 */
+	0x28, /* 00 0 000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 35 0x23 '#' */
+	0x00, /* 00000000 */
+	0x28, /* 00 0 000 */
+	0x7c, /* 0     00 */
+	0x28, /* 00 0 000 */
+	0x7c, /* 0     00 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 36 0x24 '$' */
+	0x10, /* 000 0000 */
+	0x38, /* 00   000 */
+	0x54, /* 0 0 0 00 */
+	0x50, /* 0 0 0000 */
+	0x38, /* 00   000 */
+	0x14, /* 000 0 00 */
+	0x54, /* 0 0 0 00 */
+	0x38, /* 00   000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 37 0x25 '%' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x54, /* 0 0 0 00 */
+	0x58, /* 0 0  000 */
+	0x28, /* 00 0 000 */
+	0x34, /* 00  0 00 */
+	0x54, /* 0 0 0 00 */
+	0x48, /* 0 00 000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 38 0x26 '&' */
+	0x00, /* 00000000 */
+	0x30, /* 00  0000 */
+	0x48, /* 0 00 000 */
+	0x50, /* 0 0 0000 */
+	0x20, /* 00 00000 */
+	0x54, /* 0 0 0 00 */
+	0x48, /* 0 00 000 */
+	0x34, /* 00  0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 39 0x27 ''' */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 40 0x28 '(' */
+	0x04, /* 00000 00 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x08, /* 0000 000 */
+	0x04, /* 00000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 41 0x29 ')' */
+	0x20, /* 00 00000 */
+	0x10, /* 000 0000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x20, /* 00 00000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 42 0x2a '*' */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x54, /* 0 0 0 00 */
+	0x38, /* 00   000 */
+	0x54, /* 0 0 0 00 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 43 0x2b '+' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x7c, /* 0     00 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 44 0x2c ',' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x30, /* 00  0000 */
+	0x30, /* 00  0000 */
+	0x10, /* 000 0000 */
+	0x20, /* 00 00000 */
+	0x00, /* 00000000 */
+
+	/* 45 0x2d '-' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 46 0x2e '.' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 000  000 */
+	0x18, /* 000  000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 47 0x2f '/' */
+	0x04, /* 00000 00 */
+	0x04, /* 00000 00 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x20, /* 00 00000 */
+	0x20, /* 00 00000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x00, /* 00000000 */
+
+	/* 48 0x30 '0' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x4c, /* 0 00  00 */
+	0x54, /* 0 0 0 00 */
+	0x64, /* 0  00 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 49 0x31 '1' */
+	0x00, /* 00000000 */
+	0x08, /* 0000 000 */
+	0x18, /* 000  000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x1c, /* 000   00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 50 0x32 '2' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x04, /* 00000 00 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x20, /* 00 00000 */
+	0x7c, /* 0     00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 51 0x33 '3' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x04, /* 00000 00 */
+	0x18, /* 000  000 */
+	0x04, /* 00000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 52 0x34 '4' */
+	0x00, /* 00000000 */
+	0x08, /* 0000 000 */
+	0x18, /* 000  000 */
+	0x28, /* 00 0 000 */
+	0x48, /* 0 00 000 */
+	0x7c, /* 0     00 */
+	0x08, /* 0000 000 */
+	0x1c, /* 000   00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 53 0x35 '5' */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x40, /* 0 000000 */
+	0x78, /* 0    000 */
+	0x04, /* 00000 00 */
+	0x04, /* 00000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 54 0x36 '6' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x40, /* 0 000000 */
+	0x78, /* 0    000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 55 0x37 '7' */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x04, /* 00000 00 */
+	0x04, /* 00000 00 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 56 0x38 '8' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 57 0x39 '9' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x3c, /* 00    00 */
+	0x04, /* 00000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 58 0x3a ':' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 000  000 */
+	0x18, /* 000  000 */
+	0x00, /* 00000000 */
+	0x18, /* 000  000 */
+	0x18, /* 000  000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 59 0x3b ';' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x30, /* 00  0000 */
+	0x30, /* 00  0000 */
+	0x00, /* 00000000 */
+	0x30, /* 00  0000 */
+	0x30, /* 00  0000 */
+	0x10, /* 000 0000 */
+	0x20, /* 00 00000 */
+	0x00, /* 00000000 */
+
+	/* 60 0x3c '<' */
+	0x00, /* 00000000 */
+	0x04, /* 00000 00 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x20, /* 00 00000 */
+	0x10, /* 000 0000 */
+	0x08, /* 0000 000 */
+	0x04, /* 00000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 61 0x3d '=' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 62 0x3e '>' */
+	0x00, /* 00000000 */
+	0x20, /* 00 00000 */
+	0x10, /* 000 0000 */
+	0x08, /* 0000 000 */
+	0x04, /* 00000 00 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x20, /* 00 00000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 63 0x3f '?' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x04, /* 00000 00 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 64 0x40 '@' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x74, /* 0   0 00 */
+	0x54, /* 0 0 0 00 */
+	0x78, /* 0    000 */
+	0x40, /* 0 000000 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 65 0x41 'A' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x7c, /* 0     00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 66 0x42 'B' */
+	0x00, /* 00000000 */
+	0x78, /* 0    000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x78, /* 0    000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x78, /* 0    000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 67 0x43 'C' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 68 0x44 'D' */
+	0x00, /* 00000000 */
+	0x78, /* 0    000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x78, /* 0    000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 69 0x45 'E' */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x78, /* 0    000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x7c, /* 0     00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 70 0x46 'F' */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x78, /* 0    000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 71 0x47 'G' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x40, /* 0 000000 */
+	0x4c, /* 0 00  00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 72 0x48 'H' */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x7c, /* 0     00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 73 0x49 'I' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 74 0x4a 'J' */
+	0x00, /* 00000000 */
+	0x04, /* 00000 00 */
+	0x04, /* 00000 00 */
+	0x04, /* 00000 00 */
+	0x04, /* 00000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 75 0x4b 'K' */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x48, /* 0 00 000 */
+	0x50, /* 0 0 0000 */
+	0x60, /* 0  00000 */
+	0x50, /* 0 0 0000 */
+	0x48, /* 0 00 000 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 76 0x4c 'L' */
+	0x00, /* 00000000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x7c, /* 0     00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 77 0x4d 'M' */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x6c, /* 0  0  00 */
+	0x54, /* 0 0 0 00 */
+	0x54, /* 0 0 0 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 78 0x4e 'N' */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x64, /* 0  00 00 */
+	0x54, /* 0 0 0 00 */
+	0x4c, /* 0 00  00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 79 0x4f 'O' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 80 0x50 'P' */
+	0x00, /* 00000000 */
+	0x78, /* 0    000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x78, /* 0    000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 81 0x51 'Q' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x54, /* 0 0 0 00 */
+	0x38, /* 00   000 */
+	0x04, /* 00000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 82 0x52 'R' */
+	0x00, /* 00000000 */
+	0x78, /* 0    000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x78, /* 0    000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 83 0x53 'S' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x40, /* 0 000000 */
+	0x38, /* 00   000 */
+	0x04, /* 00000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 84 0x54 'T' */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 85 0x55 'U' */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 86 0x56 'V' */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x28, /* 00 0 000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 87 0x57 'W' */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x54, /* 0 0 0 00 */
+	0x54, /* 0 0 0 00 */
+	0x6c, /* 0  0  00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 88 0x58 'X' */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x28, /* 00 0 000 */
+	0x10, /* 000 0000 */
+	0x28, /* 00 0 000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 89 0x59 'Y' */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x28, /* 00 0 000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 90 0x5a 'Z' */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x04, /* 00000 00 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x20, /* 00 00000 */
+	0x40, /* 0 000000 */
+	0x7c, /* 0     00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 91 0x5b '[' */
+	0x0c, /* 0000  00 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x0c, /* 0000  00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 92 0x5c '\' */
+	0x20, /* 00 00000 */
+	0x20, /* 00 00000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x04, /* 00000 00 */
+	0x04, /* 00000 00 */
+	0x02, /* 000000 0 */
+	0x02, /* 000000 0 */
+	0x00, /* 00000000 */
+
+	/* 93 0x5d ']' */
+	0x30, /* 00  0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x30, /* 00  0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 94 0x5e '^' */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x28, /* 00 0 000 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 95 0x5f '_' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 0      0 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 96 0x60 '`' */
+	0x20, /* 00 00000 */
+	0x10, /* 000 0000 */
+	0x08, /* 0000 000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 97 0x61 'a' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x4c, /* 0 00  00 */
+	0x34, /* 00  0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 98 0x62 'b' */
+	0x00, /* 00000000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x78, /* 0    000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x78, /* 0    000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 99 0x63 'c' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x40, /* 0 000000 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 100 0x64 'd' */
+	0x00, /* 00000000 */
+	0x04, /* 00000 00 */
+	0x04, /* 00000 00 */
+	0x3c, /* 00    00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 101 0x65 'e' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x7c, /* 0     00 */
+	0x40, /* 0 000000 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 102 0x66 'f' */
+	0x00, /* 00000000 */
+	0x0c, /* 0000  00 */
+	0x10, /* 000 0000 */
+	0x38, /* 00   000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 103 0x67 'g' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x3c, /* 00    00 */
+	0x04, /* 00000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+
+	/* 104 0x68 'h' */
+	0x00, /* 00000000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x78, /* 0    000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 105 0x69 'i' */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x30, /* 00  0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 106 0x6a 'j' */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x30, /* 00  0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x60, /* 0  00000 */
+	0x00, /* 00000000 */
+
+	/* 107 0x6b 'k' */
+	0x00, /* 00000000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x48, /* 0 00 000 */
+	0x50, /* 0 0 0000 */
+	0x70, /* 0   0000 */
+	0x48, /* 0 00 000 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 108 0x6c 'l' */
+	0x00, /* 00000000 */
+	0x30, /* 00  0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 109 0x6d 'm' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x78, /* 0    000 */
+	0x54, /* 0 0 0 00 */
+	0x54, /* 0 0 0 00 */
+	0x54, /* 0 0 0 00 */
+	0x54, /* 0 0 0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 110 0x6e 'n' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x58, /* 0 0  000 */
+	0x64, /* 0  00 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 111 0x6f 'o' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 112 0x70 'p' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x78, /* 0    000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x78, /* 0    000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x00, /* 00000000 */
+
+	/* 113 0x71 'q' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x3c, /* 00    00 */
+	0x04, /* 00000 00 */
+	0x04, /* 00000 00 */
+	0x00, /* 00000000 */
+
+	/* 114 0x72 'r' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x58, /* 0 0  000 */
+	0x64, /* 0  00 00 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 115 0x73 's' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x40, /* 0 000000 */
+	0x38, /* 00   000 */
+	0x04, /* 00000 00 */
+	0x78, /* 0    000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 116 0x74 't' */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x38, /* 00   000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x0c, /* 0000  00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 117 0x75 'u' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x4c, /* 0 00  00 */
+	0x34, /* 00  0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 118 0x76 'v' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x28, /* 00 0 000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 119 0x77 'w' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x54, /* 0 0 0 00 */
+	0x54, /* 0 0 0 00 */
+	0x54, /* 0 0 0 00 */
+	0x54, /* 0 0 0 00 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 120 0x78 'x' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x28, /* 00 0 000 */
+	0x10, /* 000 0000 */
+	0x28, /* 00 0 000 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 121 0x79 'y' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x3c, /* 00    00 */
+	0x04, /* 00000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+
+	/* 122 0x7a 'z' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x20, /* 00 00000 */
+	0x7c, /* 0     00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 123 0x7b '{' */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x04, /* 00000 00 */
+	0x00, /* 00000000 */
+
+	/* 124 0x7c '|' */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 125 0x7d '}' */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x20, /* 00 00000 */
+	0x00, /* 00000000 */
+
+	/* 126 0x7e '~' */
+	0x00, /* 00000000 */
+	0x34, /* 00  0 00 */
+	0x58, /* 0 0  000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 127 0x7f '^?' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 128 0x80 '\200' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x7c, /* 0     00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 129 0x81 '\201' */
+	0x28, /* 00 0 000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x7c, /* 0     00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 130 0x82 '\202' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 131 0x83 '\203' */
+	0x10, /* 000 0000 */
+	0x7c, /* 0     00 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x78, /* 0    000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x7c, /* 0     00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 132 0x84 '\204' */
+	0x58, /* 0 0  000 */
+	0x44, /* 0 000 00 */
+	0x64, /* 0  00 00 */
+	0x54, /* 0 0 0 00 */
+	0x4c, /* 0 00  00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 133 0x85 '\205' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 134 0x86 '\206' */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 135 0x87 '\207' */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x4c, /* 0 00  00 */
+	0x34, /* 00  0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 136 0x88 '\210' */
+	0x10, /* 000 0000 */
+	0x08, /* 0000 000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x4c, /* 0 00  00 */
+	0x34, /* 00  0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 137 0x89 '\211' */
+	0x10, /* 000 0000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x4c, /* 0 00  00 */
+	0x34, /* 00  0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 138 0x8a '\212' */
+	0x00, /* 00000000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x4c, /* 0 00  00 */
+	0x34, /* 00  0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 139 0x8b '\213' */
+	0x34, /* 00  0 00 */
+	0x58, /* 0 0  000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x4c, /* 0 00  00 */
+	0x34, /* 00  0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 140 0x8c '\214' */
+	0x18, /* 000  000 */
+	0x24, /* 00 00 00 */
+	0x18, /* 000  000 */
+	0x3c, /* 00    00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x4c, /* 0 00  00 */
+	0x34, /* 00  0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 141 0x8d '\215' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x3c, /* 00    00 */
+	0x10, /* 000 0000 */
+	0x20, /* 00 00000 */
+	0x00, /* 00000000 */
+
+	/* 142 0x8e '\216' */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x7c, /* 0     00 */
+	0x40, /* 0 000000 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 143 0x8f '\217' */
+	0x20, /* 00 00000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x7c, /* 0     00 */
+	0x40, /* 0 000000 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 144 0x90 '\220' */
+	0x10, /* 000 0000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x7c, /* 0     00 */
+	0x40, /* 0 000000 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 145 0x91 '\221' */
+	0x00, /* 00000000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x7c, /* 0     00 */
+	0x40, /* 0 000000 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 146 0x92 '\222' */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 147 0x93 '\223' */
+	0x20, /* 00 00000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 148 0x94 '\224' */
+	0x10, /* 000 0000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 149 0x95 '\225' */
+	0x00, /* 00000000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 150 0x96 '\226' */
+	0x34, /* 00  0 00 */
+	0x58, /* 0 0  000 */
+	0x00, /* 00000000 */
+	0x58, /* 0 0  000 */
+	0x64, /* 0  00 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 151 0x97 '\227' */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 152 0x98 '\230' */
+	0x20, /* 00 00000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 153 0x99 '\231' */
+	0x10, /* 000 0000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 154 0x9a '\232' */
+	0x00, /* 00000000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 155 0x9b '\233' */
+	0x34, /* 00  0 00 */
+	0x58, /* 0 0  000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 156 0x9c '\234' */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x4c, /* 0 00  00 */
+	0x34, /* 00  0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 157 0x9d '\235' */
+	0x20, /* 00 00000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x4c, /* 0 00  00 */
+	0x34, /* 00  0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 158 0x9e '\236' */
+	0x10, /* 000 0000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x4c, /* 0 00  00 */
+	0x34, /* 00  0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 159 0x9f '\237' */
+	0x00, /* 00000000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x4c, /* 0 00  00 */
+	0x34, /* 00  0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 160 0xa0 '\240' */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x38, /* 00   000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 161 0xa1 '\241' */
+	0x18, /* 000  000 */
+	0x24, /* 00 00 00 */
+	0x24, /* 00 00 00 */
+	0x18, /* 000  000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 162 0xa2 '\242' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x38, /* 00   000 */
+	0x54, /* 0 0 0 00 */
+	0x50, /* 0 0 0000 */
+	0x54, /* 0 0 0 00 */
+	0x38, /* 00   000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 163 0xa3 '\243' */
+	0x30, /* 00  0000 */
+	0x48, /* 0 00 000 */
+	0x40, /* 0 000000 */
+	0x70, /* 0   0000 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x44, /* 0 000 00 */
+	0x78, /* 0    000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 164 0xa4 '\244' */
+	0x44, /* 0 000 00 */
+	0x24, /* 00 00 00 */
+	0x50, /* 0 0 0000 */
+	0x48, /* 0 00 000 */
+	0x24, /* 00 00 00 */
+	0x14, /* 000 0 00 */
+	0x48, /* 0 00 000 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 165 0xa5 '\245' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x7c, /* 0     00 */
+	0x7c, /* 0     00 */
+	0x7c, /* 0     00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 166 0xa6 '\246' */
+	0x3c, /* 00    00 */
+	0x54, /* 0 0 0 00 */
+	0x54, /* 0 0 0 00 */
+	0x54, /* 0 0 0 00 */
+	0x3c, /* 00    00 */
+	0x14, /* 000 0 00 */
+	0x14, /* 000 0 00 */
+	0x14, /* 000 0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 167 0xa7 '\247' */
+	0x18, /* 000  000 */
+	0x24, /* 00 00 00 */
+	0x44, /* 0 000 00 */
+	0x48, /* 0 00 000 */
+	0x48, /* 0 00 000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x58, /* 0 0  000 */
+	0x40, /* 0 000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 168 0xa8 '\250' */
+	0x00, /* 00000000 */
+	0x70, /* 0   0000 */
+	0x08, /* 0000 000 */
+	0x64, /* 0  00 00 */
+	0x54, /* 0 0 0 00 */
+	0x64, /* 0  00 00 */
+	0x58, /* 0 0  000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 169 0xa9 '\251' */
+	0x00, /* 00000000 */
+	0x70, /* 0   0000 */
+	0x08, /* 0000 000 */
+	0x34, /* 00  0 00 */
+	0x44, /* 0 000 00 */
+	0x34, /* 00  0 00 */
+	0x08, /* 0000 000 */
+	0x70, /* 0   0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 170 0xaa '\252' */
+	0x00, /* 00000000 */
+	0x7a, /* 0    0 0 */
+	0x2e, /* 00 0   0 */
+	0x2e, /* 00 0   0 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 171 0xab '\253' */
+	0x00, /* 00000000 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 172 0xac '\254' */
+	0x00, /* 00000000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 173 0xad '\255' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x08, /* 0000 000 */
+	0x7c, /* 0     00 */
+	0x10, /* 000 0000 */
+	0x7c, /* 0     00 */
+	0x20, /* 00 00000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 174 0xae '\256' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x50, /* 0 0 0000 */
+	0x50, /* 0 0 0000 */
+	0x78, /* 0    000 */
+	0x50, /* 0 0 0000 */
+	0x50, /* 0 0 0000 */
+	0x5c, /* 0 0   00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 175 0xaf '\257' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x4c, /* 0 00  00 */
+	0x54, /* 0 0 0 00 */
+	0x64, /* 0  00 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 176 0xb0 '\260' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x6c, /* 0  0  00 */
+	0x54, /* 0 0 0 00 */
+	0x6c, /* 0  0  00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 177 0xb1 '\261' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x7c, /* 0     00 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x7c, /* 0     00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 178 0xb2 '\262' */
+	0x00, /* 00000000 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x20, /* 00 00000 */
+	0x10, /* 000 0000 */
+	0x08, /* 0000 000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 179 0xb3 '\263' */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x08, /* 0000 000 */
+	0x04, /* 00000 00 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x1c, /* 000   00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 180 0xb4 '\264' */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x28, /* 00 0 000 */
+	0x7c, /* 0     00 */
+	0x10, /* 000 0000 */
+	0x7c, /* 0     00 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 181 0xb5 '\265' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x48, /* 0 00 000 */
+	0x48, /* 0 00 000 */
+	0x48, /* 0 00 000 */
+	0x48, /* 0 00 000 */
+	0x74, /* 0   0 00 */
+	0x40, /* 0 000000 */
+	0x40, /* 0 000000 */
+	0x00, /* 00000000 */
+
+	/* 182 0xb6 '\266' */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x08, /* 0000 000 */
+	0x0c, /* 0000  00 */
+	0x14, /* 000 0 00 */
+	0x24, /* 00 00 00 */
+	0x24, /* 00 00 00 */
+	0x18, /* 000  000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 183 0xb7 '\267' */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x24, /* 00 00 00 */
+	0x10, /* 000 0000 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x24, /* 00 00 00 */
+	0x7c, /* 0     00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 184 0xb8 '\270' */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x28, /* 00 0 000 */
+	0x28, /* 00 0 000 */
+	0x28, /* 00 0 000 */
+	0x28, /* 00 0 000 */
+	0x28, /* 00 0 000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 185 0xb9 '\271' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x28, /* 00 0 000 */
+	0x28, /* 00 0 000 */
+	0x28, /* 00 0 000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 186 0xba '\272' */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x60, /* 0  00000 */
+	0x00, /* 00000000 */
+
+	/* 187 0xbb '\273' */
+	0x00, /* 00000000 */
+	0x1c, /* 000   00 */
+	0x24, /* 00 00 00 */
+	0x24, /* 00 00 00 */
+	0x1c, /* 000   00 */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 188 0xbc '\274' */
+	0x00, /* 00000000 */
+	0x18, /* 000  000 */
+	0x24, /* 00 00 00 */
+	0x24, /* 00 00 00 */
+	0x18, /* 000  000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 189 0xbd '\275' */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x28, /* 00 0 000 */
+	0x6c, /* 0  0  00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 190 0xbe '\276' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x54, /* 0 0 0 00 */
+	0x5c, /* 0 0   00 */
+	0x50, /* 0 0 0000 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 191 0xbf '\277' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x4c, /* 0 00  00 */
+	0x54, /* 0 0 0 00 */
+	0x64, /* 0  00 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 192 0xc0 '\300' */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x20, /* 00 00000 */
+	0x40, /* 0 000000 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 193 0xc1 '\301' */
+	0x00, /* 00000000 */
+	0x08, /* 0000 000 */
+	0x00, /* 00000000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x08, /* 0000 000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 194 0xc2 '\302' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x04, /* 00000 00 */
+	0x04, /* 00000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 195 0xc3 '\303' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x0c, /* 0000  00 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x50, /* 0 0 0000 */
+	0x20, /* 00 00000 */
+	0x20, /* 00 00000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 196 0xc4 '\304' */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x38, /* 00   000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x60, /* 0  00000 */
+	0x00, /* 00000000 */
+
+	/* 197 0xc5 '\305' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x04, /* 00000 00 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x40, /* 0 000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 198 0xc6 '\306' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x10, /* 000 0000 */
+	0x28, /* 00 0 000 */
+	0x28, /* 00 0 000 */
+	0x44, /* 0 000 00 */
+	0x7c, /* 0     00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 199 0xc7 '\307' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x24, /* 00 00 00 */
+	0x48, /* 0 00 000 */
+	0x48, /* 0 00 000 */
+	0x24, /* 00 00 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 200 0xc8 '\310' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x48, /* 0 00 000 */
+	0x24, /* 00 00 00 */
+	0x24, /* 00 00 00 */
+	0x48, /* 0 00 000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 201 0xc9 '\311' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x54, /* 0 0 0 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 202 0xca '\312' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 203 0xcb '\313' */
+	0x10, /* 000 0000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x7c, /* 0     00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 204 0xcc '\314' */
+	0x58, /* 0 0  000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x7c, /* 0     00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 205 0xcd '\315' */
+	0x58, /* 0 0  000 */
+	0x38, /* 00   000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 206 0xce '\316' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x50, /* 0 0 0000 */
+	0x50, /* 0 0 0000 */
+	0x58, /* 0 0  000 */
+	0x50, /* 0 0 0000 */
+	0x50, /* 0 0 0000 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 207 0xcf '\317' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x28, /* 00 0 000 */
+	0x54, /* 0 0 0 00 */
+	0x5c, /* 0 0   00 */
+	0x50, /* 0 0 0000 */
+	0x2c, /* 00 0  00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 208 0xd0 '\320' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 209 0xd1 '\321' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 0      0 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 210 0xd2 '\322' */
+	0x00, /* 00000000 */
+	0x14, /* 000 0 00 */
+	0x28, /* 00 0 000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 211 0xd3 '\323' */
+	0x00, /* 00000000 */
+	0x14, /* 000 0 00 */
+	0x14, /* 000 0 00 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 212 0xd4 '\324' */
+	0x00, /* 00000000 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x18, /* 000  000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 213 0xd5 '\325' */
+	0x00, /* 00000000 */
+	0x18, /* 000  000 */
+	0x08, /* 0000 000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 214 0xd6 '\326' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x7c, /* 0     00 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 215 0xd7 '\327' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x10, /* 000 0000 */
+	0x28, /* 00 0 000 */
+	0x44, /* 0 000 00 */
+	0x28, /* 00 0 000 */
+	0x10, /* 000 0000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 216 0xd8 '\330' */
+	0x00, /* 00000000 */
+	0x28, /* 00 0 000 */
+	0x00, /* 00000000 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x44, /* 0 000 00 */
+	0x3c, /* 00    00 */
+	0x04, /* 00000 00 */
+	0x38, /* 00   000 */
+	0x00, /* 00000000 */
+
+	/* 217 0xd9 '\331' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 0      0 */
+	0x00, /* 00000000 */
+	0x7e, /* 0      0 */
+	0x00, /* 00000000 */
+	0x7e, /* 0      0 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 218 0xda '\332' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 219 0xdb '\333' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 220 0xdc '\334' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 221 0xdd '\335' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 222 0xde '\336' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 223 0xdf '\337' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 224 0xe0 '\340' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 225 0xe1 '\341' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 226 0xe2 '\342' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 227 0xe3 '\343' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 228 0xe4 '\344' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 229 0xe5 '\345' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 230 0xe6 '\346' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 231 0xe7 '\347' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 232 0xe8 '\350' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 233 0xe9 '\351' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 234 0xea '\352' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 235 0xeb '\353' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 236 0xec '\354' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 237 0xed '\355' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 238 0xee '\356' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 239 0xef '\357' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 240 0xf0 '\360' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 241 0xf1 '\361' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 242 0xf2 '\362' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 243 0xf3 '\363' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 244 0xf4 '\364' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 245 0xf5 '\365' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 246 0xf6 '\366' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 247 0xf7 '\367' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 248 0xf8 '\370' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 249 0xf9 '\371' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 250 0xfa '\372' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 251 0xfb '\373' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 252 0xfc '\374' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 253 0xfd '\375' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 254 0xfe '\376' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 255 0xff '\377' */
+	0x00, /* 00000000 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x3c, /* 00    00 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+};
+
diff -u --recursive --new-file v2.1.90/linux/drivers/video/fonts.c linux/drivers/video/fonts.c
--- v2.1.90/linux/drivers/video/fonts.c	Mon Feb 23 18:12:10 1998
+++ linux/drivers/video/fonts.c	Sat Mar 21 11:12:49 1998
@@ -9,7 +9,7 @@
  */
 
 
-#include <linux/config.h> /* for CONFIG_AMIGA */
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/string.h>
 #ifdef __mc68000__
@@ -25,17 +25,22 @@
 /* VGA8x8 */
 extern char fontname_8x8[];
 extern int fontwidth_8x8, fontheight_8x8;
-extern u_char fontdata_8x8[];
+extern u8 fontdata_8x8[];
 
 /* VGA8x16 */
 extern char fontname_8x16[];
 extern int fontwidth_8x16, fontheight_8x16;
-extern u_char fontdata_8x16[];
+extern u8 fontdata_8x16[];
 
 /* PEARL8x8 */
 extern char fontname_pearl8x8[];
 extern int fontwidth_pearl8x8, fontheight_pearl8x8;
-extern u_char fontdata_pearl8x8[];
+extern u8 fontdata_pearl8x8[];
+
+/* VGA6x11 */
+extern char fontname_6x11[];
+extern int fontwidth_6x11, fontheight_6x11;
+extern u8 fontdata_6x11[];
 
 
    /*
@@ -46,18 +51,20 @@
    char *name;
    int *width;
    int *height;
-   u_char *data;
+   u8 *data;
 };
 
 #define VGA8x8_IDX	0
 #define VGA8x16_IDX	1
 #define PEARL8x8_IDX	2
+#define VGA6x11_IDX	3
 
 static struct softfontdesc softfonts[] = {
    { fontname_8x8, &fontwidth_8x8, &fontheight_8x8, fontdata_8x8 },
    { fontname_8x16, &fontwidth_8x16, &fontheight_8x16, fontdata_8x16 },
    { fontname_pearl8x8, &fontwidth_pearl8x8, &fontheight_pearl8x8,
      fontdata_pearl8x8 },
+   { fontname_6x11, &fontwidth_6x11, &fontheight_6x11, fontdata_6x11 },
 };
 
 static unsigned int numsoftfonts = sizeof(softfonts)/sizeof(*softfonts);
@@ -67,7 +74,7 @@
     *    Find a font with a specific name
     */
 
-int findsoftfont(char *name, int *width, int *height, u_char *data[])
+int findsoftfont(char *name, int *width, int *height, u8 *data[])
 {
    unsigned int i;
 
@@ -90,7 +97,7 @@
     */
 
 void getdefaultfont(int xres, int yres, char *name[], int *width, int *height,
-                    u_char *data[])
+                    u8 *data[])
 {
     int i;
     
@@ -102,6 +109,16 @@
 #endif
     } else
 	i = VGA8x16_IDX;
+
+#if defined(CONFIG_MAC)
+    if (MACH_IS_MAC) {
+#if 0  /* MSch: removed until 6x11 is debugged */
+        i = VGA6x11_IDX;   /* I added this for fun ... I like 6x11 */
+#endif
+       if (xres < 640)
+           i = VGA6x11_IDX;
+    }
+#endif
 
     if (name)
 	*name = softfonts[i].name;
diff -u --recursive --new-file v2.1.90/linux/drivers/video/macfb.c linux/drivers/video/macfb.c
--- v2.1.90/linux/drivers/video/macfb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/macfb.c	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,459 @@
+/*
+ *	We've been given MAC frame buffer info by the booter. Now go set it up
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/nubus.h>
+#include <linux/init.h>
+
+#include <asm/setup.h>
+#include <asm/bootinfo.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+#include <asm/macintosh.h>
+#include <linux/fb.h>
+
+
+/* conditionalize these ?? */
+#include "fbcon-mfb.h"
+#include "fbcon-cfb2.h"
+#include "fbcon-cfb4.h"
+#include "fbcon-cfb8.h"
+
+#define arraysize(x)    (sizeof(x)/sizeof(*(x)))
+
+static struct fb_var_screeninfo macfb_defined={
+	0,0,0,0,	/* W,H, W, H (virtual) load xres,xres_virtual*/
+	0,0,		/* virtual -> visible no offset */
+	8,		/* depth -> load bits_per_pixel */
+	0,		/* greyscale ? */
+	{0,0,0},	/* R */
+	{0,0,0},	/* G */
+	{0,0,0},	/* B */
+	{0,0,0},	/* transparency */
+	0,		/* standard pixel format */
+	FB_ACTIVATE_NOW,
+	274,195,	/* 14" monitor *Mikael Nykvist's anyway* */
+	FB_ACCEL_NONE,	/* The only way to accelerate a mac is .. */
+	0L,0L,0L,0L,0L,
+	0L,0L,0,	/* No sync info */
+	FB_VMODE_NONINTERLACED,
+	{0,0,0,0,0,0}
+};
+
+#define NUM_TOTAL_MODES		1
+#define NUM_PREDEF_MODES	1
+
+static struct display disp;
+static struct fb_info fb_info;
+
+static int inverse = 0;
+
+struct macfb_par
+{
+	void *unused;
+};
+
+static int currcon = 0;
+static int current_par_valid = 0;
+struct macfb_par current_par;
+
+static int mac_xres,mac_yres,mac_depth, mac_xbytes, mac_vxres;
+static unsigned long mac_videobase;
+static unsigned long mac_videosize;
+
+	/*
+	 * Open/Release the frame buffer device
+	 */
+
+static int macfb_open(struct fb_info *info)
+{
+	/*
+	 * Nothing, only a usage count for the moment
+	 */
+	MOD_INC_USE_COUNT;
+	return(0);
+}
+
+static int macfb_release(struct fb_info *info)
+{
+	MOD_DEC_USE_COUNT;
+	return(0);
+}
+
+static void macfb_encode_var(struct fb_var_screeninfo *var, 
+				struct macfb_par *par)
+{
+	int i=0;
+	var->xres=mac_xres;
+	var->yres=mac_yres;
+	var->xres_virtual=mac_vxres;
+	var->yres_virtual=var->yres;
+	var->xoffset=0;
+	var->yoffset=0;
+	var->bits_per_pixel = mac_depth;
+	var->grayscale=0;
+	var->transp.offset=0;
+	var->transp.length=0;
+	var->transp.msb_right=0;
+	var->nonstd=0;
+	var->activate=0;
+	var->height= -1;
+	var->width= -1;
+	var->accel=0;
+	var->vmode=FB_VMODE_NONINTERLACED;
+	var->pixclock=0;
+	var->sync=0;
+	var->left_margin=0;
+	var->right_margin=0;
+	var->upper_margin=0;
+	var->lower_margin=0;
+	var->hsync_len=0;
+	var->vsync_len=0;
+	for(i=0;i<arraysize(var->reserved);i++)
+		var->reserved[i]=0;
+	return;
+}
+
+
+static void macfb_get_par(struct macfb_par *par)
+{
+	*par=current_par;
+}
+
+static void macfb_set_par(struct macfb_par *par)
+{
+	current_par_valid=1;
+}
+
+static int fb_update_var(int con, struct fb_info *info)
+{
+	return 0;
+}
+
+static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
+{
+	struct macfb_par par;
+	
+	macfb_get_par(&par);
+	macfb_encode_var(var, &par);
+	return 0;
+}
+
+extern int console_loglevel;
+
+static void macfb_encode_fix(struct fb_fix_screeninfo *fix, 
+				struct macfb_par *par)
+{
+	int i;
+
+	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+	strcpy(fix->id,"Macintosh");
+
+	/*
+	 * X works, but screen wraps ... 
+	 */
+	fix->smem_start=(char *)(mac_videobase&PAGE_MASK);
+	fix->smem_offset=(mac_videobase&~PAGE_MASK);
+	fix->smem_len=PAGE_ALIGN(mac_videosize);
+	fix->type = FB_TYPE_PACKED_PIXELS;
+	fix->visual = FB_VISUAL_PSEUDOCOLOR;
+	fix->xpanstep=0;
+	fix->ypanstep=0;
+	fix->ywrapstep=0;
+	fix->line_length=mac_xbytes;
+	return;
+}
+
+static int macfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			 struct fb_info *info)
+{
+	struct macfb_par par;
+	macfb_get_par(&par);
+	macfb_encode_fix(fix, &par);
+	return 0;
+}
+
+static int macfb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info)
+{
+	struct macfb_par par;
+	if(con==-1)
+	{
+		macfb_get_par(&par);
+		macfb_encode_var(var, &par);
+	}
+	else
+		*var=fb_display[con].var;
+	return 0;
+}
+
+static void macfb_set_disp(int con)
+{
+	struct fb_fix_screeninfo fix;
+	struct display *display;
+	
+	if (con >= 0)
+		display = &fb_display[con];
+	else
+		display = &disp;	/* used during initialization */
+
+	macfb_get_fix(&fix, con, 0);
+
+	display->screen_base = (u_char *)(fix.smem_start+fix.smem_offset);
+	display->visual = fix.visual;
+	display->type = fix.type;
+	display->type_aux = fix.type_aux;
+	display->ypanstep = fix.ypanstep;
+	display->ywrapstep = fix.ywrapstep;
+	display->line_length = fix.line_length;
+	display->next_line = fix.line_length;
+	display->can_soft_blank = 0;
+	display->inverse = inverse;
+
+	switch (mac_depth) {
+#ifdef CONFIG_FBCON_MFB
+	    case 1:
+		display->dispsw = &fbcon_mfb;
+		break;
+#endif
+#ifdef CONFIG_FBCON_CFB2
+	    case 2:
+		display->dispsw = &fbcon_cfb2;
+		break;
+#endif
+#ifdef CONFIG_FBCON_CFB4
+	    case 4:
+		display->dispsw = &fbcon_cfb4;
+		break;
+#endif
+#ifdef CONFIG_FBCON_CFB8
+	    case 8:
+		display->dispsw = &fbcon_cfb8;
+		break;
+#endif
+	    default:
+		display->dispsw = NULL;
+		break;
+	}
+}
+
+static int macfb_set_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info)
+{
+	int err;
+	
+	if ((err=do_fb_set_var(var, 1)))
+		return err;
+	return 0;
+}
+
+static int macfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info)
+{
+#if 0
+	printk("macfb_get_cmap: not supported!\n");
+	/* interferes with X11 */
+	if (console_loglevel < 7)
+		return -EINVAL;
+	if (con == currcon) /* current console? */
+		return fb_get_cmap(cmap, &fb_display[con].var, kspc, 0 /*offb_getcolreg*/, info);
+	else if (fb_display[con].cmap.len) /* non default colormap? */
+		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+	else
+		fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+		     cmap, kspc ? 0 : 2);
+#endif
+	return 0;
+
+}
+
+static int macfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info)
+{
+#if 0
+	printk("macfb_set_cmap: not supported!\n");
+	if (console_loglevel < 7)
+		return -EINVAL;
+	if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
+		if ((err = fb_alloc_cmap(&fb_display[con].cmap,
+				 1<<fb_display[con].var.bits_per_pixel, 0)))
+		return err;
+	}
+	if (con == currcon)			/* current console? */
+		return fb_set_cmap(cmap, &fb_display[con].var, kspc, 1 /*offb_setcolreg*/, info);
+	else
+		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+#endif
+	return 0;
+}
+
+static int macfb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info)
+{
+	/* no panning */
+	printk("macfb_pan: not supported!\n");
+	return -EINVAL;
+}
+
+static int macfb_ioctl(struct inode *inode, struct file *file, 
+		       unsigned int cmd, unsigned long arg, int con,
+		       struct fb_info *info)
+{
+	printk("macfb_ioctl: not supported!\n");
+	return -EINVAL;
+}
+
+static struct fb_ops macfb_ops = {
+	macfb_open,
+	macfb_release,
+	macfb_get_fix,
+	macfb_get_var,
+	macfb_set_var,
+	macfb_get_cmap,
+	macfb_set_cmap,
+	macfb_pan_display,
+	NULL,
+	macfb_ioctl
+};
+
+void macfb_setup(char *options, int *ints)
+{
+    char *this_opt;
+    int temp;
+
+    fb_info.fontname[0] = '\0';
+
+    if (!options || !*options)
+		return;
+     
+    for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {
+	if (!*this_opt) continue;
+
+	if (! strcmp(this_opt, "inverse"))
+		inverse=1;
+	else if (!strncmp(this_opt, "font:", 5)) {
+	   strcpy(fb_info.fontname, this_opt+5);
+	   printk("macfb_setup: option %s\n", this_opt);
+	}
+    }
+}
+
+static int macfb_switch(int con, struct fb_info *info)
+{
+	do_fb_set_var(&fb_display[con].var,1);
+	currcon=con;
+	return 0;
+}
+
+/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
+
+static void macfb_blank(int blank, struct fb_info *info)
+{
+	/* Not supported */
+}
+
+/*
+ *	Nubus call back. This will give us our board identity and also
+ *	other useful info we need later
+ */
+ 
+static int nubus_video_card(struct nubus_device_specifier *ns, int slot, struct nubus_type *nt)
+{
+	if(nt->category==NUBUS_CAT_DISPLAY)
+		return 0;
+	/* Claim all video cards. We dont yet do driver specifics tho. */
+	return -ENODEV;
+}
+
+static struct nubus_device_specifier nb_video={
+	nubus_video_card,
+	NULL
+};
+
+__initfunc(unsigned long macfb_init(unsigned long mem_start))
+{
+	/* nubus_remap the video .. */
+	int err;
+
+	if (!MACH_IS_MAC) 
+		return mem_start;
+
+	mac_xres=mac_bi_data.dimensions&0xFFFF;
+	mac_yres=(mac_bi_data.dimensions&0xFFFF0000)>>16;
+	mac_depth=mac_bi_data.videodepth;
+	mac_xbytes=mac_bi_data.videorow;
+	mac_vxres = (mac_xbytes/mac_depth)*8;
+	mac_videosize=mac_xbytes*mac_yres;
+	mac_videobase=mac_bi_data.videoaddr;
+
+	printk("macfb_init: xres %d yres %d bpp %d addr %x size %d \n",
+		mac_xres, mac_yres, mac_depth, mac_videobase, mac_videosize);
+
+	mac_debugging_penguin(4);
+	
+	/*
+	 *	Fill in the available video resolution
+	 */
+	 
+	 macfb_defined.xres=mac_xres;
+	 macfb_defined.yres=mac_yres;
+	 macfb_defined.xres_virtual=mac_vxres;
+	 macfb_defined.yres_virtual=mac_yres;
+	 macfb_defined.bits_per_pixel=mac_depth;
+	 
+	 
+	/*
+	 *	Let there be consoles..
+	 */
+	strcpy(fb_info.modename, "Macintosh Builtin ");
+	fb_info.changevar = NULL;
+	fb_info.node = -1;
+	fb_info.fbops = &macfb_ops;
+	fb_info.disp=&disp;
+	fb_info.switch_con=&macfb_switch;
+	fb_info.updatevar=&fb_update_var;
+	fb_info.blank=&macfb_blank;
+	do_fb_set_var(&macfb_defined,1);
+
+	err=register_framebuffer(&fb_info);
+	if(err<0)
+	{
+		mac_boom(6);
+		return NULL;
+	}
+
+	macfb_get_var(&disp.var, -1, &fb_info);
+	macfb_set_disp(-1);
+
+	/*
+	 *	Register the nubus hook
+	 */
+	 
+	register_nubus_device(&nb_video);
+
+	printk("fb%d: %s frame buffer device using %ldK of video memory\n",
+	       GET_FB_IDX(fb_info.node), fb_info.modename, mac_videosize>>10);
+
+	return mem_start;
+}
+
+#if 0
+/*
+ * These two auxiliary debug functions should go away ASAP. Only usage: 
+ * before the console output is up (after head.S come some other crucial
+ * setup routines :-) 
+ *
+ * Now in debug.c ...
+ */
+#endif
diff -u --recursive --new-file v2.1.90/linux/drivers/video/offb.c linux/drivers/video/offb.c
--- v2.1.90/linux/drivers/video/offb.c	Mon Nov 24 01:18:58 1997
+++ linux/drivers/video/offb.c	Sat Mar 21 11:12:49 1998
@@ -12,6 +12,7 @@
  *  more details.
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -23,53 +24,83 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/fb.h>
+#include <linux/selection.h>
 #include <linux/init.h>
+#ifdef CONFIG_FB_COMPAT_XPMAC
+#include <linux/vc_ioctl.h>
+#endif
 #include <asm/io.h>
 #include <asm/prom.h>
 
+#include "fbcon-cfb8.h"
 
-#define arraysize(x)	(sizeof(x)/sizeof(*(x)))
 
 static int currcon = 0;
-static struct display disp;
-static struct fb_info fb_info;
-static struct { u_char red, green, blue, pad; } palette[256];
-static char offb_name[16] = "OFfb ";
 
-static volatile unsigned char *unknown_cmap_adr = NULL;
-static volatile unsigned char *unknown_cmap_data = NULL;
+struct fb_info_offb {
+    struct fb_info info;
+    struct fb_fix_screeninfo fix;
+    struct fb_var_screeninfo var;
+    struct display disp;
+    struct { u_char red, green, blue, pad; } palette[256];
+    volatile unsigned char *cmap_adr;
+    volatile unsigned char *cmap_data;
+};
+
+static struct fb_info_offb fb_info[FB_MAX];
+
+#ifdef __powerpc__
+#define mach_eieio()	eieio()
+#else
+#define mach_eieio()	do {} while (0)
+#endif
 
-static struct fb_fix_screeninfo fb_fix = { 0, };
-static struct fb_var_screeninfo fb_var = { 0, };
+static int ofonly = 0;
 
 
     /*
      *  Interface used by the world
      */
 
-void offb_video_setup(char *options, int *ints);
+unsigned long offb_init(unsigned long mem_start);
+void offb_setup(char *options, int *ints);
 
-static int offb_open(int fbidx);
-static int offb_release(int fbidx);
-static int offb_get_fix(struct fb_fix_screeninfo *fix, int con);
-static int offb_get_var(struct fb_var_screeninfo *var, int con);
-static int offb_set_var(struct fb_var_screeninfo *var, int con);
-static int offb_pan_display(struct fb_var_screeninfo *var, int con);
-static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con);
-static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int offb_open(struct fb_info *info);
+static int offb_release(struct fb_info *info);
+static int offb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			struct fb_info *info);
+static int offb_get_var(struct fb_var_screeninfo *var, int con,
+			struct fb_info *info);
+static int offb_set_var(struct fb_var_screeninfo *var, int con,
+			struct fb_info *info);
+static int offb_pan_display(struct fb_var_screeninfo *var, int con,
+			struct fb_info *info);
+static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			struct fb_info *info);
+static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			struct fb_info *info);
 static int offb_ioctl(struct inode *inode, struct file *file, u_int cmd,
-			    u_long arg, int con);
+			    u_long arg, int con, struct fb_info *info);
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+int console_getmode(struct vc_mode *);
+int console_setmode(struct vc_mode *, int);
+int console_powermode(int);
+struct fb_info *console_fb_info = NULL;
+int (*console_setmode_ptr)(struct vc_mode *, int) = NULL;
+int (*console_set_cmap_ptr)(struct fb_cmap *, int, int, struct fb_info *)
+    = NULL;
+struct vc_mode display_info;
+#endif /* CONFIG_FB_COMPAT_XPMAC */
 
 
     /*
      *  Interface to the low level console driver
      */
 
-unsigned long offb_init(unsigned long mem_start);
-static int offbcon_switch(int con);
-static int offbcon_updatevar(int con);
-static void offbcon_blank(int blank);
-static int offbcon_setcmap(struct fb_cmap *cmap, int con);
+static int offbcon_switch(int con, struct fb_info *info);
+static int offbcon_updatevar(int con, struct fb_info *info);
+static void offbcon_blank(int blank, struct fb_info *info);
 
 
     /*
@@ -77,15 +108,15 @@
      */
 
 static int offb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
-                         u_int *transp);
+			 u_int *transp, struct fb_info *info);
 static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp);
-static void do_install_cmap(int con);
+			 u_int transp, struct fb_info *info);
+static void do_install_cmap(int con, struct fb_info *info);
 
 
 static struct fb_ops offb_ops = {
     offb_open, offb_release, offb_get_fix, offb_get_var, offb_set_var,
-    offb_get_cmap, offb_set_cmap, offb_pan_display, offb_ioctl
+    offb_get_cmap, offb_set_cmap, offb_pan_display, NULL, offb_ioctl
 };
 
 
@@ -93,20 +124,20 @@
      *  Open/Release the frame buffer device
      */
 
-static int offb_open(int fbidx)                                       
+static int offb_open(struct fb_info *info)
 {
-    /*                                                                     
-     *  Nothing, only a usage count for the moment                          
-     */                                                                    
+    /*
+     *  Nothing, only a usage count for the moment
+     */
 
     MOD_INC_USE_COUNT;
-    return(0);                              
+    return(0);
 }
-        
-static int offb_release(int fbidx)
+
+static int offb_release(struct fb_info *info)
 {
     MOD_DEC_USE_COUNT;
-    return(0);                                                    
+    return(0);
 }
 
 
@@ -114,9 +145,12 @@
      *  Get the Fixed Part of the Display
      */
 
-static int offb_get_fix(struct fb_fix_screeninfo *fix, int con)
+static int offb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			struct fb_info *info)
 {
-    memcpy(fix, &fb_fix, sizeof(fb_fix));
+    struct fb_info_offb *info2 = (struct fb_info_offb *)info;
+
+    memcpy(fix, &info2->fix, sizeof(struct fb_fix_screeninfo));
     return 0;
 }
 
@@ -125,9 +159,12 @@
      *  Get the User Defined Part of the Display
      */
 
-static int offb_get_var(struct fb_var_screeninfo *var, int con)
+static int offb_get_var(struct fb_var_screeninfo *var, int con,
+			struct fb_info *info)
 {
-    memcpy(var, &fb_var, sizeof(fb_var));
+    struct fb_info_offb *info2 = (struct fb_info_offb *)info;
+
+    memcpy(var, &info2->var, sizeof(struct fb_var_screeninfo));
     return 0;
 }
 
@@ -136,33 +173,36 @@
      *  Set the User Defined Part of the Display
      */
 
-static int offb_set_var(struct fb_var_screeninfo *var, int con)
+static int offb_set_var(struct fb_var_screeninfo *var, int con,
+			struct fb_info *info)
 {
     struct display *display;
     int oldbpp = -1, err;
+    int activate = var->activate;
+    struct fb_info_offb *info2 = (struct fb_info_offb *)info;
 
     if (con >= 0)
 	display = &fb_display[con];
     else
-	display = &disp;	/* used during initialization */
+	display = &info2->disp;	/* used during initialization */
 
-    if (var->xres > fb_var.xres || var->yres > fb_var.yres ||
-	var->xres_virtual > fb_var.xres_virtual ||
-	var->yres_virtual > fb_var.yres_virtual ||
-	var->bits_per_pixel > fb_var.bits_per_pixel ||
+    if (var->xres > info2->var.xres || var->yres > info2->var.yres ||
+	var->xres_virtual > info2->var.xres_virtual ||
+	var->yres_virtual > info2->var.yres_virtual ||
+	var->bits_per_pixel > info2->var.bits_per_pixel ||
 	var->nonstd ||
 	(var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
 	return -EINVAL;
-    memcpy(var, &fb_var, sizeof(fb_var));
+    memcpy(var, &info2->var, sizeof(struct fb_var_screeninfo));
 
-    if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+    if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
 	oldbpp = display->var.bits_per_pixel;
 	display->var = *var;
     }
     if (oldbpp != var->bits_per_pixel) {
 	if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
 	    return err;
-	do_install_cmap(con);
+	do_install_cmap(con, info);
     }
     return 0;
 }
@@ -174,7 +214,8 @@
      *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
      */
 
-static int offb_pan_display(struct fb_var_screeninfo *var, int con)
+static int offb_pan_display(struct fb_var_screeninfo *var, int con,
+			    struct fb_info *info)
 {
     if (var->xoffset || var->yoffset)
 	return -EINVAL;
@@ -186,14 +227,16 @@
      *  Get the Colormap
      */
 
-static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			 struct fb_info *info)
 {
     if (con == currcon) /* current console? */
-	return fb_get_cmap(cmap, &fb_display[con].var, kspc, offb_getcolreg);
+	return fb_get_cmap(cmap, &fb_display[con].var, kspc, offb_getcolreg,
+			   info);
     else if (fb_display[con].cmap.len) /* non default colormap? */
 	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
     else
-	fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+	fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
 		     cmap, kspc ? 0 : 2);
     return 0;
 }
@@ -202,11 +245,13 @@
      *  Set the Colormap
      */
 
-static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			 struct fb_info *info)
 {
+    struct fb_info_offb *info2 = (struct fb_info_offb *)info;
     int err;
 
-    if (!unknown_cmap_adr)
+    if (!info2->cmap_adr)
 	return -ENOSYS;
 
     if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
@@ -215,7 +260,8 @@
 	    return err;
     }
     if (con == currcon)			/* current console? */
-	return fb_set_cmap(cmap, &fb_display[con].var, kspc, offb_setcolreg);
+	return fb_set_cmap(cmap, &fb_display[con].var, kspc, offb_setcolreg,
+			   info);
     else
 	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
     return 0;
@@ -223,12 +269,24 @@
 
 
 static int offb_ioctl(struct inode *inode, struct file *file, u_int cmd,
-		      u_long arg, int con)
+		      u_long arg, int con, struct fb_info *info)
 {
     return -EINVAL;
 }
 
 
+#ifdef CONFIG_FB_ATY
+extern unsigned long atyfb_of_init(unsigned long mem_start,
+				   struct device_node *dp);
+
+static const char *aty_names[] = {
+    "ATY,mach64", "ATY,XCLAIM", "ATY,264VT", "ATY,mach64ii", "ATY,264GT-B", 
+    "ATY,mach64_3D_pcc", "ATY,XCLAIM3D", "ATY,XCLAIMVR", "ATY,RAGEII_M",
+    "ATY,XCLAIMVRPro", "ATY,mach64_3DU"
+};
+#endif /* CONFIG_FB_ATY */
+
+
     /*
      *  Initialisation
      */
@@ -236,128 +294,196 @@
 __initfunc(unsigned long offb_init(unsigned long mem_start))
 {
     struct device_node *dp;
-    int i, err, *pp, len;
+    int dpy, i, err, *pp, len;
     unsigned *up, address;
+    struct fb_fix_screeninfo *fix;
+    struct fb_var_screeninfo *var;
+    struct display *disp;
+    struct fb_info_offb *info;
+
+    for (dpy = 0; dpy < prom_num_displays; dpy++) {
+	if (!(dp = find_path_device(prom_display_paths[dpy])))
+	    continue;
+
+	info = &fb_info[dpy];
+	fix = &info->fix;
+	var = &info->var;
+	disp = &info->disp;
+
+	if (!ofonly) {
+#ifdef CONFIG_FB_ATY
+	    for (i = 0; i < sizeof(aty_names)/sizeof(*aty_names); i++)
+		if (!strcmp(dp->name, aty_names[i]))
+		    break;
+	    if (i < sizeof(aty_names)/sizeof(*aty_names)) {
+		mem_start = atyfb_of_init(mem_start, dp);
+		continue;
+	    }
+#endif /* CONFIG_FB_ATY */
+	}
 
-    if (!prom_display_path[0])
-	return mem_start;
-    if (!(dp = find_path_device(prom_display_path)))
-	return mem_start;
-
-    strncat(offb_name, dp->name, sizeof(offb_name));
-    offb_name[sizeof(offb_name)-1] = '\0';
-    strcpy(fb_fix.id, offb_name);
-
-    if ((pp = (int *)get_property(dp, "depth", &len)) != NULL
-	&& len == sizeof(int) && *pp != 8) {
-	printk("%s: can't use depth = %d\n", dp->full_name, *pp);
-	return mem_start;
-    }
-    if ((pp = (int *)get_property(dp, "width", &len)) != NULL
-	&& len == sizeof(int))
-	fb_var.xres = fb_var.xres_virtual = *pp;
-    if ((pp = (int *)get_property(dp, "height", &len)) != NULL
-	&& len == sizeof(int))
-	fb_var.yres = fb_var.yres_virtual = *pp;
-    if ((pp = (int *)get_property(dp, "linebytes", &len)) != NULL
-	&& len == sizeof(int))
-	fb_fix.line_length = *pp;
-    else
-	fb_fix.line_length = fb_var.xres_virtual;
-    fb_fix.smem_len = fb_fix.line_length*fb_var.yres;
-    if ((up = (unsigned *)get_property(dp, "address", &len)) != NULL
-	&& len == sizeof(unsigned))
-	address = (u_long)*up;
-    else {
-	for (i = 0; i < dp->n_addrs; ++i)
-	    if (dp->addrs[i].size >= len)
-		break;
-	if (i >= dp->n_addrs) {
-	    printk("no framebuffer address found for %s\n", dp->full_name);
-	    return mem_start;
+	strcpy(fix->id, "OFfb ");
+	strncat(fix->id, dp->name, sizeof(fix->id));
+	fix->id[sizeof(fix->id)-1] = '\0';
+
+	if ((pp = (int *)get_property(dp, "depth", &len)) != NULL
+	    && len == sizeof(int) && *pp != 8) {
+	    printk("%s: can't use depth = %d\n", dp->full_name, *pp);
+	    continue;
+	}
+	if ((pp = (int *)get_property(dp, "width", &len)) != NULL
+	    && len == sizeof(int))
+	    var->xres = var->xres_virtual = *pp;
+	if ((pp = (int *)get_property(dp, "height", &len)) != NULL
+	    && len == sizeof(int))
+	    var->yres = var->yres_virtual = *pp;
+	if ((pp = (int *)get_property(dp, "linebytes", &len)) != NULL
+	    && len == sizeof(int))
+	    fix->line_length = *pp;
+	else
+	    fix->line_length = var->xres_virtual;
+	fix->smem_len = fix->line_length*var->yres;
+	if ((up = (unsigned *)get_property(dp, "address", &len)) != NULL
+	    && len == sizeof(unsigned))
+	    address = (u_long)*up;
+	else {
+	    for (i = 0; i < dp->n_addrs; ++i)
+		if (dp->addrs[i].size >= len)
+		    break;
+	    if (i >= dp->n_addrs) {
+		printk("no framebuffer address found for %s\n", dp->full_name);
+		continue;
+	    }
+	    address = (u_long)dp->addrs[i].address;
+	}
+	fix->smem_start = ioremap(address, fix->smem_len);
+	fix->type = FB_TYPE_PACKED_PIXELS;
+	fix->type_aux = 0;
+
+	/* XXX kludge for ati */
+	if (strncmp(dp->name, "ATY,", 4) == 0) {
+	    info->cmap_adr = ioremap(address + 0x7ff000, 0x1000) + 0xcc0;
+	    info->cmap_data = info->cmap_adr + 1;
 	}
-	address = (u_long)dp->addrs[i].address;
-    }
-    fb_fix.smem_start = ioremap(address, fb_fix.smem_len);
-    fb_fix.type = FB_TYPE_PACKED_PIXELS;
-    fb_fix.type_aux = 0;
-
-    /* XXX kludge for ati */
-    if (strncmp(dp->name, "ATY,", 4) == 0) {
-	unknown_cmap_adr = ioremap(address + 0x7ff000, 0x1000) + 0xcc0;
-	unknown_cmap_data = unknown_cmap_adr + 1;
-    }
 
-    fb_fix.visual = unknown_cmap_adr ? FB_VISUAL_PSEUDOCOLOR :
+	fix->visual = info->cmap_adr ? FB_VISUAL_PSEUDOCOLOR :
 				       FB_VISUAL_STATIC_PSEUDOCOLOR;
 
-    fb_var.xoffset = fb_var.yoffset = 0;
-    fb_var.bits_per_pixel = 8;
-    fb_var.grayscale = 0;
-    fb_var.red.offset = fb_var.green.offset = fb_var.blue.offset = 0;
-    fb_var.red.length = fb_var.green.length = fb_var.blue.length = 8;
-    fb_var.red.msb_right = fb_var.green.msb_right = fb_var.blue.msb_right = 0;
-    fb_var.transp.offset = fb_var.transp.length = fb_var.transp.msb_right = 0;
-    fb_var.nonstd = 0;
-    fb_var.activate = 0;
-    fb_var.height = fb_var.width = -1;
-    fb_var.accel = FB_ACCEL_NONE;
-    fb_var.pixclock = 10000;
-    fb_var.left_margin = fb_var.right_margin = 16;
-    fb_var.upper_margin = fb_var.lower_margin = 16;
-    fb_var.hsync_len = fb_var.vsync_len = 8;
-    fb_var.sync = 0;
-    fb_var.vmode = FB_VMODE_NONINTERLACED;
-
-    disp.var = fb_var;
-    disp.cmap.start = 0;
-    disp.cmap.len = 0;
-    disp.cmap.red = disp.cmap.green = disp.cmap.blue = disp.cmap.transp = NULL;
-    disp.screen_base = fb_fix.smem_start;
-    disp.visual = fb_fix.visual;
-    disp.type = fb_fix.type;
-    disp.type_aux = fb_fix.type_aux;
-    disp.ypanstep = 0;
-    disp.ywrapstep = 0;
-    disp.line_length = fb_fix.line_length;
-    disp.can_soft_blank = 1;
-    disp.inverse = 0;
-
-    strcpy(fb_info.modename, "OFfb ");
-    strncat(fb_info.modename, dp->full_name, sizeof(fb_info.modename));
-    fb_info.node = -1;
-    fb_info.fbops = &offb_ops;
-    fb_info.fbvar_num = 1;
-    fb_info.fbvar = &fb_var;
-    fb_info.disp = &disp;
-    fb_info.fontname[0] = '\0';
-    fb_info.changevar = NULL;
-    fb_info.switch_con = &offbcon_switch;
-    fb_info.updatevar = &offbcon_updatevar;
-    fb_info.blank = &offbcon_blank;
-    fb_info.setcmap = &offbcon_setcmap;
-
-    err = register_framebuffer(&fb_info);
-    if (err < 0)
-	return mem_start;
+	var->xoffset = var->yoffset = 0;
+	var->bits_per_pixel = 8;
+	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;
+	var->accel = FB_ACCEL_NONE;
+	var->pixclock = 10000;
+	var->left_margin = var->right_margin = 16;
+	var->upper_margin = var->lower_margin = 16;
+	var->hsync_len = var->vsync_len = 8;
+	var->sync = 0;
+	var->vmode = FB_VMODE_NONINTERLACED;
+
+	disp->var = *var;
+	disp->cmap.start = 0;
+	disp->cmap.len = 0;
+	disp->cmap.red = disp->cmap.green = disp->cmap.blue = disp->cmap.transp = NULL;
+	disp->screen_base = fix->smem_start;
+	disp->visual = fix->visual;
+	disp->type = fix->type;
+	disp->type_aux = fix->type_aux;
+	disp->ypanstep = 0;
+	disp->ywrapstep = 0;
+	disp->line_length = fix->line_length;
+	disp->can_soft_blank = info->cmap_adr ? 1 : 0;
+	disp->inverse = 0;
+#ifdef CONFIG_FBCON_CFB8
+	disp->dispsw = &fbcon_cfb8;
+#else
+	disp->dispsw = NULL;
+#endif
+
+	strcpy(info->info.modename, "OFfb ");
+	strncat(info->info.modename, dp->full_name,
+		sizeof(info->info.modename));
+	info->info.node = -1;
+	info->info.fbops = &offb_ops;
+	info->info.disp = disp;
+	info->info.fontname[0] = '\0';
+	info->info.changevar = NULL;
+	info->info.switch_con = &offbcon_switch;
+	info->info.updatevar = &offbcon_updatevar;
+	info->info.blank = &offbcon_blank;
+
+	err = register_framebuffer(&info->info);
+	if (err < 0)
+	    continue;
+
+	for (i = 0; i < 16; i++) {
+	    int j = color_table[i];
+	    info->palette[i].red = default_red[j];
+	    info->palette[i].green = default_grn[j];
+	    info->palette[i].blue = default_blu[j];
+	}
+	offb_set_var(var, -1, &info->info);
 
-    offb_set_var(&fb_var, -1);
+	printk("fb%d: Open Firmware frame buffer device on %s\n",
+	       GET_FB_IDX(info->info.node), dp->full_name);
 
-    printk("Open Firmware frame buffer device on %s\n", dp->full_name);
+#ifdef CONFIG_FB_COMPAT_XPMAC
+	if (!console_fb_info) {
+	    display_info.height = var->yres;
+	    display_info.width = var->xres;
+	    display_info.depth = 8;
+	    display_info.pitch = fix->line_length;
+	    display_info.mode = 0;
+	    strncpy(display_info.name, dp->name, sizeof(display_info.name));
+	    display_info.fb_address = iopa((unsigned long)fix->smem_start);
+	    display_info.cmap_adr_address = 0;
+	    display_info.cmap_data_address = 0;
+	    display_info.disp_reg_address = 0;
+	    /* XXX kludge for ati */
+	    if (strncmp(dp->name, "ATY,", 4) == 0) {
+		    display_info.disp_reg_address = iopa(address + 0x7ffc00);
+		    display_info.cmap_adr_address = iopa(address + 0x7ffcc0);
+		    display_info.cmap_data_address = iopa(address + 0x7ffcc1);
+	    }
+	    console_fb_info = &info->info;
+	    console_set_cmap_ptr = offb_set_cmap;
+	}
+#endif /* CONFIG_FB_COMPAT_XPMAC) */
+    }
     return mem_start;
 }
 
 
-static int offbcon_switch(int con)
+    /*
+     *  Setup: parse used options
+     */
+
+void offb_setup(char *options, int *ints)
+{
+    if (!options || !*options)
+	return;
+
+    if (!strcmp(options, "ofonly"))
+	ofonly = 1;
+}
+
+
+static int offbcon_switch(int con, struct fb_info *info)
 {
     /* Do we have to save the colormap? */
     if (fb_display[currcon].cmap.len)
 	fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
-		    offb_getcolreg);
+		    offb_getcolreg, info);
 
     currcon = con;
     /* Install new colormap */
-    do_install_cmap(con);
+    do_install_cmap(con, info);
     return 0;
 }
 
@@ -365,7 +491,7 @@
      *  Update the `var' structure (called by fbcon.c)
      */
 
-static int offbcon_updatevar(int con)
+static int offbcon_updatevar(int con, struct fb_info *info)
 {
     /* Nothing */
     return 0;
@@ -375,34 +501,42 @@
      *  Blank the display.
      */
 
-static void offbcon_blank(int blank)
+static void offbcon_blank(int blank, struct fb_info *info)
 {
-    /* Nothing */
-}
+    struct fb_info_offb *info2 = (struct fb_info_offb *)info;
+    int i, j;
 
-    /*
-     *  Set the colormap
-     */
+    if (!info2->cmap_adr)
+	return;
 
-static int offbcon_setcmap(struct fb_cmap *cmap, int con)
-{
-    return(offb_set_cmap(cmap, 1, con));
+    if (blank)
+	for (i = 0; i < 256; i++) {
+	    *info2->cmap_adr = i;
+	    mach_eieio();
+	    for (j = 0; j < 3; j++) {
+		*info2->cmap_data = 0;
+		mach_eieio();
+	    }
+	}
+    else
+	do_install_cmap(currcon, info);
 }
 
-
     /*
      *  Read a single color register and split it into
      *  colors/transparent. Return != 0 for invalid regno.
      */
 
 static int offb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
-                         u_int *transp)
+			  u_int *transp, struct fb_info *info)
 {
-    if (!unknown_cmap_adr || regno > 255)
+    struct fb_info_offb *info2 = (struct fb_info_offb *)info;
+
+    if (!info2->cmap_adr || regno > 255)
 	return 1;
-    *red = palette[regno].red;
-    *green = palette[regno].green;
-    *blue = palette[regno].blue;
+    *red = info2->palette[regno].red;
+    *green = info2->palette[regno].green;
+    *blue = info2->palette[regno].blue;
     return 0;
 }
 
@@ -414,41 +548,113 @@
      */
 
 static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp)
+			 u_int transp, struct fb_info *info)
 {
-    if (!unknown_cmap_adr || regno > 255)
+    struct fb_info_offb *info2 = (struct fb_info_offb *)info;
+
+    if (!info2->cmap_adr || regno > 255)
 	return 1;
-    palette[regno].red = red;
-    palette[regno].green = green;
-    palette[regno].blue = blue;
-    *unknown_cmap_adr = regno;
-#ifdef __powerpc__
-    eieio();
-#endif
-    *unknown_cmap_data = red;
-#ifdef __powerpc__
-    eieio();
-#endif
-    *unknown_cmap_data = green;
-#ifdef __powerpc__
-    eieio();
-#endif
-    *unknown_cmap_data = blue;
-#ifdef __powerpc__
-    eieio();
-#endif
+    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;
+    mach_eieio();
+    *info2->cmap_data = green;
+    mach_eieio();
+    *info2->cmap_data = blue;
+    mach_eieio();
     return 0;
 }
 
 
-static void do_install_cmap(int con)
+static void do_install_cmap(int con, struct fb_info *info)
 {
     if (con != currcon)
 	return;
     if (fb_display[con].cmap.len)
 	fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
-		    offb_setcolreg);
+		    offb_setcolreg, info);
     else
-	fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
-				    &fb_display[con].var, 1, offb_setcolreg);
+	fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+				    &fb_display[con].var, 1, offb_setcolreg,
+				    info);
+}
+
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+
+    /*
+     *  Backward compatibility mode for Xpmac
+     *
+     *  To do:
+     *
+     *    - console_setmode() should fill in a struct fb_var_screeninfo (using
+     *	    the MacOS video mode database) and simply call a decode_var()
+     *	    function, so console_setmode_ptr is no longer needed.
+     *
+     *    - instead of using the console_* stuff (filled in by the frame
+     *      buffer), we should use the correct struct fb_info for the
+     *	    foreground virtual console.
+     */
+
+int console_getmode(struct vc_mode *mode)
+{
+    *mode = display_info;
+    return 0;
 }
+
+int console_setmode(struct vc_mode *mode, int doit)
+{
+    int err;
+
+    if (console_setmode_ptr == NULL)
+	return -EINVAL;
+
+    err = (*console_setmode_ptr)(mode, doit);
+    return err;
+}
+
+static u16 palette_red[16];
+static u16 palette_green[16];                                                 
+static u16 palette_blue[16];
+
+static struct fb_cmap palette_cmap = {
+    0, 16, palette_red, palette_green, palette_blue, NULL
+};
+
+int console_setcmap(int n_entries, unsigned char *red, unsigned char *green,
+		    unsigned char *blue)
+{
+    int i, j, n;
+
+    if (console_set_cmap_ptr == NULL)
+	return -EOPNOTSUPP;
+    for (i = 0; i < n_entries; i += n) {
+	n = n_entries-i;
+	if (n > 16)
+	    n = 16;
+	palette_cmap.start = i;
+	palette_cmap.len = n;
+	for (j = 0; j < n; j++) {
+	    palette_cmap.red[j]   = (red[i+j] << 8) | red[i+j];
+	    palette_cmap.green[j] = (green[i+j] << 8) | green[i+j];
+	    palette_cmap.blue[j]  = (blue[i+j] << 8) | blue[i+j];
+	}
+	(*console_set_cmap_ptr)(&palette_cmap, 1, fg_console, console_fb_info);
+    }
+    return 0;
+}
+
+int console_powermode(int mode)
+{
+    if (mode == VC_POWERMODE_INQUIRY)
+	return 0;
+    if (mode < VESA_NO_BLANKING || mode > VESA_POWERDOWN)
+	return -EINVAL;
+    /* Not supported */
+    return -ENXIO;
+}
+
+#endif /* CONFIG_FB_COMPAT_XPMAC */
diff -u --recursive --new-file v2.1.90/linux/drivers/video/retz3fb.c linux/drivers/video/retz3fb.c
--- v2.1.90/linux/drivers/video/retz3fb.c	Mon Nov 24 01:18:59 1997
+++ linux/drivers/video/retz3fb.c	Sat Mar 21 11:12:49 1998
@@ -21,6 +21,7 @@
  */
 
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -39,6 +40,10 @@
 #include <asm/pgtable.h>
 
 #include "retz3fb.h"
+#include "fbcon.h"
+#include "fbcon-cfb8.h"
+#include "fbcon-cfb16.h"
+
 
 /* #define DEBUG if(1) */
 #define DEBUG if(0)
@@ -54,7 +59,7 @@
 
 #define arraysize(x)    (sizeof(x)/sizeof(*(x)))
 
-struct retz3_fb_par {
+struct retz3fb_par {
 	int xres;
 	int yres;
 	int xres_vir;
@@ -93,7 +98,7 @@
 	long v_dispend;		/* Horizontal Display End */
 };
 
-static struct retz3_fb_par current_par;
+static struct retz3fb_par current_par;
 
 static int current_par_valid = 0;
 static int currcon = 0;
@@ -114,13 +119,15 @@
 
    /* Display Control */
 
-   int (*encode_fix)(struct fb_fix_screeninfo *fix, struct retz3_fb_par *par);
-   int (*decode_var)(struct fb_var_screeninfo *var, struct retz3_fb_par *par);
-   int (*encode_var)(struct fb_var_screeninfo *var, struct retz3_fb_par *par);
+   int (*encode_fix)(struct fb_fix_screeninfo *fix, struct retz3fb_par *par);
+   int (*decode_var)(struct fb_var_screeninfo *var, struct retz3fb_par *par);
+   int (*encode_var)(struct fb_var_screeninfo *var, struct retz3fb_par *par);
    int (*getcolreg)(unsigned int regno, unsigned int *red, unsigned
-		    int *green, unsigned int *blue, unsigned int *transp);
+		    int *green, unsigned int *blue, unsigned int *transp,
+			struct fb_info *info);
    int (*setcolreg)(unsigned int regno, unsigned int red, unsigned int
-		    green, unsigned int blue, unsigned int transp);
+		    green, unsigned int blue, unsigned int transp,
+			struct fb_info *info);
    void (*blank)(int blank);
 } *fbhw;
 
@@ -129,7 +136,7 @@
  *    Frame Buffer Name
  */
 
-static char retz3_fb_name[16] = "RetinaZ3";
+static char retz3fb_name[16] = "RetinaZ3";
 
 
 static unsigned char retz3_color_table [256][4];
@@ -140,46 +147,6 @@
 
 
 /*
- *    Predefined Video Mode Names
- */
-
-static char *retz3_fb_modenames[] = {
-
-	/*
-	 *    Autodetect (Default) Video Mode
-	 */
-
-	"default",
-
-	/*
-	 *    Predefined Video Modes
-	 */
-
-	"640x480",		/* RetinaZ3 8 bpp */
-	"800x600",		/* RetinaZ3 8 bpp */
-	"1024x768i",
-	"640x480-16",		/* RetinaZ3 16 bpp */
-	"640x480-24",		/* RetinaZ3 24 bpp */
-	
-	/*
-	 *    Dummy Video Modes
-	 */
-
-	"dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy",
-	"dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy",
-	"dummy", "dummy", "dummy", "dummy", "dummy", "dummy",
-
-	/*
-	 *    User Defined Video Modes
-	 *
-	 *    This doesn't work yet!!
-	 */
-
-	"user0", "user1", "user2", "user3",
-	"user4", "user5", "user6", "user7"
-};
-
-/*
  * A small info on how to convert XFree86 timing values into fb
  * timings - by Frank Neumann:
  *
@@ -217,131 +184,124 @@
 */
 
 /*
- *    Predefined Video Mode Definitions
+ *    Predefined Video Modes
  */
 
-static struct fb_var_screeninfo retz3_fb_predefined[] = {
-
-   /*
-    *    Autodetect (Default) Video Mode
-    */
-
-   { 0, },
-
-   /*
-    *    Predefined Video Modes
-    */
-
-   /*
-    * NB: it is very important to adjust the pixel-clock to the color-depth.
-    */
-
-   {
-	   640, 480, 640, 480, 0, 0, 8, 0,
-	   {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-	   0, 0, -1, -1, FB_ACCEL_RETINAZ3, 38461, 28, 32, 12, 10, 96, 2,
-	   FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
-   },
-   /*
-    ModeLine "800x600" 36 800 824 896 1024 600 601 603 625
-             < name > DCF HR  SH1 SH2  HFL VR  SV1 SV2 VFL
-    */
-   {
-	   /* 800 x 600, 8 bpp */
-	   800, 600, 800, 600, 0, 0, 8, 0,
-	   {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-	   0, 0, -1, -1, FB_ACCEL_RETINAZ3, 27778, 64, 24, 22, 1, 120, 2,
-	   FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
-   },
-   /*
-    ModeLine "1024x768i" 45 1024 1064 1224 1264 768 777 785 817 interlace
-              < name >   DCF HR  SH1  SH2  HFL  VR  SV1 SV2 VFL
-    */
-   {
-	   /* 1024 x 768, 8 bpp, interlaced */
-	   1024, 768, 1024, 768, 0, 0, 8, 0,
-	   {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-	   0, 0, -1, -1, FB_ACCEL_RETINAZ3, 22222, 40, 40, 32, 9, 160, 8,
-	   FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_INTERLACED
-   },
-   {
-	   640, 480, 640, 480, 0, 0, 16, 0,
-	   {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
-	   0, 0, -1, -1, FB_ACCEL_RETINAZ3, 38461/2, 28, 32, 12, 10, 96, 2,
-	   FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
-   },
-   {
-	   640, 480, 640, 480, 0, 0, 24, 0,
-	   {8, 8, 8}, {8, 8, 8}, {8, 8, 8}, {0, 0, 0}, 
-	   0, 0, -1, -1, FB_ACCEL_RETINAZ3, 38461/3, 28, 32, 12, 10, 96, 2,
-	   FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
-   },
-
-   /*
-    *    Dummy Video Modes
-    */
-
-   { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, },
-   { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, },
-   { 0, }, { 0, },
-
-   /*
-    *    User Defined Video Modes
-    */
-
-   { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }
+static struct fb_videomode retz3fb_predefined[] __initdata = {
+    /*
+     * NB: it is very important to adjust the pixel-clock to the color-depth.
+     */
+
+    {
+	"640x480", {		/* 640x480, 8 bpp */
+	    640, 480, 640, 480, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NCR77C32BLT, 38461, 28, 32, 12, 10, 96, 2,
+	    FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    },
+    /*
+     ModeLine "800x600" 36 800 824 896 1024 600 601 603 625
+	      < name > DCF HR  SH1 SH2  HFL VR  SV1 SV2 VFL
+     */
+    {
+	"800x600", {		/* 800x600, 8 bpp */
+	    800, 600, 800, 600, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NCR77C32BLT, 27778, 64, 24, 22, 1, 120, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    },
+    /*
+     ModeLine "1024x768i" 45 1024 1064 1224 1264 768 777 785 817 interlace
+	       < name >   DCF HR  SH1  SH2  HFL  VR  SV1 SV2 VFL
+     */
+    {
+	"1024x768i", {		/* 1024x768, 8 bpp, interlaced */
+	    1024, 768, 1024, 768, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NCR77C32BLT, 22222, 40, 40, 32, 9, 160, 8,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_INTERLACED
+	}
+    }, {
+	"640x480-16", {		/* 640x480, 16 bpp */
+	    640, 480, 640, 480, 0, 0, 16, 0,
+	    {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NCR77C32BLT, 38461/2, 28, 32, 12, 10, 96, 2,
+	    FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }, {
+	"640x480-24", {		/* 640x480, 24 bpp */
+	    640, 480, 640, 480, 0, 0, 24, 0,
+	    {8, 8, 8}, {8, 8, 8}, {8, 8, 8}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NCR77C32BLT, 38461/3, 28, 32, 12, 10, 96, 2,
+	    FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    },
 };
 
 
-#define NUM_TOTAL_MODES    arraysize(retz3_fb_predefined)
-#define NUM_PREDEF_MODES   5
+#define NUM_TOTAL_MODES    arraysize(retz3fb_predefined)
 
+static struct fb_var_screeninfo retz3fb_default;
 
 static int z3fb_inverse = 0;
-static int z3fb_mode = 0;
+static int z3fb_mode __initdata = 0;
 
 
 /*
  *    Interface used by the world
  */
 
-void retz3_video_setup(char *options, int *ints);
+void retz3fb_setup(char *options, int *ints);
 
-static int retz3_fb_open(int fbidx);
-static int retz3_fb_release(int fbidx);
-static int retz3_fb_get_fix(struct fb_fix_screeninfo *fix, int con);
-static int retz3_fb_get_var(struct fb_var_screeninfo *var, int con);
-static int retz3_fb_set_var(struct fb_var_screeninfo *var, int con);
-static int retz3_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con);
-static int retz3_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con);
-static int retz3_fb_pan_display(struct fb_var_screeninfo *var, int con);
-static int retz3_fb_ioctl(struct inode *inode, struct file *file,
-			  unsigned int cmd, unsigned long arg, int con);
+static int retz3fb_open(struct fb_info *info);
+static int retz3fb_release(struct fb_info *info);
+static int retz3fb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			   struct fb_info *info);
+static int retz3fb_get_var(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info);
+static int retz3fb_set_var(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info);
+static int retz3fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info);
+static int retz3fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info);
+static int retz3fb_pan_display(struct fb_var_screeninfo *var, int con,
+			       struct fb_info *info);
+static int retz3fb_ioctl(struct inode *inode, struct file *file,
+			 unsigned int cmd, unsigned long arg, int con,
+			 struct fb_info *info);
 
 
 /*
  *    Interface to the low level console driver
  */
 
-unsigned long retz3_fb_init(unsigned long mem_start);
-static int z3fb_switch(int con);
-static int z3fb_updatevar(int con);
-static void z3fb_blank(int blank);
-static int z3fb_setcmap(struct fb_cmap *cmap, int con);
+unsigned long retz3fb_init(unsigned long mem_start);
+static int z3fb_switch(int con, struct fb_info *info);
+static int z3fb_updatevar(int con, struct fb_info *info);
+static void z3fb_blank(int blank, struct fb_info *info);
+
+
+/*
+ *    Text console acceleration
+ */
+
+#ifdef CONFIG_FBCON_CFB8
+static struct display_switch fbcon_retz3_8;
+#endif
 
 
 /*
  *    Accelerated Functions used by the low level console driver
  */
 
-void retz3_bitblt(struct fb_var_screeninfo *scr,
-		  unsigned short curx, unsigned short cury, unsigned
-		  short destx, unsigned short desty, unsigned short
-		  width, unsigned short height, unsigned short cmd,
-		  unsigned short mask);
-void retz3_fill(unsigned short x, unsigned short y, unsigned short
-		width, unsigned short height, unsigned short mode,
-		unsigned short color);
+static void retz3_bitblt(struct fb_var_screeninfo *scr,
+			 unsigned short curx, unsigned short cury, unsigned
+			 short destx, unsigned short desty, unsigned short
+			 width, unsigned short height, unsigned short cmd,
+			 unsigned short mask);
 
 /*
  *   Hardware Specific Routines
@@ -349,17 +309,17 @@
 
 static int retz3_init(void);
 static int retz3_encode_fix(struct fb_fix_screeninfo *fix,
-                          struct retz3_fb_par *par);
+                          struct retz3fb_par *par);
 static int retz3_decode_var(struct fb_var_screeninfo *var,
-                          struct retz3_fb_par *par);
+                          struct retz3fb_par *par);
 static int retz3_encode_var(struct fb_var_screeninfo *var,
-                          struct retz3_fb_par *par);
+                          struct retz3fb_par *par);
 static int retz3_getcolreg(unsigned int regno, unsigned int *red,
 			   unsigned int *green, unsigned int *blue,
-			   unsigned int *transp);
+			   unsigned int *transp, struct fb_info *info);
 static int retz3_setcolreg(unsigned int regno, unsigned int red,
 			   unsigned int green, unsigned int blue,
-			   unsigned int transp);
+			   unsigned int transp, struct fb_info *info);
 static void retz3_blank(int blank);
 
 
@@ -367,13 +327,11 @@
  *    Internal routines
  */
 
-static void retz3_fb_get_par(struct retz3_fb_par *par);
-static void retz3_fb_set_par(struct retz3_fb_par *par);
+static void retz3fb_get_par(struct retz3fb_par *par);
+static void retz3fb_set_par(struct retz3fb_par *par);
 static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive);
-static void do_install_cmap(int con);
-/*
-static void retz3_fb_set_disp(int con);
-*/
+static void do_install_cmap(int con, struct fb_info *info);
+static void retz3fb_set_disp(int con, struct fb_info *info);
 static int get_video_mode(const char *name);
 
 
@@ -397,7 +355,7 @@
 	else if (freq <= 250000000)
 		n2 = 0;
 	else
-		return(0);
+		return 0;
 
 
 	do {
@@ -424,10 +382,12 @@
 
 
 static int retz3_set_video(struct fb_var_screeninfo *var,
-			   struct retz3_fb_par *par)
+			   struct retz3fb_par *par)
 {
+#if 0
 	float freq_f;
-	long freq;
+#endif
+	unsigned int freq;
 
 	int xres, hfront, hsync, hback;
 	int yres, vfront, vsync, vback;
@@ -478,7 +438,7 @@
 		vback  = var->upper_margin;
 	}
 
-	data.h_total	= (hback / 8) + (xres / 8) 
+	data.h_total	= (hback / 8) + (xres / 8)
 			+ (hfront / 8) + (hsync / 8) - 1 /* + 1 */;
 	data.h_dispend	= ((xres + bpp - 1)/ 8) - 1;
 	data.h_bstart	= xres / 8 /* + 1 */;
@@ -736,8 +696,13 @@
 	/*
 	 * Convert from ps to Hz.
 	 */
+#if 0
 	freq_f = (1.0/(float)var->pixclock) * 1000000000;
-	freq = ((long)freq_f) * 1000;
+	freq = ((unsigned int)freq_f) * 1000;
+#else
+	freq = 2000000000 / var->pixclock;
+	freq = freq * 500;
+#endif
 
 	best_freq = find_fq(freq);
 	pll_w(0x02, best_freq);
@@ -791,7 +756,7 @@
 	 */
 	switch (bpp){
 	case 8:
-		reg_w(0x83c6, 0x00); 
+		reg_w(0x83c6, 0x00);
 		break;
 	case 16:
 		reg_w(0x83c6, 0x60);
@@ -805,7 +770,7 @@
 
 	reg_w(VDAC_ADDRESS, 0x00);
 
-	seq_w(SEQ_MAP_MASK, 0x0f );  
+	seq_w(SEQ_MAP_MASK, 0x0f );
 
 	return 0;
 }
@@ -854,8 +819,8 @@
 	}
 #endif
 
-	retz3_setcolreg (255, 56, 100, 160, 0);
-	retz3_setcolreg (254, 0, 0, 0, 0);
+	retz3_setcolreg (255, 56, 100, 160, 0, NULL /* unused */);
+	retz3_setcolreg (254, 0, 0, 0, 0, NULL /* unused */);
 
 	return 0;
 }
@@ -867,11 +832,11 @@
  */
 
 static int retz3_encode_fix(struct fb_fix_screeninfo *fix,
-			    struct retz3_fb_par *par)
+			    struct retz3fb_par *par)
 {
 	short i;
 
-	strcpy(fix->id, retz3_fb_name);
+	strcpy(fix->id, retz3fb_name);
 	fix->smem_start = (char *)z3_fbmem;
 	fix->smem_len = z3_size;
 	fix->mmio_start = (unsigned char *)z3_regs;
@@ -889,6 +854,8 @@
 	fix->ywrapstep = 0;
 	fix->line_length = 0;
 
+	fix->accel = FB_ACCEL_NCR77C32BLT;
+
 	for (i = 0; i < arraysize(fix->reserved); i++)
 		fix->reserved[i] = 0;
 
@@ -902,7 +869,7 @@
  */
 
 static int retz3_decode_var(struct fb_var_screeninfo *var,
-			    struct retz3_fb_par *par)
+			    struct retz3fb_par *par)
 {
 	par->xres = var->xres;
 	par->yres = var->yres;
@@ -934,7 +901,7 @@
  */
 
 static int retz3_encode_var(struct fb_var_screeninfo *var,
-			    struct retz3_fb_par *par)
+			    struct retz3fb_par *par)
 {
 	short i;
 
@@ -959,7 +926,7 @@
 	var->height = -1;
 	var->width = -1;
 
-	var->accel = FB_ACCEL_RETINAZ3;
+	var->accel = FB_ACCEL_NCR77C32BLT;
 
 	var->pixclock = par->pixclock;
 
@@ -987,7 +954,7 @@
 
 static int retz3_setcolreg(unsigned int regno, unsigned int red,
 			   unsigned int green, unsigned int blue,
-			   unsigned int transp)
+			   unsigned int transp, struct fb_info *info)
 {
 	/* We'll get to this */
 
@@ -1015,7 +982,7 @@
 
 static int retz3_getcolreg(unsigned int regno, unsigned int *red,
 			   unsigned int *green, unsigned int *blue,
-			   unsigned int *transp)
+			   unsigned int *transp, struct fb_info *info)
 {
 	if (regno > 255)
 		return 1;
@@ -1052,11 +1019,11 @@
 }
 
 
-void retz3_bitblt (struct fb_var_screeninfo *var,
-		   unsigned short srcx, unsigned short srcy, unsigned
-		   short destx, unsigned short desty, unsigned short
-		   width, unsigned short height, unsigned short cmd,
-		   unsigned short mask)
+static void retz3_bitblt (struct fb_var_screeninfo *var,
+			  unsigned short srcx, unsigned short srcy,
+			  unsigned short destx, unsigned short desty,
+			  unsigned short width, unsigned short height,
+			  unsigned short cmd, unsigned short mask)
 {
 
 	volatile unsigned long *acm = (unsigned long *) (z3_mem + ACM_OFFSET);
@@ -1129,7 +1096,7 @@
 	*(acm + ACM_CONTROL/4) = tmp;
 
 	tmp  = width | (height << 16);
-	
+
 	*(acm + ACM_BITMAP_DIMENSION/4) = cpu_to_le32(tmp);
 
 	*(((volatile unsigned char *)acm) + ACM_START_STATUS) = 0x00;
@@ -1154,20 +1121,10 @@
 }
 
 #if 0
-void retz3_fill (unsigned short x, unsigned short y, unsigned
-		 short width, unsigned short height,
-		 unsigned short mode, unsigned short color)
-{
-
-}
-#endif
-
-
-#if 0
 /*
  * Move cursor to x, y
  */
-void retz3_MoveCursor (unsigned short x, unsigned short y)
+static void retz3_MoveCursor (unsigned short x, unsigned short y)
 {
 	/* Guess we gotta deal with the cursor at some point */
 }
@@ -1189,16 +1146,16 @@
  *    Fill the hardware's `par' structure.
  */
 
-static void retz3_fb_get_par(struct retz3_fb_par *par)
+static void retz3fb_get_par(struct retz3fb_par *par)
 {
 	if (current_par_valid)
 		*par = current_par;
 	else
-		fbhw->decode_var(&retz3_fb_predefined[z3fb_mode], par);
+		fbhw->decode_var(&retz3fb_default, par);
 }
 
 
-static void retz3_fb_set_par(struct retz3_fb_par *par)
+static void retz3fb_set_par(struct retz3fb_par *par)
 {
 	current_par = *par;
 	current_par_valid = 1;
@@ -1208,7 +1165,7 @@
 static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
 {
 	int err, activate;
-	struct retz3_fb_par par;
+	struct retz3fb_par par;
 
 	if ((err = fbhw->decode_var(var, &par)))
 		return err;
@@ -1217,7 +1174,7 @@
 	/* XXX ... what to do about isactive ? */
 
 	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive)
-		retz3_fb_set_par(&par);
+		retz3fb_set_par(&par);
 	fbhw->encode_var(var, &par);
 	var->activate = activate;
 
@@ -1227,17 +1184,17 @@
 }
 
 
-static void do_install_cmap(int con)
+static void do_install_cmap(int con, struct fb_info *info)
 {
 	if (con != currcon)
 		return;
 	if (fb_display[con].cmap.len)
 		fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
-			    fbhw->setcolreg);
+			    fbhw->setcolreg, info);
 	else
-		fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
 					    &fb_display[con].var, 1,
-					    fbhw->setcolreg);
+					    fbhw->setcolreg, info);
 }
 
 
@@ -1245,7 +1202,7 @@
  *    Open/Release the frame buffer device
  */
 
-static int retz3_fb_open(int fbidx)
+static int retz3fb_open(struct fb_info *info)
 {
 	/*
 	 * Nothing, only a usage count for the moment
@@ -1255,7 +1212,7 @@
 	return 0;
 }
 
-static int retz3_fb_release(int fbidx)
+static int retz3fb_release(struct fb_info *info)
 {
 	MOD_DEC_USE_COUNT;
 	return 0;
@@ -1266,13 +1223,14 @@
  *    Get the Fixed Part of the Display
  */
 
-static int retz3_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
+static int retz3fb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			   struct fb_info *info)
 {
-	struct retz3_fb_par par;
+	struct retz3fb_par par;
 	int error = 0;
 
 	if (con == -1)
-		retz3_fb_get_par(&par);
+		retz3fb_get_par(&par);
 	else
 		error = fbhw->decode_var(&fb_display[con].var, &par);
 	return(error ? error : fbhw->encode_fix(fix, &par));
@@ -1283,13 +1241,14 @@
  *    Get the User Defined Part of the Display
  */
 
-static int retz3_fb_get_var(struct fb_var_screeninfo *var, int con)
+static int retz3fb_get_var(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info)
 {
-	struct retz3_fb_par par;
+	struct retz3fb_par par;
 	int error = 0;
 
 	if (con == -1) {
-		retz3_fb_get_par(&par);
+		retz3fb_get_par(&par);
 		error = fbhw->encode_var(var, &par);
 	} else
 		*var = fb_display[con].var;
@@ -1298,7 +1257,7 @@
 
 
 #if 1
-static void retz3_fb_set_disp(int con)
+static void retz3fb_set_disp(int con, struct fb_info *info)
 {
 	struct fb_fix_screeninfo fix;
 	struct display *display;
@@ -1308,7 +1267,7 @@
 	else
 		display = &disp;	/* used during initialization */
 
-	retz3_fb_get_fix(&fix, con);
+	retz3fb_get_fix(&fix, con, info);
 
 	if (con == -1)
 		con = 0;
@@ -1321,6 +1280,21 @@
 	display->ywrapstep = fix.ywrapstep;
 	display->can_soft_blank = 1;
 	display->inverse = z3fb_inverse;
+	switch (display->var.bits_per_pixel) {
+#ifdef CONFIG_FBCON_CFB8
+	case 8:
+		display->dispsw = &fbcon_retz3_8;
+		break;
+#endif
+#ifdef CONFIG_FBCON_CFB16
+	case 16:
+		display->dispsw = &fbcon_cfb16;
+		break;
+#endif
+	default:
+		display->dispsw = NULL;
+		break;
+	}
 }
 #endif
 
@@ -1328,7 +1302,8 @@
  *    Set the User Defined Part of the Display
  */
 
-static int retz3_fb_set_var(struct fb_var_screeninfo *var, int con)
+static int retz3fb_set_var(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info)
 {
 	int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp;
 	struct display *display;
@@ -1359,7 +1334,7 @@
 		    oldbpp != var->bits_per_pixel) {
 
 			struct fb_fix_screeninfo fix;
-			retz3_fb_get_fix(&fix, con);
+			retz3fb_get_fix(&fix, con, info);
 
 			display->screen_base = fix.smem_start;
 			display->visual = fix.visual;
@@ -1370,8 +1345,23 @@
 			display->line_length = fix.line_length;
 			display->can_soft_blank = 1;
 			display->inverse = z3fb_inverse;
+			switch (display->var.bits_per_pixel) {
+#ifdef CONFIG_FBCON_CFB8
+			case 8:
+				display->dispsw = &fbcon_retz3_8;
+				break;
+#endif
+#ifdef CONFIG_FBCON_CFB16
+			case 16:
+				display->dispsw = &fbcon_cfb16;
+				break;
+#endif
+			default:
+				display->dispsw = NULL;
+				break;
+			}
 /*
-	retz3_fb_set_disp(con);
+	retz3fb_set_disp(con, info);
 */
 			if (fb_info.changevar)
 				(*fb_info.changevar)(con);
@@ -1380,7 +1370,7 @@
 		if (oldbpp != var->bits_per_pixel) {
 			if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
 				return err;
-			do_install_cmap(con);
+			do_install_cmap(con, info);
 		}
 	}
 	return 0;
@@ -1391,15 +1381,16 @@
  *    Get the Colormap
  */
 
-static int retz3_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+static int retz3fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info)
 {
 	if (con == currcon) /* current console? */
 		return(fb_get_cmap(cmap, &fb_display[con].var, kspc,
-				   fbhw->getcolreg));
+				   fbhw->getcolreg, info));
 	else if (fb_display[con].cmap.len) /* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
 	else
-		fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+		fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
 			     cmap, kspc ? 0 : 2);
 	return 0;
 }
@@ -1409,7 +1400,8 @@
  *    Set the Colormap
  */
 
-static int retz3_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+static int retz3fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info)
 {
 	int err;
 
@@ -1421,7 +1413,7 @@
 	}
 	if (con == currcon)              /* current console? */
 		return(fb_set_cmap(cmap, &fb_display[con].var, kspc,
-				   fbhw->setcolreg));
+				   fbhw->setcolreg, info));
 	else
 		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
 	return 0;
@@ -1434,7 +1426,8 @@
  *    This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
  */
 
-static int retz3_fb_pan_display(struct fb_var_screeninfo *var, int con)
+static int retz3fb_pan_display(struct fb_var_screeninfo *var, int con,
+				struct fb_info *info)
 {
 	return -EINVAL;
 }
@@ -1444,21 +1437,22 @@
  *    RetinaZ3 Frame Buffer Specific ioctls
  */
 
-static int retz3_fb_ioctl(struct inode *inode, struct file *file,
-                          unsigned int cmd, unsigned long arg, int con)
+static int retz3fb_ioctl(struct inode *inode, struct file *file,
+                         unsigned int cmd, unsigned long arg, int con,
+                         struct fb_info *info)
 {
 	return -EINVAL;
 }
 
 
-static struct fb_ops retz3_fb_ops = {
-	retz3_fb_open, retz3_fb_release, retz3_fb_get_fix, retz3_fb_get_var,
-	retz3_fb_set_var, retz3_fb_get_cmap, retz3_fb_set_cmap,
-	retz3_fb_pan_display, retz3_fb_ioctl
+static struct fb_ops retz3fb_ops = {
+	retz3fb_open, retz3fb_release, retz3fb_get_fix, retz3fb_get_var,
+	retz3fb_set_var, retz3fb_get_cmap, retz3fb_set_cmap,
+	retz3fb_pan_display, NULL, retz3fb_ioctl
 };
 
 
-__initfunc(void retz3_video_setup(char *options, int *ints))
+__initfunc(void retz3fb_setup(char *options, int *ints))
 {
 	char *this_opt;
 
@@ -1467,7 +1461,7 @@
 	if (!options || !*options)
 		return;
 
-	for (this_opt = strtok(options, ","); this_opt; 
+	for (this_opt = strtok(options, ","); this_opt;
 	     this_opt = strtok(NULL, ",")){
 		if (!strcmp(this_opt, "inverse")) {
 			z3fb_inverse = 1;
@@ -1484,14 +1478,14 @@
  *    Initialization
  */
 
-__initfunc(unsigned long retz3_fb_init(unsigned long mem_start))
+__initfunc(unsigned long retz3fb_init(unsigned long mem_start))
 {
 	int err;
 	unsigned long board_addr, board_size;
 	unsigned int key;
 	const struct ConfigDev *cd;
 
-	struct retz3_fb_par par;
+	struct retz3fb_par par;
 
 	if (!(key = zorro_find(ZORRO_PROD_MACROSYSTEMS_RETINA_Z3, 0, 0)))
 		return mem_start;
@@ -1511,43 +1505,38 @@
 
 	z3_size = 0x00400000; /* 4 MB */
 
-	memset ((char*)z3_fbmem, 0, z3_size);
-
 	fbhw = &retz3_switch;
 
 	fbhw->init();
 
-	strcpy(fb_info.modename, retz3_fb_name);
+	strcpy(fb_info.modename, retz3fb_name);
 	fb_info.changevar = NULL;
 	fb_info.node = -1;
-	fb_info.fbops = &retz3_fb_ops;
-	fb_info.fbvar_num = NUM_TOTAL_MODES;
-	fb_info.fbvar = retz3_fb_predefined;
+	fb_info.fbops = &retz3fb_ops;
 	fb_info.disp = &disp;
 	fb_info.switch_con = &z3fb_switch;
 	fb_info.updatevar = &z3fb_updatevar;
 	fb_info.blank = &z3fb_blank;
-	fb_info.setcmap = &z3fb_setcmap;
 
 	err = register_framebuffer(&fb_info);
 	if (err < 0)
 		return mem_start;
 
 	if (z3fb_mode == -1)
-		z3fb_mode = 1;
+		retz3fb_default = retz3fb_predefined[0].var;
 
-	fbhw->decode_var(&retz3_fb_predefined[z3fb_mode], &par);
-	fbhw->encode_var(&retz3_fb_predefined[0], &par);
+	fbhw->decode_var(&retz3fb_default, &par);
+	fbhw->encode_var(&retz3fb_default, &par);
 
-	do_fb_set_var(&retz3_fb_predefined[0], 0);
-	retz3_fb_get_var(&disp.var, -1);
+	do_fb_set_var(&retz3fb_default, 0);
+	retz3fb_get_var(&disp.var, -1, &fb_info);
 
-	retz3_fb_set_disp(-1);
+	retz3fb_set_disp(-1, &fb_info);
 
-	do_install_cmap(0);
+	do_install_cmap(0, &fb_info);
 
-	printk("%s frame buffer device, using %ldK of video memory\n",
-	       fb_info.modename, z3_size>>10);
+	printk("fb%d: %s frame buffer device, using %ldK of video memory\n",
+	       GET_FB_IDX(fb_info.node), fb_info.modename, z3_size>>10);
 
 	/* TODO: This driver cannot be unloaded yet */
 	MOD_INC_USE_COUNT;
@@ -1556,17 +1545,18 @@
 }
 
 
-static int z3fb_switch(int con)
+static int z3fb_switch(int con, struct fb_info *info)
 {
 	/* Do we have to save the colormap? */
 	if (fb_display[currcon].cmap.len)
 		fb_get_cmap(&fb_display[currcon].cmap,
-			    &fb_display[currcon].var, 1, fbhw->getcolreg);
+			    &fb_display[currcon].var, 1, fbhw->getcolreg,
+			    info);
 
 	do_fb_set_var(&fb_display[con].var, 1);
 	currcon = con;
 	/* Install new colormap */
-	do_install_cmap(con);
+	do_install_cmap(con, info);
 	return 0;
 }
 
@@ -1578,7 +1568,7 @@
  *    Since it's called by a kernel driver, no range checking is done.
  */
 
-static int z3fb_updatevar(int con)
+static int z3fb_updatevar(int con, struct fb_info *info)
 {
 	return 0;
 }
@@ -1588,33 +1578,23 @@
  *    Blank the display.
  */
 
-static void z3fb_blank(int blank)
+static void z3fb_blank(int blank, struct fb_info *info)
 {
 	fbhw->blank(blank);
 }
 
 
 /*
- *    Set the colormap
- */
-
-static int z3fb_setcmap(struct fb_cmap *cmap, int con)
-{
-	return(retz3_fb_set_cmap(cmap, 1, con));
-}
-
-
-/*
  *    Get a Video Mode
  */
 
-static int get_video_mode(const char *name)
+__initfunc(static int get_video_mode(const char *name))
 {
 	short i;
 
-	for (i = 1; i <= NUM_PREDEF_MODES; i++)
-		if (!strcmp(name, retz3_fb_modenames[i])){
-			retz3_fb_predefined[0] = retz3_fb_predefined[i];
+	for (i = 0; i <= NUM_TOTAL_MODES; i++)
+		if (!strcmp(name, retz3fb_predefined[i].name)){
+			retz3fb_default = retz3fb_predefined[i].var;
 			return i;
 		}
 	return -1;
@@ -1624,7 +1604,7 @@
 #ifdef MODULE
 int init_module(void)
 {
-	return(retz3_fb_init(NULL));
+	return(retz3fb_init(NULL));
 }
 
 void cleanup_module(void)
@@ -1636,11 +1616,60 @@
 	unregister_framebuffer(&fb_info);
 	/* TODO: clean up ... */
 }
-#endif /* MODULE */
+#endif
 
 
 /*
- *  Visible symbols for modules
+ *  Text console acceleration
  */
 
-EXPORT_SYMBOL(retz3_bitblt);
+#ifdef CONFIG_FBCON_CFB8
+static void fbcon_retz3_8_bmove(struct display *p, int sy, int sx, int dy, int dx,
+	                int height, int width)
+{
+	int fontwidth = p->fontwidth;
+
+	sx *= fontwidth;
+	dx *= fontwidth;
+	width *= fontwidth;
+
+	retz3_bitblt(&p->var,
+		     (unsigned short)sx,
+		     (unsigned short)(sy*p->fontheight),
+		     (unsigned short)dx,
+		     (unsigned short)(dy*p->fontheight),
+		     (unsigned short)width,
+		     (unsigned short)(height*p->fontheight),
+		     Z3BLTcopy,
+		     0xffff);
+}
+
+static void fbcon_retz3_8_clear(struct vc_data *conp, struct display *p, int
+			sy, int sx, int height, int width)
+{
+	unsigned short col;
+	int fontwidth = p->fontwidth;
+
+	sx *= fontwidth;
+	width *= fontwidth;
+
+	col = attr_bgcol_ec(p, conp);
+	col &= 0xff;
+	col |= (col << 8);
+
+	retz3_bitblt(&p->var,
+		     (unsigned short)sx,
+		     (unsigned short)(sy*p->fontheight),
+		     (unsigned short)sx,
+		     (unsigned short)(sy*p->fontheight),
+		     (unsigned short)width,
+		     (unsigned short)(height*p->fontheight),
+		     Z3BLTset,
+		     col);
+}
+
+static struct display_switch fbcon_retz3_8 = {
+    fbcon_cfb8_setup, fbcon_retz3_8_bmove, fbcon_retz3_8_clear,
+    fbcon_cfb8_putc, fbcon_cfb8_putcs, fbcon_cfb8_revc
+};
+#endif
diff -u --recursive --new-file v2.1.90/linux/drivers/video/skeletonfb.c linux/drivers/video/skeletonfb.c
--- v2.1.90/linux/drivers/video/skeletonfb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/skeletonfb.c	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,388 @@
+/*
+ * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device
+ *
+ *  Created 28 Dec 1997 by Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file README.legal in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+
+
+    /*
+     *  This is just simple sample code.
+     *
+     *  No warranty that it actually compiles.
+     *  Even less warranty that it actually works :-)
+     */
+
+
+struct xxxfb_info {
+    /*
+     *  Choose _one_ of the two alternatives:
+     *
+     *    1. Use the generic frame buffer operations (fbgen_*).
+     */
+    struct fb_info_gen gen;
+    /*
+     *    2. Provide your own frame buffer operations.
+     */
+    struct fb_info info;
+
+    /* Here starts the frame buffer device dependent part */
+    /* You can use this to store e.g. the board number if you support */
+    /* multiple boards */
+};
+
+
+struct xxxfb_par {
+    /*
+     *  The hardware specific data in this structure uniquely defines a video
+     *  mode.
+     *
+     *  If your hardware supports only one video mode, you can leave it empty.
+     */
+};
+
+
+    /*
+     *  If your driver supports multiple boards, you should make these arrays,
+     *  or allocate them dynamically (using mem_start for builtin drivers, and
+     *  kmalloc() for loaded modules).
+     */
+
+static struct xxxfb_info fb_info;
+static struct xxxfb_par current_par;
+static int current_par_valid = 0;
+static struct display disp;
+
+static struct fb_var_screeninfo default_var;
+
+static int currcon = 0;
+static int inverse = 0;
+
+
+/* ------------------- chipset specific functions -------------------------- */
+
+
+static void xxx_detect(void)
+{
+    /*
+     *  This function should detect the current video mode settings and store
+     *  it as the default video mode
+     */
+
+    /* ... */
+    xxx_get_par(&par);
+    xxx_encode_var(&default_var, &par);
+}
+
+static int xxx_encode_fix(struct fb_fix_screeninfo *fix, struct xxxfb_par *par,
+			  const struct fb_info *fb_info)
+{
+    /*
+     *  This function should fill in the 'fix' structure based on the values
+     *  in the `par' structure.
+     */
+
+    /* ... */
+    return 0;
+}
+
+static int xxx_decode_var(struct fb_var_screeninfo *var, struct xxxfb_par *par,
+			  const struct fb_info *fb_info)
+{
+    /*
+     *  Get the video params out of 'var'. If a value doesn't fit, round it up,
+     *  if it's too big, return -EINVAL.
+     *
+     *  Suggestion: Round up in the following order: bits_per_pixel, xres,
+     *  yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
+     *  bitfields, horizontal timing, vertical timing.
+     */
+
+    /* ... */
+
+    /* pixclock in picos, htotal in pixels, vtotal in scanlines */
+    if (!fbmon_valid_timings(pixclock, htotal, vtotal, info))
+	    return -EINVAL;
+
+    return 0;
+}
+
+static int xxx_encode_var(struct fb_var_screeninfo *var, struct xxxfb_par *par,
+			  const struct fb_info *fb_info)
+{
+    /*
+     *  Fill the 'var' structure based on the values in 'par' and maybe other
+     *  values read out of the hardware.
+     */
+
+    /* ... */
+    return 0;
+}
+
+static void xxx_get_par(struct xxxfb_par *par, const struct fb_info *fb_info)
+{
+    /*
+     *  Fill the hardware's 'par' structure.
+     */
+
+    if (current_par_valid)
+	*par = current_par;
+    else {
+	/* ... */
+    }
+}
+
+static void xxx_set_par(struct xxxfb_par *par, const struct fb_info *fb_info)
+{
+    /*
+     *  Set the hardware according to 'par'.
+     */
+
+    current_par = *par;
+    current_par_valid = 1;
+    /* ... */
+}
+
+static int xxx_getcolreg(unsigned regno, unsigned *red, unsigned *green,
+			 unsigned *blue, unsigned *transp,
+			 const struct fb_info *fb_info)
+{
+    /*
+     *  Read a single color register and split it into colors/transparent.
+     *  Return != 0 for invalid regno.
+     */
+
+    /* ... */
+    return 0;
+}
+
+static int xxx_setcolreg(unsigned regno, unsigned red, unsigned green,
+			 unsigned blue, unsigned transp,
+			 const struct fb_info *fb_info)
+{
+    /*
+     *  Set a single color register. The values supplied are already rounded
+     *  down to the hardware's capabilities (according to the entries in the
+     *  `var' structure). Return != 0 for invalid regno.
+     */
+
+    if (regno < 16) {
+	/*
+	 *  Make the first 16 colors of the palette available to fbcon
+	 */
+	if (is_cfb15)		/* RGB 555 */
+	    fbcon_cfb15_cmap[regno] = be16_to_cpu((red << 10) | (green << 5) |
+						  blue);
+	if (is_cfb16)		/* RGB 565 */
+	    fbcon_cfb16_cmap[regno] = be16_to_cpu((red << 11) | (green << 5) |
+						  blue);
+	if (is_cfb24)		/* RGB 888 */
+	    fbcon_cfb24_cmap[regno] = be32_to_cpu((red << 16) | (green << 8) |
+						  blue);
+	if (is_cfb32)		/* RGBA 8888 */
+	    fbcon_cfb32_cmap[regno] = be32_to_cpu((red << 24) | (green << 16) |
+						  (blue << 8) | transp);
+    }
+    /* ... */
+    return 0;
+}
+
+static int xxx_pan_display(struct fb_var_screeninfo *var,
+			   struct xxxfb_par *par,
+			   const struct fb_info *fb_info)
+{
+    /*
+     *  Pan (or wrap, depending on the `vmode' field) the display using the
+     *  `xoffset' and `yoffset' fields of the `var' structure.
+     *  If the values don't fit, return -EINVAL.
+     */
+
+    /* ... */
+    return 0;
+}
+
+static int xxx_blank(int blank_mode, const struct fb_info *fb_info)
+{
+    /*
+     *  Blank the screen if blank_mode != 0, else unblank. If blank == NULL
+     *  then the caller blanks by setting the CLUT (Color Look Up Table) to all
+     *  black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
+     *  to e.g. a video mode which doesn't support it. Implements VESA suspend
+     *  and powerdown modes on hardware that supports disabling hsync/vsync:
+     *    blank_mode == 2: suspend vsync
+     *    blank_mode == 3: suspend hsync
+     *    blank_mode == 4: powerdown
+     */
+
+    /* ... */
+    return 0;
+}
+
+static struct display_switch *xxx_get_dispsw(const void *par,
+					     struct fb_info_gen *info)
+{
+    /*
+     *  Return a pointer to appropriate low level text console operations for
+     *  the video mode `par' of your video hardware. These can be generic
+     *  software routines, or hardware accelerated routines specifically
+     *  tailored for your hardware.
+     *  If you don't have any appropriate operations, simple fill in the NULL
+     *  pointer, and there will be no text output.
+     */
+#ifdef CONFIG_FBCON_CFB8
+    if (is_cfb8)
+	return &fbcon_cfb8;
+#endif
+#ifdef CONFIG_FBCON_CFB16
+    if (is_cfb16)
+	return &fbcon_cfb16;
+#endif
+#ifdef CONFIG_FBCON_CFB32
+    if (is_cfb32)
+	return &fbcon_cfb32;
+#endif
+    return NULL;
+}
+
+
+/* ------------ Interfaces to hardware functions ------------ */
+
+
+struct fbgen_hwswitch xxx_switch = {
+    xxx_detect, xxx_encode_fix, xxx_decode_var, xxx_encode_var, xxx_get_par,
+    xxx_set_par, xxx_getcolreg, xxx_setcolreg, xxx_blank, xxx_dispsw
+};
+
+
+
+/* ------------ Hardware Independant Functions ------------ */
+
+
+    /*
+     *  Initialization
+     */
+
+__initfunc(unsigned long xxxfb_init(unsigned long mem_start))
+{
+    int err;
+    struct fb_var_screeninfo var;
+
+    fb_info.fbhw = &xxx_switch;
+    fbhw->detect();
+    strcpy(fb_info.modename, "XXX");
+    fb_info.changevar = NULL;
+    fb_info.node = -1;
+    fb_info.fbops = &xxxfb_ops;
+    fb_info.disp = disp;
+    fb_info.switch_con = &xxxfb_switch;
+    fb_info.updatevar = &xxxfb_update_var;
+    fb_info.blank = &xxxfb_blank;
+    /* This should give a reasonable default video mode */
+    fbgen_get_var(&disp.var, -1, &fb_info.gen);
+    fbgen_do_set_var(var, 1, &fbinfo.gen);
+    err = register_framebuffer(&fb_info.gen.info);
+    if (err < 0)
+	return err;
+    fbgen_set_disp(-1, &fb_info.gen.info);
+    fbgen_install_cmap(0, &fb_info.gen);
+    printk("fb%d: %s frame buffer device\n", GET_FB_IDX(fb_info.node),
+	   fb_info.modename);
+
+    /* uncomment this if your driver cannot be unloaded */
+    /* MOD_INC_USE_COUNT; */
+
+    return mem_start;
+}
+
+
+    /*
+     *  Cleanup
+     */
+
+void xxxfb_cleanup(struct fb_info *info)
+{
+    /*
+     *  If your driver supports multiple boards, you should unregister and
+     *  clean up all instances.
+     */
+
+    unregister_framebuffer(&fb_info);
+    /* ... */
+}
+
+
+    /*
+     *  Setup
+     */
+
+__initfunc(void xxxfb_setup(char *options, int *ints))
+{
+    /* Parse user speficied options (`video=xxxfb:') */
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+
+    /*
+     *  Frame buffer operations
+     */
+
+static int xxxfb_open(const struct fb_info *info)
+{
+    /* Nothing, only a usage count for the moment */
+    MOD_INC_USE_COUNT;
+    return 0;
+}
+
+static int xxxfb_release(const struct fb_info *info)
+{
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+
+
+    /*
+     *  In most cases the `generic' routines (fbgen_*) should be satisfactory.
+     *  However, you're free to fill in your own replacements.
+     */
+
+static struct fb_ops xxxfb_ops = {
+    xxxfb_open, xxxfb_release, fbgen_get_fix, fbgen_get_var, fbgen_set_var,
+    fbgen_get_cmap, fbgen_set_cmap, fbgen_pan_display, NULL, fbgen_ioctl
+};
+
+
+/* ------------------------------------------------------------------------- */
+
+
+    /*
+     *  Modularization
+     */
+
+#ifdef MODULE
+int init_module(void)
+{
+    return xxxfb_init(NULL);
+}
+
+void cleanup_module(void)
+{
+    xxxfb_cleanup(void);
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.90/linux/drivers/video/tgafb.c linux/drivers/video/tgafb.c
--- v2.1.90/linux/drivers/video/tgafb.c	Mon Nov 24 01:18:59 1997
+++ linux/drivers/video/tgafb.c	Sat Mar 21 11:12:49 1998
@@ -17,13 +17,12 @@
  *
  *	- How to set a single color register?
  *
- *	- We don't have support for CFB32 yet (fbcon-cfb32.c)
- *
  *	- Hardware cursor (useful for other graphics boards too)
  *
  * KNOWN PROBLEMS/TO DO ==================================================== */
 
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -37,11 +36,13 @@
 #include <linux/interrupt.h>
 #include <linux/fb.h>
 #include <linux/init.h>
-#include <linux/bios32.h>
 #include <linux/pci.h>
 #include <linux/selection.h>
 #include <asm/io.h>
 
+#include "fbcon-cfb8.h"
+#include "fbcon-cfb32.h"
+
 
 /* TGA hardware description (minimal) */
 /*
@@ -188,10 +189,17 @@
 unsigned char PLLbits[7] __initdata = { 0x80, 0x04, 0x00, 0x24, 0x44, 0x80, 0xb8 };
 
 const unsigned long bt485_cursor_source[64] __initdata = {
+#if 1
+  0x0000000000000000,0x0000000000000000,0x0000000000000000,0x0000000000000000,
+  0x0000000000000000,0x0000000000000000,0x0000000000000000,0x0000000000000000,
+  0x0000000000000000,0x0000000000000000,0x0000000000000000,0x0000000000000000,
+  0x0000000000000000,0x0000000000000000,0x0000000000000000,0x0000000000000000,
+#else
   0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,
   0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,
   0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,
   0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,
+#endif
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
@@ -235,9 +243,8 @@
 static struct display disp;
 static struct fb_info fb_info;
 static struct { u_char red, green, blue, pad; } palette[256];
-static char tgafb_name[16] = "DEC TGA ";
 
-static struct fb_fix_screeninfo fb_fix;
+static struct fb_fix_screeninfo fb_fix = { { "DEC TGA ", } };
 static struct fb_var_screeninfo fb_var = { 0, };
 
 
@@ -245,18 +252,22 @@
      *  Interface used by the world
      */
 
-void tgafb_video_setup(char *options, int *ints);
-
-static int tgafb_open(int fbidx);
-static int tgafb_release(int fbidx);
-static int tgafb_get_fix(struct fb_fix_screeninfo *fix, int con);
-static int tgafb_get_var(struct fb_var_screeninfo *var, int con);
-static int tgafb_set_var(struct fb_var_screeninfo *var, int con);
-static int tgafb_pan_display(struct fb_var_screeninfo *var, int con);
-static int tgafb_get_cmap(struct fb_cmap *cmap, int kspc, int con);
-static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int tgafb_open(struct fb_info *info);
+static int tgafb_release(struct fb_info *info);
+static int tgafb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			 struct fb_info *info);
+static int tgafb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info);
+static int tgafb_set_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info);
+static int tgafb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info);
+static int tgafb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info);
+static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info);
 static int tgafb_ioctl(struct inode *inode, struct file *file, u_int cmd,
-		       u_long arg, int con);
+		       u_long arg, int con, struct fb_info *info);
 
 
     /*
@@ -264,10 +275,9 @@
      */
 
 unsigned long tgafb_init(unsigned long mem_start);
-static int tgafbcon_switch(int con);
-static int tgafbcon_updatevar(int con);
-static void tgafbcon_blank(int blank);
-static int tgafbcon_setcmap(struct fb_cmap *cmap, int con);
+static int tgafbcon_switch(int con, struct fb_info *info);
+static int tgafbcon_updatevar(int con, struct fb_info *info);
+static void tgafbcon_blank(int blank, struct fb_info *info);
 
 
     /*
@@ -275,18 +285,18 @@
      */
 
 static int tgafb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
-			   u_int *transp);
+			   u_int *transp, struct fb_info *info);
 static int tgafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			   u_int transp);
+			   u_int transp, struct fb_info *info);
 #if 1
 static void tga_update_palette(void);
 #endif
-static void do_install_cmap(int con);
+static void do_install_cmap(int con, struct fb_info *info);
 
 
 static struct fb_ops tgafb_ops = {
     tgafb_open, tgafb_release, tgafb_get_fix, tgafb_get_var, tgafb_set_var,
-    tgafb_get_cmap, tgafb_set_cmap, tgafb_pan_display, tgafb_ioctl
+    tgafb_get_cmap, tgafb_set_cmap, tgafb_pan_display, NULL, tgafb_ioctl
 };
 
 
@@ -294,7 +304,7 @@
      *  Open/Release the frame buffer device
      */
 
-static int tgafb_open(int fbidx)                                       
+static int tgafb_open(struct fb_info *info)                                       
 {
     /*                                                                     
      *  Nothing, only a usage count for the moment                          
@@ -304,7 +314,7 @@
     return(0);                              
 }
         
-static int tgafb_release(int fbidx)
+static int tgafb_release(struct fb_info *info)
 {
     MOD_DEC_USE_COUNT;
     return(0);                                                    
@@ -315,7 +325,8 @@
      *  Get the Fixed Part of the Display
      */
 
-static int tgafb_get_fix(struct fb_fix_screeninfo *fix, int con)
+static int tgafb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			 struct fb_info *info)
 {
     memcpy(fix, &fb_fix, sizeof(fb_fix));
     return 0;
@@ -326,7 +337,8 @@
      *  Get the User Defined Part of the Display
      */
 
-static int tgafb_get_var(struct fb_var_screeninfo *var, int con)
+static int tgafb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info)
 {
     memcpy(var, &fb_var, sizeof(fb_var));
     return 0;
@@ -337,7 +349,8 @@
      *  Set the User Defined Part of the Display
      */
 
-static int tgafb_set_var(struct fb_var_screeninfo *var, int con)
+static int tgafb_set_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info)
 {
     struct display *display;
     int oldbpp = -1, err;
@@ -363,7 +376,7 @@
     if (oldbpp != var->bits_per_pixel) {
 	if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
 	    return err;
-	do_install_cmap(con);
+	do_install_cmap(con, info);
     }
     return 0;
 }
@@ -375,7 +388,8 @@
      *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
      */
 
-static int tgafb_pan_display(struct fb_var_screeninfo *var, int con)
+static int tgafb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info)
 {
     if (var->xoffset || var->yoffset)
 	return -EINVAL;
@@ -387,14 +401,16 @@
      *  Get the Colormap
      */
 
-static int tgafb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+static int tgafb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info)
 {
     if (con == currcon) /* current console? */
-	return fb_get_cmap(cmap, &fb_display[con].var, kspc, tgafb_getcolreg);
+	return fb_get_cmap(cmap, &fb_display[con].var, kspc, tgafb_getcolreg,
+			   info);
     else if (fb_display[con].cmap.len) /* non default colormap? */
 	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
     else
-	fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+	fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
 		     cmap, kspc ? 0 : 2);
     return 0;
 }
@@ -403,7 +419,8 @@
      *  Set the Colormap
      */
 
-static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info)
 {
     int err;
 
@@ -413,7 +430,8 @@
 	    return err;
     }
     if (con == currcon) {		/* current console? */
-	err = fb_set_cmap(cmap, &fb_display[con].var, kspc, tgafb_setcolreg);
+	err = fb_set_cmap(cmap, &fb_display[con].var, kspc, tgafb_setcolreg,
+			  info);
 #if 1
 	tga_update_palette();
 #endif
@@ -425,7 +443,7 @@
 
 
 static int tgafb_ioctl(struct inode *inode, struct file *file, u_int cmd,
-		       u_long arg, int con)
+		       u_long arg, int con, struct fb_info *info)
 {
     return -EINVAL;
 }
@@ -437,22 +455,14 @@
 
 __initfunc(unsigned long tgafb_init(unsigned long mem_start))
 {
-    unsigned char pci_bus, pci_devfn;
-    int status;
     int i, j, temp, err;
     unsigned char *cbp;
+    struct pci_dev *pdev;
 
-    status = pcibios_find_device (PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA,
-				  0, &pci_bus, &pci_devfn);
-    if (status == PCIBIOS_DEVICE_NOT_FOUND)
+    pdev = pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, NULL);
+    if (!pdev)
 	return mem_start;
-
-    /*
-     * read BASE_REG_0 for memory address
-     */
-    pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_0,
-			      &tga_mem_base);
-    tga_mem_base &= ~15;
+    tga_mem_base = pdev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK;
 #ifdef DEBUG
     printk("tgafb_init: mem_base 0x%x\n", tga_mem_base);
 #endif /* DEBUG */
@@ -460,19 +470,18 @@
     tga_type = (readl((unsigned long)tga_mem_base) >> 12) & 0x0f;
     switch (tga_type) {
 	case 0:
-	    strcat(tgafb_name, "8plane");
+	    strcat(fb_fix.id, "8plane");
 	    break;
 	case 1:
-	    strcat(tgafb_name, "24plane");
+	    strcat(fb_fix.id, "24plane");
 	    break;
 	case 3:
-	    strcat(tgafb_name, "24plusZ");
+	    strcat(fb_fix.id, "24plusZ");
 	    break;
 	default:
 	    printk("TGA type (0x%x) unrecognized!\n", tga_type);
 	    return mem_start;
     }
-    strcpy(fb_fix.id, tgafb_name);
 
     tga_regs_base = ((unsigned long)tga_mem_base + TGA_REGS_OFFSET);
     tga_fb_base = ((unsigned long)tga_mem_base + fb_offset_presets[tga_type]);
@@ -537,6 +546,9 @@
 	    TGA_WRITE_REG(default_red[j]|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG);
 	    TGA_WRITE_REG(default_grn[j]|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG);
 	    TGA_WRITE_REG(default_blu[j]|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG);
+	    palette[i].red=default_red[j];
+	    palette[i].green=default_grn[j];
+	    palette[i].blue=default_blu[j];
 	}
 	for (i = 0; i < 240*3; i += 4) {
 	    TGA_WRITE_REG(0x55|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG);
@@ -548,9 +560,9 @@
 	/* initialize RAMDAC cursor colors */
 	BT485_WRITE(0, BT485_ADDR_CUR_WRITE);
 
-	BT485_WRITE(0xaa, BT485_DATA_CUR); /* overscan WHITE */
-	BT485_WRITE(0xaa, BT485_DATA_CUR); /* overscan WHITE */
-	BT485_WRITE(0xaa, BT485_DATA_CUR); /* overscan WHITE */
+	BT485_WRITE(0x00, BT485_DATA_CUR); /* overscan WHITE */
+	BT485_WRITE(0x00, BT485_DATA_CUR); /* overscan WHITE */
+	BT485_WRITE(0x00, BT485_DATA_CUR); /* overscan WHITE */
 
 	BT485_WRITE(0x00, BT485_DATA_CUR); /* color 1 BLACK */
 	BT485_WRITE(0x00, BT485_DATA_CUR); /* color 1 BLACK */
@@ -677,7 +689,7 @@
     fb_var.xres = fb_var.xres_virtual = 640;
     fb_var.yres = fb_var.yres_virtual = 480;
     fb_fix.line_length = 80*fb_var.bits_per_pixel;
-    fb_fix.smem_start = (char *)tga_fb_base;
+    fb_fix.smem_start = (char *)(tga_fb_base + LCA_DENSE_MEM);
     fb_fix.smem_len = fb_fix.line_length*fb_var.yres;
     fb_fix.type = FB_TYPE_PACKED_PIXELS;
     fb_fix.type_aux = 0;
@@ -686,7 +698,16 @@
 
     fb_var.xoffset = fb_var.yoffset = 0;
     fb_var.grayscale = 0;
-    fb_var.red.offset = fb_var.green.offset = fb_var.blue.offset = 0;
+    if (tga_type == 0) { /* 8-plane */
+	fb_var.red.offset = 0;
+	fb_var.green.offset = 0;
+	fb_var.blue.offset = 0;
+    } else { /* 24-plane or 24plusZ */
+	/* XXX: is this correct?? */
+	fb_var.red.offset = 16;
+	fb_var.green.offset = 8;
+	fb_var.blue.offset = 0;
+    }
     fb_var.red.length = fb_var.green.length = fb_var.blue.length = 8;
     fb_var.red.msb_right = fb_var.green.msb_right = fb_var.blue.msb_right = 0;
     fb_var.transp.offset = fb_var.transp.length = fb_var.transp.msb_right = 0;
@@ -717,41 +738,54 @@
     disp.line_length = fb_fix.line_length;
     disp.can_soft_blank = 1;
     disp.inverse = 0;
+    switch (tga_type) {
+#ifdef CONFIG_FBCON_CFB8
+	case 0: /* 8-plane */
+	    disp.dispsw = &fbcon_cfb8;
+	    break;
+#endif
+#ifdef CONFIG_FBCON_CFB32
+	case 1: /* 24-plane */
+	case 3: /* 24plusZ */
+	    disp.dispsw = &fbcon_cfb32;
+	    break;
+#endif
+	default:
+	    disp.dispsw = NULL;
+    }
 
-    strcpy(fb_info.modename, tgafb_name);
+    strcpy(fb_info.modename, fb_fix.id);
     fb_info.node = -1;
     fb_info.fbops = &tgafb_ops;
-    fb_info.fbvar_num = 1;
-    fb_info.fbvar = &fb_var;
     fb_info.disp = &disp;
     fb_info.fontname[0] = '\0';
     fb_info.changevar = NULL;
     fb_info.switch_con = &tgafbcon_switch;
     fb_info.updatevar = &tgafbcon_updatevar;
     fb_info.blank = &tgafbcon_blank;
-    fb_info.setcmap = &tgafbcon_setcmap;
 
     err = register_framebuffer(&fb_info);
     if (err < 0)
 	return mem_start;
 
-    tgafb_set_var(&fb_var, -1);
+    tgafb_set_var(&fb_var, -1, &fb_info);
 
-    printk("%s frame buffer device\n", tgafb_name);
+    printk("fb%d: %s frame buffer device\n", GET_FB_IDX(fb_info.node),
+	   fb_fix.id);
     return mem_start;
 }
 
 
-static int tgafbcon_switch(int con)
+static int tgafbcon_switch(int con, struct fb_info *info)
 {
     /* Do we have to save the colormap? */
     if (fb_display[currcon].cmap.len)
 	fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
-		    tgafb_getcolreg);
+		    tgafb_getcolreg, info);
 
     currcon = con;
     /* Install new colormap */
-    do_install_cmap(con);
+    do_install_cmap(con, info);
     return 0;
 }
 
@@ -759,7 +793,7 @@
      *  Update the `var' structure (called by fbcon.c)
      */
 
-static int tgafbcon_updatevar(int con)
+static int tgafbcon_updatevar(int con, struct fb_info *info)
 {
     /* Nothing */
     return 0;
@@ -769,28 +803,18 @@
      *  Blank the display.
      */
 
-static void tgafbcon_blank(int blank)
+static void tgafbcon_blank(int blank, struct fb_info *info)
 {
     /* Nothing */
 }
 
     /*
-     *  Set the colormap
-     */
-
-static int tgafbcon_setcmap(struct fb_cmap *cmap, int con)
-{
-    return(tgafb_set_cmap(cmap, 1, con));
-}
-
-
-    /*
      *  Read a single color register and split it into
      *  colors/transparent. Return != 0 for invalid regno.
      */
 
 static int tgafb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
-                         u_int *transp)
+                         u_int *transp, struct fb_info *info)
 {
     if (regno > 255)
 	return 1;
@@ -808,7 +832,7 @@
      */
 
 static int tgafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp)
+                         u_int transp, struct fb_info *info)
 {
     if (regno > 255)
 	return 1;
@@ -816,6 +840,11 @@
     palette[regno].green = green;
     palette[regno].blue = blue;
 
+#ifdef CONFIG_FBCON_CFB32
+    if (regno < 16 && tga_type != 0)
+	fbcon_cfb32_cmap[regno] = (red << 16) | (green << 8) | blue;
+#endif /* CONFIG_FBCON_CFB32 */
+
     /* How to set a single color register?? */
 
     return 0;
@@ -853,21 +882,21 @@
 }
 #endif
 
-static void do_install_cmap(int con)
+static void do_install_cmap(int con, struct fb_info *info)
 {
     if (con != currcon)
 	return;
     if (fb_display[con].cmap.len)
 	fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
-		    tgafb_setcolreg);
+		    tgafb_setcolreg, info);
     else
-	fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
-				    &fb_display[con].var, 1, tgafb_setcolreg);
+	fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+				    &fb_display[con].var, 1, tgafb_setcolreg,
+				    info);
 #if 1
     tga_update_palette();
 #endif
 }
-
 
 #if 0	/* No cursor stuff yet */
 
diff -u --recursive --new-file v2.1.90/linux/drivers/video/txtcon.c linux/drivers/video/txtcon.c
--- v2.1.90/linux/drivers/video/txtcon.c	Mon Nov 24 01:19:01 1997
+++ linux/drivers/video/txtcon.c	Sat Mar 21 11:12:49 1998
@@ -14,48 +14,55 @@
  */
 
 
+#include <linux/errno.h>
 #include <linux/types.h>
+#include <linux/kdev_t.h>
 #include <linux/console.h>
 
 
-   /*
-    *    Interface used by the world
-    */
+    /*
+     *  Interface used by the world
+     */
 
-static int txtcon_startup(u_long *kmem_start, const char **display_desc);
+static unsigned long txtcon_startup(unsigned long kmem_start,
+				    const char **display_desc);
 static void txtcon_init(struct vc_data *conp);
-static int txtcon_deinit(struct vc_data *conp);
-static int txtcon_clear(struct vc_data *conp, int sy, int sx, int height,
-                        int width);
-static int txtcon_putc(struct vc_data *conp, int c, int y, int x);
-static int txtcon_putcs(struct vc_data *conp, const char *s, int count, int y,
-                        int x);
-static int txtcon_cursor(struct vc_data *conp, int mode);
-static int txtcon_scroll(struct vc_data *conp, int t, int b, int dir, int count);
-static int txtcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
-                        int height, int width);
+static void txtcon_deinit(struct vc_data *conp);
+static void txtcon_clear(struct vc_data *conp, int sy, int sx, int height,
+			 int width);
+static void txtcon_putc(struct vc_data *conp, int c, int y, int x);
+static void txtcon_putcs(struct vc_data *conp, const char *s, int count, int y,
+			 int x);
+static void txtcon_cursor(struct vc_data *conp, int mode);
+static void txtcon_scroll(struct vc_data *conp, int t, int b, int dir,
+			  int count);
+static void txtcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+			 int height, int width);
 static int txtcon_switch(struct vc_data *conp);
 static int txtcon_blank(int blank);
 static int txtcon_get_font(struct vc_data *conp, int *w, int *h, char *data);
 static int txtcon_set_font(struct vc_data *conp, int w, int h, char *data);
 static int txtcon_set_palette(struct vc_data *conp, unsigned char *table);
 static int txtcon_scrolldelta(int lines);
+static int txtcon_set_mode(struct vc_data *conp, int mode);
 
 
-static int txtcon_startup(u_long *kmem_start, const char **display_desc)
+static unsigned long txtcon_startup(unsigned long kmem_start,
+				    const char **display_desc)
 {
-   return -ENODEV;
+    return kmem_start;
 }
 
 
 static void txtcon_init(struct vc_data *conp)
 {
+    /* ... */
 }
 
 
-static int txtcon_deinit(struct vc_data *conp)
+static void txtcon_deinit(struct vc_data *conp)
 {
-   return 0;
+    /* ... */
 }
 
 
@@ -64,90 +71,108 @@
 /* txtcon_XXX routines - interface used by the world */
 
 
-static int txtcon_clear(struct vc_data *conp, int sy, int sx, int height,
-                        int width)
+static void txtcon_clear(struct vc_data *conp, int sy, int sx, int height,
+			 int width)
 {
-   return -ENOSYS;
+    /* ... */
 }
 
 
-static int txtcon_putc(struct vc_data *conp, int c, int y, int x)
+static void txtcon_putc(struct vc_data *conp, int c, int y, int x)
 {
-   return -ENOSYS;
+    /* ... */
 }
 
 
-static int txtcon_putcs(struct vc_data *conp, const char *s, int count, int y,
-                        int x)
+static void txtcon_putcs(struct vc_data *conp, const char *s, int count, int y,
+			 int x)
 {
-   return -ENOSYS;
+    /* ... */
 }
 
 
-static int txtcon_cursor(struct vc_data *conp, int mode)
+static void txtcon_cursor(struct vc_data *conp, int mode)
 {
-   return -ENOSYS;
+    /* ... */
 }
 
 
-static int txtcon_scroll(struct vc_data *conp, int t, int b, int dir, int count)
+static void txtcon_scroll(struct vc_data *conp, int t, int b, int dir,
+			  int count)
 {
-   return -ENOSYS;
+    /* ... */
 }
 
 
-static int txtcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
-                        int height, int width)
+static void txtcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+			 int height, int width)
 {
-   return -ENOSYS;
+    /* ... */
 }
 
 
 static int txtcon_switch(struct vc_data *conp)
 {
-   return -ENOSYS;
+    return -ENOSYS;
 }
 
 
 static int txtcon_blank(int blank)
 {
-   return -ENOSYS;
+    return -ENOSYS;
 }
 
 
 static int txtcon_get_font(struct vc_data *conp, int *w, int *h, char *data)
 {
-   return -ENOSYS;
+    return -ENOSYS;
 }
 
 
 static int txtcon_set_font(struct vc_data *conp, int w, int h, char *data)
 {
-   return -ENOSYS;
+    return -ENOSYS;
 }
 
 
 static int txtcon_set_palette(struct vc_data *conp, unsigned char *table)
 {
-   return -ENOSYS;
+    return -ENOSYS;
 }
 
 
 static int txtcon_scrolldelta(int lines)
 {
-   return -ENOSYS;
+    return -ENOSYS;
+}
+
+static int txtcon_set_mode(struct vc_data *conp, int mode)
+{
+    return -ENOSYS;
 }
 
 
 /* ====================================================================== */
 
-   /*
-    *    The console `switch' structure for the text mode based console
-    */
+    /*
+     *  The console `switch' structure for the text mode based console
+     */
 
 struct consw txt_con = {
-   txtcon_startup, txtcon_init, txtcon_deinit, txtcon_clear, txtcon_putc,
-   txtcon_putcs, txtcon_cursor, txtcon_scroll, txtcon_bmove, txtcon_switch,
-   txtcon_blank, txtcon_get_font, txtcon_set_font, txtcon_set_palette,
-   txtcon_scrolldelta
+    txtcon_startup,
+    txtcon_init,
+    txtcon_deinit,
+    txtcon_clear,
+    txtcon_putc,
+    txtcon_putcs,
+    txtcon_cursor,
+    txtcon_scroll,
+    txtcon_bmove,
+    txtcon_switch,
+    txtcon_blank,
+    txtcon_get_font,
+    txtcon_set_font,
+    txtcon_set_palette,
+    txtcon_scrolldelta,
+    txtcon_set_mode
 };
diff -u --recursive --new-file v2.1.90/linux/drivers/video/vfb.c linux/drivers/video/vfb.c
--- v2.1.90/linux/drivers/video/vfb.c	Mon Nov 24 01:19:01 1997
+++ linux/drivers/video/vfb.c	Sat Mar 21 11:12:49 1998
@@ -8,6 +8,7 @@
  *  more details.
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -22,6 +23,14 @@
 #include <linux/fb.h>
 #include <linux/init.h>
 
+#include "fbcon-mfb.h"
+#include "fbcon-cfb2.h"
+#include "fbcon-cfb4.h"
+#include "fbcon-cfb8.h"
+#include "fbcon-cfb16.h"
+#include "fbcon-cfb24.h"
+#include "fbcon-cfb32.h"
+
 
 #define arraysize(x)	(sizeof(x)/sizeof(*(x)))
 
@@ -41,61 +50,51 @@
 static struct display disp;
 static struct fb_info fb_info;
 static struct { u_char red, green, blue, pad; } palette[256];
-static char virtual_fb_name[16] = "Virtual FB";
-
-static struct fb_var_screeninfo virtual_fb_predefined[] = {
+static char vfb_name[16] = "Virtual FB";
 
-    /*
-     *  Autodetect (Default) Video Mode
-     */
-
-    {
-	/* 640x480, 8 bpp */
-	640, 480, 640, 480, 0, 0, 8, 0,
-	{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-	0, 0, -1, -1, FB_ACCEL_NONE, 20000, 64, 64, 32, 32, 64, 2,
-	0, FB_VMODE_NONINTERLACED
-    },
-
-    /*
-     *  User Defined Video Modes (8)
-     */
-
-    { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }
+static struct fb_var_screeninfo vfb_default = {
+    /* 640x480, 8 bpp */
+    640, 480, 640, 480, 0, 0, 8, 0,
+    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+    0, 0, -1, -1, FB_ACCEL_NONE, 20000, 64, 64, 32, 32, 64, 2,
+    0, FB_VMODE_NONINTERLACED
 };
 
-#define NUM_USER_MODES		(8)
-#define NUM_TOTAL_MODES		arraysize(virtual_fb_predefined)
-#define NUM_PREDEF_MODES	(1)
+static int vfb_enable = 0;	/* disabled by default */
 
 
     /*
      *  Interface used by the world
      */
 
-void vfb_video_setup(char *options, int *ints);
+void vfb_setup(char *options, int *ints);
 
-static int virtual_fb_open(int fbidx);
-static int virtual_fb_release(int fbidx);
-static int virtual_fb_get_fix(struct fb_fix_screeninfo *fix, int con);
-static int virtual_fb_get_var(struct fb_var_screeninfo *var, int con);
-static int virtual_fb_set_var(struct fb_var_screeninfo *var, int con);
-static int virtual_fb_pan_display(struct fb_var_screeninfo *var, int con);
-static int virtual_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con);
-static int virtual_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con);
-static int virtual_fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
-			    u_long arg, int con);
+static int vfb_open(struct fb_info *info);
+static int vfb_release(struct fb_info *info);
+static int vfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+		       struct fb_info *info);
+static int vfb_get_var(struct fb_var_screeninfo *var, int con,
+		       struct fb_info *info);
+static int vfb_set_var(struct fb_var_screeninfo *var, int con,
+		       struct fb_info *info);
+static int vfb_pan_display(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info);
+static int vfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			struct fb_info *info);
+static int vfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			struct fb_info *info);
+static int vfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		     u_long arg, int con, struct fb_info *info);
 
 
     /*
      *  Interface to the low level console driver
      */
 
-unsigned long virtual_fb_init(unsigned long mem_start);
-static int vfbcon_switch(int con);
-static int vfbcon_updatevar(int con);
-static void vfbcon_blank(int blank);
-static int vfbcon_setcmap(struct fb_cmap *cmap, int con);
+unsigned long vfb_init(unsigned long mem_start);
+static int vfbcon_switch(int con, struct fb_info *info);
+static int vfbcon_updatevar(int con, struct fb_info *info);
+static void vfbcon_blank(int blank, struct fb_info *info);
 
 
     /*
@@ -107,16 +106,15 @@
 			   struct fb_var_screeninfo *var);
 static void set_color_bitfields(struct fb_var_screeninfo *var);
 static int vfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
-                         u_int *transp);
+                         u_int *transp, struct fb_info *info);
 static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp);
-static void do_install_cmap(int con);
+                         u_int transp, struct fb_info *info);
+static void do_install_cmap(int con, struct fb_info *info);
 
 
-static struct fb_ops virtual_fb_ops = {
-    virtual_fb_open, virtual_fb_release, virtual_fb_get_fix,
-    virtual_fb_get_var, virtual_fb_set_var, virtual_fb_get_cmap,
-    virtual_fb_set_cmap, virtual_fb_pan_display, virtual_fb_ioctl
+static struct fb_ops vfb_ops = {
+    vfb_open, vfb_release, vfb_get_fix, vfb_get_var, vfb_set_var, vfb_get_cmap,
+    vfb_set_cmap, vfb_pan_display, NULL, vfb_ioctl
 };
 
 
@@ -124,7 +122,7 @@
      *  Open/Release the frame buffer device
      */
 
-static int virtual_fb_open(int fbidx)                                       
+static int vfb_open(struct fb_info *info)                                       
 {
     /*                                                                     
      *  Nothing, only a usage count for the moment                          
@@ -134,7 +132,7 @@
     return(0);                              
 }
         
-static int virtual_fb_release(int fbidx)
+static int vfb_release(struct fb_info *info)
 {
     MOD_DEC_USE_COUNT;
     return(0);                                                    
@@ -145,12 +143,13 @@
      *  Get the Fixed Part of the Display
      */
 
-static int virtual_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
+static int vfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+		       struct fb_info *info)
 {
     struct fb_var_screeninfo *var;
 
     if (con == -1)
-	var = &virtual_fb_predefined[0];
+	var = &vfb_default;
     else
 	var = &fb_display[con].var;
     vfb_encode_fix(fix, var);
@@ -162,10 +161,11 @@
      *  Get the User Defined Part of the Display
      */
 
-static int virtual_fb_get_var(struct fb_var_screeninfo *var, int con)
+static int vfb_get_var(struct fb_var_screeninfo *var, int con,
+		       struct fb_info *info)
 {
     if (con == -1)
-	*var = virtual_fb_predefined[0];
+	*var = vfb_default;
     else
 	*var = fb_display[con].var;
     set_color_bitfields(var);
@@ -177,7 +177,8 @@
      *  Set the User Defined Part of the Display
      */
 
-static int virtual_fb_set_var(struct fb_var_screeninfo *var, int con)
+static int vfb_set_var(struct fb_var_screeninfo *var, int con,
+		       struct fb_info *info)
 {
     int err, activate = var->activate;
     int oldxres, oldyres, oldvxres, oldvyres, oldbpp;
@@ -258,13 +259,53 @@
 	    display->line_length = fix.line_length;
 	    display->can_soft_blank = 1;
 	    display->inverse = 0;
+	    switch (var->bits_per_pixel) {
+#ifdef CONFIG_FBCON_MFB
+		case 1:
+		    display->dispsw = &fbcon_mfb;
+		    break;
+#endif
+#ifdef CONFIG_FBCON_CFB2
+		case 2:
+		    display->dispsw = &fbcon_cfb2;
+		    break;
+#endif
+#ifdef CONFIG_FBCON_CFB4
+		case 4:
+		    display->dispsw = &fbcon_cfb4;
+		    break;
+#endif
+#ifdef CONFIG_FBCON_CFB8
+		case 8:
+		    display->dispsw = &fbcon_cfb8;
+		    break;
+#endif
+#ifdef CONFIG_FBCON_CFB16
+		case 16:
+		    display->dispsw = &fbcon_cfb16;
+		    break;
+#endif
+#ifdef CONFIG_FBCON_CFB24
+		case 24:
+		    display->dispsw = &fbcon_cfb24;
+		    break;
+#endif
+#ifdef CONFIG_FBCON_CFB32
+		case 32:
+		    display->dispsw = &fbcon_cfb32;
+		    break;
+#endif
+		default:
+		    display->dispsw = NULL;
+		    break;
+	    }
 	    if (fb_info.changevar)
 		(*fb_info.changevar)(con);
 	}
 	if (oldbpp != var->bits_per_pixel) {
 	    if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
 		return err;
-	    do_install_cmap(con);
+	    do_install_cmap(con, info);
 	}
     }
     return 0;
@@ -277,7 +318,8 @@
      *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
      */
 
-static int virtual_fb_pan_display(struct fb_var_screeninfo *var, int con)
+static int vfb_pan_display(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info)
 {
     if (var->vmode & FB_VMODE_YWRAP) {
 	if (var->yoffset < 0 ||
@@ -304,14 +346,16 @@
      *  Get the Colormap
      */
 
-static int virtual_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+static int vfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			struct fb_info *info)
 {
     if (con == currcon) /* current console? */
-	return fb_get_cmap(cmap, &fb_display[con].var, kspc, vfb_getcolreg);
+	return fb_get_cmap(cmap, &fb_display[con].var, kspc, vfb_getcolreg,
+			   info);
     else if (fb_display[con].cmap.len) /* non default colormap? */
 	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
     else
-	fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+	fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
 		     cmap, kspc ? 0 : 2);
     return 0;
 }
@@ -320,7 +364,8 @@
      *  Set the Colormap
      */
 
-static int virtual_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+static int vfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			struct fb_info *info)
 {
     int err;
 
@@ -330,7 +375,8 @@
 	    return err;
     }
     if (con == currcon)			/* current console? */
-	return fb_set_cmap(cmap, &fb_display[con].var, kspc, vfb_setcolreg);
+	return fb_set_cmap(cmap, &fb_display[con].var, kspc, vfb_setcolreg,
+			   info);
     else
 	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
     return 0;
@@ -341,19 +387,21 @@
      *  Virtual Frame Buffer Specific ioctls
      */
 
-static int virtual_fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
-			    u_long arg, int con)
+static int vfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		     u_long arg, int con, struct fb_info *info)
 {
     return -EINVAL;
 }
 
 
-__initfunc(void vfb_video_setup(char *options, int *ints))
+__initfunc(void vfb_setup(char *options, int *ints))
 {
     char *this_opt;
 
     fb_info.fontname[0] = '\0';
 
+    vfb_enable = 1;
+
     if (!options || !*options)
 	return;
 
@@ -369,10 +417,13 @@
      *  Initialisation
      */
 
-__initfunc(unsigned long virtual_fb_init(unsigned long mem_start))
+__initfunc(unsigned long vfb_init(unsigned long mem_start))
 {
     int err;
 
+    if (!vfb_enable)
+	return mem_start;
+
     if (mem_start) {
 	videomemory = mem_start;
 	mem_start += videomemorysize;
@@ -382,40 +433,37 @@
     if (!videomemory)
 	return mem_start;
 
-    strcpy(fb_info.modename, virtual_fb_name);
+    strcpy(fb_info.modename, vfb_name);
     fb_info.changevar = NULL;
     fb_info.node = -1;
-    fb_info.fbops = &virtual_fb_ops;
-    fb_info.fbvar_num = NUM_TOTAL_MODES;
-    fb_info.fbvar = virtual_fb_predefined;
+    fb_info.fbops = &vfb_ops;
     fb_info.disp = &disp;
     fb_info.switch_con = &vfbcon_switch;
     fb_info.updatevar = &vfbcon_updatevar;
     fb_info.blank = &vfbcon_blank;
-    fb_info.setcmap = &vfbcon_setcmap;
 
     err = register_framebuffer(&fb_info);
     if (err < 0)
 	return mem_start;
 
-    virtual_fb_set_var(&virtual_fb_predefined[0], -1);
+    vfb_set_var(&vfb_default, -1, &fb_info);
 
-    printk("Virtual frame buffer device, using %ldK of video memory\n",
-	   videomemorysize>>10);
+    printk("fb%d: Virtual frame buffer device, using %ldK of video memory\n",
+	   GET_FB_IDX(fb_info.node), videomemorysize>>10);
     return mem_start;
 }
 
 
-static int vfbcon_switch(int con)
+static int vfbcon_switch(int con, struct fb_info *info)
 {
     /* Do we have to save the colormap? */
     if (fb_display[currcon].cmap.len)
 	fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
-		    vfb_getcolreg);
+		    vfb_getcolreg, info);
 
     currcon = con;
     /* Install new colormap */
-    do_install_cmap(con);
+    do_install_cmap(con, info);
     return 0;
 }
 
@@ -423,7 +471,7 @@
      *  Update the `var' structure (called by fbcon.c)
      */
 
-static int vfbcon_updatevar(int con)
+static int vfbcon_updatevar(int con, struct fb_info *info)
 {
     /* Nothing */
     return 0;
@@ -433,21 +481,11 @@
      *  Blank the display.
      */
 
-static void vfbcon_blank(int blank)
+static void vfbcon_blank(int blank, struct fb_info *info)
 {
     /* Nothing */
 }
 
-    /*
-     *  Set the colormap
-     */
-
-static int vfbcon_setcmap(struct fb_cmap *cmap, int con)
-{
-    return(virtual_fb_set_cmap(cmap, 1, con));
-}
-
-
 static u_long get_line_length(int xres_virtual, int bpp)
 {
     u_long length;
@@ -462,7 +500,7 @@
 			   struct fb_var_screeninfo *var)
 {
     memset(fix, 0, sizeof(struct fb_fix_screeninfo));
-    strcpy(fix->id, virtual_fb_name);
+    strcpy(fix->id, vfb_name);
     fix->smem_start = (caddr_t)videomemory;
     fix->smem_len = videomemorysize;
     fix->type = FB_TYPE_PACKED_PIXELS;
@@ -471,6 +509,8 @@
 	case 1:
 	    fix->visual = FB_VISUAL_MONO01;
 	    break;
+	case 2:
+	case 4:
 	case 8:
 	    fix->visual = FB_VISUAL_PSEUDOCOLOR;
 	    break;
@@ -544,7 +584,7 @@
      */
 
 static int vfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
-                         u_int *transp)
+                         u_int *transp, struct fb_info *info)
 {
     if (regno > 255)
 	return 1;
@@ -562,7 +602,7 @@
      */
 
 static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp)
+                         u_int transp, struct fb_info *info)
 {
     if (regno > 255)
 	return 1;
@@ -573,23 +613,23 @@
 }
 
 
-static void do_install_cmap(int con)
+static void do_install_cmap(int con, struct fb_info *info)
 {
     if (con != currcon)
 	return;
     if (fb_display[con].cmap.len)
 	fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
-		    vfb_setcolreg);
+		    vfb_setcolreg, info);
     else
-	fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
-				    &fb_display[con].var, 1, vfb_setcolreg);
+	fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+		    &fb_display[con].var, 1, vfb_setcolreg, info);
 }
 
 
 #ifdef MODULE
 int init_module(void)
 {
-    return(virtual_fb_init(NULL));
+    return(vfb_init(NULL));
 }
 
 void cleanup_module(void)
diff -u --recursive --new-file v2.1.90/linux/drivers/video/vgacon.c linux/drivers/video/vgacon.c
--- v2.1.90/linux/drivers/video/vgacon.c	Mon Nov 24 01:19:02 1997
+++ linux/drivers/video/vgacon.c	Sat Mar 21 11:12:49 1998
@@ -87,16 +87,16 @@
 static unsigned long vgacon_startup(unsigned long kmem_start,
 				   const char **display_desc);
 static void vgacon_init(struct vc_data *conp);
-static int vgacon_deinit(struct vc_data *conp);
-static int vgacon_clear(struct vc_data *conp, int sy, int sx, int height,
-		       int width);
-static int vgacon_putc(struct vc_data *conp, int c, int ypos, int xpos);
-static int vgacon_putcs(struct vc_data *conp, const char *s, int count,
-		       int ypos, int xpos);
-static int vgacon_cursor(struct vc_data *conp, int mode);
-static int vgacon_scroll(struct vc_data *conp, int t, int b,
-			int dir, int count);
-static int vgacon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+static void vgacon_deinit(struct vc_data *conp);
+static void vgacon_clear(struct vc_data *conp, int sy, int sx, int height,
+			 int width);
+static void vgacon_putc(struct vc_data *conp, int c, int ypos, int xpos);
+static void vgacon_putcs(struct vc_data *conp, const char *s, int count,
+			 int ypos, int xpos);
+static void vgacon_cursor(struct vc_data *conp, int mode);
+static void vgacon_scroll(struct vc_data *conp, int t, int b, int dir,
+			  int count);
+static void vgacon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
 		       int height, int width);
 static int vgacon_switch(struct vc_data *conp);
 static int vgacon_blank(int blank);
@@ -104,6 +104,7 @@
 static int vgacon_set_font(struct vc_data *conp, int w, int h, char *data);
 static int vgacon_set_palette(struct vc_data *conp, unsigned char *table);
 static int vgacon_scrolldelta(int lines);
+static int vgacon_set_mode(struct vc_data *conp, int mode);
 
 
 /*
@@ -215,25 +216,6 @@
 	unsigned short saved;
 	unsigned short *p;
 
-	/*
-	 *	Find out if there is a graphics card present.
-	 *	Are there smarter methods around?
-	 */
-	p = (unsigned short *)(((ORIG_VIDEO_MODE == 7) ? 0xb0000 : 0xb8000) +
-			       + VGA_OFFSET);
-	saved = vga_readw(p);
-	vga_writew(0xAA55, p);
-	if (vga_readw(p) != 0xAA55) {
-		vga_writew(saved, p);
-		return kmem_start;
-	}
-	vga_writew(0x55AA, p);
-	if (vga_readw(p) != 0x55AA) {
-		vga_writew(saved, p);
-		return kmem_start;
-	}
-	vga_writew(saved, p);
-
 	vga_video_num_lines = ORIG_VIDEO_LINES;
 	vga_video_num_columns = ORIG_VIDEO_COLS;
 	vga_video_size_row = 2 * ORIG_VIDEO_COLS;
@@ -327,6 +309,24 @@
 		}
 	}
 
+	/*
+	 *	Find out if there is a graphics card present.
+	 *	Are there smarter methods around?
+	 */
+	p = (unsigned short *)vga_video_mem_base;
+	saved = vga_readw(p);
+	vga_writew(0xAA55, p);
+	if (vga_readw(p) != 0xAA55) {
+		vga_writew(saved, p);
+		return kmem_start;
+	}
+	vga_writew(0x55AA, p);
+	if (vga_readw(p) != 0x55AA) {
+		vga_writew(saved, p);
+		return kmem_start;
+	}
+	vga_writew(saved, p);
+
 	vga_hardscroll_enabled = (vga_hardscroll_disabled_by_init ? 0 :
 	  (vga_video_type == VIDEO_TYPE_EGAC
 	    || vga_video_type == VIDEO_TYPE_VGAC
@@ -358,22 +358,21 @@
     conp->vc_can_do_color = vga_can_do_color;
 }
 
-static int vgacon_deinit(struct vc_data *conp)
+static void vgacon_deinit(struct vc_data *conp)
 {
-    return 0;
 }
 
 
 /* ====================================================================== */
 
-static int vgacon_clear(struct vc_data *conp, int sy, int sx, int height,
-			      int width)
+static void vgacon_clear(struct vc_data *conp, int sy, int sx, int height,
+			 int width)
 {
     int rows;
     unsigned long dest;
 
     if (console_blanked)
-	return 0;
+	return;
 
     dest = vga_video_mem_base + sy*vga_video_size_row + sx*2;
     if (sx == 0 && width == vga_video_num_columns)      
@@ -381,41 +380,38 @@
     else
         for (rows = height; rows-- ; dest += vga_video_size_row)
 	    vga_memsetw((void *)dest, conp->vc_video_erase_char, width);
-    return 0;
 }
 
 
-static int vgacon_putc(struct vc_data *conp, int c, int ypos, int xpos)
+static void vgacon_putc(struct vc_data *conp, int c, int ypos, int xpos)
 {
-    u_short *p;
+    u16 *p;
 
     if (console_blanked)
-	    return 0;
+	return;
 
-    p = (u_short *)(vga_video_mem_base+ypos*vga_video_size_row+xpos*2);
+    p = (u16 *)(vga_video_mem_base+ypos*vga_video_size_row+xpos*2);
     vga_writew(conp->vc_attr << 8 | c, p);
-    return 0;
 }
 
 
-static int vgacon_putcs(struct vc_data *conp, const char *s, int count,
-		       int ypos, int xpos)
+static void vgacon_putcs(struct vc_data *conp, const char *s, int count,
+			 int ypos, int xpos)
 {
-    u_short *p;
-    u_short sattr;
+    u16 *p;
+    u16 sattr;
 
     if (console_blanked)
-	    return 0;
+	return;
 
-    p = (u_short *)(vga_video_mem_base+ypos*vga_video_size_row+xpos*2);
+    p = (u16 *)(vga_video_mem_base+ypos*vga_video_size_row+xpos*2);
     sattr = conp->vc_attr << 8;
     while (count--)
 	vga_writew(sattr | *s++, p++);
-    return 0;
 }
 
 
-static int vgacon_cursor(struct vc_data *conp, int mode)
+static void vgacon_cursor(struct vc_data *conp, int mode)
 {
     switch (mode) {
 	case CM_ERASE:
@@ -427,14 +423,14 @@
 	    write_vga(14, conp->vc_y*vga_video_num_columns+conp->vc_x);
 	    break;
     }
-    return 0;
 }
 
 
-static int vgacon_scroll(struct vc_data *conp, int t, int b, int dir, int count)
+static void vgacon_scroll(struct vc_data *conp, int t, int b, int dir,
+			  int count)
 {
     if (console_blanked)
-	return 0;
+	return;
 
     vgacon_cursor(conp, CM_ERASE);
 
@@ -469,19 +465,17 @@
 	    vgacon_clear(conp, 0, t, conp->vc_rows, count);
 	    break;
     }
-
-    return 0;
 }
 
 
-static int vgacon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
-		       int height, int width)
+static void vgacon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+			 int height, int width)
 {
     unsigned long src, dst;
     int rows;
 
     if (console_blanked)
-	return 0;
+	return;
 
     if (sx == 0 && dx == 0 && width == vga_video_num_columns) {
 	src = vga_video_mem_base + sy * vga_video_size_row;
@@ -505,7 +499,6 @@
 	    dst -= vga_video_size_row;
 	}
     }
-    return 0;
 }
 
 
@@ -522,7 +515,7 @@
 	return 0;
     } else {
 	/* Tell console.c that it has to restore the screen itself */
-	return(1);
+	return 1;
     }
     return 0;
 }
@@ -564,6 +557,11 @@
     return -ENOSYS;
 }
 
+static int vgacon_set_mode(struct vc_data *conp, int mode)
+{
+    return -ENOSYS;
+}
+
 
 __initfunc(static int vgacon_show_logo( void ))
 {
@@ -587,5 +585,5 @@
     vgacon_startup, vgacon_init, vgacon_deinit, vgacon_clear, vgacon_putc,
     vgacon_putcs, vgacon_cursor, vgacon_scroll, vgacon_bmove, vgacon_switch,
     vgacon_blank, vgacon_get_font, vgacon_set_font, vgacon_set_palette,
-    vgacon_scrolldelta
+    vgacon_scrolldelta, vgacon_set_mode
 };
diff -u --recursive --new-file v2.1.90/linux/drivers/video/virgefb.c linux/drivers/video/virgefb.c
--- v2.1.90/linux/drivers/video/virgefb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/virgefb.c	Sat Mar 21 11:12:49 1998
@@ -0,0 +1,1190 @@
+/*
+ * linux/drivers/video/virgefb.c -- CyberVision64/3D frame buffer device
+ *
+ *    Copyright (C) 1997 André Heynatz
+ *
+ *
+ * This file is based on the CyberVision frame buffer device (cyberfb.c):
+ *
+ *    Copyright (C) 1996 Martin Apel
+ *                       Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#undef VIRGEFBDEBUG
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/zorro.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/amigahw.h>
+
+#include "s3blit.h"
+#include "fbcon.h"
+#include "fbcon-cfb8.h"
+#include "fbcon-cfb16.h"
+
+
+#ifdef VIRGEFBDEBUG
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#define arraysize(x)    (sizeof(x)/sizeof(*(x)))
+
+#if 1
+#define vgawb_3d(reg,dat) \
+                (*((unsigned char *)(CyberVGARegs + (reg ^ 3))) = dat)
+#define vgaww_3d(reg,dat) \
+                (*((unsigned word *)(CyberVGARegs + (reg ^ 2))) = swab16(dat))
+#define vgawl_3d(reg,dat) \
+                (*((unsigned long *)(CyberVGARegs + reg)) = swab32(dat))
+#else
+     /*
+      * Dunno why this doesn't work at the moment - we'll have to look at
+      * it later.
+      */
+#define vgawb_3d(reg,dat) \
+                (*((unsigned char *)(CyberRegs + 0x8000 + reg)) = dat)
+#define vgaww_3d(reg,dat) \
+                (*((unsigned word *)(CyberRegs + 0x8000 + reg)) = dat)
+#define vgawl_3d(reg,dat) \
+                (*((unsigned long *)(CyberRegs + 0x8000 + reg)) = dat)
+#endif
+
+     /*
+      * We asume P5 mapped the big-endian version of these registers.
+      */
+#define wb_3d(reg,dat) \
+                (*((unsigned char volatile *)(CyberRegs + reg)) = dat)
+#define ww_3d(reg,dat) \
+                (*((unsigned word volatile *)(CyberRegs + reg)) = dat)
+#define wl_3d(reg,dat) \
+                (*((unsigned long volatile *)(CyberRegs + reg)) = dat)
+
+#define rl_3d(reg) \
+                (*((unsigned long volatile *)(CyberRegs + reg)))
+
+
+
+
+
+
+struct virgefb_par {
+   int xres;
+   int yres;
+   int bpp;
+};
+
+static struct virgefb_par current_par;
+
+static int current_par_valid = 0;
+static int currcon = 0;
+
+static struct display disp;
+static struct fb_info fb_info;
+
+
+/*
+ *    Switch for Chipset Independency
+ */
+
+static struct fb_hwswitch {
+
+   /* Initialisation */
+
+   int (*init)(void);
+
+   /* Display Control */
+
+   int (*encode_fix)(struct fb_fix_screeninfo *fix, struct virgefb_par *par);
+   int (*decode_var)(struct fb_var_screeninfo *var, struct virgefb_par *par);
+   int (*encode_var)(struct fb_var_screeninfo *var, struct virgefb_par *par);
+   int (*getcolreg)(u_int regno, u_int *red, u_int *green, u_int *blue,
+                    u_int *transp, struct fb_info *info);
+   int (*setcolreg)(u_int regno, u_int red, u_int green, u_int blue,
+                    u_int transp, struct fb_info *info);
+   void (*blank)(int blank);
+} *fbhw;
+
+
+/*
+ *    Frame Buffer Name
+ */
+
+static char virgefb_name[16] = "Cybervision/3D";
+
+
+/*
+ *    Cybervision Graphics Board
+ */
+
+#define VIRGE8_WIDTH 1152
+#define VIRGE8_HEIGHT 886
+#define VIRGE8_PIXCLOCK 12500    /* ++Geert: Just a guess */
+
+#if 0
+#define VIRGE16_WIDTH 800
+#define VIRGE16_HEIGHT 600
+#endif
+#define VIRGE16_PIXCLOCK 25000   /* ++Geert: Just a guess */
+
+
+static unsigned int CyberKey = 0;
+static unsigned char Cyber_colour_table [256][4];
+static unsigned long CyberMem;
+static unsigned long CyberSize;
+static volatile char *CyberRegs;
+static volatile unsigned long CyberVGARegs; /* ++Andre: for CV64/3D, see macros at the beginning */
+ 
+
+/*
+ *    Predefined Video Modes
+ */
+
+static struct fb_videomode virgefb_predefined[] __initdata = {
+    {
+	"640x480-8", {		/* Cybervision 8 bpp */
+	    640, 480, 640, 480, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NONE, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }, {
+	"800x600-8", {		/* Cybervision 8 bpp */
+	    800, 600, 800, 600, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NONE, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }, {
+	"1024x768-8", {		/* Cybervision 8 bpp */
+	    1024, 768, 1024, 768, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NONE, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }, {
+	"1152x886-8", {		/* Cybervision 8 bpp */
+	    1152, 886, 1152, 886, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NONE, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }, {
+	"1280x1024-8", {	/* Cybervision 8 bpp */
+	    1280, 1024, 1280, 1024, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NONE, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }, {
+	"1600x1200-8", {	/* Cybervision 8 bpp */
+	    1600, 1200, 1600, 1200, 0, 0, 8, 0,
+	    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NONE, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }, {
+	"800x600-16", {		/* Cybervision 16 bpp */
+	    800, 600, 800, 600, 0, 0, 16, 0,
+	    {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
+	    0, 0, -1, -1, FB_ACCEL_NONE, VIRGE16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+	    FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+	}
+    }
+};
+
+
+#define NUM_TOTAL_MODES    arraysize(virgefb_predefined)
+
+
+static int Cyberfb_inverse = 0;
+#if 0
+static int Cyberfb_Cyber8 = 0;        /* Use Cybervision board */
+static int Cyberfb_Cyber16 = 0;       /* Use Cybervision board */
+#endif
+
+/*
+ *    Some default modes
+ */
+
+#define VIRGE8_DEFMODE     (0)
+#define VIRGE16_DEFMODE    (6)
+
+static struct fb_var_screeninfo virgefb_default;
+
+
+/*
+ *    Interface used by the world
+ */
+
+void virgefb_setup(char *options, int *ints);
+
+static int virgefb_open(struct fb_info *info);
+static int virgefb_release(struct fb_info *info);
+static int virgefb_get_fix(struct fb_fix_screeninfo *fix, int con, struct
+fb_info *info);
+static int virgefb_get_var(struct fb_var_screeninfo *var, int con, struct
+fb_info *info);
+static int virgefb_set_var(struct fb_var_screeninfo *var, int con, struct
+fb_info *info);
+static int virgefb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info);
+static int virgefb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info);
+static int virgefb_pan_display(struct fb_var_screeninfo *var, int con,
+			       struct fb_info *info);
+static int virgefb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+                         u_long arg, int con, struct fb_info *info);
+
+
+/*
+ *    Interface to the low level console driver
+ */
+
+unsigned long virgefb_init(unsigned long mem_start);
+static int Cyberfb_switch(int con, struct fb_info *info);
+static int Cyberfb_updatevar(int con, struct fb_info *info);
+static void Cyberfb_blank(int blank, struct fb_info *info);
+
+
+/*
+ *    Text console acceleration
+ */
+
+#ifdef CONFIG_FBCON_CFB8
+static struct display_switch fbcon_virge8;
+#endif
+
+
+/*
+ *   Hardware Specific Routines
+ */
+
+static int Cyber_init(void);
+static int Cyber_encode_fix(struct fb_fix_screeninfo *fix,
+                          struct virgefb_par *par);
+static int Cyber_decode_var(struct fb_var_screeninfo *var,
+                          struct virgefb_par *par);
+static int Cyber_encode_var(struct fb_var_screeninfo *var,
+                          struct virgefb_par *par);
+static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+                         u_int *transp, struct fb_info *info);
+static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                         u_int transp, struct fb_info *info);
+static void Cyber_blank(int blank);
+
+
+/*
+ *    Internal routines
+ */
+
+static void virgefb_get_par(struct virgefb_par *par);
+static void virgefb_set_par(struct virgefb_par *par);
+static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive);
+static void do_install_cmap(int con, struct fb_info *info);
+static void virgefb_set_disp(int con, struct fb_info *info);
+static int get_video_mode(const char *name);
+
+
+/* -------------------- Hardware specific routines ------------------------- */
+
+
+/*
+ *    Initialization
+ *
+ *    Set the default video mode for this chipset. If a video mode was
+ *    specified on the command line, it will override the default mode.
+ */
+
+static int Cyber_init(void)
+{
+	int i;
+
+	for (i = 0; i < 256; i++)
+	{
+		Cyber_colour_table [i][0] = i;
+		Cyber_colour_table [i][1] = i;
+		Cyber_colour_table [i][2] = i;
+		Cyber_colour_table [i][3] = 0;
+	}
+
+	/*
+	 * Just clear the thing for the biggest mode.
+	 *
+	 * ++Andre, TODO: determine size first, then clear all memory
+	 *                (the 3D penguin might need texture memory :-) )
+	 */
+
+	memset ((char*)CyberMem, 0, 1600 * 1200);
+
+	/* Disable hardware cursor */
+	CyberSize = 0x00400000; /* 4 MB */
+
+	vgawb_3d(0x3c8, 255);
+	vgawb_3d(0x3c9, 56);
+	vgawb_3d(0x3c9, 100);
+	vgawb_3d(0x3c9, 160);
+
+	vgawb_3d(0x3c8, 254);
+	vgawb_3d(0x3c9, 0);
+	vgawb_3d(0x3c9, 0);
+	vgawb_3d(0x3c9, 0);
+
+	/* Disable hardware cursor */
+	vgawb_3d(S3_CRTC_ADR, S3_REG_LOCK2);
+	vgawb_3d(S3_CRTC_DATA, 0xa0);
+	vgawb_3d(S3_CRTC_ADR, S3_HGC_MODE);
+	vgawb_3d(S3_CRTC_DATA, 0x00);
+	vgawb_3d(S3_CRTC_ADR, S3_HWGC_DX);
+	vgawb_3d(S3_CRTC_DATA, 0x00);
+	vgawb_3d(S3_CRTC_ADR, S3_HWGC_DY);
+	vgawb_3d(S3_CRTC_DATA, 0x00);
+
+	return 0; /* TODO: hardware cursor for CV64/3D */
+}
+
+
+/*
+ *    This function should fill in the `fix' structure based on the
+ *    values in the `par' structure.
+ */
+
+static int Cyber_encode_fix(struct fb_fix_screeninfo *fix,
+			    struct virgefb_par *par)
+{
+	int i;
+
+	strcpy(fix->id, virgefb_name);
+	fix->smem_start = (caddr_t)CyberMem;
+	fix->smem_len = CyberSize;
+	fix->mmio_start = (unsigned char *)CyberRegs;
+	fix->mmio_len = 0x10000; /* TODO: verify this for the CV64/3D */
+
+	fix->type = FB_TYPE_PACKED_PIXELS;
+	fix->type_aux = 0;
+	if (par->bpp == 8)
+		fix->visual = FB_VISUAL_PSEUDOCOLOR;
+	else
+		fix->visual = FB_VISUAL_DIRECTCOLOR;
+
+	fix->xpanstep = 0;
+	fix->ypanstep = 0;
+	fix->ywrapstep = 0;
+	fix->line_length = 0;
+
+	for (i = 0; i < arraysize(fix->reserved); i++)
+		fix->reserved[i] = 0;
+
+	return(0);
+}
+
+
+/*
+ *    Get the video params out of `var'. If a value doesn't fit, round
+ *    it up, if it's too big, return -EINVAL.
+ */
+
+static int Cyber_decode_var(struct fb_var_screeninfo *var,
+			    struct virgefb_par *par)
+{
+#if 1
+	par->xres = var->xres;
+	par->yres = var->yres;
+	par->bpp = var->bits_per_pixel;
+#else
+	if (Cyberfb_Cyber8) {
+		par->xres = VIRGE8_WIDTH;
+		par->yres = VIRGE8_HEIGHT;
+		par->bpp = 8;
+	} else {
+		par->xres = VIRGE16_WIDTH;
+		par->yres = VIRGE16_HEIGHT;
+		par->bpp = 16;
+	}
+#endif
+	return(0);
+}
+
+
+/*
+ *    Fill the `var' structure based on the values in `par' and maybe
+ *    other values read out of the hardware.
+ */
+
+static int Cyber_encode_var(struct fb_var_screeninfo *var,
+			    struct virgefb_par *par)
+{
+	int i;
+
+	var->xres = par->xres;
+	var->yres = par->yres;
+	var->xres_virtual = par->xres;
+	var->yres_virtual = par->yres;
+	var->xoffset = 0;
+	var->yoffset = 0;
+
+	var->bits_per_pixel = par->bpp;
+	var->grayscale = 0;
+
+	if (par->bpp == 8) {
+		var->red.offset = 0;
+		var->red.length = 8;
+		var->red.msb_right = 0;
+		var->blue = var->green = var->red;
+	} else {
+		var->red.offset = 11;
+		var->red.length = 5;
+		var->red.msb_right = 0;
+		var->green.offset = 5;
+		var->green.length = 6;
+		var->green.msb_right = 0;
+		var->blue.offset = 0;
+		var->blue.length = 5;
+		var->blue.msb_right = 0;
+	}
+	var->transp.offset = 0;
+	var->transp.length = 0;
+	var->transp.msb_right = 0;
+
+	var->nonstd = 0;
+	var->activate = 0;
+
+	var->height = -1;
+	var->width = -1;
+
+	var->accel = FB_ACCEL_S3VIRGE;
+	DPRINTK("accel CV64/3D\n");
+
+	var->vmode = FB_VMODE_NONINTERLACED;
+
+	/* Dummy values */
+
+	if (par->bpp == 8)
+		var->pixclock = VIRGE8_PIXCLOCK;
+	else
+		var->pixclock = VIRGE16_PIXCLOCK;
+	var->sync = 0;
+	var->left_margin = 64;
+	var->right_margin = 96;
+	var->upper_margin = 35;
+	var->lower_margin = 12;
+	var->hsync_len = 112;
+	var->vsync_len = 2;
+
+	for (i = 0; i < arraysize(var->reserved); i++)
+		var->reserved[i] = 0;
+
+	return(0);
+}
+
+
+/*
+ *    Set a single color register. The values supplied are already
+ *    rounded down to the hardware's capabilities (according to the
+ *    entries in the var structure). Return != 0 for invalid regno.
+ */
+
+static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			   u_int transp, struct fb_info *info)
+{
+	if (regno > 255)
+	{
+		return (1);
+	}
+
+	/*
+	 * No colors on the CV3D yet.
+	 */
+
+	vgawb_3d(0x3c8, (unsigned char) regno);
+	Cyber_colour_table [regno][0] = red & 0xff;
+	Cyber_colour_table [regno][1] = green & 0xff;
+	Cyber_colour_table [regno][2] = blue & 0xff;
+	Cyber_colour_table [regno][3] = transp;
+
+	vgawb_3d(0x3c9, ((red & 0xff) >> 2));
+	vgawb_3d(0x3c9, ((green & 0xff) >> 2));
+	vgawb_3d(0x3c9, ((blue & 0xff) >> 2));
+
+	return (0);
+}
+
+
+/*
+ *    Read a single color register and split it into
+ *    colors/transparent. Return != 0 for invalid regno.
+ */
+
+static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+			   u_int *transp, struct fb_info *info)
+{
+	if (regno >= 256)
+		return (1);
+	*red	= Cyber_colour_table [regno][0];
+	*green	= Cyber_colour_table [regno][1];
+	*blue	= Cyber_colour_table [regno][2];
+	*transp = Cyber_colour_table [regno][3];
+	return (0);
+}
+
+
+/*
+ *    (Un)Blank the screen
+ */
+
+void Cyber_blank(int blank)
+{
+	int i;
+
+	if (blank)
+	{
+		for (i = 0; i < 256; i++)
+		{
+			vgawb_3d(0x3c8, (unsigned char) i);
+			vgawb_3d(0x3c9, 0);
+			vgawb_3d(0x3c9, 0);
+			vgawb_3d(0x3c9, 0);
+		}
+	}
+	else
+	{
+		for (i = 0; i < 256; i++)
+		{
+			vgawb_3d(0x3c8, (unsigned char) i);
+			vgawb_3d(0x3c9, Cyber_colour_table[i][0] >> 2);
+			vgawb_3d(0x3c9, Cyber_colour_table[i][1] >> 2);
+			vgawb_3d(0x3c9, Cyber_colour_table[i][2] >> 2);
+		}
+	}
+}
+
+/*
+ * CV3D low-level support
+ */
+
+#define Cyber3D_WaitQueue(v)	 { do { while ((rl_3d(0x8504) & 0x1f00) < (((v)+2) << 8)); } while (0); }
+
+static inline void Cyber3D_WaitBusy(void)
+{
+unsigned long status;
+
+	do {
+		status = rl_3d(0x8504);
+	} while (!(status & (1 << 13)));
+}
+
+#define S3V_BITBLT	(0x0 << 27)
+#define S3V_RECTFILL	(0x2 << 27)
+#define S3V_AUTOEXE	0x01
+#define S3V_HWCLIP	0x02
+#define S3V_DRAW	0x20
+#define S3V_DST_8BPP	0x00
+#define S3V_DST_16BPP	0x04
+#define S3V_DST_24BPP	0x08
+#define S3V_MONO_PAT	0x100
+
+#define S3V_BLT_COPY	(0xcc<<17)
+#define S3V_BLT_CLEAR	(0x00<<17)
+#define S3V_BLT_SET	(0xff<<17)
+
+ /*
+  * BitBLT - Through the Plane
+  */
+
+static void Cyber3D_BitBLT(u_short curx, u_short cury, u_short destx,
+			   u_short desty, u_short width, u_short height)
+{
+	unsigned int blitcmd = S3V_BITBLT | S3V_DRAW | S3V_DST_8BPP;
+
+	blitcmd |= S3V_BLT_COPY;
+
+	/* Set drawing direction */
+	/* -Y, X maj, -X (default) */
+	if (curx > destx)
+	{
+		blitcmd |= (1 << 25);  /* Drawing direction +X */
+	}
+	else
+	{
+		curx  += (width - 1);
+		destx += (width - 1);
+	}
+
+	if (cury > desty)
+	{
+		blitcmd |= (1 << 26);  /* Drawing direction +Y */
+	}
+	else
+	{
+		cury  += (height - 1);
+		desty += (height - 1);
+	}
+
+	wl_3d(0xa4f4, 1); /* pattern fb color */
+
+	wl_3d(0xa4e8, ~0); /* mono pat 0 */
+	wl_3d(0xa4ec, ~0); /* mono pat 1 */
+
+	wl_3d(0xa504, ((width << 16) | height));	/* rwidth_height */
+	wl_3d(0xa508, ((curx << 16)  | cury));		/* rsrc_xy */
+	wl_3d(0xa50c, ((destx << 16) | desty));		/* rdest_xy */
+
+	wl_3d(0xa500, blitcmd);				/* GO! */
+
+	Cyber3D_WaitBusy();
+}
+
+/*
+ * Rectangle Fill Solid
+ */
+
+static void Cyber3D_RectFill(u_short x, u_short y, u_short width,
+			     u_short height, u_short color)
+{
+	unsigned int tmp;
+	unsigned int blitcmd = S3V_RECTFILL | S3V_DRAW | S3V_DST_8BPP |
+		S3V_BLT_CLEAR | S3V_MONO_PAT | (1 << 26) | (1 << 25);
+
+	tmp = color & 0xff;
+	wl_3d(0xa4f4, tmp);
+
+	wl_3d(0xa504, ((width << 16) | height));	/* rwidth_height */
+	wl_3d(0xa50c, ((x << 16) | y));			/* rdest_xy */
+
+	wl_3d(0xa500, blitcmd);				/* GO! */
+	Cyber3D_WaitBusy();
+}
+
+
+/**************************************************************
+ * Move cursor to x, y
+ */
+static void Cyber_MoveCursor (u_short x, u_short y)
+{
+	printk("Yuck .... MoveCursor on a 3D\n");
+	return;
+}
+
+
+/* -------------------- Interfaces to hardware functions -------------------- */
+
+
+static struct fb_hwswitch Cyber_switch = {
+	Cyber_init, Cyber_encode_fix, Cyber_decode_var, Cyber_encode_var,
+	Cyber_getcolreg, Cyber_setcolreg, Cyber_blank
+};
+
+
+/* -------------------- Generic routines ------------------------------------ */
+
+
+/*
+ *    Fill the hardware's `par' structure.
+ */
+
+static void virgefb_get_par(struct virgefb_par *par)
+{
+	if (current_par_valid)
+	{
+		*par = current_par;
+	}
+	else
+	{
+		fbhw->decode_var(&virgefb_default, par);
+	}
+}
+
+
+static void virgefb_set_par(struct virgefb_par *par)
+{
+	current_par = *par;
+	current_par_valid = 1;
+}
+
+
+static void virge_set_video(struct fb_var_screeninfo *var)
+{
+	/* Set clipping rectangle to current screen size */
+ 
+	unsigned int clip;
+
+	clip = ((0 << 16) | (var->xres - 1));
+	wl_3d(0xa4dc, clip);
+	clip = ((0 << 16) | (var->yres - 1));
+	wl_3d(0xa4e0, clip);
+}
+
+
+static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
+{
+	int err, activate;
+	struct virgefb_par par;
+
+	if ((err = fbhw->decode_var(var, &par)))
+		return(err);
+	activate = var->activate;
+	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive)
+		virgefb_set_par(&par);
+	fbhw->encode_var(var, &par);
+	var->activate = activate;
+
+	virge_set_video(var);
+	return 0;
+}
+
+
+static void do_install_cmap(int con, struct fb_info *info)
+{
+	if (con != currcon)
+		return;
+	if (fb_display[con].cmap.len)
+		fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
+			    fbhw->setcolreg, info);
+	else
+		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+			    &fb_display[con].var, 1, fbhw->setcolreg, info);
+}
+
+
+/*
+ *  Open/Release the frame buffer device
+ */
+
+static int virgefb_open(struct fb_info *info)
+{
+	/*
+	 * Nothing, only a usage count for the moment
+	 */
+
+	MOD_INC_USE_COUNT;
+	return(0);
+}
+
+static int virgefb_release(struct fb_info *info)
+{
+	MOD_DEC_USE_COUNT;
+	return(0);
+}
+
+
+/*
+ *    Get the Fixed Part of the Display
+ */
+
+static int virgefb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			   struct fb_info *info)
+{
+	struct virgefb_par par;
+	int error = 0;
+
+	if (con == -1)
+		virgefb_get_par(&par);
+	else
+		error = fbhw->decode_var(&fb_display[con].var, &par);
+	return(error ? error : fbhw->encode_fix(fix, &par));
+}
+
+
+/*
+ *    Get the User Defined Part of the Display
+ */
+
+static int virgefb_get_var(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info)
+{
+	struct virgefb_par par;
+	int error = 0;
+
+	if (con == -1)
+	{
+		virgefb_get_par(&par);
+		error = fbhw->encode_var(var, &par);
+		disp.var = *var;   /* ++Andre: don't know if this is the right place */
+	}
+	else
+	{
+		*var = fb_display[con].var;
+	}
+
+	return(error);
+}
+
+
+static void virgefb_set_disp(int con, struct fb_info *info)
+{
+	struct fb_fix_screeninfo fix;
+	struct display *display;
+
+	if (con >= 0)
+		display = &fb_display[con];
+	else
+		display = &disp;	/* used during initialization */
+
+	virgefb_get_fix(&fix, con, info);
+	if (con == -1)
+		con = 0;
+	display->screen_base = (u_char *)fix.smem_start;
+	display->visual = fix.visual;
+	display->type = fix.type;
+	display->type_aux = fix.type_aux;
+	display->ypanstep = fix.ypanstep;
+	display->ywrapstep = fix.ywrapstep;
+	display->can_soft_blank = 1;
+	display->inverse = Cyberfb_inverse;
+	switch (display->var.bits_per_pixel) {
+#ifdef CONFIG_FBCON_CFB8
+	    case 8:
+		display->dispsw = &fbcon_virge8;
+		break;
+#endif
+#ifdef CONFIG_FBCON_CFB16
+	    case 16:
+		display->dispsw = &fbcon_cfb16;
+		break;
+#endif
+	    default:
+		display->dispsw = NULL;
+		break;
+	}
+}
+
+
+/*
+ *    Set the User Defined Part of the Display
+ */
+
+static int virgefb_set_var(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info)
+{
+	int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp;
+
+	if ((err = do_fb_set_var(var, con == currcon)))
+		return(err);
+	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+		oldxres = fb_display[con].var.xres;
+		oldyres = fb_display[con].var.yres;
+		oldvxres = fb_display[con].var.xres_virtual;
+		oldvyres = fb_display[con].var.yres_virtual;
+		oldbpp = fb_display[con].var.bits_per_pixel;
+		fb_display[con].var = *var;
+		if (oldxres != var->xres || oldyres != var->yres ||
+		    oldvxres != var->xres_virtual ||
+		    oldvyres != var->yres_virtual ||
+		    oldbpp != var->bits_per_pixel) {
+			virgefb_set_disp(con, info);
+			(*fb_info.changevar)(con);
+			fb_alloc_cmap(&fb_display[con].cmap, 0, 0);
+			do_install_cmap(con, info);
+		}
+	}
+	var->activate = 0;
+	return(0);
+}
+
+
+/*
+ *    Get the Colormap
+ */
+
+static int virgefb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info)
+{
+	if (con == currcon) /* current console? */
+		return(fb_get_cmap(cmap, &fb_display[con].var,
+				   kspc, fbhw->getcolreg, info));
+	else if (fb_display[con].cmap.len) /* non default colormap? */
+		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+	else
+		fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+			     cmap, kspc ? 0 : 2);
+	return(0);
+}
+
+
+/*
+ *    Set the Colormap
+ */
+
+static int virgefb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info)
+{
+	int err;
+
+	if (!fb_display[con].cmap.len) {       /* no colormap allocated? */
+		if ((err = fb_alloc_cmap(&fb_display[con].cmap,
+				 1<<fb_display[con].var.bits_per_pixel, 0)))
+			return(err);
+	}
+	if (con == currcon)		 /* current console? */
+		return(fb_set_cmap(cmap, &fb_display[con].var,
+				   kspc, fbhw->setcolreg, info));
+	else
+		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+	return(0);
+}
+
+
+/*
+ *    Pan or Wrap the Display
+ *
+ *    This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ */
+
+static int virgefb_pan_display(struct fb_var_screeninfo *var, int con,
+			       struct fb_info *info)
+{
+	return(-EINVAL);
+}
+
+
+/*
+    *	 Cybervision Frame Buffer Specific ioctls
+    */
+
+static int virgefb_ioctl(struct inode *inode, struct file *file,
+			 u_int cmd, u_long arg, int con, struct fb_info *info)
+{
+	return(-EINVAL);
+}
+
+
+static struct fb_ops virgefb_ops = {
+	virgefb_open, virgefb_release, virgefb_get_fix, virgefb_get_var,
+	virgefb_set_var, virgefb_get_cmap, virgefb_set_cmap,
+	virgefb_pan_display, NULL, virgefb_ioctl
+};
+
+
+__initfunc(void virgefb_setup(char *options, int *ints))
+{
+	char *this_opt;
+
+	fb_info.fontname[0] = '\0';
+
+	if (!options || !*options)
+		return;
+
+	for (this_opt = strtok(options, ","); this_opt; this_opt = strtok(NULL, ","))
+		if (!strcmp(this_opt, "inverse")) {
+			Cyberfb_inverse = 1;
+			fb_invert_cmaps();
+		} else if (!strncmp(this_opt, "font:", 5))
+			strcpy(fb_info.fontname, this_opt+5);
+		else if (!strcmp (this_opt, "virge8")){
+			virgefb_default = virgefb_predefined[VIRGE8_DEFMODE].var;
+		}
+		else if (!strcmp (this_opt, "virge16")){
+			virgefb_default = virgefb_predefined[VIRGE16_DEFMODE].var;
+		}
+		else
+			get_video_mode(this_opt);
+
+	DPRINTK("default mode: xres=%d, yres=%d, bpp=%d\n",virgefb_default.xres,
+                                                           virgefb_default.yres,
+		                                           virgefb_default.bits_per_pixel);
+}
+
+
+/*
+ *    Initialization
+ */
+
+__initfunc(unsigned long virgefb_init(unsigned long mem_start))
+{
+	int err;
+	struct virgefb_par par;
+	unsigned long board_addr;
+	const struct ConfigDev *cd;
+
+	if (!(CyberKey = zorro_find(ZORRO_PROD_PHASE5_CYBERVISION64_3D, 0, 0)))
+		return mem_start;
+
+	cd = zorro_get_board (CyberKey);
+	zorro_config_board (CyberKey, 0);
+	board_addr = (unsigned long)cd->cd_BoardAddr;
+
+	/* This includes the video memory as well as the S3 register set */
+	if ((unsigned long)cd->cd_BoardAddr < 0x01000000)
+	{
+		/*
+		 * Ok we got the board running in Z2 space.
+		 */
+
+		CyberMem = ZTWO_VADDR(board_addr);
+		printk("CV3D detected running in Z2 mode ... not yet supported!\n");
+		return -ENODEV;
+	}
+	else
+	{
+		CyberVGARegs = kernel_map(board_addr +0x0c000000,
+					       0x00010000,
+					       KERNELMAP_NOCACHE_SER,
+					       &mem_start);
+		CyberRegs = (char *)kernel_map(board_addr +0x05000000,
+					       0x00010000,
+					       KERNELMAP_NOCACHE_SER,
+					       &mem_start);
+		CyberMem = kernel_map(board_addr + 0x04800000,
+				      0x00400000,
+				      KERNELMAP_NOCACHE_SER,
+				      &mem_start);
+		printk("CV3D detected running in Z3 mode\n");
+	}
+
+	fbhw = &Cyber_switch;
+
+	strcpy(fb_info.modename, virgefb_name);
+	fb_info.changevar = NULL;
+	fb_info.node = -1;
+	fb_info.fbops = &virgefb_ops;
+	fb_info.disp = &disp;
+	fb_info.switch_con = &Cyberfb_switch;
+	fb_info.updatevar = &Cyberfb_updatevar;
+	fb_info.blank = &Cyberfb_blank;
+
+	err = register_framebuffer(&fb_info);
+	if (err < 0)
+		return mem_start;
+
+	fbhw->init();
+	fbhw->decode_var(&virgefb_default, &par);
+	fbhw->encode_var(&virgefb_default, &par);
+
+	do_fb_set_var(&virgefb_default, 1);
+	virgefb_get_var(&fb_display[0].var, -1, &fb_info);
+	virgefb_set_disp(-1, &fb_info);
+	do_install_cmap(0, &fb_info);
+
+	printk("fb%d: %s frame buffer device, using %ldK of video memory\n",
+	       GET_FB_IDX(fb_info.node), fb_info.modename, CyberSize>>10);
+
+	/* TODO: This driver cannot be unloaded yet */
+	MOD_INC_USE_COUNT;
+
+	return mem_start;
+}
+
+
+static int Cyberfb_switch(int con, struct fb_info *info)
+{
+	/* Do we have to save the colormap? */
+	if (fb_display[currcon].cmap.len)
+		fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
+			    fbhw->getcolreg, info);
+
+	do_fb_set_var(&fb_display[con].var, 1);
+	currcon = con;
+	/* Install new colormap */
+	do_install_cmap(con, info);
+	return(0);
+}
+
+
+/*
+ *    Update the `var' structure (called by fbcon.c)
+ *
+ *    This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'.
+ *    Since it's called by a kernel driver, no range checking is done.
+ */
+
+static int Cyberfb_updatevar(int con, struct fb_info *info)
+{
+	return(0);
+}
+
+
+/*
+    *    Blank the display.
+    */
+
+static void Cyberfb_blank(int blank, struct fb_info *info)
+{
+	fbhw->blank(blank);
+}
+
+
+/*
+ *    Get a Video Mode
+ */
+
+__initfunc(static int get_video_mode(const char *name))
+{
+	int i;
+
+	for (i = 0; i < NUM_TOTAL_MODES; i++) {
+		if (!strcmp(name, virgefb_predefined[i].name)) {
+			virgefb_default = virgefb_predefined[i].var;
+			return(i);
+		}
+	}
+	/* ++Andre: set virgefb default mode */
+	virgefb_default = virgefb_predefined[VIRGE8_DEFMODE].var;
+	return(0);
+}
+
+
+/*
+ *    Text console acceleration
+ */
+
+#ifdef CONFIG_FBCON_CFB8
+static void fbcon_virge8_bmove(struct display *p, int sy, int sx, int dy,
+			       int dx, int height, int width)
+{
+        sx *= 8; dx *= 8; width *= 8;
+        Cyber3D_BitBLT((u_short)sx, (u_short)(sy*p->fontheight), (u_short)dx,
+                       (u_short)(dy*p->fontheight), (u_short)width,
+                       (u_short)(height*p->fontheight));
+}
+
+static void fbcon_virge8_clear(struct vc_data *conp, struct display *p, int sy,
+			       int sx, int height, int width)
+{
+        unsigned char bg;
+
+        sx *= 8; width *= 8;
+        bg = attr_bgcol_ec(p,conp);
+        Cyber3D_RectFill((u_short)sx, (u_short)(sy*p->fontheight),
+                         (u_short)width, (u_short)(height*p->fontheight),
+                         (u_short)bg);
+}
+
+static struct display_switch fbcon_virge8 = {
+   fbcon_cfb8_setup, fbcon_virge8_bmove, fbcon_virge8_clear, fbcon_cfb8_putc,
+   fbcon_cfb8_putcs, fbcon_cfb8_revc
+};
+#endif
+
+
+#ifdef MODULE
+int init_module(void)
+{
+	return(virgefb_init(NULL));
+}
+
+void cleanup_module(void)
+{
+	/* Not reached because the usecount will never be
+	   decremented to zero */
+	unregister_framebuffer(&fb_info);
+	/* TODO: clean up ... */
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.90/linux/fs/binfmt_aout.c linux/fs/binfmt_aout.c
--- v2.1.90/linux/fs/binfmt_aout.c	Mon Dec  1 10:34:11 1997
+++ linux/fs/binfmt_aout.c	Mon Mar 23 10:13:52 1998
@@ -6,7 +6,6 @@
 
 #include <linux/module.h>
 
-#include <linux/fs.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
@@ -15,6 +14,8 @@
 #include <linux/errno.h>
 #include <linux/signal.h>
 #include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/stat.h>
 #include <linux/fcntl.h>
 #include <linux/ptrace.h>
@@ -401,10 +402,10 @@
 			printk(KERN_NOTICE "executable not page aligned\n");
 
 		fd = open_dentry(bprm->dentry, O_RDONLY);
-
 		if (fd < 0)
 			return fd;
-		file = current->files->fd[fd];
+		file = fcheck(fd);
+
 		if (!file->f_op || !file->f_op->mmap) {
 			sys_close(fd);
 			do_mmap(NULL, 0, ex.a_text+ex.a_data,
@@ -479,48 +480,44 @@
 do_load_aout_library(int fd)
 {
         struct file * file;
-	struct exec ex;
-	struct dentry * dentry;
 	struct inode * inode;
-	unsigned int len;
-	unsigned int bss;
-	unsigned int start_addr;
+	unsigned long bss, start_addr, len;
 	unsigned long error;
+	int retval;
+	loff_t offset = 0;
+	struct exec ex;
 
-	file = current->files->fd[fd];
-
-	if (!file || !file->f_op)
-		return -EACCES;
-
-	dentry = file->f_dentry;
-	inode = dentry->d_inode;
-
-	/* Seek into the file */
-	if (file->f_op->llseek) {
-		if ((error = file->f_op->llseek(file, 0, 0)) != 0)
-			return -ENOEXEC;
-	} else
-		file->f_pos = 0;
+	retval = -EACCES;
+	file = fget(fd);
+	if (!file)
+		goto out;
+	if (!file->f_op)
+		goto out_putf;
+	inode = file->f_dentry->d_inode;
 
+	retval = -ENOEXEC;
+	/* N.B. Save current fs? */
 	set_fs(KERNEL_DS);
-	error = file->f_op->read(file, (char *) &ex, sizeof(ex), &file->f_pos);
+	error = file->f_op->read(file, (char *) &ex, sizeof(ex), &offset);
 	set_fs(USER_DS);
 	if (error != sizeof(ex))
-		return -ENOEXEC;
+		goto out_putf;
 
 	/* We come in here for the regular a.out style of shared libraries */
 	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
 	    N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
 	    inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
-		return -ENOEXEC;
+		goto out_putf;
 	}
+
 	if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) &&
 	    (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
 		printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
-		return -ENOEXEC;
+		goto out_putf;
 	}
 
-	if (N_FLAGS(ex)) return -ENOEXEC;
+	if (N_FLAGS(ex))
+		goto out_putf;
 
 	/* For  QMAGIC, the starting address is 0x20 into the page.  We mask
 	   this off to get the starting address for the page */
@@ -532,18 +529,26 @@
 			PROT_READ | PROT_WRITE | PROT_EXEC,
 			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
 			N_TXTOFF(ex));
+	retval = error;
 	if (error != start_addr)
-		return error;
+		goto out_putf;
+
 	len = PAGE_ALIGN(ex.a_text + ex.a_data);
 	bss = ex.a_text + ex.a_data + ex.a_bss;
 	if (bss > len) {
-		error = do_mmap(NULL, start_addr + len, bss-len,
-				PROT_READ|PROT_WRITE|PROT_EXEC,
-				MAP_PRIVATE|MAP_FIXED, 0);
+		error = do_mmap(NULL, start_addr + len, bss - len,
+				PROT_READ | PROT_WRITE | PROT_EXEC,
+				MAP_PRIVATE | MAP_FIXED, 0);
+		retval = error;
 		if (error != start_addr + len)
-			return error;
+			goto out_putf;
 	}
-	return 0;
+	retval = 0;
+
+out_putf:
+	fput(file);
+out:
+	return retval;
 }
 
 static int
diff -u --recursive --new-file v2.1.90/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.90/linux/fs/buffer.c	Tue Mar 17 22:18:15 1998
+++ linux/fs/buffer.c	Wed Mar 25 13:14:26 1998
@@ -55,12 +55,9 @@
 					     number of unused buffer heads */
 
 /*
- * How large a hash table do we need?
+ * Hash table mask..
  */
-#define HASH_PAGES_ORDER	4
-#define HASH_PAGES		(1UL << HASH_PAGES_ORDER)
-#define NR_HASH			(HASH_PAGES*PAGE_SIZE/sizeof(struct buffer_head *))
-#define HASH_MASK		(NR_HASH-1)
+static unsigned long bh_hash_mask = 0;
 
 static int grow_buffers(int pri, int size);
 
@@ -421,7 +418,7 @@
 	}
 }
 
-#define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block))&HASH_MASK)
+#define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block)) & bh_hash_mask)
 #define hash(dev,block) hash_table[_hashfn(dev,block)]
 
 static inline void remove_from_hash_queue(struct buffer_head * bh)
@@ -732,7 +729,7 @@
 	needed = bdf_prm.b_un.nrefill * size;  
 
 	while ((nr_free_pages > freepages.min*2) &&
-	        BUFFER_MEM < (buffer_mem.max_percent * num_physpages / 100) &&
+	        (buffermem >> PAGE_SHIFT) * 100 < (buffer_mem.max_percent * num_physpages) &&
 		grow_buffers(GFP_BUFFER, size)) {
 		obtained += PAGE_SIZE;
 		if (obtained >= needed)
@@ -817,7 +814,6 @@
 	 */
 	while (obtained < (needed >> 1) &&
 	       nr_free_pages > freepages.min + 5 &&
-	       BUFFER_MEM < (buffer_mem.max_percent * num_physpages / 100) &&
 	       grow_buffers(GFP_BUFFER, size))
 		obtained += PAGE_SIZE;
 
@@ -1707,11 +1703,16 @@
  */
 void buffer_init(void)
 {
-	hash_table = (struct buffer_head **)
-		__get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER);
+	int order = 5;		/* Currently maximum order.. */
+	unsigned int nr_hash;
+
+	nr_hash = (1UL << order) * PAGE_SIZE / sizeof(struct buffer_head *);
+	hash_table = (struct buffer_head **) __get_free_pages(GFP_ATOMIC, order);
+	
 	if (!hash_table)
 		panic("Failed to allocate buffer hash table\n");
-	memset(hash_table,0,NR_HASH*sizeof(struct buffer_head *));
+	memset(hash_table, 0, nr_hash * sizeof(struct buffer_head *));
+	bh_hash_mask = nr_hash-1;
 
 	bh_cachep = kmem_cache_create("buffer_head",
 				      sizeof(struct buffer_head),
diff -u --recursive --new-file v2.1.90/linux/fs/exec.c linux/fs/exec.c
--- v2.1.90/linux/fs/exec.c	Tue Mar 17 22:18:15 1998
+++ linux/fs/exec.c	Mon Mar 23 10:13:52 1998
@@ -138,10 +138,9 @@
 /* N.B. Error returns must be < 0 */
 int open_dentry(struct dentry * dentry, int mode)
 {
-	int fd;
 	struct inode * inode = dentry->d_inode;
 	struct file * f;
-	int error;
+	int fd, error;
 
 	error = -EINVAL;
 	if (!inode->i_op || !inode->i_op->default_file_ops)
@@ -163,7 +162,7 @@
 			if (error)
 				goto out_filp;
 		}
-		current->files->fd[fd] = f;
+		fd_install(fd, f);
 		dget(dentry);
 	}
 	return fd;
@@ -195,18 +194,20 @@
 	retval = fd;
 	if (fd < 0)
 		goto out;
-	file = current->files->fd[fd];
+	file = fget(fd);
 	retval = -ENOEXEC;
 	if (file && file->f_dentry && file->f_op && file->f_op->read) {
 		for (fmt = formats ; fmt ; fmt = fmt->next) {
 			int (*fn)(int) = fmt->load_shlib;
 			if (!fn)
 				continue;
+			/* N.B. Should use file instead of fd */
 			retval = fn(fd);
 			if (retval != -ENOEXEC)
 				break;
 		}
 	}
+	fput(file);
 	sys_close(fd);
 out:
 	unlock_kernel();
@@ -491,7 +492,7 @@
 		unsigned long set, i;
 
 		i = j * __NFDBITS;
-		if (i >= NR_OPEN)
+		if (i >= files->max_fds)
 			break;
 		set = files->close_on_exec.fds_bits[j];
 		files->close_on_exec.fds_bits[j] = 0;
diff -u --recursive --new-file v2.1.90/linux/fs/ext2/truncate.c linux/fs/ext2/truncate.c
--- v2.1.90/linux/fs/ext2/truncate.c	Sun Sep  7 13:01:03 1997
+++ linux/fs/ext2/truncate.c	Sat Mar 21 11:05:13 1998
@@ -181,7 +181,7 @@
 	for (i = 0; i < addr_per_block; i++)
 		if (le32_to_cpu(*(ind++)))
 			break;
-	if (i >= addr_per_block)
+	if (i >= addr_per_block) {
 		if (ind_bh->b_count != 1)
 			retry = 1;
 		else {
@@ -193,6 +193,7 @@
 			bforget(ind_bh);
 			ind_bh = NULL;
 		}
+	}
 	if (IS_SYNC(inode) && ind_bh && buffer_dirty(ind_bh)) {
 		ll_rw_block (WRITE, 1, &ind_bh);
 		wait_on_buffer (ind_bh);
@@ -243,7 +244,7 @@
 	for (i = 0; i < addr_per_block; i++)
 		if (le32_to_cpu(*(dind++)))
 			break;
-	if (i >= addr_per_block)
+	if (i >= addr_per_block) {
 		if (dind_bh->b_count != 1)
 			retry = 1;
 		else {
@@ -255,6 +256,7 @@
 			bforget(dind_bh);
 			dind_bh = 0;
 		}
+	}
 	if (IS_SYNC(inode) && dind_bh && buffer_dirty(dind_bh)) {
 		ll_rw_block (WRITE, 1, &dind_bh);
 		wait_on_buffer (dind_bh);
@@ -304,7 +306,7 @@
 	for (i = 0; i < addr_per_block; i++)
 		if (le32_to_cpu(*(tind++)))
 			break;
-	if (i >= addr_per_block)
+	if (i >= addr_per_block) {
 		if (tind_bh->b_count != 1)
 			retry = 1;
 		else {
@@ -316,6 +318,7 @@
 			bforget(tind_bh);
 			tind_bh = 0;
 		}
+	}
 	if (IS_SYNC(inode) && tind_bh && buffer_dirty(tind_bh)) {
 		ll_rw_block (WRITE, 1, &tind_bh);
 		wait_on_buffer (tind_bh);
diff -u --recursive --new-file v2.1.90/linux/fs/minix/truncate.c linux/fs/minix/truncate.c
--- v2.1.90/linux/fs/minix/truncate.c	Wed Jul 16 08:05:24 1997
+++ linux/fs/minix/truncate.c	Sat Mar 21 11:05:13 1998
@@ -117,7 +117,7 @@
 	for (i = 0; i < 512; i++)
 		if (*(ind++))
 			break;
-	if (i >= 512)
+	if (i >= 512) {
 		if (ind_bh->b_count != 1)
 			retry = 1;
 		else {
@@ -125,6 +125,7 @@
 			*p = 0;
 			minix_free_block(inode->i_sb,tmp);
 		}
+	}
 	brelse(ind_bh);
 	return retry;
 }
@@ -161,7 +162,7 @@
 	for (i = 0; i < 512; i++)
 		if (*(dind++))
 			break;
-	if (i >= 512)
+	if (i >= 512) {
 		if (dind_bh->b_count != 1)
 			retry = 1;
 		else {
@@ -170,6 +171,7 @@
 			mark_inode_dirty(inode);
 			minix_free_block(inode->i_sb,tmp);
 		}
+	}
 	brelse(dind_bh);
 	return retry;
 }
@@ -279,7 +281,7 @@
 	for (i = 0; i < 256; i++)
 		if (*(ind++))
 			break;
-	if (i >= 256)
+	if (i >= 256) {
 		if (ind_bh->b_count != 1)
 			retry = 1;
 		else {
@@ -287,6 +289,7 @@
 			*p = 0;
 			minix_free_block(inode->i_sb,tmp);
 		}
+	}
 	brelse(ind_bh);
 	return retry;
 }
@@ -323,7 +326,7 @@
 	for (i = 0; i < 256; i++)
 		if (*(dind++))
 			break;
-	if (i >= 256)
+	if (i >= 256) {
 		if (dind_bh->b_count != 1)
 			retry = 1;
 		else {
@@ -332,6 +335,7 @@
 			mark_inode_dirty(inode);
 			minix_free_block(inode->i_sb,tmp);
 		}
+	}
 	brelse(dind_bh);
 	return retry;
 }
@@ -368,7 +372,7 @@
         for (i = 0; i < 256; i++)
                 if (*(tind++))
                         break;
-        if (i >= 256)
+        if (i >= 256) {
                 if (tind_bh->b_count != 1)
                         retry = 1;
                 else {
@@ -377,6 +381,7 @@
                         mark_inode_dirty(inode);
                         minix_free_block(inode->i_sb,tmp);
 		}
+	}
         brelse(tind_bh);
         return retry;
 }
diff -u --recursive --new-file v2.1.90/linux/fs/pipe.c linux/fs/pipe.c
--- v2.1.90/linux/fs/pipe.c	Tue Mar 10 10:03:34 1998
+++ linux/fs/pipe.c	Mon Mar 23 10:13:52 1998
@@ -46,11 +46,12 @@
 	if (filp->f_flags & O_NONBLOCK) {
 		if (PIPE_LOCK(*inode))
 			return -EAGAIN;
-		if (PIPE_EMPTY(*inode))
+		if (PIPE_EMPTY(*inode)) {
 			if (PIPE_WRITERS(*inode))
 				return -EAGAIN;
 			else
 				return 0;
+		}
 	} else while (PIPE_EMPTY(*inode) || PIPE_LOCK(*inode)) {
 		if (PIPE_EMPTY(*inode)) {
 			if (!PIPE_WRITERS(*inode))
@@ -485,8 +486,9 @@
 	f2->f_flags = O_WRONLY;
 	f2->f_op = &write_pipe_fops;
 	f2->f_mode = 2;
-	current->files->fd[i] = f1;
-	current->files->fd[j] = f2;
+
+	fd_install(i, f1);
+	fd_install(j, f2);
 	fd[0] = i;
 	fd[1] = j;
 	return 0;
diff -u --recursive --new-file v2.1.90/linux/fs/proc/fd.c linux/fs/proc/fd.c
--- v2.1.90/linux/fs/proc/fd.c	Tue Mar 17 22:18:15 1998
+++ linux/fs/proc/fd.c	Mon Mar 23 10:13:52 1998
@@ -6,13 +6,14 @@
  *  proc fd directory handling functions
  */
 
-#include <asm/uaccess.h>
-
 #include <linux/errno.h>
 #include <linux/sched.h>
+#include <linux/file.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 
+#include <asm/uaccess.h>
+
 static int proc_readfd(struct file *, void *, filldir_t);
 static int proc_lookupfd(struct inode *, struct dentry *);
 
@@ -65,19 +66,20 @@
 {
 	unsigned int ino, pid, fd, c;
 	struct task_struct * p;
-	struct super_block * sb;
+	struct file * file;
 	struct inode *inode;
 	const char *name;
-	int len;
+	int len, err;
 
+	err = -ENOENT;
+	if (!dir)
+		goto out;
 	ino = dir->i_ino;
 	pid = ino >> 16;
 	ino &= 0x0000ffff;
-	if (!dir)
-		return -ENOENT;
-	sb = dir->i_sb;
+
 	if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode))
-		return -ENOENT;
+		goto out;
 
 	fd = 0;
 	len = dentry->d_name.len;
@@ -85,22 +87,20 @@
 	while (len-- > 0) {
 		c = *name - '0';
 		name++;
-		if (c > 9) {
-			fd = 0xfffff;
-			break;
-		}
+		if (c > 9)
+			goto out;
 		fd *= 10;
 		fd += c;
-		if (fd & 0xffff0000) {
-			fd = 0xfffff;
-			break;
-		}
+		if (fd & 0xffff0000)
+			goto out;
 	}
+
 	read_lock(&tasklist_lock);
+	file = NULL;
 	p = find_task_by_pid(pid);
-	read_unlock(&tasklist_lock);	/* FIXME!! This should be done only after not using 'p' any more */
-	if (!pid || !p)
-		return -ENOENT;
+	if (p)
+		file = fcheck_task(p, fd);
+	read_unlock(&tasklist_lock);
 
 	/*
 	 *	File handle is invalid if it is out of range, if the process
@@ -108,60 +108,61 @@
 	 *	is NULL
 	 */
 
- 	if (fd >= NR_OPEN	||
-	    !p->files		||
-	    !p->files->fd[fd]	||
-	    !p->files->fd[fd]->f_dentry)
-		return -ENOENT;
+	if (!file || !file->f_dentry)
+		goto out;
 
+	/* N.B. What happens if fd > 255?? */
 	ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd;
-
-	inode = proc_get_inode(sb, ino, NULL);
-	if (!inode)
-		return -ENOENT;
-
-	d_add(dentry, inode);
-	return 0;
+	inode = proc_get_inode(dir->i_sb, ino, NULL);
+	if (inode) {
+		d_add(dentry, inode);
+		err = 0;
+	}
+out:
+	return err;
 }
 
 #define NUMBUF 10
 
-static int proc_readfd(struct file * filp,
-	void * dirent, filldir_t filldir)
+static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir)
 {
-	char buf[NUMBUF];
+	struct inode *inode = filp->f_dentry->d_inode;
 	struct task_struct * p, **tarrayp;
 	unsigned int fd, pid, ino;
-	unsigned long i,j;
-	struct inode *inode = filp->f_dentry->d_inode;
+	int retval;
+	char buf[NUMBUF];
 
+	retval = -EBADF;
 	if (!inode || !S_ISDIR(inode->i_mode))
-		return -EBADF;
+		goto out;
+
+	retval = 0;
 	ino = inode->i_ino;
 	pid = ino >> 16;
 	ino &= 0x0000ffff;
 	if (ino != PROC_PID_FD)
-		return 0;
+		goto out;
 
 	for (fd = filp->f_pos; fd < 2; fd++, filp->f_pos++) {
 		unsigned long ino = inode->i_ino;
 		if (fd)
 			ino = (ino & 0xffff0000) | PROC_PID_INO;
 		if (filldir(dirent, "..", fd+1, fd, ino) < 0)
-			return 0;
+			goto out;
 	}
 
 	read_lock(&tasklist_lock);
 	p = find_task_by_pid(pid);
-	read_unlock(&tasklist_lock);	/* FIXME!! This should be done only after not using 'p' any more */
-	if(!p)
-		return 0;
+	if (!p)
+		goto out_unlock;
 	tarrayp = p->tarray_ptr;
 
-	for (fd -= 2 ; fd < NR_OPEN; fd++, filp->f_pos++) {
-		if (!p->files)
-			break;
-		if (!p->files->fd[fd] || !p->files->fd[fd]->f_dentry)
+	for (fd -= 2 ; p->files && fd < p->files->max_fds; fd++, filp->f_pos++)
+	{
+		struct file * file = fcheck_task(p, fd);
+		unsigned int i,j;
+
+		if (!file || !file->f_dentry)
 			continue;
 
 		j = NUMBUF;
@@ -172,13 +173,21 @@
 			i /= 10;
 		} while (i);
 
+		/* Drop the task lock, as the filldir function may block */
+		read_unlock(&tasklist_lock);
+
 		ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd;
 		if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino) < 0)
-			break;
+			goto out;
 
+		read_lock(&tasklist_lock);
 		/* filldir() might have slept, so we must re-validate "p" */
 		if (p != *tarrayp || p->pid != pid)
 			break;
 	}
-	return 0;
+out_unlock:
+	read_unlock(&tasklist_lock);
+
+out:
+	return retval;
 }
diff -u --recursive --new-file v2.1.90/linux/fs/proc/inode.c linux/fs/proc/inode.c
--- v2.1.90/linux/fs/proc/inode.c	Tue Mar 17 22:18:15 1998
+++ linux/fs/proc/inode.c	Mon Mar 23 10:13:52 1998
@@ -10,6 +10,7 @@
 #include <linux/mm.h>
 #include <linux/string.h>
 #include <linux/stat.h>
+#include <linux/file.h>
 #include <linux/locks.h>
 #include <linux/limits.h>
 #include <linux/config.h>
@@ -250,24 +251,24 @@
 	inode->i_blocks = 0;
 	inode->i_blksize = 1024;
 	ino = inode->i_ino;
-	if (ino >= PROC_OPENPROM_FIRST && ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM)
-		return;
+	if (ino >= PROC_OPENPROM_FIRST && 
+	    ino <  PROC_OPENPROM_FIRST + PROC_NOPENPROM)
+		goto out;
 	inode->i_op = NULL;
 	inode->i_mode = 0;
 	inode->i_uid = 0;
 	inode->i_gid = 0;
 	inode->i_nlink = 1;
 	inode->i_size = 0;
-	pid = ino >> 16;
 
+	pid = ino >> 16;
 	if (!pid)
-		return;
+		goto out;
+
 	read_lock(&tasklist_lock);
 	p = find_task_by_pid(pid);
-	read_unlock(&tasklist_lock);	/* FIXME!! This should be done only after we have stopped using 'p' */
-
 	if (!p)
-		return;
+		goto out_unlock;
 
 	ino &= 0x0000ffff;
 	if (ino == PROC_PID_INO || p->dumpable) {
@@ -275,19 +276,27 @@
 		inode->i_gid = p->egid;
 	}
 	switch (ino >> 8) {
-		case PROC_PID_FD_DIR:
-			ino &= 0xff;
-			if (ino >= NR_OPEN || !p->files->fd[ino])
-				return;
-			inode->i_op = &proc_link_inode_operations;
-			inode->i_size = 64;
-			inode->i_mode = S_IFLNK;
-			if (p->files->fd[ino]->f_mode & 1)
-				inode->i_mode |= S_IRUSR | S_IXUSR;
-			if (p->files->fd[ino]->f_mode & 2)
-				inode->i_mode |= S_IWUSR | S_IXUSR;
-			return;
+		struct file * file;
+	case PROC_PID_FD_DIR:
+		ino &= 0xff;
+		file = fcheck_task(p, ino);
+		if (!file)
+			goto out_unlock;
+
+		inode->i_op = &proc_link_inode_operations;
+		inode->i_size = 64;
+		inode->i_mode = S_IFLNK;
+		if (file->f_mode & 1)
+			inode->i_mode |= S_IRUSR | S_IXUSR;
+		if (file->f_mode & 2)
+			inode->i_mode |= S_IWUSR | S_IXUSR;
 	}
+out_unlock:
+	/* Defer unlocking until we're done with the task */
+	read_unlock(&tasklist_lock);
+
+out:
+	return;
 }
 
 void proc_write_inode(struct inode * inode)
diff -u --recursive --new-file v2.1.90/linux/fs/proc/link.c linux/fs/proc/link.c
--- v2.1.90/linux/fs/proc/link.c	Tue Mar 10 10:03:34 1998
+++ linux/fs/proc/link.c	Mon Mar 23 10:13:52 1998
@@ -10,8 +10,9 @@
 
 #include <linux/errno.h>
 #include <linux/sched.h>
-#include <linux/fs.h>
 #include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 
@@ -81,53 +82,58 @@
 	pid = ino >> 16;
 	ino &= 0x0000ffff;
 
-	p = find_task_by_pid(pid);
 	result = ERR_PTR(-ENOENT);
+	read_lock(&tasklist_lock);
+	p = find_task_by_pid(pid);
 	if (!p)
-		goto out;
+		goto out_unlock;
 
 	switch (ino) {
 		case PROC_PID_CWD:
 			if (!p->fs || !p->fs->pwd)
-				break;
-			result = dget(p->fs->pwd);
-			break;
+				goto out_unlock;
+			result = p->fs->pwd;
+			goto out_dget;
 
 		case PROC_PID_ROOT:
 			if (!p->fs || !p->fs->root)
-				break;
-			result = dget(p->fs->root);
-			break;
+				goto out_unlock;
+			result = p->fs->root;
+			goto out_dget;
 
 		case PROC_PID_EXE: {
 			struct vm_area_struct * vma;
 			if (!p->mm)
-				break;
+				goto out_unlock;
 			vma = p->mm->mmap;
 			while (vma) {
-				if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file)
-					return dget(vma->vm_file->f_dentry);
-
+				if ((vma->vm_flags & VM_EXECUTABLE) && 
+				    vma->vm_file) {
+					result = vma->vm_file->f_dentry;
+					goto out_dget;
+				}
 				vma = vma->vm_next;
 			}
-			break;
+			goto out_unlock;
 		}
 		default:
 			switch (ino >> 8) {
+				struct file * file;
 			case PROC_PID_FD_DIR:
-				if (!p->files)
-					break;
 				ino &= 0xff;
-				if (ino >= NR_OPEN)
-					break;
-				if (!p->files->fd[ino])
-					break;
-				if (!p->files->fd[ino]->f_dentry)
-					break;
-				result = dget(p->files->fd[ino]->f_dentry);
-				break;
+				file = fcheck_task(p, ino);
+				if (!file || !file->f_dentry)
+					goto out_unlock;
+				result = file->f_dentry;
+				goto out_dget;
 			}
 	}
+out_dget:
+	result = dget(result);
+
+out_unlock:
+	read_unlock(&tasklist_lock);
+
 out:
 	return result;
 }
diff -u --recursive --new-file v2.1.90/linux/fs/select.c linux/fs/select.c
--- v2.1.90/linux/fs/select.c	Tue Mar 10 10:03:34 1998
+++ linux/fs/select.c	Mon Mar 23 10:13:52 1998
@@ -41,7 +41,7 @@
  * sleep/wakeup mechanism works.
  *
  * Two very simple procedures, poll_wait() and free_wait() make all the
- * work.  poll_wait() is an inline-function defined in <linux/sched.h>,
+ * work.  poll_wait() is an inline-function defined in <linux/poll.h>,
  * as all select/poll functions have to call it to add an entry to the
  * poll table.
  */
@@ -152,9 +152,8 @@
 	n = retval;
 	retval = 0;
 	for (;;) {
-		struct file ** fd = current->files->fd;
 		current->state = TASK_INTERRUPTIBLE;
-		for (i = 0 ; i < n ; i++, fd++) {
+		for (i = 0 ; i < n; i++) {
 			unsigned long bit = BIT(i);
 			unsigned long *in = MEM(i,fds->in);
 			unsigned long mask;
@@ -162,8 +161,12 @@
 
 			if (!(bit & BITS(in)))
 				continue;
-
-			file = *fd;
+			/*
+			 * The poll_wait routine will increment f_count if
+			 * the file is added to the wait table, so we don't
+			 * need to increment it now.
+			 */
+			file = fcheck(i);
 			mask = POLLNVAL;
 			if (file) {
 				mask = DEFAULT_POLLMASK;
@@ -286,23 +289,21 @@
 
 static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait)
 {
-	int count;
-	struct file ** fd = current->files->fd;
+	int count = 0;
 
-	count = 0;
 	for (;;) {
 		unsigned int j;
 		struct pollfd * fdpnt;
 
 		current->state = TASK_INTERRUPTIBLE;
 		for (fdpnt = fds, j = 0; j < nfds; j++, fdpnt++) {
-			unsigned int i;
 			unsigned int mask;
 			struct file * file;
 
 			mask = POLLNVAL;
-			i = fdpnt->fd;
-			if (i < NR_OPEN && (file = fd[i]) != NULL) {
+			/* poll_wait increments f_count if needed */
+			file = fcheck(fdpnt->fd);
+			if (file != NULL) {
 				mask = DEFAULT_POLLMASK;
 				if (file->f_op && file->f_op->poll)
 					mask = file->f_op->poll(file, wait);
@@ -326,18 +327,22 @@
 
 asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout)
 {
-	int i, count, fdcount, err;
+	int i, fdcount, err, size;
 	struct pollfd * fds, *fds1;
-	poll_table wait_table, *wait;
+	poll_table wait_table, *wait = NULL;
 
 	lock_kernel();
+	/* Do a sanity check on nfds ... */
+	err = -EINVAL;
+	if (nfds > NR_OPEN)
+		goto out;
+
 	if (timeout < 0)
 		timeout = 0x7fffffff;
 	else if (timeout)
 		timeout = ((unsigned long)timeout*HZ+999)/1000+jiffies+1;
-	err = -ENOMEM;
 
-	wait = NULL;
+	err = -ENOMEM;
 	if (timeout) {
 		struct poll_table_entry *entry;
 		entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
@@ -347,34 +352,32 @@
 		wait_table.entry = entry;
 		wait = &wait_table;
 	}
-	fds = (struct pollfd *) kmalloc(nfds*sizeof(struct pollfd), GFP_KERNEL);
-	if (!fds) {
+
+	size = nfds * sizeof(struct pollfd);
+	fds = (struct pollfd *) kmalloc(size, GFP_KERNEL);
+	if (!fds)
 		goto out;
-	}
 
 	err = -EFAULT;
-	if (copy_from_user(fds, ufds, nfds*sizeof(struct pollfd))) {
-		kfree(fds);
-		goto out;
-	}
+	if (copy_from_user(fds, ufds, size))
+		goto out_fds;
 
 	current->timeout = timeout;
-
-	count = 0;
-
 	fdcount = do_poll(nfds, fds, wait);
 	current->timeout = 0;
 
 	/* OK, now copy the revents fields back to user space. */
 	fds1 = fds;
-	for(i=0; i < (int)nfds; i++, ufds++, fds++) {
-		__put_user(fds->revents, &ufds->revents);
+	for(i=0; i < (int)nfds; i++, ufds++, fds1++) {
+		__put_user(fds1->revents, &ufds->revents);
 	}
-	kfree(fds1);
+
+	err = fdcount;
 	if (!fdcount && signal_pending(current))
 		err = -EINTR;
-	else
-		err = fdcount;
+
+out_fds:
+	kfree(fds);
 out:
 	if (wait) {
 		free_wait(&wait_table);
diff -u --recursive --new-file v2.1.90/linux/include/asm-alpha/init.h linux/include/asm-alpha/init.h
--- v2.1.90/linux/include/asm-alpha/init.h	Wed Apr 23 19:01:27 1997
+++ linux/include/asm-alpha/init.h	Wed Mar 25 22:04:36 1998
@@ -7,6 +7,12 @@
 	__arginit __init; \
 	__arginit
 
+#if __GNUC__ >= 2 && __GNUC_MINOR__ >= 8
+#define __initlocaldata  __initdata
+#else
+#define __initlocaldata
+#endif
+
 /* For assembly routines */
 #define __INIT		.section	.text.init,"ax"
 #define __FINIT		.previous
diff -u --recursive --new-file v2.1.90/linux/include/asm-alpha/unistd.h linux/include/asm-alpha/unistd.h
--- v2.1.90/linux/include/asm-alpha/unistd.h	Tue Mar 10 10:03:34 1998
+++ linux/include/asm-alpha/unistd.h	Wed Mar 25 22:04:36 1998
@@ -421,6 +421,12 @@
 	return sys_read(fd, buf, nr);
 }
 
+extern int sys_fork(void);
+static inline int fork(void)
+{
+	return sys_fork();
+}
+
 extern int __kernel_execve(char *, char **, char **, struct pt_regs *);
 static inline int execve(char * file, char ** argvp, char ** envp)
 {
@@ -450,6 +456,12 @@
 static inline pid_t wait(int * wait_stat)
 {
 	return waitpid(-1,wait_stat,0);
+}
+
+extern int sys_delete_module(const char *name);
+static inline int delete_module(const char *name)
+{
+	return sys_delete_module(name);
 }
 
 #endif
diff -u --recursive --new-file v2.1.90/linux/include/asm-arm/unistd.h linux/include/asm-arm/unistd.h
--- v2.1.90/linux/include/asm-arm/unistd.h	Tue Jan 20 16:39:43 1998
+++ linux/include/asm-arm/unistd.h	Wed Mar 25 22:04:36 1998
@@ -325,6 +325,7 @@
 static inline _syscall1(int,close,int,fd);
 static inline _syscall1(int,_exit,int,exitcode);
 static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options);
+static inline _syscall1(int,delete_module,const char *,name)
 
 static inline pid_t wait(int * wait_stat)
 {
diff -u --recursive --new-file v2.1.90/linux/include/asm-i386/string.h linux/include/asm-i386/string.h
--- v2.1.90/linux/include/asm-i386/string.h	Wed Aug 27 18:32:47 1997
+++ linux/include/asm-i386/string.h	Thu Mar 26 09:24:39 1998
@@ -114,7 +114,7 @@
 	"xorl %%eax,%%eax\n\t"
 	"jmp 3f\n"
 	"2:\tsbbl %%eax,%%eax\n\t"
-	"orb $1,%%eax\n"
+	"orb $1,%%al\n"
 	"3:"
 	:"=a" (__res):"S" (cs),"D" (ct):"si","di");
 return __res;
diff -u --recursive --new-file v2.1.90/linux/include/asm-i386/unistd.h linux/include/asm-i386/unistd.h
--- v2.1.90/linux/include/asm-i386/unistd.h	Thu Feb 12 20:56:12 1998
+++ linux/include/asm-i386/unistd.h	Wed Mar 25 22:04:36 1998
@@ -296,6 +296,7 @@
 static inline _syscall1(int,close,int,fd)
 static inline _syscall1(int,_exit,int,exitcode)
 static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+static inline _syscall1(int,delete_module,const char *,name)
 
 static inline pid_t wait(int * wait_stat)
 {
diff -u --recursive --new-file v2.1.90/linux/include/asm-m68k/amifd.h linux/include/asm-m68k/amifd.h
--- v2.1.90/linux/include/asm-m68k/amifd.h	Sat Mar  2 17:59:14 1996
+++ linux/include/asm-m68k/amifd.h	Sat Mar 21 11:04:04 1998
@@ -5,21 +5,23 @@
 
 #include <linux/fd.h>
 
-#define FD_MAX_UNITS    4
+#define FD_MAX_UNITS    4	/* Max. Number of drives */
+#define FLOPPY_MAX_SECTORS	22	/* Max. Number of sectors per track */
+
+#ifndef ASSEMBLER
 
 struct fd_data_type {
     char *name;			/* description of data type */
     int sects;			/* sectors per track */
 #ifdef __STDC__
-    int (*read_fkt)(int, unsigned char *, unsigned long, int);
-    void (*write_fkt)(int, unsigned long, unsigned char *, int);
+    int (*read_fkt)(int);
+    void (*write_fkt)(int);
 #else
     int (*read_fkt)();		/* read whole track */
     void (*write_fkt)();		/* write whole track */
 #endif
 };
 
-#ifndef ASSEMBLER
 /*
 ** Floppy type descriptions
 */
@@ -43,13 +45,15 @@
     struct fd_drive_type *type;	/* type of floppy for this unit */
     struct fd_data_type *dtype;	/* type of floppy for this unit */
     int track;			/* current track (-1 == unknown) */
+    unsigned char *trackbuf;    /* current track (kmaloc()'d */
 
     int blocks;			/* total # blocks on disk */
-    int sects;			/* number of sectors per track */
 
+    int changed;		/* true when not known */
     int disk;			/* disk in drive (-1 == unknown) */
     int motor;			/* true when motor is at speed */
     int busy;			/* true when drive is active */
+    int dirty;			/* true when trackbuf is not on disk */
     int status;			/* current error code for unit */
 };
 #endif
diff -u --recursive --new-file v2.1.90/linux/include/asm-m68k/ide.h linux/include/asm-m68k/ide.h
--- v2.1.90/linux/include/asm-m68k/ide.h	Tue Feb 17 13:12:49 1998
+++ linux/include/asm-m68k/ide.h	Sat Mar 21 11:04:04 1998
@@ -406,7 +406,7 @@
 #endif /* CONFIG_ATARI */
 }
 
-#define ide_ack_intr(hwif) (hwif)->ack_intr((hwif))
+#define ide_ack_intr(hwif)	((hwif)->ack_intr ? (hwif)->ack_intr(hwif) : 1)
 
 /*
  * On the Atari, we sometimes can't enable interrupts:
diff -u --recursive --new-file v2.1.90/linux/include/asm-m68k/setup.h linux/include/asm-m68k/setup.h
--- v2.1.90/linux/include/asm-m68k/setup.h	Tue Feb 17 13:12:49 1998
+++ linux/include/asm-m68k/setup.h	Sat Mar 21 11:04:04 1998
@@ -131,7 +131,7 @@
      *
      *      CPU_68020 == MMU_68851
      *      CPU_68030 == MMU_68030
-     *      CPU_68040 == FPU_68040 == MMU_68040 (not strictly, think of 68LC040!)
+     *      CPU_68040 == FPU_68040 == MMU_68040
      *      CPU_68060 == FPU_68060 == MMU_68060
      */
 
diff -u --recursive --new-file v2.1.90/linux/include/asm-m68k/signal.h linux/include/asm-m68k/signal.h
--- v2.1.90/linux/include/asm-m68k/signal.h	Tue Feb 17 13:12:49 1998
+++ linux/include/asm-m68k/signal.h	Sat Mar 21 11:04:04 1998
@@ -128,6 +128,13 @@
 	void (*sa_restorer)(void);
 };
 
+struct sigaction {
+	__sighandler_t sa_handler;
+	unsigned long sa_flags;
+	void (*sa_restorer)(void);
+	sigset_t sa_mask;		/* mask last for extensibility */
+};
+
 struct k_sigaction {
 	struct sigaction sa;
 };
@@ -173,9 +180,9 @@
 
 extern __inline__ int __gen_sigismember(sigset_t *set, int _sig)
 {
-	char ret;
-	__asm__("bftst %1{%2,#1}\n\t sne %0"
-		: "=rm"(ret) : "m"(*set), "id"((_sig-1) ^ 31) : "cc");
+	int ret;
+	__asm__("bfextu %1{%2,#1},%0"
+		: "=d"(ret) : "m"(*set), "id"((_sig-1) ^ 31));
 	return ret;
 }
 
diff -u --recursive --new-file v2.1.90/linux/include/asm-m68k/unistd.h linux/include/asm-m68k/unistd.h
--- v2.1.90/linux/include/asm-m68k/unistd.h	Tue Feb 17 13:12:49 1998
+++ linux/include/asm-m68k/unistd.h	Wed Mar 25 22:04:36 1998
@@ -316,6 +316,7 @@
 static inline _syscall1(int,close,int,fd)
 static inline _syscall1(int,_exit,int,exitcode)
 static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+static inline _syscall1(int,delete_module,const char *,name)
 
 /*
  * This is the mechanism for creating a new kernel thread.
diff -u --recursive --new-file v2.1.90/linux/include/asm-mips/unistd.h linux/include/asm-mips/unistd.h
--- v2.1.90/linux/include/asm-mips/unistd.h	Mon Sep 22 14:55:59 1997
+++ linux/include/asm-mips/unistd.h	Wed Mar 25 22:04:37 1998
@@ -1410,6 +1410,7 @@
 static inline _syscall1(int,close,int,fd)
 static inline _syscall1(int,_exit,int,exitcode)
 static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+static inline _syscall1(int,delete_module,const char *,name)
 
 static inline pid_t wait(int * wait_stat)
 {
diff -u --recursive --new-file v2.1.90/linux/include/asm-sparc/unistd.h linux/include/asm-sparc/unistd.h
--- v2.1.90/linux/include/asm-sparc/unistd.h	Mon Jan 12 15:15:54 1998
+++ linux/include/asm-sparc/unistd.h	Wed Mar 25 22:04:37 1998
@@ -438,6 +438,7 @@
 static __inline__ _syscall1(int,close,int,fd)
 static __inline__ _syscall1(int,_exit,int,exitcode)
 static __inline__ _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+static __inline__ _syscall1(int,delete_module,const char *,name)
 
 static __inline__ pid_t wait(int * wait_stat)
 {
diff -u --recursive --new-file v2.1.90/linux/include/asm-sparc64/unistd.h linux/include/asm-sparc64/unistd.h
--- v2.1.90/linux/include/asm-sparc64/unistd.h	Mon Jan 12 15:15:58 1998
+++ linux/include/asm-sparc64/unistd.h	Wed Mar 25 22:04:37 1998
@@ -426,6 +426,7 @@
 static __inline__ _syscall1(int,close,int,fd)
 static __inline__ _syscall1(int,_exit,int,exitcode)
 static __inline__ _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+static __inline__ _syscall1(int,delete_module,const char *,name)
 
 static __inline__ pid_t wait(int * wait_stat)
 {
diff -u --recursive --new-file v2.1.90/linux/include/linux/fb.h linux/include/linux/fb.h
--- v2.1.90/linux/include/linux/fb.h	Tue May 13 22:41:18 1997
+++ linux/include/linux/fb.h	Sat Mar 21 11:12:49 1998
@@ -1,16 +1,31 @@
 #ifndef _LINUX_FB_H
 #define _LINUX_FB_H
 
+#include <asm/types.h>
+
 /* Definitions of frame buffers						*/
 
+#define FB_MAJOR	29
+
+#define FB_MODES_SHIFT		5	/* 32 modes per framebuffer */
+#define FB_NUM_MINORS		256	/* 256 Minors               */
+#define FB_MAX			(FB_NUM_MINORS / (1 << FB_MODES_SHIFT))
+#define GET_FB_IDX(node)	(MINOR(node) >> FB_MODES_SHIFT)
+
 /* ioctls
    0x46 is 'F'								*/
-#define FBIOGET_VSCREENINFO 	0x4600
-#define FBIOPUT_VSCREENINFO 	0x4601
-#define FBIOGET_FSCREENINFO 	0x4602
+#define FBIOGET_VSCREENINFO	0x4600
+#define FBIOPUT_VSCREENINFO	0x4601
+#define FBIOGET_FSCREENINFO	0x4602
 #define FBIOGETCMAP		0x4604
 #define FBIOPUTCMAP		0x4605
-#define FBIOPAN_DISPLAY         0x4606
+#define FBIOPAN_DISPLAY		0x4606
+/* 0x4607-0x460B are defined below */
+/* #define FBIOGET_MONITORSPEC	0x460C */
+/* #define FBIOPUT_MONITORSPEC	0x460D */
+/* #define FBIOSWITCH_MONIBIT	0x460E */
+#define FBIOGET_CON2FBMAP	0x460F
+#define FBIOPUT_CON2FBMAP	0x4610
 
 #define FB_TYPE_PACKED_PIXELS		0	/* Packed Pixels	*/
 #define FB_TYPE_PLANES			1	/* Non interleaved planes */
@@ -26,22 +41,25 @@
 
 struct fb_fix_screeninfo {
 	char id[16];			/* identification string eg "TT Builtin" */
-	unsigned long smem_start;	/* Start of frame buffer mem */
-	unsigned long smem_len;		/* Length of frame buffer mem */	
-	int type;			/* see FB_TYPE_* 		*/
-	int type_aux;			/* Interleave for interleaved Planes */
-	int visual;			/* see FB_VISUAL_*  		*/ 
-	u_short xpanstep;               /* zero if no hardware panning  */
-        u_short ypanstep;               /* zero if no hardware panning  */
-        u_short ywrapstep;              /* zero if no hardware ywrap    */
-        u_long line_length;             /* length of a line in bytes    */
-        short reserved[9];              /* Reserved for future compatibility */
+	char *smem_start;		/* Start of frame buffer mem */
+	__u32 smem_len;			/* Length of frame buffer mem */
+	__u32 type;			/* see FB_TYPE_*		*/
+	__u32 type_aux;			/* Interleave for interleaved Planes */
+	__u32 visual;			/* see FB_VISUAL_*		*/ 
+	__u16 xpanstep;			/* zero if no hardware panning  */
+	__u16 ypanstep;			/* zero if no hardware panning  */
+	__u16 ywrapstep;		/* zero if no hardware ywrap    */
+	__u32 line_length;		/* length of a line in bytes    */
+	unsigned char *mmio_start;	/* Start of Memory Mapped I/O   */
+	__u32 mmio_len;			/* Length of Memory Mapped I/O  */
+	__u32 accel;			/* Type of acceleration available */
+	__u16 reserved[3];		/* Reserved for future compatibility */
 };
 
 struct fb_bitfield {
-	int offset;			/* beginning of bitfield	*/
-	int length;			/* length of bitfield		*/
-	int msb_right;			/* != 0 : Most significant bit is */ 
+	__u32 offset;			/* beginning of bitfield	*/
+	__u32 length;			/* length of bitfield		*/
+	__u32 msb_right;		/* != 0 : Most significant bit is */ 
 					/* right */ 
 };
 
@@ -58,8 +76,12 @@
 #define FB_ACCEL_NONE		0	/* no hardware accelerator	*/
 #define FB_ACCEL_ATARIBLITT	1	/* Atari Blitter		*/
 #define FB_ACCEL_AMIGABLITT	2	/* Amiga Blitter                */
-#define FB_ACCEL_CYBERVISION	3	/* Cybervision64 (S3 Trio64)    */
-#define FB_ACCEL_RETINAZ3	4	/* RetinaZ3 (NCR77C32BLT)       */
+#define FB_ACCEL_S3TRIO64	3	/* Cybervision64 (S3 Trio64)    */
+#define FB_ACCEL_NCR77C32BLT	4	/* RetinaZ3 (NCR77C32BLT)       */
+#define FB_ACCEL_S3VIRGE	5	/* Cybervision64/3D (S3 ViRGE)	*/
+#define FB_ACCEL_MACH64		6	/* ATI Mach 64			*/
+#define FB_ACCEL_TGA		7	/* DEC 21030 TGA		*/
+#define FB_ACCEL_ATY		8	/* atyfb (ATI Mach64)		*/
 
 #define FB_SYNC_HOR_HIGH_ACT	1	/* horizontal sync high active	*/
 #define FB_SYNC_VERT_HIGH_ACT	2	/* vertical sync high active	*/
@@ -68,9 +90,10 @@
 #define FB_SYNC_BROADCAST	16	/* broadcast video timings      */
 					/* vtotal = 144d/288n/576i => PAL  */
 					/* vtotal = 121d/242n/484i => NTSC */
+#define FB_SYNC_ON_GREEN	32	/* sync on green */
 
 #define FB_VMODE_NONINTERLACED  0	/* non interlaced */
-#define FB_VMODE_INTERLACED 	1	/* interlaced	*/
+#define FB_VMODE_INTERLACED	1	/* interlaced	*/
 #define FB_VMODE_DOUBLE		2	/* double scan */
 #define FB_VMODE_MASK		255
 
@@ -79,77 +102,107 @@
 #define FB_VMODE_CONUPDATE	512	/* don't update x/yoffset	*/
 
 struct fb_var_screeninfo {
-	int xres;			/* visible resolution		*/
-	int yres;
-	int xres_virtual;		/* virtual resolution		*/
-	int yres_virtual;
-	int xoffset;			/* offset from virtual to visible */
-	int yoffset;			/* resolution			*/
+	__u32 xres;			/* visible resolution		*/
+	__u32 yres;
+	__u32 xres_virtual;		/* virtual resolution		*/
+	__u32 yres_virtual;
+	__u32 xoffset;			/* offset from virtual to visible */
+	__u32 yoffset;			/* resolution			*/
 
-	int bits_per_pixel;		/* guess what 			*/
-	int grayscale;			/* != 0 Graylevels instead of colors */
+	__u32 bits_per_pixel;		/* guess what			*/
+	__u32 grayscale;		/* != 0 Graylevels instead of colors */
 
 	struct fb_bitfield red;		/* bitfield in fb mem if true color, */
 	struct fb_bitfield green;	/* else only length is significant */
 	struct fb_bitfield blue;
 	struct fb_bitfield transp;	/* transparency			*/	
 
-	int nonstd;			/* != 0 Non standard pixel format */
+	__u32 nonstd;			/* != 0 Non standard pixel format */
 
-	int activate;			/* see FB_ACTIVATE_* 		*/
+	__u32 activate;			/* see FB_ACTIVATE_*		*/
 
-	int height;			/* height of picture in mm    */
-	int width;			/* width of picture in mm     */
+	__u32 height;			/* height of picture in mm    */
+	__u32 width;			/* width of picture in mm     */
 
-	int accel;			/* see FB_ACCEL_*		*/
+	__u32 accel;			/* see FB_ACCEL_*		*/
 
 	/* Timing: All values in pixclocks, except pixclock (of course) */
-	unsigned long pixclock;		/* pixel clock in ps (pico seconds) */
-	unsigned long left_margin;	/* time from sync to picture	*/
-	unsigned long right_margin;	/* time from picture to sync	*/
-	unsigned long upper_margin;	/* time from sync to picture	*/
-	unsigned long lower_margin;
-	unsigned long hsync_len;	/* length of horizontal sync	*/
-	unsigned long vsync_len;	/* length of vertical sync	*/
-	int sync;			/* see FB_SYNC_*		*/
-	int vmode;			/* see FB_VMODE_*		*/
-	int reserved[6];		/* Reserved for future compatibility */
+	__u32 pixclock;			/* pixel clock in ps (pico seconds) */
+	__u32 left_margin;		/* time from sync to picture	*/
+	__u32 right_margin;		/* time from picture to sync	*/
+	__u32 upper_margin;		/* time from sync to picture	*/
+	__u32 lower_margin;
+	__u32 hsync_len;		/* length of horizontal sync	*/
+	__u32 vsync_len;		/* length of vertical sync	*/
+	__u32 sync;			/* see FB_SYNC_*		*/
+	__u32 vmode;			/* see FB_VMODE_*		*/
+	__u32 reserved[6];		/* Reserved for future compatibility */
 };
 
 struct fb_cmap {
-	int start;			/* First entry	*/
-	int len;			/* Number of entries */
-	unsigned short *red;		/* Red values	*/
-	unsigned short *green;
-	unsigned short *blue;
-	unsigned short *transp;		/* transparency, can be NULL */
+	__u32 start;			/* First entry	*/
+	__u32 len;			/* Number of entries */
+	__u16 *red;			/* Red values	*/
+	__u16 *green;
+	__u16 *blue;
+	__u16 *transp;			/* transparency, can be NULL */
+};
+
+struct fb_con2fbmap {
+	__u32 console;
+	__u32 framebuffer;
+};
+
+struct fb_monspecs {
+	unsigned hfmin : 20;		/* hfreq lower limit (Hz) */
+	unsigned hfmax : 20; 		/* hfreq upper limit (Hz) */
+	unsigned vfmin : 10;		/* vfreq lower limit (Hz) */
+	unsigned vfmax : 10;		/* vfreq upper limit (Hz) */
+	unsigned dpms : 1;		/* supports DPMS */
 };
 
 #ifdef __KERNEL__
 
 #include <linux/fs.h>
 
+
+struct fb_info;
+struct fb_info_gen;
+
+
+    /*
+     *  Frame buffer operations
+     */
+
 struct fb_ops {
-	/* get non settable parameters	*/
-	int (*fb_get_fix) (struct fb_fix_screeninfo *, int); 
-	/* get settable parameters	*/
-	int (*fb_get_var) (struct fb_var_screeninfo *, int);		
-	/* set settable parameters	*/
-	int (*fb_set_var) (struct fb_var_screeninfo *, int);		
-	/* get colormap			*/
-	int (*fb_get_cmap) (struct fb_cmap *, int, int);
-	/* set colormap			*/
-	int (*fb_set_cmap) (struct fb_cmap *, int, int);
-	/* pan display                   */
-        int (*fb_pan_display) (struct fb_var_screeninfo *, int);
-        /* perform fb specific ioctl	*/
-	int (*fb_ioctl)(struct inode *, struct file *, unsigned int,
-			unsigned long, int);
-};
-
-int register_framebuffer(char *, int *, struct fb_ops *, int, 
-			 struct fb_var_screeninfo *);
-int unregister_framebuffer(int);
+    /* open/release and usage marking */
+    int (*fb_open)(struct fb_info *info);
+    int (*fb_release)(struct fb_info *info);
+    /* get non settable parameters */
+    int (*fb_get_fix)(struct fb_fix_screeninfo *fix, int con,
+		      struct fb_info *info); 
+    /* get settable parameters */
+    int (*fb_get_var)(struct fb_var_screeninfo *var, int con,
+		      struct fb_info *info);		
+    /* set settable parameters */
+    int (*fb_set_var)(struct fb_var_screeninfo *var, int con,
+		      struct fb_info *info);		
+    /* get colormap */
+    int (*fb_get_cmap)(struct fb_cmap *cmap, int kspc, int con,
+		       struct fb_info *info);
+    /* set colormap */
+    int (*fb_set_cmap)(struct fb_cmap *cmap, int kspc, int con,
+		       struct fb_info *info);
+    /* pan display */
+    int (*fb_pan_display)(struct fb_var_screeninfo *var, int con,
+			  struct fb_info *info);
+    /* switch between text and graphics mode */
+    int (*fb_set_mode)(int mode, struct fb_info *info);
+    /* perform fb specific ioctl */
+    int (*fb_ioctl)(struct inode *inode, struct file *file, unsigned int cmd,
+		    unsigned long arg, int con, struct fb_info *info);
+};
+
 
    /*
     *    This is the interface between the low-level console driver and the
@@ -162,7 +215,7 @@
    struct fb_var_screeninfo var;    /* variable infos. yoffset and vmode */
                                     /* are updated by fbcon.c */
    struct fb_cmap cmap;             /* colormap */
-   u_char *screen_base;             /* pointer to top of virtual screen */    
+   char *screen_base;               /* pointer to top of virtual screen */    
    int visual;
    int type;                        /* see FB_TYPE_* */
    int type_aux;                    /* Interleave for interleaved Planes */
@@ -181,6 +234,7 @@
    /* Filled in by the low-level console driver */
 
    struct vc_data *conp;            /* pointer to console data */
+   struct fb_info *fb_info;         /* frame buffer for this console */
    int vrows;                       /* number of virtual rows */
    int cursor_x;                    /* current cursor position */
    int cursor_y;
@@ -199,22 +253,109 @@
 
 
 struct fb_info {
-   char modename[40];               /* at boottime detected video mode */
-   struct display *disp;            /* pointer to display variables */
-   char fontname[40];               /* default font name */
-   int (*changevar)(int);           /* tell console var has changed */
-   int (*switch_con)(int);          /* tell fb to switch consoles */
-   int (*updatevar)(int);           /* tell fb to update the vars */
-   void (*blank)(int);              /* tell fb to (un)blank the screen */
-   int (*setcmap)(struct fb_cmap *, int); /* tell fb to set the colormap */
-};
+   char modename[40];			/* default video mode */
+   int node;
+   struct fb_ops *fbops;
+   struct fb_monspecs monspecs;
+   struct display *disp;		/* initial display variable */
+   char fontname[40];			/* default font name */
+   int (*changevar)(int);		/* tell console var has changed */
+   int (*switch_con)(int, struct fb_info*);
+					/* tell fb to switch consoles */
+   int (*updatevar)(int, struct fb_info*);
+					/* tell fb to update the vars */
+   void (*blank)(int, struct fb_info*);	/* tell fb to (un)blank the screen */
+
+   /* From here on everything is device dependent */
+};
+
+
+    /*
+     *  This structure abstracts from the underlying hardware. It is not
+     *  mandatory but used by the `generic' frame buffer operations.
+     *  Read drivers/video/skeletonfb.c for more information.
+     */
+
+struct fbgen_hwswitch {
+    void (*detect)(void);
+    int (*encode_fix)(struct fb_fix_screeninfo *fix, const void *par,
+		      struct fb_info_gen *info);
+    int (*decode_var)(const struct fb_var_screeninfo *var, void *par,
+		      struct fb_info_gen *info);
+    int (*encode_var)(struct fb_var_screeninfo *var, const void *par,
+		      struct fb_info_gen *info);
+    void (*get_par)(void *par, struct fb_info_gen *info);
+    void (*set_par)(const void *par, struct fb_info_gen *info);
+    int (*getcolreg)(unsigned regno, unsigned *red, unsigned *green,
+		     unsigned *blue, unsigned *transp, struct fb_info *info);
+    int (*setcolreg)(unsigned regno, unsigned red, unsigned green,
+		     unsigned blue, unsigned transp, struct fb_info *info);
+    int (*pan_display)(const struct fb_var_screeninfo *var,
+		       struct fb_info_gen *info);
+    int (*blank)(int blank_mode, struct fb_info_gen *info);
+    struct display_switch *(*get_dispsw)(const void *par,
+					 struct fb_info_gen *info);
+};
+
+struct fb_info_gen {
+    struct fb_info info;
+
+    /* Entries for a generic frame buffer device */
+    /* Yes, this starts looking like C++ */
+    u_int parsize;
+    struct fbgen_hwswitch *fbhw;
+
+   /* From here on everything is device dependent */
+};
+
+
+struct fb_videomode {
+    const char *name;
+    struct fb_var_screeninfo var;
+};
+
+
+/* prototypes */
+typedef unsigned long fb_init_func(unsigned long);
+
+/* drivers/char/fbmem.c */
+extern int register_framebuffer(struct fb_info *fb_info);
+extern int unregister_framebuffer(const struct fb_info *fb_info);
+extern unsigned long probe_framebuffers(unsigned long kmem_start);
+extern int fbmon_valid_timings(u_int pixclock, u_int htotal, u_int vtotal,
+			       const struct fb_info *fb_info);
+extern int fbmon_dpms(const struct fb_info *fb_info);
+
+
+extern int num_registered_fb;
+extern struct fb_info *registered_fb[FB_MAX];
+extern char con2fb_map[MAX_NR_CONSOLES];
+
+/* drivers/video/fbcon.c */
+extern struct display fb_display[MAX_NR_CONSOLES];
+
+/* drivers/video/fbcmap.c */
+extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp);
+extern void fb_copy_cmap(struct fb_cmap *from, struct fb_cmap *to,
+			 int fsfromto);
+extern int fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var,
+		       int kspc, int (*getcolreg)(u_int, u_int *, u_int *,
+						  u_int *, u_int *,
+						  struct fb_info *),
+		       struct fb_info *fb_info);
+extern int fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var,
+		       int kspc, int (*setcolreg)(u_int, u_int, u_int, u_int,
+						  u_int, struct fb_info *),
+		       struct fb_info *fb_info);
+extern struct fb_cmap *fb_default_cmap(int len);
+extern void fb_invert_cmaps(void);
 
 #endif /* __KERNEL__ */
 
 #if 1
 
-#define FBCMD_GET_CURRENTPAR	    0xDEAD0005
-#define FBCMD_SET_CURRENTPAR        0xDEAD8005
+#define FBCMD_GET_CURRENTPAR	0xDEAD0005
+#define FBCMD_SET_CURRENTPAR	0xDEAD8005
 
 #endif
 
@@ -233,26 +374,26 @@
 
 
 struct fb_fix_cursorinfo {
-	u_short crsr_width;		/* width and height of the cursor in */
-	u_short crsr_height;		/* pixels (zero if no cursor)	*/
-	u_short crsr_xsize;		/* cursor size in display pixels */
-	u_short crsr_ysize;
-	u_short crsr_color1;		/* colormap entry for cursor color1 */
-	u_short crsr_color2;		/* colormap entry for cursor color2 */
+	__u16 crsr_width;		/* width and height of the cursor in */
+	__u16 crsr_height;		/* pixels (zero if no cursor)	*/
+	__u16 crsr_xsize;		/* cursor size in display pixels */
+	__u16 crsr_ysize;
+	__u16 crsr_color1;		/* colormap entry for cursor color1 */
+	__u16 crsr_color2;		/* colormap entry for cursor color2 */
 };
 
 struct fb_var_cursorinfo {
-        u_short width;
-        u_short height;
-        u_short xspot;
-        u_short yspot;
-        u_char data[1];                 /* field with [height][width]        */
+	__u16 width;
+	__u16 height;
+	__u16 xspot;
+	__u16 yspot;
+	__u8 data[1];			/* field with [height][width]        */
 };
 
 struct fb_cursorstate {
-	short xoffset;
-	short yoffset;
-	u_short mode;
+	__s16 xoffset;
+	__s16 yoffset;
+	__u16 mode;
 };
 
 #define FB_CURSOR_OFF		0
@@ -267,24 +408,23 @@
 #define FB_LINE_FILLED	4
 
 struct fb_line {
-	int start_x;
-	int start_y;
-	int end_x;
-	int end_y;
-	int color;
-	int option;
+	__s32 start_x;
+	__s32 start_y;
+	__s32 end_x;
+	__s32 end_y;
+	__u32 color;
+	__u32 option;
 };
 
 struct fb_move {
-	int src_x;
-	int src_y;
-	int dest_x;
-	int dest_y;
-	int height;
-	int width;
+	__s32 src_x;
+	__s32 src_y;
+	__s32 dest_x;
+	__s32 dest_y;
+	__u32 height;
+	__u32 width;
 };
 
 #endif /* Preliminary */
-
 
 #endif /* _LINUX_FB_H */
diff -u --recursive --new-file v2.1.90/linux/include/linux/file.h linux/include/linux/file.h
--- v2.1.90/linux/include/linux/file.h	Tue Mar 17 22:18:15 1998
+++ linux/include/linux/file.h	Mon Mar 23 10:13:52 1998
@@ -9,13 +9,26 @@
 extern void insert_file_free(struct file *file);
 
 /*
+ * Check whether the specified task has the fd open. Since the task
+ * may not have a files_struct, we must test for p->files != NULL.
+ */
+extern inline struct file * fcheck_task(struct task_struct *p, unsigned int fd)
+{
+	struct file * file = NULL;
+
+	if (p->files && fd < p->files->max_fds)
+		file = p->files->fd[fd];
+	return file;
+}
+
+/*
  * Check whether the specified fd has an open file.
  */
 extern inline struct file * fcheck(unsigned int fd)
 {
 	struct file * file = NULL;
 
-	if (fd < NR_OPEN)
+	if (fd < current->files->max_fds)
 		file = current->files->fd[fd];
 	return file;
 }
diff -u --recursive --new-file v2.1.90/linux/include/linux/ip_fw.h linux/include/linux/ip_fw.h
--- v2.1.90/linux/include/linux/ip_fw.h	Fri Feb  6 15:35:11 1998
+++ linux/include/linux/ip_fw.h	Thu Mar 26 14:57:21 1998
@@ -52,11 +52,13 @@
 #ifndef _IP_FW_H
 #define _IP_FW_H
 
+#ifdef __KERNEL__
 #include <linux/icmp.h>
 #include <linux/in.h>
 #include <linux/ip.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
+#endif
 
 struct ip_fw 
 {
diff -u --recursive --new-file v2.1.90/linux/include/linux/ipv6_route.h linux/include/linux/ipv6_route.h
--- v2.1.90/linux/include/linux/ipv6_route.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/ipv6_route.h	Mon Mar 23 16:48:25 1998
@@ -28,6 +28,7 @@
 
 #define RTF_LINKRT	0x00100000	/* link specific - device match	*/
 #define RTF_NONEXTHOP	0x00200000	/* route with no nexthop	*/
+#define RTF_EXPIRES	0x00400000
 
 #define RTF_CACHE	0x01000000	/* cache entry			*/
 #define RTF_FLOW	0x02000000	/* flow significant route	*/
diff -u --recursive --new-file v2.1.90/linux/include/linux/miscdevice.h linux/include/linux/miscdevice.h
--- v2.1.90/linux/include/linux/miscdevice.h	Mon Jan 12 15:12:42 1998
+++ linux/include/linux/miscdevice.h	Sat Mar 21 11:06:17 1998
@@ -8,7 +8,9 @@
 #define AMIGAMOUSE_MINOR 4
 #define ATARIMOUSE_MINOR 5
 #define SUN_MOUSE_MINOR 6
+#define APOLLO_MOUSE_MINOR 7
 #define PC110PAD_MINOR 9
+#define MAC_MOUSE_MINOR 10
 #define WATCHDOG_MINOR		130	/* Watchdog timer     */
 #define TEMP_MINOR		131	/* Temperature Sensor */
 #define RTC_MINOR 135
diff -u --recursive --new-file v2.1.90/linux/include/linux/mm.h linux/include/linux/mm.h
--- v2.1.90/linux/include/linux/mm.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/mm.h	Thu Mar 26 14:54:53 1998
@@ -251,7 +251,23 @@
 }
 
 /* memory.c & swap.c*/
-extern int free_memory_available(void);
+
+/*
+ * This traverses "nr" memory size lists,
+ * and returns true if there is enough memory.
+ *
+ * For example, we want to keep on waking up
+ * kswapd every once in a while until the highest
+ * memory order has an entry (ie nr == 0), but
+ * we want to do it in the background.
+ *
+ * We want to do it in the foreground only if
+ * none of the three highest lists have enough
+ * memory. Random number.
+ */
+extern int free_memory_available(int nr);
+#define kswapd_continue()	(!free_memory_available(3))
+#define kswapd_wakeup()		(!free_memory_available(0))
 
 #define free_page(addr) free_pages((addr),0)
 extern void FASTCALL(free_pages(unsigned long addr, unsigned long order));
diff -u --recursive --new-file v2.1.90/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.1.90/linux/include/linux/netdevice.h	Tue Mar 17 22:18:15 1998
+++ linux/include/linux/netdevice.h	Thu Mar 26 14:55:36 1998
@@ -355,7 +355,6 @@
 #define HAVE_NETIF_RX 1
 extern void		netif_rx(struct sk_buff *skb);
 extern void		net_bh(void);
-extern void		dev_tint(struct device *dev);
 extern int		dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
 extern int		dev_ioctl(unsigned int cmd, void *);
 extern int		dev_change_flags(struct device *, unsigned);
diff -u --recursive --new-file v2.1.90/linux/include/linux/nubus.h linux/include/linux/nubus.h
--- v2.1.90/linux/include/linux/nubus.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nubus.h	Sat Mar 21 11:06:51 1998
@@ -0,0 +1,96 @@
+
+struct nubus_slot
+{
+	int slot_flags;
+#define NUBUS_DEVICE_PRESENT	1
+#define NUBUS_DEVICE_ACTIVE	2
+#define NUBUS_DEVICE_IRQ	4
+	__u32 slot_directory;
+	__u32 slot_dlength;
+	__u32 slot_crc;
+	__u8  slot_rev;
+	__u8  slot_format;
+	__u8  slot_lanes;
+	/*
+	 *	Stuff we pulled from the directory
+	 */
+	__u32  slot_dirbase;
+	__u32  slot_thisdir;
+	char   slot_vendor[64];
+	char   slot_cardname[64];
+};
+
+struct nbnamevec 
+{
+	char *name;
+	int id;
+};
+
+struct nubus_dir
+{
+	unsigned char *base;
+	int length;
+	int count;
+	int mask;
+};
+
+struct nubus_dirent
+{
+	unsigned char type;
+	int value;	/* Actually 24bits used */
+	int mask;
+	int base;	/* For dirptr function */
+};
+
+struct nubus_type
+{
+	__u16 category;
+	__u16 type;
+	__u16 DrHW;
+	__u16 DrSW;
+};
+
+#define NUBUS_CAT_BOARD			0x0001
+#define NUBUS_CAT_DISPLAY		0x0003
+#define NUBUS_CAT_NETWORK		0x0004
+#define NUBUS_CAT_COMMUNICATIONS	0x0006
+#define NUBUS_CAT_FONT			0x0009
+#define NUBUS_CAT_CPU			0x000A
+
+#define RES_ID_TYPE		0x0001
+#define RES_ID_NAME		0x0002
+#define RES_ID_BOARD_DIR	0x0001
+#define RES_ID_FLAGS		0x0007
+
+struct nubus_device_specifier
+{
+	int (*setup)(struct nubus_device_specifier *, int slot, struct nubus_type *);
+	struct nubus_device_specifier *next;
+};
+
+
+extern void register_nubus_device(struct nubus_device_specifier *nb);
+extern void unregister_nubus_device(struct nubus_device_specifier *nb);
+
+extern struct nubus_dir *nubus_openrootdir(int slot);
+extern struct nubus_dir *nubus_opensubdir(struct nubus_dirent *d);
+extern void nubus_closedir(struct nubus_dir *);
+extern struct nubus_dirent *nubus_readdir(struct nubus_dir *);
+extern unsigned char *nubus_dirptr(struct nubus_dirent *d);
+extern void nubus_strncpy(int slot, void *to, unsigned char *p, int len);
+extern void nubus_memcpy(int slot, void *to, unsigned char *p, int len);
+extern void nubus_init(void);
+extern void nubus_sweep_video(void);
+extern int nubus_ethernet_addr(int slot, unsigned char *addr);
+
+extern __inline void *nubus_slot_addr(int slot)
+{
+	return (void *)(0xF0000000|(slot<<24));
+}
+
+extern int nubus_hwreg_present(volatile void *ptr);
+
+extern void nubus_init_via(void);
+extern int nubus_free_irq(int slot);
+extern int nubus_request_irq(int slot, void *dev_id, void (*handler)(int,void *,struct pt_regs *));
+
diff -u --recursive --new-file v2.1.90/linux/include/linux/rtnetlink.h linux/include/linux/rtnetlink.h
--- v2.1.90/linux/include/linux/rtnetlink.h	Tue Mar 17 22:18:15 1998
+++ linux/include/linux/rtnetlink.h	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
 #include <linux/netlink.h>
 
 #define RTNL_DEBUG 1
-#define CONFIG_RTNL_OLD_IFINFO 1
+/* #define CONFIG_RTNL_OLD_IFINFO 1 */
 
 
 /****
diff -u --recursive --new-file v2.1.90/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v2.1.90/linux/include/linux/skbuff.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/skbuff.h	Thu Mar 26 14:54:53 1998
@@ -19,9 +19,11 @@
 
 #include <asm/atomic.h>
 #include <asm/types.h>
+#include <asm/spinlock.h>
 
 #define HAVE_ALLOC_SKB		/* For the drivers to know */
 #define HAVE_ALIGNABLE_SKB	/* Ditto 8)		   */
+#define SLAB_SKB 		/* Slabified skbuffs 	   */
 
 #define CHECKSUM_NONE 0
 #define CHECKSUM_HW 1
@@ -88,27 +90,27 @@
   
 	unsigned int 	len;			/* Length of actual data			*/
 	unsigned int	csum;			/* Checksum 					*/
-	volatile char 	used;
-	unsigned char	tries,			/* Times tried					*/
-  			inclone,		/* Inline clone					*/
+	volatile char 	used;			/* Data moved to user and not MSG_PEEK		*/
+	unsigned char	is_clone,		/* We are a clone				*/
+			cloned, 		/* head may be cloned (check refcnt to be sure). */
   			pkt_type,		/* Packet class					*/
   			pkt_bridged,		/* Tracker for bridging 			*/
   			ip_summed;		/* Driver fed us an IP checksum			*/
-	__u32		priority;
+	__u32		priority;		/* Packet queueing priority			*/
 	atomic_t	users;			/* User count - see datagram.c,tcp.c 		*/
 	unsigned short	protocol;		/* Packet protocol from driver. 		*/
 	unsigned short	security;		/* Security level of packet			*/
 	unsigned int	truesize;		/* Buffer size 					*/
 
+#ifndef SLAB_SKB
 	atomic_t	count;			/* reference count				*/
 	struct sk_buff	*data_skb;		/* Link to the actual data skb			*/
+#endif
 	unsigned char	*head;			/* Head of buffer 				*/
 	unsigned char	*data;			/* Data head pointer				*/
 	unsigned char	*tail;			/* Tail pointer					*/
 	unsigned char 	*end;			/* End pointer					*/
 	void 		(*destructor)(struct sk_buff *);	/* Destruct function		*/
-#define SKB_CLONE_ORIG		1
-#define SKB_CLONE_INLINE	2
 	
 #if defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE)
 	__u32		shapelatency;		/* Latency on frame */
@@ -163,6 +165,12 @@
 extern void			skb_reserve(struct sk_buff *skb, unsigned int len);
 extern void 			skb_trim(struct sk_buff *skb, unsigned int len);
 
+/* Internal */
+extern __inline__ atomic_t *skb_datarefp(struct sk_buff *skb)
+{
+	return (atomic_t *)(skb->end);
+}
+
 extern __inline__ int skb_queue_empty(struct sk_buff_head *list)
 {
 	return (list->next == (struct sk_buff *) list);
@@ -174,9 +182,16 @@
 		__kfree_skb(skb);
 }
 
+/* Use this if you didn't touch the skb state [for fast switching] */
+extern __inline__ void kfree_skb_fast(struct sk_buff *skb)
+{
+	if (atomic_dec_and_test(&skb->users))
+		kfree_skbmem(skb);	
+}
+
 extern __inline__ int skb_cloned(struct sk_buff *skb)
 {
-	return (atomic_read(&skb->data_skb->count) != 1);
+	return skb->cloned && atomic_read(skb_datarefp(skb)) != 1;
 }
 
 extern __inline__ int skb_shared(struct sk_buff *skb)
@@ -261,14 +276,15 @@
 	prev->next = newsk;
 }
 
+extern spinlock_t skb_queue_lock;
+
 extern __inline__ void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&skb_queue_lock, flags);
 	__skb_queue_head(list, newsk);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&skb_queue_lock, flags);
 }
 
 /*
@@ -293,10 +309,9 @@
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&skb_queue_lock, flags);
 	__skb_queue_tail(list, newsk);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&skb_queue_lock, flags);
 }
 
 /*
@@ -328,10 +343,9 @@
 	long flags;
 	struct sk_buff *result;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&skb_queue_lock, flags);
 	result = __skb_dequeue(list);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&skb_queue_lock, flags);
 	return result;
 }
 
@@ -358,10 +372,9 @@
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&skb_queue_lock, flags);
 	__skb_insert(newsk, old->prev, old, old->list);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&skb_queue_lock, flags);
 }
 
 /*
@@ -372,10 +385,9 @@
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&skb_queue_lock, flags);
 	__skb_insert(newsk, old, old->next, old->list);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&skb_queue_lock, flags);
 }
 
 /*
@@ -407,11 +419,10 @@
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&skb_queue_lock, flags);
 	if(skb->list)
 		__skb_unlink(skb, skb->list);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&skb_queue_lock, flags);
 }
 
 /* XXX: more streamlined implementation */
@@ -428,10 +439,9 @@
 	long flags;
 	struct sk_buff *result;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&skb_queue_lock, flags);
 	result = __skb_dequeue_tail(list);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&skb_queue_lock, flags);
 	return result;
 }
 
@@ -451,7 +461,12 @@
 	if(skb->tail>skb->end)
 	{
 		__label__ here;
+#if 1
+		printk(KERN_DEBUG "skbput: over: %p:tail=%p:end=%p:len=%u\n",
+						&&here, skb->tail, skb->end, len);
+#else
 		panic(skb_put_errstr,&&here,len);
+#endif
 here:          ;
 	}
 	return tmp;
@@ -542,6 +557,9 @@
 extern int			skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size);
 extern int			skb_copy_datagram_iovec(struct sk_buff *from, int offset, struct iovec *to,int size);
 extern void			skb_free_datagram(struct sock * sk, struct sk_buff *skb);
+
+extern void skb_init(void);
+extern void skb_add_mtu(int mtu);
 
 #endif	/* __KERNEL__ */
 #endif	/* _LINUX_SKBUFF_H */
diff -u --recursive --new-file v2.1.90/linux/include/linux/socket.h linux/include/linux/socket.h
--- v2.1.90/linux/include/linux/socket.h	Tue Mar 17 22:18:15 1998
+++ linux/include/linux/socket.h	Mon Mar 23 16:48:25 1998
@@ -197,6 +197,8 @@
 #define MSG_SYN		0x400
 #define MSG_URG		0x800
 #define MSG_RST		0x1000
+#define MSG_ERRQUEUE	0x2000
+#define MSG_NOSIGNAL	0x4000
 
 #define MSG_CTLIGNORE   0x80000000
 
diff -u --recursive --new-file v2.1.90/linux/include/linux/swap.h linux/include/linux/swap.h
--- v2.1.90/linux/include/linux/swap.h	Tue Mar 17 22:18:15 1998
+++ linux/include/linux/swap.h	Thu Mar 26 09:37:46 1998
@@ -39,7 +39,6 @@
 extern struct inode swapper_inode;
 extern unsigned long page_cache_size;
 extern int buffermem;
-#define BUFFER_MEM ((buffermem >> PAGE_SHIFT) + page_cache_size)
 
 /* Incomplete types for prototype declarations: */
 struct task_struct;
@@ -120,6 +119,21 @@
 	if (PageFreeAfter(page))
 		count--;
 	return (count > 1);
+}
+
+/*
+ * When we're freeing pages from a user application, we want
+ * to cluster swapouts too.	-- Rik.
+ * linux/mm/page_alloc.c
+ */
+static inline int try_to_free_pages(int gfp_mask, int count)
+{
+	int retval = 0;
+	while (count--) {
+		if (try_to_free_page(gfp_mask))
+			retval = 1;
+	}
+	return retval;
 }
 
 /*
diff -u --recursive --new-file v2.1.90/linux/include/linux/swapctl.h linux/include/linux/swapctl.h
--- v2.1.90/linux/include/linux/swapctl.h	Tue Mar 17 22:18:15 1998
+++ linux/include/linux/swapctl.h	Thu Mar 26 14:54:58 1998
@@ -39,6 +39,7 @@
 } buffer_mem_v1;
 typedef buffer_mem_v1 buffer_mem_t;
 extern buffer_mem_t buffer_mem;
+extern buffer_mem_t page_cache;
 
 typedef struct freepages_v1
 {
diff -u --recursive --new-file v2.1.90/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.1.90/linux/include/linux/sysctl.h	Tue Mar 17 22:18:15 1998
+++ linux/include/linux/sysctl.h	Thu Mar 26 12:56:36 1998
@@ -84,7 +84,8 @@
 	VM_FREEPG,		/* struct: Set free page thresholds */
 	VM_BDFLUSH,		/* struct: Control buffer cache flushing */
 	VM_OVERCOMMIT_MEMORY,	/* Turn off the virtual memory safety limit */
-	VM_BUFFERMEM		/* struct: Set cache memory thresholds */
+	VM_BUFFERMEM,		/* struct: Set buffer memory thresholds */
+	VM_PAGECACHE		/* struct: Set cache memory thresholds */
 };
 
 
@@ -151,6 +152,7 @@
 	NET_IPV4_TCP_HOE_RETRANSMITS=32,
 	NET_IPV4_TCP_TIMESTAMPS,
 	NET_IPV4_TCP_WINDOW_SCALING,
+	NET_IPV4_TCP_SACK,
 	NET_IPV4_TCP_VEGAS_CONG_AVOID,
 	NET_IPV4_DEFAULT_TTL,
 	NET_IPV4_AUTOCONFIG,
@@ -196,6 +198,7 @@
 	NET_IPV4_ROUTE_REDIRECT_SILENCE,
 	NET_IPV4_ROUTE_ERROR_COST,
 	NET_IPV4_ROUTE_ERROR_BURST,
+	NET_IPV4_ROUTE_GC_ELASTICITY,
 };
 
 enum
@@ -235,6 +238,7 @@
 	NET_IPV6_ROUTE_GC_MIN_INTERVAL,
 	NET_IPV6_ROUTE_GC_TIMEOUT,
 	NET_IPV6_ROUTE_GC_INTERVAL,
+	NET_IPV6_ROUTE_GC_ELASTICITY,
 };
 
 enum {
diff -u --recursive --new-file v2.1.90/linux/include/linux/timer.h linux/include/linux/timer.h
--- v2.1.90/linux/include/linux/timer.h	Mon Feb 23 18:12:12 1998
+++ linux/include/linux/timer.h	Mon Mar 23 16:48:25 1998
@@ -46,7 +46,7 @@
  * to distinguish between the different invocations.
  */
 struct timer_list {
-	struct timer_list *next;
+	struct timer_list *next; /* MUST be first element */
 	struct timer_list *prev;
 	unsigned long expires;
 	unsigned long data;
@@ -55,6 +55,13 @@
 
 extern void add_timer(struct timer_list * timer);
 extern int  del_timer(struct timer_list * timer);
+
+/*
+ * mod_timer is a more efficient way to update the expire field of an
+ * active timer (if the timer is inactive it will be activated)
+ * mod_timer(a,b) is equivalent to del_timer(a); a->expires = b; add_timer(a)
+ */
+void mod_timer(struct timer_list *timer, unsigned long expires);
 
 extern void it_real_fn(unsigned long);
 
diff -u --recursive --new-file v2.1.90/linux/include/linux/zorro.h linux/include/linux/zorro.h
--- v2.1.90/linux/include/linux/zorro.h	Sat May 24 09:10:25 1997
+++ linux/include/linux/zorro.h	Sat Mar 21 11:06:17 1998
@@ -1,503 +1,651 @@
 /*
- * linux/zorro.h -- Amiga AutoConfig (Zorro) Expansion Device Definitions
+ *  linux/zorro.h -- Amiga AutoConfig (Zorro) Expansion Device Definitions
  *
- * Copyright (C) 1995 Geert Uytterhoeven
+ *  Copyright (C) 1995 Geert Uytterhoeven
  *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file COPYING in the main directory of this archive
- * for more details.
+ *  Please update arch/m68k/amiga/zorro.c if you make changes here!
+ *
+ *  Many IDs were obtained from ExpName/Identify ((C) Richard Körber)
+ *  and by looking at the NetBSD-Amiga kernel sources
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive
+ *  for more details.
  */
 
-#ifndef __ZORRO_H
-#define __ZORRO_H
+#ifndef _LINUX_ZORRO_H
+#define _LINUX_ZORRO_H
 
 #ifndef __ASSEMBLY__
 
-/*
- * Defined Board Manufacturers
- *
- * Please update arch/m68k/amiga/zorro.c if you make changes here
- * Many IDs were obtained from ExpName/Identify ((C) Richard Körber)
- * and by looking at the NetBSD-Amiga kernel sources
- */
+    /*
+     *  Zorro Product Classes
+     *
+     *  Make sure to keep these in sync with arch/m68k/amiga/zorro.c!
+     */
+
+enum Zorro_Classes {
+    ZORRO_CLASS_UNKNOWN = 0x00,
+    ZORRO_CLASS_ARCNET,
+    ZORRO_CLASS_AUDIO,
+    ZORRO_CLASS_BRIDGE,
+    ZORRO_CLASS_DSP,
+    ZORRO_CLASS_ETHERNET,
+    ZORRO_CLASS_ETHERNET_PARALLEL,
+    ZORRO_CLASS_FLASHROM,
+    ZORRO_CLASS_FPU_RAM,
+    ZORRO_CLASS_GFX,
+    ZORRO_CLASS_GFXRAM,
+    ZORRO_CLASS_HD,
+    ZORRO_CLASS_HD_RAM,
+    ZORRO_CLASS_IDE,
+    ZORRO_CLASS_IDE_RAM,
+    ZORRO_CLASS_IDE_FLOPPY,
+    ZORRO_CLASS_ISDN,
+    ZORRO_CLASS_MACEMU,
+    ZORRO_CLASS_MISC,
+    ZORRO_CLASS_MODEM,
+    ZORRO_CLASS_MULTIIO,
+    ZORRO_CLASS_RAM,
+    ZORRO_CLASS_SCANNER,
+    ZORRO_CLASS_SCSI,
+    ZORRO_CLASS_SCSI_IDE,
+    ZORRO_CLASS_SCSI_RAM,
+    ZORRO_CLASS_SCSI_SERIAL,
+    ZORRO_CLASS_SERIAL,
+    ZORRO_CLASS_TABLET,
+    ZORRO_CLASS_TURBO,
+    ZORRO_CLASS_TURBO_RAM,
+    ZORRO_CLASS_TURBO_HD,
+    ZORRO_CLASS_TURBO_IDE,
+    ZORRO_CLASS_TURBO_SCSI,
+    ZORRO_CLASS_TURBO_SCSI_RAM,
+    ZORRO_CLASS_VIDEO,
+};
 
-#define MANUF_PACIFIC          (0x00D3)	/* Pacific Peripherals */
-#define PROD_SE_2000_A500      (0x00)	/* SE 2000 A500 */
-#define PROD_PACIFIC_HD        (0x0A)	/* HD Controller */
-
-#define MANUF_KUPKE            (0x00DD)	/* Kupke */
-#define PROD_GOLEM_BOX_2       (0x00)	/* Golem RAM Box 2MB */
-
-#define MANUF_MEMPHIS          (0x0100)	/* Memphis */
-#define PROD_STORMBRINGER      (0x00)	/* Stormbringer */
-
-#define MANUF_3_STATE          (0x0200)	/* 3-State */
-#define PROD_MEGAMIX_2000      (0x02)	/* Megamix 2000 RAM */
-
-#define MANUF_COMMODORE2       (0x0201)	/* Commodore Braunschweig */
-#define PROD_A2088             (0x01)	/* CBM A2088 XT Bridgeboard */
-#define PROD_A2286             (0x02)	/* CBM A2286 AT Bridgeboard */
-#define PROD_A4091_2           (0x54)	/* CBM A4091 SCSI Controller */
-#define PROD_A2386SX           (0x67)	/* CBM A2386-SX Bridgeboard */
-
-#define MANUF_COMMODORE        (0x0202)	/* Commodore West Chester */
-#define PROD_A2090A            (0x01)	/* CBM A2090/A2090A HD Controller */
-#define PROD_A590              (0x02)	/* CBM A590 SCSI Controller */
-#define PROD_A2091             (0x03)	/* CBM A2091 SCSI Controller */
-#define PROD_A2090B            (0x04)	/* CBM A2090B 2090 Autoboot Card */
-#define PROD_ARCNET            (0x09)	/* CBM A2060 Arcnet Card */
-#define PROD_CBMRAM            (0x0A)	/* CBM A2052/58.RAM | 590/2091.RAM */
-#define PROD_A560RAM           (0x20)	/* CBM A560 Memory Module */
-#define PROD_A2232PROTO        (0x45)	/* CBM A2232 Serial Prototype */
-#define PROD_A2232             (0x46)	/* CBM A2232 Serial Production */
-#define PROD_A2620             (0x50)	/* CBM A2620 68020/RAM Card */
-#define PROD_A2630             (0x51)	/* CBM A2630 68030/RAM Card */
-#define PROD_A4091             (0x54)	/* CBM A4091 SCSI Controller */
-#define PROD_A2065_2           (0x5A)	/* A2065 Ethernet Card */
-#define PROD_ROMULATOR         (0x60)	/* CBM Romulator Card */
-#define PROD_A3000TESTFIX      (0x61)	/* CBM A3000 Test Fixture */
-#define PROD_A2386SX_2         (0x67)	/* A2386-SX Bridgeboard */
-#define PROD_A2065             (0x70)	/* CBM A2065 Ethernet Card */
-
-#define MANUF_COMMODORE3       (0x0203)	/* Commodore West Chester */
-#define PROD_A2090A_CM         (0x03)	/* A2090A Combitec/MacroSystem */
-
-#define MANUF_KCS              (0x02FF)	/* Kolff Computer Supplies */
-#define PROD_POWER_BOARD       (0x00)	/* KCS Power PC Board */
-
-#define MANUF_CARDCO           (0x03EC)	/* Cardco */
-#define PROD_KRONOS_2000_SCSI  (0x04)	/* Kronos 2000 SCSI Controller */
-#define PROD_A1000_SCSI        (0x0C)	/* A1000 SCSI Controller */
-#define PROD_ESCORT_SCSI       (0x0E)	/* Escort SCSI Controller */
-#define PROD_CC_A2410          (0xF5)	/* Cardco A2410 Hires Graphics Card */
-
-#define MANUF_A_SQUARED        (0x03ED)	/* A-Squared */
-#define PROD_LIVE_2000         (0x01)	/* Live! 2000 */
-
-#define MANUF_COMSPEC          (0x03EE)	/* ComSpec Communications */
-#define PROD_AX2000            (0x01)	/* AX2000 */
-
-#define MANUF_ANAKIN           (0x03F1)	/* Anakin */
-#define PROD_EASYL             (0x01)	/* Easyl Tablet */
-
-#define MANUF_MICROBOTICS      (0x03F2)	/* MicroBotics */
-#define PROD_STARBOARD_II      (0x00)	/* StarBoard II */
-#define PROD_STARDRIVE         (0x02)	/* StarDrive */
-#define PROD_8_UP_A            (0x03)	/* 8-Up (Rev A) */
-#define PROD_8_UP_Z            (0x04)	/* 8-Up (Rev Z) */
-#define PROD_DELTA_RAM         (0x20)	/* Delta Card RAM */
-#define PROD_8_STAR_RAM        (0x40)	/* 8-Star RAM */
-#define PROD_8_STAR            (0x41)	/* 8-Star */
-#define PROD_VXL_RAM           (0x44)	/* VXL RAM */
-#define PROD_VXL_30            (0x45)	/* VXL-30 Turbo Board */
-#define PROD_DELTA             (0x60)	/* Delta Card */
-#define PROD_MBX_1200          (0x81)	/* MBX 1200 */
-#define PROD_HARDFRAME_2000    (0x9E)	/* Hardframe 2000 */
-#define PROD_MBX_1200_2        (0xC1)	/* MBX 1200 */
-
-#define MANUF_ACCESS           (0x03F4)	/* Access Associates */
-
-#define MANUF_EXPANSION_TECH   (0x03F6)	/* Expansion Technologies */
-
-#define MANUF_ASDG             (0x03FF)	/* ASDG */
-#define PROD_ASDG_MEMORY       (0x01)	/* Memory Expansion */
-#define PROD_ASDG_MEMORY_2     (0x02)	/* Memory Expansion */
-#define PROD_LAN_ROVER         (0xFE)	/* Lan Rover Ethernet */
-#define PROD_TWIN_X            (0xFF)	/* Twin-X Serial Card */
-
-#define MANUF_IMTRONICS        (0x0404)	/* Imtronics */
-#define PROD_HURRICANE_2800    (0x39)	/* Hurricane 2800 68030 */
-#define PROD_HURRICANE_2800_2  (0x57)	/* Hurricane 2800 68030 */
-
-#define MANUF_UNIV_OF_LOWELL   (0x0406)	/* University of Lowell */
-#define PROD_A2410             (0x00)	/* CBM A2410 Hires Graphics Card */
-
-#define MANUF_AMERISTAR        (0x041D)	/* Ameristar */
-#define PROD_AMERISTAR2065     (0x01)	/* A2065 Ethernet Card */
-#define PROD_A560              (0x09)	/* Arcnet Card */
-#define PROD_A4066             (0x0A)	/* A4066 Ethernet Card */
-
-#define MANUF_SUPRA            (0x0420)	/* Supra */
-#define PROD_SUPRADRIVE_4x4    (0x01)	/* SupraDrive 4x4 SCSI Controller */
-#define PROD_SUPRA_2000        (0x03)	/* 2000 DMA HD */
-#define PROD_SUPRA_500         (0x05)	/* 500 HD/RAM */
-#define PROD_SUPRA_500XP       (0x09)	/* 500XP/2000 RAM */
-#define PROD_SUPRA_500RX       (0x0A)	/* 500RX/2000 RAM */
-#define PROD_SUPRA_2400ZI      (0x0B)	/* 2400zi Modem */
-#define PROD_WORDSYNC          (0x0C)	/* Supra Wordsync SCSI Controller */
-#define PROD_WORDSYNC_II       (0x0D)	/* Supra Wordsync II SCSI Controller */
-#define PROD_SUPRA_2400ZIPLUS  (0x10)	/* 2400zi+ Modem */
-
-#define MANUF_CSA              (0x0422)	/* Computer Systems Ass. */
-#define PROD_MAGNUM            (0x11)	/* Magnum 40 SCSI Controller */
-#define PROD_12GAUGE           (0x15)	/* 12 Gauge SCSI Controller */
-
-#define MANUF_MTEC2            (0x0502)	/* M-Tech */
-#define PROD_AT500_2           (0x03)	/* AT500 RAM */
-
-#define MANUF_GVP3             (0x06E1)	/* Great Valley Products */
-#define PROD_IMPACT            (0x08)	/* Impact SCSI/Memory */
-
-#define MANUF_BYTEBOX          (0x07DA)	/* ByteBox */
-#define PROD_BYTEBOX_A500      (0x00)	/* A500 */
-
-#define MANUF_HACKER           (0x07DB)	/* Test only: no product definitions */
-
-#define MANUF_POWER_COMPUTING  (0x07DC)	/* Power Computing (DKB) */
-#define PROD_DKB_3128          (0x0E)	/* DKB 3128 RAM */
-#define PROD_RAPID_FIRE        (0x0F)	/* Rapid Fire SCSI Controller */
-#define PROD_DKB_1202          (0x10)	/* DKB 1202 RAM */
-#define PROD_VIPER_II_COBRA    (0x12)	/* Viper II Turbo Board (DKB Cobra) */
-#define PROD_WILDFIRE_060      (0x17)	/* WildFire 060 Turbo Board */
-#define PROD_WILDFIRE_060_2    (0xFF)	/* WildFire 060 Turbo Board */
-
-#define MANUF_GVP              (0x07E1)	/* Great Valley Products */
-#define PROD_IMPACT_I_4K       (0x01)	/* Impact Series-I SCSI 4K */
-#define PROD_IMPACT_I_16K_2    (0x02)	/* Impact Series-I SCSI 16K/2 */
-#define PROD_IMPACT_I_16K_3    (0x03)	/* Impact Series-I SCSI 16K/3 */
-#define PROD_IMPACT_3001_IDE   (0x08)	/* Impact 3001 IDE */
-#define PROD_IMPACT_3001_RAM   (0x09)	/* Impact 3001 RAM */
-#define PROD_GVPIISCSI         (0x0B)	/* GVP Series II SCSI Controller */
-#define PROD_GVPIISCSI_2       (0x09)	/* evidence that the driver works
-					   for this product code also */
-#define PROD_GVPIIRAM          (0x0A)	/* GVP Series II RAM */
-#define PROD_GVP               (0x0B)	/* This code is used by a wide range of
-					   GVP products - use the epc to
-					   identify it correctly */
-#define PROD_GVP_A2000_030     (0x0D)	/* GVP A2000 68030 Turbo Board */
-#define PROD_IMPACT_3001_IDE_2 (0x0D)	/* Impact 3001 IDE */
-#define PROD_GFORCE_040_SCSI   (0x16)	/* GForce 040 with SCSI (new) */
-#define PROD_GVPIV_24          (0x20)	/* GVP IV-24 Graphics Board */
-#define PROD_GFORCE_040        (0xFF)	/* GForce 040 Turbo Board */
-/* #define PROD_GVPIO_EXT      (0xFF)*/	/* GVP I/O Extender */
-
-#define MANUF_SYNERGY          (0x07E5)	/* Synergy */
-
-#define MANUF_XETEC            (0x07E6)	/* Xetec */
-#define PROD_FASTCARD_SCSI     (0x01)	/* FastCard SCSI Controller */
-#define PROD_FASTCARD_RAM      (0x02)	/* FastCard RAM */
-
-#define MANUF_PPI              (0x07EA)	/* Progressive Peripherals Inc. */
-#define PROD_MERCURY           (0x00)	/* Mercury Turbo Board */
-#define PROD_PPS_A3000_040     (0x01)	/* PP&S A3000 68040 Turbo Board */
-#define PROD_PPS_A2000_040     (0x69)	/* PP&S A2000 68040 Turbo Board */
-#define PROD_ZEUS              (0x96)	/* Zeus SCSI Controller */
-#define PROD_PPS_A500_040      (0xBB)	/* PP&S A500 68040 Turbo Board */
-
-#define MANUF_XEBEC            (0x07EC)	/* Xebec */
-
-#define MANUF_SPIRIT           (0x07F2)	/* Spirit */
-#define PROD_HDA_506           (0x04)	/* HDA 506 Harddisk */
-#define PROD_OCTABYTE_RAM      (0x06)	/* OctaByte RAM */
-
-#define MANUF_BSC              (0x07FE)	/* BSC */
-#define PROD_ALF_3_SCSI        (0x03)	/* BSC ALF 3 SCSI Controller */
-
-#define MANUF_BSC3             (0x0801)	/* BSC */
-#define PROD_ALF_2_SCSI        (0x01)	/* ALF 2 SCSI Controller */
-#define PROD_ALF_2_SCSI_2      (0x02)	/* ALF 2 SCSI Controller */
-#define PROD_ALF_3_SCSI_2      (0x03)	/* ALF 3 SCSI Controller */
-
-#define MANUF_C_LTD            (0x0802)	/* C Ltd. */
-#define PROD_KRONOS_SCSI       (0x04)	/* Kronos SCSI Controller */
-#define PROD_A1000_SCSI_2      (0x0C)	/* A1000 SCSI Controller */
-
-#define MANUF_JOCHHEIM         (0x0804)	/* Jochheim */
-#define PROD_JOCHHEIM_RAM      (0x01)	/* Jochheim RAM */
-
-#define MANUF_CHECKPOINT       (0x0807)	/* Checkpoint Technologies */
-#define PROD_SERIAL_SOLUTION   (0x00)	/* Serial Solution */
-
-#define MANUF_ICD              (0x0817)	/* ICD */
-#define PROD_ADVANTAGE_2000    (0x01)	/* Advantage 2000 SCSI Controller */
-
-#define MANUF_KUPKE2           (0x0819)	/* Kupke */
-#define PROD_KUPKE_SCSI_II     (0x02)	/* Golem SCSI-II Controller */
-#define PROD_GOLEM_BOX         (0x03)	/* Golem Box */
-#define PROD_KUPKE_TURBO       (0x04)	/* 030/882 Turbo Board */
-#define PROD_KUPKE_SCSI_AT     (0x05)	/* SCSI/AT Controller */
-
-#define MANUF_GVP4             (0x081D)	/* Great Valley Products */
-#define PROD_A2000_RAM8        (0x09)	/* A2000-RAM8/2 */
-
-#define MANUF_INTERWORKS_NET   (0x081E)	/* Interworks Network */
-
-#define MANUF_HARDITAL         (0x0820)	/* Hardital Synthesis */
-#define PROD_TQM               (0x14)	/* TQM 68030+68882 Turbo Board */
-
-#define MANUF_BSC2             (0x082C)	/* BSC */
-#define PROD_OKTAGON_SCSI      (0x05)	/* BSC Oktagon 2008 SCSI Controller */
-#define PROD_TANDEM            (0x06)	/* BSC Tandem AT-2008/508 IDE */
-#define PROD_ALPHA_RAM_1200    (0x07)	/* Alpha RAM 1200 */
-#define PROD_OKTAGON_RAM       (0x08)	/* BSC Oktagon 2008 RAM */
-#define PROD_MULTIFACE_I       (0x10)	/* Alfa Data MultiFace I */
-#define PROD_MULTIFACE_II      (0x11)	/* Alfa Data MultiFace II */
-#define PROD_MULTIFACE_III     (0x12)	/* Alfa Data MultiFace III */
-#define PROD_BSC_FRAEMBUFFER   (0x20)	/* Framebuffer */
-#define PROD_GRAFFITI_RAM      (0x21)	/* Graffiti Graphics Board */
-#define PROD_GRAFFITI_REG      (0x22)
-#define PROD_ISDN_MASTERCARD   (0x40)	/* BSC ISDN MasterCard */
-#define PROD_ISDN_MASTERCARD_2 (0x41)	/* BSC ISDN MasterCard II */
-
-#define MANUF_ADV_SYS_SOFT     (0x0836)	/* Advanced Systems & Software */
-#define PROD_NEXUS_SCSI        (0x01)	/* Nexus SCSI Controller */
-#define PROD_NEXUS_RAM         (0x08)	/* Nexus RAM */
-
-#define MANUF_IMPULSE          (0x0838)	/* Impulse */
-#define PROD_FIRECRACKER_24    (0x00)	/* FireCracker 24 */
-
-#define MANUF_IVS              (0x0840)	/* IVS */
-#define PROD_GRANDSLAM_PIC_2   (0x02)	/* GrandSlam PIC 2 RAM */
-#define PROD_GRANDSLAM_PIC_1   (0x04)	/* GrandSlam PIC 1 RAM */
-#define PROD_IVS_OVERDRIVE     (0x10)	/* OverDrive HD */
-#define PROD_TRUMPCARD_CLASSIC (0x30)	/* Trumpcard Classic SCSI Controller */
-#define PROD_TRUMPCARD_PRO     (0x34)	/* Trumpcard Pro SCSI Controller */
-#define PROD_META_4            (0x40)	/* Meta-4 RAM */
-#define PROD_WAVETOOLS         (0xBF)	/* Wavetools Sound Board */
-#define PROD_VECTOR            (0xF3)	/* Vector SCSI Controller */
-#define PROD_VECTOR_2          (0xF4)	/* Vector SCSI Controller */
-
-#define MANUF_VECTOR           (0x0841)	/* Vector */
-#define PROD_CONNECTION        (0xE3)	/* Connection Serial IO */
-
-#define MANUF_XPERT_PRODEV     (0x0845)	/* XPert/ProDev */
-#define PROD_VISIONA_RAM       (0x01)	/* Visiona Graphics Board */
-#define PROD_VISIONA_REG       (0x02)
-#define PROD_MERLIN_RAM        (0x03)	/* Merlin Graphics Board */
-#define PROD_MERLIN_REG        (0x04)
-#define PROD_MERLIN_REG_2      (0xC9)
-
-#define MANUF_HYDRA_SYSTEMS    (0x0849)	/* Hydra Systems */
-#define PROD_AMIGANET          (0x01)	/* Amiganet Board */
-
-#define MANUF_SUNRIZE          (0x084F)	/* Sunrize Industries */
-#define PROD_AD1012            (0x01)	/* AD1012 Sound Board */
-#define PROD_AD516             (0x02)	/* AD516 Sound Board */
-#define PROD_DD512             (0x03)	/* DD512 Sound Board */
-
-#define MANUF_TRICERATOPS      (0x0850)	/* Triceratops */
-#define PROD_TRICERATOPS       (0x01)	/* Triceratops Multi I/O Board */
-
-#define MANUF_APPLIED_MAGIC    (0x0851)	/* Applied Magic Inc */
-#define PROD_DMI_RESOLVER      (0x01)	/* DMI Resolver Graphics Board */
-#define PROD_DIGITAL_BCASTER   (0x06)	/* Digital Broadcaster */
-
-#define MANUF_GFX_BASE         (0x085E)	/* GFX-Base */
-#define PROD_GDA_1_RAM         (0x00)	/* GDA-1 Graphics Board */
-#define PROD_GDA_1_REG         (0x01)
-
-#define MANUF_ROCTEC           (0x0860)	/* RocTec */
-#define PROD_RH_800C           (0x01)	/* RH 800C Hard Disk Controller */
-#define PROD_RH_800C_RAM       (0x01)	/* RH 800C RAM */
-
-#define MANUF_HELFRICH1        (0x0861)	/* Helfrich */
-#define PROD_RAINBOW3          (0x21)	/* Rainbow3 Graphics Board */
-
-#define MANUF_SW_RESULT_ENTS   (0x0866)	/* Software Result Enterprises */
-#define PROD_GG2PLUS           (0x01)	/* GG2+ Bus Converter */
-
-#define MANUF_MASOBOSHI        (0x086D)	/* Masoboshi */
-#define PROD_MASTER_CARD_RAM   (0x03)	/* Master Card RAM */
-#define PROD_MASTER_CARD_SCSI  (0x04)	/* Master Card SCSI Controller */
-#define PROD_MVD_819           (0x07)	/* MVD 819 */
-
-#define MANUF_VILLAGE_TRONIC   (0x0877)	/* Village Tronic */
-#define PROD_DOMINO_RAM        (0x01)	/* Domino Graphics Board */
-#define PROD_DOMINO_REG        (0x02)
-#define PROD_PICASSO_II_RAM    (0x0B)	/* Picasso II/II+ Graphics Board */
-#define PROD_PICASSO_II_REG    (0x0C)
-#define PROD_PICASSO_II_SEGM   (0x0D)	/* Picasso II/II+ (Segmented Mode) */
-#define PROD_PICASSO_IV        (0x15)	/* Picassio IV Graphics Board */
-#define PROD_PICASSO_IV_2      (0x16)
-#define PROD_PICASSO_IV_3      (0x17)
-#define PROD_PICASSO_IV_4      (0x18)
-#define PROD_ARIADNE           (0xC9)	/* Ariadne Ethernet */
-
-#define MANUF_UTILITIES_ULTD   (0x087B)	/* Utilities Unlimited */
-#define PROD_EMPLANT_DELUXE    (0x15)	/* Emplant Deluxe SCSI Controller */
-#define PROD_EMPLANT_DELUXE2   (0x20)	/* Emplant Deluxe SCSI Controller */
-
-#define MANUF_AMITRIX          (0x0880)	/* Amitrix */
-#define PROD_AMITRIX_MULTI_IO  (0x01)	/* Multi-IO */
-#define PROD_AMITRIX_CD_RAM    (0x02)	/* CD-RAM Memory */
-
-#define MANUF_ARMAX            (0x0885)	/* ArMax */
-#define PROD_OMNIBUS           (0x00)	/* OmniBus Graphics Board */
-
-#define MANUF_NEWTEK           (0x088F)	/* NewTek */
-#define PROD_VIDEOTOASTER      (0x00)	/* VideoToaster */
-
-#define MANUF_MTEC             (0x0890)	/* M-Tech Germany */
-#define PROD_AT500             (0x01)	/* AT500 IDE Controller */
-#define PROD_MTEC_68030        (0x03)	/* 68030 Turbo Board */
-#define PROD_MTEC_68020I       (0x06)	/* 68020i Turbo Board */
-#define PROD_MTEC_T1230        (0x20)	/* A1200 T68030/42 RTC Turbo Board */
-#define PROD_MTEC_RAM          (0x22)	/* MTEC 8MB RAM */
-
-#define MANUF_GVP2             (0x0891)	/* Great Valley Products */
-#define PROD_SPECTRUM_RAM      (0x01)	/* EGS 28/24 Spectrum Graphics Board */
-#define PROD_SPECTRUM_REG      (0x02)
-
-#define MANUF_HELFRICH2        (0x0893)	/* Helfrich */
-#define PROD_PICCOLO_RAM       (0x05)	/* Piccolo Graphics Board */
-#define PROD_PICCOLO_REG       (0x06)
-#define PROD_PEGGY_PLUS        (0x07)	/* PeggyPlus MPEG Decoder Board */
-#define PROD_VIDEOCRUNCHER     (0x08)	/* VideoCruncher */
-#define PROD_SD64_RAM          (0x0A)	/* SD64 Graphics Board */
-#define PROD_SD64_REG          (0x0B)
-
-#define MANUF_MACROSYSTEMS     (0x089B)	/* MacroSystems USA */
-#define PROD_WARP_ENGINE       (0x13)	/* Warp Engine 40xx SCSI Controller */
-
-#define MANUF_ELBOX            (0x089E)	/* ElBox Computer */
-#define PROD_ELBOX_1200        (0x06)	/* Elbox 1200/4 RAM */
-
-#define MANUF_HARMS_PROF       (0x0A00)	/* Harms Professional */
-#define PROD_HARMS_030_PLUS    (0x10)	/* 030 plus */
-#define PROD_3500_TURBO        (0xD0)	/* 3500 Turbo board */
-
-#define MANUF_MICRONIK         (0x0A50)	/* Micronik */
-#define PROD_RCA_120           (0x0A)	/* RCA 120 RAM */
-
-#define MANUF_MEGA_MICRO       (0x1000)	/* MegaMicro */
-#define PROD_SCRAM_500_SCSI    (0x03)	/* SCRAM 500 SCSI Controller */
-#define PROD_SCRAM_500_RAM     (0x04)	/* SCRAM 500 RAM */
-
-#define MANUF_IMTRONICS2       (0x1028)	/* Imtronics */
-#define PROD_HURRICANE_2800_3  (0x39)	/* Hurricane 2800 68030 */
-#define PROD_HURRICANE_2800_4  (0x57)	/* Hurricane 2800 68030 */
-
-#define MANUF_KUPKE3           (0x1248)	/* Kupke */
-#define PROD_GOLEM_3000        (0x01)	/* Golem HD 3000 */
-
-#define MANUF_ITH              (0x1388)	/* ITH */
-#define PROD_ISDN_MASTER_II    (0x01)	/* ISDN-Master II */
-
-#define MANUF_VMC              (0x1389)	/* VMC */
-#define PROD_ISDN_BLASTER_Z2   (0x01)	/* ISDN Blaster Z2 */
-#define PROD_HYPERCOM_4        (0x02)	/* HyperCom 4 */
-
-#define MANUF_INFORMATION      (0x157C)	/* Information */
-#define PROD_ISDN_ENGINE_I     (0x64)	/* ISDN Engine I */
-
-#define MANUF_VORTEX           (0x2017)	/* Vortex */
-#define PROD_GOLDEN_GATE_386SX (0x07)	/* Golden Gate 80386SX Board */
-#define PROD_GOLDEN_GATE_RAM   (0x08)	/* Golden Gate RAM */
-#define PROD_GOLDEN_GATE_486   (0x09)	/* Golden Gate 80486 Board */
-
-#define MANUF_DATAFLYER        (0x2062)	/* DataFlyer */
-#define PROD_DATAFLYER_4000SXS (0x01)	/* DataFlyer 4000SX SCSI Controller */
-#define PROD_DATAFLYER_4000SXR (0x02)	/* DataFlyer 4000SX RAM */
-
-#define MANUF_READYSOFT        (0x2100)	/* ReadySoft */
-#define PROD_AMAX              (0x01)	/* AMax II/IV */
-
-#define MANUF_PHASE5           (0x2140)	/* Phase5 */
-#define PROD_BLIZZARD_RAM      (0x01)	/* Blizzard RAM */
-#define PROD_BLIZZARD          (0x02)	/* Blizzard */
-#define PROD_BLIZZARD_1220_IV  (0x06)	/* Blizzard 1220-IV Turbo Board */
-#define PROD_FASTLANE_RAM      (0x0A)	/* FastLane RAM */
-#define PROD_FASTLANE_SCSI     (0x0B)	/* FastLane/Blizzard 1230-II SCSI/CyberSCSI */
-#define PROD_CYBERSTORM_SCSI   (0x0C)	/* Blizzard 1220/CyberStorm */
-#define PROD_BLIZZARD_1230_III (0x0D)	/* Blizzard 1230-III Turbo Board */
-#define PROD_BLIZZARD_1230_IV  (0x11)	/* Blizzard 1230-IV/1260 Turbo Board */
-#define PROD_BLIZZARD_2060SCSI (0x18)	/* Blizzard 2060 SCSI Controller */
-#define PROD_CYBERSTORM_II     (0x19)	/* CyberStorm Mk II */
-#define PROD_CYBERVISION       (0x22)	/* CyberVision64 Graphics Board */
-#define PROD_CYBERVISION3D_PRT (0x32)	/* CyberVision64-3D Prototype */
-#define PROD_CYBERVISION3D     (0x43)	/* CyberVision64-3D Graphics Board */
-
-#define MANUF_DPS              (0x2169)	/* DPS */
-#define PROD_DPS_PAR           (0x01)	/* Personal Animation Recorder */
-
-#define MANUF_APOLLO2          (0x2200)	/* Apollo */
-#define PROD_A620              (0x00)	/* A620 68020 Accelerator */
-#define PROD_A620_2            (0x01)	/* A620 68020 Accelerator */
-
-#define MANUF_APOLLO           (0x2222)	/* Apollo */
-#define PROD_AT_APOLLO         (0x22)	/* AT-Apollo */
-#define PROD_APOLLO_TURBO      (0x23)	/* Apollo Turbo Board */
-
-#define MANUF_PETSOFF          (0x38A5)	/* Petsoff LP */
-#define PROD_DELFINA           (0x00)	/* Delfina DSP */
-
-#define MANUF_UWE_GERLACH      (0x3FF7)	/* Uwe Gerlach */
-#define PROD_UG_RAM_ROM        (0xd4)	/* RAM/ROM */
-
-#define MANUF_MACROSYSTEMS2    (0x4754)	/* MacroSystems Germany */
-#define PROD_MAESTRO           (0x03)	/* Maestro */
-#define PROD_VLAB              (0x04)	/* VLab */
-#define PROD_MAESTRO_PRO       (0x05)	/* Maestro Pro */
-#define PROD_RETINA_Z2         (0x06)	/* Retina Z2 Graphics Board */
-#define PROD_MULTI_EVOLUTION   (0x08)	/* MultiEvolution */
-#define PROD_TOCCATA           (0x0C)	/* Toccata Sound Board */
-#define PROD_RETINA_Z3         (0x10)	/* Retina Z3 Graphics Board */
-#define PROD_VLAB_MOTION       (0x12)	/* VLab Motion */
-#define PROD_ALTAIS            (0x13)	/* Altais Graphics Board */
-#define PROD_FALCON_040        (0xFD)	/* Falcon '040 Turbo Board */
-
-#define MANUF_COMBITEC         (0x6766)	/* Combitec */
-
-#define MANUF_SKI              (0x8000)	/* SKI Peripherals */
-#define PROD_MAST_FIREBALL     (0x08)	/* M.A.S.T. Fireball SCSI Controller */
-#define PROD_SKI_SCSI_SERIAL   (0x80)	/* SCSI / Dual Serial */
-
-#define MANUF_CAMERON          (0xAA01)	/* Cameron */
-#define PROD_PERSONAL_A4       (0x10)	/* Personal A4 */
-
-#define MANUF_REIS_WARE        (0xAA11)	/* Reis-Ware */
-#define PROD_RW_HANDYSCANNER   (0x11)	/* Handyscanner */
-
-
-/* Illegal Manufacturer IDs. These do NOT appear in arch/m68k/amiga/zorro.c! */
-
-#define MANUF_HACKER_INC       (0x07DB)	/* Hacker Inc. */
-#define PROD_HACKER_SCSI       (0x01)	/* Hacker Inc. SCSI Controller */
-
-#define MANUF_RES_MNGT_FORCE   (0x07DB)	/* Resource Management Force */
-#define PROD_QUICKNET          (0x02)	/* QuickNet Ethernet */
-
-#define MANUF_VECTOR2          (0x07DB)	/* Vector */
-#define PROD_CONNECTION_2      (0xE0)	/* Vector Connection */
-#define PROD_CONNECTION_3      (0xE1)	/* Vector Connection */
-#define PROD_CONNECTION_4      (0xE2)	/* Vector Connection */
-#define PROD_CONNECTION_5      (0xE3)	/* Vector Connection */
-
-
-/*
- * GVP's identifies most of their product through the 'extended
- * product code' (epc). The epc has to be and'ed with the GVP_PRODMASK
- * before the identification.
- */
 
-#define GVP_PRODMASK    (0xf8)
-#define GVP_SCSICLKMASK (0x01)
+    /*
+     *  Known Zorro Boards
+     *
+     *  Each Zorro board has a 32-bit ID of the form
+     *
+     *      mmmmmmmmmmmmmmmmppppppppeeeeeeee
+     *
+     *  with
+     *
+     *      mmmmmmmmmmmmmmmm	16-bit Manufacturer ID (assigned by CBM (sigh))
+     *      pppppppp		8-bit Product ID (assigned by manufacturer)
+     *      eeeeeeee		8-bit Extended Product ID (currently only used
+     *				for some GVP boards)
+     */
+
+
+#define ZORRO_MANUF(id)		((id) >> 16)
+#define ZORRO_PROD(id)		(((id) >> 8) & 0xff)
+#define ZORRO_EPC(id)		((id) & 0xff)
+
+#define ZORRO_ID(manuf, prod, epc) \
+    ((ZORRO_MANUF_##manuf << 16) | ((prod) << 8) | (epc))
+
+typedef u32 zorro_id;
+
+
+#define ZORRO_MANUF_PACIFIC_PERIPHERALS				0x00D3
+#define  ZORRO_PROD_PACIFIC_PERIPHERALS_SE_2000_A500		ZORRO_ID(PACIFIC_PERIPHERALS, 0x00, 0)
+#define  ZORRO_PROD_PACIFIC_PERIPHERALS_SCSI			ZORRO_ID(PACIFIC_PERIPHERALS, 0x0A, 0)
+
+#define ZORRO_MANUF_MACROSYSTEMS_USA_2				0x0100
+#define  ZORRO_PROD_MACROSYSTEMS_WARP_ENGINE			ZORRO_ID(MACROSYSTEMS_USA_2, 0x13, 0)
+
+#define ZORRO_MANUF_KUPKE_1					0x00DD
+#define  ZORRO_PROD_KUPKE_GOLEM_RAM_BOX_2MB			ZORRO_ID(KUPKE_1, 0x00, 0)
+
+#define ZORRO_MANUF_MEMPHIS					0x0100
+#define  ZORRO_PROD_MEMPHIS_STORMBRINGER			ZORRO_ID(MEMPHIS, 0x00, 0)
+
+#define ZORRO_MANUF_3_STATE					0x0200
+#define  ZORRO_PROD_3_STATE_MEGAMIX_2000			ZORRO_ID(3_STATE, 0x02, 0)
+
+#define ZORRO_MANUF_COMMODORE_BRAUNSCHWEIG			0x0201
+#define  ZORRO_PROD_CBM_A2088_A2286				ZORRO_ID(COMMODORE_BRAUNSCHWEIG, 0x01, 0)
+#define  ZORRO_PROD_CBM_A2286					ZORRO_ID(COMMODORE_BRAUNSCHWEIG, 0x02, 0)
+#define  ZORRO_PROD_CBM_A4091_1					ZORRO_ID(COMMODORE_BRAUNSCHWEIG, 0x54, 0)
+#define  ZORRO_PROD_CBM_A2386SX_1				ZORRO_ID(COMMODORE_BRAUNSCHWEIG, 0x67, 0)
+
+#define ZORRO_MANUF_COMMODORE_WEST_CHESTER_1			0x0202
+#define  ZORRO_PROD_CBM_A2090A					ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x01, 0)
+#define  ZORRO_PROD_CBM_A590_A2091_1				ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x02, 0)
+#define  ZORRO_PROD_CBM_A590_A2091_2				ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x03, 0)
+#define  ZORRO_PROD_CBM_A2090B					ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x04, 0)
+#define  ZORRO_PROD_CBM_A2060					ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x09, 0)
+#define  ZORRO_PROD_CBM_A590_A2052_A2058_A2091			ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x0A, 0)
+#define  ZORRO_PROD_CBM_A560_RAM				ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x20, 0)
+#define  ZORRO_PROD_CBM_A2232_PROTOTYPE				ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x45, 0)
+#define  ZORRO_PROD_CBM_A2232					ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x46, 0)
+#define  ZORRO_PROD_CBM_A2620					ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x50, 0)
+#define  ZORRO_PROD_CBM_A2630					ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x51, 0)
+#define  ZORRO_PROD_CBM_A4091_2					ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x54, 0)
+#define  ZORRO_PROD_CBM_A2065_1					ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x5A, 0)
+#define  ZORRO_PROD_CBM_ROMULATOR				ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x60, 0)
+#define  ZORRO_PROD_CBM_A3000_TEST_FIXTURE			ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x61, 0)
+#define  ZORRO_PROD_CBM_A2386SX_2				ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x67, 0)
+#define  ZORRO_PROD_CBM_A2065_2					ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x70, 0)
+
+#define ZORRO_MANUF_COMMODORE_WEST_CHESTER_2			0x0203
+#define  ZORRO_PROD_CBM_A2090A_CM				ZORRO_ID(COMMODORE_WEST_CHESTER_2, 0x03, 0)
+
+#define ZORRO_MANUF_PROGRESSIVE_PERIPHERALS_AND_SYSTEMS_2	0x02F4
+#define  ZORRO_PROD_PPS_EXP8000					ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS_2, 0x02, 0)
+
+#define ZORRO_MANUF_KOLFF_COMPUTER_SUPPLIES			0x02FF
+#define  ZORRO_PROD_KCS_POWER_PC_BOARD				ZORRO_ID(KOLFF_COMPUTER_SUPPLIES, 0x00, 0)
+
+#define ZORRO_MANUF_CARDCO_1					0x03EC
+#define  ZORRO_PROD_CARDCO_KRONOS_2000_1			ZORRO_ID(CARDCO_1, 0x04, 0)
+#define  ZORRO_PROD_CARDCO_A1000_1				ZORRO_ID(CARDCO_1, 0x0C, 0)
+#define  ZORRO_PROD_CARDCO_ESCORT				ZORRO_ID(CARDCO_1, 0x0E, 0)
+#define  ZORRO_PROD_CARDCO_A2410				ZORRO_ID(CARDCO_1, 0xF5, 0)
+
+#define ZORRO_MANUF_A_SQUARED					0x03ED
+#define  ZORRO_PROD_A_SQUARED_LIVE_2000				ZORRO_ID(A_SQUARED, 0x01, 0)
+
+#define ZORRO_MANUF_COMSPEC_COMMUNICATIONS			0x03EE
+#define  ZORRO_PROD_COMSPEC_COMMUNICATIONS_AX2000		ZORRO_ID(COMSPEC_COMMUNICATIONS, 0x01, 0)
+
+#define ZORRO_MANUF_ANAKIN_RESEARCH				0x03F1
+#define  ZORRO_PROD_ANAKIN_RESEARCH_EASYL			ZORRO_ID(ANAKIN_RESEARCH, 0x01, 0)
+
+#define ZORRO_MANUF_MICROBOTICS					0x03F2
+#define  ZORRO_PROD_MICROBOTICS_STARBOARD_II			ZORRO_ID(MICROBOTICS, 0x00, 0)
+#define  ZORRO_PROD_MICROBOTICS_STARDRIVE			ZORRO_ID(MICROBOTICS, 0x02, 0)
+#define  ZORRO_PROD_MICROBOTICS_8_UP_A				ZORRO_ID(MICROBOTICS, 0x03, 0)
+#define  ZORRO_PROD_MICROBOTICS_8_UP_Z				ZORRO_ID(MICROBOTICS, 0x04, 0)
+#define  ZORRO_PROD_MICROBOTICS_DELTA_RAM			ZORRO_ID(MICROBOTICS, 0x20, 0)
+#define  ZORRO_PROD_MICROBOTICS_8_STAR_RAM			ZORRO_ID(MICROBOTICS, 0x40, 0)
+#define  ZORRO_PROD_MICROBOTICS_8_STAR				ZORRO_ID(MICROBOTICS, 0x41, 0)
+#define  ZORRO_PROD_MICROBOTICS_VXL_RAM_32			ZORRO_ID(MICROBOTICS, 0x44, 0)
+#define  ZORRO_PROD_MICROBOTICS_VXL_68030			ZORRO_ID(MICROBOTICS, 0x45, 0)
+#define  ZORRO_PROD_MICROBOTICS_DELTA				ZORRO_ID(MICROBOTICS, 0x60, 0)
+#define  ZORRO_PROD_MICROBOTICS_MBX_1200_1200Z_RAM		ZORRO_ID(MICROBOTICS, 0x81, 0)
+#define  ZORRO_PROD_MICROBOTICS_HARDFRAME_2000_1		ZORRO_ID(MICROBOTICS, 0x96, 0)
+#define  ZORRO_PROD_MICROBOTICS_HARDFRAME_2000_2		ZORRO_ID(MICROBOTICS, 0x9E, 0)
+#define  ZORRO_PROD_MICROBOTICS_MBX_1200_1200Z			ZORRO_ID(MICROBOTICS, 0xC1, 0)
+
+#define ZORRO_MANUF_ACCESS_ASSOCIATES_ALEGRA			0x03F4
+
+#define ZORRO_MANUF_EXPANSION_TECHNOLOGIES			0x03F6
+
+#define ZORRO_MANUF_ASDG					0x03FF
+#define  ZORRO_PROD_ASDG_MEMORY_1				ZORRO_ID(ASDG, 0x01, 0)
+#define  ZORRO_PROD_ASDG_MEMORY_2				ZORRO_ID(ASDG, 0x02, 0)
+#define  ZORRO_PROD_ASDG_EB920_LAN_ROVER			ZORRO_ID(ASDG, 0xFE, 0)
+#define  ZORRO_PROD_ASDG_GPIB_DUALIEEE488_TWIN_X		ZORRO_ID(ASDG, 0xFF, 0)
+
+#define ZORRO_MANUF_IMTRONICS_1					0x0404
+#define  ZORRO_PROD_IMTRONICS_HURRICANE_2800_1			ZORRO_ID(IMTRONICS_1, 0x39, 0)
+#define  ZORRO_PROD_IMTRONICS_HURRICANE_2800_2			ZORRO_ID(IMTRONICS_1, 0x57, 0)
+
+#define ZORRO_MANUF_CBM_UNIVERSITY_OF_LOWELL			0x0406
+#define  ZORRO_PROD_CBM_A2410					ZORRO_ID(CBM_UNIVERSITY_OF_LOWELL, 0x00, 0)
+
+#define ZORRO_MANUF_AMERISTAR					0x041D
+#define  ZORRO_PROD_AMERISTAR_A2065				ZORRO_ID(AMERISTAR, 0x01, 0)
+#define  ZORRO_PROD_AMERISTAR_A560				ZORRO_ID(AMERISTAR, 0x09, 0)
+#define  ZORRO_PROD_AMERISTAR_A4066				ZORRO_ID(AMERISTAR, 0x0A, 0)
+
+#define ZORRO_MANUF_SUPRA					0x0420
+#define  ZORRO_PROD_SUPRA_SUPRADRIVE_4x4			ZORRO_ID(SUPRA, 0x01, 0)
+#define  ZORRO_PROD_SUPRA_1000_RAM				ZORRO_ID(SUPRA, 0x02, 0)
+#define  ZORRO_PROD_SUPRA_2000_DMA				ZORRO_ID(SUPRA, 0x03, 0)
+#define  ZORRO_PROD_SUPRA_500					ZORRO_ID(SUPRA, 0x05, 0)
+#define  ZORRO_PROD_SUPRA_500_SCSI				ZORRO_ID(SUPRA, 0x08, 0)
+#define  ZORRO_PROD_SUPRA_500XP_2000_RAM			ZORRO_ID(SUPRA, 0x09, 0)
+#define  ZORRO_PROD_SUPRA_500RX_2000_RAM			ZORRO_ID(SUPRA, 0x0A, 0)
+#define  ZORRO_PROD_SUPRA_2400ZI				ZORRO_ID(SUPRA, 0x0B, 0)
+#define  ZORRO_PROD_SUPRA_500XP_SUPRADRIVE_WORDSYNC		ZORRO_ID(SUPRA, 0x0C, 0)
+#define  ZORRO_PROD_SUPRA_SUPRADRIVE_WORDSYNC_II		ZORRO_ID(SUPRA, 0x0D, 0)
+#define  ZORRO_PROD_SUPRA_2400ZIPLUS				ZORRO_ID(SUPRA, 0x10, 0)
+
+#define ZORRO_MANUF_COMPUTER_SYSTEMS_ASSOCIATES			0x0422
+#define  ZORRO_PROD_CSA_MAGNUM					ZORRO_ID(COMPUTER_SYSTEMS_ASSOCIATES, 0x11, 0)
+#define  ZORRO_PROD_CSA_12_GAUGE				ZORRO_ID(COMPUTER_SYSTEMS_ASSOCIATES, 0x15, 0)
+
+#define ZORRO_MANUF_MARC_MICHAEL_GROTH				0x0439
+
+#define ZORRO_MANUF_M_TECH					0x0502
+#define  ZORRO_PROD_MTEC_AT500_1				ZORRO_ID(M_TECH, 0x03, 0)
+
+#define ZORRO_MANUF_GREAT_VALLEY_PRODUCTS_1			0x06E1
+#define  ZORRO_PROD_GVP_IMPACT_SERIES_I				ZORRO_ID(GREAT_VALLEY_PRODUCTS_1, 0x08, 0)
+
+#define ZORRO_MANUF_BYTEBOX					0x07DA
+#define  ZORRO_PROD_BYTEBOX_A500				ZORRO_ID(BYTEBOX, 0x00, 0)
+
+#define ZORRO_MANUF_DKB_POWER_COMPUTING				0x07DC
+#define  ZORRO_PROD_DKB_POWER_COMPUTING_SECUREKEY		ZORRO_ID(DKB_POWER_COMPUTING, 0x09, 0)
+#define  ZORRO_PROD_DKB_POWER_COMPUTING_DKM_3128		ZORRO_ID(DKB_POWER_COMPUTING, 0x0E, 0)
+#define  ZORRO_PROD_DKB_POWER_COMPUTING_RAPID_FIRE		ZORRO_ID(DKB_POWER_COMPUTING, 0x0F, 0)
+#define  ZORRO_PROD_DKB_POWER_COMPUTING_DKM_1202		ZORRO_ID(DKB_POWER_COMPUTING, 0x10, 0)
+#define  ZORRO_PROD_DKB_POWER_COMPUTING_COBRA_VIPER_II_68EC030	ZORRO_ID(DKB_POWER_COMPUTING, 0x12, 0)
+#define  ZORRO_PROD_DKB_POWER_COMPUTING_WILDFIRE_060_1		ZORRO_ID(DKB_POWER_COMPUTING, 0x17, 0)
+#define  ZORRO_PROD_DKB_POWER_COMPUTING_WILDFIRE_060_2		ZORRO_ID(DKB_POWER_COMPUTING, 0xFF, 0)
+
+#define ZORRO_MANUF_GREAT_VALLEY_PRODUCTS_2			0x07E1
+#define  ZORRO_PROD_GVP_IMPACT_SERIES_I_4K			ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x01, 0)
+#define  ZORRO_PROD_GVP_IMPACT_SERIES_I_16K_2			ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x02, 0)
+#define  ZORRO_PROD_GVP_IMPACT_SERIES_I_16K_3			ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x03, 0)
+#define  ZORRO_PROD_GVP_IMPACT_3001_IDE_1			ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x08, 0)
+#define  ZORRO_PROD_GVP_IMPACT_3001_RAM				ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x09, 0)
+#define  ZORRO_PROD_GVP_IMPACT_SERIES_II_RAM_1			ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0A, 0)
+#define  ZORRO_PROD_GVP_EPC_BASE				ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0)
+#define  ZORRO_PROD_GVP_GFORCE_040_1				ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x20)
+#define  ZORRO_PROD_GVP_GFORCE_040_SCSI_1			ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x30)
+#define  ZORRO_PROD_GVP_A1291					ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x40)
+#define  ZORRO_PROD_GVP_COMBO_030_R4				ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x60)
+#define  ZORRO_PROD_GVP_COMBO_030_R4_SCSI			ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x70)
+#define  ZORRO_PROD_GVP_PHONEPAK				ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x78)
+#define  ZORRO_PROD_GVP_IO_EXTENDER				ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x98)
+#define  ZORRO_PROD_GVP_GFORCE_030				ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xa0)
+#define  ZORRO_PROD_GVP_GFORCE_030_SCSI				ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xb0)
+#define  ZORRO_PROD_GVP_A530					ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xc0)
+#define  ZORRO_PROD_GVP_A530_SCSI				ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xd0)
+#define  ZORRO_PROD_GVP_COMBO_030_R3				ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xe0)
+#define  ZORRO_PROD_GVP_COMBO_030_R3_SCSI			ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xf0)
+#define  ZORRO_PROD_GVP_SERIES_II				ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xf8)
+#define  ZORRO_PROD_GVP_IMPACT_3001_IDE_2			ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0D, 0)
+/*#define  ZORRO_PROD_GVP_A2000_030				ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0D, 0)*/
+/*#define  ZORRO_PROD_GVP_GFORCE_040_SCSI_2			ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0D, 0)*/
+#define  ZORRO_PROD_GVP_GFORCE_040_060				ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x16, 0)
+#define  ZORRO_PROD_GVP_IMPACT_VISION_24			ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x20, 0)
+#define  ZORRO_PROD_GVP_GFORCE_040_2				ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0xFF, 0)
+
+#define ZORRO_MANUF_CALIFORNIA_ACCESS_SYNERGY			0x07E5
+#define  ZORRO_PROD_CALIFORNIA_ACCESS_SYNERGY_MALIBU		ZORRO_ID(CALIFORNIA_ACCESS_SYNERGY, 0x01, 0)
+
+#define ZORRO_MANUF_XETEC					0x07E6
+#define  ZORRO_PROD_XETEC_FASTCARD				ZORRO_ID(XETEC, 0x01, 0)
+#define  ZORRO_PROD_XETEC_FASTCARD_RAM				ZORRO_ID(XETEC, 0x02, 0)
+#define  ZORRO_PROD_XETEC_FASTCARD_PLUS				ZORRO_ID(XETEC, 0x03, 0)
+
+#define ZORRO_MANUF_PROGRESSIVE_PERIPHERALS_AND_SYSTEMS		0x07EA
+#define  ZORRO_PROD_PPS_MERCURY					ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0x00, 0)
+#define  ZORRO_PROD_PPS_A3000_68040				ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0x01, 0)
+#define  ZORRO_PROD_PPS_A2000_68040				ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0x69, 0)
+#define  ZORRO_PROD_PPS_ZEUS					ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0x96, 0)
+#define  ZORRO_PROD_PPS_A500_68040				ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0xBB, 0)
+
+#define ZORRO_MANUF_XEBEC					0x07EC
+
+#define ZORRO_MANUF_SPIRIT_TECHNOLOGY				0x07F2
+#define  ZORRO_PROD_SPIRIT_TECHNOLOGY_INSIDER_IN1000		ZORRO_ID(SPIRIT_TECHNOLOGY, 0x01, 0)
+#define  ZORRO_PROD_SPIRIT_TECHNOLOGY_INSIDER_IN500		ZORRO_ID(SPIRIT_TECHNOLOGY, 0x02, 0)
+#define  ZORRO_PROD_SPIRIT_TECHNOLOGY_SIN500			ZORRO_ID(SPIRIT_TECHNOLOGY, 0x03, 0)
+#define  ZORRO_PROD_SPIRIT_TECHNOLOGY_HDA_506			ZORRO_ID(SPIRIT_TECHNOLOGY, 0x04, 0)
+#define  ZORRO_PROD_SPIRIT_TECHNOLOGY_AX_S			ZORRO_ID(SPIRIT_TECHNOLOGY, 0x05, 0)
+#define  ZORRO_PROD_SPIRIT_TECHNOLOGY_OCTABYTE			ZORRO_ID(SPIRIT_TECHNOLOGY, 0x06, 0)
+#define  ZORRO_PROD_SPIRIT_TECHNOLOGY_INMATE			ZORRO_ID(SPIRIT_TECHNOLOGY, 0x08, 0)
+
+#define ZORRO_MANUF_SPIRIT_TECHNOLOGY_2				0x07F3
+
+#define ZORRO_MANUF_BSC_ALFADATA_1				0x07FE
+#define  ZORRO_PROD_BSC_ALF_3_1					ZORRO_ID(BSC_ALFADATA_1, 0x03, 0)
+
+#define ZORRO_MANUF_BSC_ALFADATA_2				0x0801
+#define  ZORRO_PROD_BSC_ALF_2_1					ZORRO_ID(BSC_ALFADATA_2, 0x01, 0)
+#define  ZORRO_PROD_BSC_ALF_2_2					ZORRO_ID(BSC_ALFADATA_2, 0x02, 0)
+#define  ZORRO_PROD_BSC_ALF_3_2					ZORRO_ID(BSC_ALFADATA_2, 0x03, 0)
+
+#define ZORRO_MANUF_CARDCO_2					0x0802
+#define  ZORRO_PROD_CARDCO_KRONOS_2000_2			ZORRO_ID(CARDCO_2, 0x04, 0)
+#define  ZORRO_PROD_CARDCO_A1000_2				ZORRO_ID(CARDCO_2, 0x0C, 0)
+
+#define ZORRO_MANUF_JOCHHEIM					0x0804
+#define  ZORRO_PROD_JOCHHEIM_RAM				ZORRO_ID(JOCHHEIM, 0x01, 0)
+
+#define ZORRO_MANUF_CHECKPOINT_TECHNOLOGIES			0x0807
+#define  ZORRO_PROD_CHECKPOINT_TECHNOLOGIES_SERIAL_SOLUTION	ZORRO_ID(CHECKPOINT_TECHNOLOGIES, 0x00, 0)
+
+#define ZORRO_MANUF_EDOTRONIK					0x0810
+#define  ZORRO_PROD_EDOTRONIK_IEEE_488				ZORRO_ID(EDOTRONIK, 0x01, 0)
+#define  ZORRO_PROD_EDOTRONIK_8032				ZORRO_ID(EDOTRONIK, 0x02, 0)
+#define  ZORRO_PROD_EDOTRONIK_MULTISERIAL			ZORRO_ID(EDOTRONIK, 0x03, 0)
+#define  ZORRO_PROD_EDOTRONIK_VIDEODIGITIZER			ZORRO_ID(EDOTRONIK, 0x04, 0)
+#define  ZORRO_PROD_EDOTRONIK_PARALLEL_IO			ZORRO_ID(EDOTRONIK, 0x05, 0)
+#define  ZORRO_PROD_EDOTRONIK_PIC_PROTOYPING			ZORRO_ID(EDOTRONIK, 0x06, 0)
+#define  ZORRO_PROD_EDOTRONIK_ADC				ZORRO_ID(EDOTRONIK, 0x07, 0)
+#define  ZORRO_PROD_EDOTRONIK_VME				ZORRO_ID(EDOTRONIK, 0x08, 0)
+#define  ZORRO_PROD_EDOTRONIK_DSP96000				ZORRO_ID(EDOTRONIK, 0x09, 0)
+
+#define ZORRO_MANUF_NES_INC					0x0813
+#define  ZORRO_PROD_NES_INC_RAM					ZORRO_ID(NES_INC, 0x00, 0)
+
+#define ZORRO_MANUF_ICD						0x0817
+#define  ZORRO_PROD_ICD_ADVANTAGE_2000_SCSI			ZORRO_ID(ICD, 0x01, 0)
+#define  ZORRO_PROD_ICD_ADVANTAGE_IDE				ZORRO_ID(ICD, 0x03, 0)
+#define  ZORRO_PROD_ICD_ADVANTAGE_2080_RAM			ZORRO_ID(ICD, 0x04, 0)
+
+#define ZORRO_MANUF_KUPKE_2					0x0819
+#define  ZORRO_PROD_KUPKE_OMTI					ZORRO_ID(KUPKE_2, 0x01, 0)
+#define  ZORRO_PROD_KUPKE_SCSI_II				ZORRO_ID(KUPKE_2, 0x02, 0)
+#define  ZORRO_PROD_KUPKE_GOLEM_BOX				ZORRO_ID(KUPKE_2, 0x03, 0)
+#define  ZORRO_PROD_KUPKE_030_882				ZORRO_ID(KUPKE_2, 0x04, 0)
+#define  ZORRO_PROD_KUPKE_SCSI_AT				ZORRO_ID(KUPKE_2, 0x05, 0)
+
+#define ZORRO_MANUF_GREAT_VALLEY_PRODUCTS_3			0x081D
+#define  ZORRO_PROD_GVP_A2000_RAM8				ZORRO_ID(GREAT_VALLEY_PRODUCTS_3, 0x09, 0)
+#define  ZORRO_PROD_GVP_IMPACT_SERIES_II_RAM_2			ZORRO_ID(GREAT_VALLEY_PRODUCTS_3, 0x0A, 0)
+
+#define ZORRO_MANUF_INTERWORKS_NETWORK				0x081E
+
+#define ZORRO_MANUF_HARDITAL_SYNTHESIS				0x0820
+#define  ZORRO_PROD_HARDITAL_SYNTHESIS_TQM_68030_68882		ZORRO_ID(HARDITAL_SYNTHESIS, 0x14, 0)
+
+#define ZORRO_MANUF_APPLIED_ENGINEERING				0x0828
+#define  ZORRO_PROD_APPLIED_ENGINEERING_DL2000			ZORRO_ID(APPLIED_ENGINEERING, 0x10, 0)
+#define  ZORRO_PROD_APPLIED_ENGINEERING_RAM_WORKS		ZORRO_ID(APPLIED_ENGINEERING, 0xE0, 0)
+
+#define ZORRO_MANUF_BSC_ALFADATA_3				0x082C
+#define  ZORRO_PROD_BSC_OKTAGON_2008				ZORRO_ID(BSC_ALFADATA_3, 0x05, 0)
+#define  ZORRO_PROD_BSC_TANDEM_AT_2008_508			ZORRO_ID(BSC_ALFADATA_3, 0x06, 0)
+#define  ZORRO_PROD_BSC_ALFA_RAM_1200				ZORRO_ID(BSC_ALFADATA_3, 0x07, 0)
+#define  ZORRO_PROD_BSC_OKTAGON_2008_RAM			ZORRO_ID(BSC_ALFADATA_3, 0x08, 0)
+#define  ZORRO_PROD_BSC_MULTIFACE_I				ZORRO_ID(BSC_ALFADATA_3, 0x10, 0)
+#define  ZORRO_PROD_BSC_MULTIFACE_II				ZORRO_ID(BSC_ALFADATA_3, 0x11, 0)
+#define  ZORRO_PROD_BSC_MULTIFACE_III				ZORRO_ID(BSC_ALFADATA_3, 0x12, 0)
+#define  ZORRO_PROD_BSC_FRAMEBUFFER				ZORRO_ID(BSC_ALFADATA_3, 0x20, 0)
+#define  ZORRO_PROD_BSC_GRAFFITI_RAM				ZORRO_ID(BSC_ALFADATA_3, 0x21, 0)
+#define  ZORRO_PROD_BSC_GRAFFITI_REG				ZORRO_ID(BSC_ALFADATA_3, 0x22, 0)
+#define  ZORRO_PROD_BSC_ISDN_MASTERCARD				ZORRO_ID(BSC_ALFADATA_3, 0x40, 0)
+#define  ZORRO_PROD_BSC_ISDN_MASTERCARD_II			ZORRO_ID(BSC_ALFADATA_3, 0x41, 0)
+
+#define ZORRO_MANUF_PHOENIX					0x0835
+#define  ZORRO_PROD_PHOENIX_ST506				ZORRO_ID(PHOENIX, 0x21, 0)
+#define  ZORRO_PROD_PHOENIX_SCSI				ZORRO_ID(PHOENIX, 0x22, 0)
+#define  ZORRO_PROD_PHOENIX_RAM					ZORRO_ID(PHOENIX, 0xBE, 0)
+
+#define ZORRO_MANUF_ADVANCED_STORAGE_SYSTEMS			0x0836
+#define  ZORRO_PROD_ADVANCED_STORAGE_SYSTEMS_NEXUS		ZORRO_ID(ADVANCED_STORAGE_SYSTEMS, 0x01, 0)
+#define  ZORRO_PROD_ADVANCED_STORAGE_SYSTEMS_NEXUS_RAM		ZORRO_ID(ADVANCED_STORAGE_SYSTEMS, 0x08, 0)
+
+#define ZORRO_MANUF_IMPULSE					0x0838
+#define  ZORRO_PROD_IMPULSE_FIRECRACKER_24			ZORRO_ID(IMPULSE, 0x00, 0)
+
+#define ZORRO_MANUF_IVS						0x0840
+#define  ZORRO_PROD_IVS_GRANDSLAM_PIC_2				ZORRO_ID(IVS, 0x02, 0)
+#define  ZORRO_PROD_IVS_GRANDSLAM_PIC_1				ZORRO_ID(IVS, 0x04, 0)
+#define  ZORRO_PROD_IVS_OVERDRIVE				ZORRO_ID(IVS, 0x10, 0)
+#define  ZORRO_PROD_IVS_TRUMPCARD_CLASSIC			ZORRO_ID(IVS, 0x30, 0)
+#define  ZORRO_PROD_IVS_TRUMPCARD_PRO_GRANDSLAM			ZORRO_ID(IVS, 0x34, 0)
+#define  ZORRO_PROD_IVS_META_4					ZORRO_ID(IVS, 0x40, 0)
+#define  ZORRO_PROD_IVS_WAVETOOLS				ZORRO_ID(IVS, 0xBF, 0)
+#define  ZORRO_PROD_IVS_VECTOR_1				ZORRO_ID(IVS, 0xF3, 0)
+#define  ZORRO_PROD_IVS_VECTOR_2				ZORRO_ID(IVS, 0xF4, 0)
+
+#define ZORRO_MANUF_VECTOR_1					0x0841
+#define  ZORRO_PROD_VECTOR_CONNECTION_1				ZORRO_ID(VECTOR_1, 0xE3, 0)
+
+#define ZORRO_MANUF_XPERT_PRODEV				0x0845
+#define  ZORRO_PROD_XPERT_PRODEV_VISIONA_RAM			ZORRO_ID(XPERT_PRODEV, 0x01, 0)
+#define  ZORRO_PROD_XPERT_PRODEV_VISIONA_REG			ZORRO_ID(XPERT_PRODEV, 0x02, 0)
+#define  ZORRO_PROD_XPERT_PRODEV_MERLIN_RAM			ZORRO_ID(XPERT_PRODEV, 0x03, 0)
+#define  ZORRO_PROD_XPERT_PRODEV_MERLIN_REG_1			ZORRO_ID(XPERT_PRODEV, 0x04, 0)
+#define  ZORRO_PROD_XPERT_PRODEV_MERLIN_REG_2			ZORRO_ID(XPERT_PRODEV, 0xC9, 0)
+
+#define ZORRO_MANUF_HYDRA_SYSTEMS				0x0849
+#define  ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET			ZORRO_ID(HYDRA_SYSTEMS, 0x01, 0)
+
+#define ZORRO_MANUF_SUNRIZE_INDUSTRIES				0x084F
+#define  ZORRO_PROD_SUNRIZE_INDUSTRIES_AD1012			ZORRO_ID(SUNRIZE_INDUSTRIES, 0x01, 0)
+#define  ZORRO_PROD_SUNRIZE_INDUSTRIES_AD516			ZORRO_ID(SUNRIZE_INDUSTRIES, 0x02, 0)
+#define  ZORRO_PROD_SUNRIZE_INDUSTRIES_DD512			ZORRO_ID(SUNRIZE_INDUSTRIES, 0x03, 0)
+
+#define ZORRO_MANUF_TRICERATOPS					0x0850
+#define  ZORRO_PROD_TRICERATOPS_MULTI_IO			ZORRO_ID(TRICERATOPS, 0x01, 0)
+
+#define ZORRO_MANUF_APPLIED_MAGIC				0x0851
+#define  ZORRO_PROD_APPLIED_MAGIC_DMI_RESOLVER			ZORRO_ID(APPLIED_MAGIC, 0x01, 0)
+#define  ZORRO_PROD_APPLIED_MAGIC_DIGITAL_BROADCASTER		ZORRO_ID(APPLIED_MAGIC, 0x06, 0)
+
+#define ZORRO_MANUF_GFX_BASE					0x085E
+#define  ZORRO_PROD_GFX_BASE_GDA_1_VRAM				ZORRO_ID(GFX_BASE, 0x00, 0)
+#define  ZORRO_PROD_GFX_BASE_GDA_1				ZORRO_ID(GFX_BASE, 0x01, 0)
+
+#define ZORRO_MANUF_ROCTEC					0x0860
+#define  ZORRO_PROD_ROCTEC_RH_800C				ZORRO_ID(ROCTEC, 0x01, 0)
+#define  ZORRO_PROD_ROCTEC_RH_800C_RAM				ZORRO_ID(ROCTEC, 0x01, 0)
+
+#define ZORRO_MANUF_KATO					0x0861
+#define  ZORRO_PROD_KATO_MELODY					ZORRO_ID(KATO, 0x80, 0)
+/* ID clash!! */
+#define ZORRO_MANUF_HELFRICH_1					0x0861
+#define  ZORRO_PROD_HELFRICH_RAINBOW_II				ZORRO_ID(HELFRICH_1, 0x20, 0)
+#define  ZORRO_PROD_HELFRICH_RAINBOW_III			ZORRO_ID(HELFRICH_1, 0x21, 0)
+
+#define ZORRO_MANUF_ATLANTIS					0x0862
+
+#define ZORRO_MANUF_PROTAR					0x0864
+
+#define ZORRO_MANUF_ACS						0x0865
+
+#define ZORRO_MANUF_SOFTWARE_RESULTS_ENTERPRISES		0x0866
+#define  ZORRO_PROD_SOFTWARE_RESULTS_ENTERPRISES_GOLDEN_GATE_2_BUS_PLUS	ZORRO_ID(SOFTWARE_RESULTS_ENTERPRISES, 0x01, 0)
+
+#define ZORRO_MANUF_MASOBOSHI					0x086D
+#define  ZORRO_PROD_MASOBOSHI_MASTER_CARD_SC201			ZORRO_ID(MASOBOSHI, 0x03, 0)
+#define  ZORRO_PROD_MASOBOSHI_MASTER_CARD_MC702			ZORRO_ID(MASOBOSHI, 0x04, 0)
+#define  ZORRO_PROD_MASOBOSHI_MVD_819				ZORRO_ID(MASOBOSHI, 0x07, 0)
+
+#define ZORRO_MANUF_MAINHATTAN_DATA				0x086F
+#define  ZORRO_PROD_MAINHATTAN_DATA_IDE				ZORRO_ID(MAINHATTAN_DATA, 0x01, 0)
+
+#define ZORRO_MANUF_VILLAGE_TRONIC				0x0877
+#define  ZORRO_PROD_VILLAGE_TRONIC_DOMINO_RAM			ZORRO_ID(VILLAGE_TRONIC, 0x01, 0)
+#define  ZORRO_PROD_VILLAGE_TRONIC_DOMINO_REG			ZORRO_ID(VILLAGE_TRONIC, 0x02, 0)
+#define  ZORRO_PROD_VILLAGE_TRONIC_DOMINO_16M_PROTOTYPE		ZORRO_ID(VILLAGE_TRONIC, 0x03, 0)
+#define  ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM	ZORRO_ID(VILLAGE_TRONIC, 0x0B, 0)
+#define  ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG	ZORRO_ID(VILLAGE_TRONIC, 0x0C, 0)
+#define  ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_SEGMENTED_MODE	ZORRO_ID(VILLAGE_TRONIC, 0x0D, 0)
+#define  ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_MEM1		ZORRO_ID(VILLAGE_TRONIC, 0x15, 0)
+#define  ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_MEM2		ZORRO_ID(VILLAGE_TRONIC, 0x16, 0)
+#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_MANUF_UTILITIES_UNLIMITED				0x087B
+#define  ZORRO_PROD_UTILITIES_UNLIMITED_EMPLANT_DELUXE		ZORRO_ID(UTILITIES_UNLIMITED, 0x15, 0)
+#define  ZORRO_PROD_UTILITIES_UNLIMITED_EMPLANT_DELUXE2		ZORRO_ID(UTILITIES_UNLIMITED, 0x20, 0)
+
+#define ZORRO_MANUF_AMITRIX					0x0880
+#define  ZORRO_PROD_AMITRIX_MULTI_IO				ZORRO_ID(AMITRIX, 0x01, 0)
+#define  ZORRO_PROD_AMITRIX_CD_RAM				ZORRO_ID(AMITRIX, 0x02, 0)
+
+#define ZORRO_MANUF_ARMAX					0x0885
+#define  ZORRO_PROD_ARMAX_OMNIBUS				ZORRO_ID(ARMAX, 0x00, 0)
+
+#define ZORRO_MANUF_NEWTEK					0x088F
+#define  ZORRO_PROD_NEWTEK_VIDEOTOASTER				ZORRO_ID(NEWTEK, 0x00, 0)
+
+#define ZORRO_MANUF_M_TECH_GERMANY				0x0890
+#define  ZORRO_PROD_MTEC_AT500_2				ZORRO_ID(M_TECH_GERMANY, 0x01, 0)
+#define  ZORRO_PROD_MTEC_68030					ZORRO_ID(M_TECH_GERMANY, 0x03, 0)
+#define  ZORRO_PROD_MTEC_68020I					ZORRO_ID(M_TECH_GERMANY, 0x06, 0)
+#define  ZORRO_PROD_MTEC_A1200_T68030_RTC			ZORRO_ID(M_TECH_GERMANY, 0x20, 0)
+#define  ZORRO_PROD_MTEC_VIPER_MK_V_E_MATRIX_530		ZORRO_ID(M_TECH_GERMANY, 0x21, 0)
+#define  ZORRO_PROD_MTEC_8_MB_RAM				ZORRO_ID(M_TECH_GERMANY, 0x22, 0)
+#define  ZORRO_PROD_MTEC_VIPER_MK_V_E_MATRIX_530_SCSI_IDE	ZORRO_ID(M_TECH_GERMANY, 0x24, 0)
+
+#define ZORRO_MANUF_GREAT_VALLEY_PRODUCTS_4			0x0891
+#define  ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG			ZORRO_ID(GREAT_VALLEY_PRODUCTS_4, 0x01, 0)
+#define  ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM			ZORRO_ID(GREAT_VALLEY_PRODUCTS_4, 0x02, 0)
+
+#define ZORRO_MANUF_APOLLO_1					0x0892
+#define  ZORRO_PROD_APOLLO_A1200				ZORRO_ID(APOLLO_1, 0x01, 0)
+
+#define ZORRO_MANUF_HELFRICH_2					0x0893
+#define  ZORRO_PROD_HELFRICH_PICCOLO_RAM			ZORRO_ID(HELFRICH_2, 0x05, 0)
+#define  ZORRO_PROD_HELFRICH_PICCOLO_REG			ZORRO_ID(HELFRICH_2, 0x06, 0)
+#define  ZORRO_PROD_HELFRICH_PEGGY_PLUS_MPEG			ZORRO_ID(HELFRICH_2, 0x07, 0)
+#define  ZORRO_PROD_HELFRICH_VIDEOCRUNCHER			ZORRO_ID(HELFRICH_2, 0x08, 0)
+#define  ZORRO_PROD_HELFRICH_SD64_RAM				ZORRO_ID(HELFRICH_2, 0x0A, 0)
+#define  ZORRO_PROD_HELFRICH_SD64_REG				ZORRO_ID(HELFRICH_2, 0x0B, 0)
+
+#define ZORRO_MANUF_MACROSYSTEMS_USA				0x089B
+#define  ZORRO_PROD_MACROSYSTEMS_WARP_ENGINE_40xx		ZORRO_ID(MACROSYSTEMS_USA, 0x13, 0)
+
+#define ZORRO_MANUF_ELBOX_COMPUTER				0x089E
+#define  ZORRO_PROD_ELBOX_COMPUTER_1200_4			ZORRO_ID(ELBOX_COMPUTER, 0x06, 0)
+
+#define ZORRO_MANUF_HARMS_PROFESSIONAL				0x0A00
+#define  ZORRO_PROD_HARMS_PROFESSIONAL_030_PLUS			ZORRO_ID(HARMS_PROFESSIONAL, 0x10, 0)
+#define  ZORRO_PROD_HARMS_PROFESSIONAL_3500			ZORRO_ID(HARMS_PROFESSIONAL, 0xD0, 0)
+
+#define ZORRO_MANUF_MICRONIK					0x0A50
+#define  ZORRO_PROD_MICRONIK_RCA_120				ZORRO_ID(MICRONIK, 0x0A, 0)
+
+#define ZORRO_MANUF_MICRONIK2					0x0F0F
+#define  ZORRO_PROD_MICRONIK2_Z3I				ZORRO_ID(MICRONIK2, 0x01, 0)
+
+#define ZORRO_MANUF_MEGAMICRO					0x1000
+#define  ZORRO_PROD_MEGAMICRO_SCRAM_500				ZORRO_ID(MEGAMICRO, 0x03, 0)
+#define  ZORRO_PROD_MEGAMICRO_SCRAM_500_RAM			ZORRO_ID(MEGAMICRO, 0x04, 0)
+
+#define ZORRO_MANUF_IMTRONICS_2					0x1028
+#define  ZORRO_PROD_IMTRONICS_HURRICANE_2800_3			ZORRO_ID(IMTRONICS_2, 0x39, 0)
+#define  ZORRO_PROD_IMTRONICS_HURRICANE_2800_4			ZORRO_ID(IMTRONICS_2, 0x57, 0)
+
+/* unofficial ID */
+#define ZORRO_MANUF_INDIVIDUAL_COMPUTERS			0x1212
+#define  ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA			ZORRO_ID(INDIVIDUAL_COMPUTERS, 0x00, 0)
+#define  ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL		ZORRO_ID(INDIVIDUAL_COMPUTERS, 0x2A, 0)
+
+#define ZORRO_MANUF_KUPKE_3					0x1248
+#define  ZORRO_PROD_KUPKE_GOLEM_HD_3000				ZORRO_ID(KUPKE_3, 0x01, 0)
+
+#define ZORRO_MANUF_ITH						0x1388
+#define  ZORRO_PROD_ITH_ISDN_MASTER_II				ZORRO_ID(ITH, 0x01, 0)
+
+#define ZORRO_MANUF_VMC						0x1389
+#define  ZORRO_PROD_VMC_ISDN_BLASTER_Z2				ZORRO_ID(VMC, 0x01, 0)
+#define  ZORRO_PROD_VMC_HYPERCOM_4				ZORRO_ID(VMC, 0x02, 0)
+
+#define ZORRO_MANUF_INFORMATION					0x157C
+#define  ZORRO_PROD_INFORMATION_ISDN_ENGINE_I			ZORRO_ID(INFORMATION, 0x64, 0)
+
+#define ZORRO_MANUF_VORTEX					0x2017
+#define  ZORRO_PROD_VORTEX_GOLDEN_GATE_80386SX			ZORRO_ID(VORTEX, 0x07, 0)
+#define  ZORRO_PROD_VORTEX_GOLDEN_GATE_RAM			ZORRO_ID(VORTEX, 0x08, 0)
+#define  ZORRO_PROD_VORTEX_GOLDEN_GATE_80486			ZORRO_ID(VORTEX, 0x09, 0)
+
+#define ZORRO_MANUF_EXPANSION_SYSTEMS				0x2062
+#define  ZORRO_PROD_EXPANSION_SYSTEMS_DATAFLYER_4000SX		ZORRO_ID(EXPANSION_SYSTEMS, 0x01, 0)
+#define  ZORRO_PROD_EXPANSION_SYSTEMS_DATAFLYER_4000SX_RAM	ZORRO_ID(EXPANSION_SYSTEMS, 0x02, 0)
+
+#define ZORRO_MANUF_READYSOFT					0x2100
+#define  ZORRO_PROD_READYSOFT_AMAX_II_IV			ZORRO_ID(READYSOFT, 0x01, 0)
+
+#define ZORRO_MANUF_PHASE5					0x2140
+#define  ZORRO_PROD_PHASE5_BLIZZARD_RAM				ZORRO_ID(PHASE5, 0x01, 0)
+#define  ZORRO_PROD_PHASE5_BLIZZARD				ZORRO_ID(PHASE5, 0x02, 0)
+#define  ZORRO_PROD_PHASE5_BLIZZARD_1220_IV			ZORRO_ID(PHASE5, 0x06, 0)
+#define  ZORRO_PROD_PHASE5_FASTLANE_Z3_RAM			ZORRO_ID(PHASE5, 0x0A, 0)
+#define  ZORRO_PROD_PHASE5_BLIZZARD_1230_II_FASTLANE_Z3_CYBERSCSI_CYBERSTORM060	ZORRO_ID(PHASE5, 0x0B, 0)
+#define  ZORRO_PROD_PHASE5_BLIZZARD_1220_CYBERSTORM		ZORRO_ID(PHASE5, 0x0C, 0)
+#define  ZORRO_PROD_PHASE5_BLIZZARD_1230			ZORRO_ID(PHASE5, 0x0D, 0)
+#define  ZORRO_PROD_PHASE5_BLIZZARD_1230_IV_1260		ZORRO_ID(PHASE5, 0x11, 0)
+#define  ZORRO_PROD_PHASE5_BLIZZARD_2060			ZORRO_ID(PHASE5, 0x18, 0)
+#define  ZORRO_PROD_PHASE5_CYBERSTORM_MK_II			ZORRO_ID(PHASE5, 0x19, 0)
+#define  ZORRO_PROD_PHASE5_CYBERVISION64			ZORRO_ID(PHASE5, 0x22, 0)
+#define  ZORRO_PROD_PHASE5_CYBERVISION64_3D_PROTOTYPE		ZORRO_ID(PHASE5, 0x32, 0)
+#define  ZORRO_PROD_PHASE5_CYBERVISION64_3D			ZORRO_ID(PHASE5, 0x43, 0)
+#define  ZORRO_PROD_PHASE5_CYBERSTORM_MK_III			ZORRO_ID(PHASE5, 0x64, 0)
+
+#define ZORRO_MANUF_DPS						0x2169
+#define  ZORRO_PROD_DPS_PERSONAL_ANIMATION_RECORDER		ZORRO_ID(DPS, 0x01, 0)
+
+#define ZORRO_MANUF_APOLLO_2					0x2200
+#define  ZORRO_PROD_APOLLO_A620_68020_1				ZORRO_ID(APOLLO_2, 0x00, 0)
+#define  ZORRO_PROD_APOLLO_A620_68020_2				ZORRO_ID(APOLLO_2, 0x01, 0)
+
+#define ZORRO_MANUF_APOLLO_3					0x2222
+#define  ZORRO_PROD_APOLLO_AT_APOLLO				ZORRO_ID(APOLLO_3, 0x22, 0)
+#define  ZORRO_PROD_APOLLO_1230_1240_1260_2030_4040_4060	ZORRO_ID(APOLLO_3, 0x23, 0)
+
+#define ZORRO_MANUF_PETSOFF_LP					0x38A5
+#define  ZORRO_PROD_PETSOFF_LP_DELFINA				ZORRO_ID(PETSOFF_LP, 0x00, 0)
+#define  ZORRO_PROD_PETSOFF_LP_DELFINA_LITE			ZORRO_ID(PETSOFF_LP, 0x01, 0)
+
+#define ZORRO_MANUF_UWE_GERLACH					0x3FF7
+#define  ZORRO_PROD_UWE_GERLACH_RAM_ROM				ZORRO_ID(UWE_GERLACH, 0xd4, 0)
+
+#define ZORRO_MANUF_MACROSYSTEMS_GERMANY			0x4754
+#define  ZORRO_PROD_MACROSYSTEMS_MAESTRO			ZORRO_ID(MACROSYSTEMS_GERMANY, 0x03, 0)
+#define  ZORRO_PROD_MACROSYSTEMS_VLAB				ZORRO_ID(MACROSYSTEMS_GERMANY, 0x04, 0)
+#define  ZORRO_PROD_MACROSYSTEMS_MAESTRO_PRO			ZORRO_ID(MACROSYSTEMS_GERMANY, 0x05, 0)
+#define  ZORRO_PROD_MACROSYSTEMS_RETINA				ZORRO_ID(MACROSYSTEMS_GERMANY, 0x06, 0)
+#define  ZORRO_PROD_MACROSYSTEMS_MULTI_EVOLUTION		ZORRO_ID(MACROSYSTEMS_GERMANY, 0x08, 0)
+#define  ZORRO_PROD_MACROSYSTEMS_TOCCATA			ZORRO_ID(MACROSYSTEMS_GERMANY, 0x0C, 0)
+#define  ZORRO_PROD_MACROSYSTEMS_RETINA_Z3			ZORRO_ID(MACROSYSTEMS_GERMANY, 0x10, 0)
+#define  ZORRO_PROD_MACROSYSTEMS_VLAB_MOTION			ZORRO_ID(MACROSYSTEMS_GERMANY, 0x12, 0)
+#define  ZORRO_PROD_MACROSYSTEMS_ALTAIS				ZORRO_ID(MACROSYSTEMS_GERMANY, 0x13, 0)
+#define  ZORRO_PROD_MACROSYSTEMS_FALCON_040			ZORRO_ID(MACROSYSTEMS_GERMANY, 0xFD, 0)
+
+#define ZORRO_MANUF_COMBITEC					0x6766
+
+#define ZORRO_MANUF_SKI_PERIPHERALS				0x8000
+#define  ZORRO_PROD_SKI_PERIPHERALS_MAST_FIREBALL		ZORRO_ID(SKI_PERIPHERALS, 0x08, 0)
+#define  ZORRO_PROD_SKI_PERIPHERALS_SCSI_DUAL_SERIAL		ZORRO_ID(SKI_PERIPHERALS, 0x80, 0)
+
+#define ZORRO_MANUF_REIS_WARE_2					0xA9AD
+#define  ZORRO_PROD_REIS_WARE_SCAN_KING				ZORRO_ID(REIS_WARE_2, 0x11, 0)
+
+#define ZORRO_MANUF_CAMERON					0xAA01
+#define  ZORRO_PROD_CAMERON_PERSONAL_A4				ZORRO_ID(CAMERON, 0x10, 0)
+
+#define ZORRO_MANUF_REIS_WARE					0xAA11
+#define  ZORRO_PROD_REIS_WARE_HANDYSCANNER			ZORRO_ID(REIS_WARE, 0x11, 0)
+
+#define ZORRO_MANUF_PHOENIX_2					0xB5A8
+#define  ZORRO_PROD_PHOENIX_ST506_2				ZORRO_ID(PHOENIX_2, 0x21, 0)
+#define  ZORRO_PROD_PHOENIX_SCSI_2				ZORRO_ID(PHOENIX_2, 0x22, 0)
+#define  ZORRO_PROD_PHOENIX_RAM_2				ZORRO_ID(PHOENIX_2, 0xBE, 0)
+
+#define ZORRO_MANUF_COMBITEC_2					0xC008
+#define  ZORRO_PROD_COMBITEC_HD					ZORRO_ID(COMBITEC_2, 0x2A, 0)
+#define  ZORRO_PROD_COMBITEC_SRAM				ZORRO_ID(COMBITEC_2, 0x2B, 0)
+
+
+    /*
+     *  Test and illegal Manufacturer IDs.
+     *  These do NOT appear in arch/m68k/amiga/zorro.c!
+     */
+
+#define ZORRO_MANUF_HACKER					0x07DB
+#define  ZORRO_PROD_GENERAL_PROTOTYPE				ZORRO_ID(HACKER, 0x00, 0)
+#define  ZORRO_PROD_HACKER_SCSI					ZORRO_ID(HACKER, 0x01, 0)
+#define  ZORRO_PROD_RESOURCE_MANAGEMENT_FORCE_QUICKNET_QN2000	ZORRO_ID(HACKER, 0x02, 0)
+#define  ZORRO_PROD_VECTOR_CONNECTION_2				ZORRO_ID(HACKER, 0xE0, 0)
+#define  ZORRO_PROD_VECTOR_CONNECTION_3				ZORRO_ID(HACKER, 0xE1, 0)
+#define  ZORRO_PROD_VECTOR_CONNECTION_4				ZORRO_ID(HACKER, 0xE2, 0)
+#define  ZORRO_PROD_VECTOR_CONNECTION_5				ZORRO_ID(HACKER, 0xE3, 0)
+
+
+    /*
+     *  GVP identifies most of its products through the 'extended product code'
+     *  (epc). The epc has to be and'ed with the GVP_PRODMASK before the
+     *  identification.
+     */
 
-enum GVP_ident {
-  GVP_GFORCE_040      = 0x20,
-  GVP_GFORCE_040_SCSI = 0x30,
-  GVP_A1291_SCSI      = 0x40,
-  GVP_COMBO_R4        = 0x60,
-  GVP_COMBO_R4_SCSI   = 0x70,
-  GVP_PHONEPAK        = 0x78,
-  GVP_IOEXT           = 0x98,
-  GVP_GFORCE_030      = 0xa0,
-  GVP_GFORCE_030_SCSI = 0xb0,
-  GVP_A530            = 0xc0,
-  GVP_A530_SCSI       = 0xd0,
-  GVP_COMBO_R3        = 0xe0,
-  GVP_COMBO_R3_SCSI   = 0xf0,
-  GVP_SERIESII        = 0xf8,
-};
+#define GVP_PRODMASK			(0xf8)
+#define GVP_SCSICLKMASK			(0x01)
 
 enum GVP_flags {
-  GVP_IO       = 0x01,
-  GVP_ACCEL    = 0x02,
-  GVP_SCSI     = 0x04,
-  GVP_24BITDMA = 0x08,
-  GVP_25BITDMA = 0x10,
-  GVP_NOBANK   = 0x20,
-  GVP_14MHZ    = 0x40,
+    GVP_IO		= 0x01,
+    GVP_ACCEL		= 0x02,
+    GVP_SCSI		= 0x04,
+    GVP_24BITDMA	= 0x08,
+    GVP_25BITDMA	= 0x10,
+    GVP_NOBANK		= 0x20,
+    GVP_14MHZ		= 0x40,
 };
 
 
@@ -515,8 +663,8 @@
     u_char	er_Product;	/* Product number, assigned by manufacturer */
     u_char	er_Flags;	/* Flags */
     u_char	er_Reserved03;	/* Must be zero ($ff inverted) */
-    u_short	er_Manufacturer; /* Unique ID,ASSIGNED BY COMMODORE-AMIGA! */
-    u_long	er_SerialNumber; /* Available for use by manufacturer */
+    u_short	er_Manufacturer;/* Unique ID,ASSIGNED BY COMMODORE-AMIGA! */
+    u_long	er_SerialNumber;/* Available for use by manufacturer */
     u_short	er_InitDiagVec;	/* Offset to optional "DiagArea" structure */
     u_char	er_Reserved0c;
     u_char	er_Reserved0d;
@@ -547,7 +695,7 @@
     u_long		cd_Unused[4];	/* for whatever the driver wants */
 };
 
-#else	/* __ASSEMBLY__ */
+#else /* __ASSEMBLY__ */
 
 LN_Succ		= 0
 LN_Pred		= LN_Succ+4
@@ -582,7 +730,7 @@
 CD_Unused	= CD_NextCD+4
 CD_sizeof	= CD_Unused+(4*4)
 
-#endif	/* __ASSEMBLY__ */
+#endif /* __ASSEMBLY__ */
 
 #ifndef __ASSEMBLY__
 
@@ -590,30 +738,30 @@
 
 #ifdef __KERNEL__
 
-extern int zorro_num_autocon;		/* # of autoconfig devices found */
+extern unsigned int zorro_num_autocon;		/* # of autoconfig devices found */
 extern struct ConfigDev zorro_autocon[ZORRO_NUM_AUTO];
 
 
-/*
- * Zorro Functions
- */
-
-extern int zorro_find(int manuf, int prod, int part, int index);
-extern struct ConfigDev *zorro_get_board(int key);
-extern void zorro_config_board(int key, int part);
-extern void zorro_unconfig_board(int key, int part);
+    /*
+     *  Zorro Functions
+     */
+
+extern unsigned int zorro_find(zorro_id id, unsigned int part, unsigned int index);
+extern const struct ConfigDev *zorro_get_board(unsigned int key);
+extern void zorro_config_board(unsigned int key, unsigned int part);
+extern void zorro_unconfig_board(unsigned int key, unsigned int part);
+
+
+    /*
+     *  Bitmask indicating portions of available Zorro II RAM that are unused
+     *  by the system. Every bit represents a 64K chunk, for a maximum of 8MB
+     *  (128 chunks, physical 0x00200000-0x009fffff).
+     *
+     *  If you want to use (= allocate) portions of this RAM, you should clear
+     *  the corresponding bits.
+     */
 
-
-/*
- * Bitmask indicating portions of available Zorro II RAM that are unused
- * by the system. Every bit represents a 64K chunk, for a maximum of 8MB
- * (128 chunks, physical 0x00200000-0x009fffff).
- *
- * If you want to use (= allocate) portions of this RAM, you should clear
- * the corresponding bits.
- */
-
-extern u_long zorro_unused_z2ram[4];
+extern u32 zorro_unused_z2ram[4];
 
 #define Z2RAM_START		(0x00200000)
 #define Z2RAM_END		(0x00a00000)
@@ -623,14 +771,14 @@
 #define Z2RAM_CHUNKSHIFT	(16)
 
 
-/*
- * Verbose Board Identification
- */
+    /*
+     *  Verbose Board Identification
+     */
 
 extern void zorro_identify(void);
 extern int zorro_get_list(char *buffer);
 
-#endif	/* !__ASSEMBLY__ */
-#endif	/* __KERNEL__ */
+#endif /* !__ASSEMBLY__ */
+#endif /* __KERNEL__ */
 
-#endif /* __ZORRO_H */
+#endif /* _LINUX_ZORRO_H */
diff -u --recursive --new-file v2.1.90/linux/include/net/flow.h linux/include/net/flow.h
--- v2.1.90/linux/include/net/flow.h	Thu Mar 27 14:40:11 1997
+++ linux/include/net/flow.h	Mon Mar 23 16:48:25 1998
@@ -22,7 +22,7 @@
 		} ip6_u;
 	} nl_u;
 
-	struct device	*dev;
+	int	oif;
 
 	union {
 		struct {
diff -u --recursive --new-file v2.1.90/linux/include/net/ip.h linux/include/net/ip.h
--- v2.1.90/linux/include/net/ip.h	Tue Mar 10 10:03:35 1998
+++ linux/include/net/ip.h	Thu Mar 26 14:57:04 1998
@@ -85,10 +85,9 @@
  */
 
 extern int		ip_ioctl(struct sock *sk, int cmd, unsigned long arg);
-extern int		ip_build_pkt(struct sk_buff *skb, struct sock *sk,
-				     u32 saddr, u32 daddr,
-				     struct ip_options *opt);
-extern int 		ip_build_header(struct sk_buff *skb, struct sock *sk);
+extern void		ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
+					      u32 saddr, u32 daddr,
+					      struct ip_options *opt);
 extern int		ip_rcv(struct sk_buff *skb, struct device *dev,
 			       struct packet_type *pt);
 extern int		ip_local_deliver(struct sk_buff *skb);
diff -u --recursive --new-file v2.1.90/linux/include/net/ip6_route.h linux/include/net/ip6_route.h
--- v2.1.90/linux/include/net/ip6_route.h	Tue Mar 17 22:18:15 1998
+++ linux/include/net/ip6_route.h	Mon Mar 23 16:48:25 1998
@@ -72,7 +72,7 @@
 
 extern struct rt6_info		*rt6_lookup(struct in6_addr *daddr,
 					    struct in6_addr *saddr,
-					    struct device *dev, int flags);
+					    int oif, int flags);
 
 /*
  *	support functions for ND
diff -u --recursive --new-file v2.1.90/linux/include/net/protocol.h linux/include/net/protocol.h
--- v2.1.90/linux/include/net/protocol.h	Fri Feb  6 15:33:34 1998
+++ linux/include/net/protocol.h	Thu Mar 26 14:55:36 1998
@@ -55,7 +55,7 @@
 			unsigned short len,
 			int redo, struct inet6_protocol *protocol);
 
-	void	(*err_handler)(int type, int code, unsigned char *buff,
+	void	(*err_handler)(struct sk_buff *skb, int type, int code, unsigned char *buff,
 			__u32 info, struct in6_addr *saddr,
 			struct in6_addr *daddr,
 			struct inet6_protocol *protocol);
diff -u --recursive --new-file v2.1.90/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.90/linux/include/net/sock.h	Tue Mar 17 22:18:15 1998
+++ linux/include/net/sock.h	Thu Mar 26 14:55:38 1998
@@ -150,6 +150,7 @@
 	__u32			flow_lbl;
 	int			hop_limit;
 	int			mcast_hops;
+	int			mcast_oif;
 	__u8			priority;
 
 
@@ -163,10 +164,6 @@
 				mc_loop:1,
                                 unused:2;
 
-	/* device for outgoing packets */
-
-	struct device		*oif;
-
 	struct ipv6_mc_socklist	*ipv6_mc_list;
 	__u32			dst_cookie;
 
@@ -188,6 +185,11 @@
 };
 #endif
 
+/* This defines a selective acknowledgement block. */
+struct tcp_sack_block {
+	__u32	start_seq;
+	__u32	end_seq;
+};
 
 struct tcp_opt
 {
@@ -259,7 +261,8 @@
  *      Options received (usually on last packet, some only on SYN packets).
  */
 	char	tstamp_ok,	/* TIMESTAMP seen on SYN packet		*/
-		wscale_ok;	/* Wscale seen on SYN packet		*/
+		wscale_ok,	/* Wscale seen on SYN packet		*/
+		sack_ok;	/* SACK seen on SYN packet		*/
 	char	saw_tstamp;	/* Saw TIMESTAMP on last packet		*/
         __u16	in_mss;		/* MSS option received from sender	*/
         __u8	snd_wscale;	/* Window scaling received from sender	*/
@@ -268,6 +271,8 @@
         __u32	rcv_tsecr;	/* Time stamp echo reply        	*/
         __u32	ts_recent;	/* Time stamp to echo next		*/
         __u32	ts_recent_stamp;/* Time we stored ts_recent (for aging) */
+	int	num_sacks;	/* Number of SACK blocks		*/
+	struct tcp_sack_block selective_acks[4]; /* The SACKS themselves*/
 
  	struct timer_list	probe_timer;		/* Probes	*/
 	__u32	basertt;	/* Vegas baseRTT			*/
@@ -355,7 +360,12 @@
 	unsigned short		num;		/* Local port				*/
 	volatile unsigned char	state,		/* Connection state			*/
 				zapped;		/* In ax25 & ipx means not linked	*/
-	struct tcphdr		dummy_th;	/* TCP header template			*/
+	__u16			sport;		/* Source port				*/
+	__u16			dport;		/* Destination port			*/
+
+	unsigned short		family;
+	unsigned char		reuse,
+				nonagle;
 
 	int			sock_readers;	/* user count				*/
 	int			rcvbuf;
@@ -379,13 +389,11 @@
 	volatile char		dead,
 				done,
 				urginline,
-				reuse,
 				keepopen,
 				linger,
 				destroy,
 				no_check,
 				broadcast,
-				nonagle,
 				bsdism;
 	unsigned char		debug;
 	int			proc;
@@ -398,7 +406,6 @@
 	struct sk_buff_head	back_log,
 	                        error_queue;
 
-	unsigned short		family;
 	struct proto		*prot;
 
 /*
diff -u --recursive --new-file v2.1.90/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.1.90/linux/include/net/tcp.h	Tue Mar 17 22:18:15 1998
+++ linux/include/net/tcp.h	Thu Mar 26 14:57:06 1998
@@ -97,7 +97,7 @@
 #define TCP_RHASH_FN(__fport) \
 	((((__fport) >> 7) ^ (__fport)) & (TCP_NUM_REGS - 1))
 #define TCP_RHASH(__fport)	tcp_regs[TCP_RHASH_FN((__fport))]
-#define TCP_SK_RHASH_FN(__sock)	TCP_RHASH_FN((__sock)->dummy_th.dest)
+#define TCP_SK_RHASH_FN(__sock)	TCP_RHASH_FN((__sock)->dport)
 #define TCP_SK_RHASH(__sock)	tcp_regs[TCP_SK_RHASH_FN((__sock))]
 
 static __inline__ void tcp_reg_zap(struct sock *sk)
@@ -161,9 +161,12 @@
 	int			bound_dev_if;
 	unsigned short		num;
 	unsigned char		state,
-				family;		/* sk->zapped */
-	__u16			source;		/* sk->dummy_th.source */
-	__u16			dest;		/* sk->dummy_th.dest */
+				zapped;
+	__u16			sport;
+	__u16			dport;
+	unsigned short		family;
+	unsigned char		reuse,
+				nonagle;
 
 	/* And these are ours. */
 	__u32			rcv_nxt;
@@ -187,6 +190,7 @@
  */
 extern int sysctl_tcp_timestamps;
 extern int sysctl_tcp_window_scaling;
+extern int sysctl_tcp_sack;
 
 /* These can have wildcards, don't try too hard. */
 static __inline__ int tcp_lhashfn(unsigned short num)
@@ -294,15 +298,12 @@
 #define TCPOLEN_TIMESTAMP      10
 
 /* But this is what stacks really send out. */
-#define TCPOLEN_TSTAMP_ALIGNED	12
-#define TCPOLEN_WSCALE_ALIGNED	4
-
-/*
- *      TCP option flags for parsed options.
- */
-
-#define TCPOPTF_SACK_PERM       1
-#define TCPOPTF_TIMESTAMP       2
+#define TCPOLEN_TSTAMP_ALIGNED		12
+#define TCPOLEN_WSCALE_ALIGNED		4
+#define TCPOLEN_SACKPERM_ALIGNED	4
+#define TCPOLEN_SACK_BASE		2
+#define TCPOLEN_SACK_BASE_ALIGNED	4
+#define TCPOLEN_SACK_PERBLOCK		8
 
 /*
  *	TCP Vegas constants
@@ -331,7 +332,7 @@
 	struct in6_addr		loc_addr;
 	struct in6_addr		rmt_addr;
 	struct ipv6_options	*opt;
-	struct device		*dev;
+	int			iif;
 };
 #endif
 
@@ -347,6 +348,7 @@
 	unsigned snd_wscale : 4, 
 		rcv_wscale : 4, 
 		tstamp_ok : 1,
+		sack_ok : 1,
 		wscale_ok : 1;
 	/* The following two fields can be easily recomputed I think -AK */
 	__u32			window_clamp;	/* window clamp at creation time */
@@ -378,9 +380,6 @@
  */
 
 struct tcp_func {
-	int			(*build_net_header)	(struct sock *sk, 
-							 struct sk_buff *skb);
-
 	void			(*queue_xmit)		(struct sk_buff *skb);
 
 	void			(*send_check)		(struct sock *sk,
@@ -388,8 +387,7 @@
 							 int len,
 							 struct sk_buff *skb);
 
-	int			(*rebuild_header)	(struct sock *sk,
-							 struct sk_buff *skb);
+	int			(*rebuild_header)	(struct sock *sk);
 
 	int			(*conn_request)		(struct sock *sk,
 							 struct sk_buff *skb,
@@ -497,15 +495,14 @@
 					    int len, int nonblock, 
 					    int flags, int *addr_len);
 
-extern void			tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, 
-									  int no_fancy);
+extern void			tcp_parse_options(struct sock *sk, struct tcphdr *th,
+						  struct tcp_opt *tp, int no_fancy);
 
 /*
  *	TCP v4 functions exported for the inet6 API
  */
 
-extern int		       	tcp_v4_rebuild_header(struct sock *sk, 
-						      struct sk_buff *skb);
+extern int		       	tcp_v4_rebuild_header(struct sock *sk);
 
 extern int		       	tcp_v4_build_header(struct sock *sk, 
 						    struct sk_buff *skb);
@@ -520,7 +517,8 @@
 
 extern struct sock *		tcp_create_openreq_child(struct sock *sk,
 							 struct open_request *req,
-							 struct sk_buff *skb);
+							 struct sk_buff *skb,
+							 int mss);
 
 extern struct sock *		tcp_v4_syn_recv_sock(struct sock *sk,
 						     struct sk_buff *skb,
@@ -534,6 +532,16 @@
 					       struct sockaddr *uaddr,
 					       int addr_len);
 
+extern void			tcp_connect(struct sock *sk,
+					    struct sk_buff *skb,
+					    int est_mss);
+
+extern struct sk_buff *		tcp_make_synack(struct sock *sk,
+						struct dst_entry *dst,
+						struct open_request *req,
+						int mss);
+
+
 /* From syncookies.c */
 extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, 
 				    struct ip_options *opt);
@@ -543,7 +551,8 @@
 extern void tcp_read_wakeup(struct sock *);
 extern void tcp_write_xmit(struct sock *);
 extern void tcp_time_wait(struct sock *);
-extern void tcp_do_retransmit(struct sock *, int);
+extern int tcp_retransmit_skb(struct sock *, struct sk_buff *);
+extern void tcp_xmit_retransmit_queue(struct sock *);
 extern void tcp_simple_retransmit(struct sock *);
 
 /* tcp_output.c */
@@ -554,6 +563,7 @@
 extern void tcp_send_fin(struct sock *sk);
 extern void tcp_send_active_reset(struct sock *sk);
 extern int  tcp_send_synack(struct sock *);
+extern void tcp_transmit_skb(struct sock *, struct sk_buff *);
 extern void tcp_send_skb(struct sock *, struct sk_buff *, int force_queue);
 extern void tcp_send_ack(struct sock *sk);
 extern void tcp_send_delayed_ack(struct tcp_opt *tp, int max_timeout);
@@ -639,6 +649,32 @@
 	return (new_win && (new_win > (cur_win << 1)));
 }
 
+/* This is what the send packet queueing engine uses to pass
+ * TCP per-packet control information to the transmission
+ * code.
+ */
+struct tcp_skb_cb {
+	__u8	flags;			/* TCP header flags.		*/
+
+	/* NOTE: These must match up to the flags byte in a
+	 *       real TCP header.
+	 */
+#define TCPCB_FLAG_FIN		0x01
+#define TCPCB_FLAG_SYN		0x02
+#define TCPCB_FLAG_RST		0x04
+#define TCPCB_FLAG_PSH		0x08
+#define TCPCB_FLAG_ACK		0x10
+#define TCPCB_FLAG_URG		0x20
+
+	__u8	sacked;			/* State flags for SACK/FACK.	*/
+#define TCPCB_SACKED_ACKED	0x01	/* SKB ACK'd by a SACK block	*/
+#define TCPCB_SACKED_RETRANS	0x02	/* SKB retransmitted		*/
+
+	__u16	urg_ptr;		/* Valid w/URG flags is set.	*/
+};
+
+#define TCP_SKB_CB(__skb)	((struct tcp_skb_cb *)&((__skb)->cb[0]))
+
 /* This checks if the data bearing packet SKB (usually tp->send_head)
  * should be put on the wire right now.
  */
@@ -663,7 +699,7 @@
 	 */
 	len = skb->end_seq - skb->seq;
 	if (!sk->nonagle && len < (sk->mss >> 1) && tp->packets_out && 
-	    !skb->h.th->urg)
+	    !(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_URG))
 		nagle_check = 0;
 
 	return (nagle_check && tp->packets_out < tp->snd_cwnd &&
@@ -739,72 +775,39 @@
 	}
 }
 
-static __inline__ void tcp_build_options(__u32 *ptr, struct tcp_opt *tp)
-{
-	if (tp->tstamp_ok) {
-		*ptr = __constant_htonl((TCPOPT_NOP << 24) |
-					(TCPOPT_NOP << 16) |
-					(TCPOPT_TIMESTAMP << 8) |
-					TCPOLEN_TIMESTAMP);
-		/* rest filled in by tcp_update_options */
-	}
-}
-
-static __inline__ void tcp_update_options(__u32 *ptr, struct tcp_opt *tp)
-{
-	if (tp->tstamp_ok) {
-		*++ptr = htonl(jiffies);
-		*++ptr = htonl(tp->ts_recent);
-	}
-}
-
-static __inline__ void tcp_build_and_update_options(__u32 *ptr, struct tcp_opt *tp)
+static __inline__ void tcp_build_and_update_options(__u32 *ptr, struct tcp_opt *tp, __u32 tstamp)
 {
 	if (tp->tstamp_ok) {
 		*ptr++ = __constant_htonl((TCPOPT_NOP << 24) |
 					  (TCPOPT_NOP << 16) |
 					  (TCPOPT_TIMESTAMP << 8) |
 					  TCPOLEN_TIMESTAMP);
-		*ptr++ = htonl(jiffies);
-		*ptr   = htonl(tp->ts_recent);
+		*ptr++ = htonl(tstamp);
+		*ptr++ = htonl(tp->ts_recent);
 	}
-}
-
-/* 
- *	This routines builds a generic TCP header. 
- *	They also build the RFC1323 Timestamp, but don't fill the
- *	actual timestamp in (you need to call tcp_update_options for this).
- *	XXX: pass tp instead of sk here.
- */
- 
-static inline void tcp_build_header_data(struct tcphdr *th, struct sock *sk, int push)
-{
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	if(tp->sack_ok && tp->num_sacks) {
+		int this_sack;
 
-	memcpy(th,(void *) &(sk->dummy_th), sizeof(*th));
-	th->seq = htonl(tp->write_seq);
-	if (!push)
-		th->psh = 1;
-	tcp_build_options((__u32*)(th+1), tp);
+		*ptr++ = __constant_htonl((TCPOPT_NOP << 24) |
+					  (TCPOPT_NOP << 16) |
+					  (TCPOPT_SACK << 8) |
+					  (TCPOLEN_SACK_BASE +
+					   (tp->num_sacks * TCPOLEN_SACK_PERBLOCK)));
+		for(this_sack = 0; this_sack < tp->num_sacks; this_sack++) {
+			*ptr++ = htonl(tp->selective_acks[this_sack].start_seq);
+			*ptr++ = htonl(tp->selective_acks[this_sack].end_seq);
+		}
+	}
 }
 
-/*
- * Construct a tcp options header for a SYN or SYN_ACK packet.
+/* Construct a tcp options header for a SYN or SYN_ACK packet.
  * If this is every changed make sure to change the definition of
  * MAX_SYN_SIZE to match the new maximum number of options that you
  * can generate.
- * FIXME: This is completely disgusting.
- * This is probably a good candidate for a bit of assembly magic.
- * It would be especially magical to compute the checksum for this
- * stuff on the fly here.
- */
-extern __inline__ int tcp_syn_build_options(struct sk_buff *skb, int mss, int ts, int offer_wscale, int wscale)
-{
-	int count = 4 + (offer_wscale ? TCPOLEN_WSCALE_ALIGNED : 0) +
-		((ts) ? TCPOLEN_TSTAMP_ALIGNED : 0);
-	unsigned char *optr = skb_put(skb,count);
-	__u32 *ptr = (__u32 *)optr;
-
+ */
+extern __inline__ void tcp_syn_build_options(__u32 *ptr, int mss, int ts, int sack,
+					     int offer_wscale, int wscale, __u32 tstamp)
+{
 	/* We always get an MSS option.
 	 * The option bytes which will be seen in normal data
 	 * packets should timestamps be used, must be in the MSS
@@ -815,20 +818,26 @@
 	 * recognize data packets as being full sized when we
 	 * should, and thus we won't abide by the delayed ACK
 	 * rules correctly.
+	 * SACKs don't matter, we never delay an ACK when we
+	 * have any of those going out.
 	 */
 	if(ts)
 		mss += TCPOLEN_TSTAMP_ALIGNED;
 	*ptr++ = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | mss);
 	if (ts) {
-		*ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
-					  (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
-		*ptr++ = htonl(jiffies);	/* TSVAL */
+		if(sack)
+			*ptr++ = __constant_htonl((TCPOPT_SACK_PERM << 24) | (TCPOLEN_SACK_PERM << 16) |
+						  (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
+		else
+			*ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
+						  (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
+		*ptr++ = htonl(tstamp);		/* TSVAL */
 		*ptr++ = __constant_htonl(0);	/* TSECR */
-	}
+	} else if(sack)
+		*ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
+					  (TCPOPT_SACK_PERM << 8) | TCPOLEN_SACK_PERM);
 	if (offer_wscale)
-		*ptr++ = htonl((TCPOPT_WINDOW << 24) | (TCPOLEN_WINDOW << 16) | (wscale << 8));
-	skb->csum = csum_partial(optr, count, 0);
-	return count;
+		*ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_WINDOW << 16) | (TCPOLEN_WINDOW << 8) | (wscale));
 }
 
 /* Determine a window scaling and initial window to offer.
diff -u --recursive --new-file v2.1.90/linux/include/net/transp_v6.h linux/include/net/transp_v6.h
--- v2.1.90/linux/include/net/transp_v6.h	Fri Feb  6 15:35:44 1998
+++ linux/include/net/transp_v6.h	Thu Mar 26 14:58:05 1998
@@ -26,7 +26,7 @@
 						  struct sk_buff *skb);
 
 extern int			datagram_send_ctl(struct msghdr *msg,
-						  struct device **src_dev,
+						  int *oif,
 						  struct in6_addr **src_addr,
 						  struct ipv6_options *opt,
 						  int *hlimit);
diff -u --recursive --new-file v2.1.90/linux/kernel/kmod.c linux/kernel/kmod.c
--- v2.1.90/linux/kernel/kmod.c	Tue Mar 17 22:18:15 1998
+++ linux/kernel/kmod.c	Wed Mar 25 22:04:37 1998
@@ -9,24 +9,22 @@
 #include <linux/types.h>
 #include <linux/unistd.h>
 
-static inline _syscall1(int,delete_module,const char *,name_user)
-
 /*
 	kmod_unload_delay and modprobe_path are set via /proc/sys.
 */
 int kmod_unload_delay = 60;
 char modprobe_path[256] = "/sbin/modprobe";
-char module_name[64] = "";
-char * argv[] = { "modprobe", "-k", NULL, NULL, };
-char * envp[] = { "HOME=/", "TERM=linux", NULL, };
+static char module_name[64] = "";
+static char * argv[] = { "modprobe", "-k", module_name, NULL, };
+static char * envp[] = { "HOME=/", "TERM=linux", NULL, };
 
 /*
 	kmod_queue synchronizes the kmod thread and the rest of the system
 	kmod_unload_timer is what we use to unload modules
 	after kmod_unload_delay seconds
 */
-struct wait_queue * kmod_queue = NULL;
-struct timer_list kmod_unload_timer;
+static struct wait_queue * kmod_queue = NULL;
+static struct timer_list kmod_unload_timer;
 
 /*
 	kmod_thread is the thread that does most of the work.  kmod_unload and
@@ -74,7 +72,6 @@
 					Call modprobe with module_name.  If execve returns,
 					print out an error.
 				*/
-				argv[2] = module_name;
 				execve(modprobe_path, argv, envp);
 
 				printk("kmod: failed to load module %s\n", module_name);
@@ -136,7 +133,8 @@
 		the module into module_name.  Once that is done, wake up
 		kmod_thread.
 	*/
-	strcpy(module_name, name);
+	strncpy(module_name, name, sizeof(module_name));
+	module_name[sizeof(module_name)-1] = '\0';
 	wake_up(&kmod_queue);
 
 	/*
diff -u --recursive --new-file v2.1.90/linux/kernel/sched.c linux/kernel/sched.c
--- v2.1.90/linux/kernel/sched.c	Tue Mar 17 22:18:15 1998
+++ linux/kernel/sched.c	Mon Mar 23 16:48:25 1998
@@ -333,20 +333,27 @@
 
 static inline int detach_timer(struct timer_list *timer)
 {
-	int ret = 0;
-	struct timer_list *next, *prev;
-	next = timer->next;
-	prev = timer->prev;
-	if (next) {
-		next->prev = prev;
-	}
+	struct timer_list *prev = timer->prev;
 	if (prev) {
-		ret = 1;
+		struct timer_list *next = timer->next;
 		prev->next = next;
+		if (next)
+			next->prev = prev;
+		return 1;
 	}
-	return ret;
+	return 0;
 }
 
+void mod_timer(struct timer_list *timer, unsigned long expires)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&timerlist_lock, flags);
+	timer->expires = expires;
+	detach_timer(timer);
+	internal_add_timer(timer);
+	spin_unlock_irqrestore(&timerlist_lock, flags);
+}
 
 int del_timer(struct timer_list * timer)
 {
diff -u --recursive --new-file v2.1.90/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.1.90/linux/kernel/sysctl.c	Tue Mar 17 22:18:15 1998
+++ linux/kernel/sysctl.c	Thu Mar 26 12:56:36 1998
@@ -191,7 +191,7 @@
 	{VM_SWAPCTL, "swapctl", 
 	 &swap_control, sizeof(swap_control_t), 0600, NULL, &proc_dointvec},
 	{VM_SWAPOUT, "swapout_interval",
-	 &swapout_interval, sizeof(int), 0600, NULL, &proc_dointvec_jiffies},
+	 &swapout_interval, sizeof(int), 0600, NULL, &proc_dointvec},
 	{VM_FREEPG, "freepages", 
 	 &freepages, sizeof(freepages_t), 0600, NULL, &proc_dointvec},
 	{VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL,
@@ -201,6 +201,8 @@
 	 sizeof(sysctl_overcommit_memory), 0644, NULL, &proc_dointvec},
 	{VM_BUFFERMEM, "buffermem",
 	 &buffer_mem, sizeof(buffer_mem_t), 0600, NULL, &proc_dointvec},
+	{VM_PAGECACHE, "pagecache",
+	 &page_cache, sizeof(buffer_mem_t), 0600, NULL, &proc_dointvec},
 	{0}
 };
 
diff -u --recursive --new-file v2.1.90/linux/lib/vsprintf.c linux/lib/vsprintf.c
--- v2.1.90/linux/lib/vsprintf.c	Fri Sep  5 20:31:57 1997
+++ linux/lib/vsprintf.c	Sat Mar 21 11:05:13 1998
@@ -112,13 +112,14 @@
 			*str++ = ' ';
 	if (sign)
 		*str++ = sign;
-	if (type & SPECIAL)
+	if (type & SPECIAL) {
 		if (base==8)
 			*str++ = '0';
 		else if (base==16) {
 			*str++ = '0';
 			*str++ = digits[33];
 		}
+	}
 	if (!(type & LEFT))
 		while (size-- > 0)
 			*str++ = c;
@@ -281,12 +282,12 @@
 		}
 		if (qualifier == 'l')
 			num = va_arg(args, unsigned long);
-		else if (qualifier == 'h')
+		else if (qualifier == 'h') {
 			if (flags & SIGN)
 				num = va_arg(args, short);
 			else
 				num = va_arg(args, unsigned short);
-		else if (flags & SIGN)
+		} else if (flags & SIGN)
 			num = va_arg(args, int);
 		else
 			num = va_arg(args, unsigned int);
diff -u --recursive --new-file v2.1.90/linux/mm/filemap.c linux/mm/filemap.c
--- v2.1.90/linux/mm/filemap.c	Tue Mar 10 10:03:36 1998
+++ linux/mm/filemap.c	Thu Mar 26 12:56:36 1998
@@ -150,6 +150,10 @@
 				}
 				tmp = tmp->b_this_page;
 			} while (tmp != bh);
+
+			/* Refuse to swap out all buffer pages */
+			if ((buffermem >> PAGE_SHIFT) * 100 < (buffer_mem.min_percent * num_physpages))
+				goto next;
 		}
 
 		/* We can't throw away shared pages, but we do mark
@@ -167,7 +171,7 @@
 						break;
 					}
 					age_page(page);
-					if (page->age)
+					if (page->age || page_cache_size * 100 < (page_cache.min_percent * num_physpages))
 						break;
 					if (PageSwapCache(page)) {
 						delete_from_swap_cache(page);
diff -u --recursive --new-file v2.1.90/linux/mm/mprotect.c linux/mm/mprotect.c
--- v2.1.90/linux/mm/mprotect.c	Tue Mar 10 10:03:36 1998
+++ linux/mm/mprotect.c	Sat Mar 21 11:05:13 1998
@@ -185,12 +185,12 @@
 	if (newflags == vma->vm_flags)
 		return 0;
 	newprot = protection_map[newflags & 0xf];
-	if (start == vma->vm_start)
+	if (start == vma->vm_start) {
 		if (end == vma->vm_end)
 			error = mprotect_fixup_all(vma, newflags, newprot);
 		else
 			error = mprotect_fixup_start(vma, end, newflags, newprot);
-	else if (end == vma->vm_end)
+	} else if (end == vma->vm_end)
 		error = mprotect_fixup_end(vma, start, newflags, newprot);
 	else
 		error = mprotect_fixup_middle(vma, start, end, newflags, newprot);
diff -u --recursive --new-file v2.1.90/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.1.90/linux/mm/page_alloc.c	Tue Mar 17 22:18:15 1998
+++ linux/mm/page_alloc.c	Thu Mar 26 09:37:46 1998
@@ -118,26 +118,33 @@
  *
  * [previously, there had to be two entries of the highest memory
  *  order, but this lead to problems on large-memory machines.]
+ *
+ * This will return zero if no list was found, non-zero
+ * if there was memory (the bigger, the better).
  */
-int free_memory_available(void)
+int free_memory_available(int nr)
 {
-	int i, retval = 0;
+	int retval = 0;
 	unsigned long flags;
 	struct free_area_struct * list = NULL;
 
+	list = free_area + NR_MEM_LISTS;
 	spin_lock_irqsave(&page_alloc_lock, flags);
 	/* We fall through the loop if the list contains one
 	 * item. -- thanks to Colin Plumb <colin@nyx.net>
 	 */
-	for (i = 1; i < 4; ++i) {
-		list = free_area + NR_MEM_LISTS - i;
+	do {
+		list--;
+		/* Empty list? Bad - we need more memory */
 		if (list->next == memory_head(list))
 			break;
+		/* One item on the list? Look further */
 		if (list->next->next == memory_head(list))
 			continue;
-		retval = 1;
+		/* More than one item? We're ok */
+		retval = nr + 1;
 		break;
-	}
+	} while (--nr >= 0);
 	spin_unlock_irqrestore(&page_alloc_lock, flags);
 	return retval;
 }
@@ -275,7 +282,7 @@
 	spin_lock_irqsave(&page_alloc_lock, flags);
 	RMQUEUE(order, maxorder, (gfp_mask & GFP_DMA));
 	spin_unlock_irqrestore(&page_alloc_lock, flags);
-	if ((gfp_mask & __GFP_WAIT) && try_to_free_page(gfp_mask))
+	if ((gfp_mask & __GFP_WAIT) && try_to_free_pages(gfp_mask,SWAP_CLUSTER_MAX))
 		goto repeat;
 nopage:
 	return 0;
diff -u --recursive --new-file v2.1.90/linux/mm/swap.c linux/mm/swap.c
--- v2.1.90/linux/mm/swap.c	Tue Mar 17 22:18:15 1998
+++ linux/mm/swap.c	Thu Mar 26 15:36:55 1998
@@ -67,8 +67,13 @@
 swapstat_t swapstats = {0};
 
 buffer_mem_t buffer_mem = {
-	6,	/* minimum percent buffer + cache memory */
-	20,	/* borrow percent buffer + cache memory */
-	90	/* maximum percent buffer + cache memory */
+	3,	/* minimum percent buffer */
+	10,	/* borrow percent buffer */
+	30	/* maximum percent buffer */
 };
 
+buffer_mem_t page_cache = {
+	10,	/* minimum percent page cache */
+	30,	/* borrow percent page cache */
+	75	/* maximum */
+};
diff -u --recursive --new-file v2.1.90/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.1.90/linux/mm/vmscan.c	Tue Mar 17 22:18:15 1998
+++ linux/mm/vmscan.c	Thu Mar 26 13:44:07 1998
@@ -31,7 +31,7 @@
 /* 
  * When are we next due for a page scan? 
  */
-static int next_swap_jiffies = 0;
+static unsigned long next_swap_jiffies = 0;
 
 /* 
  * How often do we do a pageout scan during normal conditions?
@@ -44,11 +44,6 @@
  */
 static struct wait_queue * kswapd_wait = NULL;
 
-/* 
- * We avoid doing a reschedule if the pageout daemon is already awake;
- */
-static int kswapd_awake = 0;
-
 static void init_swap_timer(void);
 
 /*
@@ -456,14 +451,14 @@
 	stop = 3;
 	if (gfp_mask & __GFP_WAIT)
 		stop = 0;
-	if (BUFFER_MEM > buffer_mem.borrow_percent * num_physpages / 100)
+	if (((buffermem >> PAGE_SHIFT) * 100 > buffer_mem.borrow_percent * num_physpages)
+		   || (page_cache_size * 100 > page_cache.borrow_percent * num_physpages))
 		state = 0;
 
 	switch (state) {
 		do {
 		case 0:
-			if (BUFFER_MEM > (buffer_mem.min_percent * num_physpages /100) &&
-					shrink_mmap(i, gfp_mask))
+			if (shrink_mmap(i, gfp_mask))
 				return 1;
 			state = 1;
 		case 1:
@@ -545,30 +540,41 @@
 	add_wait_queue(&kswapd_wait, &wait);
 	while (1) {
 		int tries;
+		int tried = 0;
 
 		current->state = TASK_INTERRUPTIBLE;
-		kswapd_awake = 0;
 		flush_signals(current);
 		run_task_queue(&tq_disk);
 		schedule();
-		kswapd_awake = 1;
 		swapstats.wakeups++;
-		/* Do the background pageout: 
-		 * When we've got loads of memory, we try
-		 * (freepages.high - nr_free_pages) times to
-		 * free memory. As memory gets tighter, kswapd
-		 * gets more and more agressive. -- Rik.
+	
+		/*
+		 * Do the background pageout: be
+		 * more aggressive if we're really
+		 * low on free memory.
+		 *
+		 * Normally this is called 4 times
+		 * a second if we need more memory,
+		 * so this has a normal rate of
+		 * X*4 pages of memory free'd per
+		 * second. That rate goes up when
+		 *
+		 * - we're really low on memory (we get woken
+		 *   up a lot more)
+		 * - other processes fail to allocate memory,
+		 *   at which time they try to do their own
+		 *   freeing.
+		 *
+		 * A "tries" value of 50 means up to 200 pages
+		 * per second (1.6MB/s). This should be a /proc
+		 * thing.
 		 */
-		tries = freepages.high - nr_free_pages;
-		if (tries < freepages.min) {
-			tries = freepages.min;
-		}
-		if (nr_free_pages < freepages.high + freepages.low)
-			tries <<= 1;
+		tries = (50 << 2) >> free_memory_available(3);
+	
 		while (tries--) {
 			int gfp_mask;
 
-			if (free_memory_available())
+			if (++tried > SWAP_CLUSTER_MAX && free_memory_available(0))
 				break;
 			gfp_mask = __GFP_IO;
 			try_to_free_page(gfp_mask);
@@ -589,27 +595,38 @@
 /* 
  * The swap_tick function gets called on every clock tick.
  */
-
 void swap_tick(void)
 {
-	int want_wakeup = 0, memory_low = 0;
-	int pages = nr_free_pages + atomic_read(&nr_async_pages);
+	unsigned long now, want;
+	int want_wakeup = 0;
 
-	if (pages < freepages.low)
-		memory_low = want_wakeup = 1;
-	else if ((pages < freepages.high || BUFFER_MEM > (num_physpages * buffer_mem.max_percent / 100))
-			&& jiffies >= next_swap_jiffies)
-		want_wakeup = 1;
+	want = next_swap_jiffies;
+	now = jiffies;
 
-	if (want_wakeup) { 
-		if (!kswapd_awake) {
+	/*
+	 * Examine the memory queues. Mark memory low
+	 * if there is nothing available in the three
+	 * highest queues.
+	 *
+	 * Schedule for wakeup if there isn't lots
+	 * of free memory.
+	 */
+	switch (free_memory_available(3)) {
+	case 0:
+		want = now;
+		/* Fall through */
+	case 1 ... 3:
+		want_wakeup = 1;
+	default:
+	}
+ 
+	if ((long) (now - want) >= 0) {
+		if (want_wakeup || (num_physpages * buffer_mem.max_percent) < (buffermem >> PAGE_SHIFT) * 100
+				|| (num_physpages * page_cache.max_percent < page_cache_size)) {
+			/* Set the next wake-up time */
+			next_swap_jiffies = now + swapout_interval;
 			wake_up(&kswapd_wait);
-			need_resched = 1;
 		}
-		/* Set the next wake-up time */
-		next_swap_jiffies = jiffies;
-		if (!memory_low) 
-			next_swap_jiffies += swapout_interval;
 	}
 	timer_active |= (1<<SWAP_TIMER);
 }
diff -u --recursive --new-file v2.1.90/linux/net/core/datagram.c linux/net/core/datagram.c
--- v2.1.90/linux/net/core/datagram.c	Tue Mar 10 10:03:36 1998
+++ linux/net/core/datagram.c	Tue Mar 24 10:28:39 1998
@@ -132,15 +132,13 @@
 		unsigned long flags;
 		save_flags(flags);
 		cli();
-		skb=skb_peek(&sk->receive_queue);
+		skb = skb_peek(&sk->receive_queue);
 		if(skb!=NULL)
 			atomic_inc(&skb->users);
 		restore_flags(flags);
-		if(skb==NULL)		/* shouldn't happen but .. */
-			goto restart;
-		return skb;
-	}
-	skb = skb_dequeue(&sk->receive_queue);
+	} else
+		skb = skb_dequeue(&sk->receive_queue);
+
 	if (!skb)	/* Avoid race if someone beats us to the data */
 		goto restart;
 	return skb;
@@ -163,30 +161,23 @@
 
 int skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size)
 {
-	int err;
-	err = copy_to_user(to, skb->h.raw+offset, size);
-	if (err)
-	{
-		err = -EFAULT;
-	}
+	int err = -EFAULT;
+
+	if (!copy_to_user(to, skb->h.raw + offset, size))
+		err = 0;
 	return err;
 }
 
 
 /*
  *	Copy a datagram to an iovec.
+ *	Note: the iovec is modified during the copy.
  */
  
 int skb_copy_datagram_iovec(struct sk_buff *skb, int offset, struct iovec *to,
 			    int size)
 {
-	int err;
-	err = memcpy_toiovec(to, skb->h.raw+offset, size);
-	if (err)
-	{
-		err = -EFAULT;
-	}
-	return err;
+	return memcpy_toiovec(to, skb->h.raw + offset, size);
 }
 
 /*
diff -u --recursive --new-file v2.1.90/linux/net/core/dst.c linux/net/core/dst.c
--- v2.1.90/linux/net/core/dst.c	Tue Mar 17 22:18:16 1998
+++ linux/net/core/dst.c	Mon Mar 23 16:48:25 1998
@@ -101,6 +101,14 @@
 void __dst_free(struct dst_entry * dst)
 {
 	start_bh_atomic();
+	/* The first case (dev==NULL) is required, when
+	   protocol module is unloaded.
+	 */
+	if (dst->dev == NULL || !(dst->dev->flags&IFF_UP)) {
+		dst->input = dst_discard;
+		dst->output = dst_blackhole;
+		dst->dev = &loopback_dev;
+	}
 	dst->obsolete = 2;
 	dst->next = dst_garbage_list;
 	dst_garbage_list = dst;
diff -u --recursive --new-file v2.1.90/linux/net/core/iovec.c linux/net/core/iovec.c
--- v2.1.90/linux/net/core/iovec.c	Tue Mar 17 22:18:16 1998
+++ linux/net/core/iovec.c	Tue Mar 24 10:28:39 1998
@@ -30,7 +30,6 @@
 
 /*
  *	Verify iovec
- *	verify area does a simple check for completly bogus addresses
  *
  *	Save time not doing verify_area. copy_*_user will make this work
  *	in any case.
@@ -79,22 +78,21 @@
 }
 
 /*
- *	Copy kernel to iovec.
+ *	Copy kernel to iovec. Returns -EFAULT on error.
  *
  *	Note: this modifies the original iovec.
  */
  
 int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
 {
-	int err;
+	int err = -EFAULT; 
 
 	while(len>0)
 	{
 		if(iov->iov_len)
 		{
 			int copy = min(iov->iov_len, len);
-			err = copy_to_user(iov->iov_base, kdata, copy);
-			if (err)
+			if (copy_to_user(iov->iov_base, kdata, copy))
 				goto out;
 			kdata+=copy;
 			len-=copy;
@@ -109,7 +107,7 @@
 }
 
 /*
- *	Copy iovec to kernel.
+ *	Copy iovec to kernel. Returns -EFAULT on error.
  *
  *	Note: this modifies the original iovec.
  */
@@ -147,35 +145,23 @@
 {
 	int err = -EFAULT;
 
-	while(offset>0)
+	/* Skip over the finished iovecs */
+	while(offset >= iov->iov_len)
 	{
-		if (offset > iov->iov_len)
-		{
-			offset -= iov->iov_len;
-		}
-		else
-		{
-			u8 *base = iov->iov_base + offset;
-			int copy = min(len, iov->iov_len - offset);
-
-			offset = 0;
-
-			if (copy_from_user(kdata, base, copy))
-				goto out;
-			len-=copy;
-			kdata+=copy;
-		}
+		offset -= iov->iov_len;
 		iov++;
 	}
 
-	while (len>0)
+	while (len > 0)
 	{
-		int copy = min(len, iov->iov_len);
+		u8 *base = iov->iov_base + offset;
+		int copy = min(len, iov->iov_len - offset);
 
-		if (copy_from_user(kdata, iov->iov_base, copy))
+		offset = 0;
+		if (copy_from_user(kdata, base, copy))
 			goto out;
-		len-=copy;
-		kdata+=copy;
+		len   -= copy;
+		kdata += copy;
 		iov++;
 	}
 	err = 0;
@@ -195,51 +181,22 @@
 int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
 				 int offset, unsigned int len, int *csump)
 {
-	int partial_cnt = 0;
-	int err = 0;
-	int csum;
-
-	do {
-		int copy = iov->iov_len - offset;
-
-		if (copy > 0) {
-			u8 *base = iov->iov_base + offset;
-
-			/* Normal case (single iov component) is fastly detected */
-			if (len <= copy) {
-				*csump = csum_and_copy_from_user(base, kdata, 
-								 len, *csump, &err);
-				goto out;
-			}
+	int csum = *csump;
+	int partial_cnt = 0, err = 0;
 
-			partial_cnt = copy % 4;
-			if (partial_cnt) {
-				copy -= partial_cnt;
-				if (copy_from_user(kdata + copy, base + copy,
-						partial_cnt))
-					goto out_fault;
-			}
-
-			*csump = csum_and_copy_from_user(base, kdata, copy,
-							 *csump, &err);
-			if (err)
-				goto out;
-			len   -= copy + partial_cnt;
-			kdata += copy + partial_cnt;
-			iov++;
-			break;
-		}
+	/* Skip over the finished iovecs */
+	while (offset >= iov->iov_len)
+	{
+		offset -= iov->iov_len;
 		iov++;
-		offset = -copy;
-	} while (offset > 0);
-
-	csum = *csump;
+	}
 
 	while (len > 0)
 	{
-		u8 *base = iov->iov_base;
-		unsigned int copy = min(len, iov->iov_len);
+		u8 *base = iov->iov_base + offset;
+		unsigned int copy = min(len, iov->iov_len - offset);
 
+		offset = 0;
 		/* There is a remnant from previous iov. */
 		if (partial_cnt)
 		{
diff -u --recursive --new-file v2.1.90/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v2.1.90/linux/net/core/skbuff.c	Tue Mar 10 10:03:36 1998
+++ linux/net/core/skbuff.c	Thu Mar 26 14:59:02 1998
@@ -16,6 +16,7 @@
  *					only put in the headers
  *		Ray VanTassle	:	Fixed --skb->lock in free
  *		Alan Cox	:	skb_copy copy arp field
+ *		Andi Kleen	:	slabified it.
  *
  *	NOTE:
  *		The __skb_ routines should be called with interrupts 
@@ -45,6 +46,8 @@
 #include <linux/netdevice.h>
 #include <linux/string.h>
 #include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/init.h>
 
 #include <net/ip.h>
 #include <net/protocol.h>
@@ -57,6 +60,11 @@
 #include <asm/system.h>
 
 /*
+ * Skb list spinlock
+ */
+spinlock_t skb_queue_lock = SPIN_LOCK_UNLOCKED;
+
+/*
  *	Resource tracking variables
  */
 
@@ -66,6 +74,8 @@
 
 extern atomic_t ip_frag_mem;
 
+static kmem_cache_t *skbuff_head_cache;
+
 /*
  *	Strings we don't want inline's duplicating
  */
@@ -87,138 +97,119 @@
 #endif	
 }
 
-/*
- *	Free an sk_buff. Release anything attached to the buffer.
- */
-
-void __kfree_skb(struct sk_buff *skb)
-{
-	if (skb->list)
-	 	printk(KERN_WARNING "Warning: kfree_skb passed an skb still "
-		       "on a list (from %p).\n", __builtin_return_address(0));
-
-	dst_release(skb->dst);
-	if(skb->destructor)
-		skb->destructor(skb);
-	kfree_skbmem(skb);
-}
-
-/*
- *	Allocate a new skbuff. We do this ourselves so we can fill in a few 'private'
- *	fields and also do memory statistics to find all the [BEEP] leaks.
- *
- *	Note: For now we put the header after the data to get better cache
- *	usage. Once we have a good cache aware kmalloc this will cease
- *	to be a good idea.
+/* 	Allocate a new skbuff. We do this ourselves so we can fill in a few
+ *	'private' fields and also do memory statistics to find all the
+ *	[BEEP] leaks.
+ * 
  */
 
 struct sk_buff *alloc_skb(unsigned int size,int gfp_mask)
 {
 	struct sk_buff *skb;
-	unsigned char *bptr;
-	int len;
+	u8 *data;
 
 	if (in_interrupt() && (gfp_mask & __GFP_WAIT)) {
 		static int count = 0;
 		if (++count < 5) {
 			printk(KERN_ERR "alloc_skb called nonatomically "
 			       "from interrupt %p\n", __builtin_return_address(0));
-			gfp_mask &= ~__GFP_WAIT;
 		}
+		gfp_mask &= ~__GFP_WAIT;
 	}
 
-	/*
-	 *	FIXME: We could do with an architecture dependent
-	 *	'alignment mask'.
-	 */
-	 
-	/* Allow for alignments. Make a multiple of 16 bytes */
-	size = (size + 15) & ~15;
-	len = size;
-	
-	/* And stick the control itself on the end */
-	size += sizeof(struct sk_buff);
-	
-	/*
-	 *	Allocate some space
-	 */
-	 
-	bptr = kmalloc(size,gfp_mask);
-	if (bptr == NULL) {
-		atomic_inc(&net_fails);
-		return NULL;
-	}
-
-	/*
-	 *	Now we play a little game with the caches. Linux kmalloc is
-	 *	a bit cache dumb, in fact its just about maximally non 
-	 *	optimal for typical kernel buffers. We actually run faster
-	 *	by doing the following. Which is to deliberately put the
-	 *	skb at the _end_ not the start of the memory block.
+	/* Get the HEAD */
+	skb = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
+	if (skb == NULL) 
+		goto nohead;
+
+	/* Get the DATA. Size must match skb_add_mtu(). */
+	size = ((size + 15) & ~15); 
+	data = kmalloc(size + sizeof(atomic_t), gfp_mask);
+	if (data == NULL)
+		goto nodata;
+
+	/* Note that this counter is useless now - you can just look in the
+	 * skbuff_head entry in /proc/slabinfo. We keep it only for emergency
+	 * cases.
 	 */
 	atomic_inc(&net_allocs);
-	
-	skb = (struct sk_buff *)(bptr + size) - 1;
 
-	atomic_set(&skb->count, 1);		/* only one reference to this */
-	skb->data_skb = skb;			/* and we're our own data skb */
+	skb->truesize = size;
+
+	atomic_inc(&net_skbcount);
+
+	/* Load the data pointers. */
+	skb->head = data;
+	skb->data = data;
+	skb->tail = data;
+	skb->end = data + size;
+
+	/* Set up other state */
+	skb->len = 0;
+	skb->is_clone = 0;
+	skb->cloned = 0;
+
+	atomic_set(&skb->users, 1); 
+	atomic_set(skb_datarefp(skb), 1);
+	return skb;
+
+nodata:
+	kmem_cache_free(skbuff_head_cache, skb);
+nohead:
+	atomic_inc(&net_fails);
+	return NULL;
+}
+
+
+/*
+ *	Slab constructor for a skb head. 
+ */ 
+static inline void skb_headerinit(void *p, kmem_cache_t *cache, 
+				  unsigned long flags)
+{
+	struct sk_buff *skb = p;
 
+	skb->destructor = NULL;
 	skb->pkt_type = PACKET_HOST;	/* Default type */
 	skb->pkt_bridged = 0;		/* Not bridged */
 	skb->prev = skb->next = NULL;
 	skb->list = NULL;
 	skb->sk = NULL;
-	skb->truesize=size;
 	skb->stamp.tv_sec=0;	/* No idea about time */
 	skb->ip_summed = 0;
 	skb->security = 0;	/* By default packets are insecure */
 	skb->dst = NULL;
-	skb->destructor = NULL;
 	memset(skb->cb, 0, sizeof(skb->cb));
 	skb->priority = 0;
-	atomic_inc(&net_skbcount);
-	atomic_set(&skb->users, 1);
-
-	/* Load the data pointers. */
-	skb->head = bptr;
-	skb->data = bptr;
-	skb->tail = bptr;
-	skb->end = bptr + len;
-	skb->len = 0;
-	skb->inclone = 0;
-	return skb;
 }
 
 /*
- *	Free an skbuff by memory
+ *	Free an skbuff by memory without cleaning the state. 
  */
-
-extern inline void __kfree_skbmem(struct sk_buff *skb)
+void kfree_skbmem(struct sk_buff *skb)
 {
-	/* don't do anything if somebody still uses us */
-	if (atomic_dec_and_test(&skb->count)) {
+	if (!skb->cloned || atomic_dec_and_test(skb_datarefp(skb)))  
 		kfree(skb->head);
-		atomic_dec(&net_skbcount);
-	}
+
+	kmem_cache_free(skbuff_head_cache, skb);
+	atomic_dec(&net_skbcount);
 }
 
-void kfree_skbmem(struct sk_buff *skb)
+/*
+ *	Free an sk_buff. Release anything attached to the buffer. Clean the state.
+ */
+
+void __kfree_skb(struct sk_buff *skb)
 {
-	void * addr = skb->head;
+	if (skb->list)
+	 	printk(KERN_WARNING "Warning: kfree_skb passed an skb still "
+		       "on a list (from %p).\n", __builtin_return_address(0));
 
-	/* don't do anything if somebody still uses us */
-	if (atomic_dec_and_test(&skb->count)) {
-		int free_head = (skb->inclone != SKB_CLONE_INLINE);
-
-		/* free the skb that contains the actual data if we've clone()'d */
-		if (skb->data_skb != skb) {
-			addr = skb;
-			__kfree_skbmem(skb->data_skb);
-		}
-		if (free_head)
-			kfree(addr);
-		atomic_dec(&net_skbcount);
-	}
+	dst_release(skb->dst);
+	if(skb->destructor)
+		skb->destructor(skb);
+	skb_headerinit(skb, NULL, 0);  /* clean state */
+	kfree_skbmem(skb);
 }
 
 /*
@@ -228,32 +219,24 @@
 struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask)
 {
 	struct sk_buff *n;
-	int inbuff = 0;
 	
-	if (!skb->inclone && skb_tailroom(skb) >= sizeof(struct sk_buff)) {
-		n = ((struct sk_buff *) skb->end) - 1;
-		skb->end -= sizeof(struct sk_buff);
-		skb->inclone = SKB_CLONE_ORIG;
-		inbuff = SKB_CLONE_INLINE;
-	} else {
-		n = kmalloc(sizeof(*n), gfp_mask);
-		if (!n)
-			return NULL;
-	}
+	n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
+	if (!n)
+		return NULL;
+
 	memcpy(n, skb, sizeof(*n));
-	atomic_set(&n->count, 1);
-	skb = skb->data_skb;
-	atomic_inc(&skb->count);
+	atomic_inc(skb_datarefp(skb));
+	skb->cloned = 1;
+       
 	atomic_inc(&net_allocs);
 	atomic_inc(&net_skbcount);
 	dst_clone(n->dst);
-	n->data_skb = skb;
+	n->cloned = 1;
 	n->next = n->prev = NULL;
 	n->list = NULL;
 	n->sk = NULL;
-	n->tries = 0;
+	n->is_clone = 1;
 	atomic_set(&n->users, 1);
-	n->inclone = inbuff;
 	n->destructor = NULL;
 	return n;
 }
@@ -287,6 +270,7 @@
 	skb_put(n,skb->len);
 	/* Copy the bytes */
 	memcpy(n->head,skb->head,skb->end-skb->head);
+	n->csum = skb->csum;
 	n->list=NULL;
 	n->sk=NULL;
 	n->when=skb->when;
@@ -302,7 +286,7 @@
 	n->ack_seq=skb->ack_seq;
 	memcpy(n->cb, skb->cb, sizeof(skb->cb));
 	n->used=skb->used;
-	n->tries=0;
+	n->is_clone=0;
 	atomic_set(&n->users, 1);
 	n->pkt_type=skb->pkt_type;
 	n->stamp=skb->stamp;
@@ -321,7 +305,7 @@
 	 *	Allocate the copy buffer
 	 */
  	 
-	n=alloc_skb(skb->truesize+newheadroom-headroom-sizeof(struct sk_buff), GFP_ATOMIC);
+	n=alloc_skb(skb->truesize+newheadroom-headroom, GFP_ATOMIC);
 	if(n==NULL)
 		return NULL;
 
@@ -352,7 +336,7 @@
  	n->end_seq=skb->end_seq;
 	n->ack_seq=skb->ack_seq;
 	n->used=skb->used;
-	n->tries=0;
+	n->is_clone=0;
 	atomic_set(&n->users, 1);
 	n->pkt_type=skb->pkt_type;
 	n->stamp=skb->stamp;
@@ -360,4 +344,28 @@
 	n->security=skb->security;
 
 	return n;
+}
+
+#if 0
+/* 
+ * 	Tune the memory allocator for a new MTU size.
+ */
+void skb_add_mtu(int mtu)
+{
+	/* Must match allocation in alloc_skb */
+	mtu = ((mtu + 15) & ~15) + sizeof(atomic_t);
+
+	kmem_add_cache_size(mtu);
+}
+#endif
+
+__initfunc(void skb_init(void))
+{
+	skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
+					      sizeof(struct sk_buff),
+					      0,
+					      SLAB_HWCACHE_ALIGN,
+					      skb_headerinit, NULL);
+	if (!skbuff_head_cache)
+		panic("cannot create skbuff cache");
 }
diff -u --recursive --new-file v2.1.90/linux/net/core/sock.c linux/net/core/sock.c
--- v2.1.90/linux/net/core/sock.c	Tue Mar 17 22:18:16 1998
+++ linux/net/core/sock.c	Mon Mar 23 16:48:25 1998
@@ -663,31 +663,13 @@
 			goto failure;
 
 		/*
-		 *	FIXME: Check 1003.1g should we deliver
-		 *	a signal here ???
+		 *	We should send SIGPIPE in these cases according to
+		 *	1003.1g draft 6.4. If we (the user) did a shutdown()
+		 *	call however we should not. 
 		 *
-		 *	Alan, could we solve this question once and forever?
-		 *
-		 *	I believe, datagram sockets should never
-		 *	generate SIGPIPE. Moreover, I DO think that
-		 *	TCP is allowed to generate it only on write()
-		 *	call, but never on send/sendto/sendmsg.
-		 *	(btw, Solaris generates it even on read() :-))
-		 *
-		 *	The reason is that SIGPIPE is global flag,
-		 *	so that library function using sockets (f.e. syslog()),
-		 *	must save/disable it on entry and restore on exit.
-		 *	As result, signal arriving for another thread will
-		 *	be lost. Generation it on write() is still necessary
-		 *	because a lot of stupid programs never check write()
-		 *	return value.
-		 *
-		 *	Seems, SIGPIPE is very bad idea, sort of gets().
-		 *	At least, we could have an option disabling
-		 *	this behaviour on per-socket and/or per-message base.
-		 *	BTW it is very easy - MSG_SIGPIPE flag, which
-		 *	always set by read/write and checked here.
-		 *						--ANK
+		 *	Note: This routine isnt just used for datagrams and
+		 *	anyway some datagram protocols have a notion of
+		 *	close down.
 		 */
 
 		err = -EPIPE;
@@ -699,7 +681,7 @@
 		else {
 			/* The buffer get won't block, or use the atomic queue. It does
 			   produce annoying no free page messages still.... */
-			skb = sock_wmalloc(sk, size, 0 , GFP_BUFFER);
+			skb = sock_wmalloc(sk, size, 0, GFP_BUFFER);
 			if (!skb)
 				skb=sock_wmalloc(sk, fallback, 0, sk->allocation);
 		}
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.1.90/linux/net/ipv4/af_inet.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/af_inet.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *
  *		AF_INET protocol family socket handler.
  *
- * Version:	$Id: af_inet.c,v 1.63 1998/03/08 05:56:12 davem Exp $
+ * Version:	$Id: af_inet.c,v 1.66 1998/03/21 07:27:58 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -274,7 +274,7 @@
 		sk->num = sk->prot->good_socknum();
 		if (sk->num == 0) 
 			return(-EAGAIN);
-		sk->dummy_th.source = htons(sk->num);
+		sk->sport = htons(sk->num);
 		sk->prot->hash(sk);
 		add_to_prot_sklist(sk);
 	}
@@ -304,6 +304,7 @@
 	if (sk->state != TCP_LISTEN) {
 		sk->ack_backlog = 0;
 		sk->state = TCP_LISTEN;
+		dst_release(xchg(&sk->dst_cache, NULL));
 		sk->prot->rehash(sk);
 		add_to_prot_sklist(sk);
 	}
@@ -348,7 +349,6 @@
 
 	switch (sock->type) {
 	case SOCK_STREAM:
-		/* Note for tcp that also wiped the dummy_th block for us. */
 		if (protocol && protocol != IPPROTO_TCP)
 			goto free_and_noproto;
 		protocol = IPPROTO_TCP;
@@ -412,17 +412,13 @@
 	sk->ip_mc_index=0;
 	sk->ip_mc_list=NULL;
 	
-	/*	Speed up by setting some standard state for the dummy_th
-	 *	if TCP uses it (maybe move to tcp_init later)
-	 */
-  	
 	if (sk->num) {
 		/* It assumes that any protocol which allows
 		 * the user to assign a number at socket
 		 * creation time automatically
 		 * shares.
 		 */
-		sk->dummy_th.source = htons(sk->num);
+		sk->sport = htons(sk->num);
 
 		/* Add to protocol hash chains. */
 		sk->prot->hash(sk);
@@ -552,9 +548,9 @@
 		return -EADDRINUSE;
 
 	sk->num = snum;
-	sk->dummy_th.source = htons(snum);
+	sk->sport = htons(snum);
 	sk->daddr = 0;
-	sk->dummy_th.dest = 0;
+	sk->dport = 0;
 	sk->prot->rehash(sk);
 	add_to_prot_sklist(sk);
 	dst_release(sk->dst_cache);
@@ -753,13 +749,13 @@
 	if (peer) {
 		if (!tcp_connected(sk->state)) 
 			return(-ENOTCONN);
-		sin->sin_port = sk->dummy_th.dest;
+		sin->sin_port = sk->dport;
 		sin->sin_addr.s_addr = sk->daddr;
 	} else {
 		__u32 addr = sk->rcv_saddr;
 		if (!addr)
 			addr = sk->saddr;
-		sin->sin_port = sk->dummy_th.source;
+		sin->sin_port = sk->sport;
 		sin->sin_addr.s_addr = addr;
 	}
 	*uaddr_len = sizeof(*sin);
@@ -798,7 +794,8 @@
 	struct sock *sk = sock->sk;
 
 	if (sk->shutdown & SEND_SHUTDOWN) {
-		send_sig(SIGPIPE, current, 1);
+		if (!(msg->msg_flags&MSG_NOSIGNAL))
+			send_sig(SIGPIPE, current, 1);
 		return(-EPIPE);
 	}
 	if (sk->prot->sendmsg == NULL) 
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/fib_rules.c linux/net/ipv4/fib_rules.c
--- v2.1.90/linux/net/ipv4/fib_rules.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/fib_rules.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: policy rules.
  *
- * Version:	$Id: fib_rules.c,v 1.3 1998/03/08 05:56:17 davem Exp $
+ * Version:	$Id: fib_rules.c,v 1.4 1998/03/21 07:27:58 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -86,7 +86,7 @@
 		    rtm->rtm_dst_len == r->r_dst_len &&
 		    (!rta[RTA_DST-1] || memcmp(RTA_DATA(rta[RTA_DST-1]), &r->r_dst, 4) == 0) &&
 		    rtm->rtm_tos == r->r_tos &&
-		    rtm->rtm_type == r->r_action &&
+		    (!rtm->rtm_type || rtm->rtm_type == r->r_action) &&
 		    (!rta[RTA_PRIORITY-1] || memcmp(RTA_DATA(rta[RTA_PRIORITY-1]), &r->r_preference, 4) == 0) &&
 		    (!rta[RTA_IFNAME-1] || strcmp(RTA_DATA(rta[RTA_IFNAME-1]), r->r_ifname) == 0) &&
 		    (!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) {
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.1.90/linux/net/ipv4/ip_fragment.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/ip_fragment.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *
  *		The IP fragmentation functionality.
  *		
- * Version:	$Id: ip_fragment.c,v 1.32 1998/03/08 05:56:21 davem Exp $
+ * Version:	$Id: ip_fragment.c,v 1.33 1998/03/19 08:34:08 davem Exp $
  *
  * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
  *		Alan Cox <Alan.Cox@linux.org>
@@ -430,11 +430,8 @@
 			qp->ihlen = ihl;
 			memcpy(qp->iph, iph, ihl+8);
 		}
-		del_timer(&qp->timer);
-		qp->timer.expires = jiffies + sysctl_ipfrag_time;	/* about 30 seconds */
-		qp->timer.data = (unsigned long) qp;	/* pointer to queue */
-		qp->timer.function = ip_expire;		/* expire function */
-		add_timer(&qp->timer);
+		/* about 30 seconds */
+		mod_timer(&qp->timer, jiffies + sysctl_ipfrag_time);
 	} else {
 		/* If we failed to create it, then discard the frame. */
 		if ((qp = ip_create(skb, iph)) == NULL) {
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v2.1.90/linux/net/ipv4/ip_fw.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/ip_fw.c	Mon Mar 23 16:48:25 1998
@@ -6,7 +6,7 @@
  *	license in recognition of the original copyright. 
  *				-- Alan Cox.
  *
- *	$Id: ip_fw.c,v 1.33 1998/03/15 03:31:43 davem Exp $
+ *	$Id: ip_fw.c,v 1.34 1998/03/20 09:12:06 davem Exp $
  *
  *	Ported from BSD to Linux,
  *		Alan Cox 22/Nov/1994.
@@ -683,11 +683,6 @@
 	if ((ftmp->fw_vianame)[0]) {
 		if (!(ftmp->fw_viadev = dev_get(ftmp->fw_vianame)))
 			ftmp->fw_viadev = (struct device *) -1;
-	} else if (ftmp->fw_via.s_addr) {
-		if (!(ftmp->fw_viadev = ip_dev_find(ftmp->fw_via.s_addr)))
-			ftmp->fw_viadev = (struct device *) -1;
-		else
-			memcpy(ftmp->fw_vianame, ftmp->fw_viadev->name, IFNAMSIZ);
 	} else
 		ftmp->fw_viadev = NULL;
 
@@ -732,11 +727,6 @@
 	if ((ftmp->fw_vianame)[0]) {
 		if (!(ftmp->fw_viadev = dev_get(ftmp->fw_vianame)))
 			ftmp->fw_viadev = (struct device *) -1;
-	} else if (ftmp->fw_via.s_addr) {
-		if (!(ftmp->fw_viadev = ip_dev_find(ftmp->fw_via.s_addr)))
-			ftmp->fw_viadev = (struct device *) -1;
-		else
-			memcpy(ftmp->fw_vianame, ftmp->fw_viadev->name, IFNAMSIZ);
 	} else
 		ftmp->fw_viadev = NULL;
 
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
--- v2.1.90/linux/net/ipv4/ip_masq.c	Wed Dec 10 09:45:16 1997
+++ linux/net/ipv4/ip_masq.c	Mon Mar 23 16:48:25 1998
@@ -1819,12 +1819,8 @@
 	struct ip_fw_masqctl *mctl = arg;
 	int ret = EINVAL;
 
-	ip_masq_lockz(&__ip_masq_lock, &masq_wait, 0);
-
 	if (1) /*  (mctl->mctl_action == IP_MASQ_MOD_CTL)  */
 		ret = ip_masq_mod_ctl(optname, mctl, arglen);
-
-	ip_masq_unlockz(&__ip_masq_lock, &masq_wait, 0);
 
 	return ret;
 }
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/ip_masq_autofw.c linux/net/ipv4/ip_masq_autofw.c
--- v2.1.90/linux/net/ipv4/ip_masq_autofw.c	Wed Dec 10 09:45:16 1997
+++ linux/net/ipv4/ip_masq_autofw.c	Mon Mar 23 16:48:25 1998
@@ -119,10 +119,8 @@
 		{
 			if (af->flags & IP_AUTOFW_USETIME)
 			{
-				if (af->timer.expires)
-					del_timer(&af->timer);
-				af->timer.expires=jiffies+IP_AUTOFW_EXPIRE;
-				add_timer(&af->timer);
+				mod_timer(&af->timer,
+					  jiffies+IP_AUTOFW_EXPIRE);
 			}
 			af->flags|=IP_AUTOFW_ACTIVE;
 			af->lastcontact=where;
@@ -139,9 +137,7 @@
 	af=ip_autofw_check_range(where, port,protocol);
 	if (af)
 	{
-		del_timer(&af->timer);
-		af->timer.expires=jiffies+IP_AUTOFW_EXPIRE;
-		add_timer(&af->timer);
+		mod_timer(&af->timer, jiffies+IP_AUTOFW_EXPIRE);
 	}
 }
 #endif
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/ip_masq_mod.c linux/net/ipv4/ip_masq_mod.c
--- v2.1.90/linux/net/ipv4/ip_masq_mod.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/ip_masq_mod.c	Mon Mar 23 16:48:25 1998
@@ -275,7 +275,7 @@
 
 	IP_MASQ_DEBUG(1, "searching mmod_name \"%s\"\n", mmod_name);
 	
-	for (mmod=ip_masq_mod_reg_base; mmod ; mmod=mmod->next) {
+	for (mmod=ip_masq_mod_reg_base; mmod ; mmod=mmod->next_reg) {
 		if (mmod->mmod_ctl && *(mmod_name)
 				&& (strcmp(mmod_name, mmod->mmod_name)==0)) {
 			/* HIT */
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.1.90/linux/net/ipv4/ip_output.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/ip_output.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) output module.
  *
- * Version:	$Id: ip_output.c,v 1.48 1998/03/08 05:56:25 davem Exp $
+ * Version:	$Id: ip_output.c,v 1.50 1998/03/20 09:12:08 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -81,46 +81,24 @@
 
 int ip_id_count = 0;
 
-int ip_build_pkt(struct sk_buff *skb, struct sock *sk, u32 saddr, u32 daddr,
-		 struct ip_options *opt)
+/* Generate a checksum for an outgoing IP datagram. */
+__inline__ void ip_send_check(struct iphdr *iph)
 {
-	struct rtable *rt;
-	u32 final_daddr = daddr;
+	iph->check = 0;
+	iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+}
+
+void ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
+			   u32 saddr, u32 daddr, struct ip_options *opt)
+{
+	struct rtable *rt = (struct rtable *)skb->dst;
 	struct iphdr *iph;
-	int err;
 	
-	if (opt && opt->srr)
-		daddr = opt->faddr;
-
-	err = ip_route_output(&rt, daddr, saddr, RT_TOS(sk->ip_tos) |
-			      RTO_CONN | sk->localroute, sk->bound_dev_if);
-	if (err)
-	{
-		ip_statistics.IpOutNoRoutes++;
-		return err;
-	}
-
-	if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) {
-		ip_rt_put(rt);
-		ip_statistics.IpOutNoRoutes++;
-		return -ENETUNREACH;
-	}
-
-	skb->dst = dst_clone(&rt->u.dst);
-	skb_reserve(skb, (rt->u.dst.dev->hard_header_len+15)&~15);
-
-	/*
-	 *	Now build the IP header.
-	 */
-
-	/*
-	 *	Build the IP addresses
-	 */
-	 
+	/* Build the IP header. */
 	if (opt)
-		iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr) + opt->optlen);
+		iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr) + opt->optlen);
 	else
-		iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr));
+		iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));
 
 	iph->version  = 4;
 	iph->ihl      = 5;
@@ -133,92 +111,19 @@
 	iph->daddr    = rt->rt_dst;
 	iph->saddr    = rt->rt_src;
 	iph->protocol = sk->protocol;
+	iph->tot_len  = htons(skb->len);
+	iph->id       = htons(ip_id_count++);
 	skb->nh.iph   = iph;
-	skb->h.raw    = (unsigned char*)(iph+1);
 
-	if (opt && opt->optlen)
-	{
+	if (opt && opt->optlen) {
 		iph->ihl += opt->optlen>>2;
-		skb->h.raw += opt->optlen;
-		ip_options_build(skb, opt, final_daddr, rt, 0);
-	}
-	
-	ip_rt_put(rt);
-	return 0;
-}
-
-/*
- * This routine builds the appropriate hardware/IP headers for
- * the routine.
- */
-int ip_build_header(struct sk_buff *skb, struct sock *sk)
-{
-	struct rtable *rt;
-	struct ip_options *opt = sk->opt;
-	u32 daddr = sk->daddr;
-	u32 final_daddr = daddr;
-	struct iphdr *iph;
-	int err;
-
-	if (opt && opt->srr)
-		daddr = opt->faddr;
-
-	rt = (struct rtable*)sk->dst_cache;
-
-	if (!rt || rt->u.dst.obsolete) {
-		sk->dst_cache = NULL;
-		ip_rt_put(rt);
-		err = ip_route_output(&rt, daddr, sk->saddr, RT_TOS(sk->ip_tos) |
-				      RTO_CONN | sk->localroute, sk->bound_dev_if);
-		if (err)
-			return err;
-		sk->dst_cache = &rt->u.dst;
+		ip_options_build(skb, opt, daddr, rt, 0);
 	}
 
-	if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) {
-		sk->dst_cache = NULL;
-		ip_rt_put(rt);
-		ip_statistics.IpOutNoRoutes++;
-		return -ENETUNREACH;
-	}
-
-	skb->dst = dst_clone(sk->dst_cache);
-	skb_reserve(skb, MAX_HEADER);
-	
-	/*
-	 *	Now build the IP header.
-	 */
-
-	/*
-	 *	Build the IP addresses
-	 */
-	 
-	if (opt)
-		iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr) + opt->optlen);
-	else
-		iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr));
-
-	iph->version  = 4;
-	iph->ihl      = 5;
-	iph->tos      = sk->ip_tos;
-	iph->frag_off = 0;
-	if (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
-		!(rt->u.dst.mxlock&(1<<RTAX_MTU)))
-		iph->frag_off |= htons(IP_DF);
-	iph->ttl      = sk->ip_ttl;
-	iph->daddr    = rt->rt_dst;
-	iph->saddr    = rt->rt_src;
-	iph->protocol = sk->protocol;
-	skb->nh.iph   = iph;
-	skb->h.raw    = (unsigned char*)(iph+1);
-
-	if (!opt || !opt->optlen)
-		return 0;
-	iph->ihl += opt->optlen>>2;
-	skb->h.raw += opt->optlen;
-	ip_options_build(skb, opt, final_daddr, rt, 0);
+	ip_send_check(iph);
 
-	return 0;
+	/* Send it out. */
+	skb->dst->output(skb);
 }
 
 int __ip_finish_output(struct sk_buff *skb)
@@ -322,78 +227,101 @@
 }
 #endif
 
-/*
- *	Generate a checksum for an outgoing IP datagram.
- */
-
-void ip_send_check(struct iphdr *iph)
-{
-	iph->check = 0;
-	iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
-}
-
-
-
-/*
- * Queues a packet to be sent, and starts the transmitter if necessary.  
+/* Queues a packet to be sent, and starts the transmitter if necessary.  
  * This routine also needs to put in the total length and compute the 
- * checksum
+ * checksum.  We use to do this in two stages, ip_build_header() then
+ * this, but that scheme created a mess when routes disappeared etc.
+ * So we do it all here, and the TCP send engine has been changed to
+ * match. (No more unroutable FIN disasters, etc. wheee...)  This will
+ * most likely make other reliable transport layers above IP easier
+ * to implement under Linux.
  */
-
 void ip_queue_xmit(struct sk_buff *skb)
 {
 	struct sock *sk = skb->sk;
-	struct rtable *rt = (struct rtable*)skb->dst;
+	struct ip_options *opt = sk->opt;
+	struct rtable *rt;
 	struct device *dev;
+	struct iphdr *iph;
 	unsigned int tot_len;
-	struct iphdr *iph = skb->nh.iph;
 
-	tot_len = skb->len;
-	iph->tot_len = htons(tot_len);
-	iph->id = htons(ip_id_count++);
+	/* Make sure we can route this packet. */
+	rt = (struct rtable *) sk->dst_cache;
+	if(rt == NULL || rt->u.dst.obsolete) {
+		u32 daddr;
 
-	if (rt->u.dst.obsolete) {
-		/* Ugly... ugly... but what can I do?
-		   Essentially it is "ip_reroute_output" function. --ANK
-		*/
-		struct rtable *nrt;
-		if (ip_route_output(&nrt, rt->key.dst, rt->key.src,
-				    rt->key.tos | RTO_CONN, 
-				    sk?sk->bound_dev_if:0)) 
-			goto drop;
-		skb->dst = &nrt->u.dst;
+		sk->dst_cache = NULL;
 		ip_rt_put(rt);
-		rt = nrt;
+
+		/* Use correct destination address if we have options. */
+		daddr = sk->daddr;
+		if(opt && opt->srr)
+			daddr = opt->faddr;
+
+		/* If this fails, retransmit mechanism of transport layer will
+		 * keep trying until route appears or the connection times itself
+		 * out.
+		 */
+		if(ip_route_output(&rt, daddr, sk->saddr,
+				   RT_TOS(sk->ip_tos) | RTO_CONN | sk->localroute,
+				   sk->bound_dev_if))
+			goto drop;
+		sk->dst_cache = &rt->u.dst;
+	}
+	if(opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
+		goto no_route;
+
+	/* We have a route, so grab a reference. */
+	skb->dst = dst_clone(sk->dst_cache);
+
+	/* OK, we know where to send it, allocate and build IP header. */
+	iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));
+	iph->version  = 4;
+	iph->ihl      = 5;
+	iph->tos      = sk->ip_tos;
+	iph->frag_off = 0;
+	if(sk->ip_pmtudisc == IP_PMTUDISC_WANT && !(rt->u.dst.mxlock & (1 << RTAX_MTU)))
+		iph->frag_off |= __constant_htons(IP_DF);
+	iph->ttl      = sk->ip_ttl;
+	iph->daddr    = rt->rt_dst;
+	iph->saddr    = rt->rt_src;
+	iph->protocol = sk->protocol;
+	skb->nh.iph   = iph;
+	/* Transport layer set skb->h.foo itself. */
+
+	if(opt && opt->optlen) {
+		iph->ihl += opt->optlen >> 2;
+		ip_options_build(skb, opt, sk->daddr, rt, 0);
 	}
 
+	tot_len = skb->len;
+	iph->tot_len = htons(tot_len);
+	iph->id = htons(ip_id_count++);
+
 	dev = rt->u.dst.dev;
 
-	if (call_out_firewall(PF_INET, dev, iph, NULL,&skb) < FW_ACCEPT) 
+	if (call_out_firewall(PF_INET, dev, iph, NULL, &skb) < FW_ACCEPT) 
 		goto drop;
 
 #ifdef CONFIG_NET_SECURITY	
-	/*
-	 *	Add an IP checksum (must do this before SECurity because
-	 *	of possible tunneling)
+	/* Add an IP checksum (must do this before SECurity because
+	 * of possible tunneling).
 	 */
-
 	ip_send_check(iph);
-
-	if (call_out_firewall(PF_SECURITY, NULL, NULL, (void *) 4, &skb)<FW_ACCEPT)
+	if (call_out_firewall(PF_SECURITY, NULL, NULL, (void *) 4, &skb) < FW_ACCEPT)
 		goto drop;
-
 	iph = skb->nh.iph;
-	/* don't update tot_len, as the dev->mtu is already decreased */	
+	/* Don't update tot_len, as the dev->mtu is already decreased. */
 #endif
-
+	/* This can happen when the transport layer has segments queued
+	 * with a cached route, and by the time we get here things are
+	 * re-routed to a device with a different MTU than the original
+	 * device.  Sick, but we must cover it.
+	 */
 	if (skb_headroom(skb) < dev->hard_header_len && dev->hard_header) {
 		struct sk_buff *skb2;
-		/* ANK: It is almost impossible, but
-		 * if you loaded module device with hh_len > MAX_HEADER,
-		 * and if a route changed to this device,
-		 * and if (uh...) TCP had segments queued on this route...
-		 */
-		skb2 = skb_realloc_headroom(skb, (dev->hard_header_len+15)&~15);
+
+		skb2 = skb_realloc_headroom(skb, (dev->hard_header_len + 15) & ~15);
 		kfree_skb(skb);
 		if (skb2 == NULL)
 			return;
@@ -401,40 +329,35 @@
 		iph = skb->nh.iph;
 	}
 
-	/*
-	 *	Do we need to fragment. Again this is inefficient.
-	 *	We need to somehow lock the original buffer and use
-	 *	bits of it.
+	/* Do we need to fragment.  Again this is inefficient.  We
+	 * need to somehow lock the original buffer and use bits of it.
 	 */
-
 	if (tot_len > rt->u.dst.pmtu)
 		goto fragment;
 
 #ifndef CONFIG_NET_SECURITY
-	/*
-	 *	Add an IP checksum
-	 */
-
+	/* Add an IP checksum. */
 	ip_send_check(iph);
 #endif
-
-	if (sk)
-		skb->priority = sk->priority;
+	skb->priority = sk->priority;
 	skb->dst->output(skb);
 	return;
 
 fragment:
-	if ((iph->frag_off & htons(IP_DF)))
-	{
+	if ((iph->frag_off & htons(IP_DF)) != 0) {
 		printk(KERN_DEBUG "sending pkt_too_big to self\n");
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
 			  htonl(rt->u.dst.pmtu));
 		goto drop;
 	}
-	
 	ip_fragment(skb, skb->dst->output);
 	return;
 
+no_route:
+	sk->dst_cache = NULL;
+	ip_rt_put(rt);
+	ip_statistics.IpOutNoRoutes++;
+	/* Fall through... */
 drop:
 	kfree_skb(skb);
 }
@@ -948,14 +871,7 @@
 	reply->dst = &rt->u.dst;
 	skb_reserve(reply, (rt->u.dst.dev->hard_header_len+15)&~15);
 
-	/*
-	 *	Now build the IP header.
-	 */
-
-	/*
-	 *	Build the IP addresses
-	 */
-	 
+	/* Now build the IP header. */
 	reply->nh.iph = iph = (struct iphdr *)skb_put(reply, iphlen);
 
 	iph->version  = 4;
@@ -966,6 +882,7 @@
 	iph->daddr    = rt->rt_dst;
 	iph->saddr    = rt->rt_src;
 	iph->protocol = skb->nh.iph->protocol;
+	iph->id       = htons(ip_id_count++);
 	
 	ip_options_build(reply, &replyopts.opt, daddr, rt, 0);
 
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/proc.c linux/net/ipv4/proc.c
--- v2.1.90/linux/net/ipv4/proc.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/proc.c	Mon Mar 23 16:48:25 1998
@@ -7,7 +7,7 @@
  *		PROC file system.  It is mainly used for debugging and
  *		statistics.
  *
- * Version:	$Id: proc.c,v 1.26 1998/03/13 08:02:12 davem Exp $
+ * Version:	$Id: proc.c,v 1.27 1998/03/18 07:51:59 davem Exp $
  *
  * Authors:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de>
@@ -59,7 +59,7 @@
 		" %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu",
 		i,
 		(long unsigned int)req->af.v4_req.loc_addr,
-		ntohs(sk->dummy_th.source),
+		ntohs(sk->sport),
 		(long unsigned int)req->af.v4_req.rmt_addr,
 		req->rmt_port,
 		TCP_SYN_RECV,
@@ -83,8 +83,8 @@
 
 	dest  = sp->daddr;
 	src   = sp->rcv_saddr;
-	destp = sp->dummy_th.dest;
-	srcp  = sp->dummy_th.source;
+	destp = sp->dport;
+	srcp  = sp->sport;
 	
 	/* FIXME: The fact that retransmit_timer occurs as a field
 	 * in two different parts of the socket structure is,
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.1.90/linux/net/ipv4/route.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/route.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.41 1998/03/08 20:52:38 davem Exp $
+ * Version:	$Id: route.c,v 1.42 1998/03/20 09:12:09 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -104,6 +104,7 @@
 int ip_rt_redirect_silence = ((HZ/50) << (9+1));
 int ip_rt_error_cost = HZ;
 int ip_rt_error_burst = 5*HZ;
+int ip_rt_gc_elasticity = 8;
 
 static unsigned long rt_deadline = 0;
 
@@ -398,10 +399,10 @@
 
 	last_gc = now;
 	if (atomic_read(&ipv4_dst_ops.entries) < ipv4_dst_ops.gc_thresh)
-		expire = ip_rt_gc_timeout;
+		expire = ip_rt_gc_timeout>>1;
 
 out:
-	expire >>= 1;
+	expire -= expire>>ip_rt_gc_elasticity;
 	end_bh_atomic();
 	return (atomic_read(&ipv4_dst_ops.entries) > ip_rt_max_size);
 }
@@ -1739,6 +1740,9 @@
          &proc_dointvec},
 	{NET_IPV4_ROUTE_ERROR_BURST, "error_burst",
          &ip_rt_error_burst, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+	{NET_IPV4_ROUTE_GC_ELASTICITY, "gc_elasticity",
+         &ip_rt_gc_elasticity, sizeof(int), 0644, NULL,
          &proc_dointvec},
 	 {0}
 };
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c
--- v2.1.90/linux/net/ipv4/sysctl_net_ipv4.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/sysctl_net_ipv4.c	Mon Mar 23 16:48:25 1998
@@ -1,7 +1,7 @@
 /*
  * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem.
  *
- * $Id: sysctl_net_ipv4.c,v 1.28 1998/03/15 03:23:21 davem Exp $
+ * $Id: sysctl_net_ipv4.c,v 1.30 1998/03/23 23:56:29 davem Exp $
  *
  * Begun April 1, 1996, Mike Shaver.
  * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS]
@@ -47,6 +47,7 @@
 extern int sysctl_tcp_hoe_retransmits;
 extern int sysctl_tcp_timestamps;
 extern int sysctl_tcp_window_scaling;
+extern int sysctl_tcp_sack;
 extern int sysctl_tcp_keepalive_time;
 extern int sysctl_tcp_keepalive_probes;
 extern int sysctl_tcp_max_ka_probes;
@@ -103,6 +104,9 @@
          &proc_dointvec},
         {NET_IPV4_TCP_WINDOW_SCALING, "tcp_window_scaling",
          &sysctl_tcp_window_scaling, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+        {NET_IPV4_TCP_SACK, "tcp_sack",
+         &sysctl_tcp_sack, sizeof(int), 0644, NULL,
          &proc_dointvec},
 	{NET_IPV4_TCP_VEGAS_CONG_AVOID, "tcp_vegas_cong_avoid",
 	 &sysctl_tcp_cong_avoidance, sizeof(int), 0644,
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.90/linux/net/ipv4/tcp.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/tcp.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.96 1998/03/16 02:25:55 davem Exp $
+ * Version:	$Id: tcp.c,v 1.104 1998/03/22 22:10:30 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -668,7 +668,7 @@
 			return sock_error(sk);
 		if((1 << sk->state) &
 		   ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) {
-			if(sk->keepopen)
+			if(sk->keepopen && !(flags&MSG_NOSIGNAL))
 				send_sig(SIGPIPE, tsk, 0);
 			return -EPIPE;
 		}
@@ -733,15 +733,25 @@
 
 int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags)
 {
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	int mss_now = sk->mss;
 	int err = 0;
 	int copied  = 0;
-	struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
 
 	/* Wait for a connection to finish. */
 	if ((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
 		if((err = wait_for_tcp_connect(sk, flags)) != 0)
 			return err;
 
+	/* The socket is locked, nothing can change the state of pending
+	 * SACKs or IP options.
+	 */
+	if(tp->sack_ok && tp->num_sacks)
+		mss_now -= (TCPOLEN_SACK_BASE_ALIGNED +
+			    (tp->num_sacks * TCPOLEN_SACK_PERBLOCK));
+	if(sk->opt && sk->opt->optlen)
+		mss_now -= (sk->opt->optlen);
+
 	/* Ok commence sending. */
 	while(--iovlen >= 0) {
 		int seglen=iov->iov_len;
@@ -769,22 +779,19 @@
 			 */
 			if (tp->send_head && !(flags & MSG_OOB)) {
 				skb = sk->write_queue.prev;
-				copy = skb->tail -
-					((unsigned char *)(skb->h.th) +
-					 tp->tcp_header_len);
-				/* This window_seq test is somewhat dangerous
-				 * If the remote does SWS avoidance we should
+				copy = skb->len;
+				/* If the remote does SWS avoidance we should
 				 * queue the best we can if not we should in 
 				 * fact send multiple packets...
-				 * a method for detecting this would be most
-				 * welcome
+				 * A method for detecting this would be most
+				 * welcome.
 				 */
 				if (skb_tailroom(skb) > 0 &&
-				    (sk->mss - copy) > 0 &&
+				    (mss_now - copy) > 0 &&
 				    tp->snd_nxt < skb->end_seq) {
-					int last_byte_was_odd = (copy & 1);
+					int last_byte_was_odd = (copy % 4);
 
-					copy = sk->mss - copy;
+					copy = mss_now - copy;
 					if(copy > skb_tailroom(skb))
 						copy = skb_tailroom(skb);
 					if(copy > seglen)
@@ -793,12 +800,8 @@
 						if(copy_from_user(skb_put(skb, copy),
 								  from, copy))
 							err = -EFAULT;
-						skb->csum = csum_partial(
-							(((unsigned char *)skb->h.th) +
-							 tp->tcp_header_len),
-							(skb->tail -
-							 (((unsigned char *)skb->h.th) +
-							  tp->tcp_header_len)), 0);
+						skb->csum = csum_partial(skb->data,
+									 skb->len, 0);
 					} else {
 						skb->csum =
 							csum_and_copy_from_user(
@@ -810,6 +813,8 @@
 					from += copy;
 					copied += copy;
 					seglen -= copy;
+					if(!seglen && !iovlen)
+						TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH;
 					continue;
 				}
 			}
@@ -828,18 +833,17 @@
 			 */
 			copy = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
 			if(copy >= (tp->max_window >> 1))
-				copy = min(copy, sk->mss);
+				copy = min(copy, mss_now);
 			else
-				copy = sk->mss;
+				copy = mss_now;
 			if(copy > seglen)
 				copy = seglen;
 
-			tmp = MAX_HEADER + sk->prot->max_header +
-				sizeof(struct sk_buff) + 15;
+			tmp = MAX_HEADER + sk->prot->max_header + 15;
 			queue_it = 0;
-			if (copy < min(sk->mss, tp->max_window >> 1) &&
+			if (copy < min(mss_now, tp->max_window >> 1) &&
 			    !(flags & MSG_OOB)) {
-				tmp += min(sk->mss, tp->max_window);
+				tmp += min(mss_now, tp->max_window);
 
 				/* What is happening here is that we want to
 				 * tack on later members of the users iovec
@@ -869,35 +873,34 @@
 				continue;
 			}
 
-			/* FIXME: we need to optimize this.
-			 * Perhaps some hints here would be good.
-			 */
-			tmp = tp->af_specific->build_net_header(sk, skb);
-			if (tmp < 0) {
-				kfree_skb(skb);
-				err = tmp;
-				goto do_interrupted;
-			}
-
-			skb->h.th =(struct tcphdr *)
-			  skb_put(skb,tp->tcp_header_len);
-
 			seglen -= copy;
-			tcp_build_header_data(skb->h.th, sk, seglen || iovlen);
 
+			/* Prepare control bits for TCP header creation engine. */
+			TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK |
+						  ((!seglen && !iovlen) ?
+						   TCPCB_FLAG_PSH : 0));
+			TCP_SKB_CB(skb)->sacked = 0;
 			if (flags & MSG_OOB) {
-				skb->h.th->urg = 1;
-				skb->h.th->urg_ptr = ntohs(copy);
-			}
-
+				TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_URG;
+				TCP_SKB_CB(skb)->urg_ptr = copy;
+			} else
+				TCP_SKB_CB(skb)->urg_ptr = 0;
+
+			/* TCP data bytes are SKB_PUT() on top, later
+			 * TCP+IP+DEV headers are SKB_PUSH()'d beneath.
+			 * Reserve header space and checksum the data.
+			 */
+			skb_reserve(skb, MAX_HEADER + sk->prot->max_header);
 			skb->csum = csum_and_copy_from_user(from,
 					skb_put(skb, copy), copy, 0, &err);
 
 			from += copy;
 			copied += copy;
 
-			tp->write_seq += copy;
+			skb->seq = tp->write_seq;
+			skb->end_seq = skb->seq + copy;
 
+			/* This advances tp->write_seq for us. */
 			tcp_send_skb(sk, skb, queue_it);
 		}
 	}
@@ -913,7 +916,8 @@
 do_shutdown:
 	if(copied)
 		return copied;
-	send_sig(SIGPIPE, current, 0);
+	if (!(flags&MSG_NOSIGNAL))
+		send_sig(SIGPIPE, current, 0);
 	return -EPIPE;
 do_interrupted:
 	if(copied)
@@ -1044,9 +1048,20 @@
   	/* We send an ACK if we can now advertise a non-zero window
 	 * which has been raised "significantly".
   	 */
-	if((copied > 0) &&
-	   (copied >= tcp_receive_window(&sk->tp_pinfo.af_tcp)))
-		tcp_read_wakeup(sk);
+	if(copied > 0) {
+		struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+		__u32 rcv_window_now = tcp_receive_window(tp);
+
+		/* We won't be raising the window any further than
+		 * the window-clamp allows.  Our window selection
+		 * also keeps things a nice multiple of MSS.  These
+		 * checks are necessary to prevent spurious ACKs
+		 * which don't advertize a larger window.
+		 */
+		if((copied >= rcv_window_now) &&
+		   ((rcv_window_now + sk->mss) <= tp->window_clamp))
+			tcp_read_wakeup(sk);
+	}
 }
 
 
@@ -1319,12 +1334,8 @@
 	 *	that we won't make the old 4*rto = almost no time - whoops
 	 *	reset mistake.
 	 */
-	if(dead && ns==TCP_FIN_WAIT2) {
-		if(sk->timer.prev && del_timer(&sk->timer))
-			add_timer(&sk->timer);
-		else
-			tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout);
-	}
+	if(dead && ns == TCP_FIN_WAIT2 && !sk->timer.prev)
+		tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout);
 
 	return send_fin;
 }
@@ -1448,12 +1459,8 @@
 	/* Now that the socket is dead, if we are in the FIN_WAIT2 state
 	 * we may need to set up a timer.
          */
-	if (sk->state==TCP_FIN_WAIT2) {
-		if(sk->timer.prev && del_timer(&sk->timer))
-			add_timer(&sk->timer);
-		else
-			tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout);
-	}
+	if (sk->state == TCP_FIN_WAIT2 && !sk->timer.prev)
+		tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout);
 
 	sk->dead = 1;
 	release_sock(sk);
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.1.90/linux/net/ipv4/tcp_input.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/tcp_input.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.84 1998/03/15 03:23:20 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.98 1998/03/23 22:54:48 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -72,9 +72,10 @@
  */
 int sysctl_tcp_timestamps = 1;
 int sysctl_tcp_window_scaling = 1;
+int sysctl_tcp_sack = 1;
+int sysctl_tcp_hoe_retransmits = 1;
 
 int sysctl_tcp_cong_avoidance;
-int sysctl_tcp_hoe_retransmits;
 int sysctl_tcp_syncookies = SYNC_INIT; 
 int sysctl_tcp_stdurg;
 int sysctl_tcp_rfc1337;
@@ -177,7 +178,6 @@
  * some modification to the RTO calculation that takes delayed
  * ack bais into account? This needs serious thought. -- erics
  */
-
 static __inline__ void tcp_bound_rto(struct tcp_opt *tp)
 {
 	if (tp->rto > 120*HZ)
@@ -187,7 +187,6 @@
 }
 
 /* WARNING: this must not be called if tp->saw_timestamp was false. */
-
 extern __inline__ void tcp_replace_ts_recent(struct tcp_opt *tp, __u32 end_seq)
 {
 	/* From draft-ietf-tcplw-high-performance: the correct
@@ -226,10 +225,7 @@
 	return 0;
 }
 
-/*
- *	This functions checks to see if the tcp header is actually acceptable. 
- */
- 
+/* This functions checks to see if the tcp header is actually acceptable. */
 extern __inline__ int tcp_sequence(struct tcp_opt *tp, u32 seq, u32 end_seq)
 {
 	if (seq == tp->rcv_nxt)
@@ -238,11 +234,7 @@
 	return __tcp_sequence(tp, seq, end_seq);
 }
 
-/*
- *	When we get a reset we do this. This probably is a tcp_output routine
- *	really.
- */
-
+/* When we get a reset we do this. */
 static void tcp_reset(struct sock *sk, struct sk_buff *skb)
 {
 	sk->zapped = 1;
@@ -264,14 +256,36 @@
 		sk->state_change(sk);
 }
 
-/*
- *	Look for tcp options. Normally only called on SYN and SYNACK packets.
- *	But, this can also be called on packets in the established flow when
- *	the fast version below fails.
- *	FIXME: surely this can be more efficient. -- erics
+/* This tags the retransmission queue when SACKs arrive. */
+static void tcp_sacktag_write_queue(struct sock *sk, struct tcp_sack_block *sp, int nsacks)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	int i = nsacks;
+
+	while(i--) {
+		struct sk_buff *skb = skb_peek(&sk->write_queue);
+		__u32 start_seq = ntohl(sp->start_seq);
+		__u32 end_seq = ntohl(sp->end_seq);
+
+		while((skb != NULL) &&
+		      (skb != tp->send_head) &&
+		      (skb != (struct sk_buff *)&sk->write_queue)) {
+			/* We play conservative, we don't allow SACKS to partially
+			 * tag a sequence space.
+			 */
+			if(!after(start_seq, skb->seq) && !before(end_seq, skb->end_seq))
+				TCP_SKB_CB(skb)->sacked |= TCPCB_SACKED_ACKED;
+			skb = skb->next;
+		}
+		sp++; /* Move on to the next SACK block. */
+	}
+}
+
+/* Look for tcp options. Normally only called on SYN and SYNACK packets.
+ * But, this can also be called on packets in the established flow when
+ * the fast version below fails.
  */
- 
-void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, int no_fancy)
+void tcp_parse_options(struct sock *sk, struct tcphdr *th, struct tcp_opt *tp, int no_fancy)
 {
 	unsigned char *ptr;
 	int length=(th->doff*4)-sizeof(struct tcphdr);
@@ -281,49 +295,68 @@
 
 	while(length>0) {
 	  	int opcode=*ptr++;
-	  	int opsize=*ptr++;
-		if (length - opsize < 0)	/* Don't parse partial options */
-			break;
-	  	switch(opcode) {
-	  		case TCPOPT_EOL:
-	  			return;
-	  		case TCPOPT_NOP:	/* Ref: RFC 793 section 3.1 */
-	  			length--;
-	  			ptr--;		/* the opsize=*ptr++ above was a mistake */
-	  			continue;
-	  		
-	  		default:
-	  			if(opsize<=2)	/* Avoid silly options looping forever */
-	  				return;
+		int opsize;
+
+		switch (opcode) {
+			case TCPOPT_EOL:
+				return;
+			case TCPOPT_NOP:	/* Ref: RFC 793 section 3.1 */
+				length--;
+				continue;
+			default:
+				opsize=*ptr++;
+				if (opsize < 2) /* "silly options" */
+					return;
+				if (opsize > length)
+					break;	/* don't parse partial options */
 	  			switch(opcode) {
-	  				case TCPOPT_MSS:
-	  					if(opsize==TCPOLEN_MSS && th->syn) {
-							tp->in_mss = ntohs(*(__u16 *)ptr);
-							if (tp->in_mss == 0)
-								tp->in_mss = 536;
+				case TCPOPT_MSS:
+					if(opsize==TCPOLEN_MSS && th->syn) {
+						tp->in_mss = ntohs(*(__u16 *)ptr);
+						if (tp->in_mss == 0)
+							tp->in_mss = 536;
+					}
+					break;
+				case TCPOPT_WINDOW:
+					if(opsize==TCPOLEN_WINDOW && th->syn)
+						if (!no_fancy && sysctl_tcp_window_scaling) {
+							tp->wscale_ok = 1;
+							tp->snd_wscale = *(__u8 *)ptr;
+						}
+					break;
+				case TCPOPT_TIMESTAMP:
+					if(opsize==TCPOLEN_TIMESTAMP) {
+						if (sysctl_tcp_timestamps && !no_fancy) {
+							tp->tstamp_ok = 1;
+							tp->saw_tstamp = 1;
+							tp->rcv_tsval = ntohl(*(__u32 *)ptr);
+							tp->rcv_tsecr = ntohl(*(__u32 *)(ptr+4));
 						}
-	  					break;
-					case TCPOPT_WINDOW:
-	  					if(opsize==TCPOLEN_WINDOW && th->syn)
-							if (!no_fancy && sysctl_tcp_window_scaling) {
-								tp->wscale_ok = 1;
-								tp->snd_wscale = *(__u8 *)ptr;
-							}
-						break;
-					case TCPOPT_TIMESTAMP:
-	  					if(opsize==TCPOLEN_TIMESTAMP) {
-							/* Cheaper to set again then to
-							 * test syn. Optimize this?
-							 */
-							if (sysctl_tcp_timestamps && !no_fancy) {
-								tp->tstamp_ok = 1;
-								tp->saw_tstamp = 1;
-								tp->rcv_tsval = ntohl(*(__u32 *)ptr);
-								tp->rcv_tsecr = ntohl(*(__u32 *)(ptr+4));
-							}
+					}
+					break;
+				case TCPOPT_SACK_PERM:
+					if(opsize==TCPOLEN_SACK_PERM && th->syn) {
+						if (sysctl_tcp_sack && !no_fancy) {
+							tp->sack_ok = 1;
+							tp->num_sacks = 0;
 						}
-						break;
-	  			}
+					}
+					break;
+
+				case TCPOPT_SACK:
+					if((opsize >= (TCPOLEN_SACK_BASE + TCPOLEN_SACK_PERBLOCK)) &&
+					   sysctl_tcp_sack && (sk != NULL) && !th->syn) {
+						int sack_bytes = opsize - TCPOLEN_SACK_BASE;
+
+						if(!(sack_bytes % TCPOLEN_SACK_PERBLOCK)) {
+							int num_sacks = sack_bytes >> 3;
+							struct tcp_sack_block *sackp;
+
+							sackp = (struct tcp_sack_block *)ptr;
+							tcp_sacktag_write_queue(sk, sackp, num_sacks);
+						}
+					}
+	  			};
 	  			ptr+=opsize-2;
 	  			length-=opsize;
 	  	};
@@ -331,13 +364,11 @@
 }
 
 /* Fast parse options. This hopes to only see timestamps.
- * If it is wrong it falls back on tcp_parse_option().
- * This should probably get extended for timestamps as well.
- * Assembly code anyone? -- erics
+ * If it is wrong it falls back on tcp_parse_options().
  */
-static __inline__ int tcp_fast_parse_options(struct tcphdr *th, struct tcp_opt *tp)
+static __inline__ int tcp_fast_parse_options(struct sock *sk, struct tcphdr *th, struct tcp_opt *tp)
 {
-	/* If we didn't send out any options ignore them all */
+	/* If we didn't send out any options ignore them all. */
 	if (tp->tcp_header_len == sizeof(struct tcphdr))
 		return 0;
 	if (th->doff == sizeof(struct tcphdr)>>2) {
@@ -353,13 +384,14 @@
 			return 1;
 		}
 	}
-	tcp_parse_options(th,tp,0);
+	tcp_parse_options(sk, th, tp, 0);
 	return 1;
 }
 
-#define FLAG_DATA		0x01
-#define FLAG_WIN_UPDATE		0x02
-#define FLAG_DATA_ACKED		0x04
+#define FLAG_DATA		0x01 /* Incoming frame contained data.		*/
+#define FLAG_WIN_UPDATE		0x02 /* Incoming ACK was a window update.	*/
+#define FLAG_DATA_ACKED		0x04 /* This ACK acknowledged new data.		*/
+#define FLAG_RETRANS_DATA_ACKED	0x08 /* "" "" some of which was retransmitted.	*/
 
 static __inline__ void clear_fast_retransmit(struct sock *sk)
 {
@@ -372,11 +404,9 @@
 	tp->dup_acks = 0;
 }
 
-/*
- * NOTE: This code assumes that tp->dup_acks gets cleared when a
+/* NOTE: This code assumes that tp->dup_acks gets cleared when a
  * retransmit timer fires.
  */
-
 static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup)
 {
 	struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
@@ -407,7 +437,7 @@
                                 tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2);
                                 tp->snd_cwnd = tp->snd_ssthresh + 3;
 				tp->high_seq = tp->snd_nxt;
-                                tcp_do_retransmit(sk, 0);
+                                tcp_retransmit_skb(sk, skb_peek(&sk->write_queue));
                                 tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 			}
 		}
@@ -425,7 +455,7 @@
 		 * block on duplicate fast retransmits, and if requested
 		 * we do Hoe style secondary fast retransmits.
 		 */
-		if (!before(ack,tp->high_seq) || (not_dup&FLAG_DATA) != 0) {
+		if (!before(ack, tp->high_seq) || (not_dup & FLAG_DATA) != 0) {
 			/* Once we have acked all the packets up to high_seq
 			 * we are done this fast retransmit phase.
 			 * Alternatively data arrived. In this case we
@@ -438,7 +468,7 @@
 			/* After we have cleared up to high_seq we can
 			 * clear the Floyd style block.
 			 */
-			if (after(ack,tp->high_seq))
+			if (after(ack, tp->high_seq))
 				tp->high_seq = 0;
 		} else if (tp->dup_acks >= 3) {
 			if (sysctl_tcp_hoe_retransmits) {
@@ -455,10 +485,9 @@
 				 * the only way to get here without advancing
 				 * from snd_una is if this was a window update.
 				 */
-				if (ack != tp->snd_una && before(ack,tp->high_seq)) {
-                                	tcp_do_retransmit(sk, 0);
-                                	tcp_reset_xmit_timer(sk, TIME_RETRANS,
-						tp->rto);
+				if (ack != tp->snd_una && before(ack, tp->high_seq)) {
+                                	tcp_retransmit_skb(sk, skb_peek(&sk->write_queue));
+                                	tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 				}
 			} else {
 				/* Reno style. We didn't ack the whole
@@ -589,9 +618,9 @@
         }       
 }
 
-
-static int tcp_clean_rtx_queue(struct sock *sk, __u32 ack, __u32 *seq,
-			       __u32 *seq_rtt)
+/* Remove acknowledged frames from the retransmission queue. */
+static int tcp_clean_rtx_queue(struct sock *sk, __u32 ack,
+			       __u32 *seq, __u32 *seq_rtt)
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	struct sk_buff *skb;
@@ -600,8 +629,8 @@
 
 	while((skb=skb_peek(&sk->write_queue)) && (skb != tp->send_head)) {
 		/* If our packet is before the ack sequence we can
-		 * discard it as it's confirmed to have arrived the 
-		 * other end.
+		 * discard it as it's confirmed to have arrived at
+		 * the other end.
 		 */
 		if (after(skb->end_seq, ack))
 			break;
@@ -613,26 +642,22 @@
 		 * connection startup slow start one packet too
 		 * quickly.  This is severely frowned upon behavior.
 		 */
-		if(!skb->h.th->syn)
-			acked = FLAG_DATA_ACKED;
-		
-		/* FIXME: packet counting may break if we have to
-		 * do packet "repackaging" for stacks that don't
-		 * like overlapping packets.
-		 */
+		if(!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN)) {
+			acked |= FLAG_DATA_ACKED;
+			if(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS)
+				acked |= FLAG_RETRANS_DATA_ACKED;
+		} else {
+			tp->retrans_head = NULL;
+		}		
 		tp->packets_out--;
-
 		*seq = skb->seq;
 		*seq_rtt = now - skb->when;
-
 		skb_unlink(skb);
-		
 		kfree_skb(skb);
 	}
 
 	if (acked)
 		tp->retrans_head = NULL;
-
 	return acked;
 }
 
@@ -686,41 +711,23 @@
 
 static void tcp_ack_packets_out(struct sock *sk, struct tcp_opt *tp)
 {
-	struct sk_buff *skb;
-	long when;
+	struct sk_buff *skb = skb_peek(&sk->write_queue);
+	long when = tp->rto - (jiffies - skb->when);
 
-	skb = skb_peek(&sk->write_queue);
-	when = tp->rto - (jiffies - skb->when);
-
-	/* FIXME: This assumes that when we are retransmitting
-	 * we should only ever respond with one packet.
-	 * This means congestion windows should not grow
-	 * during recovery. In 2.0.X we allow the congestion
-	 * window to grow. It is not clear to me which
-	 * decision is correct. The RFCs should be double
-	 * checked as should the behavior of other stacks.
-	 * Also note that if we do want to allow the
-	 * congestion window to grow during retransmits
-	 * we have to fix the call to congestion window
-	 * updates so that it works during retransmission.
+	/* Some data was ACK'd, if still retransmitting (due to a
+	 * timeout), resend more of the retransmit queue.  The
+	 * congestion window is handled properly by that code.
 	 */
 	if (tp->retransmits) {
 		tp->retrans_head = NULL;
-
-		/* This is tricky. We are retransmiting a 
-		 * segment of a window when congestion occured.
-		 */
-		tcp_do_retransmit(sk, 0);
+		tcp_xmit_retransmit_queue(sk);
 		tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 	} else {
 		tcp_reset_xmit_timer(sk, TIME_RETRANS, when);
 	}
 }
 
-/*
- *	This routine deals with incoming acks, but not outgoing ones.
- */
-
+/* This routine deals with incoming acks, but not outgoing ones. */
 static int tcp_ack(struct sock *sk, struct tcphdr *th, 
 		   u32 ack_seq, u32 ack, int len)
 {
@@ -805,7 +812,8 @@
 			 * where the network delay has increased suddenly.
 			 * I.e. Karn's algorithm. (SIGCOMM '87, p5.)
 			 */
-			if (flag & FLAG_DATA_ACKED) {
+			if ((flag & FLAG_DATA_ACKED) &&
+			    !(flag & FLAG_RETRANS_DATA_ACKED)) {
 				tp->backoff = 0;
 				tcp_rtt_estimator(tp, seq_rtt);
 				tcp_set_rto(tp);
@@ -923,9 +931,7 @@
 	} else {
 		if(th->ack) {
 			/* In this case we must reset the TIMEWAIT timer. */
-			del_timer(&tw->timer);
-			tw->timer.expires = jiffies + TCP_TIMEWAIT_LEN;
-			add_timer(&tw->timer);
+			mod_timer(&tw->timer, jiffies + TCP_TIMEWAIT_LEN);
 		}
 	}
 	return 0; /* Discard the frame. */
@@ -981,9 +987,10 @@
 		tw->bound_dev_if= sk->bound_dev_if;
 		tw->num		= sk->num;
 		tw->state	= TCP_TIME_WAIT;
+		tw->sport	= sk->sport;
+		tw->dport	= sk->dport;
 		tw->family	= sk->family;
-		tw->source	= sk->dummy_th.source;
-		tw->dest	= sk->dummy_th.dest;
+		tw->reuse	= sk->reuse;
 		tw->rcv_nxt	= sk->tp_pinfo.af_tcp.rcv_nxt;
 		tw->af_specific	= sk->tp_pinfo.af_tcp.af_specific;
 
@@ -1098,6 +1105,175 @@
 	};
 }
 
+/* These routines update the SACK block as out-of-order packets arrive or
+ * in-order packets close up the sequence space.
+ */
+static void tcp_sack_maybe_coalesce(struct tcp_opt *tp, struct tcp_sack_block *sp)
+{
+	int this_sack, num_sacks = tp->num_sacks;
+	struct tcp_sack_block *swalk = &tp->selective_acks[0];
+
+	/* If more than one SACK block, see if the recent change to SP eats into
+	 * or hits the sequence space of other SACK blocks, if so coalesce.
+	 */
+	if(num_sacks != 1) {
+		for(this_sack = 0; this_sack < num_sacks; this_sack++, swalk++) {
+			if(swalk == sp)
+				continue;
+
+			/* First case, bottom of SP moves into top of the
+			 * sequence space of SWALK.
+			 */
+			if(between(sp->start_seq, swalk->start_seq, swalk->end_seq)) {
+				sp->start_seq = swalk->start_seq;
+				goto coalesce;
+			}
+			/* Second case, top of SP moves into bottom of the
+			 * sequence space of SWALK.
+			 */
+			if(between(sp->end_seq, swalk->start_seq, swalk->end_seq)) {
+				sp->end_seq = swalk->end_seq;
+				goto coalesce;
+			}
+		}
+	}
+	/* SP is the only SACK, or no coalescing cases found. */
+	return;
+
+coalesce:
+	/* Zap SWALK, by moving every further SACK up by one slot.
+	 * Decrease num_sacks.
+	 */
+	for(this_sack += 1; this_sack < num_sacks; this_sack++, swalk++) {
+		struct tcp_sack_block *next = (swalk + 1);
+		swalk->start_seq = next->start_seq;
+		swalk->end_seq = next->end_seq;
+	}
+	tp->num_sacks--;
+}
+
+static __inline__ void tcp_sack_swap(struct tcp_sack_block *sack1, struct tcp_sack_block *sack2)
+{
+	__u32 tmp;
+
+	tmp = sack1->start_seq;
+	sack1->start_seq = sack2->start_seq;
+	sack2->start_seq = tmp;
+
+	tmp = sack1->end_seq;
+	sack1->end_seq = sack2->end_seq;
+	sack2->end_seq = tmp;
+}
+
+static void tcp_sack_new_ofo_skb(struct sock *sk, struct sk_buff *skb)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	struct tcp_sack_block *sp = &tp->selective_acks[0];
+
+	/* Optimize for the common case, new ofo frames arrive
+	 * "in order". ;-)  This also satisfies the requirements
+	 * of RFC2018 about ordering of SACKs.
+	 */
+	if(sp->end_seq == skb->seq) {
+		sp->end_seq = skb->end_seq;
+		tcp_sack_maybe_coalesce(tp, sp);
+	} else if(sp->start_seq == skb->end_seq) {
+		/* Re-ordered arrival, in this case, can be optimized
+		 * as well.
+		 */
+		sp->start_seq = skb->seq;
+		tcp_sack_maybe_coalesce(tp, sp);
+	} else {
+		int cur_sacks = tp->num_sacks;
+		int max_sacks = (tp->tstamp_ok ? 3 : 4);
+
+		/* Oh well, we have to move things around.
+		 * Try to find a SACK we can tack this onto.
+		 */
+		if(cur_sacks > 1) {
+			struct tcp_sack_block *swap = sp + 1;
+			int this_sack;
+
+			for(this_sack = 1; this_sack < cur_sacks; this_sack++, swap++) {
+				if((swap->end_seq == skb->seq) ||
+				   (swap->start_seq == skb->end_seq)) {
+					if(swap->end_seq == skb->seq)
+						swap->end_seq = skb->end_seq;
+					else
+						swap->start_seq = skb->seq;
+					tcp_sack_swap(sp, swap);
+					tcp_sack_maybe_coalesce(tp, sp);
+					return;
+				}
+			}
+		}
+
+		/* Could not find an adjacent existing SACK, build a new one,
+		 * put it at the front, and shift everyone else down.  We
+		 * always know there is at least one SACK present already here.
+		 */
+		while(cur_sacks >= 1) {
+			struct tcp_sack_block *this = &tp->selective_acks[cur_sacks];
+			struct tcp_sack_block *prev = (this - 1);
+			this->start_seq = prev->start_seq;
+			this->end_seq = prev->end_seq;
+			cur_sacks--;
+		}
+
+		/* Build head SACK, and we're done. */
+		sp->start_seq = skb->seq;
+		sp->end_seq = skb->end_seq;
+		if(tp->num_sacks < max_sacks)
+			tp->num_sacks++;
+	}
+}
+
+static void tcp_sack_remove_skb(struct tcp_opt *tp, struct sk_buff *skb)
+{
+	struct tcp_sack_block *sp = &tp->selective_acks[0];
+	int num_sacks = tp->num_sacks;
+	int this_sack;
+
+	/* We know this removed SKB will eat from the front of a SACK. */
+	for(this_sack = 0; this_sack < num_sacks; this_sack++, sp++) {
+		if(sp->start_seq == skb->seq)
+			break;
+	}
+
+	/* This should only happen if so many SACKs get built that some get
+	 * pushed out before we get here, or we eat some in sequence packets
+	 * which are before the first SACK block.
+	 */
+	if(this_sack >= num_sacks)
+		return;
+
+	sp->start_seq = skb->end_seq;
+	if(!before(sp->start_seq, sp->end_seq)) {
+		/* Zap this SACK, by moving forward any other SACKS. */
+		for(this_sack += 1; this_sack < num_sacks; this_sack++, sp++) {
+			struct tcp_sack_block *next = (sp + 1);
+			sp->start_seq = next->start_seq;
+			sp->end_seq = next->end_seq;
+		}
+		tp->num_sacks--;
+	}
+}
+
+static void tcp_sack_extend(struct tcp_opt *tp, struct sk_buff *old_skb, struct sk_buff *new_skb)
+{
+	struct tcp_sack_block *sp = &tp->selective_acks[0];
+	int num_sacks = tp->num_sacks;
+	int this_sack;
+
+	for(this_sack = 0; this_sack < num_sacks; this_sack++, tp++) {
+		if(sp->end_seq == old_skb->end_seq)
+			break;
+	}
+	if(this_sack >= num_sacks)
+		return;
+	sp->end_seq = new_skb->end_seq;
+}
+
 /* This one checks to see if we can put data from the
  * out_of_order queue into the receive_queue.
  */
@@ -1119,6 +1295,8 @@
 		SOCK_DEBUG(sk, "ofo requeuing : rcv_next %X seq %X - %X\n",
 			   tp->rcv_nxt, skb->seq, skb->end_seq);
 
+		if(tp->sack_ok)
+			tcp_sack_remove_skb(tp, skb);
 		skb_unlink(skb);
 		skb_queue_tail(&sk->receive_queue, skb);
 		tp->rcv_nxt = skb->end_seq;
@@ -1142,13 +1320,23 @@
 		dst_confirm(sk->dst_cache);
 		skb_queue_tail(&sk->receive_queue, skb);
 		tp->rcv_nxt = skb->end_seq;
-		if(skb->h.th->fin)
+		if(skb->h.th->fin) {
 			tcp_fin(skb, sk, skb->h.th);
-		else
+		} else {
 			tp->delayed_acks++;
+
+			/* Tiny-grams with PSH set make us ACK quickly. */
+			if(skb->h.th->psh && (skb->len < (sk->mss >> 1)))
+				tp->ato = HZ/50;
+		}
+		/* This may have eaten into a SACK block. */
+		if(tp->sack_ok && tp->num_sacks)
+			tcp_sack_remove_skb(tp, skb);
 		tcp_ofo_queue(sk);
 		if (skb_queue_len(&tp->out_of_order_queue) == 0)
-			tp->pred_flags = htonl((0x5010 << 16) | tp->snd_wnd);
+			tp->pred_flags = htonl(((tp->tcp_header_len >> 2) << 28) |
+					       (0x10 << 16) |
+					       tp->snd_wnd);
 		return;
 	}
 	
@@ -1180,25 +1368,44 @@
 		   tp->rcv_nxt, skb->seq, skb->end_seq);
 
 	if (skb_peek(&tp->out_of_order_queue) == NULL) {
+		/* Initial out of order segment, build 1 SACK. */
+		if(tp->sack_ok) {
+			tp->num_sacks = 1;
+			tp->selective_acks[0].start_seq = skb->seq;
+			tp->selective_acks[0].end_seq = skb->end_seq;
+		}
 		skb_queue_head(&tp->out_of_order_queue,skb);
 	} else {
 		for(skb1=tp->out_of_order_queue.prev; ; skb1 = skb1->prev) {
 			/* Already there. */
-			if (skb->seq == skb1->seq && skb->len >= skb1->len) {
- 				skb_append(skb1, skb);
- 				skb_unlink(skb1);
- 				kfree_skb(skb1);
+			if (skb->seq == skb1->seq) {
+				if (skb->len >= skb1->len) {
+					if(tp->sack_ok)
+						tcp_sack_extend(tp, skb1, skb);
+					skb_append(skb1, skb);
+					skb_unlink(skb1);
+					kfree_skb(skb1);
+				} else {
+					/* A duplicate, smaller than what is in the
+					 * out-of-order queue right now, toss it.
+					 */
+					kfree_skb(skb);
+				}
 				break;
 			}
 			
 			if (after(skb->seq, skb1->seq)) {
 				skb_append(skb1,skb);
+				if(tp->sack_ok)
+					tcp_sack_new_ofo_skb(sk, skb);
 				break;
 			}
 
                         /* See if we've hit the start. If so insert. */
 			if (skb1 == skb_peek(&tp->out_of_order_queue)) {
 				skb_queue_head(&tp->out_of_order_queue,skb);
+				if(tp->sack_ok)
+					tcp_sack_new_ofo_skb(sk, skb);
 				break;
 			}
 		}
@@ -1244,8 +1451,8 @@
 
 static void tcp_data_snd_check(struct sock *sk)
 {
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	struct sk_buff *skb;
-	struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
 
 	if ((skb = tp->send_head)) {
 		if (!after(skb->end_seq, tp->snd_una + tp->snd_wnd) &&
@@ -1273,6 +1480,7 @@
 	 *      - delay time <= 0.5 HZ
 	 *      - we don't have a window update to send
 	 *      - must send at least every 2 full sized packets
+	 *	- must send an ACK if we have any SACKs
 	 *
 	 * With an extra heuristic to handle loss of packet
 	 * situations and also helping the sender leave slow
@@ -1283,8 +1491,10 @@
 	if (((tp->rcv_nxt - tp->rcv_wup) >= (sk->mss << 1)) ||
 	    /* We will update the window "significantly" or... */
 	    tcp_raise_window(sk) ||
-	    /* We entered "quick ACK" mode */
-	    tcp_in_quickack_mode(tp)) {
+	    /* We entered "quick ACK" mode or... */
+	    tcp_in_quickack_mode(tp) ||
+	    /* We have pending SACKs */
+	    (tp->sack_ok && tp->num_sacks)) {
 		/* Then ack it now */
 		tcp_send_ack(sk);
 	} else {
@@ -1446,7 +1656,7 @@
 	/*
 	 * RFC1323: H1. Apply PAWS check first.
 	 */
-	if (tcp_fast_parse_options(th,tp)) {
+	if (tcp_fast_parse_options(sk, th, tp)) {
 		if (tp->saw_tstamp) {
 			if (tcp_paws_discard(tp)) {
 				if (!th->rst) {
@@ -1460,10 +1670,10 @@
 
 	flg = *(((u32 *)th) + 3);
 		
-	/*
-	 *	pred_flags is 0x5?10 << 16 + snd_wnd
+	/*	pred_flags is 0xS?10 << 16 + snd_wnd
 	 *	if header_predition is to be made
-	 *	? will be 0 else it will be !0
+	 *	'S' will always be tp->tcp_header_len >> 2
+	 *	'?' will be 0 else it will be !0
 	 *	(when there are holes in the receive 
 	 *	 space for instance)
 	 */
@@ -1498,6 +1708,11 @@
 			 */
 			sk->data_ready(sk, 0);
 			tcp_delack_estimator(tp);
+
+			/* Tiny-grams with PSH set make us ACK quickly. */
+			if(th->psh && (skb->len < (sk->mss >> 1)))
+				tp->ato = HZ/50;
+
 			tp->delayed_acks++;
 			__tcp_ack_snd_check(sk);
 			return 0;
@@ -1703,7 +1918,7 @@
 			tp->fin_seq = skb->seq;
 
 			tcp_set_state(sk, TCP_ESTABLISHED);
-			tcp_parse_options(th,tp,0);
+			tcp_parse_options(sk, th, tp, 0);
 
         		if (tp->wscale_ok == 0) {
                 		tp->snd_wscale = tp->rcv_wscale = 0;
@@ -1712,7 +1927,6 @@
 			if (tp->tstamp_ok) {
 				tp->tcp_header_len =
 					sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
-				sk->dummy_th.doff += (TCPOLEN_TSTAMP_ALIGNED >> 2);
 			} else
 				tp->tcp_header_len = sizeof(struct tcphdr);
 			if (tp->saw_tstamp) {
@@ -1745,7 +1959,7 @@
 				sk->mss = min(sk->mss, real_mss);
 			}
 
-			sk->dummy_th.dest = th->source;
+			sk->dport = th->source;
 			tp->copied_seq = tp->rcv_nxt;
 
 			if(!sk->dead) {
@@ -1763,7 +1977,7 @@
 				 * tcp_connect.
 				 */
 				tcp_set_state(sk, TCP_SYN_RECV);
-				tcp_parse_options(th,tp,0);
+				tcp_parse_options(sk, th, tp, 0);
 				if (tp->saw_tstamp) {
 					tp->ts_recent = tp->rcv_tsval;
 					tp->ts_recent_stamp = jiffies;
@@ -1788,7 +2002,7 @@
 	 *   Note that this really has to be here and not later for PAWS
 	 *   (RFC1323) to work.
 	 */
-	if (tcp_fast_parse_options(th,tp)) {
+	if (tcp_fast_parse_options(sk, th, tp)) {
 		/* NOTE: assumes saw_tstamp is never set if we didn't
 		 * negotiate the option. tcp_fast_parse_options() must
 		 * guarantee this.
@@ -1849,7 +2063,7 @@
 		case TCP_SYN_RECV:
 			if (acceptable) {
 				tcp_set_state(sk, TCP_ESTABLISHED);
-				sk->dummy_th.dest=th->source;
+				sk->dport = th->source;
 				tp->copied_seq = tp->rcv_nxt;
 
 				if(!sk->dead)
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.90/linux/net/ipv4/tcp_ipv4.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/tcp_ipv4.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.109 1998/03/15 07:24:15 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.119 1998/03/22 19:14:47 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -62,16 +62,12 @@
 
 extern int sysctl_tcp_timestamps;
 extern int sysctl_tcp_window_scaling;
+extern int sysctl_tcp_sack;
 extern int sysctl_tcp_syncookies;
 extern int sysctl_ip_dynaddr;
 
 /* Check TCP sequence numbers in ICMP packets. */
-#define ICMP_PARANOIA 1 
-#ifndef ICMP_PARANOIA
-#define ICMP_MIN_LENGTH 4
-#else
 #define ICMP_MIN_LENGTH 8
-#endif
 
 static void tcp_v4_send_reset(struct sk_buff *skb);
 
@@ -120,7 +116,7 @@
 	__u32 laddr = sk->rcv_saddr;
 	__u16 lport = sk->num;
 	__u32 faddr = sk->daddr;
-	__u16 fport = sk->dummy_th.dest;
+	__u16 fport = sk->dport;
 
 	return tcp_hashfn(laddr, lport, faddr, fport);
 }
@@ -365,7 +361,7 @@
 	sk = TCP_RHASH(sport);
 	if(sk						&&
 	   sk->daddr		== saddr		&& /* remote address */
-	   sk->dummy_th.dest	== sport		&& /* remote port    */
+	   sk->dport		== sport		&& /* remote port    */
 	   sk->num		== hnum			&& /* local port     */
 	   sk->rcv_saddr	== daddr		&& /* local address  */
 	   (!sk->bound_dev_if || sk->bound_dev_if == dif))
@@ -377,7 +373,7 @@
 	hash = tcp_hashfn(daddr, hnum, saddr, sport);
 	for(sk = tcp_established_hash[hash]; sk; sk = sk->next) {
 		if(sk->daddr		== saddr		&& /* remote address */
-		   sk->dummy_th.dest	== sport		&& /* remote port    */
+		   sk->dport		== sport		&& /* remote port    */
 		   sk->num		== hnum			&& /* local port     */
 		   sk->rcv_saddr	== daddr		&& /* local address  */
 		   (!sk->bound_dev_if || sk->bound_dev_if == dif)) {
@@ -389,7 +385,7 @@
 	/* Must check for a TIME_WAIT'er before going to listener hash. */
 	for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) {
 		if(sk->daddr		== saddr		&& /* remote address */
-		   sk->dummy_th.dest	== sport		&& /* remote port    */
+		   sk->dport		== sport		&& /* remote port    */
 		   sk->num		== hnum			&& /* local port     */
 		   sk->rcv_saddr	== daddr		&& /* local address  */
 		   (!sk->bound_dev_if || sk->bound_dev_if == dif))
@@ -456,8 +452,8 @@
 				continue;
 			score++;
 		}
-		if(s->dummy_th.dest) {
-			if(s->dummy_th.dest != rnum)
+		if(s->dport) {
+			if(s->dport != rnum)
 				continue;
 			score++;
 		}
@@ -496,12 +492,7 @@
 					  skb->h.th->source);
 }
 
-/*
- *	From tcp.c
- */
-
-/*
- * Check that a TCP address is unique, don't allow multiple
+/* Check that a TCP address is unique, don't allow multiple
  * connects to/from the same address.  Actually we can optimize
  * quite a bit, since the socket about to connect is still
  * in TCP_CLOSE, a tcp_bind_bucket for the local port he will
@@ -509,8 +500,7 @@
  * The good_socknum and verify_bind scheme we use makes this
  * work.
  */
-
-static int tcp_unique_address(struct sock *sk)
+static int tcp_v4_unique_address(struct sock *sk)
 {
 	struct tcp_bind_bucket *tb;
 	unsigned short snum = sk->num;
@@ -524,7 +514,7 @@
 			/* Almost certainly the re-use port case, search the real hashes
 			 * so it actually scales.
 			 */
-			sk = __tcp_v4_lookup(NULL, sk->daddr, sk->dummy_th.dest,
+			sk = __tcp_v4_lookup(NULL, sk->daddr, sk->dport,
 					     sk->rcv_saddr, snum, sk->bound_dev_if);
 			if((sk != NULL) && (sk->state != TCP_LISTEN))
 				retval = 0;
@@ -535,19 +525,15 @@
 	return retval;
 }
 
-
-/*
- *	This will initiate an outgoing connection. 
- */
- 
+/* This will initiate an outgoing connection. */
 int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
-	struct sk_buff *buff;
-	int tmp;
-	struct tcphdr *th;
-	struct rtable *rt;
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
+	struct sk_buff *buff;
+	struct rtable *rt;
+	int tmp;
+	int mss;
 
 	if (sk->state != TCP_CLOSE) 
 		return(-EISCONN);
@@ -567,8 +553,6 @@
 			printk(KERN_DEBUG "%s forgot to set AF_INET in " __FUNCTION__ "\n", current->comm);
 	}
 
-	dst_release(xchg(&sk->dst_cache, NULL));
-
 	tmp = ip_route_connect(&rt, usin->sin_addr.s_addr, sk->saddr,
 			       RT_TOS(sk->ip_tos)|sk->localroute, sk->bound_dev_if);
 	if (tmp < 0)
@@ -579,143 +563,52 @@
 		return -ENETUNREACH;
 	}
 
-	if (!tcp_unique_address(sk)) {
-		ip_rt_put(rt);
-		return -EADDRNOTAVAIL;
-	}
+	dst_release(xchg(&sk->dst_cache, rt));
 
-	lock_sock(sk);
-
-	/* Do this early, so there is less state to unwind on failure. */
-	buff = sock_wmalloc(sk, (MAX_SYN_SIZE + sizeof(struct sk_buff)),
+	buff = sock_wmalloc(sk, (MAX_HEADER + sk->prot->max_header),
 			    0, GFP_KERNEL);
-	if (buff == NULL) {
-		release_sock(sk);
-		ip_rt_put(rt);
-		return(-ENOBUFS);
-	}
 
-	sk->dst_cache = &rt->u.dst;
+	if (buff == NULL)
+		return -ENOBUFS;
+
+	/* Socket has no identity, so lock_sock() is useless.  Also
+	 * since state==TCP_CLOSE (checked above) the socket cannot
+	 * possibly be in the hashes.  TCP hash locking is only
+	 * needed while checking quickly for a unique address.
+	 * However, the socket does need to be (and is) locked
+	 * in tcp_connect().
+	 * Perhaps this addresses all of ANK's concerns. 8-)  -DaveM
+	 */
+	sk->dport = usin->sin_port;
 	sk->daddr = rt->rt_dst;
 	if (!sk->saddr)
 		sk->saddr = rt->rt_src;
 	sk->rcv_saddr = sk->saddr;
 
-	if (sk->priority == 0)
-		sk->priority = rt->u.dst.priority;
-
-	sk->dummy_th.dest = usin->sin_port;
-
-	tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
-						   sk->dummy_th.source,
-						   usin->sin_port);
-	tp->snd_wnd = 0;
-	tp->snd_wl1 = 0;
-	tp->snd_wl2 = tp->write_seq;
-	tp->snd_una = tp->write_seq;
-	tp->rcv_nxt = 0;
-
-	sk->err = 0;
-	
-	/* Put in the IP header and routing stuff. */
-	tmp = ip_build_header(buff, sk);
-	if (tmp < 0) {
-		/* Caller has done ip_rt_put(rt) and set sk->dst_cache
-		 * to NULL.  We must unwind the half built TCP socket
-		 * state so that this failure does not create a "stillborn"
-		 * sock (ie. future re-tries of connect() would fail).
-		 */
-		sk->daddr = 0;
-		sk->saddr = sk->rcv_saddr = 0;
+	if (!tcp_v4_unique_address(sk)) {
 		kfree_skb(buff);
-		release_sock(sk);
-		return(-ENETUNREACH);
+		return -EADDRNOTAVAIL;
 	}
 
-	/* No failure conditions can result past this point. */
-
-	/* We'll fix this up when we get a response from the other end.
-	 * See tcp_input.c:tcp_rcv_state_process case TCP_SYN_SENT.
-	 */
-	tp->tcp_header_len = sizeof(struct tcphdr) +
-		(sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);
-
-	th = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr));
-	buff->h.th = th;
-
-	memcpy(th,(void *)&(sk->dummy_th), sizeof(*th));
-	/* th->doff gets fixed up below if we tack on options. */
-
-	buff->seq = tp->write_seq++;
-	th->seq = htonl(buff->seq);
-	tp->snd_nxt = tp->write_seq;
-	buff->end_seq = tp->write_seq;
-	th->ack = 0;
-	th->syn = 1;
-
 	sk->mtu = rt->u.dst.pmtu;
 	if ((sk->ip_pmtudisc == IP_PMTUDISC_DONT ||
 	     (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
 	      (rt->u.dst.mxlock&(1<<RTAX_MTU)))) &&
-	    rt->u.dst.pmtu > 576)
+	    rt->u.dst.pmtu > 576 && rt->rt_dst != rt->rt_gateway)
 		sk->mtu = 576;
 
-	if(sk->mtu < 64)
+	if (sk->mtu < 64)
 		sk->mtu = 64;	/* Sanity limit */
 
-	sk->mss = (sk->mtu - sizeof(struct iphdr) - tp->tcp_header_len);
-	if(sk->user_mss)
-		sk->mss = min(sk->mss, sk->user_mss);
-
-	if (sk->mss < 1) {
-		printk(KERN_DEBUG "intial sk->mss below 1\n");
-		sk->mss = 1;	/* Sanity limit */
-	}
-
-	tp->window_clamp = rt->u.dst.window;
-	tcp_select_initial_window(sock_rspace(sk)/2,sk->mss,
-		&tp->rcv_wnd,
-		&tp->window_clamp,
-		sysctl_tcp_window_scaling,
-		&tp->rcv_wscale);
-	th->window = htons(tp->rcv_wnd);
-
-	tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_timestamps,
-		sysctl_tcp_window_scaling, tp->rcv_wscale);
-	buff->csum = 0;
-	th->doff = (sizeof(*th)+ tmp)>>2;
-
-	tcp_v4_send_check(sk, th, sizeof(struct tcphdr) + tmp, buff);
-
-	tcp_set_state(sk,TCP_SYN_SENT);
-
-	/* Socket identity change complete, no longer
-	 * in TCP_CLOSE, so enter ourselves into the
-	 * hash tables.
-	 */
-	tcp_v4_hash(sk);
+	mss = sk->mtu - sizeof(struct iphdr);
+	if (sk->opt)
+		mss -= sk->opt->optlen;
 
-	tp->rto = rt->u.dst.rtt;
-
-	tcp_init_xmit_timers(sk);
-
-	/* Now works the right way instead of a hacked initial setting. */
-	tp->retransmits = 0;
-
-	skb_queue_tail(&sk->write_queue, buff);
-
-	tp->packets_out++;
-	buff->when = jiffies;
-
-	ip_queue_xmit(skb_clone(buff, GFP_KERNEL));
+	tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
+						   sk->sport, usin->sin_port);
 
-	/* Timer for repeating the SYN until an answer. */
-	tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
-	tcp_statistics.TcpActiveOpens++;
-	tcp_statistics.TcpOutSegs++;
-  
-	release_sock(sk);
-	return(0);
+	tcp_connect(sk, buff, mss);
+	return 0;
 }
 
 static int tcp_v4_sendmsg(struct sock *sk, struct msghdr *msg, int len)
@@ -724,7 +617,7 @@
 	int retval = -EINVAL;
 
 	/* Do sanity checking for sendmsg/sendto/send. */
-	if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT))
+	if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT|MSG_NOSIGNAL))
 		goto out;
 	if (msg->msg_name) {
 		struct sockaddr_in *addr=(struct sockaddr_in *)msg->msg_name;
@@ -737,7 +630,7 @@
 		if(sk->state == TCP_CLOSE)
 			goto out;
 		retval = -EISCONN;
-		if (addr->sin_port != sk->dummy_th.dest)
+		if (addr->sin_port != sk->dport)
 			goto out;
 		if (addr->sin_addr.s_addr != sk->daddr)
 			goto out;
@@ -851,9 +744,7 @@
 	int code = skb->h.icmph->code;
 	struct sock *sk;
 	int opening;
-#ifdef ICMP_PARANOIA
 	__u32 seq;
-#endif
 
 	if (len < (iph->ihl << 2) + ICMP_MIN_LENGTH) { 
 		icmp_statistics.IcmpInErrors++; 
@@ -869,7 +760,6 @@
 	}
 
 	tp = &sk->tp_pinfo.af_tcp;
-#ifdef ICMP_PARANOIA
 	seq = ntohl(th->seq);
 	if (sk->state != TCP_LISTEN && 
    	    !between(seq, tp->snd_una, max(tp->snd_una+32768,tp->snd_nxt))) {
@@ -879,7 +769,6 @@
 			       (int)sk->state, seq, tp->snd_una, tp->snd_nxt); 
 		return; 
 	}
-#endif
 
 	switch (type) {
 	case ICMP_SOURCE_QUENCH:
@@ -927,7 +816,6 @@
 		req = tcp_v4_search_req(tp, iph, th, &prev); 
 		if (!req)
 			return;
-#ifdef ICMP_PARANOIA
 		if (seq != req->snt_isn) {
 			if (net_ratelimit())
 				printk(KERN_DEBUG "icmp packet for openreq "
@@ -935,7 +823,6 @@
 				       seq, req->snt_isn);
 			return;
 		}
-#endif
 		if (req->sk) {	/* not yet accept()ed */
 			sk = req->sk; /* report error in accept */
 		} else {
@@ -987,44 +874,50 @@
 
 static void tcp_v4_send_reset(struct sk_buff *skb)
 {
-	struct tcphdr  *th = skb->h.th;
-	struct sk_buff *skb1;
-	struct tcphdr  *th1;
+	struct tcphdr *th = skb->h.th;
 
-	if (th->rst)
-		return;
+	/* Never send a reset in response to a reset. */
+	if (th->rst == 0) {
+		struct tcphdr  *th = skb->h.th;
+		struct sk_buff *skb1 = ip_reply(skb, sizeof(struct tcphdr));
+		struct tcphdr  *th1;
 
-	skb1 = ip_reply(skb, sizeof(struct tcphdr));
-	if (skb1 == NULL)
-		return;
+		if (skb1 == NULL)
+			return;
  
-	skb1->h.th = th1 = (struct tcphdr *)skb_put(skb1, sizeof(struct tcphdr));
-	memset(th1, 0, sizeof(*th1));
+		skb1->h.th = th1 = (struct tcphdr *)
+			skb_put(skb1, sizeof(struct tcphdr));
 
-	/* Swap the send and the receive. */
-	th1->dest = th->source;
-	th1->source = th->dest;
-	th1->doff = sizeof(*th1)/4;
-	th1->rst = 1;
-
-	if (th->ack)
-	  	th1->seq = th->ack_seq;
-	else {
-		th1->ack = 1;
-	  	if (!th->syn)
-			th1->ack_seq = th->seq;
-		else
-			th1->ack_seq = htonl(ntohl(th->seq)+1);
-	}
+		/* Swap the send and the receive. */
+		memset(th1, 0, sizeof(*th1));
+		th1->dest = th->source;
+		th1->source = th->dest;
+		th1->doff = sizeof(*th1)/4;
+		th1->rst = 1;
+
+		if (th->ack) {
+			th1->seq = th->ack_seq;
+		} else {
+			th1->ack = 1;
+			if (!th->syn)
+				th1->ack_seq = th->seq;
+			else
+				th1->ack_seq = htonl(ntohl(th->seq)+1);
+		}
+		skb1->csum = csum_partial((u8 *) th1, sizeof(*th1), 0);
+		th1->check = tcp_v4_check(th1, sizeof(*th1), skb1->nh.iph->saddr,
+					  skb1->nh.iph->daddr, skb1->csum);
+
+		/* Finish up some IP bits. */
+		skb1->nh.iph->tot_len = htons(skb1->len);
+		ip_send_check(skb1->nh.iph);
 
-	skb1->csum = csum_partial((u8 *) th1, sizeof(*th1), 0);
-	th1->check = tcp_v4_check(th1, sizeof(*th1), skb1->nh.iph->saddr,
-				  skb1->nh.iph->daddr, skb1->csum);
+		/* All the other work was done by ip_reply(). */
+		skb1->dst->output(skb1);
 
-	/* Do not place TCP options in a reset. */
-	ip_queue_xmit(skb1);
-	tcp_statistics.TcpOutSegs++;
-	tcp_statistics.TcpOutRsts++;
+		tcp_statistics.TcpOutSegs++;
+		tcp_statistics.TcpOutRsts++;
+	}
 }
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
@@ -1055,82 +948,48 @@
 
 static void tcp_v4_send_synack(struct sock *sk, struct open_request *req)
 {
+	struct rtable *rt;
+	struct ip_options *opt;
 	struct sk_buff * skb;
-	struct tcphdr *th;
-	int tmp;
 	int mss;
 
-	skb = sock_wmalloc(sk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
-	if (skb == NULL)
+	/* First, grab a route. */
+	opt = req->af.v4_req.opt;
+	if(ip_route_output(&rt, ((opt && opt->srr) ?
+				 opt->faddr :
+				 req->af.v4_req.rmt_addr),
+			   req->af.v4_req.loc_addr,
+			   RT_TOS(sk->ip_tos) | RTO_CONN | sk->localroute,
+			   sk->bound_dev_if)) {
+		ip_statistics.IpOutNoRoutes++;
 		return;
-
-	if(ip_build_pkt(skb, sk, req->af.v4_req.loc_addr,
-			req->af.v4_req.rmt_addr, req->af.v4_req.opt) < 0) {
-		kfree_skb(skb);
+	}
+	if(opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) {
+		ip_rt_put(rt);
+		ip_statistics.IpOutNoRoutes++;
 		return;
 	}
-	
-	mss = (skb->dst->pmtu - sizeof(struct iphdr) - sizeof(struct tcphdr));
-	if (sk->user_mss)
-		mss = min(mss, sk->user_mss);
-	if(req->tstamp_ok)
-		mss -= TCPOLEN_TSTAMP_ALIGNED;
-	else
-		req->mss += TCPOLEN_TSTAMP_ALIGNED;
 
-	/* tcp_syn_build_options will do an skb_put() to obtain the TCP
-	 * options bytes below.
-	 */
-	skb->h.th = th = (struct tcphdr *) skb_put(skb, sizeof(struct tcphdr));
-
-	/* Don't offer more than they did.
-	 * This way we don't have to memorize who said what.
-	 * FIXME: maybe this should be changed for better performance
-	 * with syncookies.
-	 */
-	req->mss = min(mss, req->mss);
-
-	if (req->mss < 1) {
-		printk(KERN_DEBUG "initial req->mss below 1\n");
-		req->mss = 1;
-	}
+	mss = (rt->u.dst.pmtu - sizeof(struct iphdr) - sizeof(struct tcphdr));
+	if (opt)
+		mss -= opt->optlen;
+
+	skb = tcp_make_synack(sk, &rt->u.dst, req, mss);
+	if (skb) {
+		struct tcphdr *th = skb->h.th;
 
-	/* Yuck, make this header setup more efficient... -DaveM */
-	memset(th, 0, sizeof(struct tcphdr));
-	th->syn = 1;
-	th->ack = 1;
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
-	th->source = req->lcl_port; /* LVE */
-#else
-	th->source = sk->dummy_th.source;
+		th->source = req->lcl_port; /* LVE */
 #endif
-	th->dest = req->rmt_port;
-	skb->seq = req->snt_isn;
-	skb->end_seq = skb->seq + 1;
-	th->seq = htonl(skb->seq);
-	th->ack_seq = htonl(req->rcv_isn + 1);
-	if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */
-		__u8 rcv_wscale; 
-		/* Set this up on the first call only */
-		req->window_clamp = skb->dst->window;
-		tcp_select_initial_window(sock_rspace(sk)/2,req->mss,
-			&req->rcv_wnd,
-			&req->window_clamp,
-			req->wscale_ok,
-			&rcv_wscale);
-		req->rcv_wscale = rcv_wscale; 
-	}
-	th->window = htons(req->rcv_wnd);
-	tmp = tcp_syn_build_options(skb, req->mss, req->tstamp_ok,
-		req->wscale_ok,req->rcv_wscale);
-	skb->csum = 0;
-	th->doff = (sizeof(*th) + tmp)>>2;
-	th->check = tcp_v4_check(th, sizeof(*th) + tmp,
-				 req->af.v4_req.loc_addr, req->af.v4_req.rmt_addr,
-				 csum_partial((char *)th, sizeof(*th)+tmp, skb->csum));
 
-	ip_queue_xmit(skb);
-	tcp_statistics.TcpOutSegs++;
+		th->check = tcp_v4_check(th, skb->len,
+					 req->af.v4_req.loc_addr, req->af.v4_req.rmt_addr,
+					 csum_partial((char *)th, skb->len, skb->csum));
+
+		ip_build_and_send_pkt(skb, sk, req->af.v4_req.loc_addr,
+				      req->af.v4_req.rmt_addr, req->af.v4_req.opt);
+	}
+	ip_rt_put(rt);
 }
 
 static void tcp_v4_or_free(struct open_request *req)
@@ -1240,15 +1099,16 @@
 	req->rcv_wnd = 0;		/* So that tcp_send_synack() knows! */
 
 	req->rcv_isn = skb->seq;
- 	tp.tstamp_ok = tp.wscale_ok = tp.snd_wscale = 0;
+ 	tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
 	tp.in_mss = 536;
-	tcp_parse_options(th,&tp,want_cookie);
+	tcp_parse_options(NULL, th, &tp, want_cookie);
 	req->mss = tp.in_mss;
 	if (tp.saw_tstamp) {
 		req->mss -= TCPOLEN_TSTAMP_ALIGNED;
 		req->ts_recent = tp.rcv_tsval;
 	}
 	req->tstamp_ok = tp.tstamp_ok;
+	req->sack_ok = tp.sack_ok;
 	req->snd_wscale = tp.snd_wscale;
 	req->wscale_ok = tp.wscale_ok;
 	req->rmt_port = th->source;
@@ -1300,8 +1160,11 @@
 
 /* This is not only more efficient than what we used to do, it eliminates
  * a lot of code duplication between IPv4/IPv6 SYN recv processing. -DaveM
+ *
+ * This function wants to be moved to a common for IPv[46] file. --ANK
  */
-struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, struct sk_buff *skb)
+struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, struct sk_buff *skb,
+				      int snd_mss)
 {
 	struct sock *newsk = sk_alloc(AF_INET, GFP_ATOMIC, 0);
 
@@ -1310,27 +1173,16 @@
 
 		memcpy(newsk, sk, sizeof(*newsk));
 		newsk->sklist_next = NULL;
-		newsk->daddr = req->af.v4_req.rmt_addr;
-		newsk->rcv_saddr = req->af.v4_req.loc_addr;
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-		newsk->num = ntohs(skb->h.th->dest);
-#endif
 		newsk->state = TCP_SYN_RECV;
 
 		/* Clone the TCP header template */
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-		newsk->dummy_th.source = req->lcl_port;
-#endif
-		newsk->dummy_th.dest = req->rmt_port;
-		newsk->dummy_th.ack = 1;
-		newsk->dummy_th.doff = sizeof(struct tcphdr)>>2;
+		newsk->dport = req->rmt_port;
 
 		newsk->sock_readers = 0;
 		atomic_set(&newsk->rmem_alloc, 0);
 		skb_queue_head_init(&newsk->receive_queue);
 		atomic_set(&newsk->wmem_alloc, 0);
 		skb_queue_head_init(&newsk->write_queue);
-		newsk->saddr = req->af.v4_req.loc_addr;
 
 		newsk->done = 0;
 		newsk->proc = 0;
@@ -1395,12 +1247,40 @@
 		newsk->priority = 1;
 
 		/* IP layer stuff */
-		newsk->opt = req->af.v4_req.opt;
 		newsk->timeout = 0;
 		init_timer(&newsk->timer);
 		newsk->timer.function = &net_timer;
 		newsk->timer.data = (unsigned long) newsk;
 		newsk->socket = NULL;
+
+		newtp->tstamp_ok = req->tstamp_ok;
+		if((newtp->sack_ok = req->sack_ok) != 0)
+			newtp->num_sacks = 0;
+		newtp->window_clamp = req->window_clamp;
+		newtp->rcv_wnd = req->rcv_wnd;
+		newtp->wscale_ok = req->wscale_ok;
+		if (newtp->wscale_ok) {
+			newtp->snd_wscale = req->snd_wscale;
+			newtp->rcv_wscale = req->rcv_wscale;
+		} else {
+			newtp->snd_wscale = newtp->rcv_wscale = 0;
+			newtp->window_clamp = min(newtp->window_clamp,65535);
+		}
+		if (newtp->tstamp_ok) {
+			newtp->ts_recent = req->ts_recent;
+			newtp->ts_recent_stamp = jiffies;
+			newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
+		} else {
+			newtp->tcp_header_len = sizeof(struct tcphdr);
+		}
+
+		snd_mss -= newtp->tcp_header_len;
+
+		if (sk->user_mss)
+			snd_mss = min(snd_mss, sk->user_mss);
+
+		newsk->mss = min(req->mss, snd_mss);
+
 	}
 	return newsk;
 }
@@ -1409,77 +1289,58 @@
 				   struct open_request *req,
 				   struct dst_entry *dst)
 {
+	struct ip_options *opt = req->af.v4_req.opt;
 	struct tcp_opt *newtp;
 	struct sock *newsk;
 	int snd_mss;
+	int mtu;
 
 #ifdef NEW_LISTEN
 	if (sk->ack_backlog > sk->max_ack_backlog)
 		goto exit; /* head drop */
 #endif
-	newsk = tcp_create_openreq_child(sk, req, skb);
-	if (!newsk) 
-		goto exit;
-#ifdef NEW_LISTEN
-	sk->ack_backlog++;
-#endif
-
-	newtp = &(newsk->tp_pinfo.af_tcp);
-
-	/* options / mss / route_cache */
 	if (dst == NULL) { 
 		struct rtable *rt;
 		
 		if (ip_route_output(&rt,
-				    newsk->opt && newsk->opt->srr ? 
-				    newsk->opt->faddr : newsk->daddr,
-				    newsk->saddr, newsk->ip_tos|RTO_CONN, 0)) {
-			sk_free(newsk);
+				    opt && opt->srr ? opt->faddr : req->af.v4_req.rmt_addr,
+				    req->af.v4_req.loc_addr, sk->ip_tos|RTO_CONN, 0))
 			return NULL;
-		}
 	        dst = &rt->u.dst;
-	} 
-	newsk->dst_cache = dst;
-	
-	snd_mss = dst->pmtu;
-
-	/* FIXME: is mtu really the same as snd_mss? */
-	newsk->mtu = snd_mss;
-	/* FIXME: where does mtu get used after this? */
-	/* sanity check */
-	if (newsk->mtu < 64)
-		newsk->mtu = 64;
-
-	newtp->tstamp_ok = req->tstamp_ok;
-	newtp->window_clamp = req->window_clamp;
-	newtp->rcv_wnd = req->rcv_wnd;
-	newtp->wscale_ok = req->wscale_ok;
-	if (newtp->wscale_ok) {
-		newtp->snd_wscale = req->snd_wscale;
-		newtp->rcv_wscale = req->rcv_wscale;
-	} else {
-		newtp->snd_wscale = newtp->rcv_wscale = 0;
-		newtp->window_clamp = min(newtp->window_clamp,65535);
-	}
-	if (newtp->tstamp_ok) {
-		newtp->ts_recent = req->ts_recent;
-		newtp->ts_recent_stamp = jiffies;
-		newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
-		newsk->dummy_th.doff += (TCPOLEN_TSTAMP_ALIGNED >> 2);
-	} else {
-		newtp->tcp_header_len = sizeof(struct tcphdr);
 	}
 
-	snd_mss -= sizeof(struct iphdr) + sizeof(struct tcphdr);
-	if (sk->user_mss)
-		snd_mss = min(snd_mss, sk->user_mss);
+#ifdef NEW_LISTEN
+	sk->ack_backlog++;
+#endif
+
+	mtu = dst->pmtu;
+	if (mtu < 68)
+		mtu = 68;
+	snd_mss = mtu - sizeof(struct iphdr);
+	if (opt)
+		snd_mss -= opt->optlen;
+
+	newsk = tcp_create_openreq_child(sk, req, skb, snd_mss);
+	if (!newsk) 
+		goto exit;
+
+	newsk->dst_cache = dst;
 
-	/* Make sure our mtu is adjusted for headers. */
-	newsk->mss = min(req->mss, snd_mss) + sizeof(struct tcphdr) - newtp->tcp_header_len;
+	newtp = &(newsk->tp_pinfo.af_tcp);
+	newsk->daddr = req->af.v4_req.rmt_addr;
+	newsk->saddr = req->af.v4_req.loc_addr;
+	newsk->rcv_saddr = req->af.v4_req.loc_addr;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	newsk->num = ntohs(skb->h.th->dest);
+	newsk->sport = req->lcl_port;
+#endif
+	newsk->opt = req->af.v4_req.opt;
+	newsk->mtu = mtu;
 
 	/* Must use the af_specific ops here for the case of IPv6 mapped. */
 	newsk->prot->hash(newsk);
 	add_to_prot_sklist(newsk);
+
 	return newsk;
 
 exit:
@@ -1677,106 +1538,82 @@
 	goto discard_it;
 }
 
-int tcp_v4_build_header(struct sock *sk, struct sk_buff *skb)
+int tcp_v4_rebuild_header(struct sock *sk)
 {
-	return ip_build_header(skb, sk);
-}
-
-int tcp_v4_rebuild_header(struct sock *sk, struct sk_buff *skb)
-{
-	struct rtable *rt;
-	struct iphdr *iph;
-	struct tcphdr *th;
-	int size;
+	struct rtable *rt = (struct rtable *)sk->dst_cache;
+	__u32 new_saddr;
         int want_rewrite = sysctl_ip_dynaddr && sk->state == TCP_SYN_SENT;
 
-	/* Check route */
-
-	rt = (struct rtable*)skb->dst;
+	if(rt == NULL)
+		return 0;
 
-	/* Force route checking if want_rewrite */
-	/* The idea is good, the implementation is disguisting.
-	   Well, if I made bind on this socket, you cannot randomly ovewrite
-	   its source address. --ANK
+	/* Force route checking if want_rewrite.
+	 * The idea is good, the implementation is disguisting.
+	 * Well, if I made bind on this socket, you cannot randomly ovewrite
+	 * its source address. --ANK
 	 */
 	if (want_rewrite) {
 		int tmp;
+		struct rtable *new_rt;
 		__u32 old_saddr = rt->rt_src;
 
-		/* Query new route */
-		tmp = ip_route_connect(&rt, rt->rt_dst, 0, 
+		/* Query new route using another rt buffer */
+		tmp = ip_route_connect(&new_rt, rt->rt_dst, 0,
 					RT_TOS(sk->ip_tos)|sk->localroute,
 					sk->bound_dev_if);
 
 		/* Only useful if different source addrs */
-		if (tmp == 0 || rt->rt_src != old_saddr ) {
-			dst_release(skb->dst);
-			skb->dst = &rt->u.dst;
-		} else {
-			want_rewrite = 0;
-			dst_release(&rt->u.dst);
+		if (tmp == 0) {
+			/*
+			 *	Only useful if different source addrs
+			 */
+			if (new_rt->rt_src != old_saddr ) {
+				dst_release(sk->dst_cache);
+				sk->dst_cache = &new_rt->u.dst;
+				rt = new_rt;
+				goto do_rewrite;
+			} 
+			dst_release(&new_rt->u.dst);
 		}
-	} else 
+	}
 	if (rt->u.dst.obsolete) {
 		int err;
 		err = ip_route_output(&rt, rt->rt_dst, rt->rt_src, rt->key.tos|RTO_CONN, rt->key.oif);
 		if (err) {
 			sk->err_soft=-err;
-			sk->error_report(skb->sk);
+			sk->error_report(sk);
 			return -1;
 		}
-		dst_release(skb->dst);
-		skb->dst = &rt->u.dst;
+		dst_release(xchg(&sk->dst_cache, &rt->u.dst));
 	}
 
-	iph = skb->nh.iph;
-	th = skb->h.th;
-	size = skb->tail - skb->h.raw;
+	return 0;
 
-        if (want_rewrite) {
-        	__u32 new_saddr = rt->rt_src;
+do_rewrite:
+	new_saddr = rt->rt_src;
                 
-                /*
-                 *	Ouch!, this should not happen.
-                 */
-                if (!sk->saddr || !sk->rcv_saddr) {
-                	printk(KERN_WARNING "tcp_v4_rebuild_header(): not valid sock addrs: saddr=%08lX rcv_saddr=%08lX\n",
-			       ntohl(sk->saddr), 
-			       ntohl(sk->rcv_saddr));
-                        return 0;
-                }
-
-		/*
-		 *	Maybe whe are in a skb chain loop and socket address has
-		 *	yet been 'damaged'.
-		 */
-
-		if (new_saddr != sk->saddr) {
-			if (sysctl_ip_dynaddr > 1) {
-				printk(KERN_INFO "tcp_v4_rebuild_header(): shifting sk->saddr from %d.%d.%d.%d to %d.%d.%d.%d\n",
-					NIPQUAD(sk->saddr), 
-					NIPQUAD(new_saddr));
-			}
+	/* Ouch!, this should not happen. */
+	if (!sk->saddr || !sk->rcv_saddr) {
+		printk(KERN_WARNING "tcp_v4_rebuild_header(): not valid sock addrs: "
+		       "saddr=%08lX rcv_saddr=%08lX\n",
+		       ntohl(sk->saddr), 
+		       ntohl(sk->rcv_saddr));
+		return 0;
+	}
 
-			sk->saddr = new_saddr;
-			sk->rcv_saddr = new_saddr;
-			/* sk->prot->rehash(sk); */
-			tcp_v4_rehash(sk);
-		} 
-
-		if (new_saddr != iph->saddr) {
-			if (sysctl_ip_dynaddr > 1) {
-				printk(KERN_INFO "tcp_v4_rebuild_header(): shifting iph->saddr from %d.%d.%d.%d to %d.%d.%d.%d\n",
-					NIPQUAD(iph->saddr), 
-					NIPQUAD(new_saddr));
-			}
+	if (new_saddr != sk->saddr) {
+		if (sysctl_ip_dynaddr > 1) {
+			printk(KERN_INFO "tcp_v4_rebuild_header(): shifting sk->saddr "
+			       "from %d.%d.%d.%d to %d.%d.%d.%d\n",
+			       NIPQUAD(sk->saddr), 
+			       NIPQUAD(new_saddr));
+		}
 
-			iph->saddr = new_saddr;
-			ip_send_check(iph);
-		} 
+		sk->saddr = new_saddr;
+		sk->rcv_saddr = new_saddr;
+		tcp_v4_rehash(sk);
+	} 
         
-        }
-
 	return 0;
 }
 
@@ -1792,11 +1629,10 @@
 
 	sin->sin_family		= AF_INET;
 	sin->sin_addr.s_addr	= sk->daddr;
-	sin->sin_port		= sk->dummy_th.dest;
+	sin->sin_port		= sk->dport;
 }
 
 struct tcp_func ipv4_specific = {
-	tcp_v4_build_header,
 	ip_queue_xmit,
 	tcp_v4_send_check,
 	tcp_v4_rebuild_header,
@@ -1834,10 +1670,6 @@
 	sk->max_ack_backlog = SOMAXCONN;
 	sk->mtu = 576;
 	sk->mss = 536;
-
-	/* Speed up by setting some standard state for the dummy_th. */
-  	sk->dummy_th.ack=1;
-  	sk->dummy_th.doff=sizeof(struct tcphdr)>>2;
 
 	/* Init SYN queue. */
 	tcp_synq_init(tp);
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.1.90/linux/net/ipv4/tcp_output.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/tcp_output.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_output.c,v 1.65 1998/03/15 12:07:03 davem Exp $
+ * Version:	$Id: tcp_output.c,v 1.76 1998/03/22 22:10:24 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -29,6 +29,7 @@
  *		Linus Torvalds	:	send_delayed_ack
  *		David S. Miller	:	Charge memory using the right skb
  *					during syn/ack processing.
+ *		David S. Miller :	Output engine completely rewritten.
  *
  */
 
@@ -57,278 +58,227 @@
 		tp->send_head = NULL;
 }
 
-/*
- *	This is the main buffer sending routine. We queue the buffer
- *	having checked it is sane seeming.
+/* This routine actually transmits TCP packets queued in by
+ * tcp_do_sendmsg().  This is used by both the initial
+ * transmission and possible later retransmissions.
+ * All SKB's seen here are completely headerless.  It is our
+ * job to build the TCP header, and pass the packet down to
+ * IP so it can do the same plus pass the packet off to the
+ * device.
+ *
+ * We are working here with either a clone of the original
+ * SKB, or a fresh unique copy made by the retransmit engine.
+ */
+void tcp_transmit_skb(struct sock *sk, struct sk_buff *skb)
+{
+	if(skb != NULL) {
+		struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+		struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
+		int tcp_header_size = tp->tcp_header_len;
+		struct tcphdr *th;
+
+		if(tcb->flags & TCPCB_FLAG_SYN) {
+			tcp_header_size = sizeof(struct tcphdr) + TCPOLEN_MSS;
+			if(sysctl_tcp_timestamps)
+				tcp_header_size += TCPOLEN_TSTAMP_ALIGNED;
+			if(sysctl_tcp_window_scaling)
+				tcp_header_size += TCPOLEN_WSCALE_ALIGNED;
+			if(sysctl_tcp_sack && !sysctl_tcp_timestamps)
+				tcp_header_size += TCPOLEN_SACKPERM_ALIGNED;
+		} else if(tp->sack_ok && tp->num_sacks) {
+			/* A SACK is 2 pad bytes, a 2 byte header, plus
+			 * 2 32-bit sequence numbers for each SACK block.
+			 */
+			tcp_header_size += (TCPOLEN_SACK_BASE_ALIGNED +
+					    (tp->num_sacks * TCPOLEN_SACK_PERBLOCK));
+		}
+		th = (struct tcphdr *) skb_push(skb, tcp_header_size);
+		skb->h.th = th;
+		skb_set_owner_w(skb, sk);
+
+		/* Build TCP header and checksum it. */
+		th->source		= sk->sport;
+		th->dest		= sk->dport;
+		th->seq			= htonl(skb->seq);
+		th->ack_seq		= htonl(tp->rcv_nxt);
+		th->doff		= (tcp_header_size >> 2);
+		th->res1		= 0;
+		*(((__u8 *)th) + 13)	= tcb->flags;
+		th->window		= htons(tcp_select_window(sk));
+		th->check		= 0;
+		th->urg_ptr		= ntohs(tcb->urg_ptr);
+		if(tcb->flags & TCPCB_FLAG_SYN) {
+			th->window	= htons(tp->rcv_wnd);
+			tcp_syn_build_options((__u32 *)(th + 1), sk->mss,
+					      sysctl_tcp_timestamps,
+					      sysctl_tcp_sack,
+					      sysctl_tcp_window_scaling,
+					      tp->rcv_wscale,
+					      skb->when);
+		} else {
+			tcp_build_and_update_options((__u32 *)(th + 1),
+						     tp, skb->when);
+		}
+		tp->af_specific->send_check(sk, th, skb->len, skb);
+
+		clear_delayed_acks(sk);
+		tp->last_ack_sent = tp->rcv_nxt;
+		tcp_statistics.TcpOutSegs++;
+		tp->af_specific->queue_xmit(skb);
+	}
+}
+
+/* This is the main buffer sending routine. We queue the buffer
+ * and decide whether to queue or transmit now.
  */
- 
 void tcp_send_skb(struct sock *sk, struct sk_buff *skb, int force_queue)
 {
-	struct tcphdr *th = skb->h.th;
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-	int size;
-
-	/* Length of packet (not counting length of pre-tcp headers). */
-	size = skb->len - ((unsigned char *) th - skb->data);
-
-	/* If there is a FIN or a SYN we add it onto the size. */
-	if (th->fin || th->syn) {
-		if(th->syn)
-			size++;
-		if(th->fin)
-			size++;
-	}
-
-	/* Actual processing. */
-	skb->seq = ntohl(th->seq);
-	skb->end_seq = skb->seq + size - 4*th->doff;
 
+	/* Advance write_seq and place onto the write_queue. */
+	tp->write_seq += (skb->end_seq - skb->seq);
 	skb_queue_tail(&sk->write_queue, skb);
 
 	if (!force_queue && tp->send_head == NULL && tcp_snd_test(sk, skb)) {
-		struct sk_buff * buff;
-
-		/* This is going straight out. */
-		tp->last_ack_sent = tp->rcv_nxt;
-		th->ack_seq = htonl(tp->rcv_nxt);
-		th->window = htons(tcp_select_window(sk));
-		tcp_update_options((__u32 *)(th + 1),tp);
-
-		tp->af_specific->send_check(sk, th, size, skb);
-
-		buff = skb_clone(skb, GFP_KERNEL);
-		if (buff == NULL)
-			goto queue;
-		
-		clear_delayed_acks(sk);
-		skb_set_owner_w(buff, sk);
-
+		/* Send it out now. */
+		skb->when = jiffies;
 		tp->snd_nxt = skb->end_seq;
 		tp->packets_out++;
-
-		skb->when = jiffies;
-
-		tcp_statistics.TcpOutSegs++;
-		tp->af_specific->queue_xmit(buff);
-
-		if (!tcp_timer_is_set(sk, TIME_RETRANS))
+		tcp_transmit_skb(sk, skb_clone(skb, GFP_KERNEL));
+		if(!tcp_timer_is_set(sk, TIME_RETRANS))
 			tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
-
-		return;
-	}
-
-queue:
-	/* Remember where we must start sending. */
-	if (tp->send_head == NULL)
-		tp->send_head = skb;
-	if (!force_queue && tp->packets_out == 0 && !tp->pending) {
-		tp->pending = TIME_PROBE0;
-		tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto);
+	} else {
+		/* Queue it, remembering where we must start sending. */
+		if (tp->send_head == NULL)
+			tp->send_head = skb;
+		if (!force_queue && tp->packets_out == 0 && !tp->pending) {
+			tp->pending = TIME_PROBE0;
+			tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto);
+		}
 	}
 }
 
-/*
- *	Function to create two new tcp segments.
- *	Shrinks the given segment to the specified size and appends a new
- *	segment with the rest of the packet to the list.
- *	This won't be called frenquently, I hope... 
+/* Function to create two new tcp segments.  Shrinks the given segment
+ * to the specified size and appends a new segment with the rest of the
+ * packet to the list. This won't be called frenquently, I hope... 
+ * Remember, these are still header-less SKB's at this point.
  */
-
 static int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len)
 {
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	struct sk_buff *buff;
-	struct tcphdr *th, *nth;	
-	int nsize;
-	int tmp;
-
-	th = skb->h.th;
-
-	/* Size of new segment. */
-	nsize = skb->tail - ((unsigned char *)(th)+tp->tcp_header_len) - len;
-	if (nsize <= 0) {
-		printk(KERN_DEBUG "tcp_fragment: bug size <= 0\n");
-		return -1;
-	}
+	int nsize = skb->len - len;
+	u16 flags;
 
 	/* Get a new skb... force flag on. */
-	buff = sock_wmalloc(sk, nsize + 128 + sk->prot->max_header + 15, 1, 
-			    GFP_ATOMIC);
+	buff = sock_wmalloc(sk,
+			    (nsize +
+			     MAX_HEADER +
+			     sk->prot->max_header + 15),
+			    1, GFP_ATOMIC);
 	if (buff == NULL)
-		return -1;
+		return -1; /* We'll just try again later. */
 
-	/* Put headers on the new packet. */
-	tmp = tp->af_specific->build_net_header(sk, buff);
-	if (tmp < 0) {
-		kfree_skb(buff);
-		return -1;
-	}
+	/* Reserve space for headers. */
+	skb_reserve(buff, MAX_HEADER + sk->prot->max_header);
 		
-	/* Move the TCP header over. */
-	nth = (struct tcphdr *) skb_put(buff, tp->tcp_header_len);
-	buff->h.th = nth;
-	memcpy(nth, th, tp->tcp_header_len);
-
-	/* Correct the new header. */
+	/* Correct the sequence numbers. */
 	buff->seq = skb->seq + len;
 	buff->end_seq = skb->end_seq;
-	nth->seq = htonl(buff->seq);
-	nth->check = 0;
-	nth->doff  = th->doff;
 	
-	/* urg data is always an headache */
-	if (th->urg) {
-		if (th->urg_ptr > len) {
-			th->urg = 0;
-			nth->urg_ptr -= len;
+	/* PSH and FIN should only be set in the second packet. */
+	flags = TCP_SKB_CB(skb)->flags;
+	TCP_SKB_CB(skb)->flags = flags & ~(TCPCB_FLAG_FIN | TCPCB_FLAG_PSH);
+	if(flags & TCPCB_FLAG_URG) {
+		u16 old_urg_ptr = TCP_SKB_CB(skb)->urg_ptr;
+
+		/* Urgent data is always a pain in the ass. */
+		if(old_urg_ptr > len) {
+			TCP_SKB_CB(skb)->flags &= ~(TCPCB_FLAG_URG);
+			TCP_SKB_CB(skb)->urg_ptr = 0;
+			TCP_SKB_CB(buff)->urg_ptr = old_urg_ptr - len;
 		} else {
-			nth->urg = 0;
+			flags &= ~(TCPCB_FLAG_URG);
 		}
 	}
+	if(!(flags & TCPCB_FLAG_URG))
+		TCP_SKB_CB(buff)->urg_ptr = 0;
+	TCP_SKB_CB(buff)->flags = flags;
+	TCP_SKB_CB(buff)->sacked = 0;
 
-	/* Copy data tail to our new buffer. */
-	buff->csum = csum_partial_copy(((u8 *)(th)+tp->tcp_header_len) + len,
-				       skb_put(buff, nsize),
+	/* Copy and checksum data tail into the new buffer. */
+	buff->csum = csum_partial_copy(skb->data + len, skb_put(buff, nsize),
 				       nsize, 0);
 
 	skb->end_seq -= nsize;
 	skb_trim(skb, skb->len - nsize);
 
-	/* Remember to checksum this packet afterwards. */
-	th->check = 0;
-	skb->csum = csum_partial((u8*)(th) + tp->tcp_header_len, skb->tail - ((u8 *) (th)+tp->tcp_header_len),
-				 0);
+	/* Rechecksum original buffer. */
+	skb->csum = csum_partial(skb->data, skb->len, 0);
 
+	/* Link BUFF into the send queue. */
 	skb_append(skb, buff);
 
 	return 0;
 }
 
-static void tcp_wrxmit_prob(struct sock *sk, struct sk_buff *skb)
-{
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-
-	/* This is acked data. We can discard it. This cannot currently occur. */
-	tp->retransmits = 0;
-
-	printk(KERN_DEBUG "tcp_write_xmit: bug skb in write queue\n");
-
-	update_send_head(sk);
-
-	skb_unlink(skb);	
-	kfree_skb(skb);
-
-	if (!sk->dead)
-		sk->write_space(sk);
-}
-
-static int tcp_wrxmit_frag(struct sock *sk, struct sk_buff *skb, int size)
-{
-	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
-	
-	SOCK_DEBUG(sk, "tcp_write_xmit: frag needed size=%d mss=%d\n",
-		   size, sk->mss);
-
-	if (tcp_fragment(sk, skb, sk->mss)) {
-		/* !tcp_frament Failed! */
-		tp->send_head = skb;
-		tp->packets_out--;
-		return -1;
-	}
-	return 0;
-}
-
-/*
- * 	This routine writes packets to the network.
- *	It advances the send_head.
- *	This happens as incoming acks open up the remote window for us.
+/* This routine writes packets to the network.  It advances the
+ * send_head.  This happens as incoming acks open up the remote
+ * window for us.
  */
- 
 void tcp_write_xmit(struct sock *sk)
 {
-	struct sk_buff *skb;
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-	u16 rcv_wnd;
-	int sent_pkts = 0;
+	int mss_now = sk->mss;
 
-	/* The bytes will have to remain here. In time closedown will
-	 * empty the write queue and all will be happy.
+	/* Account for SACKS, we may need to fragment due to this.
+	 * It is just like the real MSS changing on us midstream.
+	 * We also handle things correctly when the user adds some
+	 * IP options mid-stream.  Silly to do, but cover it.
 	 */
-	if(sk->zapped)
-		return;
-
-	/*	Anything on the transmit queue that fits the window can
-	 *	be added providing we are:
-	 *
-	 *	a) following SWS avoidance [and Nagle algorithm]
-	 *	b) not exceeding our congestion window.
-	 *	c) not retransmiting [Nagle]
+	if(tp->sack_ok && tp->num_sacks)
+		mss_now -= (TCPOLEN_SACK_BASE_ALIGNED +
+			    (tp->num_sacks * TCPOLEN_SACK_PERBLOCK));
+	if(sk->opt && sk->opt->optlen)
+		mss_now -= sk->opt->optlen;
+
+	/* If we are zapped, the bytes will have to remain here.
+	 * In time closedown will empty the write queue and all
+	 * will be happy.
 	 */
-	rcv_wnd = htons(tcp_select_window(sk));
-	while((skb = tp->send_head) && tcp_snd_test(sk, skb)) {
-		struct tcphdr *th;
-		struct sk_buff *buff;
-		int size;
+	if(!sk->zapped) {
+		struct sk_buff *skb;
+		int sent_pkts = 0;
 
-		/* See if we really need to send the packet. (debugging code) */
-		if (!after(skb->end_seq, tp->snd_una)) {
-			tcp_wrxmit_prob(sk, skb);
-			continue;
-		}
-
-		/*	Put in the ack seq and window at this point rather
-		 *	than earlier, in order to keep them monotonic.
-		 *	We really want to avoid taking back window allocations.
-		 *	That's legal, but RFC1122 says it's frowned on.
-		 *	Ack and window will in general have changed since
-		 *	this packet was put on the write queue.
+		/* Anything on the transmit queue that fits the window can
+		 * be added providing we are:
+		 *
+		 * a) following SWS avoidance [and Nagle algorithm]
+		 * b) not exceeding our congestion window.
+		 * c) not retransmiting [Nagle]
 		 */
-		th = skb->h.th;
-		size = skb->len - (((unsigned char *) th) - skb->data);
-		if (size - (th->doff << 2) > sk->mss) {
-			if (tcp_wrxmit_frag(sk, skb, size))
-				break;
-			size = skb->len - (((unsigned char*)th) - skb->data);
-		}
-
-		tp->last_ack_sent = tp->rcv_nxt;
-		th->ack_seq = htonl(tp->rcv_nxt);
-		th->window = rcv_wnd;
-		tcp_update_options((__u32 *)(th + 1),tp);
-
-		tp->af_specific->send_check(sk, th, size, skb);
-
-#ifdef TCP_DEBUG
-		if (before(skb->end_seq, tp->snd_nxt))
-			printk(KERN_DEBUG "tcp_write_xmit:"
-			       " sending already sent seq\n");
-#endif
-
-		buff = skb_clone(skb, GFP_ATOMIC);
-		if (buff == NULL)
-			break;
-
-		/* Advance the send_head.  This one is going out. */
-		update_send_head(sk);
-		clear_delayed_acks(sk);
-
-		tp->packets_out++;
-		skb_set_owner_w(buff, sk);
-
-		tp->snd_nxt = skb->end_seq;
+		while((skb = tp->send_head) && tcp_snd_test(sk, skb)) {
+			if (skb->len > mss_now) {
+				if (tcp_fragment(sk, skb, mss_now))
+					break;
+			}
 
-		skb->when = jiffies;
+			/* Advance the send_head.  This one is going out. */
+			update_send_head(sk);
+			skb->when = jiffies;
+			tp->snd_nxt = skb->end_seq;
+			tp->packets_out++;
+			tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC));
+			sent_pkts = 1;
+		}
 
-		sent_pkts = 1;
-		tp->af_specific->queue_xmit(buff);
+		/* If we sent anything, make sure the retransmit
+		 * timer is active.
+		 */
+		if (sent_pkts && !tcp_timer_is_set(sk, TIME_RETRANS))
+			tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 	}
-
-	if (sent_pkts && !tcp_timer_is_set(sk, TIME_RETRANS))
-		tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 }
 
-
-
 /* This function returns the amount that we can raise the
  * usable window based on the following constraints
  *  
@@ -377,11 +327,7 @@
  * Below we obtain similar behavior by forcing the offered window to
  * a multiple of the mss when it is feasible to do so.
  *
- * FIXME: In our current implementation the value returned by sock_rpsace(sk)
- * is the total space we have allocated to the socket to store skbuf's.
- * The current design assumes that up to half of that space will be
- * taken by headers, and the remaining space will be available for TCP data.
- * This should be accounted for correctly instead.
+ * Note, we don't "adjust" for TIMESTAMP or SACK option bytes.
  */
 u32 __tcp_select_window(struct sock *sk)
 {
@@ -422,57 +368,72 @@
 	return window;
 }
 
-static int tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb)
+/* Attempt to collapse two adjacent SKB's during retransmission. */
+static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb, int mss_now)
 {
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-	struct tcphdr *th1, *th2;
-	int size1, size2, avail;
-	struct sk_buff *buff = skb->next;
+	struct sk_buff *next_skb = skb->next;
 
-	th1 = skb->h.th;
-
-	if (th1->urg)
-		return -1;
-
-	avail = skb_tailroom(skb);
+	/* The first test we must make is that neither of these two
+	 * SKB's are still referenced by someone else.
+	 */
+	if(!skb_cloned(skb) && !skb_cloned(next_skb)) {
+		int skb_size = skb->len, next_skb_size = next_skb->len;
+		u16 flags = TCP_SKB_CB(skb)->flags;
 
-	/* Size of TCP payload. */
-	size1 = skb->tail - ((u8 *) (th1)+(th1->doff<<2));
+		/* Punt if the first SKB has URG set. */
+		if(flags & TCPCB_FLAG_URG)
+			return;
 	
-	th2 = buff->h.th;
-	size2 = buff->tail - ((u8 *) (th2)+(th2->doff<<2)); 
+		/* Also punt if next skb has been SACK'd. */
+		if(TCP_SKB_CB(next_skb)->sacked & TCPCB_SACKED_ACKED)
+			return;
 
-	if (size2 > avail || size1 + size2 > sk->mss )
-		return -1;
+		/* Punt if not enough space exists in the first SKB for
+		 * the data in the second, or the total combined payload
+		 * would exceed the MSS.
+		 */
+		if ((next_skb_size > skb_tailroom(skb)) ||
+		    ((skb_size + next_skb_size) > mss_now))
+			return;
 
-	/* Ok.  We will be able to collapse the packet. */
-	skb_unlink(buff);
-	memcpy(skb_put(skb, size2), ((char *) th2) + (th2->doff << 2), size2);
+		/* Ok.  We will be able to collapse the packet. */
+		skb_unlink(next_skb);
+
+		if(skb->len % 4) {
+			/* Must copy and rechecksum all data. */
+			memcpy(skb_put(skb, next_skb_size), next_skb->data, next_skb_size);
+			skb->csum = csum_partial(skb->data, skb->len, 0);
+		} else {
+			/* Optimize, actually we could also combine next_skb->csum
+			 * to skb->csum using a single add w/carry operation too.
+			 */
+			skb->csum = csum_partial_copy(next_skb->data,
+						      skb_put(skb, next_skb_size),
+						      next_skb_size, skb->csum);
+		}
 	
-	/* Update sizes on original skb, both TCP and IP. */
-	skb->end_seq += buff->end_seq - buff->seq;
-	if (th2->urg) {
-		th1->urg = 1;
-		th1->urg_ptr = th2->urg_ptr + size1;
-	}
-	if (th2->fin)
-		th1->fin = 1;
-
-	/* ... and off you go. */
-	kfree_skb(buff);
-	tp->packets_out--;
+		/* Update sequence range on original skb. */
+		skb->end_seq += next_skb->end_seq - next_skb->seq;
 
-	/* Header checksum will be set by the retransmit procedure
-	 * after calling rebuild header.
-	 */
-	th1->check = 0;
-	skb->csum = csum_partial((u8*)(th1)+(th1->doff<<2), size1 + size2, 0);
-	return 0;
+		/* Merge over control information. */
+		flags |= TCP_SKB_CB(next_skb)->flags; /* This moves PSH/FIN etc. over */
+		if(flags & TCPCB_FLAG_URG) {
+			u16 urgptr = TCP_SKB_CB(next_skb)->urg_ptr;
+			TCP_SKB_CB(skb)->urg_ptr = urgptr + skb_size;
+		}
+		TCP_SKB_CB(skb)->flags = flags;
+
+		/* All done, get rid of second SKB and account for it so
+		 * packet counting does not break.
+		 */
+		kfree_skb(next_skb);
+		sk->tp_pinfo.af_tcp.packets_out--;
+	}
 }
 
 /* Do a simple retransmit without using the backoff mechanisms in
  * tcp_timer. This is used to speed up path mtu recovery. Note that
- * these simple retransmit aren't counted in the usual tcp retransmit
+ * these simple retransmits aren't counted in the usual tcp retransmit
  * backoff counters. 
  * The socket is already locked here.
  */ 
@@ -480,114 +441,114 @@
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
-	/* Clear delay ack timer. */
- 	tcp_clear_xmit_timer(sk, TIME_DACK);
- 
- 	tp->retrans_head = NULL; 
  	/* Don't muck with the congestion window here. */
  	tp->dup_acks = 0;
  	tp->high_seq = tp->snd_nxt;
+
  	/* FIXME: make the current rtt sample invalid */
- 	tcp_do_retransmit(sk, 0); 
+ 	tp->retrans_head = NULL; 
+ 	tcp_retransmit_skb(sk, skb_peek(&sk->write_queue)); 
 }
 
-/*
- *	A socket has timed out on its send queue and wants to do a
- *	little retransmitting.
- *	retrans_head can be different from the head of the write_queue
- *	if we are doing fast retransmit.
- */
-
-void tcp_do_retransmit(struct sock *sk, int all)
+static __inline__ void update_retrans_head(struct sock *sk)
 {
-	struct sk_buff * skb;
-	int ct=0;
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-
-	if (tp->retrans_head == NULL)
-		tp->retrans_head = skb_peek(&sk->write_queue);
-
-	if (tp->retrans_head == tp->send_head)
-		tp->retrans_head = NULL;
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 	
-	while ((skb = tp->retrans_head) != NULL) {
-		struct sk_buff *buff;
-		struct tcphdr *th;
-		int tcp_size;
-		int size;
-
-		/* In general it's OK just to use the old packet.  However we
-		 * need to use the current ack and window fields.  Urg and
-		 * urg_ptr could possibly stand to be updated as well, but we
-		 * don't keep the necessary data.  That shouldn't be a problem,
-		 * if the other end is doing the right thing.  Since we're
-		 * changing the packet, we have to issue a new IP identifier.
-		 */
+	tp->retrans_head = tp->retrans_head->next;
+	if((tp->retrans_head == tp->send_head) ||
+	   (tp->retrans_head == (struct sk_buff *) &sk->write_queue))
+		tp->retrans_head = NULL;
+}
 
-		th = skb->h.th;
+/* This retransmits one SKB.  Policy decisions and retransmit queue
+ * state updates are done by the caller.  Returns non-zero if an
+ * error occured which prevented the send.
+ */
+int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	int current_mss = sk->mss;
 
-		tcp_size = skb->tail - ((unsigned char *)(th)+tp->tcp_header_len);
+	/* Account for outgoing SACKS and IP options, if any. */
+	if(tp->sack_ok && tp->num_sacks)
+		current_mss -= (TCPOLEN_SACK_BASE_ALIGNED +
+				(tp->num_sacks * TCPOLEN_SACK_PERBLOCK));
+	if(sk->opt && sk->opt->optlen)
+		current_mss -= sk->opt->optlen;
+
+	if(skb->len > current_mss) {
+		if(tcp_fragment(sk, skb, current_mss))
+			return 1; /* We'll try again later. */
 
-		if (tcp_size > sk->mss) {
-			if (tcp_fragment(sk, skb, sk->mss)) {
-				printk(KERN_DEBUG "tcp_fragment failed\n");
-				return;
-			}
-			tp->packets_out++;
-		}
+		/* New SKB created, account for it. */
+		tp->packets_out++;
+	}
 
-		if (!th->syn &&
-		    tcp_size < (sk->mss >> 1) &&
-		    skb->next != tp->send_head &&
-		    skb->next != (struct sk_buff *)&sk->write_queue)
-			tcp_retrans_try_collapse(sk, skb);
-
-		if (tp->af_specific->rebuild_header(sk, skb)) {
-#ifdef TCP_DEBUG
-			printk(KERN_DEBUG "tcp_do_rebuild_header failed\n");
-#endif
-			break;
-		}
+	/* Collapse two adjacent packets if worthwhile and we can. */
+	if(!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN) &&
+	   (skb->len < (current_mss >> 1)) &&
+	   (skb->next != tp->send_head) &&
+	   (skb->next != (struct sk_buff *)&sk->write_queue))
+		tcp_retrans_try_collapse(sk, skb, current_mss);
 
-		SOCK_DEBUG(sk, "retransmit sending seq=%x\n", skb->seq);
+	if(tp->af_specific->rebuild_header(sk))
+		return 1; /* Routing failure or similar. */
 
-		/* Update ack and window. */
-		tp->last_ack_sent = th->ack_seq = htonl(tp->rcv_nxt);
-		th->window = ntohs(tcp_select_window(sk));
-		tcp_update_options((__u32 *)(th+1),tp);
+	/* Ok, we're gonna send it out, update state. */
+	TCP_SKB_CB(skb)->sacked |= TCPCB_SACKED_RETRANS;
 
-		size = skb->tail - (unsigned char *) th;
-		tp->af_specific->send_check(sk, th, size, skb);
+	/* Make a copy, if the first transmission SKB clone we made
+	 * is still in somebodies hands, else make a clone.
+	 */
+	skb->when = jiffies;
+	if(skb_cloned(skb))
+		skb = skb_copy(skb, GFP_ATOMIC);
+	else
+		skb = skb_clone(skb, GFP_ATOMIC);
+	tcp_transmit_skb(sk, skb);
+
+	/* Update global TCP statistics and return success. */
+	sk->prot->retransmits++;
+	tcp_statistics.TcpRetransSegs++;
 
-		skb->when = jiffies;
+	return 0;
+}
 
-		buff = skb_clone(skb, GFP_ATOMIC);
-		if (buff == NULL)
-			break;
+/* This gets called after a retransmit timeout, and the initially
+ * retransmitted data is acknowledged.  It tries to continue
+ * resending the rest of the retransmit queue, until either
+ * we've sent it all or the congestion window limit is reached.
+ */
+void tcp_xmit_retransmit_queue(struct sock *sk)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	struct sk_buff *skb;
+	int ct = 0;
 
-		skb_set_owner_w(buff, sk);
+	if (tp->retrans_head == NULL)
+		tp->retrans_head = skb_peek(&sk->write_queue);
+	if (tp->retrans_head == tp->send_head)
+		tp->retrans_head = NULL;
 
-		clear_delayed_acks(sk);
-		tp->af_specific->queue_xmit(buff);
+	while ((skb = tp->retrans_head) != NULL) {
+		/* If it has been ack'd by a SACK block, we don't
+		 * retransmit it.
+		 */
+		if(!(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) {
+			/* Send it out, punt if error occurred. */
+			if(tcp_retransmit_skb(sk, skb))
+				break;
 		
-		/* Count retransmissions. */
-		ct++;
-		sk->prot->retransmits++;
-		tcp_statistics.TcpRetransSegs++;
-
-		/* Only one retransmit requested. */
-		if (!all)
-			break;
-
-		/* This should cut it off before we send too many packets. */
-		if (ct >= tp->snd_cwnd)
-			break;
-
-		/* Advance the pointer. */
-		tp->retrans_head = skb->next;
-		if ((tp->retrans_head == tp->send_head) ||
-		    (tp->retrans_head == (struct sk_buff *) &sk->write_queue))
-			tp->retrans_head = NULL;
+			/* Count retransmissions locally. */
+			ct++;
+
+			/* Stop retransmitting if we've hit the congestion
+			 * window limit.
+			 */
+			if (ct >= tp->snd_cwnd)
+				break;
+		}
+		update_retrans_head(sk);
 	}
 }
 
@@ -597,83 +558,44 @@
 void tcp_send_fin(struct sock *sk)
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);	
+	struct sk_buff *skb = skb_peek_tail(&sk->write_queue);
+	int mss_now = sk->mss;
 	
 	/* Optimization, tack on the FIN if we have a queue of
-	 * unsent frames.
+	 * unsent frames.  But be careful about outgoing SACKS
+	 * and IP options.
 	 */
-	if(tp->send_head != NULL) {
-		struct sk_buff *tail = skb_peek_tail(&sk->write_queue);
-		struct tcphdr *th = tail->h.th;
-		int data_len;
-
-		/* Unfortunately tcp_write_xmit won't check for going over
-		 * the MSS due to the FIN sequence number, so we have to
-		 * watch out for it here.
-		 */
-		data_len = (tail->tail - (((unsigned char *)th)+tp->tcp_header_len));
-		if(data_len >= sk->mss)
-			goto build_new_frame; /* ho hum... */
-
-		/* tcp_write_xmit() will checksum the header etc. for us. */
-		th->fin = 1;
-		tail->end_seq++;
+	if(tp->sack_ok && tp->num_sacks)
+		mss_now -= (TCPOLEN_SACK_BASE_ALIGNED +
+			    (tp->num_sacks * TCPOLEN_SACK_PERBLOCK));
+	if(sk->opt && sk->opt->optlen)
+		mss_now -= sk->opt->optlen;
+	if((tp->send_head != NULL) && (skb->len < mss_now)) {
+		/* tcp_write_xmit() takes care of the rest. */
+		TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_FIN;
+		skb->end_seq++;
+		tp->write_seq++;
 	} else {
-		struct sk_buff *buff;
-		struct tcphdr *th;
-
-build_new_frame:
-		buff = sock_wmalloc(sk,
-				    (BASE_ACK_SIZE + tp->tcp_header_len +
-				     sizeof(struct sk_buff)),
-				    1, GFP_KERNEL);
-		if (buff == NULL) {
-			/* We can only fail due to low memory situations, not
-			 * due to going over our sndbuf limits (due to the
-			 * force flag passed to sock_wmalloc).  So just keep
-			 * trying.  We cannot allow this fail.  The socket is
-			 * still locked, so we need not check if the connection
-			 * was reset in the meantime etc.
-			 */
-			goto build_new_frame;
-		}
-
-		/* Administrivia. */
-		buff->csum = 0;
-
-		/* Put in the IP header and routing stuff.
-		 *
-		 * FIXME:
-		 * We can fail if the interface for the route
-		 * this socket takes goes down right before
-		 * we get here.  ANK is there a way to point
-		 * this into a "black hole" route in such a
-		 * case?  Ideally, we should still be able to
-		 * queue this and let the retransmit timer
-		 * keep trying until the destination becomes
-		 * reachable once more.  -DaveM
-		 */
-		if(tp->af_specific->build_net_header(sk, buff) < 0) {
-			kfree_skb(buff);
-			goto update_write_seq;
-		}
-		th = (struct tcphdr *) skb_put(buff, tp->tcp_header_len);
-		buff->h.th = th;
-
-		memcpy(th, (void *) &(sk->dummy_th), sizeof(*th));
-		th->seq = htonl(tp->write_seq);
-		th->fin = 1;
-		tcp_build_options((__u32 *)(th + 1), tp);
-
-		/* This makes sure we do things like abide by the congestion
-		 * window and other constraints which prevent us from sending.
-		 */
-		tcp_send_skb(sk, buff, 0);
+		/* Socket is locked, keep trying until memory is available. */
+		do {
+			skb = sock_wmalloc(sk,
+					   (MAX_HEADER +
+					    sk->prot->max_header),
+					   1, GFP_KERNEL);
+		} while (skb == NULL);
+
+		/* Reserve space for headers and prepare control bits. */
+		skb_reserve(skb, MAX_HEADER + sk->prot->max_header);
+		skb->csum = 0;
+		TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_FIN);
+		TCP_SKB_CB(skb)->sacked = 0;
+		TCP_SKB_CB(skb)->urg_ptr = 0;
+
+		/* FIN eats a sequence byte, write_seq advanced by tcp_send_skb(). */
+		skb->seq = tp->write_seq;
+		skb->end_seq = skb->seq + 1;
+		tcp_send_skb(sk, skb, 0);
 	}
-update_write_seq:
-	/* So that we recognize the ACK coming back for
-	 * this FIN as being legitimate.
-	 */
-	tp->write_seq++;
 }
 
 /* We get here when a process closes a file descriptor (either due to
@@ -685,109 +607,218 @@
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	struct sk_buff *skb;
-	struct tcphdr *th;
 
-again:
 	/* NOTE: No TCP options attached and we never retransmit this. */
-	skb = sock_wmalloc(sk, (BASE_ACK_SIZE + sizeof(*th)), 1, GFP_KERNEL);
-	if(skb == NULL)
-		goto again;
+	do {
+		skb = alloc_skb(MAX_HEADER + sk->prot->max_header, GFP_KERNEL);
+	} while(skb == NULL);
+
+	/* Reserve space for headers and prepare control bits. */
+	skb_reserve(skb, MAX_HEADER + sk->prot->max_header);
 	skb->csum = 0;
-	if(tp->af_specific->build_net_header(sk, skb) < 0) {
-		kfree_skb(skb);
-	} else {
-		th = (struct tcphdr *) skb_put(skb, sizeof(*th));
-		memcpy(th, &(sk->dummy_th), sizeof(*th));
-		th->seq = htonl(tp->write_seq);
-		th->rst = 1;
-		th->doff = sizeof(*th) / 4;
-		tp->last_ack_sent = tp->rcv_nxt;
-		th->ack_seq = htonl(tp->rcv_nxt);
-		th->window = htons(tcp_select_window(sk));
-		tp->af_specific->send_check(sk, th, sizeof(*th), skb);
-		tp->af_specific->queue_xmit(skb);
-		tcp_statistics.TcpOutSegs++;
-		tcp_statistics.TcpOutRsts++;
-	}
+	TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_RST);
+	TCP_SKB_CB(skb)->sacked = 0;
+	TCP_SKB_CB(skb)->urg_ptr = 0;
+
+	/* Send it off. */
+	skb->seq = tp->write_seq;
+	skb->end_seq = skb->seq;
+	skb->when = jiffies;
+	tcp_transmit_skb(sk, skb);
 }
 
 /* WARNING: This routine must only be called when we have already sent
  * a SYN packet that crossed the incoming SYN that caused this routine
  * to get called. If this assumption fails then the initial rcv_wnd
  * and rcv_wscale values will not be correct.
- *
- * XXX When you have time Dave, redo this to use tcp_send_skb() just
- * XXX like tcp_send_fin() above now does.... -DaveM
  */
 int tcp_send_synack(struct sock *sk)
 {
-	struct tcp_opt * tp = &(sk->tp_pinfo.af_tcp);
-	struct sk_buff * skb;	
-	struct sk_buff * buff;
-	struct tcphdr *th;
-	int tmp;
+	struct tcp_opt* tp = &(sk->tp_pinfo.af_tcp);
+	struct sk_buff* skb;	
 	
-	skb = sock_wmalloc(sk, MAX_SYN_SIZE + sizeof(struct sk_buff), 1, GFP_ATOMIC);
+	skb = sock_wmalloc(sk, (MAX_HEADER + sk->prot->max_header),
+			   1, GFP_ATOMIC);
 	if (skb == NULL) 
 		return -ENOMEM;
 
-	tmp = tp->af_specific->build_net_header(sk, skb);
-	if (tmp < 0) {
-		kfree_skb(skb);
-		return tmp;
+	/* Reserve space for headers and prepare control bits. */
+	skb_reserve(skb, MAX_HEADER + sk->prot->max_header);
+	skb->csum = 0;
+	TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_SYN);
+	TCP_SKB_CB(skb)->sacked = 0;
+	TCP_SKB_CB(skb)->urg_ptr = 0;
+
+	/* SYN eats a sequence byte. */
+	skb->seq = tp->snd_una;
+	skb->end_seq = skb->seq + 1;
+	skb_queue_tail(&sk->write_queue, skb);
+	skb->when = jiffies;
+	tp->packets_out++;
+	tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC));
+	return 0;
+}
+
+struct sk_buff * tcp_make_synack(struct sock *sk, struct dst_entry *dst,
+				 struct open_request *req, int mss)
+{
+	struct tcphdr *th;
+	int tcp_header_size;
+	struct sk_buff *skb;
+
+	skb = sock_wmalloc(sk, MAX_HEADER + sk->prot->max_header, 1, GFP_ATOMIC);
+	if (skb == NULL)
+		return NULL;
+
+	/* Reserve space for headers. */
+	skb_reserve(skb, MAX_HEADER + sk->prot->max_header);
+
+	skb->dst = dst_clone(dst);
+
+	if (sk->user_mss)
+		mss = min(mss, sk->user_mss);
+	if (req->tstamp_ok)
+		mss -= TCPOLEN_TSTAMP_ALIGNED;
+	else
+		req->mss += TCPOLEN_TSTAMP_ALIGNED;
+
+	/* Don't offer more than they did.
+	 * This way we don't have to memorize who said what.
+	 * FIXME: maybe this should be changed for better performance
+	 * with syncookies.
+	 */
+	req->mss = min(mss, req->mss);
+	if (req->mss < 1) {
+		printk(KERN_DEBUG "initial req->mss below 1\n");
+		req->mss = 1;
 	}
 
-	th =(struct tcphdr *) skb_put(skb, sizeof(struct tcphdr));
-	skb->h.th = th;
-	memset(th, 0, sizeof(struct tcphdr));
+	tcp_header_size = (sizeof(struct tcphdr) + TCPOLEN_MSS +
+			   (req->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0) +
+			   (req->wscale_ok ? TCPOLEN_WSCALE_ALIGNED : 0) +
+			   /* SACK_PERM is in the place of NOP NOP of TS */
+			   ((req->sack_ok && !req->tstamp_ok) ? TCPOLEN_SACKPERM_ALIGNED : 0));
+	skb->h.th = th = (struct tcphdr *) skb_push(skb, tcp_header_size);
 
+	memset(th, 0, sizeof(struct tcphdr));
 	th->syn = 1;
 	th->ack = 1;
+	th->source = sk->sport;
+	th->dest = req->rmt_port;
+	skb->seq = req->snt_isn;
+	skb->end_seq = skb->seq + 1;
+	th->seq = htonl(skb->seq);
+	th->ack_seq = htonl(req->rcv_isn + 1);
+	if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */
+		__u8 rcv_wscale; 
+		/* Set this up on the first call only */
+		req->window_clamp = skb->dst->window;
+		tcp_select_initial_window(sock_rspace(sk)/2,req->mss,
+			&req->rcv_wnd,
+			&req->window_clamp,
+			req->wscale_ok,
+			&rcv_wscale);
+		req->rcv_wscale = rcv_wscale; 
+	}
+	th->window = htons(req->rcv_wnd);
+
+	skb->when = jiffies;
+	tcp_syn_build_options((__u32 *)(th + 1), req->mss, req->tstamp_ok,
+			      req->sack_ok, req->wscale_ok, req->rcv_wscale,
+			      skb->when);
 
-	th->source = sk->dummy_th.source;
-	th->dest = sk->dummy_th.dest;
-	       
-	skb->seq = tp->snd_una;
-	skb->end_seq = skb->seq + 1 /* th->syn */ ;
-	th->seq = ntohl(skb->seq);
+	skb->csum = 0;
+	th->doff = (tcp_header_size >> 2);
+	tcp_statistics.TcpOutSegs++;
+	return skb;
+}
 
-	/* This is a resend of a previous SYN, now with an ACK.
-	 * we must reuse the previously offered window.
-	 */
-	th->window = htons(tp->rcv_wnd);
+void tcp_connect(struct sock *sk, struct sk_buff *buff, int mss)
+{
+	struct dst_entry *dst = sk->dst_cache;
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
-	tp->last_ack_sent = th->ack_seq = htonl(tp->rcv_nxt);
+	/* Reserve space for headers. */
+	skb_reserve(buff, MAX_HEADER + sk->prot->max_header);
 
-	tmp = tcp_syn_build_options(skb, sk->mss,
-		tp->tstamp_ok, tp->wscale_ok, tp->rcv_wscale);
-	skb->csum = 0;
-	th->doff = (sizeof(*th) + tmp)>>2;
+	if (sk->priority == 0)
+		sk->priority = dst->priority;
 
-	tp->af_specific->send_check(sk, th, sizeof(*th)+tmp, skb);
+	tp->snd_wnd = 0;
+	tp->snd_wl1 = 0;
+	tp->snd_wl2 = tp->write_seq;
+	tp->snd_una = tp->write_seq;
+	tp->rcv_nxt = 0;
 
-	skb_queue_tail(&sk->write_queue, skb);
+	sk->err = 0;
 	
-	buff = skb_clone(skb, GFP_ATOMIC);
-	if (buff) {
-		skb_set_owner_w(buff, sk);
+	/* We'll fix this up when we get a response from the other end.
+	 * See tcp_input.c:tcp_rcv_state_process case TCP_SYN_SENT.
+	 */
+	tp->tcp_header_len = sizeof(struct tcphdr) +
+		(sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);
 
-		tp->packets_out++;
-		skb->when = jiffies;
+	mss -= tp->tcp_header_len;
 
-		tp->af_specific->queue_xmit(buff);
-		tcp_statistics.TcpOutSegs++;
+	if (sk->user_mss)
+		mss = min(mss, sk->user_mss);
 
-		tcp_reset_xmit_timer(sk, TIME_RETRANS, TCP_TIMEOUT_INIT);
+	if (mss < 1) {
+		printk(KERN_DEBUG "intial sk->mss below 1\n");
+		mss = 1;	/* Sanity limit */
 	}
-	return 0;
+
+	sk->mss = mss;
+
+	TCP_SKB_CB(buff)->flags = TCPCB_FLAG_SYN;
+	TCP_SKB_CB(buff)->sacked = 0;
+	TCP_SKB_CB(buff)->urg_ptr = 0;
+	buff->csum = 0;
+	buff->seq = tp->write_seq++;
+	buff->end_seq = tp->write_seq;
+	tp->snd_nxt = buff->end_seq;
+
+	tp->window_clamp = dst->window;
+	tcp_select_initial_window(sock_rspace(sk)/2,sk->mss,
+		&tp->rcv_wnd,
+		&tp->window_clamp,
+		sysctl_tcp_window_scaling,
+		&tp->rcv_wscale);
+
+	/* Ok, now lock the socket before we make it visible to
+	 * the incoming packet engine.
+	 */
+	lock_sock(sk);
+
+	/* Socket identity change complete, no longer
+	 * in TCP_CLOSE, so enter ourselves into the
+	 * hash tables.
+	 */
+	tcp_set_state(sk,TCP_SYN_SENT);
+	sk->prot->hash(sk);
+
+	tp->rto = dst->rtt;
+	tcp_init_xmit_timers(sk);
+	tp->retransmits = 0;
+
+	/* Send it off. */
+	skb_queue_tail(&sk->write_queue, buff);
+	buff->when = jiffies;
+	tp->packets_out++;
+	tcp_transmit_skb(sk, skb_clone(buff, GFP_KERNEL));
+	tcp_statistics.TcpActiveOpens++;
+
+	/* Timer for repeating the SYN until an answer. */
+	tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+
+	/* Now, it is safe to release the socket. */
+	release_sock(sk);
 }
 
-/*
- * Send out a delayed ack, the caller does the policy checking
+/* Send out a delayed ack, the caller does the policy checking
  * to see if we should even be here.  See tcp_input.c:tcp_ack_snd_check()
  * for details.
  */
-
 void tcp_send_delayed_ack(struct tcp_opt *tp, int max_timeout)
 {
 	unsigned long timeout;
@@ -799,169 +830,120 @@
 	timeout += jiffies;
 
 	/* Use new timeout only if there wasn't a older one earlier. */
-	if ((!tp->delack_timer.prev || !del_timer(&tp->delack_timer)) ||
-	    (timeout < tp->delack_timer.expires))
+	if (!tp->delack_timer.prev) {
 		tp->delack_timer.expires = timeout;
-
-	add_timer(&tp->delack_timer);
+		add_timer(&tp->delack_timer);
+        } else {
+		if (timeout < tp->delack_timer.expires)
+			mod_timer(&tp->delack_timer, timeout);
+	}
 }
 
-
-
-/*
- *	This routine sends an ack and also updates the window. 
- */
- 
+/* This routine sends an ack and also updates the window. */
 void tcp_send_ack(struct sock *sk)
 {
-	struct sk_buff *buff;
-	struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
-	struct tcphdr *th;
-	int tmp;
-
-	if(sk->zapped)
-		return;	/* We have been reset, we may not send again. */
+	/* If we have been reset, we may not send again. */
+	if(!sk->zapped) {
+		struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+		struct sk_buff *buff;
 
-	/* We need to grab some memory, and put together an ack,
-	 * and then put it into the queue to be sent.
-	 */
-	buff = sock_wmalloc(sk, BASE_ACK_SIZE + tp->tcp_header_len, 1, GFP_ATOMIC);
-	if (buff == NULL) {
-		/*	Force it to send an ack. We don't have to do this
-		 *	(ACK is unreliable) but it's much better use of
-		 *	bandwidth on slow links to send a spare ack than
-		 *	resend packets.
+		/* We are not putting this on the write queue, so
+		 * tcp_transmit_skb() will set the ownership to this
+		 * sock.
 		 */
-		tcp_send_delayed_ack(tp, HZ/2);
-		return;
-	}
-
-	clear_delayed_acks(sk);
+		buff = alloc_skb(MAX_HEADER + sk->prot->max_header, GFP_ATOMIC);
+		if (buff == NULL) {
+			/* Force it to send an ack. We don't have to do this
+			 * (ACK is unreliable) but it's much better use of
+			 * bandwidth on slow links to send a spare ack than
+			 * resend packets.
+			 */
+			tcp_send_delayed_ack(tp, HZ/2);
+			return;
+		}
 
-	/* Assemble a suitable TCP frame. */
-	buff->csum = 0;
+		/* Reserve space for headers and prepare control bits. */
+		skb_reserve(buff, MAX_HEADER + sk->prot->max_header);
+		buff->csum = 0;
+		TCP_SKB_CB(buff)->flags = TCPCB_FLAG_ACK;
+		TCP_SKB_CB(buff)->sacked = 0;
+		TCP_SKB_CB(buff)->urg_ptr = 0;
 
-	/* Put in the IP header and routing stuff. */
-	tmp = tp->af_specific->build_net_header(sk, buff);
-	if (tmp < 0) {
-		kfree_skb(buff);
-		return;
-	}
-
-	th = (struct tcphdr *)skb_put(buff,tp->tcp_header_len);
-	memcpy(th, &sk->dummy_th, sizeof(struct tcphdr));
-
-	/* Swap the send and the receive. */
-	th->window	= ntohs(tcp_select_window(sk));
-	th->seq		= ntohl(tp->snd_nxt);
-	tp->last_ack_sent = tp->rcv_nxt;
-	th->ack_seq	= htonl(tp->rcv_nxt);
-	tcp_build_and_update_options((__u32 *)(th + 1), tp);
-
-  	/* Fill in the packet and send it. */
-	tp->af_specific->send_check(sk, th, tp->tcp_header_len, buff);
-	tp->af_specific->queue_xmit(buff);
-  	tcp_statistics.TcpOutSegs++;
+		/* Send it off, this clears delayed acks for us. */
+		buff->seq = buff->end_seq = tp->snd_nxt;
+		buff->when = jiffies;
+		tcp_transmit_skb(sk, buff);
+	}
 }
 
-/*
- *	This routine sends a packet with an out of date sequence
- *	number. It assumes the other end will try to ack it.
+/* This routine sends a packet with an out of date sequence
+ * number. It assumes the other end will try to ack it.
  */
-
 void tcp_write_wakeup(struct sock *sk)
 {
-	struct sk_buff *buff, *skb;
-	struct tcphdr *t1;
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-	int tmp;
-
-	if (sk->zapped)
-		return;	/* After a valid reset we can send no more. */
-
-	/*	Write data can still be transmitted/retransmitted in the
-	 *	following states.  If any other state is encountered, return.
-	 *	[listen/close will never occur here anyway]
-	 */
-	if ((1 << sk->state) &
-	    ~(TCPF_ESTABLISHED|TCPF_CLOSE_WAIT|TCPF_FIN_WAIT1|TCPF_LAST_ACK|TCPF_CLOSING))
-		return;
-
-	if (before(tp->snd_nxt, tp->snd_una + tp->snd_wnd) && (skb=tp->send_head)) {
-		struct tcphdr *th;
-		unsigned long win_size;
-
-		/* We are probing the opening of a window
-	    	 * but the window size is != 0
-	    	 * must have been a result SWS avoidance ( sender )
-	    	 */
-		win_size = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
-		if (win_size < skb->end_seq - skb->seq) {
-			if (tcp_fragment(sk, skb, win_size)) {
-				printk(KERN_DEBUG "tcp_write_wakeup: "
-				       "fragment failed\n");
-				return;
-			}
-		}
-
-		th = skb->h.th;
-		tcp_update_options((__u32 *)(th + 1), tp);
-		tp->af_specific->send_check(sk, th, th->doff * 4 + win_size, skb);
-		buff = skb_clone(skb, GFP_ATOMIC);
-		if (buff == NULL)
-			return;
-
-		skb_set_owner_w(buff, sk);
-		tp->packets_out++;
-
-		clear_delayed_acks(sk);
-
-		if (!tcp_timer_is_set(sk, TIME_RETRANS))
-			tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
-
-		skb->when = jiffies;
-		update_send_head(sk);
-		tp->snd_nxt = skb->end_seq;
-	} else {
-		buff = sock_wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC);
-		if (buff == NULL) 
+	/* After a valid reset we can send no more. */
+	if (!sk->zapped) {
+		struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+		struct sk_buff *skb;
+
+		/* Write data can still be transmitted/retransmitted in the
+		 * following states.  If any other state is encountered, return.
+		 * [listen/close will never occur here anyway]
+		 */
+		if ((1 << sk->state) &
+		    ~(TCPF_ESTABLISHED|TCPF_CLOSE_WAIT|TCPF_FIN_WAIT1|
+		      TCPF_LAST_ACK|TCPF_CLOSING))
 			return;
 
-		buff->csum = 0;
+		if (before(tp->snd_nxt, tp->snd_una + tp->snd_wnd) &&
+		    ((skb = tp->send_head) != NULL)) {
+			unsigned long win_size;
+
+			/* We are probing the opening of a window
+			 * but the window size is != 0
+			 * must have been a result SWS avoidance ( sender )
+			 */
+			win_size = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
+			if (win_size < skb->end_seq - skb->seq) {
+				if (tcp_fragment(sk, skb, win_size))
+					return; /* Let a retransmit get it. */
+			}
+			update_send_head(sk);
+			skb->when = jiffies;
+			tp->snd_nxt = skb->end_seq;
+			tp->packets_out++;
+			tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC));
+			if (!tcp_timer_is_set(sk, TIME_RETRANS))
+				tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+		} else {
+			/* We don't queue it, tcp_transmit_skb() sets ownership. */
+			skb = alloc_skb(MAX_HEADER + sk->prot->max_header,
+					GFP_ATOMIC);
+			if (skb == NULL) 
+				return;
 
-		/* Put in the IP header and routing stuff. */
-		tmp = tp->af_specific->build_net_header(sk, buff);
-		if (tmp < 0) {
-			kfree_skb(buff);
-			return;
+			/* Reserve space for headers and set control bits. */
+			skb_reserve(skb, MAX_HEADER + sk->prot->max_header);
+			skb->csum = 0;
+			TCP_SKB_CB(skb)->flags = TCPCB_FLAG_ACK;
+			TCP_SKB_CB(skb)->sacked = 0;
+			TCP_SKB_CB(skb)->urg_ptr = 0;
+
+			/* Use a previous sequence.  This should cause the other
+			 * end to send an ack.  Don't queue or clone SKB, just
+			 * send it.
+			 */
+			skb->seq = tp->snd_nxt - 1;
+			skb->end_seq = skb->seq;
+			skb->when = jiffies;
+			tcp_transmit_skb(sk, skb);
 		}
-
-		t1 = (struct tcphdr *) skb_put(buff, tp->tcp_header_len);
-		memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1));
-
-		/*	Use a previous sequence.
-		 *	This should cause the other end to send an ack.
-		 */
-	 
-		t1->seq = htonl(tp->snd_nxt-1);
-		t1->ack_seq = htonl(tp->rcv_nxt);
-		t1->window = htons(tcp_select_window(sk));
-		tcp_build_and_update_options((__u32 *)(t1 + 1), tp);
-
-		tp->af_specific->send_check(sk, t1, tp->tcp_header_len, buff);
 	}
-
-	/* Send it. */
-	tp->af_specific->queue_xmit(buff);
-	tcp_statistics.TcpOutSegs++;
 }
 
-/*
- *	A window probe timeout has occurred.
- *	If window is not closed send a partial packet
- *	else a zero probe.
+/* A window probe timeout has occurred.  If window is not closed send
+ * a partial packet else a zero probe.
  */
-
 void tcp_send_probe0(struct sock *sk)
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.1.90/linux/net/ipv4/tcp_timer.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/tcp_timer.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_timer.c,v 1.39 1998/03/13 08:02:17 davem Exp $
+ * Version:	$Id: tcp_timer.c,v 1.43 1998/03/22 22:10:28 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -77,11 +77,6 @@
 {
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 
-	if((long)when <= 0) {
-		printk(KERN_DEBUG "xmit_timer <= 0 - timer:%d when:%lx\n", what, when);
-		when=HZ/50;
-	}
-
 	switch (what) {
 	case TIME_RETRANS:
 		/* When seting the transmit timer the probe timer 
@@ -91,24 +86,15 @@
 		 */
 		if(tp->probe_timer.prev)
 			del_timer(&tp->probe_timer);
-		if(tp->retransmit_timer.prev)
-			del_timer(&tp->retransmit_timer);
-		tp->retransmit_timer.expires=jiffies+when;
-		add_timer(&tp->retransmit_timer);
+		mod_timer(&tp->retransmit_timer, jiffies+when);
 		break;
 
 	case TIME_DACK:
-		if(tp->delack_timer.prev)
-			del_timer(&tp->delack_timer);
-		tp->delack_timer.expires=jiffies+when;
-		add_timer(&tp->delack_timer);
+		mod_timer(&tp->delack_timer, jiffies+when);
 		break;
 
 	case TIME_PROBE0:
-		if(tp->probe_timer.prev)
-			del_timer(&tp->probe_timer);
-		tp->probe_timer.expires=jiffies+when;
-		add_timer(&tp->probe_timer);
+		mod_timer(&tp->probe_timer, jiffies+when);
 		break;	
 
 	case TIME_WRITE:
@@ -150,17 +136,12 @@
 	return 1;
 }
 
-/*
- *	A write timeout has occurred. Process the after effects. BROKEN (badly)
- */
-
+/* A write timeout has occurred. Process the after effects. */
 static int tcp_write_timeout(struct sock *sk)
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
-	/*
-	 *	Look for a 'soft' timeout.
-	 */
+	/* Look for a 'soft' timeout. */
 	if ((sk->state == TCP_ESTABLISHED &&
 	     tp->retransmits && (tp->retransmits % TCP_QUICK_TRIES) == 0) ||
 	    (sk->state != TCP_ESTABLISHED && tp->retransmits > sysctl_tcp_retries1)) {
@@ -206,11 +187,10 @@
 		return;
 	}
 
-	/*
-	 *	*WARNING* RFC 1122 forbids this 
-	 * 	It doesn't AFAIK, because we kill the retransmit timer -AK
-	 *	FIXME: We ought not to do it, Solaris 2.5 actually has fixing
-	 *	this behaviour in Solaris down as a bug fix. [AC]
+	/* *WARNING* RFC 1122 forbids this 
+	 * It doesn't AFAIK, because we kill the retransmit timer -AK
+	 * FIXME: We ought not to do it, Solaris 2.5 actually has fixing
+	 * this behaviour in Solaris down as a bug fix. [AC]
 	 */
 	if (tp->probes_out > sysctl_tcp_retries2) {
 		if(sk->err_soft)
@@ -226,9 +206,10 @@
 			/* Clean up time. */
 			tcp_set_state(sk, TCP_CLOSE);
 		}
+	} else {
+		/* Only send another probe if we didn't close things up. */
+		tcp_send_probe0(sk);
 	}
-	
-	tcp_send_probe0(sk);
 }
 
 static __inline__ int tcp_keepopen_proc(struct sock *sk)
@@ -375,6 +356,21 @@
 	/* Clear delay ack timer. */
 	tcp_clear_xmit_timer(sk, TIME_DACK);
 
+	/* RFC 2018, clear all 'sacked' flags in retransmission queue,
+	 * the sender may have dropped out of order frames and we must
+	 * send them out should this timer fire on us.
+	 */
+	if(tp->sack_ok) {
+		struct sk_buff *skb = skb_peek(&sk->write_queue);
+
+		while((skb != NULL) &&
+		      (skb != tp->send_head) &&
+		      (skb != (struct sk_buff *)&sk->write_queue)) {
+			TCP_SKB_CB(skb)->sacked = 0;
+			skb = skb->next;
+		}
+	}
+
 	/* Retransmission. */
 	tp->retrans_head = NULL;
 	if (tp->retransmits == 0) {
@@ -390,7 +386,7 @@
 
 	tp->dup_acks = 0;
 	tp->high_seq = tp->snd_nxt;
-	tcp_do_retransmit(sk, 0);
+	tcp_retransmit_skb(sk, skb_peek(&sk->write_queue));
 
 	/* Increase the timeout each time we retransmit.  Note that
 	 * we do not increase the rtt estimate.  rto is initialized
@@ -407,7 +403,7 @@
 	 * implemented ftp to mars will work nicely. We will have to fix
 	 * the 120 second clamps though!
 	 */
-	tp->backoff++;	/* FIXME: always same as retransmits? -- erics */
+	tp->backoff++;
 	tp->rto = min(tp->rto << 1, 120*HZ);
 	tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 
@@ -523,18 +519,18 @@
 void __tcp_inc_slow_timer(struct tcp_sl_timer *slt)
 {
 	unsigned long now = jiffies;
-	unsigned long next = 0;
 	unsigned long when;
 
 	slt->last = now;
-		
+
 	when = now + slt->period;
-	if (del_timer(&tcp_slow_timer))
-		next = tcp_slow_timer.expires;
 
-	if (next && ((long)(next - when) < 0))
-		when = next;
-		
-	tcp_slow_timer.expires = when;
-	add_timer(&tcp_slow_timer);
+	if (tcp_slow_timer.prev) {
+		if ((long)(tcp_slow_timer.expires - when) >= 0) {
+			mod_timer(&tcp_slow_timer, when);
+		}
+	} else {
+		tcp_slow_timer.expires = when;
+		add_timer(&tcp_slow_timer);
+	}
 }
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/timer.c linux/net/ipv4/timer.c
--- v2.1.90/linux/net/ipv4/timer.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/timer.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *
  *		TIMER - implementation of software timers for IP.
  *
- * Version:	$Id: timer.c,v 1.10 1998/03/13 08:02:18 davem Exp $
+ * Version:	$Id: timer.c,v 1.11 1998/03/19 08:34:06 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -59,10 +59,8 @@
 
 void net_reset_timer (struct sock *t, int timeout, unsigned long len)
 {
-	net_delete_timer (t);
 	t->timeout = timeout;
-	t->timer.expires = jiffies+len;
-	add_timer (&t->timer);
+	mod_timer(&t->timer, jiffies+len);
 }
 
 /* Now we will only be called whenever we need to do
diff -u --recursive --new-file v2.1.90/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.1.90/linux/net/ipv4/udp.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/udp.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *
  *		The User Datagram Protocol (UDP).
  *
- * Version:	$Id: udp.c,v 1.53 1998/03/12 03:20:00 davem Exp $
+ * Version:	$Id: udp.c,v 1.55 1998/03/21 07:28:01 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -315,8 +315,8 @@
 					continue;
 				score++;
 			}
-			if(sk->dummy_th.dest) {
-				if(sk->dummy_th.dest != sport)
+			if(sk->dport) {
+				if(sk->dport != sport)
 					continue;
 				score++;
 			}
@@ -412,8 +412,8 @@
 					continue;
 				score++;
 			}
-			if(s->dummy_th.dest) {
-				if(s->dummy_th.dest != rnum)
+			if(s->dport) {
+				if(s->dport != rnum)
 					continue;
 				score++;
 			}
@@ -453,7 +453,7 @@
 		if ((s->num != hnum)					||
 		    (s->dead && (s->state == TCP_CLOSE))		||
 		    (s->daddr && s->daddr!=raddr)			||
-		    (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) ||
+		    (s->dport != rnum && s->dport != 0) ||
 		    (s->rcv_saddr  && s->rcv_saddr != laddr))
 			continue;
 		break;
@@ -644,12 +644,12 @@
 		return -EOPNOTSUPP;
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
-	if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT|MSG_PROXY))
+	if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT|MSG_PROXY|MSG_NOSIGNAL))
 	  	return -EINVAL;
 	if ((msg->msg_flags&MSG_PROXY) && !suser() )
 	  	return -EPERM;
 #else
-	if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT))
+	if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT|MSG_NOSIGNAL))
 	  	return -EINVAL;
 #endif
 
@@ -686,7 +686,7 @@
 		if (sk->state != TCP_ESTABLISHED)
 			return -EINVAL;
 		ufh.daddr = sk->daddr;
-		ufh.uh.dest = sk->dummy_th.dest;
+		ufh.uh.dest = sk->dport;
 
 		/*
 		   BUGGG Khm... And who will validate it? Fixing it fastly...
@@ -712,7 +712,7 @@
 #endif
 	{
 		ipc.addr = sk->saddr;
-		ufh.uh.source = sk->dummy_th.source;
+		ufh.uh.source = sk->sport;
 	}
 
 	ipc.opt = NULL;
@@ -971,7 +971,7 @@
 	if(!sk->rcv_saddr)
 		sk->rcv_saddr = rt->rt_src;
 	sk->daddr = rt->rt_dst;
-	sk->dummy_th.dest = usin->sin_port;
+	sk->dport = usin->sin_port;
 	sk->state = TCP_ESTABLISHED;
 
 	if(uh_cache_sk == sk)
diff -u --recursive --new-file v2.1.90/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.1.90/linux/net/ipv6/addrconf.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv6/addrconf.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: addrconf.c,v 1.37 1998/03/08 20:52:46 davem Exp $
+ *	$Id: addrconf.c,v 1.38 1998/03/20 09:12:14 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -298,10 +298,9 @@
 	struct inet6_ifaddr *iter, **back;
 	int hash;
 
-	ipv6_ifa_notify(RTM_DELADDR, ifp);
-
 	if (atomic_read(&addr_list_lock)) {
 		ifp->flags |= ADDR_INVALID;
+		ipv6_ifa_notify(RTM_DELADDR, ifp);
 		return;
 	}
 
@@ -330,6 +329,8 @@
 		}
 		back = &(iter->if_next);
 	}
+
+	ipv6_ifa_notify(RTM_DELADDR, ifp);
 	
 	kfree(ifp);
 }
@@ -543,7 +544,7 @@
 
 static void
 addrconf_prefix_route(struct in6_addr *pfx, int plen, struct device *dev,
-		      unsigned long info)
+		      unsigned long expires, unsigned flags)
 {
 	struct in6_rtmsg rtmsg;
 	int err;
@@ -553,8 +554,8 @@
 	rtmsg.rtmsg_dst_len = plen;
 	rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
 	rtmsg.rtmsg_ifindex = dev->ifindex;
-	rtmsg.rtmsg_info = info;
-	rtmsg.rtmsg_flags = RTF_UP|RTF_ADDRCONF;
+	rtmsg.rtmsg_info = expires;
+	rtmsg.rtmsg_flags = RTF_UP|flags;
 	rtmsg.rtmsg_type = RTMSG_NEWROUTE;
 
 	/* Prevent useless cloning on PtP SIT.
@@ -608,7 +609,7 @@
 	struct in6_addr addr;
 
 	ipv6_addr_set(&addr,  __constant_htonl(0xFE800000), 0, 0, 0);
-	addrconf_prefix_route(&addr, 10, dev, 0);
+	addrconf_prefix_route(&addr, 10, dev, 0, RTF_ADDRCONF);
 }
 
 static struct inet6_dev *addrconf_add_dev(struct device *dev)
@@ -688,18 +689,20 @@
 	else
 		rt_expires = jiffies + valid_lft * HZ;
 
-	rt = rt6_lookup(&pinfo->prefix, NULL, dev, RTF_LINKRT);
+	rt = rt6_lookup(&pinfo->prefix, NULL, dev->ifindex, RTF_LINKRT);
 
 	if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
-		if (pinfo->onlink == 0 || valid_lft == 0) {
-			ip6_del_rt(rt);
-			rt = NULL;
-		} else {
-			rt->rt6i_expires = rt_expires;
+		if (rt->rt6i_flags&RTF_EXPIRES) {
+			if (pinfo->onlink == 0 || valid_lft == 0) {
+				ip6_del_rt(rt);
+				rt = NULL;
+			} else {
+				rt->rt6i_expires = rt_expires;
+			}
 		}
 	} else if (pinfo->onlink && valid_lft) {
 		addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
-				      dev, rt_expires);
+				      dev, rt_expires, RTF_ADDRCONF|RTF_EXPIRES);
 	}
 
 	/* Try to figure out our local address for this prefix */
@@ -1265,8 +1268,8 @@
 
 	addrconf_join_solict(dev, &ifp->addr);
 
-	if (ifp->prefix_len != 128)
-		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0);
+	if (ifp->prefix_len != 128 && (ifp->flags&ADDR_PERMANENT))
+		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0, RTF_ADDRCONF);
 
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
 		start_bh_atomic();
diff -u --recursive --new-file v2.1.90/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.1.90/linux/net/ipv6/af_inet6.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv6/af_inet6.c	Mon Mar 23 16:48:25 1998
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/af_inet.c
  *
- *	$Id: af_inet6.c,v 1.28 1998/03/08 05:56:49 davem Exp $
+ *	$Id: af_inet6.c,v 1.29 1998/03/18 07:52:11 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -75,7 +75,6 @@
 	if (sk == NULL) 
 		goto do_oom;
 
-	/* Note for tcp that also wiped the dummy_th block for us. */
 	if(sock->type == SOCK_STREAM || sock->type == SOCK_SEQPACKET) {
 		if (protocol && protocol != IPPROTO_TCP) 
 			goto free_and_noproto;
@@ -138,7 +137,7 @@
 		 * the user to assign a number at socket
 		 * creation time automatically shares.
 		 */
-		sk->dummy_th.source = ntohs(sk->num);
+		sk->sport = ntohs(sk->num);
 		sk->prot->hash(sk);
 		add_to_prot_sklist(sk);
 	}
@@ -229,8 +228,8 @@
 		return -EADDRINUSE;
 
 	sk->num = snum;
-	sk->dummy_th.source = ntohs(sk->num);
-	sk->dummy_th.dest = 0;
+	sk->sport = ntohs(sk->num);
+	sk->dport = 0;
 	sk->daddr = 0;
 	sk->prot->rehash(sk);
 	add_to_prot_sklist(sk);
@@ -259,7 +258,7 @@
 	if (peer) {
 		if (!tcp_connected(sk->state))
 			return(-ENOTCONN);
-		sin->sin6_port = sk->dummy_th.dest;
+		sin->sin6_port = sk->dport;
 		memcpy(&sin->sin6_addr, &sk->net_pinfo.af_inet6.daddr,
 		       sizeof(struct in6_addr));
 	} else {
@@ -272,7 +271,7 @@
 			       &sk->net_pinfo.af_inet6.rcv_saddr,
 			       sizeof(struct in6_addr));
 
-		sin->sin6_port = sk->dummy_th.source;
+		sin->sin6_port = sk->sport;
 	}
 	*uaddr_len = sizeof(*sin);	
 	return(0);
diff -u --recursive --new-file v2.1.90/linux/net/ipv6/datagram.c linux/net/ipv6/datagram.c
--- v2.1.90/linux/net/ipv6/datagram.c	Mon Jan 12 15:28:28 1998
+++ linux/net/ipv6/datagram.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: datagram.c,v 1.13 1997/12/13 21:53:09 kuznet Exp $
+ *	$Id: datagram.c,v 1.14 1998/03/20 09:12:15 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -55,7 +55,7 @@
 	return 0;
 }
 
-int datagram_send_ctl(struct msghdr *msg, struct device **src_dev,
+int datagram_send_ctl(struct msghdr *msg, int *oif,
 		      struct in6_addr **src_addr, struct ipv6_options *opt, 
 		      int *hlimit)
 {
@@ -81,15 +81,15 @@
 			src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
 			
 			if (src_info->ipi6_ifindex) {
-				int index = src_info->ipi6_ifindex;
-
-				*src_dev = dev_get_by_index(index);
+				if (*oif && src_info->ipi6_ifindex != *oif)
+					return -EINVAL;
+				*oif = src_info->ipi6_ifindex;
 			}
-			
+
 			if (!ipv6_addr_any(&src_info->ipi6_addr)) {
 				struct inet6_ifaddr *ifp;
 
-				ifp = ipv6_chk_addr(&src_info->ipi6_addr, *src_dev, 0);
+				ifp = ipv6_chk_addr(&src_info->ipi6_addr, NULL, 0);
 
 				if (ifp == NULL) {
 					err = -EINVAL;
diff -u --recursive --new-file v2.1.90/linux/net/ipv6/icmp.c linux/net/ipv6/icmp.c
--- v2.1.90/linux/net/ipv6/icmp.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv6/icmp.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: icmp.c,v 1.13 1998/02/12 07:43:41 davem Exp $
+ *	$Id: icmp.c,v 1.15 1998/03/21 07:28:03 davem Exp $
  *
  *	Based on net/ipv4/icmp.c
  *
@@ -153,7 +153,7 @@
 	struct ipv6hdr *hdr = skb->nh.ipv6h;
 	struct sock *sk = icmpv6_socket->sk;
 	struct in6_addr *saddr = NULL;
-	struct device *src_dev = NULL;
+	int iif = 0;
 	struct icmpv6_msg msg;
 	struct flowi fl;
 	int addr_type = 0;
@@ -203,7 +203,7 @@
 	 */
 
 	if (addr_type & IPV6_ADDR_LINKLOCAL)
-		src_dev = skb->dev;
+		iif = skb->dev->ifindex;
 
 	/*
 	 *	Must not send if we know that source is Anycast also.
@@ -251,12 +251,17 @@
 	fl.proto = IPPROTO_ICMPV6;
 	fl.nl_u.ip6_u.daddr = &hdr->saddr;
 	fl.nl_u.ip6_u.saddr = saddr;
-	fl.dev = src_dev;
+	fl.oif = iif;
 	fl.uli_u.icmpt.type = type;
 	fl.uli_u.icmpt.code = code;
 
 	ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
 		       MSG_DONTWAIT);
+
+	/* Oops! We must purge cached dst, otherwise
+	   all the following ICMP messages will go there :) --ANK
+	 */
+	dst_release(xchg(&sk->dst_cache, NULL));
 }
 
 static void icmpv6_echo_reply(struct sk_buff *skb)
@@ -294,12 +299,17 @@
 	fl.proto = IPPROTO_ICMPV6;
 	fl.nl_u.ip6_u.daddr = &hdr->saddr;
 	fl.nl_u.ip6_u.saddr = saddr;
-	fl.dev = skb->dev;
+	fl.oif = skb->dev->ifindex;
 	fl.uli_u.icmpt.type = ICMPV6_ECHO_REPLY;
 	fl.uli_u.icmpt.code = 0;
 
 	ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
 		       MSG_DONTWAIT);
+
+	/* Oops! We must purge cached dst, otherwise
+	   all the following ICMP messages will go there :) --ANK
+	 */
+	dst_release(xchg(&sk->dst_cache, NULL));
 }
 
 static __inline__ int ipv6_ext_hdr(u8 nexthdr)
@@ -317,7 +327,8 @@
 		 
 }
 
-static void icmpv6_notify(int type, int code, unsigned char *buff, int len,
+static void icmpv6_notify(struct sk_buff *skb,
+			  int type, int code, unsigned char *buff, int len,
 			  struct in6_addr *saddr, struct in6_addr *daddr, 
 			  struct inet6_protocol *protocol)
 {
@@ -367,7 +378,7 @@
 			continue;
 
 		if (ipprot->err_handler) 
-			ipprot->err_handler(type, code, pbuff, info,
+			ipprot->err_handler(skb, type, code, pbuff, info,
 					    saddr, daddr, ipprot);
 		return;
 	}
@@ -457,7 +468,7 @@
 	case ICMPV6_TIME_EXCEED:
 	case ICMPV6_PARAMPROB:
 
-		icmpv6_notify(hdr->icmp6_type, hdr->icmp6_code,
+		icmpv6_notify(skb, hdr->icmp6_type, hdr->icmp6_code,
 			      (char *) (hdr + 1), ulen,
 			      saddr, daddr, protocol);
 		break;
@@ -493,7 +504,7 @@
 		 * must pass to upper level 
 		 */
 
-		icmpv6_notify(hdr->icmp6_type, hdr->icmp6_code,
+		icmpv6_notify(skb, hdr->icmp6_type, hdr->icmp6_code,
 			      (char *) (hdr + 1), ulen,
 			      saddr, daddr, protocol);	
 	};
diff -u --recursive --new-file v2.1.90/linux/net/ipv6/ip6_fib.c linux/net/ipv6/ip6_fib.c
--- v2.1.90/linux/net/ipv6/ip6_fib.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv6/ip6_fib.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_fib.c,v 1.11 1998/03/08 05:56:50 davem Exp $
+ *	$Id: ip6_fib.c,v 1.12 1998/03/20 09:12:16 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -418,9 +418,13 @@
 			    (iter->rt6i_flowr == rt->rt6i_flowr) &&
 			    (ipv6_addr_cmp(&iter->rt6i_gateway,
 					   &rt->rt6i_gateway) == 0)) {
-				if (rt->rt6i_expires == 0 ||
-				    (long)(rt->rt6i_expires - iter->rt6i_expires) > 0)
-					rt->rt6i_expires = iter->rt6i_expires;
+				if (!(iter->rt6i_flags&RTF_EXPIRES))
+					return -EEXIST;
+				iter->rt6i_expires = rt->rt6i_expires;
+				if (!(rt->rt6i_flags&RTF_EXPIRES)) {
+					iter->rt6i_flags &= ~RTF_EXPIRES;
+					iter->rt6i_expires = rt->rt6i_expires;
+				}
 				return -EEXIST;
 			}
 		}
@@ -931,7 +935,8 @@
 		 *	Seems, radix tree walking is absolutely broken,
 		 *	but we will try in any case --ANK
 		 */
-		if (rt->rt6i_expires && (long)(now - rt->rt6i_expires) < 0) {
+		if ((rt->rt6i_flags&RTF_EXPIRES) && rt->rt6i_expires
+		    && (long)(now - rt->rt6i_expires) > 0) {
 			struct rt6_info *old;
 
 			old = rt;
diff -u --recursive --new-file v2.1.90/linux/net/ipv6/ip6_output.c linux/net/ipv6/ip6_output.c
--- v2.1.90/linux/net/ipv6/ip6_output.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv6/ip6_output.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_output.c,v 1.9 1998/03/08 05:56:50 davem Exp $
+ *	$Id: ip6_output.c,v 1.10 1998/03/20 09:12:17 davem Exp $
  *
  *	Based on linux/net/ipv4/ip_output.c
  *
@@ -82,64 +82,43 @@
 
 /*
  *	xmit an sk_buff (used by TCP)
- *	sk can be NULL (for sending RESETs)
  */
 
 int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
 	     struct ipv6_options *opt)
 {
-	struct ipv6_pinfo *np = NULL;
-	struct dst_entry *dst = NULL;
+	struct ipv6_pinfo * np = sk ? &sk->net_pinfo.af_inet6 : NULL;
+	struct dst_entry *dst = skb->dst;
 	struct ipv6hdr *hdr;
 	int seg_len;
+	int hlimit;
 
-	hdr = skb->nh.ipv6h;
-
-	if (sk) {
-		np = &sk->net_pinfo.af_inet6;
+	/* Do something with IPv6 options headers here. */
 
-		if (sk->dst_cache) {
-			/*
-			 *	dst_check returns NULL if route is no longer valid
-			 */
-			dst = dst_check(&sk->dst_cache, np->dst_cookie);
-		}
-	}
+	seg_len = skb->len;
 
-	if (dst == NULL) {
-		dst = ip6_route_output(sk, fl);
-
-		if (dst->error) {
-			/*
-			 *	NETUNREACH usually
-			 */
-			dst_release(dst);
-			return dst->error;
-		}
-	}
-
-	skb->dst = dst_clone(dst);
-	seg_len = skb->tail - ((unsigned char *) hdr);
-	hdr = skb->nh.ipv6h;
+	hdr = skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, sizeof(struct ipv6hdr));
 
 	/*
 	 *	Fill in the IPv6 header
 	 */
 
 	hdr->version = 6;
-	hdr->priority = np ? np->priority : 0;
-
-	if (np)
+	if (np) {
+		hdr->priority = np->priority;
 		memcpy(hdr->flow_lbl, (void *) &np->flow_lbl, 3);
-	else
+		hlimit = np->hop_limit;
+	} else {
+		hdr->priority = 0;
 		memset(hdr->flow_lbl, 0, 3);
+		hlimit = -1;
+	}
+	if (hlimit < 0)
+		hlimit = ((struct rt6_info*)dst)->rt6i_hoplimit;
 
-	hdr->payload_len = htons(seg_len - sizeof(struct ipv6hdr));
+	hdr->payload_len = htons(seg_len);
 	hdr->nexthdr = fl->proto;
-	if (np == NULL || np->hop_limit < 0)
-		hdr->hop_limit = ((struct rt6_info*)dst)->rt6i_hoplimit;
-	else
-		hdr->hop_limit = np->hop_limit;
+	hdr->hop_limit = hlimit;
 
 	ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
 	ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);
@@ -147,12 +126,6 @@
 	ipv6_statistics.Ip6OutRequests++;
 	dst->output(skb);
 
-	if (sk) {
-		if (sk->dst_cache == NULL)
-			ip6_dst_store(sk, dst);
-	} else
-		dst_release(dst);
-
 	return 0;
 }
 
@@ -412,6 +385,9 @@
 	}
 
 	dst = NULL;
+
+	if (!fl->oif && ipv6_addr_is_multicast(fl->nl_u.ip6_u.daddr))
+		fl->oif = np->mcast_oif;
 	
 	if (sk->dst_cache)
 		dst = dst_check(&sk->dst_cache, np->dst_cookie);
diff -u --recursive --new-file v2.1.90/linux/net/ipv6/ipv6_sockglue.c linux/net/ipv6/ipv6_sockglue.c
--- v2.1.90/linux/net/ipv6/ipv6_sockglue.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv6/ipv6_sockglue.c	Mon Mar 23 16:48:25 1998
@@ -7,7 +7,7 @@
  *
  *	Based on linux/net/ipv4/ip_sockglue.c
  *
- *	$Id: ipv6_sockglue.c,v 1.17 1998/03/08 05:56:51 davem Exp $
+ *	$Id: ipv6_sockglue.c,v 1.18 1998/03/20 09:12:18 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -157,15 +157,13 @@
 
 	case IPV6_MULTICAST_IF:
 	{
+		int oif = 0;
 		struct in6_addr addr;
 
-		err = copy_from_user(&addr, optval, sizeof(struct in6_addr));
-		if(err)
+		if (copy_from_user(&addr, optval, sizeof(struct in6_addr)))
 			return -EFAULT;
 				
-		if (ipv6_addr_any(&addr)) {
-			np->oif = NULL;
-		} else {
+		if (!ipv6_addr_any(&addr)) {
 			struct inet6_ifaddr *ifp;
 
 			ifp = ipv6_chk_addr(&addr, NULL, 0);
@@ -175,8 +173,13 @@
 				break;
 			}
 
-			np->oif = ifp->idev->dev;
+			oif = ifp->idev->dev->ifindex;
+		}
+		if (sk->bound_dev_if && sk->bound_dev_if != oif) {
+			retv = -EINVAL;
+			break;
 		}
+		np->mcast_oif = oif;
 		retv = 0;
 		break;
 	}
diff -u --recursive --new-file v2.1.90/linux/net/ipv6/mcast.c linux/net/ipv6/mcast.c
--- v2.1.90/linux/net/ipv6/mcast.c	Mon Jan 12 15:28:28 1998
+++ linux/net/ipv6/mcast.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: mcast.c,v 1.13 1998/01/04 15:28:31 mj Exp $
+ *	$Id: mcast.c,v 1.14 1998/03/20 09:12:18 davem Exp $
  *
  *	Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c 
  *
@@ -91,7 +91,7 @@
 
 	if (ifindex == 0) {
 		struct rt6_info *rt;
-		rt = rt6_lookup(addr, NULL, NULL, 0);
+		rt = rt6_lookup(addr, NULL, 0, 0);
 		if (rt)
 			dev = rt->rt6i_dev;
 	} else
diff -u --recursive --new-file v2.1.90/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- v2.1.90/linux/net/ipv6/ndisc.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv6/ndisc.c	Mon Mar 23 16:48:25 1998
@@ -774,7 +774,7 @@
 	int hlen;
 
 	dev = skb->dev;
-	rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev, 0);
+	rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev->ifindex, 0);
 
 	if (rt == NULL || rt->u.dst.error) {
 		ND_PRINTK1("ndisc_send_redirect: hostunreach\n");
diff -u --recursive --new-file v2.1.90/linux/net/ipv6/proc.c linux/net/ipv6/proc.c
--- v2.1.90/linux/net/ipv6/proc.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv6/proc.c	Mon Mar 23 16:48:25 1998
@@ -7,7 +7,7 @@
  *		PROC file system.  This is very similar to the IPv4 version,
  *		except it reports the sockets in the INET6 address family.
  *
- * Version:	$Id: proc.c,v 1.6 1998/03/13 08:02:19 davem Exp $
+ * Version:	$Id: proc.c,v 1.7 1998/03/18 07:52:13 davem Exp $
  *
  * Authors:	David S. Miller (davem@caip.rutgers.edu)
  *
@@ -68,8 +68,8 @@
 			dest  = &sp->net_pinfo.af_inet6.daddr;
 			src   = &sp->net_pinfo.af_inet6.rcv_saddr;
 		}
-		destp = ntohs(sp->dummy_th.dest);
-		srcp  = ntohs(sp->dummy_th.source);
+		destp = ntohs(sp->dport);
+		srcp  = ntohs(sp->sport);
 		if((format == 0) && (sp->state == TCP_TIME_WAIT)) {
 			timer_active1	= timer_active2 = 0;
 			timer_active	= 3;
diff -u --recursive --new-file v2.1.90/linux/net/ipv6/raw.c linux/net/ipv6/raw.c
--- v2.1.90/linux/net/ipv6/raw.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv6/raw.c	Mon Mar 23 16:48:25 1998
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/raw.c
  *
- *	$Id: raw.c,v 1.18 1998/03/08 05:56:54 davem Exp $
+ *	$Id: raw.c,v 1.19 1998/03/20 09:12:20 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -349,7 +349,6 @@
 	struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name;
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct ipv6_options *opt = NULL;
-	struct device *dev = NULL;
 	struct in6_addr *saddr = NULL;
 	struct flowi fl;
 	int addr_len = msg->msg_namelen;
@@ -419,15 +418,15 @@
 		return(-EINVAL);
 	}
 
+	fl.oif = sk->bound_dev_if;
+
 	if (msg->msg_controllen) {
 		opt = &opt_space;
 		memset(opt, 0, sizeof(struct ipv6_options));
 
-		err = datagram_send_ctl(msg, &dev, &saddr, opt, &hlimit);
-		if (err < 0) {
-			printk(KERN_DEBUG "invalid msg_control\n");
+		err = datagram_send_ctl(msg, &fl.oif, &saddr, opt, &hlimit);
+		if (err < 0)
 			return err;
-		}		
 	}
 
 	raw_opt = &sk->tp_pinfo.tp_raw;
@@ -435,7 +434,6 @@
 	fl.proto = proto;
 	fl.nl_u.ip6_u.daddr = daddr;
 	fl.nl_u.ip6_u.saddr = saddr;
-	fl.dev = dev;
 	fl.uli_u.icmpt.type = 0;
 	fl.uli_u.icmpt.code = 0;
 	
diff -u --recursive --new-file v2.1.90/linux/net/ipv6/route.c linux/net/ipv6/route.c
--- v2.1.90/linux/net/ipv6/route.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv6/route.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: route.c,v 1.25 1998/03/15 03:31:47 davem Exp $
+ *	$Id: route.c,v 1.27 1998/03/21 07:28:04 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -36,6 +36,7 @@
 #include <net/ip6_route.h>
 #include <net/ndisc.h>
 #include <net/addrconf.h>
+#include <net/tcp.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 
@@ -60,6 +61,7 @@
 int ip6_rt_gc_min_interval = 5*HZ;
 int ip6_rt_gc_timeout = 60*HZ;
 int ip6_rt_gc_interval = 30*HZ;
+int ip6_rt_gc_elasticity = 9;
 
 static struct rt6_info * ip6_rt_copy(struct rt6_info *ort);
 static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
@@ -205,21 +207,20 @@
  */
 
 static __inline__ struct rt6_info *rt6_device_match(struct rt6_info *rt,
-						    struct device *dev,
+						    int oif,
 						    int strict)
 {
 	struct rt6_info *local = NULL;
 	struct rt6_info *sprt;
 
-	RDBG(("rt6_device_match: (%p,%p,%d) ", rt, dev, strict));
-	if (dev) {
+	if (oif) {
 		for (sprt = rt; sprt; sprt = sprt->u.next) {
-			if (sprt->rt6i_dev == dev) {
-				RDBG(("match --> %p\n", sprt));
-				return sprt;
+			if (sprt->rt6i_dev) {
+				if (sprt->rt6i_dev->ifindex == oif)
+					return sprt;
+				if (sprt->rt6i_dev->flags&IFF_LOOPBACK)
+					local = sprt;
 			}
-			if (sprt->rt6i_dev && (sprt->rt6i_dev->flags&IFF_LOOPBACK))
-				local = sprt;
 		}
 
 		if (local)
@@ -239,13 +240,12 @@
  */
 static struct rt6_info *rt6_dflt_pointer = NULL;
 
-static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, struct device *dev)
+static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, int oif)
 {
 	struct rt6_info *match = NULL;
 	struct rt6_info *sprt;
 	int mpri = 0;
 
-	RDBG(("rt6_best_dflt(%p,%p): ", rt, dev));
 	for (sprt = rt; sprt; sprt = sprt->u.next) {
 		struct neighbour *neigh;
 
@@ -278,8 +278,7 @@
 				break;
 			};
 
-			if (dev && sprt->rt6i_dev == dev) {
-				RDBG(("dev&&sprt->rt6i_dev==dev(%p), m+=2, ", dev));
+			if (oif && sprt->rt6i_dev && sprt->rt6i_dev->ifindex == oif) {
 				m += 2;
 			}
 
@@ -316,17 +315,14 @@
 }
 
 struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr,
-			    struct device *dev, int flags)
+			    int oif, int flags)
 {
 	struct fib6_node *fn;
 	struct rt6_info *rt;
 
-	RDBG(("rt6_lookup(%p,%p,%p,%x) from %p\n",
-	      daddr, saddr, dev, flags, __builtin_return_address(0)));
 	rt6_lock();
 	fn = fib6_lookup(&ip6_routing_table, daddr, saddr);
-
-	rt = rt6_device_match(fn->leaf, dev, flags&RTF_LINKRT);
+	rt = rt6_device_match(fn->leaf, oif, flags&RTF_LINKRT);
 	rt6_unlock();
 	return rt;
 }
@@ -414,7 +410,7 @@
 
 	if ((rt->rt6i_flags & RTF_CACHE)) {
 		if (ip6_rt_policy == 0) {
-			rt = rt6_device_match(rt, skb->dev, 0);
+			rt = rt6_device_match(rt, skb->dev->ifindex, 0);
 			goto out;
 		}
 
@@ -432,7 +428,7 @@
 #endif
 	}
 
-	rt = rt6_device_match(rt, skb->dev, 0);
+	rt = rt6_device_match(rt, skb->dev->ifindex, 0);
 
 	if (ip6_rt_policy == 0) {
 		if (!rt->rt6i_nexthop && rt->rt6i_dev &&
@@ -462,44 +458,19 @@
 	struct dst_entry *dst;
 	int strict;
 
-	RDBG(("ip6_route_output(%p,%p) from(%p)", sk, fl,
-	      __builtin_return_address(0)));
 	strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL);
 
 	rt6_lock();
-#if RT6_DEBUG >= 3
-	RDBG(("lkup("));
-	if(fl->nl_u.ip6_u.daddr) {
-		struct in6_addr *addr = fl->nl_u.ip6_u.daddr;
-		int i;
-		RDBG(("daddr["));
-		for(i = 0; i < 8; i++) {
-			RDBG(("%04x%c", addr->s6_addr16[i],
-			      i == 7 ? ']' : ':'));
-		}
-	}
-	if(fl->nl_u.ip6_u.saddr) {
-		struct in6_addr *addr = fl->nl_u.ip6_u.saddr;
-		int i;
-		RDBG(("saddr["));
-		for(i = 0; i < 8; i++) {
-			RDBG(("%04x%c", addr->s6_addr16[i],
-			      i == 7 ? ']' : ':'));
-		}
-	}
-#endif
 	fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr,
 			 fl->nl_u.ip6_u.saddr);
 
-	RDBG(("-->(%p[%s])) ", fn, fn == &ip6_routing_table ? "ROOT" : "!ROOT"));
-
 restart:
 	rt = fn->leaf;
 
 	if ((rt->rt6i_flags & RTF_CACHE)) {
 		RDBG(("RTF_CACHE "));
 		if (ip6_rt_policy == 0) {
-			rt = rt6_device_match(rt, fl->dev, strict);
+			rt = rt6_device_match(rt, fl->oif, strict);
 
 			/* BUGGGG! It is capital bug, that was hidden
 			   by not-cloning multicast routes. However,
@@ -536,11 +507,11 @@
 	if (rt->rt6i_flags & RTF_DEFAULT) {
 		RDBG(("RTF_DEFAULT "));
 		if (rt->rt6i_metric >= IP6_RT_PRIO_ADDRCONF) {
-			rt = rt6_best_dflt(rt, fl->dev);
+			rt = rt6_best_dflt(rt, fl->oif);
 			RDBG(("best_dflt(%p) ", rt));
 		}
 	} else {
-		rt = rt6_device_match(rt, fl->dev, strict);
+		rt = rt6_device_match(rt, fl->oif, strict);
 		RDBG(("!RTF_DEFAULT devmatch(%p) ", rt));
 	}
 
@@ -638,10 +609,10 @@
 	fib6_run_gc(expire);
 	last_gc = now;
 	if (atomic_read(&ip6_dst_ops.entries) < ip6_dst_ops.gc_thresh)
-		expire = ip6_rt_gc_timeout;
+		expire = ip6_rt_gc_timeout>>1;
 
 out:
-	expire >>= 1;
+	expire -= expire>>ip6_rt_gc_elasticity;
 	end_bh_atomic();
 	return (atomic_read(&ip6_dst_ops.entries) > ip6_rt_max_size);
 }
@@ -780,7 +751,7 @@
 				goto out;
 			}
 
-			grt = rt6_lookup(gw_addr, NULL, dev, RTF_LINKRT);
+			grt = rt6_lookup(gw_addr, NULL, dev->ifindex, RTF_LINKRT);
 
 			if (grt == NULL || (grt->rt6i_flags&RTF_GATEWAY)) {
 				*err = -EHOSTUNREACH;
@@ -814,6 +785,7 @@
 
 	rt->rt6i_dev = dev;
 	rt->u.dst.pmtu = ipv6_get_mtu(dev);
+	rt->u.dst.rtt = TCP_TIMEOUT_INIT;
 	if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr))
 		rt->rt6i_hoplimit = IPV6_DEFAULT_MCASTHOPS;
 	else
@@ -1078,7 +1050,7 @@
 	struct rt6_info *rt, *nrt;
 
 	/* Locate old route to this destination. */
-	rt = rt6_lookup(dest, NULL, dev, 0);
+	rt = rt6_lookup(dest, NULL, dev->ifindex, 0);
 
 	if (rt == NULL || rt->u.dst.error)
 		return NULL;
@@ -1200,7 +1172,7 @@
 		return;
 	}
 
-	rt = rt6_lookup(addr, NULL, dev, 0);
+	rt = rt6_lookup(addr, NULL, dev->ifindex, 0);
 
 	if (rt == NULL || rt->u.dst.error) {
 #if RT6_DEBUG >= 2
@@ -1268,6 +1240,9 @@
 		rt->u.dst.output = ort->u.dst.output;
 
 		rt->u.dst.pmtu = ort->u.dst.pmtu;
+		rt->u.dst.rtt = ort->u.dst.rtt;
+		rt->u.dst.window = ort->u.dst.window;
+		rt->u.dst.mxlock = ort->u.dst.mxlock;
 		rt->rt6i_hoplimit = ort->rt6i_hoplimit;
 		rt->rt6i_dev = ort->rt6i_dev;
 
@@ -1472,6 +1447,7 @@
 	rt->u.dst.input = ip6_input;
 	rt->u.dst.output = ip6_output;
 	rt->rt6i_dev = dev_get("lo");
+	rt->u.dst.rtt = TCP_TIMEOUT_INIT;
 	rt->u.dst.pmtu = ipv6_get_mtu(rt->rt6i_dev);
 	rt->rt6i_hoplimit = ipv6_get_hoplimit(rt->rt6i_dev);
 	rt->u.dst.obsolete = -1;
@@ -1501,7 +1477,7 @@
 {
 	struct rt6_info *rt;
 
-	rt = rt6_lookup(addr, NULL, dev_get("lo"), RTF_LINKRT);
+	rt = rt6_lookup(addr, NULL, loopback_dev.ifindex, RTF_LINKRT);
 	if (rt && rt->rt6i_dst.plen == 128)
 		return ip6_del_rt(rt);
 
@@ -1811,6 +1787,8 @@
 #else
 	mx = (struct rtattr*)skb->tail;
 	RTA_PUT(skb, RTA_METRICS, 0, NULL);
+	if (rt->u.dst.mxlock)
+		RTA_PUT(skb, RTAX_LOCK, sizeof(unsigned), &rt->u.dst.mxlock);
 	if (rt->u.dst.pmtu)
 		RTA_PUT(skb, RTAX_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
 	if (rt->u.dst.window)
@@ -2157,6 +2135,9 @@
          &proc_dointvec_jiffies},
 	{NET_IPV6_ROUTE_GC_INTERVAL, "gc_interval",
          &ip6_rt_gc_interval, sizeof(int), 0644, NULL,
+         &proc_dointvec_jiffies},
+	{NET_IPV6_ROUTE_GC_ELASTICITY, "gc_elasticity",
+         &ip6_rt_gc_elasticity, sizeof(int), 0644, NULL,
          &proc_dointvec_jiffies},
 	 {0}
 };
diff -u --recursive --new-file v2.1.90/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.1.90/linux/net/ipv6/tcp_ipv6.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv6/tcp_ipv6.c	Mon Mar 23 16:48:25 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.60 1998/03/15 02:59:32 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.68 1998/03/22 19:14:50 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -42,8 +42,6 @@
 
 #include <asm/uaccess.h>
 
-#define ICMP_PARANOIA
-
 extern int sysctl_tcp_timestamps;
 extern int sysctl_tcp_window_scaling;
 
@@ -52,7 +50,6 @@
 				  struct sk_buff *skb);
 
 static int	tcp_v6_backlog_rcv(struct sock *sk, struct sk_buff *skb);
-static int	tcp_v6_build_header(struct sock *sk, struct sk_buff *skb);
 static void	tcp_v6_xmit(struct sk_buff *skb);
 static struct open_request *tcp_v6_search_req(struct tcp_opt *tp,
 					      struct ipv6hdr *ip6h,
@@ -79,7 +76,7 @@
 	struct in6_addr *laddr = &sk->net_pinfo.af_inet6.rcv_saddr;
 	struct in6_addr *faddr = &sk->net_pinfo.af_inet6.daddr;
 	__u16 lport = sk->num;
-	__u16 fport = sk->dummy_th.dest;
+	__u16 fport = sk->dport;
 	return tcp_v6_hashfn(laddr, lport, faddr, fport);
 }
 
@@ -113,12 +110,14 @@
 
 			/* We must walk the whole port owner list in this case. -DaveM */
 			for(sk2 = tb->owners; sk2; sk2 = sk2->bind_next) {
-				if(!sk_reuse || !sk2->reuse || sk2->state == TCP_LISTEN) {
-					if(addr_type == IPV6_ADDR_ANY	||
-					   !sk2->rcv_saddr		||
-					   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
-							  &sk2->net_pinfo.af_inet6.rcv_saddr))
-						break;
+				if(sk->bound_dev_if == sk2->bound_dev_if) {
+					if(!sk_reuse || !sk2->reuse || sk2->state == TCP_LISTEN) {
+						if(addr_type == IPV6_ADDR_ANY	||
+						   !sk2->rcv_saddr		||
+						   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+								  &sk2->net_pinfo.af_inet6.rcv_saddr))
+							break;
+					}
 				}
 			}
 			if(sk2 != NULL)
@@ -195,20 +194,35 @@
 	SOCKHASH_UNLOCK();
 }
 
-static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned short hnum)
+static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned short hnum, int dif)
 {
 	struct sock *sk;
 	struct sock *result = NULL;
+	int score, hiscore;
 
+	hiscore=0;
 	sk = tcp_listening_hash[tcp_lhashfn(hnum)];
 	for(; sk; sk = sk->next) {
 		if((sk->num == hnum) && (sk->family == AF_INET6)) {
 			struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+			
+			score = 1;
 			if(!ipv6_addr_any(&np->rcv_saddr)) {
-				if(!ipv6_addr_cmp(&np->rcv_saddr, daddr))
-					return sk; /* Best possible match. */
-			} else if(!result)
+				if(ipv6_addr_cmp(&np->rcv_saddr, daddr))
+					continue;
+				score++;
+			}
+			if (sk->bound_dev_if) {
+				if (sk->bound_dev_if != dif)
+					continue;
+				score++;
+			}
+			if (score == 3)
+				return sk;
+			if (score > hiscore) {
+				hiscore = score;
 				result = sk;
+			}
 		}
 	}
 	return result;
@@ -223,7 +237,8 @@
  */
 static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
 					   struct in6_addr *saddr, u16 sport,
-					   struct in6_addr *daddr, u16 dport)
+					   struct in6_addr *daddr, u16 dport,
+					   int dif)
 {
 	unsigned short hnum = ntohs(dport);
 	struct sock *sk;
@@ -240,9 +255,10 @@
 	if(sk						&&
 	   sk->num		== hnum			&& /* local port     */
 	   sk->family		== AF_INET6		&& /* address family */
-	   sk->dummy_th.dest	== sport		&& /* remote port    */
+	   sk->dport		== sport		&& /* remote port    */
 	   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr)	&&
-	   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
+	   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr) &&
+	   (!sk->bound_dev_if || sk->bound_dev_if == dif))
 		goto hit;
 
 	/* Optimize here for direct hit, only listening connections can
@@ -253,9 +269,10 @@
 		/* For IPV6 do the cheaper port and family tests first. */
 		if(sk->num		== hnum			&& /* local port     */
 		   sk->family		== AF_INET6		&& /* address family */
-		   sk->dummy_th.dest	== sport		&& /* remote port    */
+		   sk->dport		== sport		&& /* remote port    */
 		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr)	&&
-		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr)) {
+		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr) &&
+		   (!sk->bound_dev_if || sk->bound_dev_if == dif)) {
 			if (sk->state == TCP_ESTABLISHED)
 				TCP_RHASH(sport) = sk;
 			goto hit; /* You sunk my battleship! */
@@ -265,21 +282,22 @@
 	for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next)
 		if(sk->num		== hnum			&& /* local port     */
 		   sk->family		== AF_INET6		&& /* address family */
-		   sk->dummy_th.dest	== sport) {		   /* remote port    */
+		   sk->dport		== sport) {		   /* remote port    */
 			struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk;
 			if(!ipv6_addr_cmp(&tw->v6_daddr, saddr)	&&
-			   !ipv6_addr_cmp(&tw->v6_rcv_saddr, daddr))
+			   !ipv6_addr_cmp(&tw->v6_rcv_saddr, daddr) &&
+			   (!sk->bound_dev_if || sk->bound_dev_if == dif))
 				goto hit;
 		}
 #ifdef USE_QUICKSYNS
 listener_shortcut:
 #endif
-	sk = tcp_v6_lookup_listener(daddr, hnum);
+	sk = tcp_v6_lookup_listener(daddr, hnum, dif);
 hit:
 	return sk;
 }
 
-#define tcp_v6_lookup(sa, sp, da, dp) __tcp_v6_lookup((0),(sa),(sp),(da),(dp))
+#define tcp_v6_lookup(sa, sp, da, dp, dif) __tcp_v6_lookup((0),(sa),(sp),(da),(dp),(dif))
 
 static __inline__ u16 tcp_v6_check(struct tcphdr *th, int len,
 				   struct in6_addr *saddr, 
@@ -323,8 +341,9 @@
 			 * use passive ftp, I just cover this case for completeness)
 			 */
 			sk = __tcp_v6_lookup(NULL, &sk->net_pinfo.af_inet6.daddr,
-					     sk->dummy_th.dest,
-					     &sk->net_pinfo.af_inet6.rcv_saddr, snum);
+					     sk->dport,
+					     &sk->net_pinfo.af_inet6.rcv_saddr, snum,
+					     sk->bound_dev_if);
 			if((sk != NULL) && (sk->state != TCP_LISTEN))
 				retval = 0;
 			break;
@@ -344,11 +363,9 @@
 	struct in6_addr *saddr = NULL;
 	struct flowi fl;
 	struct dst_entry *dst;
-	struct tcphdr *th;
 	struct sk_buff *buff;
-	struct sk_buff *skb1;
-	int tmp;
 	int addr_type;
+	int mss;
 
 	if (sk->state != TCP_CLOSE) 
 		return(-EISCONN);
@@ -383,7 +400,7 @@
 	 */
 
 	if (ipv6_addr_cmp(&usin->sin6_addr, &np->saddr) == 0 &&
-	    usin->sin6_port == sk->dummy_th.source)
+	    usin->sin6_port == sk->sport)
 		return (-EINVAL);
 
 	memcpy(&np->daddr, &usin->sin6_addr, sizeof(struct in6_addr));
@@ -421,9 +438,9 @@
 	fl.proto = IPPROTO_TCP;
 	fl.nl_u.ip6_u.daddr = &np->daddr;
 	fl.nl_u.ip6_u.saddr = saddr;
-	fl.dev = NULL;
+	fl.oif = sk->bound_dev_if;
 	fl.uli_u.ports.dport = usin->sin6_port;
-	fl.uli_u.ports.sport = sk->dummy_th.source;
+	fl.uli_u.ports.sport = sk->sport;
 
 	dst = ip6_route_output(sk, &fl);
 	
@@ -431,11 +448,23 @@
 		dst_release(dst);
 		return dst->error;
 	}
-	
+
+	if (dst->pmtu < 576) {
+		dst_release(dst);
+		return -EINVAL;
+	}
+
+	if (fl.oif == 0 && addr_type&IPV6_ADDR_LINKLOCAL) {
+		/* Ough! This guy tries to connect to link local
+		 * address and did not specify interface.
+		 * Actually we should kick him out, but
+		 * we will be patient :) --ANK
+		 */
+		sk->bound_dev_if = dst->dev->ifindex;
+	}
+
 	ip6_dst_store(sk, dst);
 
-	np->oif = dst->dev;
-	
 	if (saddr == NULL) {
 		ifa = ipv6_get_saddr(dst, &np->daddr);
 	
@@ -449,117 +478,38 @@
 		ipv6_addr_copy(&np->saddr, saddr);
 	}
 
-	sk->dummy_th.dest = usin->sin6_port;
-	if (!tcp_v6_unique_address(sk))
+	buff = sock_wmalloc(sk, (MAX_HEADER + sk->prot->max_header),
+			    0, GFP_KERNEL);
+
+	if (buff == NULL)
+		return -ENOBUFS;
+
+	sk->dport = usin->sin6_port;
+
+	if (!tcp_v6_unique_address(sk)) {
+		kfree_skb(buff);
 		return -EADDRNOTAVAIL;
+	}
 
 	/*
 	 *	Init variables
 	 */
 
-	lock_sock(sk);
-
 	tp->write_seq = secure_tcp_sequence_number(np->saddr.s6_addr32[3],
 						   np->daddr.s6_addr32[3],
-						   sk->dummy_th.source,
-						   sk->dummy_th.dest);
-
-	tp->snd_wnd = 0;
-	tp->snd_wl1 = 0;
-	tp->snd_wl2 = tp->write_seq;
-	tp->snd_una = tp->write_seq;
-
-	tp->rcv_nxt = 0;
-
-	sk->err = 0;
-
-	release_sock(sk);
-
-	buff = sock_wmalloc(sk, (MAX_SYN_SIZE + sizeof(struct sk_buff)),
-			    0, GFP_KERNEL);
-	if (buff == NULL) {
-		/* FIXME: Free route references etc??? */
-		return(-ENOMEM);
-	}
-
-	lock_sock(sk);
-
-	tcp_v6_build_header(sk, buff);
-
-	tp->tcp_header_len = sizeof(struct tcphdr) +
-		(sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);
-
-	/* build the tcp header */
-	th = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr));
-	buff->h.th = th;
-
-	memcpy(th, (void *) &(sk->dummy_th), sizeof(*th));
-	buff->seq = tp->write_seq++;
-	th->seq = htonl(buff->seq);
-	tp->snd_nxt = tp->write_seq;
-	buff->end_seq = tp->write_seq;
-	th->ack = 0;
-	th->syn = 1;
-
+						   sk->sport, sk->dport);
 
 	sk->mtu = dst->pmtu;
-	sk->mss = (sk->mtu - sizeof(struct ipv6hdr) - tp->tcp_header_len);
-
-        if (sk->mss < 1) {
-                printk(KERN_DEBUG "intial ipv6 sk->mss below 1\n");
-                sk->mss = 1;    /* Sanity limit */
-        }
-
-	tp->window_clamp = 0;	/* FIXME: shouldn't ipv6 dst cache have this? */
-	tcp_select_initial_window(sock_rspace(sk)/2,sk->mss,
-		&tp->rcv_wnd,
-		&tp->window_clamp,
-		sysctl_tcp_window_scaling,
-		&tp->rcv_wscale);
-	th->window = htons(tp->rcv_wnd);
-
-	/*
-	 *	Put in the TCP options to say MTU.
-	 */
-
-        tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_timestamps,
-                sysctl_tcp_window_scaling,tp->rcv_wscale);
-        th->doff = sizeof(*th)/4 + (tmp>>2);
-	buff->csum = 0;
-	tcp_v6_send_check(sk, th, sizeof(struct tcphdr) + tmp, buff);
-
-	tcp_set_state(sk, TCP_SYN_SENT);
-
-	/* Socket identity change complete, no longer
-	 * in TCP_CLOSE, so enter ourselves into the
-	 * hash tables.
-	 */
-	sk->prot->hash(sk);
-
-	/* FIXME: should use dcache->rtt if availiable */
-	tp->rto = TCP_TIMEOUT_INIT;
-
-	tcp_init_xmit_timers(sk);
-
-	tp->retransmits = 0;
-
-	skb_queue_tail(&sk->write_queue, buff);
-	tp->packets_out++;
-	buff->when = jiffies;
-	skb1 = skb_clone(buff, GFP_KERNEL);
-	if(skb1 != NULL) {
-		skb_set_owner_w(skb1, sk);
-		tcp_v6_xmit(skb1);
+	mss = sk->mtu - sizeof(struct ipv6hdr);
+#if 0
+	if (np->opt) {
+		/* Adjust mss */
 	}
+#endif
 
-	/* Timer for repeating the SYN until an answer  */
-	tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
-	tcp_statistics.TcpActiveOpens++;
-	tcp_statistics.TcpOutSegs++;
-
-	release_sock(sk);
+	tcp_connect(sk, buff, mss);
 
-	return(0);
+	return 0;
 }
 
 static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
@@ -572,7 +522,7 @@
 	 *	Do sanity checking for sendmsg/sendto/send
 	 */
 
-	if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT))
+	if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT|MSG_NOSIGNAL))
 		goto out;
 	if (msg->msg_name) {
 		struct sockaddr_in6 *addr=(struct sockaddr_in6 *)msg->msg_name;
@@ -587,7 +537,7 @@
 		if(sk->state == TCP_CLOSE)
 			goto out;
 		retval = -EISCONN;
-		if (addr->sin6_port != sk->dummy_th.dest)
+		if (addr->sin6_port != sk->dport)
 			goto out;
 		if (ipv6_addr_cmp(&addr->sin6_addr, &np->daddr))
 			goto out;
@@ -606,7 +556,7 @@
 	return retval;
 }
 
-void tcp_v6_err(int type, int code, unsigned char *header, __u32 info,
+void tcp_v6_err(struct sk_buff *skb, int type, int code, unsigned char *header, __u32 info,
 		struct in6_addr *saddr, struct in6_addr *daddr,
 		struct inet6_protocol *protocol)
 {
@@ -616,13 +566,11 @@
 	int err;
 	int opening;
 	struct tcp_opt *tp; 
-#ifdef ICMP_PARANOIA
 	__u32 seq; 
-#endif
 
 	/* XXX: length check for tcphdr missing here */
 
-	sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source);
+	sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source, skb->dev->ifindex);
 
 	if (sk == NULL || sk->state == TCP_TIME_WAIT) {
 		/* XXX: Update ICMP error count */
@@ -630,7 +578,6 @@
 	}
 
 	tp = &sk->tp_pinfo.af_tcp;
-#ifdef ICMP_PARANOIA
 	seq = ntohl(th->seq); 
 	if (sk->state != TCP_LISTEN && !between(seq, tp->snd_una, tp->snd_nxt)) {
 		if (net_ratelimit()) 
@@ -639,8 +586,6 @@
 			       (int)sk->state, seq, tp->snd_una, tp->snd_nxt); 
 		return; 
 	}
-#endif
-
 
 	np = &sk->net_pinfo.af_inet6;
 	if (type == ICMPV6_PKT_TOOBIG && sk->state != TCP_LISTEN) {
@@ -656,9 +601,9 @@
 			fl.proto = IPPROTO_TCP;
 			fl.nl_u.ip6_u.daddr = &np->daddr;
 			fl.nl_u.ip6_u.saddr = &np->saddr;
-			fl.dev = np->oif;
-			fl.uli_u.ports.dport = sk->dummy_th.dest;
-			fl.uli_u.ports.sport = sk->dummy_th.source;
+			fl.oif = sk->bound_dev_if;
+			fl.uli_u.ports.dport = sk->dport;
+			fl.uli_u.ports.sport = sk->sport;
 
 			dst = ip6_route_output(sk, &fl);
 
@@ -696,7 +641,6 @@
 		req = tcp_v6_search_req(tp, &hd,th, &prev);
 		if (!req)
 			return;
-#ifdef ICMP_PARANOIA
 		if (seq != req->snt_isn) {
 			if (net_ratelimit())
 				printk(KERN_DEBUG "icmp packet for openreq "
@@ -704,7 +648,6 @@
 				       seq, req->snt_isn);
 			return;
 		}
-#endif		
 		if (req->sk) {
 			sk = req->sk; /* report error in accept */
 		} else {
@@ -739,87 +682,42 @@
 static void tcp_v6_send_synack(struct sock *sk, struct open_request *req)
 {
 	struct sk_buff * skb;
-	struct tcphdr *th;
 	struct dst_entry *dst;
 	struct flowi fl;
-	int tmp;
-
-	skb = sock_wmalloc(sk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
-	if (skb == NULL)
-		return;
+	int mss;
 
 	fl.proto = IPPROTO_TCP;
 	fl.nl_u.ip6_u.daddr = &req->af.v6_req.rmt_addr;
 	fl.nl_u.ip6_u.saddr = &req->af.v6_req.loc_addr;
-	fl.dev = req->af.v6_req.dev;
+	fl.oif = req->af.v6_req.iif;
 	fl.uli_u.ports.dport = req->rmt_port;
-	fl.uli_u.ports.sport = sk->dummy_th.source;
+	fl.uli_u.ports.sport = sk->sport;
 
 	dst = ip6_route_output(sk, &fl);
 	if (dst->error) {
-		kfree_skb(skb);
 		dst_release(dst);
 		return;
 	}
 
-	skb->dev = dst->dev;
-	skb_reserve(skb, (skb->dev->hard_header_len + 15) & ~15);
-	skb->nh.ipv6h = (struct ipv6hdr *) skb_put(skb,sizeof(struct ipv6hdr));
-
-	skb->h.th = th = (struct tcphdr *) skb_put(skb, sizeof(struct tcphdr));
-
-	/* Yuck, make this header setup more efficient... -DaveM */
-	memset(th, 0, sizeof(struct tcphdr));
-	th->syn = 1;
-	th->ack = 1;
-	th->source = sk->dummy_th.source;
-	th->dest = req->rmt_port;
-	skb->seq = req->snt_isn;
-	skb->end_seq = skb->seq + 1;
-	th->seq = ntohl(skb->seq);
-	th->ack_seq = htonl(req->rcv_isn + 1);
-
-	/* Don't offer more than they did.
-	 * This way we don't have to memorize who said what.
-	 * FIXME: the selection of initial mss here doesn't quite
-	 * match what happens under IPV4. Figure out the right thing to do.
-	 */
-        req->mss = min(sk->mss, req->mss);
-	if(sk->user_mss)
-		req->mss = min(req->mss, sk->user_mss);
-	if(req->tstamp_ok == 0)
-		req->mss += TCPOLEN_TSTAMP_ALIGNED;
-
-	if (req->rcv_wnd == 0) {
-		__u8 rcv_wscale;
-		/* Set this up on the first call only */
-		req->window_clamp = 0; /* FIXME: should be in dst cache */
-		tcp_select_initial_window(sock_rspace(sk)/2,req->mss,
-			&req->rcv_wnd,
-			&req->window_clamp,
-			req->wscale_ok,
-			&rcv_wscale);
-		req->rcv_wscale = rcv_wscale; 
-	}
-	th->window = htons(req->rcv_wnd);
-
-	tmp = tcp_syn_build_options(skb, req->mss, req->tstamp_ok,
-		req->wscale_ok,req->rcv_wscale);
-	skb->csum = 0;
-	th->doff = (sizeof(*th) + tmp)>>2;
-	th->check = tcp_v6_check(th, sizeof(*th) + tmp,
-				 &req->af.v6_req.loc_addr, &req->af.v6_req.rmt_addr,
-				 csum_partial((char *)th, sizeof(*th)+tmp, skb->csum));
-
-	/* Actually we should not attach dst to socket in state LISTEN,
-	   it results in stale destination per listen socket and
-	   overflow of routing cache.
-	   (IPv4 has the same flaw with more unpleasant consequences.)
-	 */
-	ip6_dst_store(sk, dst);
-	ip6_xmit(sk, skb, &fl, req->af.v6_req.opt);
+	mss = dst->pmtu - sizeof(struct ipv6hdr) - sizeof(struct tcphdr);
+#if 0
+	/* Subtract option length... */
+	if (opt) {
+		mss -= opt->optlen;
+	}
+#endif
+
+	skb = tcp_make_synack(sk, dst, req, mss);
+	if (skb) {
+		struct tcphdr *th = skb->h.th;
 
-	tcp_statistics.TcpOutSegs++;
+		th->check = tcp_v6_check(th, skb->len,
+					 &req->af.v6_req.loc_addr, &req->af.v6_req.rmt_addr,
+					 csum_partial((char *)th, skb->len, skb->csum));
+
+		ip6_xmit(sk, skb, &fl, req->af.v6_req.opt);
+	}
+	dst_release(dst);
 }
 
 static void tcp_v6_or_free(struct open_request *req)
@@ -866,8 +764,6 @@
 
 	req = tcp_openreq_alloc();
 	if (req == NULL) {
-		tcp_statistics.TcpAttemptFails++;
-		goto exit;		
 	}
 
 	sk->ack_backlog++;
@@ -876,22 +772,27 @@
 
 	req->rcv_isn = skb->seq;
 	req->snt_isn = isn;
-	tp.tstamp_ok = tp.wscale_ok = tp.snd_wscale = 0;
+	tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
 	tp.in_mss = 536;
-	tcp_parse_options(skb->h.th,&tp,0);
+	tcp_parse_options(NULL, skb->h.th, &tp, 0);
         req->mss = tp.in_mss;
 	if (tp.saw_tstamp) {
 		req->mss -= TCPOLEN_TSTAMP_ALIGNED;
                 req->ts_recent = tp.rcv_tsval;
 	}
         req->tstamp_ok = tp.tstamp_ok;
+	req->sack_ok = tp.sack_ok;
         req->snd_wscale = tp.snd_wscale;
         req->wscale_ok = tp.wscale_ok;
 	req->rmt_port = skb->h.th->source;
 	ipv6_addr_copy(&req->af.v6_req.rmt_addr, &skb->nh.ipv6h->saddr);
 	ipv6_addr_copy(&req->af.v6_req.loc_addr, &skb->nh.ipv6h->daddr);
 	req->af.v6_req.opt = NULL;	/* FIXME: options */
-	req->af.v6_req.dev = skb->dev;  /* So that link locals have meaning */
+	req->af.v6_req.iif = sk->bound_dev_if;
+
+	/* So that link locals have meaning */
+	if (!sk->bound_dev_if && ipv6_addr_type(&req->af.v6_req.rmt_addr)&IPV6_ADDR_LINKLOCAL)
+		req->af.v6_req.iif = skb->dev->ifindex;
 
 	req->class = &or_ipv6;
 	req->retrans = 0;
@@ -928,6 +829,7 @@
 	struct flowi fl;
 	struct tcp_opt *newtp;
 	struct sock *newsk;
+	int mss;
 
 	if (skb->protocol == __constant_htons(ETH_P_IP)) {
 		/*
@@ -955,13 +857,36 @@
 		return newsk;
 	}
 
-	newsk = tcp_create_openreq_child(sk, req, skb);
-	if (newsk == NULL) {
-		dst_release(dst);
-		return NULL;
+
+	if (dst == NULL) {
+		/*
+		 *	options / mss / route cache
+		 */
+	    
+		fl.proto = IPPROTO_TCP;
+		fl.nl_u.ip6_u.daddr = &req->af.v6_req.rmt_addr;
+		fl.nl_u.ip6_u.saddr = &req->af.v6_req.loc_addr;
+		fl.oif = sk->bound_dev_if;
+		fl.uli_u.ports.dport = req->rmt_port;
+		fl.uli_u.ports.sport = sk->sport;
+
+		dst = ip6_route_output(sk, &fl);
 	}
 
-	newsk->dst_cache  = NULL;
+	if (dst->error || dst->pmtu < 576)
+		goto out;
+	
+
+	mss = dst->pmtu - sizeof(struct ipv6hdr);
+#if 0
+	/* Adjust mss by option size */
+#endif
+
+	newsk = tcp_create_openreq_child(sk, req, skb, mss);
+	if (newsk == NULL)
+		goto out;
+
+	ip6_dst_store(newsk, dst);
 
 	newtp = &(newsk->tp_pinfo.af_tcp);
 
@@ -969,52 +894,9 @@
 	ipv6_addr_copy(&np->daddr, &req->af.v6_req.rmt_addr);
 	ipv6_addr_copy(&np->saddr, &req->af.v6_req.loc_addr);
 	ipv6_addr_copy(&np->rcv_saddr, &req->af.v6_req.loc_addr);
-	np->oif = req->af.v6_req.dev;
-
-	if (dst == NULL) {
-	    /*
-	     *	options / mss / route cache
-	     */
-	    
-	    fl.proto = IPPROTO_TCP;
-	    fl.nl_u.ip6_u.daddr = &np->daddr;
-	    fl.nl_u.ip6_u.saddr = &np->saddr;
-	    fl.dev = np->oif;
-	    fl.uli_u.ports.dport = newsk->dummy_th.dest;
-	    fl.uli_u.ports.sport = newsk->dummy_th.source;
-	    
-	    dst = ip6_route_output(newsk, &fl);
-	}
-
-	ip6_dst_store(newsk, dst);
-
-        newtp->tstamp_ok = req->tstamp_ok;
-	newtp->window_clamp = req->window_clamp;
-	newtp->rcv_wnd = req->rcv_wnd;
-	newtp->wscale_ok = req->wscale_ok;
-	if (newtp->wscale_ok) {
-		newtp->snd_wscale = req->snd_wscale;
-		newtp->rcv_wscale = req->rcv_wscale;
-	} else {
-		newtp->snd_wscale = newtp->rcv_wscale = 0;
-		newtp->window_clamp = min(newtp->window_clamp,65535);
-	}
-        if (newtp->tstamp_ok) {
-		newtp->ts_recent = req->ts_recent;
-		newtp->ts_recent_stamp = jiffies;
-		newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
-		newsk->dummy_th.doff += (TCPOLEN_TSTAMP_ALIGNED >> 2);
-        } else {
-                newtp->tcp_header_len = sizeof(struct tcphdr);
-        }
-  
-	if (dst->error)
-		newsk->mtu = req->af.v6_req.dev->mtu;
-	else
-		newsk->mtu = dst->pmtu;
-
-	newsk->mss = min(req->mss+sizeof(struct tcphdr)-newtp->tcp_header_len,
-		(newsk->mtu - sizeof(struct ipv6hdr) - newtp->tcp_header_len));
+	newsk->bound_dev_if = req->af.v6_req.iif;
+	newsk->mtu = dst->pmtu;
+	newsk->opt = NULL;
 
 	newsk->daddr	= LOOPBACK4_IPV6;
 	newsk->saddr	= LOOPBACK4_IPV6;
@@ -1023,6 +905,10 @@
 	newsk->prot->hash(newsk);
 	add_to_prot_sklist(newsk);
 	return newsk;
+
+out:
+	dst_release(dst);
+	return NULL;
 }
 
 static void tcp_v6_send_reset(struct sk_buff *skb)
@@ -1031,7 +917,7 @@
 	struct sk_buff *buff;
 	struct flowi fl;
 
-	if(th->rst)
+	if (th->rst)
 		return;
 
 	/*
@@ -1039,21 +925,16 @@
 	 * and then put it into the queue to be sent.
 	 */
 
-	buff = alloc_skb(MAX_RESET_SIZE, GFP_ATOMIC);
+	buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr), GFP_ATOMIC);
 	if (buff == NULL) 
 	  	return;
 
-	buff->dev = skb->dev;
+	skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr));
 
-	tcp_v6_build_header(NULL, buff);
+	t1 = (struct tcphdr *) skb_push(buff,sizeof(struct tcphdr));
 
-	t1 = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr));
+	/* Swap the send and the receive. */
 	memset(t1, 0, sizeof(*t1));
-
-	/*
-	 *	Swap the send and the receive. 
-	 */
-
 	t1->dest = th->source;
 	t1->source = th->dest;
 	t1->doff = sizeof(*t1)/4;
@@ -1080,13 +961,21 @@
 				    buff->csum);
 
 	fl.proto = IPPROTO_TCP;
-	fl.dev = skb->dev;
+	fl.oif = skb->dev->ifindex;
 	fl.uli_u.ports.dport = t1->dest;
 	fl.uli_u.ports.sport = t1->source;
 
-	ip6_xmit(NULL, buff, &fl, NULL);
-	tcp_statistics.TcpOutSegs++;
-	tcp_statistics.TcpOutRsts++;
+	/* sk = NULL, but it is safe for now. RST socket required. */
+	buff->dst = ip6_route_output(NULL, &fl);
+
+	if (buff->dst->error == 0) {
+		ip6_xmit(NULL, buff, &fl, NULL);
+		tcp_statistics.TcpOutSegs++;
+		tcp_statistics.TcpOutRsts++;
+		return;
+	}
+
+	kfree_skb(buff);
 }
 
 static struct open_request *tcp_v6_search_req(struct tcp_opt *tp,
@@ -1182,7 +1071,7 @@
 			/* CHECKSUM_UNNECESSARY */
 		};
 
-		sk = __tcp_v6_lookup(th, saddr, th->source, daddr, th->dest);
+		sk = __tcp_v6_lookup(th, saddr, th->source, daddr, th->dest, dev->ifindex);
 
 		if (!sk) {
 			printk(KERN_DEBUG "socket not found\n");
@@ -1267,37 +1156,35 @@
 	goto discard_it;
 }
 
-static int tcp_v6_rebuild_header(struct sock *sk, struct sk_buff *skb)
+static int tcp_v6_rebuild_header(struct sock *sk)
 {
+	struct dst_entry *dst = NULL;
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 
 	if (sk->dst_cache)
-		dst_check(&sk->dst_cache, np->dst_cookie);
+		dst = dst_check(&sk->dst_cache, np->dst_cookie);
 
-	if (sk->dst_cache == NULL) {
+	if (dst == NULL) {
 		struct flowi fl;
-		struct dst_entry *dst;
 
 		fl.proto = IPPROTO_TCP;
 		fl.nl_u.ip6_u.daddr = &np->daddr;
 		fl.nl_u.ip6_u.saddr = &np->saddr;
-		fl.dev = np->oif;
-		fl.uli_u.ports.dport = sk->dummy_th.dest;
-		fl.uli_u.ports.sport = sk->dummy_th.source;
+		fl.oif = sk->bound_dev_if;
+		fl.uli_u.ports.dport = sk->dport;
+		fl.uli_u.ports.sport = sk->sport;
 
 		dst = ip6_route_output(sk, &fl);
-		ip6_dst_store(sk, dst);
-	}
 
-	if (sk->dst_cache->error) {
-		/*
-		 *	lost route to destination
-		 */
-		return -EHOSTUNREACH;
+		if (dst->error) {
+			dst_release(dst);
+			return dst->error;
+		}
+
+		ip6_dst_store(sk, dst);
 	}
 
-	skb_pull(skb, skb->nh.raw - skb->data);
-	return 0;
+	return dst->error;
 }
 
 static int tcp_v6_backlog_rcv(struct sock *sk, struct sk_buff *skb)
@@ -1319,20 +1206,7 @@
 
 	saddr = &skb->nh.ipv6h->saddr;
 	daddr = &skb->nh.ipv6h->daddr;
-	return tcp_v6_lookup(saddr, th->source, daddr, th->dest);
-}
-
-static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb)
-{
-	skb_reserve(skb, (MAX_HEADER + 15) & ~15);
-	skb->nh.raw = skb_put(skb, sizeof(struct ipv6hdr));
-
-	/*
-	 *	FIXME: reserve space for option headers
-	 *	length member of np->opt
-	 */
-
-	return 0;
+	return tcp_v6_lookup(saddr, th->source, daddr, th->dest, skb->dev->ifindex);
 }
 
 static void tcp_v6_xmit(struct sk_buff *skb)
@@ -1340,22 +1214,33 @@
 	struct sock *sk = skb->sk;
 	struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6;
 	struct flowi fl;
-	int err;
+	struct dst_entry *dst = sk->dst_cache;
 
 	fl.proto = IPPROTO_TCP;
 	fl.nl_u.ip6_u.daddr = &np->daddr;
 	fl.nl_u.ip6_u.saddr = &np->saddr;
-	fl.dev = np->oif;
-	fl.uli_u.ports.sport = sk->dummy_th.source;
-	fl.uli_u.ports.dport = sk->dummy_th.dest;
+	fl.oif = sk->bound_dev_if;
+	fl.uli_u.ports.sport = sk->sport;
+	fl.uli_u.ports.dport = sk->dport;
 
-	err = ip6_xmit(sk, skb, &fl, np->opt);
+	if (sk->dst_cache)
+		dst = dst_check(&sk->dst_cache, np->dst_cookie);
 
-	/*
-	 *	FIXME: check error handling.
-	 */
+	if (dst == NULL) {
+		dst = ip6_route_output(sk, &fl);
 
-	sk->err_soft = err;
+		if (dst->error) {
+			sk->err_soft = dst->error;
+			dst_release(dst);
+			return;
+		}
+
+		ip6_dst_store(sk, dst);
+	}
+
+	skb->dst = dst_clone(dst);
+
+	ip6_xmit(sk, skb, &fl, np->opt);
 }
 
 static void v6_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
@@ -1365,11 +1250,10 @@
 
 	sin6->sin6_family = AF_INET6;
 	memcpy(&sin6->sin6_addr, &np->daddr, sizeof(struct in6_addr));
-	sin6->sin6_port	= sk->dummy_th.dest;
+	sin6->sin6_port	= sk->dport;
 }
 
 static struct tcp_func ipv6_specific = {
-	tcp_v6_build_header,
 	tcp_v6_xmit,
 	tcp_v6_send_check,
 	tcp_v6_rebuild_header,
@@ -1387,7 +1271,6 @@
  */
 
 static struct tcp_func ipv6_mapped = {
-	tcp_v4_build_header,
 	ip_queue_xmit,
 	tcp_v4_send_check,
 	tcp_v4_rebuild_header,
@@ -1425,11 +1308,6 @@
 	sk->max_ack_backlog = SOMAXCONN;
 	sk->mtu = 576;
 	sk->mss = 536;
-	sk->dummy_th.doff = sizeof(sk->dummy_th)/4;
-
-	/* Speed up by setting some standard state for the dummy_th. */
-  	sk->dummy_th.ack=1;
-  	sk->dummy_th.doff=sizeof(struct tcphdr)>>2;
 
 	/* Init SYN queue. */
 	tcp_synq_init(tp);
diff -u --recursive --new-file v2.1.90/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.1.90/linux/net/ipv6/udp.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv6/udp.c	Mon Mar 23 16:48:25 1998
@@ -7,7 +7,7 @@
  *
  *	Based on linux/ipv4/udp.c
  *
- *	$Id: udp.c,v 1.24 1998/03/12 03:20:21 davem Exp $
+ *	$Id: udp.c,v 1.27 1998/03/21 07:28:06 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -151,8 +151,8 @@
 		   !(sk->dead && (sk->state == TCP_CLOSE))) {
 			struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 			int score = 0;
-			if(sk->dummy_th.dest) {
-				if(sk->dummy_th.dest != sport)
+			if(sk->dport) {
+				if(sk->dport != sport)
 					continue;
 				score++;
 			}
@@ -241,7 +241,7 @@
 
 	ipv6_addr_copy(&np->daddr, daddr);
 
-	sk->dummy_th.dest = usin->sin6_port;
+	sk->dport = usin->sin6_port;
 
 	/*
 	 *	Check for a route to destination an obtain the
@@ -251,9 +251,9 @@
 	fl.proto = IPPROTO_UDP;
 	fl.nl_u.ip6_u.daddr = daddr;
 	fl.nl_u.ip6_u.saddr = NULL;
-	fl.dev = NULL;
-	fl.uli_u.ports.dport = sk->dummy_th.dest;
-	fl.uli_u.ports.sport = sk->dummy_th.source;
+	fl.oif = sk->bound_dev_if;
+	fl.uli_u.ports.dport = sk->dport;
+	fl.uli_u.ports.sport = sk->sport;
 
 	dst = ip6_route_output(sk, &fl);
        
@@ -363,7 +363,7 @@
 	return err;
 }
 
-void udpv6_err(int type, int code, unsigned char *buff, __u32 info,
+void udpv6_err(struct sk_buff *skb, int type, int code, unsigned char *buff, __u32 info,
 	       struct in6_addr *saddr, struct in6_addr *daddr,
 	       struct inet6_protocol *protocol)
 {
@@ -428,8 +428,8 @@
 		if((s->num == num)		&&
 		   !(s->dead && (s->state == TCP_CLOSE))) {
 			struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
-			if(s->dummy_th.dest) {
-				if(s->dummy_th.dest != rmt_port)
+			if(s->dport) {
+				if(s->dport != rmt_port)
 					continue;
 			}
 			if(!ipv6_addr_any(&np->daddr) &&
@@ -644,7 +644,6 @@
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name;
 	struct ipv6_options *opt = NULL;
-	struct device *dev = NULL;
 	struct flowi fl;
 	int addr_len = msg->msg_namelen;
 	struct in6_addr *daddr;
@@ -692,7 +691,7 @@
 		if (sk->state != TCP_ESTABLISHED)
 			return(-EINVAL);
 		
-		udh.uh.dest = sk->dummy_th.dest;
+		udh.uh.dest = sk->dport;
 		daddr = &sk->net_pinfo.af_inet6.daddr;
 	}
 
@@ -708,22 +707,21 @@
 	}
 
 	udh.daddr = NULL;
+	fl.oif = sk->bound_dev_if;
 	
 	if (msg->msg_controllen) {
 		opt = &opt_space;
 		memset(opt, 0, sizeof(struct ipv6_options));
 
-		err = datagram_send_ctl(msg, &dev, &saddr, opt, &hlimit);
-		if (err < 0) {
-			printk(KERN_DEBUG "invalid msg_control\n");
+		err = datagram_send_ctl(msg, &fl.oif, &saddr, opt, &hlimit);
+		if (err < 0)
 			return err;
-		}
 		
 		if (opt->srcrt)
 			udh.daddr = daddr;
 	}
 	
-	udh.uh.source = sk->dummy_th.source;
+	udh.uh.source = sk->sport;
 	udh.uh.len = htons(len);
 	udh.uh.check = 0;
 	udh.iov = msg->msg_iov;
@@ -733,7 +731,6 @@
 	fl.proto = IPPROTO_UDP;
 	fl.nl_u.ip6_u.daddr = daddr;
 	fl.nl_u.ip6_u.saddr = saddr;
-	fl.dev = dev;
 	fl.uli_u.ports.dport = udh.uh.dest;
 	fl.uli_u.ports.sport = udh.uh.source;
 
diff -u --recursive --new-file v2.1.90/linux/net/netbeui/README linux/net/netbeui/README
--- v2.1.90/linux/net/netbeui/README	Thu Dec 12 06:54:26 1996
+++ linux/net/netbeui/README	Wed Dec 31 16:00:00 1969
@@ -1,19 +0,0 @@
-
-NetBEUI is a rather weird protocol. There are about three different set
-of connection and name spaces here.
-
-Firstly we have an array of 802.2 LLC links acting as reliable inter node
-links for the nodes we are talking to do. We create and tear these down as
-needed. In effect it goes around pretending ethernet is a set of bits of
-wire and running pseudo X.25 over it. The LLC code is elsewhere (net/802).
-
-Secondly we have the netbios name space. When we sit on multiple networks
-we have fun. Netbios isnt routable, so we have to arse around looking on
-all our devices for names.
-
-Thirdly we have logical netbeui sessions on top of the whole heap. 
-
-			*Don't blame us*
-
-We didn't design the protocol.
-
diff -u --recursive --new-file v2.1.90/linux/net/netbeui/af_netbeui.c linux/net/netbeui/af_netbeui.c
--- v2.1.90/linux/net/netbeui/af_netbeui.c	Tue Mar 17 22:18:16 1998
+++ linux/net/netbeui/af_netbeui.c	Wed Dec 31 16:00:00 1969
@@ -1,659 +0,0 @@
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/in.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/if_ether.h>
-#include <linux/route.h>
-#include <linux/inet.h>
-#include <linux/notifier.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <linux/termios.h>	/* For TIOCOUTQ/INQ */
-#include <linux/poll.h>
-#include <net/datalink.h>
-#include <net/p8022.h>
-#include <net/psnap.h>
-#include <net/sock.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/firewall.h>
-#include <linux/init.h>
-
-
-#undef NETBEUI_DEBUG
-
-
-#ifdef NETBEUI_DEBUG
-#define DPRINT(x)		print(x)
-#else
-#define DPRINT(x)
-#endif
-
-#define min(a,b)	(((a)<(b))?(a):(b))
-
-/***********************************************************************************************************************\
-*															*
-*						Handlers for the socket list.						*
-*															*
-\***********************************************************************************************************************/
-
-static netbeui_socket *netbeui_socket_list=NULL;
-
-/*
- *	Note: Sockets may not be removed _during_ an interrupt or inet_bh
- *	handler using this technique. They can be added although we do not
- *	use this facility.
- */
-
-extern inline void netbeui_remove_socket(netbeui_socket *sk)
-{
-	sklist_remove_socket(&netbeui_socket_list,sk);
-}
-
-extenr inline void netbeui_insert_socket(netbeui_socket *sk)
-{
-	sklist_insert_socket(&netbeui_socket_list,sk);
-	netbeui_socket_list=sk;
-	restore_flags(flags);
-}
-
-static void netbeui_destroy_socket(netbeui_socket *sk)
-{
-	/*
-	 *	Release netbios logical channels first
-	 */
-	if(sk->af_nb.nb_link)
-	{
-		netbeui_delete_channel(sk->af_nb.nb_link);
-		sk->af_nb.nb_link=NULL;
-	}
-	if(sk->af_nb.src_name)
-	{
-		netbeui_release_name(sk->af_nb.src_name);
-		sk->af_nb.src_name=NULL;
-	}
-	if(sk->af_nb.dst_name)
-	{
-		netbeui_release_name(sk->af_nb.dst_name);
-		sk->af_nb.dst_name=NULL;
-	}
-	netbeui_remove_listener(sk);
-	sklist_destroy_socket(&netbeui_socket,sk);
-}
-
-/*
- *	Called from proc fs
- */
-
-int netbeui_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-	return 0;
-}
-
-/*
- *	A device event has occurred. Watch for devices going down and
- *	delete our use of them (iface and route).
- */
-
-static int nb_device_event(struct notifier_block *this, unsigned long event, void *ptr)
-{
-	if(event==NETDEV_DOWN)
-	{
-		/* Discard any use of this */
-		netbeui_drop_device((struct device *)ptr);
-	}
-	return NOTIFY_DONE;
-}
-
-/*******************************************************************************************************************\
-*													            *
-*	      Handling for system calls applied via the various interfaces to a netbeui socket object		    *
-*														    *
-\*******************************************************************************************************************/
-
-static int netbeui_listen(struct socket *sock, int backlog)
-{
-	struct sock *sk=(netbeui_socket *)sock->data;
-	if(sk->state!=TCP_CLOSED)
-		return -EINVAL;
-	if(backlog<0)
-		return -EINVAL;
-	if(backlog<128)
-		sk->backlog=backlog;
-	else
-		sk->backlog=128;
-	sk->state=TCP_LISTEN;
-	sk->state_change(sk);
-	netbeui_add_listener(sk);
-	return 0;
-}
-
-/*
- *	Create a socket. Initialise the socket, blank the addresses
- *	set the state.
- */
-
-static int netbeui_create(struct socket *sock, int protocol)
-{
-	netbeui_socket *sk;
-	sk=(netbeui_socket *)sk_alloc(GFP_KERNEL, 1);
-	if(sk==NULL)
-		return(-ENOBUFS);
-	switch(sock->type)
-	{
-		case SOCK_DGRAM:
-			break;
-		case SOCK_SEQPACKET:
-			break;
-		default:
-			sk_free((void *)sk);
-			return(-ESOCKTNOSUPPORT);
-	}
-
-	MOD_INC_USE_COUNT;
-
-	sock_init_data(sock,sk);
-	sk->mtu=1500;
-	return(0);
-}
-
-/*
- *	Copy a socket. No work needed.
- */
-
-static int netbeui_dup(struct socket *newsock,struct socket *oldsock)
-{
-	return(netbeui_create(newsock,oldsock->type));
-}
-
-/*
- *	Free a socket. No work needed
- */
-
-static int netbeui_release(struct socket *sock, struct socket *peer)
-{
-	netbeui_socket *sk=(netbeui_socket *)sock->data;
-	if(sk==NULL)
-		return(0);
-	if(!sk->dead)
-		sk->state_change(sk);
-	sk->dead=1;
-	sock->data=NULL;
-	netbeui_destroy_socket(sk);
-	return(0);
-}
-
-/*
- *	Set the address 'our end' of the connection.
- */
-
-static int netbeui_bind(struct socket *sock, struct sockaddr *uaddr,size_t addr_len)
-{
-	netbeui_socket *sk;
-	struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr;
-	int err;
-
-	sk=(netbeui_socket *)sock->data;
-
-	if(sk->zapped==0)
-		return(-EINVAL);
-
-	if(addr_len!=sizeof(struct sockaddr_netbeui))
-		return -EINVAL;
-
-	if(addr->snb_family!=AF_NETBEUI)
-		return -EAFNOSUPPORT;
-
-	/*
-	 *	This will sleep. To meet POSIX it is non interruptible.
-	 *	Someone should give the 1003.1g authors an injection of
-	 *	imagination...
-	 */
-	 
-	if(sk->af_nb.src_name!=NULL)
-		return -EINVAL;
-	
-	/*
-	 *	Try and get the name. It may return various 'invalid' name
-	 *	problem reports or EADDRINUSE if we or another node holds
-	 *	the desired name.
-	 */
-	 	
-	sk->af_nb.src_name=netbeui_alloc_name(addr, &err);
-	if(sk->af_nb.src_name==NULL)
-		return err;
-	/*
-	 *	Add us to the active socket list 
-	 */
-	netbeui_insert_socket(sk);
-	sk->zapped=0;
-	return(0);
-}
-
-/*
- *	Set the address we talk to.
- */
-
-static int netbeui_connect(struct socket *sock, struct sockaddr *uaddr,
-	size_t addr_len, int flags)
-{
-	netbeui_socket *sk=(netbeui_socket *)sock->data;
-	struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr;
-
-	/*
-	 *	Check pending operations
-	 */	
-	
-	if(sk->state==TCP_ESTABLISHED && sock->state == SS_CONNECTING) 
-	{
-		sock->state==SS_CONNECTED;
-		return 0;
-	}
-	
-	if(sk->state == TCP_CLOSE & sock->state == SS_CONNECTING)
-	{
-		sock->state==SS_UNCONNECTED;
-		return -ECONNREFUSED;
-	}
-	
-	if(sock->state == SS_CONNECTING && (flags & O_NONBLOCK))
-		return -EINPROGRESS;
-	
-	if(sk->state==TCP_ESTABLISHED)
-		return -EISCONN;	 
-	
-	/*
-	 *	If this is new it must really be new...
-	 */
-	 
-	if(sk->af_nb.dst_name==NULL)
-	{
-		if(addr_len != sizeof(struct sockaddr_nb))
-			return -EINVAL;
-		if(addr->snb_family!=AF_NETBEUI)
-			return -EAFNOSUPPORT;
-		/*
-		 *	Try and find the name
-		 */
-	}
-}
-
-/*
- *	Not relevant
- */
-
-static int netbeui_socketpair(struct socket *sock1, struct socket *sock2)
-{
-	return(-EOPNOTSUPP);
-}
-
-/*
- *	WRITE ME
- */
-
-static int netbeui_accept(struct socket *sock, struct socket *newsock, int flags)
-{
-	if(newsock->data)
-		sk_free(newsock->data);
-	return -EOPNOTSUPP;
-}
-
-/*
- *	Find the name of a netbeui socket. Just copy the right
- *	fields into the sockaddr.
- */
-
-static int netbeui_getname(struct socket *sock, struct sockaddr *uaddr,
-	size_t *uaddr_len, int peer)
-{
-	struct sockaddr_netbeui snb;
-	netbeui_socket *sk;
-
-	sk=(netbeui_socket *)sock->data;
-	if(sk->zapped)
-	{
-		return -EINVAL;
-	}
-
-	*uaddr_len = sizeof(struct sockaddr_netbeui);
-
-	if(peer)
-	{
-		if(sk->state!=TCP_ESTABLISHED)
-			return -ENOTCONN;
-	}
-	else
-	{
-	}
-	snb.snb_family = AF_NETBEUI;
-	memcpy(uaddr,&snb,sizeof(snb));
-	return(0);
-}
-
-/*
- *	Receive a packet (in skb) from device dev.
- */
-
-static int netbeui_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
-{
-	return nb_llc_rcv(skb);
-}
-
-static int netbeui_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
-{
-	netbeui_socket *sk=(netbeui_socket *)sock->data;
-	struct sockaddr_nb *usnb=(struct sockaddr_nb *)msg->msg_name;
-	struct sk_buff *skb;
-	struct device *dev;
-	struct nbhdr *nbp;
-	int size;
-	struct netbeui_route *rt;
-	int loopback=0;
-	int err;
-
-	if(flags)
-		return -EINVAL;
-
-	if(len>1500)	/* - headers!! */
-		return -EMSGSIZE;
-
-	if(usnb)
-	{
-		if(sk->zapped)
-		{
-			if(netbeui_autobind(sk)<0)
-				return -EBUSY;
-		}
-
-		if(msg->msg_namelen <sizeof(*usnb))
-			return(-EINVAL);
-		if(usnb->snb_family != AF_NETBEUI)
-			return -EINVAL;
-		/* Check broadcast */
-	}
-	else
-	{
-		if(sk->state!=TCP_ESTABLISHED)
-			return -ENOTCONN;
-		/* Connected .. */
-	}
-
-	/* Build a packet */
-	SOCK_DEBUG(sk, "SK %p: Got address.\n",sk);
-	size=sizeof(struct nbhdr)+len+nb_dl->header_length;	/* For headers */
-
-	SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n", sk, size, dev->name);
-	size += dev->hard_header_len;
-	skb = sock_alloc_send_skb(sk, size, 0, 0 , &err);
-	if(skb==NULL)
-		return err;
-
-	skb->sk=sk;
-	skb->free=1;
-	skb->arp=1;
-	skb_reserve(skb,nb_dl->header_length);
-	skb_reserve(skb,dev->hard_header_len);
-	skb->dev=dev;
-	SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk);
-	nbp=(struct nbhdr *)skb_put(skb,sizeof(struct nbhdr));
-	SOCK_DEBUG(sk, "SK %p: Copy user data (%d bytes).\n", sk, len);
-	err = memcpy_fromiovec(skb_put(skb,len),msg->msg_iov,len);
-	if (err)
-	{
-		kfree_skb(skb);
-		return -EFAULT;
-	}
-
-#ifdef CONFIG_FIREWALL
-
-	if(call_out_firewall(AF_NETBEUI, skb->dev, nbp, NULL)!=FW_ACCEPT)
-	{
-		kfree_skb(skb);
-		return -EPERM;
-	}
-
-#endif
-
-	if(nb_send_low(dev,skb,&usat->sat_addr, NULL)==-1)
-		kfree_skb(skb);
-	SOCK_DEBUG(sk, "SK %p: Done write (%d).\n", sk, len);
-	return len;
-}
-
-
-static int netbeui_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
-{
-	netbeui_socket *sk=(netbeui_socket *)sock->data;
-	struct sockaddr_nb *snb=(struct sockaddr_nb *)msg->msg_name;
-	struct nbphdr	*nbp = NULL;
-	int copied = 0;
-	struct sk_buff *skb;
-	int er = 0;
-
-	if(addr_len)
-		*addr_len=sizeof(*snb);
-
-	skb=skb_recv_datagram(sk,flags,noblock,&er);
-	if(skb==NULL)
-		return er;
-
-	snb = (struct nbphdr *)(skb->h.raw);
-	if(sk->type==SOCK_RAW)
-	{
-		copied=skb->len
-		if(copied > size)
-		{
-			copied=size;
-			msg->msg_flags|=MSG_TRUNC;
-		}
-		er = skb_copy_datagram_iovec(skb,0,msg->msg_iov,copied);
-		if (er)
-			goto out;
-	}
-	else
-	{
-		copied=skb->len - sizeof(*nbp);
-		if (copied > size)
-		{
-			copied = size;
-			msg->msg_flags|=MSG_TRUNC;
-		}
-		er = skb_copy_datagram_iovec(skb,sizeof(*nbp),msg->msg_iov,copied);
-		if (er)
-			goto out;
-	}
-	if(snb)
-	{
-		sat->sat_family=AF_NETBEUI;
-		/* Copy name over */
-	}
-out:
-	skb_free_datagram(sk, skb);
-	return er ? er : (copied);
-}
-
-
-static int netbeui_shutdown(struct socket *sk,int how)
-{
-	return -EOPNOTSUPP;
-}
-
-static int netbeui_poll(struct socket *sock, poll_table *wait)
-{
-	netbeui_socket *sk=(netbeui_socket *)sock->data;
-
-	return datagram_poll(sk,wait);
-}
-
-/*
- *	Netbeui ioctl calls.
- */
-
-static int netbeui_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
-{
-	long amount=0;
-	netbeui_socket *sk=(netbeui_socket *)sock->data;
-
-	switch(cmd)
-	{
-		/*
-		 *	Protocol layer
-		 */
-		case TIOCOUTQ:
-			amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
-			if(amount<0)
-				amount=0;
-			break;
-		case TIOCINQ:
-		{
-			struct sk_buff *skb;
-			/* These two are safe on a single CPU system as only user tasks fiddle here */
-			if((skb=skb_peek(&sk->receive_queue))!=NULL)
-				amount=skb->len-sizeof(struct ddpehdr);
-			break;
-		}
-		case SIOCGSTAMP:
-			if (sk)
-			{
-				if(sk->stamp.tv_sec==0)
-					return -ENOENT;
-				return copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval)) ? -EFAULT : 0;
-			}
-			return -EINVAL;
-		/*
-		 *	Routing
-		 */
-		case SIOCADDRT:
-		case SIOCDELRT:
-			if(!suser())
-				return -EPERM;
-			return(nbrtr_ioctl(cmd,(void *)arg));
-		/*
-		 *	Interface
-		 */
-		case SIOCGIFADDR:
-		case SIOCSIFADDR:
-		case SIOCGIFBRDADDR:
-			return nbif_ioctl(cmd,(void *)arg);
-		/*
-		 *	Physical layer ioctl calls
-		 */
-		case SIOCSIFLINK:
-		case SIOCGIFHWADDR:
-		case SIOCSIFHWADDR:
-		case SIOCGIFFLAGS:
-		case SIOCSIFFLAGS:
-		case SIOCGIFMTU:
-		case SIOCGIFCONF:
-		case SIOCADDMULTI:
-		case SIOCDELMULTI:
-
-			return(dev_ioctl(cmd,(void *) arg));
-
-		case SIOCSIFMETRIC:
-		case SIOCSIFBRDADDR:
-		case SIOCGIFNETMASK:
-		case SIOCSIFNETMASK:
-		case SIOCGIFMEM:
-		case SIOCSIFMEM:
-		case SIOCGIFDSTADDR:
-		case SIOCSIFDSTADDR:
-			return -EINVAL;
-
-		default:
-			return -EINVAL;
-	}
-	return put_user(amount, (int *)arg);
-}
-
-static struct proto_ops netbeui_proto_ops = {
-	AF_NETBEUI,
-
-	netbeui_create,
-	netbeui_dup,
-	netbeui_release,
-	netbeui_bind,
-	netbeui_connect,
-	netbeui_socketpair,
-	netbeui_accept,
-	netbeui_getname,
-	netbeui_poll,
-	netbeui_ioctl,
-	netbeui_listen,
-	netbeui_shutdown,
-	sock_no_setsockopt,
-	sock_no_getsockopt,
-	sock_no_fcntl,
-	netbeui_sendmsg,
-	netbeui_recvmsg
-};
-
-static struct notifier_block nb_notifier={
-	nb_device_event,
-	NULL,
-	0
-};
-
-static char nb_snap_id[]={0x08,0x00,0x07,0x80,0x9B};
-
-#ifdef CONFIG_PROC_FS
-static struct proc_dir_entry proc_netbeui = {
-	PROC_NET_NETBEUI, 9, "netbeui",
-	S_IFREG | S_IRUGO, 1, 0, 0
-	0, &proc_net_inode_operations,
-	netbeui_get_info
-};
-#endif
-
-/* Called by proto.c on kernel start up */
-
-__initfunc(void netbeui_proto_init(struct net_proto *pro))
-{
-	(void) sock_register(netbeui_proto_ops.family, &netbeui_proto_ops);
-	if ((nb_dl = register_8022_client(nb_8022_id, netbeui_rcv)) == NULL)
-		printk(KERN_CRIT "Unable to register Netbeui with 802.2.\n");
-
-	register_netdevice_notifier(&nb_notifier);
-
-#ifdef CONFIG_PROC_FS
-	proc_net_register(&proc_netbeui);
-#endif
-
-	printk(KERN_INFO "NetBEUI 0.03 for Linux NET3.037\n");
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
-{
-	netbeui_proto_init(NULL);
-	return 0;
-}
-
-void cleanup_module(void)
-{
-	unsigned long flags;
-#ifdef CONFIG_PROC_FS
-	proc_net_unregister(PROC_NET_NETBEUI);
-#endif
-	unregister_netdevice_notifier(&nb_notifier);
-	unregister_snap_client(nb_snap_id);
-	sock_unregister(netbeui_proto_ops.family);
-}
-
-#endif  /* MODULE */
diff -u --recursive --new-file v2.1.90/linux/net/netbeui/netbeui_llc.c linux/net/netbeui/netbeui_llc.c
--- v2.1.90/linux/net/netbeui/netbeui_llc.c	Thu Feb 12 20:56:15 1998
+++ linux/net/netbeui/netbeui_llc.c	Wed Dec 31 16:00:00 1969
@@ -1,265 +0,0 @@
-/*
- *	NET3:	802.2 LLC supervisor for the netbeui protocols. 
- *
- *	The basic aim is to provide a self managing link layer supervisor
- *	for netbeui. It creates and destroys the 802.2 virtual connections
- *	as needed, and copes with the various races when a link goes down
- *	just as its requested etc.
- *
- *	The upper layers are presented with the notion of an nb_link which
- *	is a potentially shared object that represents a logical path 
- *	between two hosts. Each nb_link has usage counts and users can
- *	treat it as if its their own.
- */
- 
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/notifier.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/datalink.h>
-#include <net/p8022.h>
-#include <net/psnap.h>
-#include <net/sock.h>
-#include <net/llc.h>
-#include <net/netbeui.h>
-
-
-/*
- *	When this routine is called the netbeui layer has decided to
- *	drop the link. There is a tiny risk that we might reuse the
- *	link after we decide. Thus before we blast the link into little
- *	tiny pieces we must check....
- */
- 
-static void netbeui_do_destroy(struct nb_link *nb)
-{
-	/*
-	 *	Are we wanted again. Bring it back. Sigh, wish people
-	 *	would make up their minds 8)
-	 */
-	if(nb->users>0)
-	{
-		nb->state=NETBEUI_CONNWAIT;
-		llc_connect_request(&nb->llc);
-		return;
-	}
-	/*
-	 *	Blam.... into oblivion it goes
-	 */
-	
-	llc_unregister(&nb->llc);
-	netbeui_free_link(nb);
-}
-
-/*
- *	Handle netbeui events. Basically that means keep it up when it
- *	should be up, down when it should be down and handle all the data.
- */
-
-static void netbeui_event(llcptr llc)
-{
-	struct nb_link *nb=(struct nb_link *)llc;
-	
-	/*
-	 *	See what has occured
-	 */
-	 
-
-	/*
-	 *	Connect completion confirmation
-	 */
-	 
-	if(llc->llc_callbacks&LLC_CONN_CONFIRM)
-	{
-		/*
-		 *	Link up if desired. Otherwise try frantically
-		 *	to close it.
-		 */
-		if(nb->state!=NETBEUI_DEADWAIT)
-		{
-			/*
-			 *	Wake pending writers
-			 */
-			nb->state=NETBEUI_OPEN;
-			netbeui_wakeup(nb);
-		}
-		else
-			llc_disconnect_request(llc);
-	}
-	
-	/*
-	 *	Data is passed to the upper netbeui layer
-	 */
-
-	if(llc->llc_callbacks&LLC_DATA_INDIC)
-	{
-		netbeu_rcv_stream(llc,llc->inc_skb);
-		/*
-		 *	Frame free is controlled by our stream processor
-		 */
-		return;
-	}
-
-	/*
-	 *	We got disconnected
-	 */
-	 
-	if(llc->llc_callbacks&LLC_DISC_INDICATION)
-	{
-		if(nb->state==NETBEUI_DEADWAIT)
-		{
-			netbeui_do_destroy(nb);
-			return;
-		}
-		if(nb->state==NETBEUI_DISCWAIT)
-		{
-			llc_connect_request(llc);
-			nb->state=NETBEUI_CONNWAIT;
-		}
-	}
-	
-	/*
-	 *	Miscellaneous burps
-	 */
-	
-	if(llc->llc_callbacks&(LLC_RESET_INDIC_LOC|LLC_RESET_INDIC_REM|
-					LLC_RST_CONFIRM))
-	{
-		/*
-		 *	Reset. 
-		 *	Q: Is tearing the link down the right answer ?
-		 *
-		 *	For now we just carry on
-		 */
-	}
-
-	/*
-	 *	Track link busy status
-	 */
-	 
-	if(llc->llc_callbacks&LLC_REMOTE_BUSY)
-		nb->busy=1;	/* Send no more for a bit */
-	if(llc->llc_callbacks&LLC_REMOTE_NOTBUSY)
-	{
-		/* Coming unbusy may wake sending threads */
-		nb->busy=0;
-		netbeui_wakeup(nb);
-	}		
-	/*
-	 *	UI frames are passed to the upper netbeui layer.
-	 */
-	if(llc->llc_callbacks&LLC_UI_DATA)
-	{
-		netbeui_rcv_dgram(llc,llc->inc_skb);
-		return;
-	}
-
-	/* We ignore TST, XID, FRMR stuff */
-	/* FIXME: We need to free frames here once I fix the callback! */
-	if(llc->inc_skb)
-		kfree_skb(skb);
-}
-
-/*
- *	Netbeui has created a new logical link. As a result we will
- *	need to find or create a suitable 802.2 LLC session and join
- *	it.
- */
-
-struct nb_link *netbeui_create_channel(struct device *dev, u8 *remote_mac, int pri)
-{
-	struct nb_link *nb=netbeui_find_channel(dev,remote_mac);
-	if(nb)
-	{
-		if(nb->state==NETBEUI_DEADWAIT)
-		{
-			/*
-			 *	We had commenced a final shutdown. We
-			 *	cannot abort that (we sent the packet) but
-			 *	we can shift the mode to DISCWAIT. That will
-			 *	cause the disconnect event to bounce us
-			 *	back into connected state.
-			 */
-			nb->state==NETBEUI_DISCWAIT;
-		}
-		nb->users++;
-		return nb;
-	}
-	nb=netbeui_alloc_link(pri);
-	if(nb==NULL)
-		return NULL;
-	
-	/*
-	 *	Internal book keeping
-	 */
-	 
-	nb->dev=dev;
-	nb->users=1;
-	nb->busy=0;
-	nb->wakeup=NULL;
-	nb->state=NETBEUI_CONNWAIT;
-	memcpy(nb->remote_mac, remote_mac, ETH_ALEN);
-	
-	/*
-	 *	Now try and attach an LLC.
-	 */
-	
-	if(register_cl2llc_client(&nb->llc,dev->name,netbeui_event,
-		remote_mac, NETBEUI_SAP, NETBEUI_SAP)<0)
-	{
-		netbeui_free_link(nb);
-		return NULL;
-	}
-	
-	/*
-	 *	Commence connection establishment.
-	 */
-	 
-	llc_connect_request(&nb->llc);
-	
-	/*
-	 *	Done
-	 */
-
-	nb->next=nb_link_list;
-	nb_link_list=nb;
-	 
-	return nb;
-}
-
-/*
- *	A logical netbeui channel has died. If the channel has no
- *	further users we commence shutdown.
- */
-	
-int netbeui_delete_channel(struct nb_link *nb)
-{
-	nb->users--;
-	
-	/*
-	 *	FIXME: Must remove ourselves from the nb_link chain when
-	 *	we add that bit
-	 */
-	 
-	if(nb->users)
-		return 0;
-		
-	/*
-	 *	Ensure we drop soon. The disconnect confirm will let
-	 *	us fix the deletion. If someone wants the link at
-	 *	the wrong moment nothing bad will occur. The create
-	 *	or the do_destroy will sort it.
-	 */
-
-	nb->state = NETBEUI_DEADWAIT;
-	llc_disconnect_request(lp);
-	return 0;
-}
-
-
diff -u --recursive --new-file v2.1.90/linux/net/netbeui/netbeui_name.c linux/net/netbeui/netbeui_name.c
--- v2.1.90/linux/net/netbeui/netbeui_name.c	Thu Feb 12 20:56:15 1998
+++ linux/net/netbeui/netbeui_name.c	Wed Dec 31 16:00:00 1969
@@ -1,163 +0,0 @@
-/*
- *	NetBIOS name handler
- */
-
-/*
- *	You must hold the netbios name lock before using these.
- */
-  
-struct nb_name *nb_name_find(struct device *dev,const char * name)
-{
-	struct nb_name *nb=nb_name_list;
-	while(nb!=NULL)
-	{
-		if((dev==NULL || dev==nb->dev) && 
-			strncmp(name,nb->name, NB_NAME_LEN)==0)
-			return nb;
-		nb=nb->next;
-	}
-	return NULL;
-}
-
-int nb_name_add(struct device *dev, const char *name, int ours, int pri)
-{
-	struct nb_name *nb=kmalloc(sizeof(*nb), pri);
-	if(nb==NULL)
-		return NULL;
-	nb->dev=dev;
-	strncpy(nb->name,name,NB_NAME_LEN);
-	nb->name[NB_NAME_LEN-1]=0;
-	nb->next=nb_name_list;
-	nb->ours=ours;
-	nb_name_list=nb;
-}
-
-void nb_name_delete(struct nb_name *nb)
-{
-	struct nb_name *i=&nb_name_list;
-	while((*i)!=NULL)
-	{
-		if(*i==nb)
-		{
-			*i=nb->next;
-			kfree_s(nb,sizeof(*nb));
-			return;
-		}
-		i=&((*i)->next);
-	}
-	printk(KERN_ERR "nb_name_delete: bad name pointer!\n");
-}
-
-/*
- *	NETBIOS name handlers
- */
-
-static void nb_defend(struct device *dev, const char *name)
-{
-	struct sk_buff *nskb=nb_alloc_skb(NB_CONTROL_LEN, GFP_ATOMIC);
-	if(nskb==NULL)
-		return;
-	/* Build a name defence packet */
-	nskb->dev = dev;
-	nskb->priority = TC_PRIO_CONTROL;
-	dev_queue_xmit(nskb);
-}
-
-void netbeui_heard_name(struct device *dev, struct sk_buff *skb)
-{
-	struct nb_name *nb;
-	name=...
-	
-	if((nb=nb_name_find(dev,name))!=NULL)
-	{
-		/*
-		 *	If we own the name then defend it
-		 */
-		if(nb->our && !nb->state==NB_ACQUIRE)
-			nb_defend(dev,name);
-		/*
-		 *	A name has been resolved. Wake up pending
-		 *	connectors.
-		 */
-		if(nb->state==NB_QUERY)
-		{
-			nb->state=NB_OTHER;
-			nb_complete(nb,skb);
-		}
-	}
-	kfree_skb(skb);
-	return 0;
-}
-
-/*
- *	Handle incoming name defences
- */
- 
-void netbeui_name_defence(struct dev *dev, struct sk_buff *skb)
-{
-	struct nb_name *name;
-	name=
-	
-	if((nb=nb_name_find(dev,name))!=NULL)
-	{
-		if(nb->ours)
-		{
-			/*	
-			 *	We wanted it, we got told its used
-			 */
-			if(nb->state==NB_ACQUIRE)
-			{
-				/*
-				 *	Fill in the record for its true
-				 *	owner. Set the state first as
-				 *	nb_complete may well delete the
-				 *	record.
-				 */
-				nb->state=NB_OTHER;
-				nb_complete(nb,skb);
-				nb_wakeup();
-			}
-			/*
-			 *	We own it we got told its used. This is
-			 *	a deep cack even that can only occur when
-			 *	a bridge comes back and the net was split.
-			 *	Make sure both sides lose.
-			 */
-			if(nb->state==NB_OURS || nb->state==NB_COLLIDE)
-			{
-				nb->state=NR_COLLIDE;
-				nb_wakeup();
-				/*
-				 *	Kill the other copy too
-				 */
-				nb_defend(dev,name);	
-				/*
-				 *	Timer expiry will delete our
-				 *	record.
-				 */	
-				nb_start_timer(nb, NB_TIME_COLLIDED);
-			}		
-		}
-	}
-	kfree_skb(skb);
-}
-
-void netbeui_name_query(struct dev *dev, struct sk_buff *skb)
-{
-	char *name=...
-	struct nb_name *nb=nb_find_name(dev,name);
-	
-	if(nb!=NULL && nb->ours)
-	{
-		struct sk_buff *nskb=nb_alloc_skb(NB_CONTROL_LEN, GFP_ATOMIC);
-		if(nskb!=NULL)
-		{
-			/* Build a name reply packet */
-			nskb->dev = dev;
-			nskb->priority = TC_PRIO_CONTROL;
-			dev_queue_xmit(nskb);
-		}
-	}
-	kfree_skb(skb);
-}
-
diff -u --recursive --new-file v2.1.90/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.90/linux/net/netsyms.c	Tue Mar 17 22:18:16 1998
+++ linux/net/netsyms.c	Mon Mar 23 16:48:25 1998
@@ -273,7 +273,6 @@
 EXPORT_SYMBOL(tcp_rcv_state_process);
 EXPORT_SYMBOL(tcp_timewait_state_process);
 EXPORT_SYMBOL(tcp_do_sendmsg);
-EXPORT_SYMBOL(tcp_v4_build_header);
 EXPORT_SYMBOL(tcp_v4_rebuild_header);
 EXPORT_SYMBOL(tcp_v4_send_check);
 EXPORT_SYMBOL(tcp_v4_conn_request);
@@ -291,6 +290,9 @@
 EXPORT_SYMBOL(tcp_openreq_cachep);
 EXPORT_SYMBOL(ipv4_specific);
 EXPORT_SYMBOL(tcp_simple_retransmit);
+EXPORT_SYMBOL(tcp_transmit_skb);
+EXPORT_SYMBOL(tcp_connect);
+EXPORT_SYMBOL(tcp_make_synack);
 
 EXPORT_SYMBOL(xrlim_allow);
 
@@ -374,6 +376,7 @@
 
 /* support for loadable net drivers */
 #ifdef CONFIG_NET
+EXPORT_SYMBOL(loopback_dev);
 EXPORT_SYMBOL(register_netdevice);
 EXPORT_SYMBOL(unregister_netdevice);
 EXPORT_SYMBOL(register_netdev);
diff -u --recursive --new-file v2.1.90/linux/net/packet/af_packet.c linux/net/packet/af_packet.c
--- v2.1.90/linux/net/packet/af_packet.c	Tue Mar 17 22:18:16 1998
+++ linux/net/packet/af_packet.c	Tue Mar 24 10:28:39 1998
@@ -262,9 +262,9 @@
 			       struct scm_cookie *scm)
 {
 	struct sock *sk = sock->sk;
+	struct sockaddr_pkt *saddr=(struct sockaddr_pkt *)msg->msg_name;
 	struct sk_buff *skb;
 	struct device *dev;
-	struct sockaddr_pkt *saddr=(struct sockaddr_pkt *)msg->msg_name;
 	unsigned short proto=0;
 	int err;
 	
@@ -309,6 +309,7 @@
   		return -EMSGSIZE;
 
 	dev_lock_list();
+	err = -ENOBUFS;
 	skb = sock_wmalloc(sk, len+dev->hard_header_len+15, 0, GFP_KERNEL);
 
 	/*
@@ -318,10 +319,7 @@
 	 */
 	 
 	if (skb == NULL) 
-	{
-		dev_unlock_list();
-		return(-ENOBUFS);
-	}
+		goto out_unlock;
 	
 	/*
 	 *	Fill it in 
@@ -339,36 +337,32 @@
 		skb->data -= dev->hard_header_len;
 		skb->tail -= dev->hard_header_len;
 	}
+
+	/* Returns -EFAULT on error */
 	err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
 	skb->protocol = proto;
 	skb->dev = dev;
 	skb->priority = sk->priority;
-	dev_unlock_list();
+	if (err)
+		goto out_free;
+
+	err = -ENETDOWN;
+	if (!(dev->flags & IFF_UP))
+		goto out_free;
 
 	/*
 	 *	Now send it
 	 */
 
-	if (err)
-	{
-		err = -EFAULT;
-	}
-	else
-	{
-		if (!(dev->flags & IFF_UP))
-		{
-			err = -ENETDOWN;
-		}
-	}
-
-	if (err) 
-	{
-		kfree_skb(skb);
-		return err;
-	}
-
+	dev_unlock_list();
 	dev_queue_xmit(skb);
 	return(len);
+
+out_free:
+	kfree_skb(skb);
+out_unlock:
+	dev_unlock_list();
+	return err;
 }
 #endif
 
@@ -434,13 +428,12 @@
 			  struct scm_cookie *scm)
 {
 	struct sock *sk = sock->sk;
+	struct sockaddr_ll *saddr=(struct sockaddr_ll *)msg->msg_name;
 	struct sk_buff *skb;
 	struct device *dev;
-	struct sockaddr_ll *saddr=(struct sockaddr_ll *)msg->msg_name;
 	unsigned short proto;
-	int ifindex;
-	int err;
-	int reserve = 0;
+	unsigned char *addr;
+	int ifindex, err, reserve = 0;
 	
 	/*
 	 *	Check the flags. 
@@ -454,13 +447,15 @@
 	 */
 	 
 	if (saddr == NULL) {
-		ifindex = sk->protinfo.af_packet->ifindex;
-		proto = sk->num;
+		ifindex	= sk->protinfo.af_packet->ifindex;
+		proto	= sk->num;
+		addr	= NULL;
 	} else {
 		if (msg->msg_namelen < sizeof(struct sockaddr_ll)) 
 			return -EINVAL;
-		ifindex = saddr->sll_ifindex;
-		proto = saddr->sll_protocol;
+		ifindex	= saddr->sll_ifindex;
+		proto	= saddr->sll_protocol;
+		addr	= saddr->sll_addr;
 	}
 
 	dev = dev_get_by_index(ifindex);
@@ -474,55 +469,50 @@
 
 	dev_lock_list();
 
-	skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 0, msg->msg_flags&MSG_DONTWAIT, &err);
-
-	if (skb==NULL) {
-		dev_unlock_list();
-		return err;
-	}
+	skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 0, 
+				msg->msg_flags & MSG_DONTWAIT, &err);
+	if (skb==NULL)
+		goto out_unlock;
 
 	skb_reserve(skb, (dev->hard_header_len+15)&~15);
 	skb->nh.raw = skb->data;
 
 	if (dev->hard_header) {
-		if (dev->hard_header(skb, dev, ntohs(proto),
-				     saddr ? saddr->sll_addr : NULL,
-				     NULL, len) < 0
-		    && sock->type == SOCK_DGRAM) {
-			kfree_skb(skb);
-			dev_unlock_list();
-			return -EINVAL;
-		}
+		int res;
+		err = -EINVAL;
+		res = dev->hard_header(skb, dev, ntohs(proto), addr, NULL, len);
 		if (sock->type != SOCK_DGRAM) {
 			skb->tail = skb->data;
 			skb->len = 0;
-		}
+		} else if (res < 0)
+			goto out_free;
 	}
 
+	/* Returns -EFAULT on error */
 	err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
 	skb->protocol = proto;
 	skb->dev = dev;
 	skb->priority = sk->priority;
-	dev_unlock_list();
+	if (err)
+		goto out_free;
+
+	err = -ENETDOWN;
+	if (!(dev->flags & IFF_UP))
+		goto out_free;
 
 	/*
 	 *	Now send it
 	 */
 
-	if (err) {
-		err = -EFAULT;
-	} else {
-		if (!(dev->flags & IFF_UP))
-			err = -ENETDOWN;
-	}
-
-	if (err) {
-		kfree_skb(skb);
-		return err;
-	}
-
+	dev_unlock_list();
 	dev_queue_xmit(skb);
 	return(len);
+
+out_free:
+	kfree_skb(skb);
+out_unlock:
+	dev_unlock_list();
+	return err;
 }
 
 static void packet_destroy_timer(unsigned long data)
@@ -699,6 +689,7 @@
 static int packet_create(struct socket *sock, int protocol)
 {
 	struct sock *sk;
+	int err;
 
 	if (!suser())
 		return -EPERM;
@@ -711,27 +702,23 @@
 
 	sock->state = SS_UNCONNECTED;
 	MOD_INC_USE_COUNT;
+
+	err = -ENOBUFS;
 	sk = sk_alloc(AF_PACKET, GFP_KERNEL, 1);
-	if (sk == NULL) {
-		MOD_DEC_USE_COUNT;
-		return -ENOBUFS;
-	}
+	if (sk == NULL)
+		goto out;
 
 	sk->reuse = 1;
+	sock->ops = &packet_ops;
 #ifdef CONFIG_SOCK_PACKET
 	if (sock->type == SOCK_PACKET)
 		sock->ops = &packet_ops_spkt;
-	else
 #endif
-	sock->ops = &packet_ops;
 	sock_init_data(sock,sk);
 
 	sk->protinfo.af_packet = kmalloc(sizeof(struct packet_opt), GFP_KERNEL);
-	if (sk->protinfo.af_packet == NULL) {
-		sk_free(sk);
-		MOD_DEC_USE_COUNT;
-		return -ENOBUFS;
-	}
+	if (sk->protinfo.af_packet == NULL)
+		goto out_free;
 	memset(sk->protinfo.af_packet, 0, sizeof(struct packet_opt));
 	sk->zapped=0;
 	sk->family = AF_PACKET;
@@ -741,13 +728,11 @@
 	 *	Attach a protocol block
 	 */
 
+	sk->protinfo.af_packet->prot_hook.func = packet_rcv;
 #ifdef CONFIG_SOCK_PACKET
 	if (sock->type == SOCK_PACKET)
 		sk->protinfo.af_packet->prot_hook.func = packet_rcv_spkt;
-	else
 #endif
-	sk->protinfo.af_packet->prot_hook.func = packet_rcv;
-
 	sk->protinfo.af_packet->prot_hook.data = (void *)sk;
 
 	if (protocol) {
@@ -758,6 +743,12 @@
 
 	sklist_insert_socket(&packet_sklist, sk);
 	return(0);
+
+out_free:
+	sk_free(sk);
+out:
+	MOD_DEC_USE_COUNT;
+	return err;
 }
 
 /*
@@ -832,10 +823,8 @@
 
 	/* We can't use skb_copy_datagram here */
 	err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
-	if (err) {
-		err = -EFAULT;
+	if (err)
 		goto out_free;
-	}
 	sk->stamp=skb->stamp;
 
 	if (msg->msg_name)
@@ -932,37 +921,39 @@
 
 static int packet_mc_add(struct sock *sk, struct packet_mreq *mreq)
 {
-	int err;
 	struct packet_mclist *ml, *i;
 	struct device *dev;
+	int err;
 
 	rtnl_shlock();
 
-	dev = dev_get_by_index(mreq->mr_ifindex);
-
-	i = NULL;
 	err = -ENODEV;
+	dev = dev_get_by_index(mreq->mr_ifindex);
 	if (!dev)
 		goto done;
+
 	err = -EINVAL;
 	if (mreq->mr_alen > dev->addr_len)
 		goto done;
 
+	err = -ENOBUFS;
 	i = (struct packet_mclist *)kmalloc(sizeof(*i), GFP_KERNEL);
+	if (i == NULL)
+		goto done;
 
+	err = 0;
 	for (ml=sk->protinfo.af_packet->mclist; ml; ml=ml->next) {
 		if (ml->ifindex == mreq->mr_ifindex &&
 		    ml->type == mreq->mr_type &&
 		    ml->alen == mreq->mr_alen &&
 		    memcmp(ml->addr, mreq->mr_address, ml->alen) == 0) {
 			ml->count++;
-			err = 0;
+			/* Free the new element ... */
+			kfree(i);
 			goto done;
 		}
 	}
-	err = -ENOBUFS;
-	if (i == NULL)
-		goto done;
+
 	i->type = mreq->mr_type;
 	i->ifindex = mreq->mr_ifindex;
 	i->alen = mreq->mr_alen;
@@ -971,13 +962,9 @@
 	i->next = sk->protinfo.af_packet->mclist;
 	sk->protinfo.af_packet->mclist = i;
 	packet_dev_mc(dev, i, +1);
-	i = NULL;
-	err = 0;
 
 done:
 	rtnl_shunlock();
-	if (i)
-		kfree(i);
 	return err;
 }
 
@@ -1109,13 +1096,12 @@
 		case FIOGETOWN:
 		case SIOCGPGRP:
 			return put_user(sk->proc, (int *)arg);
-			return(0);			
 		case SIOCGSTAMP:
 			if(sk->stamp.tv_sec==0)
 				return -ENOENT;
-			err = copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval));
-			if (err)
-				err = -EFAULT;
+			err = -EFAULT;
+			if (!copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)))
+				err = 0;
 			return err;
 		case SIOCGIFFLAGS:
 #ifndef CONFIG_INET
diff -u --recursive --new-file v2.1.90/linux/net/socket.c linux/net/socket.c
--- v2.1.90/linux/net/socket.c	Tue Mar 17 22:18:16 1998
+++ linux/net/socket.c	Tue Mar 24 10:28:39 1998
@@ -646,15 +646,17 @@
 		goto out;
 
 	retval = get_fd(sock->inode);
-	if (retval < 0) {
-		sock_release(sock);
-		goto out;
-	}
+	if (retval < 0)
+		goto out_release;
+	sock->file = fcheck(retval);
 
-	sock->file = current->files->fd[retval];
 out:
 	unlock_kernel();
 	return retval;
+
+out_release:
+	sock_release(sock);
+	goto out;
 }
 
 /*
@@ -787,9 +789,8 @@
 {
 	struct inode *inode;
 	struct socket *sock, *newsock;
-	int err;
+	int err, len;
 	char address[MAX_SOCK_ADDR];
-	int len;
 
 	lock_kernel();
 	sock = sockfd_lookup(fd, &err);
@@ -815,7 +816,7 @@
 
 	if ((err = get_fd(inode)) < 0) 
 		goto out_release;
-	newsock->file = current->files->fd[err];
+	newsock->file = fcheck(err);
 
 	if (upeer_sockaddr)
 	{
@@ -1141,19 +1142,21 @@
 	char address[MAX_SOCK_ADDR];
 	struct iovec iov[UIO_FASTIOV];
 	unsigned char ctl[sizeof(struct cmsghdr) + 20];	/* 20 is size of ipv6_pktinfo */
-	struct msghdr msg_sys;
-	int err= -EINVAL;
-	int total_len;
 	unsigned char *ctl_buf = ctl;
+	struct msghdr msg_sys;
+	int err, total_len;
 	
 	lock_kernel();
 
-	err=-EFAULT;
+	err = -EFAULT;
 	if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr)))
 		goto out; 
+
 	/* do not move before msg_sys is valid */
-	if (msg_sys.msg_iovlen>UIO_MAXIOV)
+	err = -EINVAL;
+	if (msg_sys.msg_iovlen > UIO_MAXIOV)
 		goto out;
+
 	/* This will also move the address data into kernel space */
 	err = verify_iovec(&msg_sys, iov, address, VERIFY_READ);
 	if (err < 0) 
@@ -1163,7 +1166,7 @@
 
 	sock = sockfd_lookup(fd, &err);
 	if (!sock) 
-		goto out; 
+		goto out_freeiov;
 
 	if (msg_sys.msg_controllen) 
 	{
@@ -1197,9 +1200,10 @@
 	if (ctl_buf != ctl)    
 		sock_kfree_s(sock->sk, ctl_buf, msg_sys.msg_controllen);
 failed2:
+	sockfd_put(sock);
+out_freeiov:
 	if (msg_sys.msg_iov != iov)
 		kfree(msg_sys.msg_iov);
-	sockfd_put(sock);
 out:       
 	unlock_kernel();
 	return err;
@@ -1228,16 +1232,13 @@
 	int *uaddr_len;
 	
 	lock_kernel();
+	err=-EFAULT;
 	if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr)))
-	{
-		err=-EFAULT;
 		goto out;
-	}
-	if (msg_sys.msg_iovlen>UIO_MAXIOV)
-	{
-		err=-EINVAL;
+
+	err=-EINVAL;
+	if (msg_sys.msg_iovlen > UIO_MAXIOV)
 		goto out;
-	}
 	
 	/*
 	 *	Save the user-mode address (verify_iovec will change the
diff -u --recursive --new-file v2.1.90/linux/net/sunrpc/auth_unix.c linux/net/sunrpc/auth_unix.c
--- v2.1.90/linux/net/sunrpc/auth_unix.c	Mon Apr  7 11:35:33 1997
+++ linux/net/sunrpc/auth_unix.c	Sun Mar 22 15:06:14 1998
@@ -14,11 +14,12 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/auth.h>
 
+#define NFS_NGROUPS	16
 struct unx_cred {
 	struct rpc_cred		uc_base;
 	uid_t			uc_fsuid;
 	gid_t			uc_gid, uc_fsgid;
-	gid_t			uc_gids[16];
+	gid_t			uc_gids[NFS_NGROUPS];
 };
 #define uc_uid			uc_base.cr_uid
 #define uc_count		uc_base.cr_count
@@ -84,12 +85,18 @@
 		cred->uc_gid = cred->uc_fsgid = 0;
 		cred->uc_gids[0] = NOGROUP;
 	} else {
+		int groups = current->ngroups;
+		if (groups > NFS_NGROUPS)
+			groups = NFS_NGROUPS;
+
 		cred->uc_uid = current->uid;
 		cred->uc_gid = current->gid;
 		cred->uc_fsuid = current->fsuid;
 		cred->uc_fsgid = current->fsgid;
-		for (i = 0; i < 16 && i < NGROUPS; i++)
+		for (i = 0; i < groups; i++)
 			cred->uc_gids[i] = (gid_t) current->groups[i];
+		if (i < NFS_NGROUPS)
+		  cred->uc_gids[i] = NOGROUP;
 	}
 
 	return (struct rpc_cred *) cred;
@@ -135,13 +142,18 @@
 	int		i;
 
 	if (!RPC_DO_ROOTOVERRIDE(task)) {
+		int groups;
+
 		if (cred->uc_uid != current->uid
 		 || cred->uc_gid != current->gid
 		 || cred->uc_fsuid != current->fsuid
 		 || cred->uc_fsgid != current->fsgid)
 			return 0;
 
-		for (i = 0; i < 16 && i < NGROUPS; i++)
+		groups = current->ngroups;
+		if (groups > NFS_NGROUPS)
+			groups = NFS_NGROUPS;
+		for (i = 0; i < groups ; i++)
 			if (cred->uc_gids[i] != (gid_t) current->groups[i])
 				return 0;
 		return 1;
diff -u --recursive --new-file v2.1.90/linux/net/sunrpc/svcsock.c linux/net/sunrpc/svcsock.c
--- v2.1.90/linux/net/sunrpc/svcsock.c	Sat Jan 17 12:32:42 1998
+++ linux/net/sunrpc/svcsock.c	Mon Mar 23 16:48:25 1998
@@ -866,8 +866,7 @@
 
 	/* Register socket with portmapper */
 	if (*errp >= 0 && pmap_register)
-		*errp = svc_register(serv, inet->protocol,
-					ntohs(inet->dummy_th.source));
+		*errp = svc_register(serv, inet->protocol, ntohs(inet->sport));
 
 	if (*errp < 0) {
 		inet->user_data = NULL;
diff -u --recursive --new-file v2.1.90/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.1.90/linux/net/unix/af_unix.c	Tue Mar 17 22:18:16 1998
+++ linux/net/unix/af_unix.c	Tue Mar 24 10:30:11 1998
@@ -687,17 +687,19 @@
 	 
 		skb=sock_alloc_send_skb(sk, len, 0, nonblock, &err); /* Marker object */
 		if(skb==NULL)
-			return err;
+			goto out;
 		memcpy(&UNIXCB(skb), cmsg, sizeof(*cmsg));
-		if (len)
-			memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
+		if (len) {
+			err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov,
+						len);
+			if (err)
+				goto out_free;
+		}
+
 		sk->state=TCP_CLOSE;
 		other=unix_find_other(sunaddr, addr_len, sk->type, hash, &err);
 		if(other==NULL)
-		{
-			kfree_skb(skb);
-			return err;
-		}
+			goto out_free;
 		other->ack_backlog++;
 		unix_peer(sk)=other;
 		skb_queue_tail(&other->receive_queue,skb);
@@ -738,6 +740,11 @@
 	if (!sk->protinfo.af_unix.addr)
 		unix_autobind(sock);
 	return 0;
+
+out_free:
+	kfree_skb(skb);
+out:
+	return err;
 }
 
 
@@ -908,8 +915,8 @@
 			      struct scm_cookie *scm)
 {
 	struct sock *sk = sock->sk;
-	unix_socket *other;
 	struct sockaddr_un *sunaddr=msg->msg_name;
+	unix_socket *other;
 	int namelen = 0; /* fake GCC */
 	int err;
 	unsigned hash;
@@ -918,7 +925,7 @@
 	if (msg->msg_flags&MSG_OOB)
 		return -EOPNOTSUPP;
 
-	if (msg->msg_flags&~MSG_DONTWAIT)
+	if (msg->msg_flags&~(MSG_DONTWAIT|MSG_NOSIGNAL))
 		return -EINVAL;
 
 	if (msg->msg_namelen) {
@@ -935,9 +942,8 @@
 		unix_autobind(sock);
 
 	skb = sock_alloc_send_skb(sk, len, 0, msg->msg_flags&MSG_DONTWAIT, &err);
-		
 	if (skb==NULL)
-		return err;
+		goto out;
 
 	memcpy(UNIXCREDS(skb), &scm->creds, sizeof(struct ucred));
 	UNIXCB(skb).attr = msg->msg_flags;
@@ -945,7 +951,9 @@
 		unix_attach_fds(scm, skb);
 
 	skb->h.raw = skb->data;
-	memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
+	err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
+	if (err)
+		goto out_free;
 
 	other = unix_peer(sk);
 	if (other && other->dead)
@@ -957,26 +965,18 @@
 		unix_unlock(other);
 		unix_peer(sk)=NULL;
 		other = NULL;
-		if (sunaddr == NULL) {
-			kfree_skb(skb);
-			return -ECONNRESET;
-		}
+		err = -ECONNRESET;
+		if (sunaddr == NULL)
+			goto out_free;
 	}
 	if (!other)
 	{
 		other = unix_find_other(sunaddr, namelen, sk->type, hash, &err);
-		
 		if (other==NULL)
-		{
-			kfree_skb(skb);
-			return err;
-		}
+			goto out_free;
+		err = -EINVAL;
 		if (!unix_may_send(sk, other))
-		{
-			unix_unlock(other);
-			kfree_skb(skb);
-			return -EINVAL;
-		}
+			goto out_unlock;
 	}
 
 	skb_queue_tail(&other->receive_queue, skb);
@@ -985,6 +985,13 @@
 	if (!unix_peer(sk))
 		unix_unlock(other);
 	return len;
+
+out_unlock:
+	unix_unlock(other);
+out_free:
+	kfree_skb(skb);
+out:
+	return err;
 }
 
 		
@@ -1005,7 +1012,7 @@
 	if (msg->msg_flags&MSG_OOB)
 		return -EOPNOTSUPP;
 
-	if (msg->msg_flags&~MSG_DONTWAIT)
+	if (msg->msg_flags&~(MSG_DONTWAIT|MSG_NOSIGNAL))
 		return -EINVAL;
 
 	if (msg->msg_namelen) {
@@ -1020,7 +1027,8 @@
 	}
 
 	if (sk->shutdown&SEND_SHUTDOWN) {
-		send_sig(SIGPIPE,current,0);
+		if (!(msg->msg_flags&MSG_NOSIGNAL))
+			send_sig(SIGPIPE,current,0);
 		return -EPIPE;
 	}
 
@@ -1085,7 +1093,8 @@
 			kfree_skb(skb);
 			if(sent)
 				goto out;
-			send_sig(SIGPIPE,current,0);
+			if (!(msg->msg_flags&MSG_NOSIGNAL))
+				send_sig(SIGPIPE,current,0);
 			return -EPIPE;
 		}
 
@@ -1265,9 +1274,7 @@
 		}
 
 		chunk = min(skb->len, size);
-		/* N.B. This could fail with a non-zero value (which means -EFAULT
-		 *      and the non-zero value is the number of bytes not copied).
-		 */
+		/* N.B. This could fail with -EFAULT */
 		memcpy_toiovec(msg->msg_iov, skb->data, chunk);
 		copied += chunk;
 		size -= chunk;
