diff -u --recursive --new-file v2.2.0-pre7/linux/CREDITS linux/CREDITS
--- v2.2.0-pre7/linux/CREDITS	Wed Jan 13 15:00:40 1999
+++ linux/CREDITS	Fri Jan 15 14:36:20 1999
@@ -16,6 +16,13 @@
 S: (ask for current address)
 S: Finland
 
+N: Dave Airlie
+E: airlied@linux.ie
+W: http://www.csn.ul.ie/~airlied
+D: NFS over TCP patches
+S: University of Limerick
+S: Ireland
+
 N: Werner Almesberger
 E: werner.almesberger@lrc.di.epfl.ch
 D: dosfs, LILO, some fd features, various other hacks here and there
@@ -1720,11 +1727,11 @@
 S: USA
 
 N: Mike Shaver
-E: shaver@netscape.com
-W: http://people.netscape.com/shaver/
+E: shaver@hungry.org
+W: http://www.hungry.org/~shaver/
 D: MIPS work, /proc/sys/net, misc net hacking
 S: 149 Union St.
-S: Ottawa, Ontario
+S: Kingston, Ontario
 S: Canada K7L 2P4
 
 N: John Shifflett
diff -u --recursive --new-file v2.2.0-pre7/linux/Documentation/Changes linux/Documentation/Changes
--- v2.2.0-pre7/linux/Documentation/Changes	Tue Dec 22 14:16:53 1998
+++ linux/Documentation/Changes	Thu Jan 14 10:51:51 1999
@@ -85,14 +85,15 @@
 compiled under 2.0 or earlier kernels should be replaced with ones
 compiled under 2.1, for example.
 
-   As of 2.1.115, Unix98 pty support was added as an option, and
-support for the deprecated major 4 /dev/ttyp* devices was removed.  If
-necessary (eg, you get "out of pty" error messages when you obviously
-are not out of pty's), create major 3 /dev/tty* and major 2 /dev/pty*
-devices (see Documentation/devices.txt for more information).  If you
-want to use the Unix98 ptys, you should be running at least
-glibc-2.0.9x, and you must switch completely to Unix98 pty's.  The
-general procedure for configuring Unix98 pty support is:
+   As of 2.1.115, support for the deprecated major 4 /dev/ttyp* devices 
+was removed.  If necessary (eg, you get "out of pty" error messages when 
+you obviously are not out of pty's), create major 3 /dev/tty* and major 2 
+/dev/pty* devices (see Documentation/devices.txt for more information).  
+
+   Optional support for Unix98 pty devices has also been added. If you
+want to use the Unix98 ptys, you should be running at least glibc-2.0.9x, 
+and you must switch completely to Unix98 pty's.  The general procedure 
+for configuring Unix98 pty support is:
 
 - Compile your kernel with CONFIG_UNIX98_PTYS and CONFIG_DEVPTS_FS.
 - mknod /dev/ptmx c 5 2
diff -u --recursive --new-file v2.2.0-pre7/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.2.0-pre7/linux/Documentation/Configure.help	Wed Jan 13 15:00:41 1999
+++ linux/Documentation/Configure.help	Sat Jan 16 16:57:53 1999
@@ -1343,8 +1343,8 @@
   boot time ("man dmesg"), please follow the instructions at the top
   of include/linux/pci.h.
 
-PCI BIOS support
-CONFIG_PCI_BIOS
+PCI access mode
+CONFIG_PCI_GOBIOS
   If you have enabled PCI bus support above, you probably want to
   allow Linux to use your PCI BIOS to detect the PCI devices and
   determine their configuration. Note: some old PCI motherboards have
@@ -1355,8 +1355,6 @@
   Except for some special cases (embedded systems with no BIOS), you
   probably should say Y here.
 
-PCI direct access support
-CONFIG_PCI_DIRECT
   If you don't want to use the PCI BIOS (e.g., because you run some
   embedded system with no BIOS at all) or Linux says it cannot use
   your PCI BIOS, you can enable direct PCI hardware here. It might
@@ -1730,6 +1728,18 @@
   Say N unless you have such a graphics board or plan to get one
   before you next recompile the kernel.
 
+Apollo support
+CONFIG_APOLLO
+  Say Y here if you want to run Linux on an MC680x0-based Apollo
+  Domain workstation such as the DN3500.
+
+Apollo 3c505 support
+CONFIG_APOLLO_ELPLUS
+  Say Y or M here if your Apollo has a 3Com 3c505 ISA Ethernet card.
+  If you don't have one made for Apollos, you can use one from a PC,
+  except that your Apollo won't be able to boot from it (because the
+  code in the ROM will be for a PC).
+
 Atari native chipset support
 CONFIG_FB_ATARI
   This is the frame buffer device driver for the builtin graphics
@@ -1799,7 +1809,7 @@
 
 Matrox unified accelerated driver
 CONFIG_FB_MATROX
-  Say Y here  if you have  Matrox Millennium,  Matrox Milleinnium  II, 
+  Say Y here  if you have  Matrox Millennium,  Matrox Millennium  II, 
   Matrox Mystique, Matrox Mystique 220, Matrox Productiva G100, Matrox
   Mystique G200, Matrox Millennium G200  or Matrox Marvel G200 in your 
   box.  At this time, G100,  Mystique G200 and  Marvel G200 support is
@@ -1807,7 +1817,7 @@
   matroxfb.o will  be created.  You can pass parameters into driver if 
   it is compiled  into kernel by  specifying "video=matrox:XXX", where 
   meaning of XXX you can found at the end of main source file
-  (drivers/video/matroxfb.c)  at  boottime.  Same  parameters  can  be 
+  (drivers/video/matroxfb.c)  at  boot time.  Same  parameters  can  be 
   passed into insmod if driver is used as module.
 
 Matrox Millennium support
@@ -1826,9 +1836,10 @@
   and  32 bpp  packed pixel.  You can  also use  font widths  different 
   from 8.
 
-Matrox G100 support
+Matrox G100/G200 support
 CONFIG_FB_MATROX_G100
-  Say Y  here if  you have  Matrox Productiva G100  in the box.  If you
+  Say Y  here if you have Matrox Productiva G100, Matrox Mystique G200,
+  Matrox  Marvel G200  or  Matrox  Millennium G200  in the box.  If you
   select  "Advanced lowlevel  driver options",  you should  check 8 bpp
   packed pixel,  16 bpp packed  pixel, 24 bpp  packed pixel  and 32 bpp
   packed pixel. You can also use font widths different from 8.
@@ -3588,11 +3599,12 @@
 
 Adaptec AHA152X/2825 support
 CONFIG_SCSI_AHA152X
-  This is support for the AHA-1510, AHA-1520, AHA-1522, and AHA-2825
-  SCSI host adapters. It is explained in section 3.3 of the
-  SCSI-HOWTO, available via FTP (user: anonymous) at
-  ftp://metalab.unc.edu/pub/Linux/docs/HOWTO. You might also want to
-  read the comments at the top of drivers/scsi/aha152x.c.
+  This is support for the AVA-1505 (irq etc must be manually specified), 
+  AHA-1510, AHA-1520, AHA-1522, and AHA-2825 SCSI host adapters. It is 
+  explained in section 3.3 of the SCSI-HOWTO, available via FTP 
+  (user: anonymous) at ftp://metalab.unc.edu/pub/Linux/docs/HOWTO. You 
+  might also want to read the comments at the top of 
+  drivers/scsi/aha152x.c.
 
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
@@ -3799,7 +3811,7 @@
 enable elevator sorting
 CONFIG_SCSI_U14_34F_LINKED_COMMANDS
   This option enables elevator sorting for all probed SCSI disks and 
-  CDROMs. It definetly reduces the average seek distance when doing
+  CDROMs. It definitely reduces the average seek distance when doing
   random seeks, but this does not necessarily results in a noticeable
   performance improvement: your mileage may vary...
   The safe answer is N.
@@ -4008,7 +4020,7 @@
   that can be queued to any device, when tagged command queuing is
   possible. The default value is 32. Minimum is 2, maximum is 64.
   Modern hard disks are able to support 64 tags and even more, but 
-  donnot seem to be faster when more than 32 tags are being used.
+  do not seem to be faster when more than 32 tags are being used.
   
   So, the normal answer here is to go with the default value 32 unless
   you are using very large hard disks with large cache (>= 1 MB) that
@@ -4144,6 +4156,17 @@
   say M here and read Documentation/modules.txt. The module will be
   called initio.o
 
+Initio 91XXU(W) SCSI support
+CONFIG_SCSI_INITIO
+  This is support for the Initio 91XXU(W) SCSI host adapter.
+  Please read the SCSI-HOWTO, available via FTP (user: anonymous) at
+  ftp://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. The module will be
+  called initio.o
+
 PAS16 SCSI support
 CONFIG_SCSI_PAS16
   This is support for a SCSI host adapter. It is explained in section
@@ -4308,7 +4331,7 @@
 CONFIG_SCSI_EATA_LINKED_COMMANDS
   This option enables elevator sorting for all probed SCSI disks and 
   CDROMs. It definetly reduces the average seek distance when doing
-  random seeks, but this does not necessarily results in a noticeable
+  random seeks, but this does not necessarily result in a noticeable
   performance improvement: your mileage may vary...
   The safe answer is N.
 
@@ -5195,27 +5218,6 @@
   The module will be called cosa.o. For general information about
   modules read Documentation/modules.txt.
 
-COSA/SRP sync serial boards support
-CONFIG_COSA
-  This is a driver for COSA and SRP synchronous serial boards.
-  These boards enable to connect synchronous serial devices (for
-  example base-band modems, or any other device with the X.21, V.24,
-  V.35 or V.36 interface) to your Linux box. The cards can work
-  as the character device, synchronous PPP network device, or the Cisco
-  HDLC network device.
-
-  To actually use the COSA or SRP board, you will need user-space
-  utilities for downloading the firmware to the cards and to set 
-  them up. Look at the http://www.fi.muni.cz/~kas/cosa/ for more
-  information about the cards (including the pointer to the user-space
-  utilities). You can also read the comment at the top of the
-  drivers/net/cosa.c for details about the cards and the driver itself.
-
-  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 cosa.o. For general information about
-  modules read Documentation/modules.txt.
-
 WAN Drivers
 CONFIG_WAN_DRIVERS
   Say Y to this option if your Linux box contains a WAN card and you
@@ -6275,16 +6277,6 @@
   under Linux, say Y here (you must also remember to enable the driver
   for your HIPPI card below). Most people will say N here.
   
-CERN HIPPI PCI adapter support
-CONFIG_CERN_HIPPI
-  Say Y here if this is your PCI HIPPI network card.
-
-  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 cern_hippi.o. If you want to compile it as
-  a module, say M here and read Documentation/modules.txt. If unsure,
-  say N.
-
 Essential RoadRunner HIPPI PCI adapter support
 CONFIG_ROADRUNNER
   Say Y here if this is your PCI HIPPI network card.
@@ -7024,7 +7016,7 @@
 
   To use the automounter you need the user-space tools from
   ftp://ftp.kernel.org/pub/linux/daemons/autofs; you also want to
-  answer Y to "NFS filesystem support", above.
+  answer Y to "NFS filesystem support", below.
 
   If you want to compile this as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
@@ -7040,7 +7032,7 @@
   OpenBSD and NeXTstep) use a filesystem called UFS. Some System V
   Unixes can create and mount hard disk partitions and diskettes using
   this filesystem as well. Saying Y here will allow you to read from
-  and write to these partitions and diskettes.
+  these partitions and diskettes.
 
   If you only intend to mount files from some other Unix over the
   network using NFS, you don't need the UFS filesystem support (but
@@ -7062,6 +7054,12 @@
 
   If you haven't heard about all of this before, it's safe to say N.
 
+UFS filesystem write support
+CONFIG_UFS_FS_WRITE
+  You will be able to write to 4.4BSD (e.g. FreeBSD, NetBSD and
+  OpenBSD) and SunOS partitions and diskettes if you say Y to this
+  option in addition to "UFS filesystem support", above.
+
 BSD disklabel (FreeBSD partition tables) support
 CONFIG_BSD_DISKLABEL
   FreeBSD uses its own hard disk partition scheme on your PC. It
@@ -8499,7 +8497,7 @@
 CONFIG_APM_IGNORE_SUSPEND_BOUNCE
   This option is necessary on the Dell Inspiron 3200 and others, but
   should be safe for all other laptops. When enabled, a system suspend
-  event that occurs within one second of a resume is ignored. Without
+  event that occurs within three seconds of a resume is ignored. Without
   this the Inspiron will shut itself off a few seconds after you open
   the lid, requiring you to press the power button to resume it a
   second time.
@@ -9265,7 +9263,7 @@
 CONFIG_AEDSP16_MPU401
   Answer Y if you want your audio card to emulate the MPU-401 midi
   interface. You should then say Y to "MPU-401 support".
-  You have to hote that the I/O base for MPU-401 support of aedsp16 is
+  You have to note that the I/O base for MPU-401 support of aedsp16 is
   the same you have selected for "MPU-401 support". If you are using
   this driver as a module you have to specify the MPU I/O base address
   with the parameter 'mpu_base=0xNNN'.
@@ -10542,7 +10540,7 @@
 
 Initial kernel command line
 CONFIG_CMDLINE
-  On some architectures (EBSA285, EBSA110 and Corel Netwinder), there is
+  On some architectures (EBSA285, EBSA110 and Corel NetWinder), there is
   currently no way for the boot loader to pass arguments to the kernel.
   For these architectures, you should supply some command-line options
   at build time by entering them here.  As a minimum, you should specify
@@ -10599,7 +10597,7 @@
 IrLAP Compression support
 CONFIG_IRDA_COMPRESSION
   Compression is _not_ part of the IrDA(tm) protocol specification,
-  but its working great! Linux is the first to try out compresson
+  but it's working great! Linux is the first to try out compression
   support at the IrLAP layer. This means that you will only benefit
   from compression if you are running a Linux <-> Linux configuration
 
diff -u --recursive --new-file v2.2.0-pre7/linux/Documentation/cdrom/cdrom-standard.tex linux/Documentation/cdrom/cdrom-standard.tex
--- v2.2.0-pre7/linux/Documentation/cdrom/cdrom-standard.tex	Sat May  2 14:19:50 1998
+++ linux/Documentation/cdrom/cdrom-standard.tex	Mon Jan 18 12:47:33 1999
@@ -23,8 +23,9 @@
 \begin{document}
 \title{A \linux\ \cdrom\ standard}
 \author{David van Leeuwen\\{\normalsize\tt david@ElseWare.cistron.nl}
-\\{\footnotesize updated by Erik Andersen {\tt(andersee@debian.org)}}}
-\date{19 November 1997}
+\\{\footnotesize updated by Erik Andersen {\tt(andersee@debian.org)}}
+\\{\footnotesize updated by Jens Axboe {\tt(axboe@image.dk)}}}
+\date{11 January 1999}
 
 \maketitle
 
@@ -145,7 +146,7 @@
 low-level \cdrom\ device drivers. The \UCD\ now provides another
 software-level, that separates the $ioctl()$ and $open()$ implementation
 from the actual hardware implementation. Note that this effort has
-made few changes which will effect a user's application programs. The
+made few changes which will affect a user's application programs. The
 greatest change involved moving the contents of the various low-level
 \cdrom\ drivers' header files to the kernel's cdrom directory. This was
 done to help ensure that the user is only presented with only one cdrom
@@ -232,7 +233,7 @@
 struct& cdrom_device_ops\ \{ \hidewidth\cr
   &int& (* open)(struct\ cdrom_device_info *, int)\cr
   &void& (* release)(struct\ cdrom_device_info *);\cr 
-  &int& (* drive_status)(struct\ cdrom_device_info *);\cr     
+  &int& (* drive_status)(struct\ cdrom_device_info *, int);\cr     
   &int& (* media_changed)(struct\ cdrom_device_info *, int);\cr 
   &int& (* tray_move)(struct\ cdrom_device_info *, int);\cr
   &int& (* lock_door)(struct\ cdrom_device_info *, int);\cr
@@ -286,12 +287,13 @@
 \noalign{\medskip}
   & kdev_t&  dev;& device number (incorporates minor)\cr
   & int& mask;& mask of capability: disables them \cr
-  &const\ int& speed;& maximum speed for reading data \cr
-  &const\ int& capacity;& number of discs in a jukebox \cr
+  & int& speed;& maximum speed for reading data \cr
+  & int& capacity;& number of discs in a jukebox \cr
 \noalign{\medskip}
   &int& options : 30;& options flags \cr
-  &long& mc_flags : 2;& media-change buffer flags \cr
+  &unsigned& mc_flags : 2;& media-change buffer flags \cr
   & int& use_count;& number of times device is opened\cr
+  & char& name[20];& name of the device type\cr
 \}\cr
 }$$
 Using this $struct$, a linked list of the registered minor devices is
@@ -371,13 +373,13 @@
 allocated buffers in the VFS is taken care of by the routine in
 \cdromc.  This is the only function returning type $void$. 
 
-\subsection{$Int\ drive_status(struct\ cdrom_device_info * cdi)$}
+\subsection{$Int\ drive_status(struct\ cdrom_device_info * cdi, int\ slot_nr)$}
 \label{drive status}
 
 The function $drive_status$, if implemented, should provide
 information on the status of the drive (not the status of the disc,
-which may or may not be in the drive). In \cdromh\ the possibilities
-are listed: 
+which may or may not be in the drive). If the drive is not a changer,
+$slot_nr$ should be ignored. In \cdromh\ the possibilities are listed: 
 $$
 \halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr
 CDS_NO_INFO& no information available\cr
@@ -810,7 +812,7 @@
 
 
 \subsection{$Int\ cdrom_ioctl(struct\ inode *ip, struct\ file *fp,
-                       unsigned\ int\ cmd, unsigned\ long\ arg)$}
+unsigned\ int\ cmd, unsigned\ long\ arg)$}
 \label{cdrom-ioctl}
 
 This function handles all the standard $ioctl$ requests for \cdrom\
@@ -833,7 +835,7 @@
 \item[CDROMEJECT_SW] If $arg\not=0$, set behavior to auto-close (close
 tray on first open) and auto-eject (eject on last release), otherwise
 set behavior to non-moving on $open()$ and $release()$ calls.
-\item[CDROM_GET_MCN or CDROM_GET_UPC] Get the Media Catalog Number from a CD.
+\item[CDROM_GET_MCN] Get the Media Catalog Number from a CD.
 \end{description}
 
 \subsubsection{$Ioctl$s routed through $audio_ioctl()$}
@@ -931,7 +933,6 @@
     CDS_NO_DISC& no disc is inserted, or tray is opened\cr
     CDS_AUDIO& Audio disc (2352 audio bytes/frame)\cr
     CDS_DATA_1& data disc, mode 1 (2048 user bytes/frame)\cr
-    CDS_DATA_2& data disc, mode 2 (2336 user bytes/frame)\cr
     CDS_XA_2_1& mixed data (XA), mode 2, form 1 (2048 user bytes)\cr
     CDS_XA_2_2& mixed data (XA), mode 2, form 1 (2324  user bytes)\cr
     CDS_MIXED& mixed audio/data disc\cr
@@ -942,6 +943,7 @@
 
 \item[CDROM_CHANGER_NSLOTS] Returns the number of slots in a
   juke-box. 
+\item[CDROMRESET] Reset the drive. 
 \end{description}
 
 \subsubsection{Device dependent $ioctl$s}
diff -u --recursive --new-file v2.2.0-pre7/linux/Documentation/cdrom/ide-cd linux/Documentation/cdrom/ide-cd
--- v2.2.0-pre7/linux/Documentation/cdrom/ide-cd	Sun Nov  8 14:02:41 1998
+++ linux/Documentation/cdrom/ide-cd	Mon Jan 18 12:47:33 1999
@@ -2,7 +2,6 @@
 Originally by scott snyder  <snyder@fnald0.fnal.gov> (19 May 1996)
 Carrying on the torch is: Erik Andersen <andersee@debian.org>
 New maintainers (19 Oct 1998): Jens Axboe <axboe@image.dk>
-                               Chris Zwilling <chris@cloudnet.com>
 
 1. Introduction
 ---------------
diff -u --recursive --new-file v2.2.0-pre7/linux/Documentation/fb/matroxfb.txt linux/Documentation/fb/matroxfb.txt
--- v2.2.0-pre7/linux/Documentation/fb/matroxfb.txt	Fri Jan  8 22:35:58 1999
+++ linux/Documentation/fb/matroxfb.txt	Fri Jan 15 13:56:49 1999
@@ -22,36 +22,61 @@
 How to use it?
 ==============
 
-Switching modes is done using the video=matrox:vesa=... boot parameter
+Switching modes is done using the video=matrox:vesa:... boot parameter
 or using `fbset' program.
 
+If you want, for example, enable a resolution of 1280x1024x24bpp you should
+pass to the kernel this command line: "video=matrox:vesa:0x1BB".
+Note that the same line, if 'appended' as a lilo parameter in lilo.conf will
+read "video=matrox:vesa:443" because lilo pass integer parameters as decimal
+numbers to the kernel.
+
 You should compile in both vgacon (to boot if you remove you Matrox from
 box) and matroxfb (for graphics mode). You should not compile-in vesafb
 unless you have primary display on non-Matrox VBE2.0 device (see 
 Documentation/vesafb.txt for details).
 
-Currently supported video modes are (through vesa=... interface, PowerMac
-has (as addon) compatibility code.
+Currently supported video modes are (through vesa:... interface, PowerMac
+has [as addon] compatibility code):
+
+
+[Graphic modes]
+
+bpp | 640x400  640x480  768x576  800x600  960x720
+----+--------------------------------------------
+  4 |            0x12             0x102            
+  8 |  0x100    0x101    0x180    0x103    0x188   
+ 15 |           0x110    0x181    0x113    0x189   
+ 16 |           0x111    0x182    0x114    0x18A   
+ 24 |           0x1B2    0x184    0x1B5    0x18C   
+ 32 |           0x112    0x183    0x115    0x18B   
+
+
+[Graphic modes (continued)]
 
-bpp | 640x400  640x480  768x576  800x600  960x720  1024x768 1152x864 1280x1024 1408x1056 1600x1200
-----+----------------------------------------------------------------------------------------------
-  4 |            0x12             0x102             0x104             0x106
-  8 |  0x100    0x101    0x180    0x103    0x188    0x105    0x190    0x107     0x198     0x11C
- 15 |           0x110    0x181    0x113    0x189    0x116    0x191    0x119     0x199     0x11D
- 16 |           0x111    0x182    0x114    0x18A    0x117    0x192    0x11A     0x19A     0x11E
- 24 |           0x1B2    0x184    0x1B5    0x18C    0x1B8    0x194    0x1BB     0x19C     0x1BF
- 32 |           0x112    0x183    0x115    0x18B    0x118    0x193    0x11B     0x19B
+bpp | 1024x768 1152x864 1280x1024 1408x1056 1600x1200
+----+------------------------------------------------
+  4 |   0x104             0x106
+  8 |   0x105    0x190    0x107     0x198     0x11C
+ 15 |   0x116    0x191    0x119     0x199     0x11D
+ 16 |   0x117    0x192    0x11A     0x19A     0x11E
+ 24 |   0x1B8    0x194    0x1BB     0x19C     0x1BF
+ 32 |   0x118    0x193    0x11B     0x19B
+
+
+[Text modes]
 
 text | 640x400  640x480  1056x344  1056x400  1056x480
 -----+------------------------------------------------
  8x8 |  0x1C0    0x108     0x10A     0x10B     0x10C
 8x16 | 2, 3, 7                       0x109
 
-You can enter these number either hexadecimal (leading `0x') or decimal (0x100 = 256). You can also
-use value + 512 to achieve compatibility with your old number passed to vesafb.
+You can enter these number either hexadecimal (leading `0x') or decimal
+(0x100 = 256). You can also use value + 512 to achieve compatibility
+with your old number passed to vesafb.
 
-Non-listed number can be achieved by more complicated command-line, for example
-1600x1200x32bpp can be specified by `video=matrox:vesa:0x11C,depth:32'.
+Non-listed number can be achieved by more complicated command-line, for
+example 1600x1200x32bpp can be specified by `video=matrox:vesa:0x11C,depth:32'.
 
 
 X11
@@ -70,8 +95,8 @@
 =======
 
 Driver contains SVGALib compatibility code. It is turned on by choosing textual
-mode for console. You can do it at boottime by using videomode 2,3,7,0x108-0x10C 
-or 0x1C0. At runtime, `fbset -depth 0' does this work.
+mode for console. You can do it at boottime by using videomode
+2,3,7,0x108-0x10C or 0x1C0. At runtime, `fbset -depth 0' does this work.
 Unfortunately, after SVGALib application exits, screen contents is corrupted.
 Switching to another console and back fixes it. I hope that it is SVGALib and
 not mine problem, but I'm not sure.
@@ -91,9 +116,9 @@
 	   memory usable for on-screen display (i.e. max. 8MB).
 disabled - do not load driver; you can use also `off', but `disabled'
            is here too.
-enabled  - load driver, if you have `video=matrox:disabled' in LILO configuration,
-           you can override it by this (you cannot override `off').
-	   It is default.
+enabled  - load driver, if you have `video=matrox:disabled' in LILO
+           configuration, you can override it by this (you cannot override
+	   `off'). It is default.
 noaccel  - do not use acceleration engine. It does not work on Alphas.
 accel    - use acceleration engine. It is default.
 nopan    - create initial consoles with vyres = yres, thus disabling virtual
@@ -102,95 +127,115 @@
            It is default.
 nopciretry - disable PCI retries. It is needed for some broken chipsets,
            it is autodetected for intel's 82437. In this case device does
-	   not comply to PCI 2.1 specs (it will not guarantee that every transaction
-	   terminate with success or retry in 32 PCLK).
+	   not comply to PCI 2.1 specs (it will not guarantee that every
+	   transaction terminate with success or retry in 32 PCLK).
 pciretry - enable PCI retries. It is default, except for intel's 82437.
 novga    - disables VGA I/O ports. It is default if BIOS did not enable device.
-           You should not use this option, some boards then do not restart without
-	   power off.
-vga      - preserve state of VGA I/O ports. It is default. Driver does not enable
-           VGA I/O if BIOS did not it (it is not safe to enable it in most cases).
+           You should not use this option, some boards then do not restart
+	   without power off.
+vga      - preserve state of VGA I/O ports. It is default. Driver does not
+           enable VGA I/O if BIOS did not it (it is not safe to enable it in
+	   most cases).
 nobios   - disables BIOS ROM. It is default if BIOS did not enable BIOS itself.
-           You should not use this option, some boards then do not restart without
-	   power off.
-bios     - preserve state of BIOS ROM. It is default. Driver does not enable BIOS
-           if BIOS was not enabled before.
-noinit   - tells driver, that devices were already initialized. You should use it
-           if you have G100 and/or if driver cannot detect memory, you see strange
-	   pattern on screen and so on. Devices not enabled by BIOS are still
-	   initialized. It is default.
+           You should not use this option, some boards then do not restart
+	   without power off.
+bios     - preserve state of BIOS ROM. It is default. Driver does not enable
+           BIOS if BIOS was not enabled before.
+noinit   - tells driver, that devices were already initialized. You should use
+           it if you have G100 and/or if driver cannot detect memory, you see
+	   strange pattern on screen and so on. Devices not enabled by BIOS
+	   are still initialized. It is default.
 init     - driver initializes every device it knows about.
 nomtrr   - disables write combining on frame buffer. This slows down driver but
-           there is reported minor incompatibility between GUS DMA and XFree under
-	   high loads if write combining is enabled (sound dropouts).
+           there is reported minor incompatibility between GUS DMA and XFree
+	   under high loads if write combining is enabled (sound dropouts).
 mtrr     - enables write combining on frame buffer. It speeds up video accesses
-           much. It is default. You must have MTRR support enabled in kernel and
-	   your CPU must have MTRR (f.e. Pentium II have them).
-sgram    - tells to driver that you have G200 with SGRAM memory. It has no effect 
-           without `init'.
-sdram    - tells to driver that you have G200 with SDRAM memory. It is a default.
-inv24    - change timings parameters for 24bpp modes on Millenium and Millenium II.
-           Specify this if you see strange color shadows around characters.
+           much. It is default. You must have MTRR support enabled in kernel
+	   and your CPU must have MTRR (f.e. Pentium II have them).
+sgram    - tells to driver that you have G200 with SGRAM memory. It has no
+           effect without `init'.
+sdram    - tells to driver that you have G200 with SDRAM memory.
+           It is a default.
+inv24    - change timings parameters for 24bpp modes on Millenium and
+           Millenium II. Specify this if you see strange color shadows around
+	   characters.
 noinv24  - use standard timmings. It is default.
 inverse  - invert colors on screen (for LCD displays)
 noinverse - show true colors on screen. It is default.
-dev:X    - bind driver to device X. Driver numbers device from 0 up to N, where device
-           0 is first `known' device found, 1 second and so on. lspci lists
-	   devices in this order.
-	   Default is `every' known device for driver with multihead support and
-	   first working device (usually dev:0) for driver without multihead support.
+dev:X    - bind driver to device X. Driver numbers device from 0 up to N,
+           where device 0 is first `known' device found, 1 second and so on.
+	   lspci lists devices in this order.
+	   Default is `every' known device for driver with multihead support
+	   and first working device (usually dev:0) for driver without
+	   multihead support.
 nohwcursor - disables hardware cursor (use software cursor instead).
-hwcursor - enables hardware cursor. It is default. If you are using non-accelerated mode
-           (`noaccel' or `fbset -accel false'), software cursor is used (except for
-	   text mode).
-noblink  - disables cursor blinking. Cursor in text mode always blinks (hw limitation).
+hwcursor - enables hardware cursor. It is default. If you are using
+           non-accelerated mode (`noaccel' or `fbset -accel false'), software
+	   cursor is used (except for text mode).
+noblink  - disables cursor blinking. Cursor in text mode always blinks (hw
+           limitation).
 blink    - enables cursor blinking. It is default.
 nofastfont - disables fastfont feature. It is default.
-fastfont:X - enables fastfont feature. X specifies size of memory reserved for font data,
-           it must be >= (fontwidth*fontheight*chars_in_font)/8. It is faster on
-	   Gx00 series, but slower on older cards.
-grayscale - enable grayscale summing. It works in PSEUDOCOLOR modes (text, 4bpp, 8bpp). In
-           DIRECTCOLOR modes it is limited to characters displayed through putc/putcs. Direct
-	   accesses to framebuffer can paint colors.
+fastfont:X - enables fastfont feature. X specifies size of memory reserved for
+             font data, it must be >= (fontwidth*fontheight*chars_in_font)/8.
+	     It is faster on Gx00 series, but slower on older cards.
+grayscale - enable grayscale summing. It works in PSEUDOCOLOR modes (text,
+            4bpp, 8bpp). In DIRECTCOLOR modes it is limited to characters
+	    displayed through putc/putcs. Direct accesses to framebuffer
+	    can paint colors.
 nograyscale - disable grayscale summing. It is default.
-cross4MB - enables that pixel line can cross 4MB boundary. It is default for non-Millenium.
-nocross4MB - pixel line must not cross 4MB boundary. It is default for Millenium I or II,
-           because of these devices have hardware limitations which do not allow this.
-	   But this option is incompatible with some (if not all yet released) versions
-	   of XF86_FBDev.
-vesa:X   - selects startup videomode. X is number from 0 to 0x1FF, see table above
-           for detailed explanation. Default is 640x480, 8bpp if driver has 8bpp support.
-	   Otherwise first available of 640x350x4bpp, 640x480x15bpp, 640x480x24bpp,
-	   640x480x32bpp or 80x25 text (80x25 text is always available).
+cross4MB - enables that pixel line can cross 4MB boundary. It is default for
+           non-Millenium.
+nocross4MB - pixel line must not cross 4MB boundary. It is default for
+             Millenium I or II, because of these devices have hardware
+	     limitations which do not allow this. But this option is
+	     incompatible with some (if not all yet released) versions of
+	     XF86_FBDev.
+vesa:X   - selects startup videomode. X is number from 0 to 0x1FF, see table
+           above for detailed explanation. Default is 640x480x8bpp if driver
+	   has 8bpp support. Otherwise first available of 640x350x4bpp,
+	   640x480x15bpp, 640x480x24bpp, 640x480x32bpp or 80x25 text
+	   (80x25 text is always available).
+
 If you are not satisfied with videomode selected by `vesa' option, you
 can modify it with these options:
-xres:X   - horizontal resolution, in pixels. Default is derived from `vesa' option.
-yres:X   - vertical resolution, in pixel lines. Default is derived from `vesa' option.
-upper:X  - top boundary: lines between end of VSYNC pulse and start of first pixel line 
-           of picture. Default is derived from `vesa' option.
-lower:X  - bottom boundary: lines between end of picture and start of VSYNC pulse.
-           Default is derived from `vesa' option.
-vslen:X  - length of VSYNC pulse, in lines. Default is derived from `vesa' option.
-left:X   - left boundary: pixels between end of HSYNC pulse and first pixel. Default
-           is derived from `vesa' option.
-right:X  - right boundary: pixels between end of picture and start of HSYNC pulse.
+
+xres:X   - horizontal resolution, in pixels. Default is derived from `vesa'
+           option.
+yres:X   - vertical resolution, in pixel lines. Default is derived from `vesa'
+           option.
+upper:X  - top boundary: lines between end of VSYNC pulse and start of first
+           pixel line of picture. Default is derived from `vesa' option.
+lower:X  - bottom boundary: lines between end of picture and start of VSYNC
+           pulse. Default is derived from `vesa' option.
+vslen:X  - length of VSYNC pulse, in lines. Default is derived from `vesa'
+           option.
+left:X   - left boundary: pixels between end of HSYNC pulse and first pixel.
            Default is derived from `vesa' option.
-hslen:X  - length of HSYNC pulse, in pixels. Default is derived from `vesa' option.
-pixclock:X - dotclocks, in ps (picoseconds). Default is derived from `vesa' option and
-           from `fh' and `fv' options.
+right:X  - right boundary: pixels between end of picture and start of HSYNC
+           pulse. Default is derived from `vesa' option.
+hslen:X  - length of HSYNC pulse, in pixels. Default is derived from `vesa'
+           option.
+pixclock:X - dotclocks, in ps (picoseconds). Default is derived from `vesa'
+             option and from `fh' and `fv' options.
 sync:X   - sync. pulse - bit 0 inverts HSYNC polarity, bit 1 VSYNC polarity.
-           If bit 3 (value 0x08) is set, composite sync instead of HSYNC is generated.
-	   If bit 5 (value 0x20) is set, sync on green is turned on.
+           If bit 3 (value 0x08) is set, composite sync instead of HSYNC is
+	   generated. If bit 5 (value 0x20) is set, sync on green is turned on.
 	   Default depends on `vesa'.
-depth:X  - Bits per pixel: 0=text, 4,8,15,16,24 or 32. Default depends on `vesa'.
-If you know capabilities of your monitor, you can specify some (or all) of `pixclk', `fh'
-and `fv'. In this case, `pixclock' is computed so that pixclock <= maxclk, real_fh <= fh
-and real_fv <= fv.
-maxclk:X - maximum dotclock. X can be specified in MHz, kHz or Hz. Default is `don't care'.
-fh:X     - maximum horizontal synchronization frequency. X can be specified in kHz or Hz.
-           Default is `don't care'.
-fv:X     - maximum vertical frequency. X must be specified in Hz. Default is 70 for modes
-           derived from `vesa' with yres <= 400, 60Hz for yres > 400.
+depth:X  - Bits per pixel: 0=text, 4,8,15,16,24 or 32. Default depends on
+           `vesa'.
+
+If you know capabilities of your monitor, you can specify some (or all) of
+`pixclk', `fh' and `fv'. In this case, `pixclock' is computed so that
+pixclock <= maxclk, real_fh <= fh and real_fv <= fv.
+
+maxclk:X - maximum dotclock. X can be specified in MHz, kHz or Hz. Default is
+           `don't care'.
+fh:X     - maximum horizontal synchronization frequency. X can be specified
+           in kHz or Hz. Default is `don't care'.
+fv:X     - maximum vertical frequency. X must be specified in Hz. Default is
+           70 for modes derived from `vesa' with yres <= 400, 60Hz for
+	   yres > 400.
 
 
 Limitations
@@ -198,8 +243,6 @@
 
 There are known and unknown bugs, features and misfeatures.
 Currently there are following known bugs:
- + G100 support does not work as expected, I'm still investigating this one.
-   Using `noinit' option works, but only for `first' head :-(
  + SVGALib does not restore screen on exit
  + generic fbcon-cfbX procedures do not work on Alphas. Due to this,
    `noaccel' (and cfb4 accel) driver does not work on Alpha. So everyone
diff -u --recursive --new-file v2.2.0-pre7/linux/Documentation/fb/vesafb.txt linux/Documentation/fb/vesafb.txt
--- v2.2.0-pre7/linux/Documentation/fb/vesafb.txt	Fri Oct  9 13:27:04 1998
+++ linux/Documentation/fb/vesafb.txt	Thu Jan 14 22:53:02 1999
@@ -1,16 +1,16 @@
 
-what is vesafb?
+What is vesafb?
 ===============
 
 This is a generic driver for a graphic framebuffer on intel boxes.
 
-Idea is simple:  Turn on graphics mode at boot time with the help of
-the BIOS, and use this as framebuffer device /dev/fb0, like the m68k
+The idea is simple:  Turn on graphics mode at boot time with the help
+of the BIOS, and use this as framebuffer device /dev/fb0, like the m68k
 (and other) ports do.
 
 This means we decide at boot time whenever we want to run in text or
 graphics mode.  Switching mode later on (in protected mode) is
-impossible, BIOS calls work in real mode only.  VESA BIOS Extentions
+impossible; BIOS calls work in real mode only.  VESA BIOS Extentions
 Version 2.0 are required, becauce we need a linear frame buffer.
 
 Advantages:
@@ -33,11 +33,12 @@
 Documentation/svga.txt for details.
 
 You should compile in both vgacon (for text mode) and vesafb (for
-graphics mode).   Which of them takes over the console depends on
+graphics mode). Which of them takes over the console depends on
 whenever the specified mode is text or graphics.
 
 The graphic modes are NOT in the list which you get if you boot with
-vga=ask and hit return.  Here are some mode numbers:
+vga=ask and hit return. The mode you wish to use is derived from the
+VESA mode number. Here are those VESA mode numbers:
 
     | 640x480  800x600  1024x768 1280x1024
 ----+-------------------------------------
@@ -46,19 +47,35 @@
 64k |  0x111    0x114    0x117    0x11A   
 16M |  0x112    0x115    0x118    0x11B   
 
-This are the VESA mode numbers.  The video mode select code expects
-0x200 + VESA mode number.  Therefore you have to enter "305" at the
-"vga=ask" prompt to boot into 1024x768x8.
-
-If this does'nt work, this might be becauce your BIOS does not support
-linear framebuffers or becauce it does'nt support this mode at all.
-Even if your board does, it might be the BIOS does not.  VESA BIOS
-Extentions v2.0 are required, 1.2 is NOT sufficient.  You'll get a
-"bad mode number" message if something goes wrong.
+The video mode number of the Linux kernel is the VESA mode number plus
+0x200.
+ 
+ Linux_kernel_mode_number = VESA_mode_number + 0x200
+
+So the table for the Kernel mode numbers are:
 
-Note: LILO can't handle hex, for booting directly with "vga=mode-number"
-      you have to transform the numbers to decimal.
+    | 640x480  800x600  1024x768 1280x1024
+----+-------------------------------------
+256 |  0x301    0x303    0x305    0x307   
+32k |  0x310    0x313    0x316    0x319   
+64k |  0x311    0x314    0x317    0x31A   
+16M |  0x312    0x315    0x318    0x31B   
+
+To enable one of those modes you have to specify "vga=ask" in the
+lilo.conf file and rerun LILO. Then you can type in the descired
+mode at the "vga=ask" prompt. For example if you like to use 
+1024x768x256 colors you have to say "305" at this prompt.
+
+If this does not work, this might be becauce your BIOS does not support
+linear framebuffers or becauce it does not support this mode at all.
+Even if your board does, it might be the BIOS which does not.  VESA BIOS
+Extentions v2.0 are required, 1.2 is NOT sufficient.  You will get a
+"bad mode number" message if something goes wrong.
 
+1. Note: LILO cannot handle hex, for booting directly with 
+         "vga=mode-number" you have to transform the numbers to decimal.
+2. Note: Some newer versions of LILO appear to work with those hex values,
+         if you set the 0x infront of the numbers.
 
 X11
 ===
@@ -68,7 +85,7 @@
 It depends on X-Server and graphics board.
 
 The X-Server must restore the video mode correctly, else you end up
-with a broken console (and vesafb can't do anything about this).
+with a broken console (and vesafb cannot do anything about this).
 
 
 Configuration
@@ -98,3 +115,6 @@
 
 --
 Gerd Knorr <kraxel@cs.tu-berlin.de>
+
+Minor (mostly typo) changes 
+by Nico Schmoigl <schmoigl@rumms.uni-mannheim.de>
diff -u --recursive --new-file v2.2.0-pre7/linux/Documentation/logo.txt linux/Documentation/logo.txt
--- v2.2.0-pre7/linux/Documentation/logo.txt	Sun Jun  9 08:01:04 1996
+++ linux/Documentation/logo.txt	Thu Jan 14 22:53:02 1999
@@ -1,6 +1,6 @@
 This is the full-colour version of the currently unofficial Linux logo
 ("currently unofficial" just means that there has been no paperwork and
-that I haven't really announced it yet).  It was created by Larry Ewing,
+that I have not really announced it yet).  It was created by Larry Ewing,
 and is freely usable as long as you acknowledge Larry as the original
 artist. 
 
diff -u --recursive --new-file v2.2.0-pre7/linux/Documentation/sound/OPL3-SA2 linux/Documentation/sound/OPL3-SA2
--- v2.2.0-pre7/linux/Documentation/sound/OPL3-SA2	Fri Jan  8 22:35:59 1999
+++ linux/Documentation/sound/OPL3-SA2	Thu Jan 14 22:53:02 1999
@@ -2,7 +2,7 @@
 ---------------------------------------------------------------
 
 Scott Murray, scottm@interlog.com
-January 5, 1998
+January 5, 1999
 
 NOTE: All trade-marked terms mentioned below are properties of their
       respective owners.
diff -u --recursive --new-file v2.2.0-pre7/linux/Documentation/sound/README.OSS linux/Documentation/sound/README.OSS
--- v2.2.0-pre7/linux/Documentation/sound/README.OSS	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sound/README.OSS	Sun Jan 17 18:23:01 1999
@@ -0,0 +1,1484 @@
+Introduction
+------------
+
+This file is a collection of all the old Readme files distributed with
+OSS/Lite by Hannu Savolainen. Since the new Linux sound driver is founded
+on it I think these information may still be interesting for users that
+have to configure their sound system.
+
+Be warned: Alan Cox is the current maintainer of the Linux sound driver so if
+you have problems with it, please contact him or the current device-specific
+driver maintainer (e.g. for aedsp16 specific problems contact me). If you have
+patches, contributions or suggestions send them to Alan: I'm sure they are
+welcome.
+
+In this document you will find a lot of references about OSS/Lite or ossfree:
+they are gone forever. Keeping this in mind and with a grain of salt this
+document can be still interesting and very helpful.
+
+[ File edited 17.01.1999 - Riccardo Facchetti ]
+
+OSS/Free version 3.8 release notes
+----------------------------------
+
+Please read the SOUND-HOWTO (available from sunsite.unc.edu and other Linux FTP 
+sites). It gives instructions about using sound with Linux. It's bit out of
+date but still very useful. Information about bug fixes and such things
+is available from the web page (see above).
+
+Please check http://www.opensound.com/pguide for more info about programming
+with OSS API.
+
+   ====================================================
+-  THIS VERSION ____REQUIRES____ Linux 2.1.57 OR LATER.
+   ====================================================
+
+Packages "snd-util-3.8.tar.gz" and "snd-data-0.1.tar.Z"
+contain useful utilities to be used with this driver.
+See http://www.opensound.com/ossfree/getting.html for
+download instructions.
+
+If you are looking for the installation instructions, please
+look forward into this document.
+
+Supported sound cards
+---------------------
+
+See below.
+
+Contributors
+------------
+
+This driver contains code by several contributors. In addition several other
+persons have given useful suggestions. The following is a list of major
+contributors. (I could have forgotten some names.)
+
+	Craig Metz	1/2 of the PAS16 Mixer and PCM support
+	Rob Hooft	Volume computation algorithm for the FM synth.
+	Mika Liljeberg	uLaw encoding and decoding routines
+	Jeff Tranter	Linux SOUND HOWTO document
+	Greg Lee	Volume computation algorithm for the GUS and
+			lot's of valuable suggestions.
+	Andy Warner	ISC port
+	Jim Lowe,
+	Amancio Hasty Jr	FreeBSD/NetBSD port
+	Anders Baekgaard 	Bug hunting and valuable suggestions.
+	Joerg Schubert	SB16 DSP support (initial version).
+	Andrew Robinson Improvements to the GUS driver
+	Megens SA	MIDI recording for SB and SB Pro (initial version).
+	Mikael Nordqvist  Linear volume support for GUS and
+			  nonblocking /dev/sequencer.
+	Ian Hartas		SVR4.2 port
+	Markus Aroharju	and
+	Risto Kankkunen		Major contributions to the mixer support
+				of GUS v3.7.
+	Hunyue Yau	Mixer support for SG NX Pro.
+	Marc Hoffman	PSS support (initial version).
+	Rainer Vranken	Initialization for Jazz16 (initial version).
+	Peter Trattler	Initial version of loadable module support for Linux.
+	JRA Gibson	16 bit mode for Jazz16 (initial version)
+	Davor Jadrijevic MAD16 support (initial version)
+	Gregor Hoffleit	Mozart support (initial version)
+	Riccardo Facchetti Audio Excel DSP 16 (aedsp16) support
+	James Hightower Spotting a tiny but important bug in CS423x support.
+	Denis Sablic	OPTi 82C924 spesific enhancements (non PnP mode)
+	Tim MacKenzie	Full duplex support for OPTi 82C930.
+	
+	Please look at lowlevel/README for more contributors.
+
+There are probably many other names missing. If you have sent me some
+patches and your name is not in the above list, please inform me.
+
+Sending your contributions or patches
+-------------------------------------
+
+First of all it's highly recommended to contact me before sending anything
+or before even starting to do any work. Tell me what you suggest to be
+changed or what you have planned to do. Also ensure you are using the
+very latest (development) version of OSS/Free since the change may already be
+implemented there. In general it's major waste of time to try to improve
+several months old version. Information about the latest version can be found
+from http://www.opensound.com/ossfree. In general there is no point in
+sending me patches relative to production kernels.
+
+Sponsors etc.
+-------------
+
+The following companies have greatly helped development of this driver 
+in form of a free copy of their product:
+
+Novell, Inc.		UnixWare personal edition + SDK
+The Santa Cruz Operation, Inc. 	A SCO OpenServer + SDK
+Ensoniq Corp,		a SoundScape card and extensive amount of assistance
+MediaTrix Peripherals Inc, a AudioTrix Pro card + SDK
+Acer, Inc.		a pair of AcerMagic S23 cards.
+
+In addition the following companies have provided me sufficient amount
+of technical information at least some of their products (free or $$$):
+
+Advanced Gravis Computer Technology Ltd.
+Media Vision Inc.
+Analog Devices Inc.
+Logitech Inc.
+Aztech Labs Inc.
+Crystal Semiconductor Corporation,
+Integrated Circuit Systems Inc.
+OAK Technology
+OPTi
+Turtle Beach
+miro
+Ad Lib Inc. ($$)
+Music Quest Inc. ($$)
+Creative Labs ($$$)
+
+If you have some problems
+=========================
+
+Read the sound HOWTO (sunsite.unc.edu:/pub/Linux/docs/...?).
+Also look at the home page (http://www.opensound.com/ossfree). It may
+contain info about some recent bug fixes.
+
+It's likely that you have some problems when trying to use the sound driver
+first time. Sound cards don't have standard configuration so there are no
+good default configuration to use. Please try to use same I/O, DMA and IRQ
+values for the sound card than with DOS.
+
+If you get an error message when trying to use the driver, please look
+at /var/adm/messages for more verbose error message.
+
+
+In general the easiest way to diagnose problems is to do "cat /dev/sndstat".
+
+If you get an error message, there are some problems with the driver setup:
+
+	- "No such file or directory" tells that the device files for
+	the sound driver are missing. Use the script at the end of
+	linux/drivers/sound/Readme.linux to create them.
+
+	- "No such device" tells that the sound driver is not in the kernel.
+	You have to reconfigure and recompile the kernel to have the sound
+	driver. Compiling the driver doesn't help alone. You have to boot
+	with the newly compiled one before the driver becomes active.
+	The Linux-HOWTO should help in this step.
+
+The following errors are likely with /dev/dsp and /dev/audio.
+
+	- "No such device or address". This error message should not happen
+	with /dev/sndstat but it's possible with the other sound devices.
+	This error indicates that there are no suitable hardware for the
+	device file or the sound driver has been compiled without support for
+	this particular device. For example /dev/audio and /dev/dsp will not
+	work if "digitized voice support" was not enabled during "make config".
+	
+	- "Device or resource busy". Probably the IRQ (or DMA) channel 
+	required by the sound card is in use by some other device/driver.
+
+	- "I/O error". Almost certainly (99%) it's an IRQ or DMA conflict.
+	Look at the kernel messages in /var/adm/notice for more info.
+
+	- "Invalid argument". The application is calling ioctl()
+	with impossible parameters. Check that the application is
+	for sound driver version 2.X or later.
+
+In general the printout of /dev/sndstat should tell what is the problem.
+It's possible that there are bugs in the sound driver but 99% of the problems
+reported to me are caused by somehow incorrect setup during "make config".
+
+Linux installation
+==================
+
+IMPORTANT!	Read this if you are installing a separately
+		distributed version of this driver.
+
+		Check that your kernel version works with this
+		release of the driver (see Readme). Also verify
+		that your current kernel version doesn't have more
+		recent sound driver version than this one. IT'S HIGHLY
+		RECOMMENDED THAT YOU USE THE SOUND DRIVER VERSION THAT
+		IS DISTRIBUTED WITH KERNEL SOURCES.
+
+- When installing separately distributed sound driver you should first
+  read the above notice. Then try to find proper directory where and how
+  to install the driver sources. You should not try to install a separately
+  distributed driver version if you are not able to find the proper way
+  yourself (in this case use the version that is distributed with kernel
+  sources). Remove old version of linux/drivers/sound directory before
+  installing new files.
+
+- To build the device files you need to run the enclosed shell script 
+  (see below). You need to do this only when installing sound driver
+  first time or when upgrading to much recent version than the earlier
+  one.
+
+- Configure and compile Linux as normally (remember to include the
+  sound support during "make config"). Please refer to kernel documentation
+  for instructions about configuring and compiling kernel. File Readme.cards
+  contains card specific instructions for configuring this driver for
+  use with various sound cards.
+
+Boot time configuration (using lilo and insmod) 
+-----------------------------------------------
+
+This information has been removed. Too many users didn't believe
+that it's really not necessary to use this method. Please look at
+Readme of sound driver version 3.0.1 if you still want to use this method.
+
+Problems
+--------
+
+If you have any kind of problems, there is a debugging feature which
+could help you to solve the problem. To use it, just execute the
+command:
+
+	cat /dev/sndstat 
+
+and look at the output. It should display some useful info about the
+driver configuration. If there is no /dev/sndstat 
+(/dev/sndstat: No such file or directory), ensure that you have executed the
+soundinstall script (at the end of this file).
+
+Common error messages:
+
+- /dev/???????: No such file or directory.
+Run the script at the end of this file.
+
+- /dev/???????: No such device.
+You are not running kernel which contains the sound driver. When using
+modularized sound driver this error means that the sound driver is not
+loaded.
+
+- /dev/????: No such device or address.
+Sound driver didn't detect suitable card when initializing. Please look at
+Readme.cards for info about configuring the driver with your card. Also
+check for possible boot (insmod) time error messages in /var/adm/messages.
+
+- Other messages or problems
+Please check http://www.opensound.com/ossfree for more info.
+
+Configuring version 3.8 (for Linux) with some common sound cards
+================================================================
+
+This document describes configuring sound cards with the freeware version of
+Open Sound Systems (OSS/Free). Information about the commercial version
+(OSS/Linux) and its configuration is available from 
+http://www.opensound.com/linux.html. Information presented here is
+not valid for OSS/Linux. 
+
+If you are unsure about how to configure OSS/Free
+you can download the free evaluation version of OSS/Linux from the above
+address. There is a chance that it can autodetect your sound card. In this case
+you can use the information included in soundon.log when configuring OSS/Free.
+
+
+IMPORTANT!	This document covers only cards that were "known" when
+		this driver version was released. Please look at
+		http://www.opensound.com/ossfree for info about
+		cards introduced recently.
+
+		When configuring the sound driver, you should carefully
+		check each sound configuration option (particularly
+		"Support for /dev/dsp and /dev/audio"). The default values
+		offered by these programs are not necessarily valid.
+
+
+THE BIGGEST MISTAKES YOU CAN MAKE
+=================================
+
+1. Assuming that the card is Sound Blaster compatible when it's not.
+--------------------------------------------------------------------
+
+The number one mistake is to assume that your card is compatible with
+Sound Blaster. Only the cards made by Creative Technology or which have
+one or more chips labeled by Creative are SB compatible. In addition there
+are few sound chipsets which are SB compatible in Linux such as ESS1688 or
+Jazz16. Note that SB compatibility in DOS/Windows does _NOT_ mean anything
+in Linux. 
+
+IF YOU REALLY ARE 150% SURE YOU HAVE A SOUND BLASTER YOU CAN SKIP THE REST OF
+THIS CHAPTER.
+
+For most other "supposed to be SB compatible" cards you have to use other
+than SB drivers (see below).  It is possible to get most sound cards to work
+in SB mode but in general it's a complete waste of time. There are several
+problems which you will encounter by using SB mode with cards that are not
+truly SB compatible:
+
+- The SB emulation is at most SB Pro (DSP version 3.x) which means that 
+you get only 8 bit audio (there is always an another ("native") mode which
+gives the 16 bit capability). The 8 bit only operation is the reason why
+many users claim that sound quality in Linux is much worse than in DOS.
+In addition some applications require 16 bit mode and they produce just
+noise with a 8 bit only device.
+- The card may work only in some cases but refuse to work most of the
+time. The SB compatible mode always requires special initialization which is 
+done by the DOS/Windows drivers. This kind of cards work in Linux after
+you have warm booted it after DOS but they don't work after cold boot
+(power on or reset).
+- You get the famous "DMA timed out" messages. Usually all SB clones have
+software selectable IRQ and DMA settings. If the (power on default) values
+currently used by the card don't match configuration of the driver you will
+get the above error message whenever you try to record or play. There are
+few other reasons to the DMA timeout message but using the SB mode seems
+to be the most common cause.
+
+2. Trying to use a PnP (Plug & Play) card just like an ordinary sound card
+--------------------------------------------------------------------------
+
+Plug & Play is a protocol defined by Intel and Microsoft. It lets operating
+systems to easily identify and reconfigure I/O ports, IRQs and DMAs of ISA
+cards. The problem with PnP cards is that the standard Linux doesn't currently
+(versions 2.1.x and earlier) don't support PnP. This means that you will have
+to use some special tricks (see later) to get a PnP card alive. Many PnP cards
+work after they have been initialized but this is not always the case.
+
+There are sometimes both PnP and non-PnP versions of the same sound card.
+The non-PnP version is the original model which usually has been discontinued
+more than an year ago. The PnP version has the same name but with "PnP"
+appended to it (sometimes not). This causes major confusion since the non-PnP
+model works with Linux but the PnP one doesn't.
+
+You should carefully check if "Plug & Play" or "PnP" is mentioned in the name
+of the card or in the documentation or package that came with the card. 
+Everything described in the rest of this document is not necessarily valid for
+PnP models of sound cards even you have managed to wake up the card properly.
+Many PnP cards are simply too different from their non-PnP ancestors which are
+covered by this document.
+
+
+Cards that are not (fully) supported by this driver
+===================================================
+
+See http://www.opensound.com/ossfree for information about sound cards 
+to be supported in future. 
+
+
+How to use sound without recompiling kernel and/or sound driver
+===============================================================
+
+There is a commercial sound driver which comes in precompiled form and doesn't
+require recompiling of the kernel. See http://www.4Front-tech.com/oss.html for
+more info.
+
+
+Configuring PnP cards
+=====================
+
+New versions of most sound cards use the so-called ISA PnP protocol for
+soft configuring their I/O, IRQ, DMA and shared memory resources.
+Currently at least cards made by Creative Technology (SB32 and SB32AWE
+PnP), Gravis (GUS PnP and GUS PnP Pro), Ensoniq (Soundscape PnP) and
+Aztech (some Sound Galaxy models) use PnP technology. The CS4232/4236 audio
+chip by Crystal Semiconductor (Intel Atlantis, HP Pavilion and many other
+motherboards) is also based on PnP technology but there is a "native" driver
+available for it (see information about CS4232 later in this document).
+
+PnP sound cards (as well as most other PnP ISA cards) are not supported
+by this version of the driver . Proper
+support for them should be released during 97 once the kernel level
+PnP support is available.
+
+There is a method to get most of the PnP cards to work. The basic method
+is the following:
+
+1) Boot DOS so the card's DOS drivers have a chance to initialize it.
+2) _Cold_ boot to Linux by using "loadlin.exe".  Hitting ctrl-alt-del
+works with older machines but causes a hard reset of all cards on recent
+(Pentium) machines.
+3) If you have the sound driver in Linux configured properly, the card should
+work now. "Proper" means that I/O, IRQ and DMA settings are the same as in
+DOS. The hard part is to find which settings were used. See the documentation of
+your card for more info.
+
+Windows 95 could work as well as DOS but running loadlin may be difficult.
+Probably you should "shut down" your machine to MS-DOS mode before running it.
+
+Some machines have a BIOS utility for setting PnP resources. This is a good
+way to configure some cards. In this case you don't need to boot DOS/Win95
+before starting Linux.
+
+Another way to initialize PnP cards without DOS/Win95 is a Linux based
+PnP isolation tool. When writing this there is a pre alpha test version
+of such a tool available from ftp://ftp.demon.co.uk/pub/unix/linux/utils. The
+file is called isapnptools-*. Please note that this tool is just a temporary
+solution which may be incompatible with future kernel versions having proper
+support for PnP cards. There are bugs in setting DMA channels in earlier
+versions of isapnptools so at least version 1.6 is required with sound cards.
+
+Yet another way to use PnP cards is to use (commercial) OSS/Linux drivers.  See
+http://www.opensound.com/linux.html for more info. This is probably the way you
+should do it if you don't want to spend time recompiling the kernel and 
+required tools.
+
+
+Read this before trying to configure the driver
+===============================================
+
+There are currently many cards that work with this driver. Some of the cards
+have native support while others work since they emulate some other
+card (usually SB, MSS/WSS and/or MPU401). The following cards have native
+support in the driver. Detailed instructions for configuring these cards
+will be given later in this document.
+
+Pro Audio Spectrum 16 (PAS16) and compatibles:
+	Pro Audio Spectrum 16
+	Pro Audio Studio 16
+	Logitech Sound Man 16
+	NOTE! The original Pro Audio Spectrum as well as the PAS+ are not
+	      and will not be supported by the driver.
+
+Media Vision Jazz16 based cards
+	Pro Sonic 16
+	Logitech SoundMan Wave
+	(Other Jazz based cards should work but I don't have any reports
+	about them).
+
+Sound Blasters
+	SB 1.0 to 2.0
+	SB Pro
+	SB 16
+	SB32/64/AWE
+		Configure SB32/64/AWE just like SB16. See lowlevel/README.awe
+		for information about using the wave table synth.
+	        NOTE! AWE63/Gold and 16/32/AWE "PnP" cards need to be activated
+		      using isapnptools before they work with OSS/Free.
+	SB16 compatible cards by other manufacturers than Creative.
+		You have been fooled since there are _no_ SB16 compatible
+		cards on the market (as of May 1997). It's likely that your card
+		is compatible just with SB Pro but there is also a non-SB-
+		compatible 16 bit mode. Usually it's MSS/WSS but it could also
+		be a proprietary one like MV Jazz16 or ESS ES688. OPTi
+		MAD16 chips are very common in so called "SB 16 bit cards"
+		(try with the MAD16 driver).
+
+	======================================================================
+	"Supposed to be SB compatible" cards.
+		Forget the SB compatibility and check for other alternatives
+		first. The only cards that work with the SB driver in
+		Linux have been made by Creative Technology (there is at least
+		one chip on the card with "CREATIVE" printed on it). The
+		only other SB compatible chips are ESS and Jazz16 chips
+		(maybe ALSxxx chips too but they probably don't work).
+		Most other "16 bit SB compatible" cards such as "OPTi/MAD16" or
+		"Crystal" are _NOT_ SB compatible in Linux.
+
+		Practically all sound cards have some kind of SB emulation mode
+		in addition to their native (16 bit) mode. In most cases this
+		(8 bit only) SB compatible mode doesn't work with Linux. If
+		you get it working it may cause problems with games and
+		applications which require 16 bit audio. Some 16 bit only
+		applications don't check if the card actually supports 16 bits.
+		They just dump 16 bit data to a 8 bit card which produces just
+		noise.
+
+		In most cases the 16 bit native mode is supported by Linux.
+		Use the SB mode with "clones" only if you don't find anything
+		better from the rest of this doc.
+	======================================================================
+
+Gravis Ultrasound (GUS)
+	GUS
+	GUS + the 16 bit option
+	GUS MAX
+	GUS ACE (No MIDI port and audio recording)
+	GUS PnP (with RAM)
+
+MPU-401	and compatibles
+	The driver works both with the full (intelligent mode) MPU-401
+	cards (such as MPU IPC-T and MQX-32M) and with the UART only
+	dumb MIDI ports. MPU-401 is currently the most common MIDI
+	interface. Most sound cards are compatible with it. However,
+	don't enable MPU401 mode blindly. Many cards with native support
+	in the driver have their own MPU401 driver. Enabling the standard one
+	will cause a conflict with these cards. So check if your card is
+	in the list of supported cards before enabling MPU401.
+
+Windows Sound System (MSS/WSS)
+	Even when Microsoft has discontinued their own Sound System card 
+	they managed to make it a standard. MSS compatible cards are based on 
+	a codec chip which is easily available from at least two manufacturers
+	(AD1848 by Analog Devices and CS4231/CS4248 by Crystal Semiconductor).
+	Currently most sound cards are based on one of the MSS compatible codec
+	chips. The CS4231 is used in the high quality cards such as GUS MAX,
+	MediaTrix AudioTrix Pro and TB Tropez (GUS MAX is not MSS compatible). 
+
+	Having a AD1848, CS4248 or CS4231 codec chip on the card is a good
+	sign. Even if the card is not MSS compatible, it could be easy to write
+	support for it. Note also that most MSS compatible cards
+	require special boot time initialization which may not be present
+	in the driver. Also, some MSS compatible cards have native support.
+	Enabling the MSS support with these cards is likely to
+	cause a conflict. So check if your card is listed in this file before
+	enabling the MSS support.
+
+Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4)
+	Most sound cards have a FM synthesizer chip. The OPL2 is a 2
+	operator chip used in the original AdLib card. Currently it's used
+	only in the cheapest (8 bit mono) cards. The OPL3 is a 4 operator 
+	FM chip which provides better sound quality and/or more available 
+	voices than the OPL2. The OPL4 is a new chip that has an OPL3 and
+	a wave table synthesizer packed onto the same chip. The driver supports
+	just the OPL3 mode directly. Most cards with an OPL4 (like
+	SM Wave and AudioTrix Pro) support the OPL4 mode using MPU401
+	emulation. Writing a native OPL4 support is difficult
+	since Yamaha doesn't give information about their sample ROM chip.
+
+	Enable the generic OPL2/OPL3 FM synthesizer support if your
+	card has a FM chip made by Yamaha. Don't enable it if your card
+	has a software (TRS) based FM emulator.
+
+	----------------------------------------------------------------
+	NOTE! OPL3-SA is different chip than the ordinary OPL3. In addition
+	to the FM synth this chip has also digital audio (WSS) and
+	MIDI (MPU401) capabilities. Support for OPL3-SA is described below.
+	----------------------------------------------------------------
+
+Yamaha OPL3-SA1
+
+	Yamaha OPL3-SA1 (YMF701) is an audio controller chip used on some
+	(Intel) motherboards and on cheap sound cards. It should not be
+	confused with the original OPL3 chip (YMF278) which is entirely
+        different chip. OPL3-SA1 has support for MSS, MPU401 and SB Pro
+	(not used in OSS/Free) in addition to the OPL3 FM synth.
+
+	There are also chips called OPL3-SA2, OPL3-SA3, ..., OPL3SA-N. They
+	are PnP chips and will not work with the OPL3-SA1 driver. You should 
+	use the standard MSS, MPU401 and OPL3 options with thses chips and to
+	activate the card using isapnptools.
+
+4Front Technologies SoftOSS
+
+	SoftOSS is a software based wave table emulation which works with
+	any 16 bit stereo sound card. Due to its nature a fast CPU is
+	required (P133 is minimum). Although SoftOSS does _not_ use MMX
+	instructions it has proven out that recent processors (which appear
+	to have MMX) perform significantly better with SoftOSS than earlier
+	ones. For example a P166MMX beats a PPro200. SoftOSS should not be used
+	on 486 or 386 machines.
+
+	The amount of CPU load caused by SoftOSS can be controlled by
+	selecting the CONFIG_SOFTOSS_RATE and CONFIG_SOFTOSS_VOICES
+	parameters properly (they will be prompted by make config). It's
+	recommended to set CONFIG_SOFTOSS_VOICES to 32. If you have a
+	P166MMX or faster (PPro200 is not faster) you can set
+	CONFIG_SOFTOSS_RATE to 44100 (kHz). However with slower systems it
+	recommended to use sampling rates around 22050 or even 16000 kHz.
+	Selecting too high values for these parameters may hang your
+	system when playing MIDI files with hight degree of polyphony
+	(number of concurrently playing notes). It's also possible to
+	decrease CONFIG_SOFTOSS_VOICES. This makes it possible to use
+	higher sampling rates. However using fewer voices decreases
+	playback quality more than decreasing the sampling rate.
+
+	SoftOSS keeps the samples loaded on the system's RAM so much RAM is
+	required. SoftOSS should never be used on machines with less than 16 MB
+	of RAM since this is potentially dangerous (you may accidently run out
+	of memory which probably crashes the machine). 
+
+	SoftOSS implements the wave table API originally designed for GUS. For
+	this reason all applications designed for GUS should work (at least
+	after minor modifications). For example gmod/xgmod and playmidi -g are
+	known to work.
+
+	To work SoftOSS will require GUS compatible
+	patch files to be installed on the system (in /dos/ultrasnd/midi). You
+	can use the public domain MIDIA patchset available from several ftp
+	sites.
+
+        *********************************************************************
+	IMPORTANT NOTICE! The original patch set distributed with the Gravis 
+	Ultrasound card is not in public domain (even though it's available from
+	some FTP sites). You should contact Voice Crystal (www.voicecrystal.com)
+	if you like to use these patches with SoftOSS included in OSS/Free.
+        *********************************************************************
+
+PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC)
+	Analog Devices and Echo Speech have together defined a sound card
+	architecture based on the above chips. The DSP chip is used
+	for emulation of SB Pro, FM and General MIDI/MT32.
+
+	There are several cards based on this architecture. The most known
+	ones are Orchid SW32 and Cardinal DSP16. 
+
+	The driver supports downloading DSP algorithms to these cards.
+
+	NOTE! You will have to use the "old" config script when configuring
+	PSS cards.
+
+MediaTrix AudioTrix Pro
+	The ATP card is built around a CS4231 codec and an OPL4 synthesizer
+	chips. The OPL4 mode is supported by a microcontroller running a
+	General MIDI emulator. There is also a SB 1.5 compatible playback mode.
+
+Ensoniq SoundScape and compatibles
+	Ensoniq has designed a sound card architecture based on the
+	OTTO synthesizer chip used in their professional MIDI synthesizers.
+	Several companies (including Ensoniq, Reveal and Spea) are selling
+	cards based on this architecture.
+
+	NOTE! The SoundScape PnP is not supported by OSS/Free. Ensoniq VIVO and
+	VIVO90 cards are not compatible with Soundscapes so the Soundscape
+        driver will not work with them. You may want to use OSS/Linux with these
+        cards.
+
+OPTi MAD16 and Mozart based cards
+	The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929),
+	OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface
+	chips are used in many different sound cards, including some
+	cards by Reveal miro and Turtle Beach (Tropez). The purpose of these
+	chips is to connect other audio components to the PC bus. The
+	interface chip performs address decoding for the other chips.
+	NOTE! Tropez Plus is not MAD16 but CS4232 based.
+	NOTE! MAD16 PnP cards (82C924, 82C925, 82C931) are not MAD16 compatible
+	in the PnP mode. You will have to use them in MSS mode after having
+	initialized them using isapnptools or DOS. 82C931 probably requires
+	initialization using DOS/Windows (running isapnptools is not enough).
+	It's possible to use 82C931 with OSS/Free by jumpering it to non-PnP
+	mode (provided that the card has a jumper for this). In non-PnP mode
+	82C931 is compatible with 82C930 and should work with the MAD16 driver
+	(without need to use isapnptools or DOS to initialize it). All OPTi
+	chips are supported by OSS/Linux (both in PnP and non-PnP modes).
+
+Audio Excel DSP16 
+	Support for this card was written by Riccardo Faccetti
+	(riccardo@cdc8g5.cdc.polimi.it). The AEDSP16 driver included in
+	the lowlevel/ directory. To use it you should enable the
+	"Additional low level drivers" option.
+
+Crystal CS4232 and CS4236 based cards such as AcerMagic S23, TB Tropez _Plus_ and 
+	many PC motherboards (Compaq, HP, Intel, ...)
+	CS4232 is a PnP multimedia chip which contains a CS3231A codec,
+	SB and MPU401 emulations. There is support for OPL3 too.
+	Unfortunately the MPU401 mode doesn't work (I don't know how to
+	initialize it). CS4236 is an enhanced (compatible) version of CS4232.
+	NOTE! Don't ever try to use isapnptools with CS4232 since this will just
+	freeze your machine (due to chip bugs). If you have problems in getting
+	CS4232 working you could try initializing it with DOS (CS4232C.EXE) and
+	then booting Linux using loadlin. CS4232C.EXE loads a secret firmware
+	patch which is not documented by Crystal.
+
+Turtle Beach Maui and Tropez "classic"
+	This driver version supports sample, patch and program loading commands
+	described in the Maui/Tropez User's manual. 
+	There is now full initialization support too. The audio side of
+	the Tropez is based on the MAD16 chip (see above).
+	NOTE! Tropez Plus is different card than Tropez "classic" and will not
+	work fully in Linux. You can get audio features working by configuring
+	the card as a CS4232 based card (above).
+
+
+Jumpers and software configuration
+==================================
+
+Some of the earliest sound cards were jumper configurable. You have to
+configure the driver use I/O, IRQ and DMA settings
+that match the jumpers. Just few 8 bit cards are fully jumper 
+configurable (SB 1.x/2.x, SB Pro and clones).
+Some cards made by Aztech have an EEPROM which contains the 
+config info. These cards behave much like hardware jumpered cards.
+
+Most cards have jumper for the base I/O address but other parameters
+are software configurable. Sometimes there are few other jumpers too.
+
+Latest cards are fully software configurable or they are PnP ISA
+compatible. There are no jumpers on the board.
+
+The driver handles software configurable cards automatically. Just configure
+the driver to use I/O, IRQ and DMA settings which are known to work.
+You could usually use the same values than with DOS and/or Windows.
+Using different settings is possible but not recommended since it may cause
+some trouble (for example when warm booting from an OS to another or
+when installing new hardware to the machine).
+
+Sound driver sets the soft configurable parameters of the card automatically
+during boot. Usually you don't need to run any extra initialization
+programs when booting Linux but there are some exceptions. See the
+card-specific instructions below for more info.
+
+The drawback of software configuration is that the driver needs to know
+how the card must be initialized. It cannot initialize unknown cards
+even if they are otherwise compatible with some other cards (like SB,
+MPU401 or Windows Sound System).
+
+
+What if your card was not listed above?
+=======================================
+
+The first thing to do is to look at the major IC chips on the card.
+Many of the latest sound cards are based on some standard chips. If you
+are lucky, all of them could be supported by the driver. The most common ones
+are the OPTi MAD16, Mozart, SoundScape (Ensoniq) and the PSS architectures
+listed above. Also look at the end of this file for list of unsupported
+cards and the ones which could be supported later.
+
+The last resort is to send _exact_ name and model information of the card
+to me together with a list of the major IC chips (manufactured, model) to 
+me. I could then try to check if your card looks like something familiar.
+
+There are many more cards in the world than listed above. The first thing to
+do with these cards is to check if they emulate some other card or interface
+such as SB, MSS and/or MPU401. In this case there is a chance to get the
+card to work by booting DOS before starting Linux (boot DOS, hit ctrl-alt-del
+and boot Linux without hard resetting the machine). In this method the
+DOS based driver initializes the hardware to use known I/O, IRQ and DMA
+settings. If sound driver is configured to use the same settings, everything
+should work OK.
+
+
+Configuring sound driver (with Linux)
+=====================================
+
+The sound driver is currently distributed as part of the Linux kernel. The 
+files are in /usr/src/linux/drivers/sound/. 
+
+****************************************************************************
+*	ALWAYS USE THE SOUND DRIVER VERSION WHICH IS DISTRIBUTED WITH	   *
+*	THE KERNEL SOURCE PACKAGE YOU ARE USING. SOME ALPHA AND BETA TEST  *
+*	VERSIONS CAN BE INSTALLED FROM A SEPARATELY DISTRIBUTED PACKAGE	   *
+*	BUT CHECK THAT THE PACKAGE IS NOT MUCH OLDER (OR NEWER) THAN THE   *
+*	KERNEL YOU ARE USING. IT'S POSSIBLE THAT THE KERNEL/DRIVER 	   *
+*	INTERFACE CHANGES BETWEEN KERNEL RELEASES WHICH MAY CAUSE SOME	   *
+*	INCOMPATIBILITY PROBLEMS.					   *
+*									   *
+*	IN CASE YOU INSTALL A SEPARATELY DISTRIBUTED SOUND DRIVER VERSION, *
+*	BE SURE TO REMOVE OR RENAME THE OLD SOUND DRIVER DIRECTORY BEFORE  *
+*	INSTALLING THE NEW ONE. LEAVING OLD FILES TO THE SOUND DRIVER	   *
+*	DIRECTORY _WILL_ CAUSE PROBLEMS WHEN THE DRIVER IS USED OR	   *
+*	COMPILED.							   *
+****************************************************************************
+
+To configure the driver, run "make config" in the kernel source directory
+(/usr/src/linux). Answer "y" or "m" to the question about Sound card support
+(after the questions about mouse, CD-ROM, ftape, etc. support).  Questions
+about options for sound will then be asked.
+
+After configuring the kernel and sound driver, run "make dep" and compile
+the kernel following instructions in the kernel README.
+
+The sound driver configuration dialog
+-------------------------------------
+
+If you already have the sound driver installed, consult a printout of
+"cat /dev/sndstat" when configuring the driver again. It gives the I/O,
+IRQ and DMA settings you used earlier.
+
+Sound configuration starts by making some yes/no questions. Be careful
+when answering to these questions since answering y to a question may
+prevent some later ones from being asked. For example don't answer y to
+the first question (PAS16) if you don't really have a PAS16. Don't enable
+more cards than you really need since they just consume memory. Also
+some drivers (like MPU401) may conflict with your SCSI controller and
+prevent kernel from booting. If you card was in the list of supported
+cards (above), please look at the card specific config instructions
+(later in this file) before starting to configure. Some cards must be
+configured in way which is not obvious.
+
+So here is the beginning of the config dialog. Answer 'y' or 'n' to these
+questions. The default answer is shown so that (y/n) means 'y' by default and
+(n/y) means 'n'. To use the default value, just hit ENTER. But be careful
+since using the default _doesn't_ guarantee anything.
+
+Note also that all questions may not be asked. The configuration program
+may disable some questions depending on the earlier choices. It may also
+select some options automatically as well.
+
+  "ProAudioSpectrum 16 support",
+	- Answer 'y'_ONLY_ if you have a Pro Audio Spectrum _16_,
+	  Pro Audio Studio 16 or Logitech SoundMan 16 (be sure that
+	  you read the above list correctly). Don't answer 'y' if you
+	  have some other card made by Media Vision or Logitech since they
+	  are not PAS16 compatible.
+	  NOTE! Since 3.5-beta10 you need to enable SB support (next question)
+	  if you want to use the SB emulation of PAS16. It's also possible to
+   	  the emulation if you want to use a true SB card together with PAS16
+	  (there is another question about this that is asked later).
+  "Sound Blaster support",
+	- Answer 'y' if you have an original SB card made by Creative Labs
+	  or a full 100% hardware compatible clone (like Thunderboard or
+	  SM Games). If your card was in the list of supported cards (above),
+	  please look at the card specific instructions later in this file
+	  before answering this question. For an unknown card you may answer 
+	  'y' if the card claims to be SB compatible.
+	 Enable this option also with PAS16 (changed since v3.5-beta9).
+
+	 Don't enable SB if you have a MAD16 or Mozart compatible card.
+
+  "Generic OPL2/OPL3 FM synthesizer support",
+	- Answer 'y' if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4).
+	  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. However I don't currently know
+	  such cards.
+  "Gravis Ultrasound support",
+	- Answer 'y' if you have GUS or GUS MAX. Answer 'n' if you don't
+	  have GUS since the GUS driver consumes much memory.
+	  Currently I don't have experiences with the GUS ACE so I don't
+	  know what to answer with it.
+  "MPU-401 support (NOT for SB16)",
+	- Be careful with this question. The MPU401 interface is supported
+	  by almost any sound card today. However some natively supported cards
+	  have their own driver for MPU401. Enabling the 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 was in the list of supported cards (above), please look at
+	  the card specific instructions later in this file.
+
+	  In MOST cases this MPU401 driver should only be used with "true"
+	  MIDI-only MPU401 professional cards. In most other cases there
+	  is another way to get the MPU401 compatible interface of a
+	  sound card to work.
+	  Support for the MPU401 compatible MIDI port of SB16, ESS1688
+	  and MV Jazz16 cards is included in the SB driver. Use it instead
+	  of this separate MPU401 driver with these cards. As well 
+	  Soundscape, PSS and Maui drivers include their own MPU401
+	  options.
+
+	  It's safe to answer 'y' if you have a true MPU401 MIDI interface
+	  card. 
+  "6850 UART Midi support",
+	- It's safe to answer 'n' to this question in all cases. The 6850
+	  UART interface is so rarely used.
+  "PSS (ECHO-ADI2111) support",
+	- Answer 'y' only if you have Orchid SW32, Cardinal DSP16 or some
+	  other card based on the PSS chipset (AD1848 codec + ADSP-2115
+	  DSP chip + Echo ESC614 ASIC CHIP).
+  "16 bit sampling option of GUS (_NOT_ GUS MAX)",
+	- Answer 'y' if you have installed the 16 bit sampling daughtercard
+	  to your GUS. Answer 'n' if you have GUS MAX. Enabling this option
+	  disables GUS MAX support.
+  "GUS MAX support",
+	- Answer 'y' only if you have a GUS MAX.
+  "Microsoft Sound System support",
+	- Again think carefully before answering 'y' to this question. It's
+	  safe to answer 'y' in case you have the original Windows Sound 
+	  System card made by Microsoft or Aztech SG 16 Pro (or NX16 Pro).
+	  Also you may answer 'y' in case your card was not listed earlier
+	  in this file. For cards having native support in the driver, consult
+	  the card specific instructions later in this file. Some drivers
+	  have their own MSS support and enabling this option will cause a
+	  conflict. 
+	  Note! The MSS driver permits configuring two DMA channels. This is a
+	  "nonstandard" feature and works only with very few cards (if any).
+	  In most cases the second DMA channel should be disabled or set to
+	  the same channel than the first one. Trying to configure two separate
+	  channels with cards that don't support this feature will prevent
+	  audio (at least recording) from working.
+  "Ensoniq Soundscape support",
+	- 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 (note that Reveal makes other cards also).  The oldest
+	  cards made by Spea don't work properly with Linux. 
+	  Soundscape PnP as well as Ensoniq VIVO work only with the commercial
+	  OSS/Linux version.
+  "MediaTrix AudioTrix Pro support",
+	- Answer 'y' if you have the AudioTrix Pro.
+  "Support for MAD16 and/or Mozart based cards",
+	- Answer y if your card has a Mozart (OAK OTI-601) or MAD16
+	  (OPTi 82C928, 82C929, 82C924/82C925 or 82C930) audio interface chip. 
+	  These chips are
+	  currently quite common so it's possible that many no-name cards
+	  have one of them. In addition the MAD16 chip is used in some
+	  cards made by known manufacturers such as Turtle Beach (Tropez),
+	  Reveal (some models) and Diamond (some recent models).
+	  Note OPTi 82C924 and 82C925 are MAD16 compatible only in non PnP
+	  mode (jumper selectable on many cards).
+  "Support for TB Maui"
+	- This enables TB Maui specific initialization. Works with TB Maui
+	and TB Tropez (may not work with Tropez Plus).
+
+
+Then the configuration program asks some y/n questions about the higher
+level services. It's recommended to answer 'y' to each of these questions.
+Answer 'n' only if you know you will not need the option.
+
+  "MIDI interface support",
+	- Answering 'n' disables /dev/midi## devices and access to any
+	  MIDI ports using /dev/sequencer and /dev/music. This option
+	  also affects any MPU401 and/or General MIDI compatible devices.
+  "FM synthesizer (YM3812/OPL-3) support",
+	- Answer 'y' here.
+  "/dev/sequencer support",
+	- Answering 'n' disables /dev/sequencer and /dev/music.
+
+Entering the I/O, IRQ and DMA config parameters
+-----------------------------------------------
+
+After the above questions the configuration program prompts for the
+card specific configuration information. Usually just a set of
+I/O address, IRQ and DMA numbers are asked. With some cards the program
+asks for some files to be used during initialization of the card. For example
+many cards have a DSP chip or microprocessor which must be initialized by
+downloading a program (microcode) file to the card.
+
+Instructions for answering these questions are given in the next section.
+
+
+Card specific information
+=========================
+
+This section gives additional instructions about configuring some cards.
+Please refer manual of your card for valid I/O, IRQ and DMA numbers. Using
+the same settings with DOS/Windows and Linux is recommended. Using
+different values could cause some problems when switching between
+different operating systems.
+
+Sound Blasters (the original ones by Creative)
+---------------------------------------------
+
+NOTE! Check if you have a PnP Sound Blaster (cards sold after summer 1995
+      are almost certainly PnP ones). With PnP cards you should use isapnptools
+      to activate them (see above).
+
+It's possible to configure these cards to use different I/O, IRQ and
+DMA settings. Since the possible/default settings have changed between various
+models, you have to consult manual of your card for the proper ones. It's
+a good idea to use the same values than with DOS/Windows. With SB and SB Pro
+it's the only choice. SB16 has software selectable IRQ and DMA channels but
+using different values with DOS and Linux is likely to cause troubles. The
+DOS driver is not able to reset the card properly after warm boot from Linux
+if Linux has used different IRQ or DMA values.
+
+The original (steam) Sound Blaster (versions 1.x and 2.x) use always
+DMA1. There is no way to change it.
+
+The SB16 needs two DMA channels. A 8 bit one (1 or 3) is required for
+8 bit operation and a 16 bit one (5, 6 or 7) for the 16 bit mode. In theory
+it's possible to use just one (8 bit) DMA channel by answering the 8 bit
+one when the configuration program asks for the 16 bit one. This may work
+in some systems but is likely to cause terrible noise on some other systems.
+
+It's possible to use two SB16/32/64 at the same time. To do this you should
+first configure OSS/Free for one card. Then edit local.h manually and define
+SB2_BASE, SB2_IRQ, SB2_DMA and SB2_DMA2 for the second one. You can't get
+the OPL3, MIDI and EMU8000 devices of the second card to work. If you are
+going to use two PnP Sound Blasters, ensure that they are of different model
+and have different PnP IDs. There is no way to get two cards with the same
+card ID and serial number to work. The easiest way to check this is trying 
+if isapnptools can see both cards or just one.
+
+NOTE!	Don't enable the SM Games option (asked by the configuration program)
+	if you are not 101% sure that your card is a Logitech Soundman Games
+	(not a SM Wave or SM16).
+
+SB Clones
+---------
+
+First of all: There are no SB16 clones. There are SB Pro clones with a
+16 bit mode which is not SB16 compatible. The most likely alternative is that
+the 16 bit mode means MSS/WSS.
+
+There are just a few fully 100% hardware SB or SB Pro compatible cards.
+I know just Thunderboard and SM Games. Other cards require some kind of
+hardware initialization before they become SB compatible. Check if your card
+was listed in the beginning of this file. In this case you should follow
+instructions for your card later in this file.
+
+For other not fully SB clones you may try initialization using DOS in
+the following way:
+
+	- Boot DOS so that the card specific driver gets run.
+	- Hit ctrl-alt-del (or use loadlin) to boot Linux. Don't
+	  switch off power or press the reset button.
+	- If you use the same I/O, IRQ and DMA settings in Linux, the
+	  card should work.
+
+If your card is both SB and MSS compatible, I recommend using the MSS mode.
+Most cards of this kind are not able to work in the SB and the MSS mode 
+simultaneously. Using the MSS mode provides 16 bit recording and playback.
+
+ProAudioSpectrum 16 and compatibles
+-----------------------------------
+
+PAS16 has a SB emulation chip which can be used together with the native
+(16 bit) mode of the card. To enable this emulation you should configure 
+the driver to have SB support too (this has been changed since version
+3.5-beta9 of this driver).
+
+With current driver versions it's also possible to use PAS16 together with 
+another SB compatible card. In this case you should configure SB support
+for the other card and to disable the SB emulation of PAS16 (there is a
+separate questions about this).
+
+With PAS16 you can use two audio device files at the same time. /dev/dsp (and
+/dev/audio) is connected to the 8/16 bit native codec and the /dev/dsp1 (and
+/dev/audio1) is connected to the SB emulation (8 bit mono only).
+
+Gravis Ultrasound
+-----------------
+
+There are many different revisions of the Ultrasound card (GUS). The
+earliest ones (pre 3.7) don't have a hardware mixer. With these cards
+the driver uses a software emulation for synth and pcm playbacks. It's
+also possible to switch some of the inputs (line in, mic) off by setting
+mixer volume of the channel level below 10%. For recording you have
+to select the channel as a recording source and to use volume above 10%.
+
+GUS 3.7 has a hardware mixer.
+
+GUS MAX and the 16 bit sampling daughtercard have a CS4231 codec chip which 
+also contains a mixer.
+
+Configuring GUS is simple. Just enable the GUS support and GUS MAX or
+the 16 bit daughtercard if you have them. Note that enabling the daughter
+card disables GUS MAX driver.
+
+NOTE for owners of the 16 bit daughtercard: By default the daughtercard
+uses /dev/dsp (and /dev/audio). Command "ln -sf /dev/dsp1 /dev/dsp"
+selects the daughter card as the default device.
+
+With just the standard GUS enabled the configuration program prompts
+for the I/O, IRQ and DMA numbers for the card. Use the same values than
+with DOS.
+
+With the daughter card option enabled you will be prompted for the I/O,
+IRQ and DMA numbers for the daughter card. You have to use different I/O
+and DMA values than for the standard GUS. The daughter card permits
+simultaneous recording and playback. Use /dev/dsp (the daughtercard) for
+recording and /dev/dsp1 (GUS GF1) for playback.
+
+GUS MAX uses the same I/O address and IRQ settings than the original GUS
+(GUS MAX = GUS + a CS4231 codec). In addition an extra DMA channel may be used.
+Using two DMA channels permits simultaneous playback using two devices
+(dev/dsp0 and /dev/dsp1). The second DMA channel is required for
+full duplex audio.
+To enable the second DMA channels, give a valid DMA channel when the config
+program asks for the GUS MAX DMA (entering -1 disables the second DMA).
+Using 16 bit DMA channels (5,6 or 7) is recommended.
+
+If you have problems in recording with GUS MAX, you could try to use
+just one 8 bit DMA channel. Recording will not work with one DMA
+channel if it's a 16 bit one.
+
+Microphone input of GUS MAX is connected to mixer in little bit nonstandard
+way. There is actually two microphone volume controls. Normal "mic" controls
+only recording level. Mixer control "speaker" is used to control volume of
+microphone signal connected directly to line/speaker out. So just decrease
+volume of "speaker" if you have problems with microphone feedback.
+
+GUS ACE works too but any attempt to record or to use the MIDI port
+will fail.
+
+GUS PnP (with RAM) is partially supported but it needs to be initialized using
+DOS or isapnptools before starting the driver.
+
+MPU401 and Windows Sound System
+-------------------------------
+
+Again. Don't enable these options in case your card is listed
+somewhere else in this file.
+
+Configuring these cards is obvious (or it should be). With MSS
+you should probably enable the OPL3 synth also since
+most MSS compatible cards have it. However check that this is true
+before enabling OPL3.
+
+Sound driver supports more than one MPU401 compatible cards at the same time
+but the config program asks config info for just the first of them.
+Adding the second or third MPU interfaces must be done manually by
+editing sound/local.h (after running the config program). Add defines for
+MPU2_BASE & MPU2_IRQ (and MPU3_BASE & MPU3_IRQ) to the file.
+
+CAUTION!
+
+The default I/O base of Adaptec AHA-1542 SCSI controller is 0x330 which
+is also the default of the MPU401 driver. Don't configure the sound driver to
+use 0x330 as the MPU401 base if you have a AHA1542. The kernel will not boot
+if you make this mistake.
+
+PSS
+---
+
+Even the PSS cards are compatible with SB, MSS and MPU401, you must not
+enable these options when configuring the driver. The configuration
+program handles these options itself. (You may use the SB, MPU and MSS options
+together with PSS if you have another card on the system).
+
+The PSS driver enables MSS and MPU401 modes of the card. SB is not enabled 
+since it doesn't work concurrently with MSS. The driver loads also a
+DSP algorithm which is used to for the general MIDI emulation. The
+algorithm file (.ld) is read by the config program and written to a
+file included when the pss.c is compiled. For this reason the config
+program asks if you want to download the file. Use the genmidi.ld file
+distributed with the DOS/Windows drivers of the card (don't use the mt32.ld).
+With some cards the file is called 'synth.ld'. You must have access to
+the file when configuring the driver. The easiest way is to mount the DOS
+partition containing the file with Linux. 
+
+It's possible to load your own DSP algorithms and run them with the card.
+Look at the directory pss_test of snd-util-3.0.tar.gz for more info.
+
+AudioTrix Pro
+-------------
+
+You have to enable the OPL3 and SB (not SB Pro or SB16) drivers in addition
+to the native AudioTrix driver. Don't enable MSS or MPU drivers.
+
+Configuring ATP is little bit tricky since it uses so many I/O, IRQ and
+DMA numbers. Using the same values than with DOS/Win is a good idea. Don't
+attempt to use the same IRQ or DMA channels twice.
+
+The SB mode of ATP is implemented so the ATP driver just enables SB
+in the proper address. The SB driver handles the rest. You have to configure
+both the SB driver and the SB mode of ATP to use the same IRQ, DMA and I/O
+settings.
+
+Also the ATP has a microcontroller for the General MIDI emulation (OPL4).
+For this reason the driver asks for the name of a file containing the
+microcode (TRXPRO.HEX). This file is usually located in the directory
+where the DOS drivers were installed. You must have access to this file
+when configuring the driver. 
+
+If you have the effects daughtercard, it must be initialized by running
+the setfx program of snd-util-3.0.tar.gz package. This step is not required
+when using the (future) binary distribution version of the driver.
+
+Ensoniq SoundScape
+------------------
+
+NOTE!	The new PnP SoundScape is not supported yet. Soundscape compatible
+	cards made by Reveal don't work with Linux. They use older revision
+	of the Soundscape chipset which is not fully compatible with
+	newer cards made by Ensoniq.
+
+The SoundScape driver handles initialization of MSS and MPU supports
+itself so you don't need to enable other drivers than SoundScape
+(enable also the /dev/dsp, /dev/sequencer and MIDI supports).
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!!!!!                                                                   !!!!
+!!!!! NOTE! Before version 3.5-beta6 there WERE two sets of audio 	!!!!
+!!!!!       device files (/dev/dsp0 and /dev/dsp1). The first one WAS   !!!!
+!!!!!	    used only for card initialization and the second for audio  !!!!
+!!!!!	    purposes. It WAS required to change /dev/dsp (a symlink) to !!!!
+!!!!!	    point to /dev/dsp1.						!!!!
+!!!!!                                                                   !!!!
+!!!!!	    This is not required with OSS versions 3.5-beta6 and later	!!!!
+!!!!!	    since there is now just one audio device file. Please 	!!!!
+!!!!!	    change /dev/dsp to point back to /dev/dsp0 if you are	!!!!
+!!!!!	    upgrading from an earlier driver version using		!!!!
+!!!!!	    (cd /dev;rm dsp;ln -s dsp0 dsp).				!!!!
+!!!!!                                                                   !!!!
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+The configuration program asks one DMA channel and two interrupts. One IRQ
+and one DMA is used by the MSS codec. The second IRQ is required for the
+MPU401 mode (you have to use different IRQs for both purposes).
+There were earlier two DMA channels for SoundScape but the current driver
+version requires just one.
+
+The SoundScape card has a Motorola microcontroller which must initialized
+_after_ boot (the driver doesn't initialize it during boot).
+The initialization is done by running the 'ssinit' program which is
+distributed in the snd-util-3.0.tar.gz package. You have to edit two
+defines in the ssinit.c and then compile the program. You may run ssinit 
+manually (after each boot) or add it to /etc/rc.d/rc.local.
+
+The ssinit program needs the microcode file that comes with the DOS/Windows
+driver of the card. You will need to use version 1.30.00 or later
+of the microcode file (sndscape.co0 or sndscape.co1 depending on
+your card model). THE OLD sndscape.cod WILL NOT WORK. IT WILL HANG YOUR
+MACHINE. The only way to get the new microcode file is to download
+and install the DOS/Windows driver from ftp://ftp.ensoniq.com/pub.
+
+Then you have to select the proper microcode file to use: soundscape.co0
+is the right one for most cards and sndscape.co1 is for few (older) cards
+made by Reveal and/or Spea. The driver has capability to detect the card
+version during boot. Look at the boot log messages in /var/adm/messages
+and locate the sound driver initialization message for the SoundScape
+card. If the driver displays string <Ensoniq Soundscape (old)>, you have
+an old card and you will need to use sndscape.co1. For other cards use
+soundscape.co0. New Soundscape revisions such as Elite and PnP use
+code files with higher numbers (.co2, .co3, etc.).
+
+NOTE!	Ensoniq Soundscape VIVO is not compatible with other Soundscape cards.
+	Currently it's possible to use it in Linux only with OSS/Linux
+	drivers.
+
+Check /var/adm/messages after running ssinit. The driver prints
+the board version after downloading the microcode file. That version
+number must match the number in the name of the microcode file (extension).
+
+Running ssinit with a wrong version of the sndscape.co? file is not
+dangerous as long as you don't try to use a file called sndscape.cod.
+If you have initialized the card using a wrong microcode file (sounds
+are terrible), just modify ssinit.c to use another microcode file and try
+again. It's possible to use an earlier version of sndscape.co[01] but it
+may sound weird.
+
+MAD16 (Pro) and Mozart
+----------------------
+
+You need to enable just the MAD16 /Mozart support when configuring
+the driver. _Don't_ enable SB, MPU401 or MSS. However you will need the
+/dev/audio, /dev/sequencer and MIDI supports.
+
+Mozart and OPTi 82C928 (the original MAD16) chips don't support
+MPU401 mode so enter just 0 when the configuration program asks the
+MPU/MIDI I/O base. The MAD16 Pro (OPTi 82C929) and 82C930 chips have MPU401
+mode.
+
+TB Tropez is based on the 82C929 chip. It has two MIDI ports.
+The one connected to the MAD16 chip is the second one (there is a second
+MIDI connector/pins somewhere??). If you have not connected the second MIDI
+port, just disable the MIDI port of MAD16. The 'Maui' compatible synth of
+Tropez is jumper configurable and not connected to the MAD16 chip (the
+Maui driver can be used with it).
+
+Some MAD16 based cards may cause feedback, whistle or terrible noise if the
+line3 mixer channel is turned too high. This happens at least with Shuttle
+Sound System. Current driver versions set volume of line3 low enough so
+this should not be a problem.
+
+If you have a MAD16 card which have an OPL4 (FM + Wave table) synthesizer
+chip (_not_ an OPL3), you have to append a line containing #define MAD16_OPL4
+to the file linux/drivers/sound/local.h (after running make config).
+
+MAD16 cards having a CS4231 codec support full duplex mode. This mode
+can be enabled by configuring the card to use two DMA channels. Possible
+DMA channel pairs are: 0&1, 1&0 and 3&0.
+
+NOTE! Cards having an OPTi 82C924/82C925 chip work with OSS/Free only in
+non-PnP mode (usually jumper selectable). The PnP mode is supported only
+by OSS/Linux.
+
+MV Jazz (ProSonic)
+------------------
+
+The Jazz16 driver is just a hack made to the SB Pro driver. However it works
+fairly well. You have to enable SB, SB Pro (_not_ SB16) and MPU401 supports
+when configuring the driver. The configuration program asks later if you
+want support for MV Jazz16 based cards (after asking SB base address). Answer
+'y' here and the driver asks the second (16 bit) DMA channel.
+
+The Jazz16 driver uses the MPU401 driver in a way which will cause
+problems if you have another MPU401 compatible card. In this case you must
+give address of the Jazz16 based MPU401 interface when the config
+program prompts for the MPU401 information. Then look at the MPU401
+specific section for instructions about configuring more than one MPU401 cards.
+
+Logitech Soundman Wave
+----------------------
+
+Read the above MV Jazz specific instructions first.
+
+The Logitech SoundMan Wave (don't confuse this with the SM16 or SM Games) is
+a MV Jazz based card which has an additional OPL4 based wave table
+synthesizer. The OPL4 chip is handled by an on board microcontroller
+which must be initialized during boot. The config program asks if
+you have a SM Wave immediately after asking the second DMA channel of jazz16.
+If you answer 'y', the config program will ask name of the file containing
+code to be loaded to the microcontroller. The file is usually called
+MIDI0001.BIN and it's located in the DOS/Windows driver directory. The file
+may also be called as TSUNAMI.BIN or something else (older cards?).
+
+The OPL4 synth will be inaccessible without loading the microcontroller code.
+
+Also remember to enable SB MPU401 support if you want to use the OPL4 mode.
+(Don't enable the 'normal' MPU401 device as with some earlier driver
+versions (pre 3.5-alpha8)).
+
+NOTE!	Don't answer 'y' when the driver asks about SM Games support
+	(the next question after the MIDI0001.BIN name). However
+	answering 'y' doesn't cause damage your computer so don't panic. 
+
+Sound Galaxies
+--------------
+
+There are many different Sound Galaxy cards made by Aztech. The 8 bit
+ones are fully SB or SB Pro compatible and there should be no problems
+with them. 
+
+The older 16 bit cards (SG Pro16, SG NX Pro16, Nova and Lyra) have
+an EEPROM chip for storing the configuration data. There is a microcontroller
+which initializes the card to match the EEPROM settings when the machine
+is powered on. These cards actually behave just like they have jumpers
+for all of the settings. Configure driver for MSS, MPU, SB/SB Pro  and OPL3 
+supports with these cards. 
+
+There are some new Sound Galaxies in the market. I have no experience with
+them so read the card's manual carefully.
+
+ESS ES1688 and ES688 'AudioDrive' based cards
+---------------------------------------------
+
+Support for these two ESS chips is embedded in the SB driver.
+Configure these cards just like SB. Enable the 'SB MPU401 MIDI port'
+if you want to use MIDI features of ES1688. ES688 doesn't have MPU mode
+so you don't need to enable it (the driver uses normal SB MIDI automatically
+with ES688).
+
+NOTE! ESS cards are not compatible with MSS/WSS so don't worry if MSS support
+of OSS doesn't work with it.
+
+There are some ES1688/688 based sound cards and (particularily) motherboards
+which use software configurable I/O port relocation feature of the chip.
+This ESS proprietary feature is supported only by OSS/Linux.
+
+There are ES1688 based cards which use different interrupt pin assignment than
+recommended by ESS (5, 7, 9/2 and 10). In this case all IRQ's don't work.
+At least a card called (Pearl?) Hypersound 16 supports IRQ15 but it doesn't
+work.
+
+ES1868 is a PnP chip which is (supposed to be) compatible with ESS1688
+brobably works with OSS/Free after initialization using isapnptools.
+
+Reveal cards
+------------
+
+There are several different cards made/marketed by Reveal. Some of them
+are compatible with SoundScape and some use the MAD16 chip. You may have
+to look at the card and try to identify its origin.
+
+Diamond
+-------
+
+The oldest (Sierra Aria based) sound cards made by Diamond are not supported
+(they may work if the card is initialized using DOS). The recent (LX?)
+models are based on the MAD16 chip which is supported by the driver.
+
+Audio Excel DSP16
+-----------------
+
+Support for this card is currently not functional. A new driver for it
+should be available later this year.
+
+PCMCIA cards
+------------
+
+Sorry, can't help. Some cards may work and some don't.
+
+TI TM4000M notebooks
+--------------------
+
+These computers have a built in sound support based on the Jazz chipset.
+Look at the instructions for MV Jazz (above). It's also important to note
+that there is something wrong with the mouse port and sound at least on
+some TM models. Don't enable the "C&T 82C710 mouse port support" when
+configuring Linux. Having it enabled is likely to cause mysterious problems
+and kernel failures when sound is used.
+
+miroSOUND
+---------
+
+The miroSOUND PCM12 has been used successfully. This card is based on
+the MAD16, OPL4, and CS4231A chips and everything said in the section
+about MAD16 cards applies here, too. The only major difference between
+the PCM12 and other MAD16 cards is that instead of the mixer in the
+CS4231 codec a separate mixer controlled by an on-board 80C32
+microcontroller is used. Control of the mixer takes place via the ACI
+(miro's audio control interface) protocol that is implemented in a
+separate lowlevel driver. Make sure you compile this ACI driver
+together with the normal MAD16 support when you use a miroSOUND PCM12
+card. The ACI mixer is controlled by /dev/mixer and the CS4231 mixer
+by /dev/mixer2. You usually don't want to change anything on the
+CS4231 mixer.
+
+The miroSOUND PCM12 is capable of full duplex operation (simultaneous
+PCM replay and recording), which allows you to implement nice
+real-time signal processing audio effect software and network
+telephones. The ACI mixer has to be configured into a special "solo"
+mode for duplex operation in order to avoid feedback caused by the
+mixer (input hears output signal). See lowlevel/aci.c for details on
+the ioctl() for activating the "solo" mode.
+
+The following configuration parameters have worked fine for the PCM12
+in Markus Kuhn's system, many other configurations might work, too:
+CONFIG_MAD16_BASE=0x530, CONFIG_MAD16_IRQ=11, CONFIG_MAD16_DMA=3, 
+CONFIG_MAD16_DMA2=0, CONFIG_MAD16_MPU_BASE=0x330, CONFIG_MAD16_MPU_IRQ=10,
+DSP_BUFFSIZE=65536, SELECTED_SOUND_OPTIONS=0x00281000.
+
+The miroSOUND PCM1 pro and the PCM20 are very similar to the PCM12.
+Perhaps the same ACI driver also works for these cards, however this
+has never actually been tested. The PCM20 contains a radio tuner,
+which is also controlled by ACI. This radio tuner is currently not
+supported by the ACI driver, but documentation for it was provided by
+miro and ACI tuner support could easily be added if someone is really
+interested.
+
+Compaq Deskpro XL
+-----------------
+
+The builtin sound hardware of Compaq Deskpro XL is now supported. 
+You need to configure the driver with MSS and OPL3 supports enabled.
+In addition you need to manually edit linux/drivers/sound/local.h and
+to add a line containing "#define DESKPROXL" if you used 
+make menuconfig/xconfig.
+
+Others?
+-------
+
+Since there are so many different sound cards, it's likely that I have 
+forgotten to mention many of them. Please inform me if you know yet another
+card which works with Linux, please inform me (or is anybody else
+willing to maintain a database of supported cards (just like in XF86)?).
+
+Cards not supported yet
+=======================
+
+Please check the version of sound driver you are using before 
+complaining that your card is not supported. It's possible you are 
+using a driver version which was released months before your card was
+introduced.  The driver's release date is listed after its version number in a
+"cat /dev/sndstat" printout and in the file linux/drivers/sound/soundvers.h.
+
+First of all, there is an easy way to make most sound cards work with Linux.
+Just use the DOS based driver to initialize the card to a known state, then use
+loadlin.exe to boot Linux. If Linux is configured to use the same I/O, IRQ and
+DMA numbers as DOS, the card could work.
+(ctrl-alt-del can be used in place of loadlin.exe but it doesn't work with
+new motherboards). This method works also with all/most PnP sound cards.
+
+Don't get fooled with SB compatibility. Most cards are compatible with
+SB but that may require a TSR which is not possible with Linux. If
+the card is compatible with MSS, it's a better choice. Some cards
+don't work in the SB and MSS modes at the same time.
+
+Then there are cards which are no longer manufactured and/or which
+are relatively rarely used (such as the 8 bit ProAudioSpectrum
+models). It's extremely unlikely that such cards ever get supported.
+Adding support for a new card requires much work and increases time
+required in maintaining the driver (some changes need to be done
+to all low level drivers and be tested too, maybe with multiple
+operating systems). For this reason I have made a decision to not support
+obsolete cards. It's possible that someone else makes a separately 
+distributed driver (diffs) for the card. 
+
+Writing a driver for a new card is not possible if there are no 
+programming information available about the card. If you don't
+find your new card from this file, look from the home page 
+(http://www.opensound.com/ossfree). Then please contact
+manufacturer of the card and ask if they have (or are willing to)
+released technical details of the card. Do this before contacting me. I
+can only answer 'no' if there are no programming information available.
+
+I have made decision to not accept code based on reverse engineering
+to the driver. There are three main reasons: First I don't want to break
+relationships to sound card manufacturers. The second reason is that
+maintaining and supporting a driver without any specs will be a pain.
+The third reason is that companies have freedom to refuse selling their
+products to other than Windows users.
+
+Some companies don't give low level technical information about their
+products to public or at least their require signing a NDA. It's not
+possible to implement a freeware driver for them. However it's possible
+that support for such cards become available in the commercial version
+of this driver (see http://www.4Front-tech.com/oss.html for more info).
+
+There are some common audio chipsets that are not supported yet. For example
+Sierra Aria and IBM Mwave. It's possible that these architectures
+get some support in future but I can't make any promises. Just look
+at the home page (http://www.opensound.com/ossfree/new_cards.html)
+for latest info.
+
+Information about unsupported sound cards and chipsets is welcome as well
+as free copies of sound cards, SDKs and operating systems.
+
+If you have any corrections and/or comments, please contact me.
+
+Hannu Savolainen
+hannu@opensound.com
+
+Personal home page:	   http://www.compusonic.fi/~hannu
+home page of OSS/Free: http://www.opensound.com/ossfree
+
+home page of commercial OSS
+(Open Sound System) drivers: http://www.opensound.com/oss.html
diff -u --recursive --new-file v2.2.0-pre7/linux/Documentation/sound/README.modules linux/Documentation/sound/README.modules
--- v2.2.0-pre7/linux/Documentation/sound/README.modules	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sound/README.modules	Sun Jan 17 18:23:01 1999
@@ -0,0 +1,99 @@
+Building a modular sound driver
+================================
+
+  The following information is current as of linux-2.1.85. Check the other
+readme files, especially README.OSS, for information not specific to
+making sound modular.
+
+  First, configure your kernel. This is an idea of what you should be
+setting in the sound section:
+
+<M> Sound card support 
+
+<M> 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support 
+
+  I have SoundBlaster. Select your card from the list.
+
+<M> Generic OPL2/OPL3 FM synthesizer support 
+<M> FM synthesizer (YM3812/OPL-3) support 
+
+  If you don't set these, you will probably find you can play .wav files
+but not .midi. As the help for them says, set them unless you know your
+card does not use one of these chips for FM support.
+
+  Once you are configured, make zlilo, modules, modules_install; reboot.
+Note that it is no longer necessary or possible to configure sound in the
+drivers/sound dir. Now one simply configures and makes one's kernel and
+modules in the usual way.
+
+ Then, add to your /etc/modules.conf or /etc/conf.modules something like:
+
+alias char-major-14 sb
+post-install sb /sbin/modprobe "-k" "adlib_card"
+options sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330
+options adlib_card io=0x388     # FM synthesizer
+
+  The effect of this is that the sound driver and all necessary bits and
+pieces autoload on demand, assuming you use kerneld (a sound choice) and
+autoclean when not in use. Also, options for the device drivers are
+set. They will not work without them. Change as appropriate for your card.
+If you are not yet using the very cool kerneld, you will have to "modprobe
+-k sb" yourself to get things going. Eventually things may be fixed so
+that this kludgery is not necessary; for the time being, it seems to work
+well.
+
+  Replace 'sb' with the driver for your card, and give it the right
+options. To find the filename of the driver, look in
+/lib/modules/<kernel-version>/misc. Mine looks like:
+
+adlib_card.o # This is the generic OPLx driver
+opl3.o # The OPL3 driver
+sb.o # <<The SoundBlaster driver. Yours may differ.>>
+sound.o # The sound driver
+uart401.o # Used by sb, maybe other cards
+
+ Whichever card you have, try feeding it the options that would be the
+default if you were making the driver wired, not as modules. You can look
+at the init_module() code for the card to see what args are expected.
+
+ Note that at present there is no way to configure the io, irq and other
+parameters for the modular drivers as one does for the wired drivers.. One
+needs to pass the modules the necessary parameters as arguments, either
+with /etc/modules.conf or with command-line args to modprobe, e.g.
+
+modprobe -k sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330
+modprobe -k adlib_card io=0x388
+
+ recommend using /etc/modules.conf.
+
+Persistent DMA Buffers:
+
+The sound modules normally allocate DMA buffers during open() and
+deallocate them during close(). Linux can often have problems allocating
+DMA buffers for ISA cards on machines with more than 16MB RAM. This is
+because ISA DMA buffers must exist below the 16MB boundry and it is quite
+possible that we can't find a large enough free block in this region after
+the machine has been running for any amount of time. The way to avoid this
+problem is to allocate the DMA buffers during module load and deallocate
+them when the module is unloaded. For this to be effective we need to load
+the sound modules right after the kernel boots, either manually or by an
+init script, and keep them around until we shut down. This is a little
+wasteful of RAM, but it guarantees that sound always works.
+
+To make the sound driver use persistent DMA buffers we need to pass the
+sound.o module a "dmabuf=1" command-line argument. This is normally done
+in /etc/conf.modules (or the more proper /etc/modules.conf) like so:
+
+options sound		dmabuf=1
+
+If you have 16MB or less RAM or a PCI sound card, this is wasteful and
+unnecessary. It is possible that machine with 16MB or less RAM will find
+this option useful, but if your machine is so memory-starved that it
+cannot find a 64K block free, you will be wasting even more RAM by keeping
+the sound modules loaded and the DMA buffers allocated when they are not
+needed. The proper solution is to upgrade your RAM. But you do also have
+this improper solution as well. Use it wisely.
+
+  I'm afraid I know nothing about anything but my setup, being more of a
+text-mode guy anyway. If you have options for other cards or other helpful
+hints, send them to me, Jim Bray, jb@as220.org, http://as220.org/jb.
diff -u --recursive --new-file v2.2.0-pre7/linux/MAINTAINERS linux/MAINTAINERS
--- v2.2.0-pre7/linux/MAINTAINERS	Thu Jan  7 15:11:35 1999
+++ linux/MAINTAINERS	Mon Jan 18 12:47:33 1999
@@ -170,6 +170,12 @@
 W:	http://www.dandelion.com/Linux/
 S:	Maintained
 
+CONFIGURE, MENUCONFIG, XCONFIG
+P:	Michael Elizabeth Chastain
+M:	mec@shout.net
+L:	linux-kbuild@torque.net
+S:	Maintained
+
 CONFIGURE.HELP
 P:	Axel Boldt
 M:	boldt@math.ucsb.edu
@@ -343,6 +349,12 @@
 M:	perex@jcu.cz
 S:	Maintained
 
+IBM MCA SCSI SUBSYSTEM DRIVER
+P:	Michael Lang
+M:	langa2@kph.uni-mainz.de
+W:	http://www.uni-mainz.de/~langm000/linux.html
+S:	Maintained
+
 IDE DRIVER [GENERAL]
 P:	Andre Hedrick
 M:	hedrick@astro.dyer.vanderbilt.edu
@@ -352,8 +364,6 @@
 IDE/ATAPI CDROM DRIVER 
 P:	Jens Axboe
 M:	axboe@image.dk
-P:	Chris Zwilling
-M:	chris@cloudnet.com
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
@@ -446,12 +456,6 @@
 W:	http://www.tazenda.demon.co.uk/phil/linux-hp
 S:	Maintained
 
-MENUCONFIG
-P:	Michael Elizabeth Chastain
-M:	mec@shout.net
-L:	linux-kernel@vger.rutgers.edu
-S:	Maintained
-
 MIPS
 P:	Ralf Baechle
 M:	ralf@gnu.ai.mit.edu
@@ -731,8 +735,6 @@
 UNIFORM CDROM DRIVER 
 P:	Jens Axboe
 M:	axboe@image.dk
-P:	Chris Zwilling
-M:	chris@cloudnet.com
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
diff -u --recursive --new-file v2.2.0-pre7/linux/Makefile linux/Makefile
--- v2.2.0-pre7/linux/Makefile	Wed Jan 13 15:00:41 1999
+++ linux/Makefile	Sun Jan 17 18:32:26 1999
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
 SUBLEVEL = 0
-EXTRAVERSION =-pre7
+EXTRAVERSION =-pre8
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
@@ -277,7 +277,7 @@
 	 else \
 	   echo \#define LINUX_COMPILE_DOMAIN ; \
 	 fi >> .ver
-	@echo \#define LINUX_COMPILER \"`$(CC) -v 2>&1 | tail -1`\" >> .ver
+	@echo \#define LINUX_COMPILER \"`$(CC) $(CFLAGS) -v 2>&1 | tail -1`\" >> .ver
 	@mv -f .ver $@
 
 include/linux/version.h: ./Makefile
diff -u --recursive --new-file v2.2.0-pre7/linux/REPORTING-BUGS linux/REPORTING-BUGS
--- v2.2.0-pre7/linux/REPORTING-BUGS	Fri Jan  8 22:36:00 1999
+++ linux/REPORTING-BUGS	Thu Jan 14 22:53:02 1999
@@ -26,12 +26,12 @@
 information they're really interested in.
 
    First run the ver_linux script included as scripts/ver_linux or
-at <URL:ftp://ftp.sai.msu.su//sai2/ftp/pub/Linux/ver_linux> It checks out
+at <URL:ftp://ftp.sai.msu.su/pub/Linux/ver_linux> It checks out
 the version of some important subsystems. Run it with the commnd
 "sh scripts/ver_linux"
 
 Use that information to fill in all fields of the bug report form, and
-post it to the mailing list with a subject of "ISSUE: <one line
+post it to the mailing list with a subject of "PROBLEM: <one line
 summary from [1.]>" for easy identification by the developers    
 
 [1.] One line summary of the problem:    
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/alpha/Makefile linux/arch/alpha/Makefile
--- v2.2.0-pre7/linux/arch/alpha/Makefile	Wed Jan 13 15:00:41 1999
+++ linux/arch/alpha/Makefile	Sat Jan 16 17:02:50 1999
@@ -19,6 +19,10 @@
 # Determine if GCC understands the -mcpu= option.
 have_mcpu := $(shell if $(CC) -mcpu=ev5 -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo y; else echo n; fi)
 
+have_mcpu_pca56 := $(shell if $(CC) -mcpu=pca56 -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo y; else echo n; fi)
+
+have_mcpu_ev6 := $(shell if $(CC) -mcpu=ev6 -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo y; else echo n; fi)
+
 # Turn on the proper cpu optimizations.
 ifeq ($(have_mcpu),y)
   # If GENERIC, make sure to turn off any instruction set extensions that
@@ -34,8 +38,19 @@
   ifeq ($(CONFIG_ALPHA_PYXIS),y)
     CFLAGS := $(CFLAGS) -mcpu=ev56
   endif
+  ifeq ($(CONFIG_ALPHA_POLARIS),y)
+    ifeq ($(have_mcpu_pca56),y)
+      CFLAGS := $(CFLAGS) -mcpu=pca56
+    else
+      CFLAGS := $(CFLAGS) -mcpu=ev56
+    endif
+  endif
   ifeq ($(CONFIG_ALPHA_EV6),y)
-    CFLAGS := $(CFLAGS) -mcpu=ev6
+    ifeq ($(have_mcpu_ev6),y)
+      CFLAGS := $(CFLAGS) -mcpu=ev6
+    else
+      CFLAGS := $(CFLAGS) -mcpu=pca56
+    endif
   endif
 endif
 
@@ -55,7 +70,7 @@
    CFLAGS := $(CFLAGS) -Wa,-m21164a -DBWIO_ENABLED
  endif
  ifeq ($(CONFIG_ALPHA_POLARIS),y)
-   CFLAGS := $(CFLAGS) -Wa,-m21164a
+   CFLAGS := $(CFLAGS) -Wa,-m21164pc
  endif
 endif
 
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v2.2.0-pre7/linux/arch/alpha/config.in	Wed Jan 13 15:00:41 1999
+++ linux/arch/alpha/config.in	Thu Jan 14 10:29:28 1999
@@ -255,8 +255,6 @@
 
 source fs/Config.in
 
-source fs/nls/Config.in
-
 if [ "$CONFIG_VT" = "y" ]; then
   mainmenu_option next_comment
   comment 'Console drivers'
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/alpha/kernel/alpha_ksyms.c linux/arch/alpha/kernel/alpha_ksyms.c
--- v2.2.0-pre7/linux/arch/alpha/kernel/alpha_ksyms.c	Thu Dec 31 10:28:58 1998
+++ linux/arch/alpha/kernel/alpha_ksyms.c	Sat Jan 16 17:02:50 1999
@@ -28,6 +28,7 @@
 #include <asm/irq.h>
 #include <asm/machvec.h>
 #include <asm/pgtable.h>
+#include <asm/semaphore.h>
 
 #define __KERNEL_SYSCALLS__
 #include <asm/unistd.h>
@@ -139,6 +140,13 @@
 EXPORT_SYMBOL_NOVERS(__do_clear_user);
 EXPORT_SYMBOL(__strncpy_from_user);
 EXPORT_SYMBOL(__strlen_user);
+
+/*
+ * The following are specially called from the semaphore assembly stubs.
+ */
+EXPORT_SYMBOL_NOVERS(__down_failed);
+EXPORT_SYMBOL_NOVERS(__down_failed_interruptible);
+EXPORT_SYMBOL_NOVERS(__up_wakeup);
 
 /* 
  * SMP-specific symbols.
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c
--- v2.2.0-pre7/linux/arch/alpha/kernel/irq.c	Wed Jan 13 15:00:41 1999
+++ linux/arch/alpha/kernel/irq.c	Sat Jan 16 17:02:50 1999
@@ -335,9 +335,12 @@
 #ifndef __SMP__
 		p += sprintf(p, "%10u ", kstat_irqs(i));
 #else
-		for (j = 0; j < smp_num_cpus; j++)
-			p += sprintf(p, "%10u ",
-				     kstat.irqs[cpu_logical_map(j)][i]);
+		{
+		  int j;
+		  for (j = 0; j < smp_num_cpus; j++)
+			  p += sprintf(p, "%10u ",
+				       kstat.irqs[cpu_logical_map(j)][i]);
+		}
 #endif
 		p += sprintf(p, "  %c%s",
 			     (action->flags & SA_INTERRUPT)?'+':' ',
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/alpha/kernel/process.c linux/arch/alpha/kernel/process.c
--- v2.2.0-pre7/linux/arch/alpha/kernel/process.c	Wed Jan 13 15:00:41 1999
+++ linux/arch/alpha/kernel/process.c	Mon Jan 18 09:55:50 1999
@@ -261,20 +261,15 @@
 {
 	if (!usp)
 		usp = rdusp();
-	return do_fork(clone_flags & ~CLONE_VFORK, usp, (struct pt_regs *) (swstack+1));
+	return do_fork(clone_flags, usp, (struct pt_regs *) (swstack+1));
 }
 
 int alpha_vfork(struct switch_stack * swstack)
 {
 	int child;
-	struct semaphore sem = MUTEX_LOCKED;
 
-	current->vfork_sem = &sem;
 	child = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(),
 			(struct pt_regs *) (swstack+1));
-
-	if (child > 0)
-		down(&sem);
 
 	return child;
 }
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/alpha/kernel/setup.c linux/arch/alpha/kernel/setup.c
--- v2.2.0-pre7/linux/arch/alpha/kernel/setup.c	Wed Jan 13 15:00:41 1999
+++ linux/arch/alpha/kernel/setup.c	Thu Jan 14 10:21:40 1999
@@ -94,66 +94,46 @@
  * Declare all of the machine vectors.
  */
 
-extern struct alpha_machine_vector alcor_mv;
-extern struct alpha_machine_vector alphabook1_mv;
-extern struct alpha_machine_vector avanti_mv;
-extern struct alpha_machine_vector cabriolet_mv;
-extern struct alpha_machine_vector dp264_mv;
-extern struct alpha_machine_vector eb164_mv;
-extern struct alpha_machine_vector eb64p_mv;
-extern struct alpha_machine_vector eb66_mv;
-extern struct alpha_machine_vector eb66p_mv;
-extern struct alpha_machine_vector jensen_mv;
-extern struct alpha_machine_vector lx164_mv;
-extern struct alpha_machine_vector miata_mv;
-extern struct alpha_machine_vector mikasa_mv;
-extern struct alpha_machine_vector mikasa_primo_mv;
-extern struct alpha_machine_vector monet_mv;
-extern struct alpha_machine_vector webbrick_mv;
-extern struct alpha_machine_vector noname_mv;
-extern struct alpha_machine_vector noritake_mv;
-extern struct alpha_machine_vector noritake_primo_mv;
-extern struct alpha_machine_vector p2k_mv;
-extern struct alpha_machine_vector pc164_mv;
-extern struct alpha_machine_vector rawhide_mv;
-extern struct alpha_machine_vector ruffian_mv;
-extern struct alpha_machine_vector rx164_mv;
-extern struct alpha_machine_vector sable_mv;
-extern struct alpha_machine_vector sable_gamma_mv;
-extern struct alpha_machine_vector sx164_mv;
-extern struct alpha_machine_vector takara_mv;
-extern struct alpha_machine_vector xl_mv;
-extern struct alpha_machine_vector xlt_mv;
-#pragma weak alcor_mv
-#pragma weak alphabook1_mv
-#pragma weak avanti_mv
-#pragma weak cabriolet_mv
-#pragma weak dp264_mv
-#pragma weak eb164_mv
-#pragma weak eb64p_mv
-#pragma weak eb66_mv
-#pragma weak eb66p_mv
-#pragma weak jensen_mv
-#pragma weak lx164_mv
-#pragma weak miata_mv
-#pragma weak mikasa_mv
-#pragma weak mikasa_primo_mv
-#pragma weak monet_mv
-#pragma weak webbrick_mv
-#pragma weak noname_mv
-#pragma weak noritake_mv
-#pragma weak noritake_primo_mv
-#pragma weak p2k_mv
-#pragma weak pc164_mv
-#pragma weak rawhide_mv
-#pragma weak ruffian_mv
-#pragma weak rx164_mv
-#pragma weak sable_mv
-#pragma weak sable_gamma_mv
-#pragma weak sx164_mv
-#pragma weak takara_mv
-#pragma weak xl_mv
-#pragma weak xlt_mv
+/* GCC 2.7.2 (on alpha at least) is lame.  It does not support either 
+   __attribute__((weak)) or #pragma weak.  Bypass it and talk directly
+   to the assembler.  */
+
+#define WEAK(X) \
+	extern struct alpha_machine_vector X; \
+	asm(".weak "#X)
+
+WEAK(alcor_mv);
+WEAK(alphabook1_mv);
+WEAK(avanti_mv);
+WEAK(cabriolet_mv);
+WEAK(dp264_mv);
+WEAK(eb164_mv);
+WEAK(eb64p_mv);
+WEAK(eb66_mv);
+WEAK(eb66p_mv);
+WEAK(jensen_mv);
+WEAK(lx164_mv);
+WEAK(miata_mv);
+WEAK(mikasa_mv);
+WEAK(mikasa_primo_mv);
+WEAK(monet_mv);
+WEAK(noname_mv);
+WEAK(noritake_mv);
+WEAK(noritake_primo_mv);
+WEAK(p2k_mv);
+WEAK(pc164_mv);
+WEAK(rawhide_mv);
+WEAK(ruffian_mv);
+WEAK(rx164_mv);
+WEAK(sable_mv);
+WEAK(sable_gamma_mv);
+WEAK(sx164_mv);
+WEAK(takara_mv);
+WEAK(webbrick_mv);
+WEAK(xl_mv);
+WEAK(xlt_mv);
+
+#undef WEAK
 
 
 void __init
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/alpha/kernel/smp.c linux/arch/alpha/kernel/smp.c
--- v2.2.0-pre7/linux/arch/alpha/kernel/smp.c	Thu Dec 31 10:28:58 1998
+++ linux/arch/alpha/kernel/smp.c	Sat Jan 16 17:02:50 1999
@@ -670,7 +670,7 @@
 {
 	int this_cpu = smp_processor_id();
 	volatile int * pending_ipis = &ipi_bits[this_cpu];
-	unsigned long ops, which;
+	unsigned long ops;
 
 	DBGS(("handle_ipi: on CPU %d ops 0x%x PC 0x%lx\n",
 	      this_cpu, *pending_ipis, regs->pc));
@@ -716,7 +716,7 @@
 			halt();
 		}
 		else {
-			printk(KERN_CRIT "unknown_ipi() on CPU %ld: %d\n",
+			printk(KERN_CRIT "unknown_ipi() on CPU %d: %lu\n",
 			       this_cpu, which);
 		}
 	  } while (ops);
@@ -894,7 +894,7 @@
 
 #else
 
-#define spinlock_raise_ipl(LOCK)	((LOCK), 0)
+#define spinlock_raise_ipl(LOCK)	((void)(LOCK), 0)
 #define spinlock_restore_ipl(PREV)	((void)(PREV))
 
 #endif /* MANAGE_SPINLOCK_IPL */
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/alpha/lib/Makefile linux/arch/alpha/lib/Makefile
--- v2.2.0-pre7/linux/arch/alpha/lib/Makefile	Fri Oct 23 22:01:19 1998
+++ linux/arch/alpha/lib/Makefile	Sat Jan 16 17:02:51 1999
@@ -7,7 +7,7 @@
 	strcat.o strcpy.o strncat.o strncpy.o stxcpy.o stxncpy.o \
 	strchr.o strrchr.o \
 	copy_user.o clear_user.o strncpy_from_user.o strlen_user.o \
-	csum_ipv6_magic.o strcasecmp.o \
+	csum_ipv6_magic.o strcasecmp.o semaphore.o \
 	srm_dispatch.o srm_fixup.o srm_puts.o srm_printk.o
 
 lib.a: $(OBJS)
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/alpha/lib/semaphore.S linux/arch/alpha/lib/semaphore.S
--- v2.2.0-pre7/linux/arch/alpha/lib/semaphore.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/alpha/lib/semaphore.S	Sat Jan 16 17:02:51 1999
@@ -0,0 +1,184 @@
+/*
+ *  linux/arch/alpha/lib/semaphore.S
+ *
+ *  Copyright (C) 1999  Richard Henderson
+ */
+
+/*
+ * The semaphore operations have a special calling sequence that
+ * allow us to do a simpler in-line version of them. These routines
+ * need to convert that sequence back into the C sequence when
+ * there is contention on the semaphore.
+ */
+
+	.set noat
+	.set noreorder
+	.align 4
+
+/* __down_failed takes the semaphore in $24, clobbers $24 and $28.  */
+
+	.globl	__down_failed
+	.ent	__down_failed
+__down_failed:
+	ldgp	$29,0($27)
+	lda	$30, -20*8($30)
+	stq	$28, 0*8($30)
+	stq	$0, 1*8($30)
+	stq	$1, 2*8($30)
+	stq	$2, 3*8($30)
+	stq	$3, 4*8($30)
+	stq	$4, 5*8($30)
+	stq	$5, 6*8($30)
+	stq	$6, 7*8($30)
+	stq	$7, 8*8($30)
+	stq	$16, 9*8($30)
+	stq	$17, 10*8($30)
+	stq	$18, 11*8($30)
+	stq	$19, 12*8($30)
+	stq	$20, 13*8($30)
+	stq	$21, 14*8($30)
+	stq	$22, 15*8($30)
+	stq	$23, 16*8($30)
+	stq	$25, 17*8($30)
+	stq	$26, 18*8($30)
+	.frame $30, 20*8, $28
+	.prologue 1
+	
+	mov	$24, $16
+	jsr	__down
+	
+	ldq	$28, 0*8($30)
+	ldq	$0, 1*8($30)
+	ldq	$1, 2*8($30)
+	ldq	$2, 3*8($30)
+	ldq	$3, 4*8($30)
+	ldq	$4, 5*8($30)
+	ldq	$5, 6*8($30)
+	ldq	$6, 7*8($30)
+	ldq	$7, 8*8($30)
+	ldq	$16, 9*8($30)
+	ldq	$17, 10*8($30)
+	ldq	$18, 11*8($30)
+	ldq	$19, 12*8($30)
+	ldq	$20, 13*8($30)
+	ldq	$21, 14*8($30)
+	ldq	$22, 15*8($30)
+	ldq	$23, 16*8($30)
+	ldq	$25, 17*8($30)
+	ldq	$26, 18*8($30)
+	lda	$30, 20*8($30)
+	ret	$31, ($28), 0
+	.end	__down_failed
+
+/* __down_failed_interruptible takes the semaphore in $24,
+   clobbers $28, returns success in $24.  */
+
+	.globl	__down_failed_interruptible
+	.ent	__down_failed_interruptible
+__down_failed_interruptible:
+	ldgp	$29,0($27)
+	ldgp	$29,0($27)
+	lda	$30, -20*8($30)
+	stq	$28, 0*8($30)
+	stq	$0, 1*8($30)
+	stq	$1, 2*8($30)
+	stq	$2, 3*8($30)
+	stq	$3, 4*8($30)
+	stq	$4, 5*8($30)
+	stq	$5, 6*8($30)
+	stq	$6, 7*8($30)
+	stq	$7, 8*8($30)
+	stq	$16, 9*8($30)
+	stq	$17, 10*8($30)
+	stq	$18, 11*8($30)
+	stq	$19, 12*8($30)
+	stq	$20, 13*8($30)
+	stq	$21, 14*8($30)
+	stq	$22, 15*8($30)
+	stq	$23, 16*8($30)
+	stq	$25, 17*8($30)
+	stq	$26, 18*8($30)
+	.frame $30, 20*8, $28
+	.prologue 1
+	
+	mov	$24, $16
+	jsr	__down
+	mov	$0, $24
+	
+	ldq	$28, 0*8($30)
+	ldq	$0, 1*8($30)
+	ldq	$1, 2*8($30)
+	ldq	$2, 3*8($30)
+	ldq	$3, 4*8($30)
+	ldq	$4, 5*8($30)
+	ldq	$5, 6*8($30)
+	ldq	$6, 7*8($30)
+	ldq	$7, 8*8($30)
+	ldq	$16, 9*8($30)
+	ldq	$17, 10*8($30)
+	ldq	$18, 11*8($30)
+	ldq	$19, 12*8($30)
+	ldq	$20, 13*8($30)
+	ldq	$21, 14*8($30)
+	ldq	$22, 15*8($30)
+	ldq	$23, 16*8($30)
+	ldq	$25, 17*8($30)
+	ldq	$26, 18*8($30)
+	lda	$30, 20*8($30)
+	ret	$31, ($28), 0
+	.end	__down_failed_interruptible
+
+/* __up_wakeup takes the semaphore in $24, clobbers $24 and $28.  */
+
+	.globl	__up_wakeup
+	.ent	__up_wakeup
+__up_wakeup:
+	ldgp	$29,0($27)
+	lda	$30, -20*8($30)
+	stq	$28, 0*8($30)
+	stq	$0, 1*8($30)
+	stq	$1, 2*8($30)
+	stq	$2, 3*8($30)
+	stq	$3, 4*8($30)
+	stq	$4, 5*8($30)
+	stq	$5, 6*8($30)
+	stq	$6, 7*8($30)
+	stq	$7, 8*8($30)
+	stq	$16, 9*8($30)
+	stq	$17, 10*8($30)
+	stq	$18, 11*8($30)
+	stq	$19, 12*8($30)
+	stq	$20, 13*8($30)
+	stq	$21, 14*8($30)
+	stq	$22, 15*8($30)
+	stq	$23, 16*8($30)
+	stq	$25, 17*8($30)
+	stq	$26, 18*8($30)
+	.frame $30, 20*8, $28
+	.prologue 1
+	
+	mov	$24, $16
+	jsr	__up
+	
+	ldq	$28, 0*8($30)
+	ldq	$0, 1*8($30)
+	ldq	$1, 2*8($30)
+	ldq	$2, 3*8($30)
+	ldq	$3, 4*8($30)
+	ldq	$4, 5*8($30)
+	ldq	$5, 6*8($30)
+	ldq	$6, 7*8($30)
+	ldq	$7, 8*8($30)
+	ldq	$16, 9*8($30)
+	ldq	$17, 10*8($30)
+	ldq	$18, 11*8($30)
+	ldq	$19, 12*8($30)
+	ldq	$20, 13*8($30)
+	ldq	$21, 14*8($30)
+	ldq	$22, 15*8($30)
+	ldq	$23, 16*8($30)
+	ldq	$25, 17*8($30)
+	ldq	$26, 18*8($30)
+	lda	$30, 20*8($30)
+	ret	$31, ($28), 0
+	.end	__up_wakeup
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/arm/config.in linux/arch/arm/config.in
--- v2.2.0-pre7/linux/arch/arm/config.in	Tue Dec 22 14:16:53 1998
+++ linux/arch/arm/config.in	Thu Jan 14 10:29:28 1999
@@ -211,8 +211,6 @@
 
 source fs/Config.in
 
-source fs/nls/Config.in
-
 mainmenu_option next_comment
 comment 'Kernel hacking'
 
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.2.0-pre7/linux/arch/i386/config.in	Fri Jan  8 22:36:00 1999
+++ linux/arch/i386/config.in	Thu Jan 14 10:29:28 1999
@@ -159,8 +159,6 @@
 
 source fs/Config.in
 
-source fs/nls/Config.in
-
 if [ "$CONFIG_VT" = "y" ]; then
   mainmenu_option next_comment
   comment 'Console drivers'
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.2.0-pre7/linux/arch/i386/defconfig	Thu Dec 31 10:28:58 1998
+++ linux/arch/i386/defconfig	Sun Jan 17 22:57:39 1999
@@ -15,8 +15,12 @@
 # CONFIG_M586 is not set
 # CONFIG_M586TSC is not set
 CONFIG_M686=y
-CONFIG_TSC=y
-CONFIG_GOOD_APIC=y
+CONFIG_X86_WP_WORKS_OK=y
+CONFIG_X86_INVLPG=y
+CONFIG_X86_BSWAP=y
+CONFIG_X86_POPAD_OK=y
+CONFIG_X86_TSC=y
+CONFIG_X86_GOOD_APIC=y
 # CONFIG_MATH_EMULATION is not set
 # CONFIG_MTRR is not set
 CONFIG_SMP=y
@@ -218,6 +222,7 @@
 # CONFIG_TR is not set
 # CONFIG_HOSTESS_SV11 is not set
 # CONFIG_COSA is not set
+# CONFIG_RCPCI is not set
 # CONFIG_WAN_DRIVERS is not set
 # CONFIG_LAPBETHER is not set
 # CONFIG_X25_ASY is not set
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c
--- v2.2.0-pre7/linux/arch/i386/kernel/apm.c	Fri Jan  8 22:36:00 1999
+++ linux/arch/i386/kernel/apm.c	Thu Jan 14 22:57:25 1999
@@ -32,6 +32,7 @@
  * Sep 1998, Version 1.6
  * Nov 1998, Version 1.7
  * Jan 1999, Version 1.8
+ * Jan 1999, Version 1.9
  *
  * History:
  *    0.6b: first version in official kernel, Linux 1.3.46
@@ -78,6 +79,17 @@
  *         change APM_NOINTS to CONFIG_APM_ALLOW_INTS
  *         remove dependency on CONFIG_PROC_FS
  *         Stephen Rothwell
+ *    1.9: Fix small typo.  <laslo@ilo.opole.pl>
+ *         Try to cope with BIOS's that need to have all display
+ *         devices blanked and not just the first one.
+ *         Ross Paterson <ross@soi.city.ac.uk>
+ *         Fix segment limit setting it has always been wrong as
+ *         the segments needed to have byte granularity.
+ *         Mark a few things __init.
+ *         Add hack to allow power off of SMP systems by popular request.
+ *         Use CONFIG_SMP instead of __SMP__
+ *         Ignore BOUNCES for three seconds.
+ *         Stephen Rothwell
  *
  * APM 1.1 Reference:
  *
@@ -213,7 +225,7 @@
 #define APM_ZERO_SEGS
 
 /*
- * Define to make all set_limit calls use 64k limits.  The APM 1.1 BIOS is
+ * Define to make all _set_limit calls use 64k limits.  The APM 1.1 BIOS is
  * supposed to provide limit information that it recognizes.  Many machines
  * do this correctly, but many others do not restrict themselves to their
  * claimed limit.  When this happens, they will cause a segmentation
@@ -242,6 +254,12 @@
 #define APM_CHECK_TIMEOUT	(HZ)
 
 /*
+ * If CONFIG_APM_IGNORE_SUSPEND_BOUNCE is defined then
+ * ignore suspend events for this amount of time
+ */
+#define BOUNCE_INTERVAL		(3 * HZ)
+
+/*
  * Save a segment register away
  */
 #define savesegment(seg, where) \
@@ -276,6 +294,7 @@
 	unsigned short	segment;
 }				apm_bios_entry;
 static int			apm_enabled = 0;
+static int			smp_hack = 0;
 #ifdef CONFIG_APM_CPU_IDLE
 static int			clock_slowed = 0;
 #endif
@@ -300,7 +319,7 @@
 
 static struct timer_list	apm_timer;
 
-static char			driver_version[] = "1.8";	/* no spaces */
+static char			driver_version[] = "1.9";	/* no spaces */
 
 #ifdef APM_DEBUG
 static char *	apm_event_name[] = {
@@ -526,7 +545,15 @@
 
 void apm_power_off(void)
 {
-	if (apm_enabled)
+	/*
+	 * smp_hack == 2 means that we would have enabled APM support
+	 * except there is more than one processor and so most of
+	 * the APM stuff is unsafe.  We will still try power down
+	 * because is is useful to some people and they know what
+	 * they are doing because they booted with the smp-power-off
+	 * kernel option.
+	 */
+	if (apm_enabled || (smp_hack == 2))
 		(void) apm_set_power_state(APM_STATE_OFF);
 }
 
@@ -534,12 +561,19 @@
 /* Called by apm_display_blank and apm_display_unblank when apm_enabled. */
 static int apm_set_display_power_state(u_short state)
 {
-	return set_power_state(0x0100, state);
+	int	error;
+
+	/* Blank the first display device */
+	error = set_power_state(0x0100, state);
+	if (error == APM_BAD_DEVICE)
+		/* try to blank them all instead */
+		error = set_power_state(0x01ff, state);
+	return error;
 }
 #endif
 
 #ifdef CONFIG_APM_DO_ENABLE
-static int apm_enable_power_management(void)
+static int __init apm_enable_power_management(void)
 {
 	u32	eax;
 
@@ -568,12 +602,9 @@
 	return APM_SUCCESS;
 }
 
-#if 0
-/* not used anywhere */
-static int apm_get_battery_status(u_short which, 
+static int apm_get_battery_status(u_short which, u_short *status,
 				  u_short *bat, u_short *life, u_short *nbat)
 {
-	u_short status;
 	u32	eax;
 	u32	ebx;
 	u32	ecx;
@@ -585,20 +616,20 @@
 		if (which != 1)
 			return APM_BAD_DEVICE;
 		*nbat = 1;
-		return apm_get_power_status(&status, bat, life);
+		return apm_get_power_status(status, bat, life);
 	}
 
 	if (apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax,
 			&ebx, &ecx, &edx, &esi))
 		return (eax >> 8) & 0xff;
+	*status = ebx;
 	*bat = ecx;
 	*life = edx;
 	*nbat = esi;
 	return APM_SUCCESS;
 }
-#endif
 
-static int apm_engage_power_management(u_short device)
+static int __init apm_engage_power_management(u_short device)
 {
 	u32	eax;
 
@@ -842,7 +873,8 @@
 			       "event 0x%02x\n", event);
 #endif
 #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE
-		if (ignore_bounce && ((jiffies - last_resume) > HZ))
+		if (ignore_bounce
+		    && ((jiffies - last_resume) > BOUNCE_INTERVAL))
 			ignore_bounce = 0;
 #endif
 		switch (event) {
@@ -1152,6 +1184,7 @@
 	unsigned short	bx;
 	unsigned short	cx;
 	unsigned short	dx;
+	unsigned short	nbat;
 	unsigned short	error;
 	unsigned short  ac_line_status = 0xff;
 	unsigned short  battery_status = 0xff;
@@ -1173,13 +1206,8 @@
 		if (apm_bios_info.version > 0x100) {
 			battery_flag = (cx >> 8) & 0xff;
 			if (dx != 0xffff) {
-				if ((dx & 0x8000) == 0x8000) {
-					units = "min";
-					time_units = dx & 0x7ffe;
-				} else {
-					units = "sec";
-					time_units = dx & 0x7fff;
-				}
+				units = (dx & 0x8000) ? "min" : "sec";
+				time_units = dx & 0x7fff;
 			}
 		}
 	}
@@ -1249,6 +1277,8 @@
 			str += 3;
 		if (strncmp(str, "debug", 5) == 0)
 			debug = !invert;
+		if (strncmp(str, "smp-power-off", 13) == 0)
+			smp_hack = !invert;
 		str = strchr(str, ',');
 		if (str != NULL)
 			str += strspn(str, ", \t");
@@ -1289,17 +1319,18 @@
 
 	/* BIOS < 1.2 doesn't set cseg_16_len */
 	if (apm_bios_info.version < 0x102)
-		apm_bios_info.cseg_16_len = 0xFFFF; /* 64k */
+		apm_bios_info.cseg_16_len = 0; /* 64k */
 
 	if (debug) {
 		printk(KERN_INFO "apm: entry %x:%lx cseg16 %x dseg %x",
 			apm_bios_info.cseg, apm_bios_info.offset,
 			apm_bios_info.cseg_16, apm_bios_info.dseg);
 		if (apm_bios_info.version > 0x100)
-			printk(" cseg len %x, cseg16 len %x, dseg len %x",
+			printk(" cseg len %x, dseg len %x",
 				apm_bios_info.cseg_len,
-				apm_bios_info.cseg_16_len,
 				apm_bios_info.dseg_len);
+		if (apm_bios_info.version > 0x101)
+			printk(" cseg16 len %x", apm_bios_info.cseg_16_len);
 		printk("\n");
 	}
 
@@ -1307,12 +1338,6 @@
 		printk(KERN_NOTICE "apm: disabled on user request.\n");
 		return;
 	}
-#ifdef __SMP__
-	if (smp_num_cpus > 1) {
-		printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
-		return;
-	}
-#endif
 
 	/*
 	 * Set up a segment that references the real mode segment 0x40
@@ -1322,7 +1347,7 @@
 	 */
 	set_base(gdt[APM_40 >> 3],
 		 __va((unsigned long)0x40 << 4));
-	set_limit(gdt[APM_40 >> 3], 4096 - (0x40 << 4));
+	_set_limit((char *)&gdt[APM_40 >> 3], 4095 - (0x40 << 4));
 
 	apm_bios_entry.offset = apm_bios_info.offset;
 	apm_bios_entry.segment = APM_CS;
@@ -1332,23 +1357,36 @@
 		 __va((unsigned long)apm_bios_info.cseg_16 << 4));
 	set_base(gdt[APM_DS >> 3],
 		 __va((unsigned long)apm_bios_info.dseg << 4));
-	if (apm_bios_info.version == 0x100) {
-		set_limit(gdt[APM_CS >> 3], 64 * 1024);
-		set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
-		set_limit(gdt[APM_DS >> 3], 64 * 1024);
-	} else {
-#ifdef APM_RELAX_SEGMENTS
+#ifndef APM_RELAX_SEGMENTS
+	if (apm_bios_info.version == 0x100)
+#endif
+	{
 		/* For ASUS motherboard, Award BIOS rev 110 (and others?) */
-		set_limit(gdt[APM_CS >> 3], 64 * 1024);
+		_set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1);
 		/* For some unknown machine. */
-		set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
+		_set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1);
 		/* For the DEC Hinote Ultra CT475 (and others?) */
-		set_limit(gdt[APM_DS >> 3], 64 * 1024);
-#else
-		set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
-		set_limit(gdt[APM_CS_16 >> 3], apm_bios_info.cseg_16_len);
-		set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
+		_set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1);
+	}
+#ifndef APM_RELAX_SEGMENTS
+	else {
+		_set_limit((char *)&gdt[APM_CS >> 3],
+			(apm_bios_info.cseg_len - 1) & 0xffff);
+		_set_limit((char *)&gdt[APM_CS_16 >> 3],
+			(apm_bios_info.cseg_16_len - 1) & 0xffff);
+		_set_limit((char *)&gdt[APM_DS >> 3],
+			(apm_bios_info.dseg_len - 1) & 0xffff);
+	}
+#endif
+#ifdef CONFIG_SMP
+	if (smp_num_cpus > 1) {
+		printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
+		if (smp_hack)
+			smp_hack = 2;
+		return;
+	}
 #endif
+	if (apm_bios_info.version > 0x100) {
 		/*
 		 * We only support BIOSs up to version 1.2
 		 */
@@ -1360,7 +1398,7 @@
 		}
 	}
 	if (debug) {
-		printk(KERN_INFO "apm: onnection version %d.%d\n",
+		printk(KERN_INFO "apm: Connection version %d.%d\n",
 			(apm_bios_info.version >> 8) & 0xff,
 			apm_bios_info.version & 0xff );
 
@@ -1381,23 +1419,23 @@
 			case 3: bat_stat = "charging"; break;
 			default: bat_stat = "unknown"; break;
 			}
-			printk(KERN_INFO "apm: AC %s, battery status %s, battery life ",
+			printk(KERN_INFO
+			       "apm: AC %s, battery status %s, battery life ",
 			       power_stat, bat_stat);
 			if ((cx & 0xff) == 0xff)
 				printk("unknown\n");
 			else
 				printk("%d%%\n", cx & 0xff);
 			if (apm_bios_info.version > 0x100) {
-				printk("apm: battery flag 0x%02x, battery life ",
+				printk(KERN_INFO
+				       "apm: battery flag 0x%02x, battery life ",
 				       (cx >> 8) & 0xff);
 				if (dx == 0xffff)
 					printk("unknown\n");
-				else {
-					if ((dx & 0x8000))
-						printk("%d minutes\n", dx & 0x7ffe );
-					else
-						printk("%d seconds\n", dx & 0x7fff );
-				}
+				else
+					printk("%d %s\n", dx & 0x7fff,
+						(dx & 0x8000) ?
+						"minutes" : "seconds");
 			}
 		}
 	}
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.2.0-pre7/linux/arch/i386/kernel/entry.S	Fri Jan  8 22:36:00 1999
+++ linux/arch/i386/kernel/entry.S	Mon Jan 18 09:55:50 1999
@@ -559,7 +559,7 @@
 	.long SYMBOL_NAME(sys_sendfile)
 	.long SYMBOL_NAME(sys_ni_syscall)		/* streams1 */
 	.long SYMBOL_NAME(sys_ni_syscall)		/* streams2 */
-	.long SYMBOL_NAME(sys_vfork)            /* 190 */
+	.long SYMBOL_NAME(sys_ni_syscall)            /* 190 */
 
 	/*
 	 * NOTE!! This doesn't have to be exact - we just have
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- v2.2.0-pre7/linux/arch/i386/kernel/head.S	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/kernel/head.S	Thu Jan 14 22:57:25 1999
@@ -534,10 +534,14 @@
 	.quad 0x00cff2000000ffff	/* 0x2b user   4GB data at 0x00000000 */
 	.quad 0x0000000000000000	/* not used */
 	.quad 0x0000000000000000	/* not used */
-	.quad 0x00c0920000000000	/* 0x40 APM set up for bad BIOS's */
-	.quad 0x00c09a0000000000	/* 0x48 APM CS    code */
-	.quad 0x00809a0000000000	/* 0x50 APM CS 16 code (16 bit) */
-	.quad 0x00c0920000000000	/* 0x58 APM DS    data */
+	/*
+	 * The APM segments have byte granularity and their bases
+	 * and limits are set at run time.
+	 */
+	.quad 0x0040920000000000	/* 0x40 APM set up for bad BIOS's */
+	.quad 0x00409a0000000000	/* 0x48 APM CS    code */
+	.quad 0x00009a0000000000	/* 0x50 APM CS 16 code (16 bit) */
+	.quad 0x0040920000000000	/* 0x58 APM DS    data */
 	.fill 2*NR_TASKS,8,0		/* space for LDT's and TSS's etc */
 
 /*
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v2.2.0-pre7/linux/arch/i386/kernel/process.c	Wed Jan 13 15:00:41 1999
+++ linux/arch/i386/kernel/process.c	Mon Jan 18 09:55:50 1999
@@ -475,24 +475,27 @@
 
 void release_segments(struct mm_struct *mm)
 {
-	/* forget local segments */
-	__asm__ __volatile__("movl %w0,%%fs ; movl %w0,%%gs"
-		: /* no outputs */
-		: "r" (0));
 	if (mm->segments) {
 		void * ldt = mm->segments;
-
-		/*
-		 * Get the LDT entry from init_task.
-		 */
-		current->tss.ldt = _LDT(0);
-		load_ldt(0);
-
 		mm->segments = NULL;
 		vfree(ldt);
 	}
 }
 
+void forget_segments(void)
+{
+	/* forget local segments */
+	__asm__ __volatile__("movl %w0,%%fs ; movl %w0,%%gs"
+		: /* no outputs */
+		: "r" (0));
+
+	/*
+	 * Get the LDT entry from init_task.
+	 */
+	current->tss.ldt = _LDT(0);
+	load_ldt(0);
+}
+
 /*
  * Create a kernel thread
  */
@@ -778,21 +781,7 @@
 	newsp = regs.ecx;
 	if (!newsp)
 		newsp = regs.esp;
-	return do_fork(clone_flags & ~CLONE_VFORK, newsp, &regs);
-}
-
-asmlinkage int sys_vfork(struct pt_regs regs)
-{
-	int child;
-	struct semaphore sem = MUTEX_LOCKED;
-
-	current->vfork_sem = &sem;
-	child = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs);
-
-	if (child > 0)
-		down(&sem);
-
-	return child;
+	return do_fork(clone_flags, newsp, &regs);
 }
 
 /*
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/i386/lib/checksum.S linux/arch/i386/lib/checksum.S
--- v2.2.0-pre7/linux/arch/i386/lib/checksum.S	Mon Dec 28 15:00:52 1998
+++ linux/arch/i386/lib/checksum.S	Fri Jan 15 14:36:20 1999
@@ -299,8 +299,8 @@
 	adcl %edx, %eax
 DST(	movl %edx, 28(%edi)	)
 
-SRC(	lea 32(%esi), %esi	)
-DST(	lea 32(%edi), %edi	)
+	lea 32(%esi), %esi
+	lea 32(%edi), %edi
 	dec %ecx
 	jne 1b
 	adcl $0, %eax
@@ -312,8 +312,8 @@
 SRC(3:	movl (%esi), %ebx	)
 	adcl %ebx, %eax
 DST(	movl %ebx, (%edi)	)
-SRC(	lea 4(%esi), %esi	)
-DST(	lea 4(%edi), %edi	)
+	lea 4(%esi), %esi
+	lea 4(%edi), %edi
 	dec %edx
 	jne 3b
 	adcl $0, %eax
@@ -322,9 +322,9 @@
 	cmpl $2, %ecx
 	jb 5f
 SRC(	movw (%esi), %cx	)
-SRC(	leal 2(%esi), %esi	)
+	leal 2(%esi), %esi
 DST(	movw %cx, (%edi)	)
-DST(	leal 2(%edi), %edi	)
+	leal 2(%edi), %edi
 	je 6f
 	shll $16,%ecx
 SRC(5:	movb (%esi), %cl	)
@@ -337,8 +337,8 @@
 # Exception handler:
 .section .fixup, "ax"							
 
-6000:
-
+6001:
+	movl ARGBASE+20(%esp), %ebx	# src_err_ptr
 	movl $-EFAULT, (%ebx)
 
 	# zero the complete destination - computing the rest
@@ -350,13 +350,10 @@
 
 	jmp 5000b
 
-6001:
-	movl ARGBASE+20(%esp), %ebx	# src_err_ptr
-	jmp 6000b
-
 6002:
 	movl ARGBASE+24(%esp), %ebx	# dst_err_ptr
-	jmp 6000b
+	movl $-EFAULT,(%ebx)
+	jmp 5000b
 
 .previous
 
@@ -426,17 +423,17 @@
 	adcl $0, %eax
 7:
 .section .fixup, "ax"
-6000:	movl $-EFAULT, (%ebx)
+6001:	movl	ARGBASE+20(%esp), %ebx	# src_err_ptr	
+	movl $-EFAULT, (%ebx)
 	# zero the complete destination (computing the rest is too much work)
 	movl ARGBASE+8(%esp),%edi	# dst
 	movl ARGBASE+12(%esp),%ecx	# len
 	xorl %eax,%eax
 	rep; stosb
 	jmp 7b
-6001:	movl ARGBASE+20(%esp), %ebx	# src_err_ptr
-	jmp 6000b	
 6002:	movl ARGBASE+24(%esp), %ebx	# dst_err_ptr
-	jmp 6000b
+	movl $-EFAULT, (%ebx)
+	jmp  7b			
 .previous				
 
 	popl %esi
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/m68k/config.in linux/arch/m68k/config.in
--- v2.2.0-pre7/linux/arch/m68k/config.in	Thu Jan  7 15:11:36 1999
+++ linux/arch/m68k/config.in	Thu Jan 14 10:29:28 1999
@@ -356,8 +356,6 @@
   endmenu
 fi
 
-source fs/nls/Config.in
-
 mainmenu_option next_comment
 comment 'Kernel hacking'
 
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/mips/config.in linux/arch/mips/config.in
--- v2.2.0-pre7/linux/arch/mips/config.in	Fri Oct 23 22:01:19 1998
+++ linux/arch/mips/config.in	Thu Jan 14 10:29:28 1999
@@ -192,8 +192,6 @@
 
 source fs/Config.in
 
-source fs/nls/Config.in
-
 comment 'Console drivers'
 source drivers/video/Config.in
 
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.2.0-pre7/linux/arch/ppc/config.in	Mon Dec 28 15:00:52 1998
+++ linux/arch/ppc/config.in	Thu Jan 14 10:29:28 1999
@@ -167,7 +167,6 @@
 
 source drivers/char/Config.in
 source fs/Config.in
-source fs/nls/Config.in
 
 mainmenu_option next_comment
 comment 'Sound'
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.2.0-pre7/linux/arch/sparc/config.in	Tue Dec 22 14:16:54 1998
+++ linux/arch/sparc/config.in	Thu Jan 14 10:29:28 1999
@@ -176,8 +176,6 @@
 
 source fs/Config.in
 
-source fs/nls/Config.in
-
 mainmenu_option next_comment
 comment 'Watchdog'
 
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/sparc64/config.in linux/arch/sparc64/config.in
--- v2.2.0-pre7/linux/arch/sparc64/config.in	Tue Dec 22 14:16:54 1998
+++ linux/arch/sparc64/config.in	Thu Jan 14 10:29:28 1999
@@ -239,8 +239,6 @@
 
 source fs/Config.in
 
-source fs/nls/Config.in
-
 mainmenu_option next_comment
 comment 'Watchdog'
 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/block/hd.c linux/drivers/block/hd.c
--- v2.2.0-pre7/linux/drivers/block/hd.c	Wed Jun 24 22:54:04 1998
+++ linux/drivers/block/hd.c	Thu Jan 14 10:31:41 1999
@@ -783,6 +783,7 @@
 	hd_ioctl,		/* ioctl */
 	NULL,			/* mmap */
 	hd_open,		/* open */
+	NULL,			/* flush */
 	hd_release,		/* release */
 	block_fsync		/* fsync */
 };
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.2.0-pre7/linux/drivers/block/ide-cd.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/block/ide-cd.c	Mon Jan 18 12:47:33 1999
@@ -2,7 +2,7 @@
  * linux/drivers/block/ide-cd.c
  * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
  * Copyright (C) 1996-1998  Erik Andersen <andersee@debian.org>
- * Copyright (C) 1998 Jens Axboe and Chris Zwilling
+ * Copyright (C) 1998, 1999 Jens Axboe
  *
  * May be copied or modified under the terms of the GNU General Public
  * License.  See linux/COPYING for more information.
@@ -17,7 +17,7 @@
  * ftp://fission.dt.wdc.com/pub/standards/SFF_atapi/spec/SFF8020-r2.6/PS/8020r26.ps
  *
  * Drives that deviate from the ATAPI standard will be accomodated as much
- * as possable via compile time or command-line options.  Since I only have
+ * as possible via compile time or command-line options.  Since I only have
  * a few drives, you generally need to send me patches...
  *
  * ----------------------------------
@@ -31,9 +31,8 @@
  *   use them (like trying to close the tray in drives that can't).
  * -Make it so that Pioneer CD DR-A24X and friends don't get screwed up on
  *   boot
- * -Handle older drives that can't report their speed. (i.e. check if they
- *   support a version of ATAPI where they can report their speed before
- *   checking their speed and believing what they return).
+ * -Integrate DVD-ROM support in driver. Thanks to Merete Gotsæd-Petersen
+ *   of Pioneer Denmark for providing me with a drive for testing.
  *
  *
  * ----------------------------------
@@ -2574,8 +2573,7 @@
 		curslot = CDROM_STATE_FLAGS (drive)->sanyo_slot;
 		if (curslot == 3)
 			curslot = 0;
-	}
-	else
+	} else
 #endif /* not STANDARD_ATAPI */
 	{
 		stat = cdrom_read_changer_info (drive);
@@ -2656,10 +2654,10 @@
 			return CDS_DISC_OK;
 
 		if (my_reqbuf.sense_key == NOT_READY) {
-			/* With my NEC260, at least, we can't distinguish
-			   between tray open and tray closed but no disc
-			   inserted. */
-			return CDS_TRAY_OPEN; 
+			/* ATAPI doesn't have anything that can help
+			   us decide whether the drive is really
+			   emtpy or the tray is just open. irk. */
+			return CDS_TRAY_OPEN;
 		}
 
 		return CDS_DRIVE_NOT_READY;
@@ -2835,13 +2833,11 @@
 static
 int ide_cdrom_probe_capabilities (ide_drive_t *drive)
 {
-	int stat, nslots, attempts = 3;
+	int stat, nslots = 0, attempts = 3;
  	struct {
 		char pad[8];
 		struct atapi_capabilities_page cap;
 	} buf;
-
-	nslots = 0;
 
 	if (CDROM_CONFIG_FLAGS (drive)->nec260)
 		return nslots;
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/block/ide-cd.h linux/drivers/block/ide-cd.h
--- v2.2.0-pre7/linux/drivers/block/ide-cd.h	Mon Dec 28 15:00:52 1998
+++ linux/drivers/block/ide-cd.h	Mon Jan 18 17:33:48 1999
@@ -4,7 +4,7 @@
  *  linux/drivers/block/ide_modes.h
  *
  *  Copyright (C) 1996  Erik Andersen
- *  Copyright (C) 1998  Jens Axboe and Chris Zwilling
+ *  Copyright (C) 1998, 1999 Jens Axboe
  */
 
 #include <asm/byteorder.h>
@@ -298,7 +298,7 @@
 	/* Drive supports reading CD-R discs with addressing method 2 */
 	__u8 method2             : 1; /* reserved in 1.2 */
 	/* Drive can read from CD-R/W (CD-E) discs (orange book, part III) */
-	__u8 cd_rw_read           : 1; /* reserved in 1.2 */
+	__u8 cd_rw_read		 : 1; /* reserved in 1.2 */
 	/* Drive supports read from CD-R discs (orange book, part II) */
 	__u8 cd_r_read           : 1; /* reserved in 1.2 */
 #elif defined(__LITTLE_ENDIAN_BITFIELD)
@@ -307,8 +307,7 @@
 	/* Drive can read from CD-R/W (CD-E) discs (orange book, part III) */
         __u8 cd_rw_read          : 1; /* reserved in 1.2 */
 	/* Drive supports reading CD-R discs with addressing method 2 */
-	__u8 method2             : 1; /* reserved in 1.2 */
-	__u8 reserved2           : 5;
+	__u8 reserved2		 : 5;
 #else
 #error "Please fix <asm/byteorder.h>"
 #endif
@@ -316,7 +315,7 @@
 #if defined(__BIG_ENDIAN_BITFIELD)
 	__u8 reserved3           : 6;
 	/* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */
-	__u8 cd_rw_write          : 1; /* reserved in 1.2 */
+	__u8 cd_rw_write	 : 1; /* reserved in 1.2 */
 	/* Drive supports write to CD-R discs (orange book, part II) */
 	__u8 cd_r_write          : 1; /* reserved in 1.2 */
 #elif defined(__LITTLE_ENDIAN_BITFIELD)
@@ -324,14 +323,14 @@
 	/* Drive can write to CD-R discs (orange book, part II) */
         __u8 cd_r_write          : 1; /* reserved in 1.2 */
 	/* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */
-        __u8 cd_rw_write          : 1; /* reserved in 1.2 */
+        __u8 cd_rw_write	 : 1; /* reserved in 1.2 */
 	__u8 reserved3           : 6;
 #else
 #error "Please fix <asm/byteorder.h>"
 #endif
 
 #if defined(__BIG_ENDIAN_BITFIELD)
-	__u8 reserved4           : 1;
+	__u8 reserved4           : 4;
 	/* Drive can read multisession discs. */
 	__u8 multisession        : 1;
 	/* Drive can read mode 2, form 2 data. */
@@ -404,7 +403,7 @@
 
 #if defined(__BIG_ENDIAN_BITFIELD)
 	/* Drive mechanism types. */
-	mechtype_t mechtype          : 3;
+	mechtype_t mechtype	 : 3;
 	__u8 reserved6           : 1;
 	/* Drive can eject a disc or changer cartridge. */
 	__u8 eject               : 1;
@@ -426,7 +425,7 @@
 	__u8 eject               : 1;
 	__u8 reserved6           : 1;
 	/* Drive mechanism types. */
-	mechtype_t mechtype          : 3;
+	mechtype_t mechtype	 : 3;
 #else
 #error "Please fix <asm/byteorder.h>"
 #endif
@@ -745,7 +744,7 @@
 
 	{ 0x6300, "End of user area encountered on this track" },
 
-	{ 0x6400, "Illegal mode for this track" },
+	{ 0x6400, "Illegal mode for this track or incompatible medium" },
 
 	{ 0xb900, "Play operation oborted (sic)" },
 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- v2.2.0-pre7/linux/drivers/block/ide-disk.c	Thu Jan  7 15:11:36 1999
+++ linux/drivers/block/ide-disk.c	Thu Jan 14 22:58:47 1999
@@ -742,8 +742,8 @@
 	 * if possible, give fdisk access to more of the drive,
 	 * by correcting bios_cyls:
 	 */
-	if ((capacity >= (id->cyls * id->heads * id->sectors)) &&
-	    (!drive->forced_geom)) {
+	if ((capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head)) &&
+	    (!drive->forced_geom) && drive->bios_sect && drive->bios_head) {
 		drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head;
 #ifdef DEBUG
 		printk("Fixing Geometry :: CHS=%d/%d/%d to CHS=%d/%d/%d\n",
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/block/ide-dma.c linux/drivers/block/ide-dma.c
--- v2.2.0-pre7/linux/drivers/block/ide-dma.c	Fri Jan  8 22:36:04 1999
+++ linux/drivers/block/ide-dma.c	Sun Jan 17 18:23:01 1999
@@ -369,7 +369,11 @@
 	return 1;
 }
 
-__initfunc(void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_ports))
+/*
+ *	This can be called for a dynamically installed interface. Don't initfunc it
+ */
+ 
+void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_ports)
 {
 	static unsigned long dmatable = 0;
 	static unsigned leftover = 0;
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/block/ide-pci.c linux/drivers/block/ide-pci.c
--- v2.2.0-pre7/linux/drivers/block/ide-pci.c	Fri Oct 23 22:01:20 1998
+++ linux/drivers/block/ide-pci.c	Thu Jan 14 22:53:02 1999
@@ -44,6 +44,7 @@
 #define DEVID_HT6565	((ide_pci_devid_t){PCI_VENDOR_ID_HOLTEK,  PCI_DEVICE_ID_HOLTEK_6565})
 #define DEVID_AEC6210	((ide_pci_devid_t){PCI_VENDOR_ID_ARTOP,   PCI_DEVICE_ID_ARTOP_ATP850UF})
 #define DEVID_W82C105	((ide_pci_devid_t){PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105})
+#define DEVID_UM8886A	((ide_pci_devid_t){PCI_VENDOR_ID_UMC,     PCI_DEVICE_ID_UMC_UM8886A})
 #define DEVID_UM8886BF	((ide_pci_devid_t){PCI_VENDOR_ID_UMC,     PCI_DEVICE_ID_UMC_UM8886BF})
 #define DEVID_HPT343	((ide_pci_devid_t){PCI_VENDOR_ID_TTI,     PCI_DEVICE_ID_TTI_HPT343})
 
@@ -137,6 +138,7 @@
 	{DEVID_NS87415,	"NS87415",	INIT_NS87415,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
 	{DEVID_AEC6210,	"AEC6210",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	OFF_BOARD,	0 },
 	{DEVID_W82C105,	"W82C105",	INIT_W82C105,	{{0x40,0x01,0x01}, {0x40,0x10,0x10}}, 	ON_BOARD,	0 },
+	{DEVID_UM8886A,	"UM8886A",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
 	{DEVID_UM8886BF,"UM8886BF",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
 	{DEVID_HPT343,	"HPT343",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	NEVER_BOARD,	16 },
 	{IDE_PCI_DEVID_NULL, "PCI_IDE",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 }};
@@ -386,6 +388,10 @@
 				mate->serialized = 1;
 			}
 		}
+		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886A) ||
+		    IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886BF))
+			hwif->irq = hwif->channel ? 15 : 14;
+
 #ifdef CONFIG_BLK_DEV_IDEDMA
 		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_SIS5513))
 			autodma = 0;
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.2.0-pre7/linux/drivers/block/ide.c	Thu Jan  7 15:11:36 1999
+++ linux/drivers/block/ide.c	Fri Jan 15 14:36:20 1999
@@ -2132,6 +2132,12 @@
 				return -EIO;
 			return 0;
 		}
+	        case HDIO_UNREGISTER_HWIF:
+			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+			/* should I check here for arg > MAX_HWIFS, or
+			   just let ide_unregister fail silently? -- shaver */
+			ide_unregister(arg);
+			return 0;
 		case HDIO_SET_NICE:
 			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
 			if (drive->driver == NULL)
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/block/loop.c linux/drivers/block/loop.c
--- v2.2.0-pre7/linux/drivers/block/loop.c	Fri Nov 27 13:09:23 1998
+++ linux/drivers/block/loop.c	Thu Jan 14 10:33:36 1999
@@ -225,6 +225,7 @@
 					if (!create_missing_block(lo, block, blksize)) {
 						goto error_out_lock;
 					}
+					real_block = bmap(lo->lo_dentry->d_inode, block);
 				}
 
 			}
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/block/ps2esdi.c linux/drivers/block/ps2esdi.c
--- v2.2.0-pre7/linux/drivers/block/ps2esdi.c	Thu Nov 12 16:21:18 1998
+++ linux/drivers/block/ps2esdi.c	Thu Jan 14 10:31:41 1999
@@ -44,6 +44,7 @@
 #include <linux/blk.h>
 #include <linux/mca.h>
 #include <linux/init.h>
+#include <linux/ioport.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- v2.2.0-pre7/linux/drivers/cdrom/cdrom.c	Fri Jan  8 22:36:04 1999
+++ linux/drivers/cdrom/cdrom.c	Mon Jan 18 17:33:01 1999
@@ -1,7 +1,7 @@
 /* linux/drivers/cdrom/cdrom.c. 
    Copyright (c) 1996, 1997 David A. van Leeuwen.
    Copyright (c) 1997, 1998 Erik Andersen <andersee@debian.org>
-   Copyright (c) 1998 Jens Axboe and Chris Zwilling
+   Copyright (c) 1998, 1999 Jens Axboe
 
    May be copied or modified under the terms of the GNU General Public
    License.  See linux/COPYING for more information.
@@ -96,11 +96,18 @@
   -- Check if drive is capable of doing what we ask before blindly changing
   cdi->options in various ioctl.
   -- Added version to proc entry.
+  
+  2.52 Jan 16, 1998 - Jens Axboe <axboe@image.dk>
+  -- Fixed an error in open_for_data where we would sometimes not return
+  the correct error value. Thanks Huba Gaspar <huba@softcell.hu>.
+  -- Fixed module usage count - usage was based on /proc/sys/dev
+  instead of /proc/sys/dev/cdrom. This could lead to an oops when other
+  modules had entries in dev.
 
 -------------------------------------------------------------------------*/
 
-#define REVISION "Revision: 2.51"
-#define VERSION "Id: cdrom.c 2.51 1998/12/20"
+#define REVISION "Revision: 2.52"
+#define VERSION "Id: cdrom.c 2.52 1999/01/16"
 
 /* I use an error-log mask to give fine grain control over the type of
    messages dumped to the system logs.  The available masks include: */
@@ -363,7 +370,7 @@
 					goto clean_up_and_return;
 				}
 			} else {
-				cdinfo(CD_OPEN, "bummer. this driver can't close the tray.\n"); 
+				cdinfo(CD_OPEN, "bummer. this drive can't close the tray.\n"); 
 				ret=-ENOMEDIUM;
 				goto clean_up_and_return;
 			}
@@ -371,6 +378,7 @@
 			ret = cdo->drive_status(cdi, CDSL_CURRENT);
 			if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN)) {
 				cdinfo(CD_OPEN, "bummer. the tray is still not closed.\n"); 
+				cdinfo(CD_OPEN, "tray might not contain a medium.\n");
 				ret=-ENOMEDIUM;
 				goto clean_up_and_return;
 			}
@@ -997,8 +1005,13 @@
 int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
                            void *buffer, size_t *lenp)
 {
-        int retv,pos;
+        int pos;
 	struct cdrom_device_info *cdi;
+	
+	if (!*lenp || (filp->f_pos && !write)) {
+		*lenp = 0;
+		return 0;
+	}
 
 	pos = sprintf(cdrom_drive_info, "CD-ROM information, " VERSION "\n");
 	
@@ -1061,18 +1074,14 @@
 
         strcpy(cdrom_drive_info+pos,"\n\n");
 	*lenp=pos+3;
-        if (!write) {
-        	retv = proc_dostring(ctl, write, filp, buffer, lenp);
-        }
-        else
-        	retv = proc_dostring(ctl, write, filp, buffer, lenp);
-        return retv;
+
+        return proc_dostring(ctl, write, filp, buffer, lenp);
 }
 
 /* Place files in /proc/sys/dev/cdrom */
 ctl_table cdrom_table[] = {
 	{DEV_CDROM_INFO, "info", &cdrom_drive_info, 
-		CDROM_STR_SIZE*sizeof(char), 0444, NULL, &cdrom_sysctl_info},
+		CDROM_STR_SIZE, 0444, NULL, &cdrom_sysctl_info},
 	{0}
 	};
 
@@ -1099,20 +1108,23 @@
  */
 static void cdrom_procfs_modcount(struct inode *inode, int fill)
 {
-       if (fill)
-               MOD_INC_USE_COUNT;
-       else
-               MOD_DEC_USE_COUNT;
+	if (fill) {
+		MOD_INC_USE_COUNT;
+	} else {
+		MOD_DEC_USE_COUNT;
+	}
 }
 
 static void cdrom_sysctl_register(void)
 {
 	static int initialized = 0;
 
-	if ( initialized == 1 )
+	if (initialized == 1)
 		return;
-	cdrom_sysctl_header = register_sysctl_table(cdrom_root_table, 0);
+
+	cdrom_sysctl_header = register_sysctl_table(cdrom_root_table, 1);
 	cdrom_root_table->de->fill_inode = &cdrom_procfs_modcount;
+
 	initialized = 1;
 }
 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.2.0-pre7/linux/drivers/char/bttv.c	Tue Dec 22 14:16:55 1998
+++ linux/drivers/char/bttv.c	Sun Jan 17 18:28:06 1999
@@ -17,66 +17,87 @@
     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-    
-    Modified to put the RISC code writer in the kernel and to fit a
-    common (and I hope safe) kernel interface. When we have an X extension
-    all will now be really sweet.
- 
-    TODO:  
-   
-    * move norm from tuner to channel struct!?
-      composite source from a satellite tuner can deliver different norms
-      depending on tuned channel
-    * mmap VBI data?
-    * fix RAW Composite grabbing for NTSC 
-    * fix VBI reading double frames when grabbing is active
-    * allow for different VDELAYs
-    * handle tda8425 properly
 */
 
 #include <linux/module.h>
+#include <linux/version.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/malloc.h>
-#include <linux/vmalloc.h>
 #include <linux/mm.h>
+#if LINUX_VERSION_CODE >= 0x020100
+#include <linux/poll.h>
+#endif
 #include <linux/pci.h>
 #include <linux/signal.h>
 #include <asm/io.h>
+#include <linux/ioport.h>
 #include <asm/pgtable.h>
 #include <asm/page.h>
 #include <linux/sched.h>
 #include <asm/segment.h>
 #include <linux/types.h>
 #include <linux/wrapper.h>
-
-#include <linux/videodev.h>
+#include <linux/interrupt.h>
 #include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x020100
 #include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#else
+#include <linux/bios32.h>
+#define mdelay(x) udelay((x)*1000)
+#define signal_pending(current)  (current->signal & ~current->blocked)
+#define sigfillset(set)
+
+static inline int time_before(unsigned long a, unsigned long b)
+{
+	return((long)((a) - (b)) < 0L);
+}
 
+static inline unsigned long
+copy_to_user(void *to, const void *from, unsigned long n)
+{
+        memcpy_tofs(to,from,n);
+	return 0;
+}
+
+static inline unsigned long
+copy_from_user(void *to, const void *from, unsigned long n)
+{
+        memcpy_fromfs(to,from,n);
+	return 0;
+}
+#define ioremap vremap 
+#define iounmap vfree
+#endif
+
+#include <linux/videodev.h>
 #include <linux/i2c.h>
 #include "bttv.h"
 #include "tuner.h"
 
-#define DEBUG(x)		/* Debug driver */	
+#define DEBUG(x) 		/* Debug driver */	
 #define IDEBUG(x) 		/* Debug interrupt handler */
 
+#if LINUX_VERSION_CODE >= 0x020117
 MODULE_PARM(vidmem,"i");
 MODULE_PARM(triton1,"i");
 MODULE_PARM(remap,"1-4i");
 MODULE_PARM(radio,"1-4i");
 MODULE_PARM(card,"1-4i");
 MODULE_PARM(pll,"1-4i");
-
-static int find_vga(void);
-static void bt848_set_risc_jmps(struct bttv *btv);
+#endif
 
 /* Anybody who uses more than four? */
 #define BTTV_MAX 4
 
+static int find_vga(void);
+static void bt848_set_risc_jmps(struct bttv *btv);
+
 static unsigned int vidmem=0;   /* manually set video mem address */
 static int triton1=0;
 #ifndef USE_PLL
@@ -98,15 +119,16 @@
 static struct bttv bttvs[BTTV_MAX];
 
 #define I2C_TIMING (0x7<<4)
-#define I2C_DELAY   50		/* Was 10 - some reports that more is needed
-				   regardless of what the spec says */
+#define I2C_DELAY   10
 #define I2C_SET(CTRL,DATA) \
     { btwrite((CTRL<<1)|(DATA), BT848_I2C); udelay(I2C_DELAY); }
 #define I2C_GET()   (btread(BT848_I2C)&1)
 
-#define EEPROM_WRITE_DELAY 20000
+#define EEPROM_WRITE_DELAY    20000
 #define BURSTOFFSET 76
 
+
+
 /*******************************/
 /* Memory management functions */
 /*******************************/
@@ -191,6 +213,8 @@
 	}
 }
 
+
+
 /*
  *	Create the giant waste of buffer space we need for now
  *	until we get DMA to user space sorted out (probably 2.3.x)
@@ -388,7 +412,9 @@
         I2C_BUSID_BT848,
 	NULL,
 
+#if LINUX_VERSION_CODE >= 0x020100
 	SPIN_LOCK_UNLOCKED,
+#endif
 
 	attach_inform,
 	detach_inform,
@@ -400,6 +426,70 @@
 };
  
 /* ----------------------------------------------------------------------- */
+/* some hauppauge specific stuff                                           */
+
+static unsigned char eeprom_data[256];
+static struct HAUPPAUGE_TUNER 
+{
+        int  id;
+        char *name;
+} 
+hauppauge_tuner[] = 
+{
+        { TUNER_ABSENT,        "" },
+        { TUNER_ABSENT,        "External" },
+        { TUNER_ABSENT,        "Unspecified" },
+        { TUNER_ABSENT,        "Philips FI1216" },
+        { TUNER_ABSENT,        "Philips FI1216MF" },
+        { TUNER_PHILIPS_NTSC,  "Philips FI1236" },
+        { TUNER_ABSENT,        "Philips FI1246" },
+        { TUNER_ABSENT,        "Philips FI1256" },
+        { TUNER_PHILIPS_PAL,   "Philips FI1216 MK2" },
+        { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" },
+        { TUNER_PHILIPS_NTSC,  "Philips FI1236 MK2" },
+        { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" },
+        { TUNER_ABSENT,        "Philips FI1256 MK2" },
+        { TUNER_ABSENT,        "Temic 4032FY5" },
+        { TUNER_TEMIC_PAL,     "Temic 4002FH5" },
+        { TUNER_TEMIC_PAL_I,   "Temic 4062FY5" },
+        { TUNER_ABSENT,        "Philips FR1216 MK2" },
+        { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" },
+        { TUNER_PHILIPS_NTSC,  "Philips FR1236 MK2" },
+        { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" },
+        { TUNER_ABSENT,        "Philips FR1256 MK2" },
+        { TUNER_PHILIPS_PAL,   "Philips FM1216" },
+        { TUNER_ABSENT,        "Philips FM1216MF" },
+        { TUNER_PHILIPS_NTSC,  "Philips FM1236" },
+};
+
+static void
+hauppauge_eeprom(struct i2c_bus *bus)
+{
+        struct bttv *btv = (struct bttv*)bus->data;
+        
+        readee(bus, eeprom_data);
+        if (eeprom_data[9] < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) 
+        {
+                btv->tuner_type = hauppauge_tuner[eeprom_data[9]].id;
+                printk("bttv%d: Hauppauge eeprom: tuner=%s (%d)\n",btv->nr,
+                       hauppauge_tuner[eeprom_data[9]].name,btv->tuner_type);
+        }
+}
+
+static void
+hauppauge_msp_reset(struct bttv *btv)
+{
+        /* Reset the MSP on some Hauppauge cards */
+        /* Thanks to Kyösti Mälkki (kmalkki@cc.hut.fi)! */
+        /* Can this hurt cards without one? What about Miros with MSP? */
+        btaor(32, ~32, BT848_GPIO_OUT_EN);
+        btaor(0, ~32, BT848_GPIO_DATA);
+        udelay(2500);
+        btaor(32, ~32, BT848_GPIO_DATA);
+        /* btaor(0, ~32, BT848_GPIO_OUT_EN); */
+}
+
+/* ----------------------------------------------------------------------- */
 
 
 struct tvcard
@@ -437,25 +527,17 @@
         {0, 0xc00, 0x800, 0x400, 0xc00, 0}},
         /* TurboTV */
         { 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 1, 1, 2, 3, 0}},
-	/* Newer Hauppauge (bt878) */
+        /* Newer Hauppauge (bt878) */
 	{ 3, 1, 0, 2, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4}},
-	/* MIRO PCTV pro */
-	{ 3, 1, 0, 2, 65551, { 2, 3, 1, 1}, {1,65537, 0, 0,10}},
+        /* MIRO PCTV pro */
+        { 3, 1, 0, 2, 65551, { 2, 3, 1, 1}, {1,65537, 0, 0,10}},
 	/* ADS Technologies Channel Surfer TV (and maybe TV+FM) */
-	{
-	 3, 4, 0, 2, 0x0F,
-	 { 0x02, 0x03, 0x01, 0x01},
-	 { 0x0D, 0x0E, 0x0B, 0x07, 0x00, 0x00},
-	 0x00
-	},
+	{ 3, 4, 0, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0}, 0},
+        /* AVerMedia TVCapture 98 */
+	{ 3, 4, 0, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0}, 0},
 };
 #define TVCARDS (sizeof(tvcards)/sizeof(tvcard))
 
-
-/*
- *	Tuner, Radio, internal, external and mute 
- */
- 
 static void audio(struct bttv *btv, int mode)
 {
 	btaor(tvcards[btv->type].gpiomask, ~tvcards[btv->type].gpiomask,
@@ -543,16 +625,16 @@
         fout*=12;
         fi=fout/fin;
 
-        fout=(fout-fin*fi)*256;
+        fout=(fout%fin)*256;
         fh=fout/fin;
 
-        fout=(fout-fin*fh)*256;
+        fout=(fout%fin)*256;
         fl=fout/fin;
 
         /*printk("0x%02x 0x%02x 0x%02x\n", fi, fh, fl);*/
-        btwrite(fl,BT848_PLL_F_LO);
-        btwrite(fh,BT848_PLL_F_HI);
-        btwrite(fi|BT848_PLL_X,BT848_PLL_XCI);
+        btwrite(fl, BT848_PLL_F_LO);
+        btwrite(fh, BT848_PLL_F_HI);
+        btwrite(fi|BT848_PLL_X, BT848_PLL_XCI);
 }
 
 static int set_pll(struct bttv *btv)
@@ -594,7 +676,7 @@
 	}
 	while(time_before(jiffies,tv));
 
-        for (i=0; i<100; i++) 
+        for (i=0; i<10; i++) 
         {
                 if ((btread(BT848_DSTATUS)&BT848_DSTATUS_PLOCK))
                         btwrite(0,BT848_DSTATUS);
@@ -615,13 +697,14 @@
 static void bt848_muxsel(struct bttv *btv, unsigned int input)
 {
 	btaor(tvcards[btv->type].gpiomask2,~tvcards[btv->type].gpiomask2,
-        	BT848_GPIO_OUT_EN);
+              BT848_GPIO_OUT_EN);
 
 	/* This seems to get rid of some synchronization problems */
 	btand(~(3<<5), BT848_IFORM);
 	mdelay(10); 
-
-       	input %= tvcards[btv->type].video_inputs;
+        
+        
+	input %= tvcards[btv->type].video_inputs;
 	if (input==tvcards[btv->type].svhs) 
 	{
 		btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
@@ -639,13 +722,83 @@
 		~tvcards[btv->type].gpiomask2, BT848_GPIO_DATA);
 }
 
+/*
+ *	Set the registers for the size we have specified. Don't bother
+ *	trying to understand this without the BT848 manual in front of
+ *	you [AC]. 
+ *
+ *	PS: The manual is free for download in .pdf format from 
+ *	www.brooktree.com - nicely done those folks.
+ */
+ 
+struct tvnorm 
+{
+        u32 Fsc;
+        u16 swidth, sheight; /* scaled standard width, height */
+	u16 totalwidth;
+	u8 adelay, bdelay, iform;
+	u32 scaledtwidth;
+	u16 hdelayx1, hactivex1;
+	u16 vdelay;
+        u8 vbipack;
+};
 
-#define VBIBUF_SIZE 65536
-
-/* Maximum sample number per VBI line is 2044, NTSC delivers 1600
-   Note that we write 2048-aligned to keep alignment to memory pages 
-   VBI_RISC is written so that it applies to either 2044 or 1600
+static struct tvnorm tvnorms[] = {
+	/* PAL-BDGHI */
+        /* max. active video is actually 922, but 924 is divisible by 4 and 3! */
+ 	/* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */
+#ifdef VIDEODAT
+        { 35468950,
+          924, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+          1135, 186, 924, 0x20, 255},
+#else
+        { 35468950,
+          924, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+          1135, 186, 924, 0x20, 255},
+#endif
+/*
+        { 35468950, 
+          768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+	  944, 186, 922, 0x20, 255},
+*/
+	/* NTSC */
+	{ 28636363,
+          768, 480,  910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0),
+          910, 128, 754, 0x1a, 144},
+/*
+	{ 28636363,
+          640, 480,  910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0),
+          780, 122, 754, 0x1a, 144},
 */
+#if 0
+	/* SECAM EAST */
+	{ 35468950, 
+          768, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1),
+	  944, 186, 922, 0x20, 255},
+#else
+	/* SECAM L */
+        { 35468950,
+          924, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1),
+          1135, 186, 922, 0x20, 255},
+#endif
+        /* PAL-NC */
+        { 28636363,
+          640, 576,  910, 0x68, 0x5d, (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
+          780, 130, 734, 0x1a, 144},
+	/* PAL-M */
+	{ 28636363, 
+          640, 480, 910, 0x68, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
+	  780, 135, 754, 0x1a, 144},
+	/* PAL-N */
+	{ 35468950, 
+          768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
+	  944, 186, 922, 0x20, 144},
+	/* NTSC-Japan */
+	{ 28636363,
+          640, 480,  910, 0x68, 0x5d, (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
+	  780, 135, 754, 0x16, 144},
+};
+#define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm))
 #define VBI_SPL 2044
 
 /* RISC command to write one VBI data line */
@@ -661,7 +814,11 @@
 	DEBUG(printk(KERN_DEBUG "vbievn: 0x%08x\n",(int)btv->vbi_even));
 	DEBUG(printk(KERN_DEBUG "po: 0x%08x\n",(int)po));
 	DEBUG(printk(KERN_DEBUG "pe: 0x%08x\n",(int)pe));
-
+        
+	/* setup proper VBI capture length for given video mode */
+	btwrite(tvnorms[btv->win.norm].vbipack, BT848_VBI_PACK_SIZE);
+	btwrite(1, BT848_VBI_PACK_DEL);
+        
 	*(po++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(po++)=0;
 	for (i=0; i<16; i++) 
 	{
@@ -697,12 +854,14 @@
        BT848_COLOR_FMT_RGB15,
        BT848_COLOR_FMT_YUY2,
        BT848_COLOR_FMT_BtYUV,
-       0,
-       0,
-       0,
+       -1,
+       -1,
+       -1,
        BT848_COLOR_FMT_RAW,
        BT848_COLOR_FMT_YCrCb422,
        BT848_COLOR_FMT_YCrCb411,
+       BT848_COLOR_FMT_YCrCb422,
+       BT848_COLOR_FMT_YCrCb411,
 };
 #define PALETTEFMT_MAX (sizeof(palette2fmt)/sizeof(int))
 
@@ -738,11 +897,120 @@
 }
 
 
-static int  make_vrisctab(struct bttv *btv, unsigned int *ro,
+static int  make_prisctab(struct bttv *btv, unsigned int *ro,
                           unsigned int *re, 
                           unsigned int *vbuf, unsigned short width,
                           unsigned short height, unsigned short fmt)
 {
+        unsigned long line, lmask;
+	unsigned long bl, blcr, blcb, rcmd;
+	unsigned long todo;
+	unsigned int **rp;
+	int inter;
+	unsigned long cbadr, cradr;
+	unsigned long vadr=(unsigned long) vbuf;
+	int shift, csize;	
+
+
+	switch(fmt)
+	{
+        case VIDEO_PALETTE_YUV422P:
+                csize=(width*height)>>1;
+                shift=1;
+                lmask=0;
+                break;
+                
+        case VIDEO_PALETTE_YUV411P:
+                csize=(width*height)>>2;
+                shift=2;
+                lmask=0;
+                break;
+	 				
+	 case VIDEO_PALETTE_YUV420P:
+                csize=(width*height)>>2;
+                shift=1;
+                lmask=1;
+                break;
+                
+	 case VIDEO_PALETTE_YUV410P:
+                csize=(width*height)>>4;
+                shift=2;
+                lmask=3;
+                break;
+                
+        default:
+                return -1;
+	}
+	cbadr=vadr+(width*height);
+	cradr=cbadr+csize;
+	inter = (height>btv->win.cropheight/2) ? 1 : 0;
+	
+	*(ro++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3; *(ro++)=0;
+	*(re++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3; *(re++)=0;
+  
+	for (line=0; line < (height<<(1^inter)); line++)
+	{
+		if(line==height)
+		{
+			vadr+=csize<<1;
+			cbadr=vadr+(width*height);
+			cradr=cbadr+csize;
+		}
+	        if (inter) 
+		        rp= (line&1) ? &re : &ro;
+		else 
+	                rp= (line>=height) ? &re : &ro; 
+	                
+
+	        if(line&lmask)
+	        	rcmd=BT848_RISC_WRITE1S23|BT848_RISC_SOL;
+	        else
+	        	rcmd=BT848_RISC_WRITE123|BT848_RISC_SOL;
+
+	        todo=width;
+		while(todo)
+		{
+                 bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr);
+                 blcr=(PAGE_SIZE-((PAGE_SIZE-1)&cradr))<<shift;
+		 blcb=(PAGE_SIZE-((PAGE_SIZE-1)&cbadr))<<shift;
+		 bl=(blcr<bl) ? blcr : bl;
+		 bl=(blcb<bl) ? blcb : bl;
+		 bl=(bl>todo) ? todo : bl;
+		 blcr=bl>>shift;
+		 blcb=blcr;
+		 /* bl now containts the longest row that can be written */
+		 todo-=bl;
+		 if(!todo) rcmd|=BT848_RISC_EOL; /* if this is the last EOL */
+		 
+		 *((*rp)++)=rcmd|bl;
+		 *((*rp)++)=blcb|(blcr<<16);
+		 *((*rp)++)=kvirt_to_bus(vadr);
+		 vadr+=bl;
+		 if((rcmd&(15<<28))==BT848_RISC_WRITE123)
+		 {
+		 	*((*rp)++)=kvirt_to_bus(cbadr);
+		 	cbadr+=blcb;
+		 	*((*rp)++)=kvirt_to_bus(cradr);
+		 	cradr+=blcr;
+		 }
+		 
+		 rcmd&=~BT848_RISC_SOL; /* only the first has SOL */
+		}
+	}
+	
+	*(ro++)=BT848_RISC_JUMP;
+	*(ro++)=btv->bus_vbi_even;
+	*(re++)=BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16);
+	*(re++)=btv->bus_vbi_odd;
+	
+	return 0;
+}
+ 
+static int  make_vrisctab(struct bttv *btv, unsigned int *ro,
+                          unsigned int *re, 
+                          unsigned int *vbuf, unsigned short width,
+                          unsigned short height, unsigned short palette)
+{
         unsigned long line;
 	unsigned long bpl;  /* bytes per line */
 	unsigned long bl;
@@ -751,11 +1019,14 @@
 	int inter;
 	unsigned long vadr=(unsigned long) vbuf;
 
-        if (btv->gfmt==BT848_COLOR_FMT_RAW) 
+        if (palette==VIDEO_PALETTE_RAW) 
                 return make_rawrisctab(btv, ro, re, vbuf);
+        if (palette>=VIDEO_PALETTE_PLANAR)
+                return make_prisctab(btv, ro, re, vbuf, width, height, palette);
+        
         
 	inter = (height>btv->win.cropheight/2) ? 1 : 0;
-	bpl=width*fmtbppx2[fmt&0xf]/2;
+	bpl=width*fmtbppx2[palette2fmt[palette]&0xf]/2;
 	
 	*(ro++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(ro++)=0;
 	*(re++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(re++)=0;
@@ -765,7 +1036,7 @@
 	        if (inter) 
 		        rp= (line&1) ? &re : &ro;
 		else 
-	                rp= (line>height) ? &re : &ro; 
+	                rp= (line>=height) ? &re : &ro; 
 
 		bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr);
 		if (bpl<=bl)
@@ -803,16 +1074,62 @@
 	return 0;
 }
 
+static unsigned char lmaskt[8] = 
+{ 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80};
+static unsigned char rmaskt[8] = 
+{ 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
+
 static void clip_draw_rectangle(unsigned char *clipmap, int x, int y, int w, int h)
 {
-	int i, j;
+        unsigned char lmask, rmask, *p;
+        int W, l, r;
+	int i;
 	/* bitmap is fixed width, 128 bytes (1024 pixels represented) */
-	if (x < 0 || y < 0 || w < 0 || h < 0)	/* catch bad clips */
+        if (x<0)
+        {
+                w+=x;
+                x=0;
+        }
+        if (y<0)
+        {
+                h+=y;
+                y=0;
+        }
+	if (w < 0 || h < 0)	/* catch bad clips */
 		return;
 	/* out of range data should just fall through */
-	for (i = y; i < y + h && i < 625; i++)
-		for (j = x; j < x + w && j < 1024; j++)
-			clipmap[(i<<7)+(j>>3)] |= (1<<(j&7));
+        if (y+h>=625)
+                h=625-y;
+        if (x+w>=1024)
+                w=1024-x;
+
+        l=x>>3;
+        r=(x+w)>>3;
+        W=r-l-1;
+        lmask=lmaskt[x&7];
+        rmask=rmaskt[(x+w)&7];
+        p=clipmap+128*y+l;
+        
+        if (W>0) 
+        {
+                for (i=0; i<h; i++, p+=128) 
+                {
+                        *p|=lmask;
+                        memset(p+1, 0xff, W);
+                        p[W+1]|=rmask;
+                }
+        } else if (!W) {
+                for (i=0; i<h; i++, p+=128) 
+                {
+                        p[0]|=lmask;
+                        p[1]|=rmask;
+                }
+        } else {
+                for (i=0; i<h; i++, p+=128) 
+                        p[0]|=lmask&rmask;
+        }
+               
+
 }
 
 static void make_clip_tab(struct bttv *btv, struct video_clip *cr, int ncr)
@@ -904,73 +1221,6 @@
 	*(re++)=btv->bus_vbi_odd;
 }
 
-
-/*
- *	Set the registers for the size we have specified. Don't bother
- *	trying to understand this without the BT848 manual in front of
- *	you [AC]. 
- *
- *	PS: The manual is free for download in .pdf format from 
- *	www.brooktree.com - nicely done those folks.
- */
- 
-struct tvnorm 
-{
-        u32 Fsc;
-        u16 swidth, sheight; /* scaled standard width, height */
-	u16 totalwidth;
-	u8 adelay, bdelay, iform;
-	u32 scaledtwidth;
-	u16 hdelayx1, hactivex1;
-	u16 vdelay;
-	u8 vbipack;
-};
-
-static struct tvnorm tvnorms[] = {
-	/* PAL-BDGHI */
-        /* max pal/secam is actually 922, but 924 is divisible by 4 and 3! */
-	/* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */
-        { 35468950,
-          924, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
-          1135, 178, 924, 0x20, 255},
-/*
-        { 35468950, 
-          768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
-	  944, 178, 922, 0x20, 255},
-*/
-	/* NTSC */
-	{ 28636363,
-          768, 480,  910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0),
-          910, 128, 754, 0x1a, 144},
-/*
-	{ 28636363,
-          640, 480,  910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0),
-          780, 122, 754, 0x1a, 144},
-*/
-	/* SECAM - phase means nothing in SECAM, bdelay is useless */
-	{ 35468950, 
-          924, 576,1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1),
-	  1135, 178, 924, 0x20, 255},
-	/* PAL-M */
-	{ 28636363, 
-          640, 480, 910, 0x68, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
-	  780, 122, 754, 0x1a, 144},
-	/* PAL-N */
-	{ 35468950, 
-          768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
-	  944, 178, 922, 0x20, 255},
-	/* PAL-NC */
-	{ 35468950, 
-          768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
-	  944, 178, 922, 0x20, 255},
-	/* NTSC-Japan */
-	{ 28636363,
-          640, 480,  910, 0x68, 0x5d, (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
-	  780, 122, 754, 0x1a, 144},
-};
-#define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm))
-
-
 /* set geometry for even/odd frames 
    just if you are wondering:
    handling of even and odd frames will be separated, e.g. for grabbing
@@ -1031,10 +1281,9 @@
 	btwrite(tvn->adelay, BT848_ADELAY);
 	btwrite(tvn->bdelay, BT848_BDELAY);
 	btaor(tvn->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), BT848_IFORM);
-	btwrite(1, BT848_VBI_PACK_DEL);
-	btwrite(tvn->vbipack, BT848_VBI_PACK_SIZE);
-	
-	set_pll(btv);
+
+        btv->pll.pll_ofreq = tvn->Fsc;
+        set_pll(btv);
 
 	btwrite(fmt, BT848_COLOR_FMT);
 	hactive=width;
@@ -1065,7 +1314,6 @@
 			hdelay, vdelay, crop);
 	bt848_set_eogeo(btv, 1, vtc, hscale, vscale, hactive, vactive,
 			hdelay, vdelay, crop);
-	
 }
 
 
@@ -1105,35 +1353,34 @@
 {
 	int fixme = freq; /* XXX */
 	
-	if (btv->radio) 
-	{
-		if (btv->have_tuner)
+        /* mute */
+        if (btv->have_msp3400)
+                i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400,
+                                   MSP_SWITCH_MUTE,0);
+
+        /* switch channel */
+        if (btv->have_tuner) {
+                if (btv->radio) {
 			i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER,
 					   TUNER_SET_RADIOFREQ,&fixme);
-	
-		if (btv->have_msp3400) {
-			i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400,
-					   MSP_SET_RADIO,0);
-			i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400,
-					   MSP_NEWCHANNEL,0);
-		}
-	}
-	else
-	{
-		if (btv->have_tuner)
+                } else {
 			i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER,
 					   TUNER_SET_TVFREQ,&fixme);
+                }
+        }
 
-		if (btv->have_msp3400) {
+        if (btv->have_msp3400) {
+                if (btv->radio) {
+			i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400,
+					   MSP_SET_RADIO,0);
+                } else {
 			i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400,
 					   MSP_SET_TVNORM,&(btv->win.norm));
 			i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400,
 					   MSP_NEWCHANNEL,0);
-		}
-	}
-
+                }
+        }
 }
-      
 
 /*
  *	Grab into virtual memory.
@@ -1172,12 +1419,12 @@
 */
 	if (mp->format >= PALETTEFMT_MAX)
 		return -EINVAL;
- 	if (mp->height*mp->width*fmtbppx2[palette2fmt[mp->format]&0x0f]/2
-		> BTTV_MAX_FBUF)
+	if (mp->height*mp->width*fmtbppx2[palette2fmt[mp->format]&0x0f]/2
+	    > BTTV_MAX_FBUF)
 		return -EINVAL;
-	if (!palette2fmt[mp->format])
+	if(-1 == palette2fmt[mp->format])
 		return -EINVAL;
- 
+
 	/*
 	 *	FIXME: Check the format of the grab here. This is probably
 	 *	also less restrictive than the normal overlay grabs. Stuff
@@ -1193,7 +1440,7 @@
                 return -EAGAIN;*/
 	ro=btv->grisc+(((btv->grabcount++)&1) ? 4096 :0);
 	re=ro+2048;
-	make_vrisctab(btv, ro, re, vbuf, mp->width, mp->height, palette2fmt[mp->format]);
+        make_vrisctab(btv, ro, re, vbuf, mp->width, mp->height, mp->format);
 	/* bt848_set_risc_jmps(btv); */
         btv->frame_stat[mp->frame] = GBUFFER_GRABBING;
         if (btv->grabbing) {
@@ -1211,8 +1458,13 @@
 		btv->gre=virt_to_bus(re);
                 btv->grf=mp->frame;
         }
-	if (!(btv->grabbing++)) 
+	if (!(btv->grabbing++)) {
+		if(mp->format>=VIDEO_PALETTE_COMPONENT) {
+			btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI);
+			btor(BT848_VSCALE_COMB, BT848_O_VSCALE_HI);
+		}
 		btv->risc_jmp[12]=BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ;
+        }
 	btor(3, BT848_CAP_CTL);
 	btor(3, BT848_GPIO_DMA_CTL);
 	/* interruptible_sleep_on(&btv->capq); */
@@ -1228,7 +1480,7 @@
 {
 	struct bttv *btv= (struct bttv *)v;
 	int q,todo;
-
+	/* BROKEN: RETURNS VBI WHEN IT SHOULD RETURN GRABBED VIDEO FRAME */
 	todo=count;
 	while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) 
 	{
@@ -1276,36 +1528,27 @@
 	struct bttv *btv = (struct bttv *)dev;
 	int users, i;
 
-	switch (flags)
-	{
-		case 0:
-			if (btv->user)
-				return -EBUSY;
-			btv->user++;
-			audio(btv, AUDIO_UNMUTE);
-			for (i=users=0; i<bttv_num; i++)
-				users+=bttvs[i].user;
-			if (users==1)
-				find_vga();
-			btv->fbuffer=NULL;
-			if (!btv->fbuffer)
-				btv->fbuffer=(unsigned char *) rvmalloc(2*BTTV_MAX_FBUF);
-			if (!btv->fbuffer)
-                        { 
-				btv->user--;
-				return -EINVAL;
-			}
-                        btv->grabbing = 0;
-                        btv->grab = 0;
-                        btv->lastgrab = 0;
-                        for (i = 0; i < MAX_GBUFFERS; i++)
-				btv->frame_stat[i] = GBUFFER_UNUSED;
-                        break;
-		case 1:
-			break;
-	}
-	MOD_INC_USE_COUNT;
-	return 0;   
+        if (btv->user)
+                return -EBUSY;
+        audio(btv, AUDIO_UNMUTE);
+        for (i=users=0; i<bttv_num; i++)
+                users+=bttvs[i].user;
+        if (users==1)
+                find_vga();
+        btv->fbuffer=NULL;
+        if (!btv->fbuffer)
+                btv->fbuffer=(unsigned char *) rvmalloc(2*BTTV_MAX_FBUF);
+        if (!btv->fbuffer)
+                return -EINVAL;
+        btv->grabbing = 0;
+        btv->grab = 0;
+        btv->lastgrab = 0;
+        for (i = 0; i < MAX_GBUFFERS; i++)
+                btv->frame_stat[i] = GBUFFER_UNUSED;
+
+        btv->user++;
+        MOD_INC_USE_COUNT;
+        return 0;   
 }
 
 static void bttv_close(struct video_device *dev)
@@ -1394,8 +1637,8 @@
 				VID_TYPE_SCALES;
 			b.channels = tvcards[btv->type].video_inputs;
 			b.audios = tvcards[btv->type].audio_inputs;
-			b.maxwidth = 768;
-			b.maxheight = 576;
+			b.maxwidth = tvnorms[btv->win.norm].swidth;
+			b.maxheight = tvnorms[btv->win.norm].sheight;
 			b.minwidth = 32;
 			b.minheight = 32;
 			if(copy_to_user(arg,&b,sizeof(b)))
@@ -1421,7 +1664,7 @@
                                 v.tuners=1;
                         } 
                         else if(v.channel==tvcards[btv->type].svhs) 
-                                strcpy(v.name,"SVHS");
+                                strcpy(v.name,"S-Video");
                         else
                                 sprintf(v.name,"Composite%d",v.channel);
 
@@ -1438,14 +1681,14 @@
 			if(copy_from_user(&v, arg,sizeof(v)))
 				return -EFAULT;
                         
-                        if (v.channel>=tvcards[btv->type].video_inputs)
+                        if (v.channel>tvcards[btv->type].video_inputs)
                                 return -EINVAL;
 			bt848_muxsel(btv, v.channel);
 			if(v.norm!=VIDEO_MODE_PAL&&v.norm!=VIDEO_MODE_NTSC
 			   &&v.norm!=VIDEO_MODE_SECAM)
 				return -EOPNOTSUPP;
 			btv->win.norm = v.norm;
-			make_vbitab(btv);
+                        make_vbitab(btv);
 			bt848_set_winsize(btv);
 			btv->channel=v.channel;
 			return 0;
@@ -1461,6 +1704,11 @@
 			v.rangelow=0;
 			v.rangehigh=0xFFFFFFFF;
 			v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
+			if (btv->audio_chip == TDA9840) {
+				v.flags |= VIDEO_AUDIO_VOLUME;
+				v.mode = VIDEO_SOUND_MONO|VIDEO_SOUND_STEREO;
+				v.mode |= VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2;
+			}
 			if (btv->audio_chip == TDA9850) {
 				unsigned char ALR1;
 				ALR1 = I2CRead(&(btv->i2c), I2C_TDA9850|1);
@@ -1479,7 +1727,6 @@
 			struct video_tuner v;
 			if(copy_from_user(&v, arg, sizeof(v)))
 				return -EFAULT;
-
 			/* Only one channel has a tuner */
                         if(v.tuner!=tvcards[btv->type].tuner)
  				return -EINVAL;
@@ -1543,13 +1790,17 @@
 			if(copy_from_user(&vw,arg,sizeof(vw)))
 				return -EFAULT;
 				
-			if(vw.flags || vw.width < 16 || vw.height < 16) {
-				bt848_cap(btv,0);
+ 			if(vw.flags || vw.width < 16 || vw.height < 16) 
+                        {
+ 			        bt848_cap(btv,0);
 				return -EINVAL;
-			}
-			if (btv->win.bpp < 4) {	/* adjust and align writes */
+                        }		
+			if (btv->win.bpp < 4) 
+                        {	/* 32-bit align start and adjust width */
+				int i = vw.x;
 				vw.x = (vw.x + 3) & ~3;
-				vw.width = (vw.width - 3) & ~3;
+				i = vw.x - i;
+				vw.width -= i;
 			}
 			btv->win.x=vw.x;
 			btv->win.y=vw.y;
@@ -1591,7 +1842,7 @@
 			make_clip_tab(btv, vcp, vw.clipcount);
 			if (vw.clipcount != 0)
 				vfree(vcp);
-			if(on && btv->win.vidadr != 0)
+			if(on && btv->win.vidadr!=0)
 				bt848_cap(btv,1);
 			return 0;
 		}
@@ -1645,7 +1896,11 @@
 		case VIDIOCSFBUF:
 		{
 			struct video_buffer v;
+#if LINUX_VERSION_CODE >= 0x020100
 			if(!capable(CAP_SYS_ADMIN))
+#else
+			if(!suser())
+#endif
 				return -EPERM;
 			if(copy_from_user(&v, arg,sizeof(v)))
 				return -EFAULT;
@@ -1653,7 +1908,13 @@
 				v.depth!=24 && v.depth!=32 && v.width > 16 &&
 				v.height > 16 && v.bytesperline > 16)
 				return -EINVAL;
-	                btv->win.vidadr=(unsigned long)v.base;
+                        if (v.base)
+                        {
+                                if ((unsigned long)v.base&1)
+                                        btv->win.vidadr=(unsigned long)(PAGE_OFFSET|uvirt_to_bus((unsigned long)v.base));
+                                else
+                                        btv->win.vidadr=(unsigned long)v.base;
+                        }
 			btv->win.sheight=v.height;
 			btv->win.swidth=v.width;
 			btv->win.bpp=((v.depth+7)&0x38)/8;
@@ -1696,15 +1957,13 @@
 			strcpy(v.name,"TV");
 			if (btv->audio_chip == TDA9850) {
 				unsigned char ALR1;
-				v.flags|=VIDEO_AUDIO_VOLUME;
 				ALR1 = I2CRead(&(btv->i2c), I2C_TDA9850|1);
 				v.mode = VIDEO_SOUND_MONO;
 				v.mode |= (ALR1 & 32) ? VIDEO_SOUND_STEREO:0;
-				v.mode |= (ALR1 & 32) ? VIDEO_SOUND_LANG1:0;
-				v.volume = 32768;       /* fixme */
-				v.step = 4096;
+				v.mode |= (ALR1 & 64) ? VIDEO_SOUND_LANG1:0;
 			}
-			else if (btv->have_msp3400) {
+			if (btv->have_msp3400) 
+			{
                                 v.flags|=VIDEO_AUDIO_VOLUME |
                                 	VIDEO_AUDIO_BASS |
                                 	VIDEO_AUDIO_TREBLE;
@@ -1735,9 +1994,8 @@
 				audio(btv, AUDIO_MUTE);
  			/* One audio source per tuner */
  			/* if(v.audio!=0) */
- 			/* Nope... I have three on my ADSTech TV card. The*/
- 			/* ADSTech TV+FM prolly has 4 <rriggs@tesser.com> */
- 			if(v.audio<0 || v.audio >= tvcards[btv->type].audio_inputs)
+			/* ADSTech TV card has more than one */
+			if(v.audio<0 || v.audio >= tvcards[btv->type].audio_inputs)
 				return -EINVAL;
 			bt848_muxsel(btv,v.audio);
 			if(!(v.flags&VIDEO_AUDIO_MUTE))
@@ -1750,12 +2008,9 @@
 					con3 = 0x40;	/* stereo */
 				I2CWrite(&(btv->i2c), I2C_TDA9850,
 					TDA9850_CON3, con3, 1);
-				if (v.flags & VIDEO_AUDIO_VOLUME)
-					I2CWrite(&(btv->i2c), I2C_TDA9850,
-						TDA9850_CON4,
-						(v.volume>>12) & 15, 1);
 			}
-			else if (btv->have_msp3400) {
+			if (btv->have_msp3400) 
+			{
                                 i2c_control_device(&(btv->i2c),
                                                 I2C_DRIVERID_MSP3400,
                                                 MSP_SET_VOLUME,&(v.volume));
@@ -1776,13 +2031,18 @@
 	        case VIDIOCSYNC:
 			if(copy_from_user((void *)&i,arg,sizeof(int)))
 				return -EFAULT;
-			if (i>1 || i<0)
-				return -EINVAL;
+/*                        if(i>1 || i<0)
+                                return -EINVAL;
+*/
                         switch (btv->frame_stat[i]) {
                         case GBUFFER_UNUSED:
                                 return -EINVAL;
                         case GBUFFER_GRABBING:
- 			        interruptible_sleep_on(&btv->capq);
+                        	while(btv->frame_stat[i]==GBUFFER_GRABBING) {
+ 			        	interruptible_sleep_on(&btv->capq);
+ 			        	if(signal_pending(current))
+ 			        		return -EINTR;
+ 			        }
                                 /* fall */
                         case GBUFFER_DONE:
                                 btv->frame_stat[i] = GBUFFER_UNUSED;
@@ -1791,7 +2051,11 @@
                         return 0;
 
 		case BTTV_WRITEE:
+#if LINUX_VERSION_CODE >= 0x020100
 			if(!capable(CAP_SYS_ADMIN))
+#else
+			if(!suser())
+#endif
 				return -EPERM;
 			if(copy_from_user((void *) eedata, (void *) arg, 256))
 				return -EFAULT;
@@ -1799,32 +2063,39 @@
 			return 0;
 
 		case BTTV_READEE:
+#if LINUX_VERSION_CODE >= 0x020100
 			if(!capable(CAP_SYS_ADMIN))
+#else
+			if(!suser())
+#endif
 				return -EPERM;
 			readee(&(btv->i2c), eedata);
 			if(copy_to_user((void *) arg, (void *) eedata, 256))
 				return -EFAULT;
 			break;
 
-		case BTTV_FIELDNR:
+                case BTTV_FIELDNR: 
 			if(copy_to_user((void *) arg, (void *) &btv->last_field, 
                                         sizeof(btv->last_field)))
 				return -EFAULT;
                         break;
-
-		case BTTV_PLLSET:
-		{
-			struct bttv_pll_info p;
+      
+                case BTTV_PLLSET: {
+                        struct bttv_pll_info p;
+#if LINUX_VERSION_CODE >= 0x020100
 			if(!capable(CAP_SYS_ADMIN))
-				return -EPERM;
-			if(copy_from_user(&p , (void *) arg, sizeof(btv->pll)))
+#else
+			if(!suser())
+#endif
+                                return -EPERM;
+                        if(copy_from_user(&p , (void *) arg, sizeof(btv->pll)))
 				return -EFAULT;
-			btv->pll.pll_ifreq = p.pll_ifreq;
-			btv->pll.pll_ofreq = p.pll_ofreq;
-			btv->pll.pll_crystal = p.pll_crystal;
-			break;
-		}
+                        btv->pll.pll_ifreq = p.pll_ifreq;
+                        btv->pll.pll_ofreq = p.pll_ofreq;
+                        btv->pll.pll_crystal = p.pll_crystal;
 
+			break;
+                }						
 	        case VIDIOCMCAPTURE:
 		{
                         struct video_mmap vm;
@@ -1868,7 +2139,7 @@
 				return -EFAULT;
 			return 0;
 		}
-
+		
 	        case BTTV_BURST_ON:
 		{
 			tvnorms[0].scaledtwidth=1135-BURSTOFFSET-2;
@@ -1883,9 +2154,14 @@
 			return 0;
 		}
 
+		case BTTV_VERSION:
+		{
+			return BTTV_VERSION_CODE;
+		}
+                        
 		case BTTV_PICNR:
 		{
-			/* return picture */
+			/* return picture;*/
 			return  0;
 		}
                         
@@ -1944,7 +2220,9 @@
 	bttv_close,
 	bttv_read,
 	bttv_write,
+#if LINUX_VERSION_CODE >= 0x020100
 	NULL,			/* poll */
+#endif
 	bttv_ioctl,
 	bttv_mmap,
 	bttv_init_done,
@@ -1998,6 +2276,7 @@
 	return count;
 }
 
+#if LINUX_VERSION_CODE >= 0x020100
 static unsigned int vbi_poll(struct video_device *dev, struct file *file,
 	poll_table *wait)
 {
@@ -2011,6 +2290,7 @@
 
 	return mask;
 }
+#endif
 
 static int vbi_open(struct video_device *dev, int flags)
 {
@@ -2049,7 +2329,9 @@
 	vbi_close,
 	vbi_read,
 	bttv_write,
+#if LINUX_VERSION_CODE >= 0x020100
 	vbi_poll,
+#endif
 	vbi_ioctl,
 	NULL,	/* no mmap yet */
 	bttv_init_done,
@@ -2159,7 +2441,9 @@
 	radio_close,
 	radio_read,          /* just returns -EINVAL */
 	bttv_write,          /* just returns -EINVAL */
+#if LINUX_VERSION_CODE >= 0x020100
 	NULL,                /* no poll */
+#endif
 	radio_ioctl,
 	NULL,	             /* no mmap */
 	bttv_init_done,      /* just returns 0 */
@@ -2177,14 +2461,14 @@
 };
 
 static struct vidbases vbs[] = {
-	{ PCI_VENDOR_ID_ALLIANCE, PCI_DEVICE_ID_ALLIANCE_AT3D,
-		"Alliance AT3D", PCI_BASE_ADDRESS_0},
+        { PCI_VENDOR_ID_ALLIANCE, PCI_DEVICE_ID_ALLIANCE_AT3D,
+                "Alliance AT3D", PCI_BASE_ADDRESS_0},
 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_215CT222,
                 "ATI MACH64 CT", PCI_BASE_ADDRESS_0},
 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_210888GX,
 		"ATI MACH64 Winturbo", PCI_BASE_ADDRESS_0},
  	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_215GT,
- 		"ATI MACH64 GT", PCI_BASE_ADDRESS_0},
+                "ATI MACH64 GT", PCI_BASE_ADDRESS_0},
 	{ PCI_VENDOR_ID_CIRRUS, 0, "Cirrus Logic", PCI_BASE_ADDRESS_0},
 	{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA,
 		"DEC DC21030", PCI_BASE_ADDRESS_0},
@@ -2195,6 +2479,7 @@
 	{ PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP,
 		"Matrox Millennium II AGP", PCI_BASE_ADDRESS_0},
 	{ PCI_VENDOR_ID_MATROX, 0x051a, "Matrox Mystique", PCI_BASE_ADDRESS_1},
+	{ PCI_VENDOR_ID_MATROX, 0x0521, "Matrox G200", PCI_BASE_ADDRESS_0},
 	{ PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128, 
 		"Number Nine Imagine 128", PCI_BASE_ADDRESS_0},
 	{ PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128_2, 
@@ -2220,6 +2505,8 @@
 /* Scan for PCI display adapter
    if more than one card is present the last one is used for now */
 
+#if LINUX_VERSION_CODE >= 0x020100
+
 static int find_vga(void)
 {
 	unsigned short badr;
@@ -2299,6 +2586,99 @@
 	return found;
 }
 
+#else
+static int find_vga(void)
+{
+	unsigned int devfn, class, vendev;
+	unsigned short vendor, device, badr;
+	int found=0, bus=0, i, tga_type;
+	unsigned int vidadr=0;
+
+
+	for (devfn = 0; devfn < 0xff; devfn++) 
+	{
+		if (PCI_FUNC(devfn) != 0)
+			continue;
+		pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &vendev);
+		if (vendev == 0xffffffff || vendev == 0x00000000) 
+			continue;
+		pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID, &vendor);
+		pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &device);
+		pcibios_read_config_dword(bus, devfn, PCI_CLASS_REVISION, &class);
+		class = class >> 16;
+/*		if (class == PCI_CLASS_DISPLAY_VGA) {*/
+		if ((class>>8) == PCI_BASE_CLASS_DISPLAY ||
+			/* Number 9 GXE64Pro needs this */
+			class == PCI_CLASS_NOT_DEFINED_VGA) 
+		{
+			badr=0;
+			printk(KERN_INFO "bttv: PCI display adapter: ");
+			for (i=0; i<NR_CARDS; i++) 
+			{
+				if (vendor==vbs[i].vendor) 
+				{
+					if (vbs[i].device) 
+						if (vbs[i].device!=device)
+							continue;
+					printk("%s.\n", vbs[i].name);
+					badr=vbs[i].badr;
+					break;
+				}
+			}
+                        if (NR_CARDS == i)
+                            printk("UNKNOWN.\n");
+			if (!badr) 
+			{
+				printk(KERN_ERR "bttv: Unknown video memory base address.\n");
+				continue;
+			}
+			pcibios_read_config_dword(bus, devfn, badr, &vidadr);
+			if (vidadr & PCI_BASE_ADDRESS_SPACE_IO) 
+			{
+				printk(KERN_ERR "bttv: Memory seems to be I/O memory.\n");
+				printk(KERN_ERR "bttv: Check entry for your card type in bttv.c vidbases struct.\n");
+				continue;
+			}
+			vidadr &= PCI_BASE_ADDRESS_MEM_MASK;
+			if (!vidadr) 
+			{
+				printk(KERN_ERR "bttv: Memory @ 0, must be something wrong!\n");
+				continue;
+			}
+      
+			if (vendor==PCI_VENDOR_ID_DEC)
+				if (device==PCI_DEVICE_ID_DEC_TGA) 
+			{
+				tga_type = (readl((unsigned long)vidadr) >> 12) & 0x0f;
+				if (tga_type != 0 && tga_type != 1 && tga_type != 3) 
+				{
+					printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type);
+					found--;
+				}
+				vidadr+=dec_offsets[tga_type];
+			}
+
+			DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr));
+			DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", devfn));
+			found++;
+		}
+	}
+  
+	if (vidmem)
+	{
+                if (vidmem < 0x1000)
+                        vidadr=vidmem<<20;
+                else
+                        vidadr=vidmem;
+		printk(KERN_INFO "bttv: Video memory override: 0x%08x\n", vidadr);
+		found=1;
+	}
+	for (i=0; i<BTTV_MAX; i++)
+		bttvs[i].win.vidadr=vidadr;
+
+	return found;
+}
+#endif
 
 
 #define  TRITON_PCON	           0x50 
@@ -2307,6 +2687,9 @@
 #define  TRITON_WRITE_BURST	  (1<<2)
 #define  TRITON_PEER_CONCURRENCY  (1<<3)
   
+
+#if LINUX_VERSION_CODE >= 0x020100
+
 static void handle_chipset(void)
 {
 	struct pci_dev *dev = NULL;
@@ -2370,6 +2753,78 @@
 #endif
 	}
 }
+#else
+static void handle_chipset(void)
+{
+	int index;
+  
+	for (index = 0; index < 8; index++)
+	{
+		unsigned char bus, devfn;
+		unsigned char b;
+    
+		/* Beware the SiS 85C496 my friend - rev 49 don't work with a bttv */
+		
+		if (!pcibios_find_device(PCI_VENDOR_ID_SI, 
+					 PCI_DEVICE_ID_SI_496, 
+					 index, &bus, &devfn))
+		{
+			printk(KERN_WARNING "BT848 and SIS 85C496 chipset don't always work together.\n");
+		}			
+
+		if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, 
+					 PCI_DEVICE_ID_INTEL_82441,
+					 index, &bus, &devfn)) 
+		{
+			pcibios_read_config_byte(bus, devfn, 0x53, &b);
+			DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, "));
+			DEBUG(printk("bufcon=0x%02x\n",b));
+		}
+
+		if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437,
+			    index, &bus, &devfn)) 
+		{
+			printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n");
+			triton1=BT848_INT_ETBF;
+			
+#if 0			
+			/* The ETBF bit SHOULD make all this unnecessary */
+			/* 430FX (Triton I) freezes with bus concurrency on -> switch it off */
+			{   
+			        unsigned char bo;
+
+				pcibios_read_config_byte(bus, devfn, TRITON_PCON, &b);
+				bo=b;
+				DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b));
+
+				if(!(b & TRITON_BUS_CONCURRENCY)) 
+				{
+					printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n");
+					b |= TRITON_BUS_CONCURRENCY;
+				}
+
+				if(b & TRITON_PEER_CONCURRENCY) 
+				{
+					printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n");
+					b &= ~TRITON_PEER_CONCURRENCY;
+				}
+				if(!(b & TRITON_STREAMING)) 
+				{
+					printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n");
+					b |=  TRITON_STREAMING;
+				}
+
+				if (b!=bo) 
+				{
+					pcibios_write_config_byte(bus, devfn, TRITON_PCON, b); 
+					printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b);
+				}
+			}
+#endif
+		}
+	}
+}
+#endif
 
 static void init_tda8425(struct i2c_bus *bus) 
 {
@@ -2377,9 +2832,13 @@
         I2CWrite(bus, I2C_TDA8425, TDA8425_VR, 0xFC, 1); /* volume right 0dB */
         I2CWrite(bus, I2C_TDA8425, TDA8425_BA, 0xF6, 1); /* bass 0dB         */
         I2CWrite(bus, I2C_TDA8425, TDA8425_TR, 0xF6, 1); /* treble 0dB       */
-	I2CWrite(bus, I2C_TDA8425, TDA8425_S1, 0xCE, 1); /* mute off         */
+        I2CWrite(bus, I2C_TDA8425, TDA8425_S1, 0xCE, 1); /* mute off         */
 }
 
+static void init_tda9840(struct i2c_bus *bus)
+{
+	I2CWrite(bus, I2C_TDA9840, TDA9840_SW, 0x2A, 1);	/* Sound mode switching */
+}
 
 static void init_tda9850(struct i2c_bus *bus)
 {
@@ -2400,7 +2859,6 @@
 {
         struct bttv *btv = &bttvs[i];
 
-	int tunertype;
 	btwrite(0, BT848_GPIO_OUT_EN);
 	DEBUG(printk(KERN_DEBUG "bttv%d: GPIO: 0x%08x\n", i, btread(BT848_GPIO_DATA)));
 
@@ -2413,99 +2871,115 @@
 	   */
 	if (btv->type == BTTV_UNKNOWN) 
 	{
-	        btv->type=BTTV_MIRO;
-    
-		if (I2CRead(&(btv->i2c), I2C_HAUPEE)>=0) {
+		if (I2CRead(&(btv->i2c), I2C_HAUPEE)>=0)
+		{
 			if(btv->id>849)
-			{
 				btv->type=BTTV_HAUPPAUGE878;
-				btv->pll.pll_ifreq = 28636363;
-				btv->pll.pll_crystal = BT848_IFORM_XT0;
-			}
 			else
-				btv->type=BTTV_HAUPPAUGE;
-		}
-		else
-		        if (I2CRead(&(btv->i2c), I2C_STBEE)>=0)
-			        btv->type=BTTV_STB;
+			        btv->type=BTTV_HAUPPAUGE;
 
-                if (btv->type == BTTV_MIRO || btv->type == BTTV_MIROPRO) {
-                        /* auto detect tuner for MIRO cards */
-                        btv->tuner_type=((btread(BT848_GPIO_DATA)>>10)-1)&7;
-                }
+		} else if (I2CRead(&(btv->i2c), I2C_STBEE)>=0) {
+			btv->type=BTTV_STB;
+
+		} else {
+			if (I2CRead(&(btv->i2c), 0x80)>=0) /* check for msp34xx */
+				btv->type = BTTV_MIROPRO;
+			else
+	 			btv->type=BTTV_MIRO;
+		}
 	}
 
+        /* board specific initialisations */
+        if (btv->type == BTTV_MIRO || btv->type == BTTV_MIROPRO) {
+                /* auto detect tuner for MIRO cards */
+                btv->tuner_type=((btread(BT848_GPIO_DATA)>>10)-1)&7;
+        }
+        if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) {
+                hauppauge_msp_reset(btv);
+                hauppauge_eeprom(&(btv->i2c));
+                if (btv->type == BTTV_HAUPPAUGE878) {
+			/* all bt878 hauppauge boards use this ... */
+			btv->pll.pll_ifreq=28636363;
+			btv->pll.pll_crystal=BT848_IFORM_XT0;
+		}
+        }
+        if(btv->type==BTTV_AVERMEDIA98)
+        {
+          btv->pll.pll_ifreq=28636363;
+          btv->pll.pll_crystal=BT848_IFORM_XT0;
+        }
+          
+	if (btv->have_tuner && btv->tuner_type != -1) 
+ 		i2c_control_device(&(btv->i2c), 
+                                   I2C_DRIVERID_TUNER,
+                                   TUNER_SET_TYPE,&btv->tuner_type);
+
+        
+        if (I2CRead(&(btv->i2c), I2C_TDA9840) >=0)
+        {
+        	btv->audio_chip = TDA9840;
+        	printk(KERN_INFO "bttv%d: audio chip: TDA9840\n", btv->nr);
+        }
+        
         if (I2CRead(&(btv->i2c), I2C_TDA9850) >=0)
         {
-        	btv->audio_chip = TDA9850;
-        	printk(KERN_INFO "bttv%d: audio chip: TDA9850\n", i);
+                btv->audio_chip = TDA9850;
+                printk(KERN_INFO "bttv%d: audio chip: TDA9850\n",btv->nr);
         }
 
         if (I2CRead(&(btv->i2c), I2C_TDA8425) >=0)
         {
-		btv->audio_chip = TDA8425;
-		printk("bttv%d: audio chip: TDA8425\n", i);
+                btv->audio_chip = TDA8425;
+                printk(KERN_INFO "bttv%d: audio chip: TDA8425\n",btv->nr);
         }
         
         switch(btv->audio_chip)
         {
                 case TDA9850:
                         init_tda9850(&(btv->i2c));
-                        break;
-		case TDA8425:
+                        break; 
+                case TDA8425:
                         init_tda8425(&(btv->i2c));
                         break;
         }
+        
+	printk(KERN_INFO "bttv%d: model: ",btv->nr);
 
-	/* How do I detect the tuner type for other cards but Miro ??? */
-	printk(KERN_INFO "bttv%d: model: ", btv->nr);
 	sprintf(btv->video_dev.name,"BT%d",btv->id);
 	switch (btv->type) 
 	{
-		case BTTV_MIRO:
-		case BTTV_MIROPRO:
-			printk("MIRO\n");
-			if (btv->have_tuner) 
-			{
-				tunertype=((btread(BT848_GPIO_DATA)>>10)-1)&7;
-				i2c_control_device(&(btv->i2c),
-						   I2C_DRIVERID_TUNER,
-						   TUNER_SET_TYPE,&tunertype);
-			}
-			strcat(btv->video_dev.name,"(Miro)");
+                case BTTV_MIRO:
+                case BTTV_MIROPRO:
+			strcat(btv->video_dev.name,
+			       (btv->type == BTTV_MIRO) ? "(Miro)" : "(Miro pro)");
 			break;
 		case BTTV_HAUPPAUGE:
+			strcat(btv->video_dev.name,"(Hauppauge old)");
+			break;
 		case BTTV_HAUPPAUGE878:
-			printk("HAUPPAUGE\n");
-			strcat(btv->video_dev.name,"(Hauppauge)");
+			strcat(btv->video_dev.name,"(Hauppauge new)");
 			break;
 		case BTTV_STB: 
-			printk("STB\n");
 			strcat(btv->video_dev.name,"(STB)");
 			break;
 		case BTTV_INTEL: 
-			printk("Intel\n");
 			strcat(btv->video_dev.name,"(Intel)");
 			break;
 		case BTTV_DIAMOND: 
-			printk("Diamond\n");
 			strcat(btv->video_dev.name,"(Diamond)");
 			break;
 		case BTTV_AVERMEDIA: 
-			printk("AVerMedia\n");
 			strcat(btv->video_dev.name,"(AVerMedia)");
 			break;
 		case BTTV_MATRIX_VISION: 
-			printk("MATRIX-Vision\n");
 			strcat(btv->video_dev.name,"(MATRIX-Vision)");
 			break;
-		case BTTV_ADSTECH_TV:
-			printk("ADSTech Channel Surfer TV\n");
-			strcat(btv->video_dev.name,"(ADSTech Channel Surfer TV)");
-			btv->tuner_type=8;
+		case BTTV_AVERMEDIA98: 
+			strcat(btv->video_dev.name,"(AVerMedia TVCapture 98)");
 			break;
 	}
-	audio(btv, AUDIO_INTERN);
+	printk("%s\n",btv->video_dev.name);
+	audio(btv, AUDIO_MUTE);
 }
 
 
@@ -2554,7 +3028,7 @@
 	btv->risc_jmp[12]=BT848_RISC_JUMP;
 	btv->risc_jmp[13]=virt_to_bus(btv->risc_jmp);
 
-	/* enable capturing and DMA */
+	/* enable cpaturing and DMA */
 	btaor(flags, ~0x0f, BT848_CAP_CTL);
 	if (flags&0x0f)
 		bt848_dma(btv, 3);
@@ -2562,7 +3036,6 @@
 		bt848_dma(btv, 0);
 }
 
-
 static int init_bt848(int i)
 {
         struct bttv *btv = &bttvs[i];
@@ -2573,19 +3046,7 @@
 	btwrite(0, BT848_SRESET);
 	DEBUG(printk(KERN_DEBUG "bttv%d: bt848_mem: 0x%08x\n",i,(unsigned int) btv->bt848_mem));
 
-#ifdef RESET_MSP_HAUPPAUGE
-        /* Reset the MSP on some Hauppauge cards */
-        /* Thanks to Kyösti Mälkki (kmalkki@cc.hut.fi)! */
-        /* Can this hurt cards without one? What about Miros with MSP? */
-        btaor(32, ~32, BT848_GPIO_OUT_EN);
-        btaor(0, ~32, BT848_GPIO_DATA);
-        udelay(2500);
-        btaor(32, ~32, BT848_GPIO_DATA);
-        btaor(0, ~32, BT848_GPIO_OUT_EN);
-#endif
-
 	/* default setup for max. PAL size in a 1024xXXX hicolor framebuffer */
-
 	btv->win.norm=0; /* change this to 1 for NTSC, 2 for SECAM */
 	btv->win.interlace=1;
 	btv->win.x=0;
@@ -2615,12 +3076,12 @@
 	btv->grab=0;
 	btv->lastgrab=0;
         btv->field=btv->last_field=0;
-        /* cevans - prevents panic if initialization bails due to memory
-         * alloc failures!
-         */
-        btv->video_dev.minor = -1;
-        btv->vbi_dev.minor = -1;
-        btv->radio_dev.minor = -1;
+	/* cevans - prevents panic if initialization bails due to memory
+	 * alloc failures!
+	 */
+	btv->video_dev.minor = -1;
+	btv->vbi_dev.minor = -1;
+	btv->radio_dev.minor = -1;
 
 	/* i2c */
 	memcpy(&(btv->i2c),&bttv_i2c_bus_template,sizeof(struct i2c_bus));
@@ -2648,6 +3109,7 @@
 
 	memset(btv->vbibuf, 0, VBIBUF_SIZE); /* We don't want to return random
 	                                        memory to the user */
+
 	btv->fbuffer=NULL;
 
 	bt848_muxsel(btv, 1);
@@ -2656,7 +3118,7 @@
 /*	btwrite(0, BT848_TDEC); */
 	btwrite(0x10, BT848_COLOR_CTL);
 	btwrite(0x00, BT848_CAP_CTL);
-	btwrite(0xfc, BT848_GPIO_DMA_CTL);
+	btwrite(0xac, BT848_GPIO_DMA_CTL);
 
         /* select direct input */
 	btwrite(0x00, BT848_GPIO_REG_INP);
@@ -2706,6 +3168,7 @@
 	memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template));
 	memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template));
 	memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template));
+
 	idcard(i);
 
 	if(video_register_device(&btv->video_dev,VFL_TYPE_GRABBER)<0)
@@ -2765,7 +3228,7 @@
 		if (astat&BT848_INT_VSYNC) 
 		{
 			IDEBUG(printk ("bttv%d: IRQ_VSYNC\n", btv->nr));
-			btv->field++;
+                        btv->field++;
 		}
 		if (astat&BT848_INT_SCERR) {
 			IDEBUG(printk ("bttv%d: IRQ_SCERR\n", btv->nr));
@@ -2791,8 +3254,9 @@
 			/* captured full frame */
 			if (stat&(2<<28)) 
 			{
+				wake_up_interruptible(&btv->capq);
                                 btv->last_field=btv->field;
-                                btv->grab++;
+			        btv->grab++;
                                 btv->frame_stat[btv->grf] = GBUFFER_DONE;
 			        if ((--btv->grabbing))
 				{
@@ -2809,7 +3273,9 @@
 						      btv->gfmt);
 				} else {
 					bt848_set_risc_jmps(btv);
-					bt848_set_geo(btv, btv->win.width, 
+					btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI);
+					btand(~BT848_VSCALE_COMB, BT848_O_VSCALE_HI);
+                                        bt848_set_geo(btv, btv->win.width, 
 						      btv->win.height,
 						      btv->win.color_fmt);
 				}
@@ -2855,12 +3321,10 @@
 		}
 		if (astat&BT848_INT_HLOCK) 
 		{
-#if 0
 			if ((dstat&BT848_DSTATUS_HLOC) || (btv->radio))
 				audio(btv, AUDIO_ON);
 			else
 				audio(btv, AUDIO_OFF);
-#endif
 		}
     
 		if (astat&BT848_INT_I2CDONE) 
@@ -2869,7 +3333,8 @@
     
 		count++;
 		if (count > 10)
-			printk (KERN_WARNING "bttv%d: irq loop %d\n", btv->nr, count);
+			printk (KERN_WARNING "bttv%d: irq loop %d\n",
+                                btv->nr,count);
 		if (count > 20) 
 		{
 			btwrite(0, BT848_INT_MASK);
@@ -2885,17 +3350,16 @@
  *	Scan for a Bt848 card, request the irq and map the io memory 
  */
 
+#if LINUX_VERSION_CODE >= 0x020100
 int configure_bt848(struct pci_dev *dev, int bttv_num)
 {
 	int result;
-	unsigned char bus, devfn, command;
+	unsigned char command;
 	struct bttv *btv;
 
         btv=&bttvs[bttv_num];
         btv->dev=dev;
         btv->nr = bttv_num;
-        btv->bus=bus=dev->bus->number;
-        btv->devfn=devfn=dev->devfn;
         btv->bt848_mem=NULL;
         btv->vbibuf=NULL;
         btv->risc_jmp=NULL;
@@ -2931,15 +3395,14 @@
         pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision);
         printk(KERN_INFO "bttv%d: Brooktree Bt%d (rev %d) ",
                bttv_num,btv->id, btv->revision);
-        printk("bus: %d, devfn: %d, ",
-               btv->bus, btv->devfn);
+        printk("bus: %d, devfn: %d, ",dev->bus->number, dev->devfn);
         printk("irq: %d, ",btv->irq);
         printk("memory: 0x%08x.\n", btv->bt848_adr);
-        
-	btv->pll.pll_ifreq=0;
-	btv->pll.pll_ofreq=0;
-	btv->pll.pll_crystal=0;
-	btv->pll.pll_current=0;
+
+        btv->pll.pll_crystal = 0;
+        btv->pll.pll_ifreq   = 0;
+        btv->pll.pll_ofreq   = 0;
+        btv->pll.pll_current = 0;
         if (!(btv->id==848 && btv->revision==0x11)) {
                 switch (pll[btv->nr]) {
                 case 0:
@@ -2957,10 +3420,10 @@
                         break;
                 }
         }
-
+        
         btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000);
         
-	/* clear interrupt mask */
+        /* clear interrupt mask */
 	btwrite(0, BT848_INT_MASK);
 
         result = request_irq(btv->irq, bttv_irq,
@@ -3023,7 +3486,170 @@
 		printk(KERN_INFO "bttv: %d Bt8xx card(s) found.\n", bttv_num);
 	return bttv_num;
 }
- 
+#else
+static int find_bt848(void)
+{
+	short pci_index;    
+	unsigned char command, latency;
+	int result;
+	unsigned char bus, devfn;
+	struct bttv *btv;
+
+	bttv_num=0;
+
+	if (!pcibios_present()) 
+	{
+		DEBUG(printk(KERN_DEBUG "bttv%d: PCI-BIOS not present or not accessable!\n",bttv_num));
+		return 0;
+	}
+
+	for (pci_index = 0;
+                !pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849,
+                                     pci_index, &bus, &devfn)
+             ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848,
+                                    pci_index, &bus, &devfn)
+             ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878,
+                                    pci_index, &bus, &devfn)
+             ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879,
+                                    pci_index, &bus, &devfn);
+             ++pci_index) 
+	{
+		btv=&bttvs[bttv_num];
+                btv->nr = bttv_num;
+		btv->bus=bus;
+		btv->devfn=devfn;
+		btv->bt848_mem=NULL;
+		btv->vbibuf=NULL;
+		btv->risc_jmp=NULL;
+		btv->vbi_odd=NULL;
+		btv->vbi_even=NULL;
+		btv->vbiq=NULL;
+		btv->capq=NULL;
+		btv->capqo=NULL;
+		btv->capqe=NULL;
+
+		btv->vbip=VBIBUF_SIZE;
+
+		pcibios_read_config_word(btv->bus, btv->devfn, PCI_DEVICE_ID,
+					 &btv->id);
+		pcibios_read_config_byte(btv->bus, btv->devfn,
+			PCI_INTERRUPT_LINE, &btv->irq);
+		pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
+			&btv->bt848_adr);
+                if (btv->id >= 878)
+                        btv->i2c_command = 0x83;                   
+                else
+                        btv->i2c_command=
+                                (I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA);
+
+		if (remap[bttv_num])
+		{
+                        if (remap[bttv_num] < 0x1000)
+                                remap[bttv_num]<<=20;
+			remap[bttv_num]&=PCI_BASE_ADDRESS_MEM_MASK;
+			printk(KERN_INFO "bttv%d: remapping to : 0x%08x.\n",
+                               bttv_num,remap[bttv_num]);
+			remap[bttv_num]|=btv->bt848_adr&(~PCI_BASE_ADDRESS_MEM_MASK);
+			pcibios_write_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
+				 remap[bttv_num]);
+			pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
+				&btv->bt848_adr);
+		}					
+                
+		btv->bt848_adr&=PCI_BASE_ADDRESS_MEM_MASK;
+		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_CLASS_REVISION,
+			     &btv->revision);
+		printk(KERN_INFO "bttv%d: Brooktree Bt%d (rev %d) ",
+                       bttv_num,btv->id, btv->revision);
+                printk("bus: %d, devfn: %d, ",
+                       btv->bus, btv->devfn);
+		printk("irq: %d, ",btv->irq);
+		printk("memory: 0x%08x.\n", btv->bt848_adr);
+    
+                btv->pll.pll_crystal = 0;
+                btv->pll.pll_ifreq   = 0;
+                btv->pll.pll_ofreq   = 0;
+                btv->pll.pll_current = 0;
+                if (!(btv->id==848 && btv->revision==0x11)) {
+                        switch (pll[btv->nr]) {
+                        case 0:
+                                /* off */
+                                break;
+                        case 1:
+                                /* 28 MHz crystal installed */
+                                btv->pll.pll_ifreq=28636363;
+                                btv->pll.pll_crystal=BT848_IFORM_XT0;
+                                break;
+                        case 2:
+                                /* 35 MHz crystal installed */
+                                btv->pll.pll_ifreq=35468950;
+                                btv->pll.pll_crystal=BT848_IFORM_XT1;
+                                break;
+                        }
+                }
+                
+		btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000);
+                
+		result = request_irq(btv->irq, bttv_irq,
+			SA_SHIRQ | SA_INTERRUPT,"bttv",(void *)btv);
+		if (result==-EINVAL) 
+		{
+			printk(KERN_ERR "bttv%d: Bad irq number or handler\n",
+                               bttv_num);
+			return -EINVAL;
+		}
+		if (result==-EBUSY)
+		{
+			printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->irq);
+			return result;
+		}
+		if (result < 0) 
+			return result;
+
+		/* Enable bus-mastering */
+		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
+		command|=PCI_COMMAND_MASTER;
+		pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command);
+		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
+		if (!(command&PCI_COMMAND_MASTER)) 
+		{
+			printk(KERN_ERR "bttv%d: PCI bus-mastering could not be enabled\n",bttv_num);
+			return -1;
+		}
+		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER,
+			&latency);
+		if (!latency) 
+		{
+			latency=32;
+			pcibios_write_config_byte(btv->bus, btv->devfn,
+						  PCI_LATENCY_TIMER, latency);
+		}
+		DEBUG(printk(KERN_DEBUG "bttv%d: latency: %02x\n",
+                             bttv_num, latency));
+
+                btv->triton1=triton1 ? BT848_INT_ETBF : 0;
+		if (triton1 && btv->id >= 878) 
+		{
+			triton1 = 0;
+			printk("bttv: Enabling 430FX compatibilty for bt878\n");
+			pcibios_read_config_byte(btv->bus, btv->devfn, BT878_DEVCTRL, &command);
+			command|=BT878_EN_TBFX;
+			pcibios_write_config_byte(btv->bus, btv->devfn, BT878_DEVCTRL, command);
+			pcibios_read_config_byte(btv->bus, btv->devfn, BT878_DEVCTRL, &command);
+			if (!(command&BT878_EN_TBFX)) 
+			{
+		   		printk("bttv: 430FX compatibility could not be enabled\n");
+	  	    		return -1;
+ 	 		}
+		}
+
+		bttv_num++;
+	}
+	if(bttv_num)
+		printk(KERN_INFO "bttv: %d Bt8xx card(s) found.\n", bttv_num);
+	return bttv_num;
+}
+#endif
 
 static void release_bttv(void)
 {
@@ -3048,10 +3674,17 @@
 		i2c_unregister_bus((&btv->i2c));
 
 		/* disable PCI bus-mastering */
+#if LINUX_VERSION_CODE >= 0x020100
 		pci_read_config_byte(btv->dev, PCI_COMMAND, &command);
 		/* Should this be &=~ ?? */
- 		command|=PCI_COMMAND_MASTER;
+		command&=~PCI_COMMAND_MASTER;
 		pci_write_config_byte(btv->dev, PCI_COMMAND, command);
+#else
+		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
+		command&=~PCI_COMMAND_MASTER;
+		pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command);
+
+#endif
     
 		/* unmap and free memory */
 		if (btv->grisc)
@@ -3081,7 +3714,7 @@
 			video_unregister_device(&btv->video_dev);
 		if(btv->vbi_dev.minor!=-1)
 			video_unregister_device(&btv->vbi_dev);
-		if (radio && btv->radio_dev.minor != -1)
+		if (radio[btv->nr] && btv->radio_dev.minor != -1)
 			video_unregister_device(&btv->radio_dev);
 	}
 }
@@ -3109,6 +3742,7 @@
 			return -EIO;
 		} 
 	}
+
 	return 0;
 }
 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/bttv.h linux/drivers/char/bttv.h
--- v2.2.0-pre7/linux/drivers/char/bttv.h	Thu Nov 12 16:21:18 1998
+++ linux/drivers/char/bttv.h	Thu Jan 14 22:53:02 1999
@@ -21,7 +21,7 @@
 #ifndef _BTTV_H_
 #define _BTTV_H_
 
-#define TEST_VBI
+#define BTTV_VERSION_CODE 0x000520
 
 #include <linux/types.h>
 #include <linux/wait.h>
@@ -31,8 +31,14 @@
 #include "bt848.h"
 #include <linux/videodev.h>
 
+#ifndef O_NONCAP  
+#define O_NONCAP	O_TRUNC
+#endif
+
 #define MAX_GBUFFERS	2
 #define RISCMEM_LEN	(32744*2)
+#define VBI_MAXLINES    19
+#define VBIBUF_SIZE     (2048*VBI_MAXLINES*2)
 
 /* maximum needed buffer size for extended VBI frame mode capturing */
 #define BTTV_MAX_FBUF	0x190000
@@ -56,11 +62,20 @@
 };
 
 struct bttv_pll_info {
-	unsigned int pll_ifreq;		/* PLL input frequency    */
-	unsigned int pll_ofreq;		/* PLL output frequency   */
-	unsigned int pll_crystal;	/* Crystal used for input */
-	unsigned int pll_current;	/* Current programmed ofrq*/
+	unsigned int pll_ifreq;	   /* PLL input frequency 	 */
+	unsigned int pll_ofreq;	   /* PLL output frequency       */
+	unsigned int pll_crystal;  /* Crystal used for input     */
+	unsigned int pll_current;  /* Currently programmed ofreq */
+};
+
+/*  Per-open data for handling multiple opens on one device */
+struct device_open
+{
+	int	     isopen;
+	int	     noncapturing;
+	struct bttv  *dev;
 };
+#define MAX_OPENS 3
 
 struct bttv 
 {
@@ -70,6 +85,10 @@
 	struct video_picture picture;		/* Current picture params */
 	struct video_audio audio_dev;		/* Current audio params */
 
+	int user;
+	int capuser;
+	struct device_open open_data[MAX_OPENS];
+	
 	struct i2c_bus i2c;
 	int have_msp3400;
 	int have_tuner;
@@ -78,9 +97,12 @@
         
         unsigned int nr;
 	unsigned short id;
+#if LINUX_VERSION_CODE < 0x020100
 	unsigned char bus;          /* PCI bus the Bt848 is on */
 	unsigned char devfn;
+#else
 	struct pci_dev *dev;
+#endif
 	unsigned char irq;          /* IRQ used by Bt848 card */
 	unsigned char revision;
 	unsigned int bt848_adr;      /* bus address of IO mem returned by PCI BIOS */
@@ -92,7 +114,6 @@
 	struct bttv_window win;
 	int type;            /* card type  */
 	int audio;           /* audio mode */
-	int user;
 	int audio_chip;
 	int radio;
 
@@ -115,7 +136,7 @@
 	struct gbuffer *ogbuffers;
 	struct gbuffer *egbuffers;
 	u16 gwidth, gheight, gfmt;
-        u16 gwidth_next, gheight_next, gfmt_next;
+	u16 gwidth_next, gheight_next, gfmt_next;
 	u32 *grisc;
 	
 	unsigned long gro;
@@ -143,7 +164,6 @@
 	int i2c_command;
 	int triton1;
 };
-
 #endif
 
 /*The following should be done in more portable way. It depends on define
@@ -165,12 +185,11 @@
 
 #define BTTV_READEE		_IOW('v',  BASE_VIDIOCPRIVATE+0, char [256])
 #define BTTV_WRITEE		_IOR('v',  BASE_VIDIOCPRIVATE+1, char [256])
-#define BTTV_GRAB		_IOR('v' , BASE_VIDIOCPRIVATE+2, struct gbuf)
 #define BTTV_FIELDNR		_IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int)
 #define BTTV_PLLSET		_IOW('v' , BASE_VIDIOCPRIVATE+3, struct bttv_pll_info)
-#define BTTV_BURST_ON		_IOR('v' , BASE_VIDIOCPRIVATE+4, int)
-#define BTTV_BURST_OFF		_IOR('v' , BASE_VIDIOCPRIVATE+5, int)
-#define BTTV_NAGRAVERSION	_IOR('v' , BASE_VIDIOCPRIVATE+6, int)
+#define BTTV_BURST_ON      	_IOR('v' , BASE_VIDIOCPRIVATE+4, int)
+#define BTTV_BURST_OFF     	_IOR('v' , BASE_VIDIOCPRIVATE+5, int)
+#define BTTV_VERSION  	        _IOR('v' , BASE_VIDIOCPRIVATE+6, int)
 #define BTTV_PICNR		_IOR('v' , BASE_VIDIOCPRIVATE+7, int)
 
 
@@ -187,6 +206,7 @@
 #define BTTV_HAUPPAUGE878  0x0a
 #define BTTV_MIROPRO       0x0b
 #define BTTV_ADSTECH_TV    0x0c
+#define BTTV_AVERMEDIA98   0x0d
 
 #define AUDIO_TUNER        0x00
 #define AUDIO_RADIO        0x01
@@ -199,12 +219,19 @@
 
 #define TDA9850            0x01
 #define TDA8425            0x02
+#define TDA9840            0x03
 
 #define I2C_TSA5522        0xc2
+#define I2C_TDA9840	   0x84
 #define I2C_TDA9850        0xb6
 #define I2C_TDA8425        0x82
 #define I2C_HAUPEE         0xa0
 #define I2C_STBEE          0xae
+
+#define TDA9840_SW	   0x00
+#define TDA9840_LVADJ	   0x02
+#define TDA9840_STADJ	   0x03
+#define TDA9840_TEST	   0x04
 
 #define TDA9850_CON1       0x04
 #define TDA9850_CON2       0x05
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/hfmodem/main.c linux/drivers/char/hfmodem/main.c
--- v2.2.0-pre7/linux/drivers/char/hfmodem/main.c	Fri Oct 23 22:01:20 1998
+++ linux/drivers/char/hfmodem/main.c	Sun Jan 17 18:28:06 1999
@@ -601,7 +601,7 @@
 MODULE_PARM(serio, "i");
 MODULE_PARM_DESC(serio, "address of serial port to output PTT");
 MODULE_PARM(pario, "i");
-MODULE_PARM_DESC(pario, "address of parial port to output PTT");
+MODULE_PARM_DESC(pario, "address of parallel port to output PTT");
 MODULE_PARM(midiio, "i");
 MODULE_PARM_DESC(midiio, "address of midi (MPU401) port to output PTT");
 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/i2c.c linux/drivers/char/i2c.c
--- v2.2.0-pre7/linux/drivers/char/i2c.c	Fri Oct  9 13:27:08 1998
+++ linux/drivers/char/i2c.c	Thu Jan 14 22:53:02 1999
@@ -24,9 +24,11 @@
 static int verbose   = 0;
 static int i2c_debug = 0;
 
+#if LINUX_VERSION_CODE >= 0x020117
 MODULE_PARM(scan,"i");
 MODULE_PARM(verbose,"i");
 MODULE_PARM(i2c_debug,"i");
+#endif
 
 /* ----------------------------------------------------------------------- */
 
@@ -34,8 +36,10 @@
 static struct i2c_driver *drivers[I2C_DRIVER_MAX];
 static int bus_count = 0, driver_count = 0;
 
+#ifdef CONFIG_VIDEO_BT848
 extern int i2c_tuner_init(void);
 extern int msp3400c_init(void);
+#endif
 
 int i2c_init(void)
 {
@@ -53,10 +57,10 @@
 
 static void i2c_attach_device(struct i2c_bus *bus, struct i2c_driver *driver)
 {
-	unsigned long flags;
 	struct i2c_device *device;
 	int i,j,ack=1;
 	unsigned char addr;
+	LOCK_FLAGS;
     
 	/* probe for device */
 	LOCK_I2C_BUS(bus);
@@ -148,8 +152,8 @@
 
 int i2c_register_bus(struct i2c_bus *bus)
 {
-	unsigned long flags;
 	int i,ack;
+	LOCK_FLAGS;
 
 	memset(bus->devices,0,sizeof(bus->devices));
 	bus->devcount = 0;
@@ -410,6 +414,7 @@
 
 #ifdef MODULE
 
+#if LINUX_VERSION_CODE >= 0x020100
 EXPORT_SYMBOL(i2c_register_bus);
 EXPORT_SYMBOL(i2c_unregister_bus);
 EXPORT_SYMBOL(i2c_register_driver);
@@ -424,7 +429,7 @@
 EXPORT_SYMBOL(i2c_readbyte);
 EXPORT_SYMBOL(i2c_read);
 EXPORT_SYMBOL(i2c_write);
-
+#endif
 
 int init_module(void)
 {
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/lp_m68k.c linux/drivers/char/lp_m68k.c
--- v2.2.0-pre7/linux/drivers/char/lp_m68k.c	Wed Jan 13 15:00:41 1999
+++ linux/drivers/char/lp_m68k.c	Fri Jan 15 17:46:27 1999
@@ -43,9 +43,7 @@
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/init.h>
-#ifdef CONFIG_KMOD
 #include <linux/kmod.h>
-#endif
 
 #ifdef CONFIG_AMIGA
 #ifdef CONFIG_MULTIFACE_III_LP
@@ -374,14 +372,12 @@
 	if (dev >= MAX_LP)
 		goto out_err;
 
-#ifdef CONFIG_KMOD
 	if (!lp_table[dev]) {
 		char modname[30];
 
 		sprintf(modname, "char-major-%d-%d", LP_MAJOR, dev);
 		request_module(modname);
 	}
-#endif
 	if (!lp_table[dev])
 		goto out_err;
 	if (!(lp_table[dev]->flags & LP_EXIST))
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.2.0-pre7/linux/drivers/char/misc.c	Mon Jan  4 15:08:17 1999
+++ linux/drivers/char/misc.c	Fri Jan 15 17:46:27 1999
@@ -49,9 +49,7 @@
 
 #include <linux/tty.h>
 #include <linux/selection.h>
-#ifdef CONFIG_KMOD
 #include <linux/kmod.h>
-#endif
 
 /*
  * Head entry for the doubly linked miscdevice list
@@ -108,7 +106,6 @@
 	while ((c != &misc_list) && (c->minor != minor))
 		c = c->next;
 	if (c == &misc_list) {
-#ifdef CONFIG_KMOD
 		char modname[20];
 		sprintf(modname, "char-major-%d-%d", MISC_MAJOR, minor);
 		request_module(modname);
@@ -116,7 +113,6 @@
 		while ((c != &misc_list) && (c->minor != minor))
 			c = c->next;
 		if (c == &misc_list)
-#endif
 			return -ENODEV;
 	}
 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/msp3400.c linux/drivers/char/msp3400.c
--- v2.2.0-pre7/linux/drivers/char/msp3400.c	Sun Nov  8 14:02:55 1998
+++ linux/drivers/char/msp3400.c	Sun Jan 17 18:28:06 1999
@@ -1,21 +1,21 @@
 /*
  * programming the msp34* sound processor family
  *
- * (c) 1997,1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
+ * (c) 1997,1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
  *
  * what works and what doesn't:
  *
- *  AM mono
+ *  AM-Mono
  *      probably doesn't (untested)
  *
- *  FM-mono
- *      should work. FM stereo modes are backward-compatible to mono.
- *      Therefore FM mono should always be available.
+ *  FM-Mono
+ *      should work. The stereo modes are backward compatible to FM-mono,
+ *      therefore FM-Mono should be allways available.
  *
- *  FM stereo (B/G, used in Germany)
+ *  FM-Stereo (B/G, used in germany)
  *      should work, with autodetect
  *
- *  FM stereo (satellite)
+ *  FM-Stereo (satellite)
  *      should work, no autodetect (i.e. default is mono, but you can
  *      switch to stereo -- untested)
  *
@@ -27,9 +27,14 @@
  * TODO:
  *   - better SAT support
  *
+ *
+ * 980623  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *         using soundcore instead of OSS
+ *
  */
 
 #include <linux/module.h>
+#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
@@ -37,6 +42,10 @@
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/malloc.h>
+#ifdef __SMP__
+#include <asm/pgtable.h>
+#include <linux/smp_lock.h>
+#endif
 
 /* kernel_thread */
 #define __KERNEL_SYSCALLS__
@@ -67,9 +76,9 @@
 	int mode;
 	int norm;
 	int stereo;
+	int main, second;	/* sound carrier */
 
-	int mixer;
-	int left, right; /* volume */
+	int left, right;	/* volume */
 	int bass, treble;
 
 	/* thread */
@@ -88,7 +97,22 @@
 
 #define dprintk     if (debug) printk
 
+#if LINUX_VERSION_CODE < 0x020100
+/* 2.0.x */
+#define signal_pending(current)  (current->signal & ~current->blocked)
+#define sigfillset(set)
+#define mdelay(x) udelay(1000*x)
+#else
 MODULE_PARM(debug,"i");
+#endif
+
+#if LINUX_VERSION_CODE < 0x02017f
+void schedule_timeout(int j)
+{
+	current->timeout = jiffies + j;
+	schedule();
+}
+#endif
 
 /* ---------------------------------------------------------------------- */
 
@@ -211,9 +235,9 @@
 	/* FM Radio */
 	{ { -8, -8, 4, 6, 78, 107 }, { -8, -8, 4, 6, 78, 107 },
 	  MSP_CARRIER(10.7), MSP_CARRIER(10.7),
-	  0x00d0, 0x0480, 0x0020, 0x3002 },
+	  0x00d0, 0x0480, 0x0020, 0x3000 },
 
-	/* Terrestial FM-mono */
+	/* Terrestial FM-mono + FM-stereo */
 	{ {  3, 18, 27, 48, 66, 72 }, {  3, 18, 27, 48, 66, 72 },
 	  MSP_CARRIER(5.5), MSP_CARRIER(5.5),
 	  0x00d0, 0x0480,   0x0030, 0x3000},
@@ -230,7 +254,7 @@
 
 	/* NICAM I */
 	{ {  2, 4, -6, -4, 40, 94 }, {  3, 18, 27, 48, 66, 72 },
-	  MSP_CARRIER(5.5), MSP_CARRIER(5.5),
+	  MSP_CARRIER(6.0), MSP_CARRIER(6.0),
 	  0x00d0, 0x0040,   0x0120, 0x3000},
 };
 
@@ -244,7 +268,7 @@
 	{ MSP_CARRIER(4.5),        "4.5   NTSC"                   }, 
 	{ MSP_CARRIER(5.5),        "5.5   PAL B/G"                }, 
 	{ MSP_CARRIER(6.0),        "6.0   PAL I"                  },
-	{ MSP_CARRIER(6.5),        "6.5   PAL SAT / SECAM"        }
+	{ MSP_CARRIER(6.5),        "6.5   PAL D/K + SAT + SECAM"  }
 };
 
 static struct CARRIER_DETECT carrier_detect_55[] = {
@@ -255,6 +279,9 @@
 
 static struct CARRIER_DETECT carrier_detect_65[] = {
 	/* PAL SAT / SECAM */
+	{ MSP_CARRIER(5.85),       "5.85  PAL D/K NICAM" },
+	{ MSP_CARRIER(6.2578125),  "6.25  PAL D/K1 FM-stereo" },
+	{ MSP_CARRIER(6.7421875),  "6.74  PAL D/K2 FM-stereo" },
 	{ MSP_CARRIER(7.02),       "7.02  PAL SAT FM-stereo s/b" },
 	{ MSP_CARRIER(7.20),       "7.20  PAL SAT FM-stereo s"   },
 	{ MSP_CARRIER(7.38),       "7.38  PAL SAT FM-stereo b"   },
@@ -270,6 +297,7 @@
 	msp3400c_write(bus,I2C_MSP3400C_DEM, 0x009b, cdo1 >> 12);
 	msp3400c_write(bus,I2C_MSP3400C_DEM, 0x00a3, cdo2 & 0xfff);
 	msp3400c_write(bus,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12);
+	msp3400c_write(bus,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/
 }
 
 static void msp3400c_setvolume(struct i2c_bus *bus, int left, int right)
@@ -329,12 +357,14 @@
 		msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005,
 			       msp_init_data[type].fir2[i]);
     
-	msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0083,          /* MODE_REG */
+	msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0083,     /* MODE_REG */
 		       msp_init_data[type].mode_reg);
     
 	msp3400c_setcarrier(msp->bus, msp_init_data[type].cdo1,
 			    msp_init_data[type].cdo2);
     
+	msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/
+
 	msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008,
 		       msp_init_data[type].dfp_src);
 	msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009,
@@ -357,9 +387,9 @@
 	/* switch demodulator */
 	switch (msp->mode) {
 	case MSP_MODE_FM_TERRA:
-		dprintk("msp3400: B/G setstereo: %d\n",mode);
+		dprintk("msp3400: FM setstereo: %d\n",mode);
 		msp->stereo = mode;
-		msp3400c_setcarrier(msp->bus,MSP_CARRIER(5.7421875),MSP_CARRIER(5.5));
+		msp3400c_setcarrier(msp->bus,msp->second,msp->main);
 		switch (mode) {
 		case VIDEO_SOUND_STEREO:
 			msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3001);
@@ -372,7 +402,7 @@
 		}
 		break;
 	case MSP_MODE_FM_SAT:
-		dprintk("msp3400: sat setstereo: %d\n",mode);
+		dprintk("msp3400: SAT setstereo: %d\n",mode);
 		msp->stereo = mode;
 		switch (mode) {
 		case VIDEO_SOUND_MONO:
@@ -390,9 +420,10 @@
 		}
 		break;
 	case MSP_MODE_FM_NICAM1:
-		dprintk("msp3400: NICAM1 setstereo: %d\n",mode);
+	case MSP_MODE_FM_NICAM2:
+		dprintk("msp3400: NICAM setstereo: %d\n",mode);
 		msp->stereo = mode;
-		msp3400c_setcarrier(msp->bus,MSP_CARRIER(5.85),MSP_CARRIER(5.5));
+		msp3400c_setcarrier(msp->bus,msp->second,msp->main);
 		nicam=0x0100;
 		break;
 	default:
@@ -406,7 +437,9 @@
 		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008,0x0020|nicam);
 		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009,0x0020|nicam);
 		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a,0x0020|nicam);
+#if 0
 		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0005,0x4000);
+#endif
 		break;
 	case VIDEO_SOUND_MONO:
 	case VIDEO_SOUND_LANG1:
@@ -422,6 +455,27 @@
 	}
 }
 
+static void
+msp3400c_print_mode(struct msp3400c *msp)
+{
+	if (msp->main == msp->second) {
+		printk("msp3400: mono sound carrier: %d.%03d MHz\n",
+		       msp->main/910000,(msp->main/910)%1000);
+	} else {
+		printk("msp3400: main sound carrier: %d.%03d MHz\n",
+		       msp->main/910000,(msp->main/910)%1000);
+	}
+	if (msp->mode == MSP_MODE_FM_NICAM1 ||
+	    msp->mode == MSP_MODE_FM_NICAM2)
+		printk("msp3400: NICAM carrier     : %d.%03d MHz\n",
+		       msp->second/910000,(msp->second/910)%1000);
+	if (msp->mode == MSP_MODE_FM_TERRA &&
+	    msp->main != msp->second) {
+		printk("msp3400: FM-stereo carrier : %d.%03d MHz\n",
+		       msp->second/910000,(msp->second/910)%1000);
+	}
+}
+
 /* ----------------------------------------------------------------------- */
 
 struct REGISTER_DUMP {
@@ -452,15 +506,17 @@
 
 static int msp3400c_thread(void *data)
 {
-	unsigned long flags;
 	struct msp3400c *msp = data;
 	struct semaphore sem = MUTEX_LOCKED;
     
 	struct CARRIER_DETECT *cd;
 	int                   count, max1,max2,val1,val2, val,this;
 	int                   newstereo;
+	LOCK_FLAGS;
     
-	/* lock_kernel(); */
+#ifdef __SMP__
+	lock_kernel();
+#endif
     
 	exit_mm(current);
 	current->session = 1;
@@ -472,7 +528,9 @@
 	msp->wait   = &sem;
 	msp->thread = current;
 
-	/* unlock_kernel(); */
+#ifdef __SMP__
+	unlock_kernel();
+#endif
 
 	dprintk("msp3400: thread: start\n");
 	if(msp->notify != NULL)
@@ -481,9 +539,11 @@
 	for (;;) {
 		if (msp->rmmod)
 			goto done;
-		dprintk("msp3400: thread: sleep\n");
+		if (debug > 1)
+			printk("msp3400: thread: sleep\n");
 		down_interruptible(&sem);
-		dprintk("msp3400: thread: wakeup\n");
+		if (debug > 1)
+			printk("msp3400: thread: wakeup\n");
 		if (msp->rmmod || signal_pending(current))
 			goto done;
 
@@ -510,6 +570,7 @@
 				}
 				break;
 			case MSP_MODE_FM_NICAM1:
+			case MSP_MODE_FM_NICAM2:
 				val = msp3400c_read(msp->bus, I2C_MSP3400C_DEM, 0x23);
 				switch ((val & 0x1e) >> 1)  {
 				case 0:
@@ -553,7 +614,7 @@
 			msp3400c_setcarrier(msp->bus, cd[this].cdo,cd[this].cdo);
 			UNLOCK_I2C_BUS(msp->bus);
 
-			current->state = TASK_INTERRUPTIBLE;
+			current->state   = TASK_INTERRUPTIBLE;
 			schedule_timeout(HZ/25);
 			if (signal_pending(current))
 				goto done;
@@ -587,7 +648,7 @@
 			msp3400c_setcarrier(msp->bus, cd[this].cdo,cd[this].cdo);
 			UNLOCK_I2C_BUS(msp->bus);
 
-			current->state = TASK_INTERRUPTIBLE;
+			current->state   = TASK_INTERRUPTIBLE;
 			schedule_timeout(HZ/25);
 			if (signal_pending(current))
 				goto done;
@@ -603,34 +664,56 @@
 			dprintk("msp3400: carrier2 val: %5d / %s\n", val,cd[this].name);
 		}
 
-		/* program the msp3400 according to the results */
+		/* programm the msp3400 according to the results */
+		msp->main   = carrier_detect_main[max1].cdo;
 		switch (max1) {
-		case 0: /* 4.5 */
 		case 1: /* 5.5 */
-			msp3400c_setmode(msp, MSP_MODE_FM_TERRA);
-			msp3400c_setcarrier(msp->bus, carrier_detect_main[max1].cdo,
-					    carrier_detect_main[max1].cdo);
 			if (max2 == 0) {
 				/* B/G FM-stereo */
+				msp->second = carrier_detect_55[max2].cdo;
+				msp3400c_setmode(msp, MSP_MODE_FM_TERRA);
 				msp3400c_setstereo(msp, VIDEO_SOUND_MONO);
 				msp->watch_stereo = 1;
-			}
-			if (max2 == 1 && msp->nicam) {
+			} else if (max2 == 1 && msp->nicam) {
 				/* B/G NICAM */
+				msp->second = carrier_detect_55[max2].cdo;
 				msp3400c_setmode(msp, MSP_MODE_FM_NICAM1);
-				/* msp3400c_write(msp->bus, I2C_MSP3400C_DFP, 0x21, 0x01); */
-				msp3400c_setcarrier(msp->bus, MSP_CARRIER(5.85),
-						    MSP_CARRIER(5.5));
+				msp3400c_setcarrier(msp->bus, msp->second, msp->main);
 				msp->watch_stereo = 1;
+			} else {
+				goto no_second;
 			}
 			break;
 		case 2: /* 6.0 */
+			/* PAL I NICAM */
+			msp->second = MSP_CARRIER(6.552);
+			msp3400c_setmode(msp, MSP_MODE_FM_NICAM2);
+			msp3400c_setcarrier(msp->bus, msp->second, msp->main);
+			msp->watch_stereo = 1;
+			break;
 		case 3: /* 6.5 */
+			if (max2 == 1 || max2 == 2) {
+				/* D/K FM-stereo */
+				msp->second = carrier_detect_65[max2].cdo;
+				msp3400c_setmode(msp, MSP_MODE_FM_TERRA);
+				msp3400c_setstereo(msp, VIDEO_SOUND_MONO);
+				msp->watch_stereo = 1;
+			} else if (max2 == 0 && msp->nicam) {
+				/* D/K NICAM */
+				msp->second = carrier_detect_65[max2].cdo;
+				msp3400c_setmode(msp, MSP_MODE_FM_NICAM1);
+				msp3400c_setcarrier(msp->bus, msp->second, msp->main);
+				msp->watch_stereo = 1;
+			} else {
+				goto no_second;
+			}
+			break;
+		case 0: /* 4.5 */
 		default:
+		no_second:
+			msp->second = carrier_detect_main[max1].cdo;
 			msp3400c_setmode(msp, MSP_MODE_FM_TERRA);
-			msp3400c_setcarrier(msp->bus, carrier_detect_main[max1].cdo,
-					    carrier_detect_main[max1].cdo);
-			msp3400c_setstereo(msp, VIDEO_SOUND_STEREO);
+			msp3400c_setcarrier(msp->bus, msp->second, msp->main);
 			break;
 		}
 
@@ -640,9 +723,13 @@
 
 		if (msp->watch_stereo) {
 			del_timer(&msp->wake_stereo);
-			msp->wake_stereo.expires = jiffies + HZ;
+			msp->wake_stereo.expires = jiffies + 2*HZ;
 			add_timer(&msp->wake_stereo);
 		}
+
+		if (debug)
+			msp3400c_print_mode(msp);
+		
 		msp->active = 0;
 	}
 
@@ -713,7 +800,8 @@
 
 		/* wait 1 sec */
 		current->state = TASK_INTERRUPTIBLE;
-		schedule_timeout(HZ);
+		current->timeout = jiffies + HZ;
+		schedule();
 		if (signal_pending(current))
 			goto done;
 		if (msp->restart) {
@@ -752,9 +840,14 @@
 
 #ifdef REGISTER_MIXER
 
+#include <linux/sound.h>
 #include <linux/soundcard.h>
-#include <../drivers/sound/sound_config.h>
-#include <../drivers/sound/dev_table.h>
+#include <asm/uaccess.h>
+
+static struct msp3400c *mspmix = NULL;  /* ugly hack, should do something more sensible */
+static int mixer_num;
+static int mixer_modcnt = 0;
+static struct semaphore mixer_sem = MUTEX;
 
 static int mix_to_v4l(int i)
 {
@@ -787,16 +880,42 @@
 	return (r << 8) | l;
 }
 
-static int msp3400c_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+static int
+msp3400c_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
-	struct msp3400c *msp = mixer_devs[dev]->devc;
-	unsigned long flags;
 	int ret,val = 0;
+	LOCK_FLAGS;
+
+        if (cmd == SOUND_MIXER_INFO) {
+                mixer_info info;
+                strncpy(info.id, "MSP3400", sizeof(info.id));
+                strncpy(info.name, "MSP 3400", sizeof(info.name));
+                info.modify_counter = mixer_modcnt;
+                if (copy_to_user((void *)arg, &info, sizeof(info)))
+                        return -EFAULT;
+                return 0;
+        }
+        if (cmd == SOUND_OLD_MIXER_INFO) {
+                _old_mixer_info info;
+                strncpy(info.id, "MSP3400", sizeof(info.id));
+                strncpy(info.name, "MSP 3400", sizeof(info.name));
+                if (copy_to_user((void *)arg, &info, sizeof(info)))
+                        return -EFAULT;
+                return 0;
+        }
+        if (cmd == OSS_GETVERSION)
+                return put_user(SOUND_VERSION, (int *)arg);
 
 	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
     
+	down(&mixer_sem);
+	if (!mspmix) {
+		up(&mixer_sem);
+		return -ENODEV;
+	}
+
 	switch (cmd) {
 	case MIXER_READ(SOUND_MIXER_RECMASK):
 	case MIXER_READ(SOUND_MIXER_CAPS):
@@ -813,91 +932,97 @@
 		break;
 
 	case MIXER_WRITE(SOUND_MIXER_VOLUME):
-		msp->left  = mix_to_v4l(val);
-		msp->right = mix_to_v4l(val >> 8);
-		LOCK_I2C_BUS(msp->bus);
-		msp3400c_setvolume(msp->bus,msp->left,msp->right);
-		UNLOCK_I2C_BUS(msp->bus);
+		mspmix->left  = mix_to_v4l(val);
+		mspmix->right = mix_to_v4l(val >> 8);
+		LOCK_I2C_BUS(mspmix->bus);
+		msp3400c_setvolume(mspmix->bus,mspmix->left,mspmix->right);
+		UNLOCK_I2C_BUS(mspmix->bus);
+		mixer_modcnt++;
 		/* fall */
 	case MIXER_READ(SOUND_MIXER_VOLUME):
-		ret = v4l_to_mix2(msp->left, msp->right);
+		ret = v4l_to_mix2(mspmix->left, mspmix->right);
 		break;
 
 	case MIXER_WRITE(SOUND_MIXER_BASS):
-		msp->bass = mix_to_v4l(val);
-		LOCK_I2C_BUS(msp->bus);
-		msp3400c_setbass(msp->bus,msp->bass);
-		UNLOCK_I2C_BUS(msp->bus);
+		mspmix->bass = mix_to_v4l(val);
+		LOCK_I2C_BUS(mspmix->bus);
+		msp3400c_setbass(mspmix->bus,mspmix->bass);
+		UNLOCK_I2C_BUS(mspmix->bus);
+		mixer_modcnt++;
 		/* fall */
 	case MIXER_READ(SOUND_MIXER_BASS):
-		ret = v4l_to_mix(msp->bass);
+		ret = v4l_to_mix(mspmix->bass);
 		break;
 
 	case MIXER_WRITE(SOUND_MIXER_TREBLE):
-		msp->treble = mix_to_v4l(val);
-		LOCK_I2C_BUS(msp->bus);
-		msp3400c_settreble(msp->bus,msp->treble);
-		UNLOCK_I2C_BUS(msp->bus);
+		mspmix->treble = mix_to_v4l(val);
+		LOCK_I2C_BUS(mspmix->bus);
+		msp3400c_settreble(mspmix->bus,mspmix->treble);
+		UNLOCK_I2C_BUS(mspmix->bus);
+		mixer_modcnt++;
 		/* fall */
 	case MIXER_READ(SOUND_MIXER_TREBLE):
-		ret = v4l_to_mix(msp->treble);
+		ret = v4l_to_mix(mspmix->treble);
 		break;
 
 	default:
+		up(&mixer_sem);
 		return -EINVAL;
 	}
+	up(&mixer_sem);
 	if (put_user(ret, (int *)arg))
 		return -EFAULT;
 	return 0;
 }
 
-struct mixer_operations msp3400c_mixer = {
-	"video4linux",
-	"TV card sound (msp3400)",
-	msp3400c_mixer_ioctl
-};
-
-static int msp3400c_mixer_init(struct msp3400c *msp)
+static int
+msp3400c_mixer_open(struct inode *inode, struct file *file)
 {
-	int m;
-    
-	msp->mixer = m = sound_alloc_mixerdev();
-	if (m == -1)
-		return -1;
-
-	mixer_devs[m] = (struct mixer_operations *)
-		kmalloc(sizeof(struct mixer_operations), GFP_KERNEL);
-	if (mixer_devs[m] == NULL) {
-		printk(KERN_ERR "msp3400c: can't allocate memory\n");
-		sound_unload_mixerdev(m);
-		return -1;
-	}
-	memcpy(mixer_devs[m],&msp3400c_mixer,sizeof(struct mixer_operations));
-	mixer_devs[m]->devc = msp;
-	return 0;
+        MOD_INC_USE_COUNT;
+        return 0;
 }
 
-static int msp3400c_mixer_close(struct msp3400c *msp)
+static int
+msp3400c_mixer_release(struct inode *inode, struct file *file)
 {
-	int m = msp->mixer;
-
-	if (m != -1 ) {
-		sound_unload_mixerdev(m);
-		kfree(mixer_devs[m]);
-	}
-	return 0;
+        MOD_DEC_USE_COUNT;
+        return 0;
 }
 
+static loff_t
+msp3400c_mixer_llseek(struct file *file, loff_t offset, int origin)
+{
+        return -ESPIPE;
+}
+
+static /*const*/ struct file_operations msp3400c_mixer_fops = {
+        &msp3400c_mixer_llseek,
+        NULL,  /* read */
+        NULL,  /* write */
+        NULL,  /* readdir */
+        NULL,  /* poll */
+        &msp3400c_mixer_ioctl,
+        NULL,  /* mmap */
+        &msp3400c_mixer_open,
+	NULL,
+        &msp3400c_mixer_release,
+        NULL,  /* fsync */
+        NULL,  /* fasync */
+        NULL,  /* check_media_change */
+        NULL,  /* revalidate */
+        NULL,  /* lock */
+};
+
 #endif
 
 /* ----------------------------------------------------------------------- */
 
 static int msp3400c_attach(struct i2c_device *device)
 {
-	unsigned long flags;
 	struct semaphore sem = MUTEX_LOCKED;
 	struct msp3400c *msp;
 	int              rev1,rev2;
+	LOCK_FLAGS;
 
 	device->data = msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL);
 	if (NULL == msp)
@@ -913,17 +1038,24 @@
 	if (-1 == msp3400c_reset(msp->bus)) {
 		UNLOCK_I2C_BUS(msp->bus);
 		kfree(msp);
-		return -EIO;
+		dprintk("msp3400: no chip found\n");
+		return -1;
 	}
     
+	rev1 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1e);
+	rev2 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1f);
+	if (0 == rev1 && 0 == rev2) {
+		UNLOCK_I2C_BUS(msp->bus);
+		kfree(msp);
+		printk("msp3400: error while reading chip version\n");
+		return -1;
+	}
+
 	msp3400c_setmode(msp, MSP_MODE_FM_TERRA);
 	msp3400c_setvolume(msp->bus, msp->left, msp->right);
 	msp3400c_setbass(msp->bus, msp->bass);
 	msp3400c_settreble(msp->bus, msp->treble);
     
-	rev1 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1e);
-	rev2 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1f);
-
 #if 0
 	/* this will turn on a 1kHz beep - might be useful for debugging... */
 	msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0014, 0x1040);
@@ -932,7 +1064,7 @@
 
 	sprintf(device->name,"MSP34%02d%c-%c%d",
 		(rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f);
-	msp->nicam = (((rev2>>8)&0xff) == 10) ? 1 : 0;
+	msp->nicam = (((rev2>>8)&0xff) != 00) ? 1 : 0;
 
 	/* timer for stereo checking */
 	msp->wake_stereo.function = msp3400c_stereo_wake;
@@ -951,8 +1083,9 @@
 	if (msp->nicam)
 		printk(", has NICAM support");
 #ifdef REGISTER_MIXER
-	if (0 == msp3400c_mixer_init(msp))
-		printk(", registered as sound mixer");
+	down(&mixer_sem);
+	mspmix = msp;
+	up(&mixer_sem);
 #endif
 	printk("\n");
 	return 0;
@@ -960,12 +1093,14 @@
 
 static int msp3400c_detach(struct i2c_device *device)
 {
-	unsigned long flags;
 	struct semaphore sem = MUTEX_LOCKED;
 	struct msp3400c *msp  = (struct msp3400c*)device->data;
+	LOCK_FLAGS;
     
 #ifdef REGISTER_MIXER
-	msp3400c_mixer_close(msp);
+	down(&mixer_sem);
+	mspmix = NULL;
+	up(&mixer_sem);
 #endif
 
 	/* shutdown control thread */
@@ -992,9 +1127,10 @@
 static int msp3400c_command(struct i2c_device *device,
 		 unsigned int cmd, void *arg)
 {
-	unsigned long flags;
 	struct msp3400c *msp  = (struct msp3400c*)device->data;
 	int             *iarg = (int*)arg;
+        __u16           *sarg = arg;
+	LOCK_FLAGS;
 
 	switch (cmd) {
 	case MSP_SET_RADIO:
@@ -1004,12 +1140,22 @@
 		LOCK_I2C_BUS(msp->bus);
 		msp3400c_setmode(msp,MSP_MODE_FM_RADIO);
 		msp3400c_setcarrier(msp->bus, MSP_CARRIER(10.7),MSP_CARRIER(10.7));
+		msp3400c_setvolume(msp->bus,msp->left, msp->right);
 		UNLOCK_I2C_BUS(msp->bus);
 		break;
 	case MSP_SET_TVNORM:
 		msp->norm = *iarg;
 		break;
+	case MSP_SWITCH_MUTE:
+		/* channels switching step one -- mute */
+		msp->watch_stereo=0;
+		del_timer(&msp->wake_stereo);
+		LOCK_I2C_BUS(msp->bus);
+		msp3400c_setvolume(msp->bus,0,0);
+		UNLOCK_I2C_BUS(msp->bus);
+		break;
 	case MSP_NEWCHANNEL:
+		/* channels switching step two -- trigger sound carrier scan */
 		msp->watch_stereo=0;
 		del_timer(&msp->wake_stereo);
 		if (!msp->active)
@@ -1019,59 +1165,55 @@
 		break;
 
 	case MSP_GET_VOLUME:
-		*iarg = (msp->left > msp->right) ? msp->left : msp->right;
+		*sarg = (msp->left > msp->right) ? msp->left : msp->right;
 		break;
 	case MSP_SET_VOLUME:
-		msp->left = msp->right = *iarg;
+		msp->left = msp->right = *sarg;
 		LOCK_I2C_BUS(msp->bus);
 		msp3400c_setvolume(msp->bus,msp->left, msp->right);
 		UNLOCK_I2C_BUS(msp->bus);
 		break;
 
 	case MSP_GET_BASS:
-		*iarg = msp->bass;
+		*sarg = msp->bass;
 		break;
 	case MSP_SET_BASS:
-		msp->bass = *iarg;
+		msp->bass = *sarg;
 		LOCK_I2C_BUS(msp->bus);
 		msp3400c_setbass(msp->bus,msp->bass);
 		UNLOCK_I2C_BUS(msp->bus);
 		break;
 
 	case MSP_GET_TREBLE:
-		*iarg = msp->treble;
+		*sarg = msp->treble;
 		break;
 	case MSP_SET_TREBLE:
-		msp->treble = *iarg;
+		msp->treble = *sarg;
 		LOCK_I2C_BUS(msp->bus);
 		msp3400c_settreble(msp->bus,msp->treble);
 		UNLOCK_I2C_BUS(msp->bus);
 		break;
 
 	case MSP_GET_STEREO:
-		*iarg = msp->stereo;
+		*sarg = msp->stereo;
 		break;
 	case MSP_SET_STEREO:
-		if (*iarg) {
+		if (*sarg) {
 			msp->watch_stereo=0;
 			del_timer(&msp->wake_stereo);
 			LOCK_I2C_BUS(msp->bus);
-			msp3400c_setstereo(msp,*iarg);
+			msp3400c_setstereo(msp,*sarg);
 			UNLOCK_I2C_BUS(msp->bus);
 		}
 		break;
 
 	case MSP_GET_DC:
 		LOCK_I2C_BUS(msp->bus);
-		*iarg = (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b) +
-			(int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c);
+		*sarg = ((int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b) +
+			 (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c));
 		UNLOCK_I2C_BUS(msp->bus);
 		break;
-		
-	case MSP_GET_UNIT:
-		*iarg = msp->mixer;
-		break;
-	
+
 	default:
 		return -EINVAL;
 	}
@@ -1090,8 +1232,6 @@
 	msp3400c_command
 };
 
-EXPORT_NO_SYMBOLS;
-
 #ifdef MODULE
 int init_module(void)
 #else
@@ -1099,6 +1239,10 @@
 #endif
 {
 	i2c_register_driver(&i2c_driver_msp);
+#ifdef REGISTER_MIXER
+	if ((mixer_num = register_sound_mixer(&msp3400c_mixer_fops, -1)) < 0)
+		printk(KERN_ERR "msp3400c: cannot allocate mixer device\n");
+#endif
 	return 0;
 }
 
@@ -1106,6 +1250,10 @@
 void cleanup_module(void)
 {
 	i2c_unregister_driver(&i2c_driver_msp);
+#ifdef REGISTER_MIXER
+	if (mixer_num >= 0)
+		unregister_sound_mixer(mixer_num);
+#endif
 }
 #endif
 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/msp3400.h linux/drivers/char/msp3400.h
--- v2.2.0-pre7/linux/drivers/char/msp3400.h	Thu Aug 20 17:05:15 1998
+++ linux/drivers/char/msp3400.h	Thu Jan 14 22:53:02 1999
@@ -7,19 +7,19 @@
 #define MSP_SET_RADIO      _IO('m',2)       /* Radio mode                */
 #define MSP_NEWCHANNEL     _IO('m',3)       /* indicate new channel      */
 
-#define MSP_GET_VOLUME     _IOR('m',4,int)
-#define MSP_SET_VOLUME     _IOW('m',5,int)
+#define MSP_GET_VOLUME     _IOR('m',4,__u16)
+#define MSP_SET_VOLUME     _IOW('m',5,__u16)
 
-#define MSP_GET_STEREO     _IOR('m',6,int)
-#define MSP_SET_STEREO     _IOW('m',7,int)
+#define MSP_GET_STEREO     _IOR('m',6,__u16)
+#define MSP_SET_STEREO     _IOW('m',7,__u16)
 
-#define MSP_GET_DC         _IOW('m',8,int)
+#define MSP_GET_DC         _IOW('m',8,__u16)
 
-#define MSP_GET_BASS       _IOR('m', 9,int)
-#define MSP_SET_BASS       _IOW('m',10,int)
-#define MSP_GET_TREBLE     _IOR('m',11,int)
-#define MSP_SET_TREBLE     _IOW('m',12,int)
-
-#define MSP_GET_UNIT	   _IOR('m',13,int)
+#define MSP_GET_BASS       _IOR('m', 9,__u16)
+#define MSP_SET_BASS       _IOW('m',10,__u16)
+#define MSP_GET_TREBLE     _IOR('m',11,__u16)
+#define MSP_SET_TREBLE     _IOW('m',12,__u16)
 
+#define MSP_GET_UNIT       _IOR('m',13,int)
+#define MSP_SWITCH_MUTE    _IO('m',14)
 #endif /* MSP3400_H */
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/n_tty.c linux/drivers/char/n_tty.c
--- v2.2.0-pre7/linux/drivers/char/n_tty.c	Fri Jan  8 22:36:05 1999
+++ linux/drivers/char/n_tty.c	Sun Jan 17 10:39:27 1999
@@ -922,10 +922,10 @@
 		}
 	}
 
-	add_wait_queue(&tty->read_wait, &wait);
-
 	if (down_interruptible(&tty->atomic_read))
 		return -ERESTARTSYS;
+
+	add_wait_queue(&tty->read_wait, &wait);
 	set_bit(TTY_DONT_FLIP, &tty->flags);
 	while (nr) {
 		/* First test for status change. */
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/pc_keyb.c linux/drivers/char/pc_keyb.c
--- v2.2.0-pre7/linux/drivers/char/pc_keyb.c	Wed Jan 13 15:00:41 1999
+++ linux/drivers/char/pc_keyb.c	Fri Jan 15 11:28:55 1999
@@ -65,7 +65,7 @@
 static volatile unsigned char resend = 0;
 
 
-#if defined(CONFIG_PSMOUSE)
+#if defined CONFIG_PSMOUSE
 /*
  *	PS/2 Auxiliary Device
  */
@@ -87,7 +87,7 @@
 #endif /* CONFIG_PSMOUSE */
 
 /*
- * Wait for keyboard controller input buffer is empty.
+ * Wait for keyboard controller input buffer to drain.
  *
  * Don't use 'jiffies' so that we don't depend on
  * interrupts..
@@ -709,6 +709,52 @@
 }
 
 #if defined CONFIG_PSMOUSE
+
+/*
+ * Check if this is a dual port controller.
+ */
+static int __init detect_auxiliary_port(void)
+{
+	unsigned long flags;
+	unsigned char status;
+	unsigned char val;
+	int loops = 5;
+	int retval = 0;
+
+	spin_lock_irqsave(&kbd_controller_lock, flags);
+
+	/* Put the value 0x5A in the output buffer using the "Write
+	 * Auxiliary Device Output Buffer" command (0xD3). Poll the
+	 * Status Register for a while to see if the value really
+	 * turns up in the Data Register. If the KBD_STAT_MOUSE_OBF
+	 * bit is also set to 1 in the Status Register, we assume this
+	 * controller has an Auxiliary Port (a.k.a. Mouse Port).
+	 */
+	kb_wait();
+	outb(KBD_CCMD_WRITE_AUX_OBUF, KBD_CNTL_REG);
+
+	kb_wait();
+	outb(0x5a, KBD_DATA_REG); /* 0x5a is a random dummy value. */
+
+	status = inb(KBD_STATUS_REG);
+	while (!(status & KBD_STAT_OBF) && loops--) {
+		mdelay(1);
+		status = inb(KBD_STATUS_REG);
+	}
+
+	if (status & KBD_STAT_OBF) {
+		val = inb(KBD_DATA_REG);
+		if (val == 0x5a && (status & KBD_STAT_MOUSE_OBF)) {
+			printk(KERN_INFO "Detected PS/2 Mouse Port.\n");
+			retval = 1;
+		}
+	}
+
+	spin_unlock_irqrestore(&kbd_controller_lock, flags);
+
+	return retval;
+}
+
 /*
  * Send a byte to the mouse.
  */
@@ -894,18 +940,9 @@
 
 static int __init psaux_init(void)
 {
-#if 0
-	/*
-	 * Don't bother with the BIOS flag: even if we don't have
-	 * a mouse connected at bootup we may still want to connect
-	 * one later, and we don't want to just let the BIOS tell
-	 * us that it has no mouse..
-	 */
-	if (aux_device_present != 0xaa)
+	if (!detect_auxiliary_port())
 		return -EIO;
 
-	printk(KERN_INFO "PS/2 auxiliary pointing device detected -- driver installed.\n");
-#endif
 	misc_register(&psaux_mouse);
 	queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
 	memset(queue, 0, sizeof(*queue));
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/pc_keyb.h linux/drivers/char/pc_keyb.h
--- v2.2.0-pre7/linux/drivers/char/pc_keyb.h	Fri Oct 23 22:01:20 1998
+++ linux/drivers/char/pc_keyb.h	Thu Jan 14 10:39:09 1999
@@ -51,6 +51,8 @@
 #define KBD_CCMD_KBD_TEST	0xAB	/* Keyboard interface test */
 #define KBD_CCMD_KBD_DISABLE	0xAD	/* Keyboard interface disable */
 #define KBD_CCMD_KBD_ENABLE	0xAE	/* Keyboard interface enable */
+#define KBD_CCMD_WRITE_AUX_OBUF	0xD3    /* Write to output buffer as if
+					   initiated by the auxiliary device */
 #define KBD_CCMD_WRITE_MOUSE	0xD4	/* Write the following byte to the mouse */
 
 /*
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/pcxx.c linux/drivers/char/pcxx.c
--- v2.2.0-pre7/linux/drivers/char/pcxx.c	Sun Nov  8 14:02:55 1998
+++ linux/drivers/char/pcxx.c	Sun Jan 17 18:28:06 1999
@@ -70,6 +70,7 @@
 #include <linux/malloc.h>
 #include <linux/string.h>
 #include <linux/init.h>
+#include <linux/version.h>
 
 #ifndef MODULE
 #include <linux/ctype.h> /* We only need it for parsing the "digi="-line */
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/rtc.c linux/drivers/char/rtc.c
--- v2.2.0-pre7/linux/drivers/char/rtc.c	Wed Aug 26 11:37:37 1998
+++ linux/drivers/char/rtc.c	Thu Jan 14 22:58:47 1999
@@ -93,7 +93,7 @@
 static inline unsigned char rtc_is_updating(void);
 
 /*
- *	Bits in rtc_status. (7 bits of room for future expansion)
+ *	Bits in rtc_status. (6 bits of room for future expansion)
  */
 
 #define RTC_IS_OPEN		0x01	/* means /dev/rtc is in use	*/
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/saa5249.c linux/drivers/char/saa5249.c
--- v2.2.0-pre7/linux/drivers/char/saa5249.c	Thu Nov 12 16:21:19 1998
+++ linux/drivers/char/saa5249.c	Thu Jan 14 22:53:02 1999
@@ -460,7 +460,7 @@
 
 				if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) &&	/* check FOUND-bit */
 					(memcmp(infobits, t->vdau[req.pgbuf].laststat, sizeof(infobits)) || 
-					jiffies >= t->vdau[req.pgbuf].expire))
+					time_after_eq(jiffies, t->vdau[req.pgbuf].expire)))
 				{		/* check if new page arrived */
 					if (i2c_senddata(t, CCTWR, 8, 0, 0, 0, -1) ||
 						i2c_getdata(t, CCTRD, VTX_PAGESIZE, t->vdau[req.pgbuf].pgbuf, FALSE))
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.2.0-pre7/linux/drivers/char/serial.c	Thu Jan  7 15:11:36 1999
+++ linux/drivers/char/serial.c	Thu Jan 14 10:27:53 1999
@@ -1681,7 +1681,7 @@
 	new_serial.irq = irq_cannonicalize(new_serial.irq);
 
 	if ((new_serial.irq >= NR_IRQS) || (new_serial.port > 0xffff) ||
-	    (new_serial.baud_base == 0) || (new_serial.type < PORT_UNKNOWN) ||
+	    (new_serial.baud_base < 9600)|| (new_serial.type < PORT_UNKNOWN) ||
 	    (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) ||
 	    (new_serial.type == PORT_STARTECH)) {
 		return -EINVAL;
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.2.0-pre7/linux/drivers/char/tty_io.c	Fri Jan  8 22:36:05 1999
+++ linux/drivers/char/tty_io.c	Fri Jan 15 17:46:27 1999
@@ -89,9 +89,7 @@
 #include <linux/vt_kern.h>
 #include <linux/selection.h>
 
-#ifdef CONFIG_KMOD
 #include <linux/kmod.h>
-#endif
 
 #define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
 #define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
@@ -219,7 +217,6 @@
 
 	if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS))
 		return -EINVAL;
-#ifdef CONFIG_KMOD
 	/* Eduardo Blanco <ejbs@cs.cs.com.uy> */
 	/* Cyrus Durgin <cider@speakeasy.org> */
 	if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) {
@@ -227,7 +224,6 @@
 		sprintf(modname, "tty-ldisc-%d", ldisc);
 		request_module (modname);
 	}
-#endif
 	if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
 		return -EINVAL;
 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/tuner.c linux/drivers/char/tuner.c
--- v2.2.0-pre7/linux/drivers/char/tuner.c	Thu Nov 12 16:21:19 1998
+++ linux/drivers/char/tuner.c	Sun Jan 17 18:28:06 1999
@@ -6,19 +6,31 @@
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/malloc.h>
+#include <linux/version.h>
 
 #include <linux/i2c.h>
 #include <linux/videodev.h>
 
 #include "tuner.h"
 
-static int debug = 0; /* insmod parameter */
-static int type  = 0; /* tuner type */
+static int debug =  0; /* insmod parameter */
+static int type  = -1; /* tuner type */
 
 #define dprintk     if (debug) printk
 
+#if LINUX_VERSION_CODE > 0x020100
 MODULE_PARM(debug,"i");
 MODULE_PARM(type,"i");
+#endif
+
+#if LINUX_VERSION_CODE < 0x02017f
+void schedule_timeout(int j)
+{
+	current->state   = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + j;
+	schedule();
+}
+#endif
 
 struct tuner 
 {
@@ -69,9 +81,10 @@
 	{"Temic NTSC", TEMIC, NTSC,
 	        16*157.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2,732},
 	{"TEMIC PAL_I", TEMIC, PAL_I,
-	        16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,0xc2,623},
+	      //  16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,0xc2,623},
+	        16*170.00,16*450.00,0x02,0x04,0x01,0x8e,0xc2,623},
 	{"Temic 4036 FY5 NTSC", TEMIC, NTSC,
-		16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc2,732},	       
+		16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc2,732},
 };
 
 /* ---------------------------------------------------------------------- */
@@ -98,11 +111,17 @@
 
 static void set_tv_freq(struct tuner *t, int freq)
 {
-        unsigned long flags;
 	u8 config;
 	u16 div;
-	struct tunertype *tun=&tuners[t->type];
+	struct tunertype *tun;
+	LOCK_FLAGS;
 
+	if (t->type == -1) {
+		printk("tuner: tuner type not set\n");
+		return;
+	}
+
+	tun=&tuners[t->type];
 	if (freq < tun->thresh1) 
 		config = tun->VHF_L;
 	else if (freq < tun->thresh2) 
@@ -125,11 +144,17 @@
 
 static void set_radio_freq(struct tuner *t, int freq)
 {
-        unsigned long flags;
 	u8 config;
 	u16 div;
-	struct tunertype *tun=&tuners[type];
+	struct tunertype *tun;
+        LOCK_FLAGS;
+
+	if (t->type == -1) {
+		printk("tuner: tuner type not set\n");
+		return;
+	}
 
+	tun=&tuners[t->type];
 	config = 0xa5;
 	div=freq + (int)(16*10.7);
   	div&=0x7fff;
@@ -143,7 +168,7 @@
 	}
 	if (debug) {
 		UNLOCK_I2C_BUS(t->bus);
-		current->state = TASK_INTERRUPTIBLE;
+		current->state   = TASK_INTERRUPTIBLE;
 		schedule_timeout(HZ/10);
 		LOCK_I2C_BUS(t->bus);
 		
@@ -181,7 +206,8 @@
 	t->bus  = device->bus;
 	t->addr = device->addr;
 	t->type = type;
-	dprintk("tuner: type is %d (%s)\n",t->type,tuners[t->type].name);
+	dprintk("tuner: type is %d (%s)\n",t->type,
+		(t->type == -1 ) ? "autodetect" : tuners[t->type].name);
 	
 	MOD_INC_USE_COUNT;
 	return 0;
@@ -204,6 +230,8 @@
 	switch (cmd) 
 	{
 		case TUNER_SET_TYPE:
+			if (t->type != -1)
+				return 0;
 			t->type = *iarg;
 			dprintk("tuner: type set to %d (%s)\n",
 			t->type,tuners[t->type].name);
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/char/videodev.c linux/drivers/char/videodev.c
--- v2.2.0-pre7/linux/drivers/char/videodev.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/char/videodev.c	Fri Jan 15 17:46:27 1999
@@ -24,12 +24,12 @@
 #include <linux/errno.h>
 #include <linux/videodev.h>
 
+#if LINUX_VERSION_CODE >= 0x020100
 #include <asm/uaccess.h>
+#endif
 #include <asm/system.h>
 
-#ifdef CONFIG_KMOD
 #include <linux/kmod.h>
-#endif
 
 
 #define VIDEO_NUM_DEVICES	256 
@@ -103,17 +103,17 @@
 #endif	
 #ifdef CONFIG_RADIO_SF16FMI
 	{"SF16FMI", fmi_init}, 
-#endif
+#endif	
 #ifdef CONFIG_RADIO_MIROPCM20
 	{"PCM20", pcm20_init}, 
-#endif	
+#endif
 #ifdef CONFIG_RADIO_GEMTEK
-	{"GemTek", gemtek_init}, 
-#endif	
+	{"GemTek", gemtek_init},
+#endif
 	{"end", NULL}
 };
 
-
+#if LINUX_VERSION_CODE >= 0x020100
 /*
  *	Read will do some smarts later on. Buffer pin etc.
  */
@@ -129,6 +129,7 @@
 }
 
 
+
 /*
  *	Write for now does nothing. No reason it shouldnt do overlay setting
  *	for some boards I guess..
@@ -144,7 +145,6 @@
 		return 0;
 }
 
-
 /*
  *	Poll to see if we're readable, can probably be used for timing on incoming
  *  frames, etc..
@@ -159,6 +159,32 @@
 		return 0;
 }
 
+
+#else
+static int video_read(struct inode *ino,struct file *file,
+			  char *buf, int count)
+{
+         int err;
+	 struct video_device *vfl=video_device[MINOR(ino->i_rdev)];
+	 if (vfl->read)
+	   return vfl->read(vfl, buf, count, file->f_flags&O_NONBLOCK);
+	 else
+	   return -EINVAL;
+}
+
+static int video_write(struct inode *ino,struct file *file, const char *buf, 
+			int count)
+{
+	int err;
+	struct video_device *vfl=video_device[MINOR(ino->i_rdev)];
+	if (vfl->write)
+	  return vfl->write(vfl, buf, count, file->f_flags&O_NONBLOCK);
+	else
+	  return 0;
+}
+
+#endif
+
 /*
  *	Open a video device.
  */
@@ -174,14 +200,12 @@
 		
 	vfl=video_device[minor];
 	if(vfl==NULL) {
-#ifdef CONFIG_KMOD
 		char modname[20];
 
 		sprintf (modname, "char-major-%d-%d", VIDEO_MAJOR, minor);
 		request_module(modname);
 		vfl=video_device[minor];
 		if (vfl==NULL)
-#endif
 			return -ENODEV;
 	}
 	if(vfl->busy)
@@ -218,11 +242,19 @@
  *	image ?
  */
  
+#if LINUX_VERSION_CODE >= 0x020100
 static long long video_lseek(struct file * file,
 			  long long offset, int origin)
 {
 	return -ESPIPE;
 }
+#else
+static long long video_lseek(struct inode *inode, struct file * file,
+			     long long offset, int origin)
+{
+	return -ESPIPE;
+}
+#endif
 
 
 static int video_ioctl(struct inode *inode, struct file *file,
@@ -246,9 +278,16 @@
  */
  
  
+#if LINUX_VERSION_CODE >= 0x020100
 int video_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
+#else
+static int video_mmap(struct inode * ino, struct file * file,
+		      struct vm_area_struct * vma)
+{
+	struct video_device *vfl=video_device[MINOR(ino->i_rdev)];
+#endif
 	if(vfl->mmap)
 		return vfl->mmap(vfl, (char *)vma->vm_start, 
 				(unsigned long)(vma->vm_end-vma->vm_start));
@@ -332,11 +371,17 @@
 	video_read,
 	video_write,
 	NULL,	/* readdir */
+#if LINUX_VERSION_CODE >= 0x020100
 	video_poll,	/* poll */
+#else
+	NULL,
+#endif
 	video_ioctl,
 	video_mmap,
 	video_open,
+#if LINUX_VERSION_CODE >= 0x020100
 	NULL,		/* flush */
+#endif
 	video_release
 };
 
@@ -380,5 +425,7 @@
 
 #endif
 
+#if LINUX_VERSION_CODE >= 0x020100
 EXPORT_SYMBOL(video_register_device);
 EXPORT_SYMBOL(video_unregister_device);
+#endif
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/3c523.c linux/drivers/net/3c523.c
--- v2.2.0-pre7/linux/drivers/net/3c523.c	Fri Jan  8 22:36:06 1999
+++ linux/drivers/net/3c523.c	Sun Jan 17 18:28:06 1999
@@ -184,7 +184,7 @@
 
 /* helper-functions */
 static int init586(struct device *dev);
-static int check586(struct device *dev, char *where, unsigned size);
+static int check586(struct device *dev, unsigned long where, unsigned size);
 static void alloc586(struct device *dev);
 static void startrecv586(struct device *dev);
 static void *alloc_rfa(struct device *dev, void *ptr);
@@ -311,19 +311,19 @@
  * Check to see if there's an 82586 out there.
  */
 
-__initfunc(static int check586(struct device *dev, char *where, unsigned size))
+__initfunc(static int check586(struct device *dev, unsigned long where, unsigned size))
 {
 	struct priv *p = (struct priv *) dev->priv;
 	char *iscp_addrs[2];
 	int i = 0;
 
-	p->base = (unsigned long) where + size - 0x01000000;
-	p->memtop = where + size;
-	p->scp = (struct scp_struct *) (p->base + SCP_DEFAULT_ADDRESS);
+	p->base = where + size - 0x01000000;
+	p->memtop = phys_to_virt(where) + size;
+	p->scp = (struct scp_struct *)phys_to_virt(p->base + SCP_DEFAULT_ADDRESS);
 	memset((char *) p->scp, 0, sizeof(struct scp_struct));
 	p->scp->sysbus = SYSBUSVAL;	/* 1 = 8Bit-Bus, 0 = 16 Bit */
 
-	iscp_addrs[0] = where;
+	iscp_addrs[0] = phys_to_virt(where);
 	iscp_addrs[1] = (char *) p->scp - sizeof(struct iscp_struct);
 
 	for (i = 0; i < 2; i++) {
@@ -358,7 +358,7 @@
 	elmc_id_reset586();
 	DELAY(2);
 
-	p->scp = (struct scp_struct *) (p->base + SCP_DEFAULT_ADDRESS);
+	p->scp = (struct scp_struct *) phys_to_virt(p->base + SCP_DEFAULT_ADDRESS);
 	p->scb = (struct scb_struct *) phys_to_virt(dev->mem_start);
 	p->iscp = (struct iscp_struct *) ((char *) p->scp - sizeof(struct iscp_struct));
 
@@ -531,7 +531,7 @@
 	elmc_id_reset586();	/* seems like a good idea before checking it... */
 
 	size = 0x4000;		/* check for 16K mem */
-	if (!check586(dev, (char *) phys_to_virt(dev->mem_start), size)) {
+	if (!check586(dev, dev->mem_start, size)) {
 		printk("%s: memprobe, Can't find memory at 0x%lx!\n", dev->name,
 		       dev->mem_start);
 		release_region(dev->base_addr, ELMC_IO_EXTENT);
@@ -539,7 +539,7 @@
 	}
 	dev->mem_end = dev->mem_start + size;	/* set mem_end showed by 'ifconfig' */
 
-	((struct priv *) (dev->priv))->base = (unsigned long)phys_to_virt(dev->mem_start + size - 0x01000000);
+	((struct priv *) (dev->priv))->base = dev->mem_start + size - 0x01000000;
 	alloc586(dev);
 
 	elmc_id_reset586();	/* make sure it doesn't generate spurious ints */
@@ -963,7 +963,7 @@
 				if (skb != NULL) {
 					skb->dev = dev;
 					skb_reserve(skb, 2);	/* 16 byte alignment */
-					memcpy(skb_put(skb, totlen), (char *) p->base + (unsigned long) rbd->buffer, totlen);
+					memcpy(skb_put(skb, totlen), (u8 *)phys_to_virt(p->base) + (unsigned long) rbd->buffer, totlen);
 					skb->protocol = eth_type_trans(skb, dev);
 					netif_rx(skb);
 					p->stats.rx_packets++;
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v2.2.0-pre7/linux/drivers/net/3c59x.c	Wed Jan 13 15:00:42 1999
+++ linux/drivers/net/3c59x.c	Thu Jan 14 22:58:47 1999
@@ -1489,6 +1489,7 @@
 			outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
 		}
 	}
+	vp->stats.tx_bytes += skb->len;
 	return 0;
 }
 
@@ -1548,6 +1549,7 @@
 			clear_bit(0, (void*)&dev->tbusy);
 		}
 		dev->trans_start = jiffies;
+		vp->stats.tx_bytes += skb->len;
 		return 0;
 	}
 }
@@ -1732,6 +1734,7 @@
 				netif_rx(skb);
 				dev->last_rx = jiffies;
 				vp->stats.rx_packets++;
+				vp->stats.rx_bytes += skb->len;
 				/* Wait a limited time to go to next packet. */
 				for (i = 200; i >= 0; i--)
 					if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
@@ -1783,6 +1786,7 @@
 			int pkt_len = rx_status & 0x1fff;
 			struct sk_buff *skb;
 
+			vp->stats.rx_bytes += pkt_len;
 			if (vortex_debug > 4)
 				printk(KERN_DEBUG "Receiving packet size %d status %4.4x.\n",
 					   pkt_len, rx_status);
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.2.0-pre7/linux/drivers/net/Config.in	Mon Dec 28 15:00:52 1998
+++ linux/drivers/net/Config.in	Sun Jan 17 18:28:06 1999
@@ -59,7 +59,7 @@
     tristate '3c515 ISA Fast EtherLink' CONFIG_3C515
     tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX
   fi
-  bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE
+  tristate 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE
   bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC
   if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
     tristate 'WD80*3 support' CONFIG_WD80x3
@@ -227,6 +227,7 @@
 # The COSA/SRP driver has not been tested as non-modular yet.
 #
 dep_tristate 'COSA/SRP sync serial boards support' CONFIG_COSA m
+tristate 'Red Creek Hardware VPN (EXPERIMENTAL)' CONFIG_RCPCI
 #
 if [ "$CONFIG_WAN_ROUTER" != "n" ]; then
   bool 'WAN drivers' CONFIG_WAN_DRIVERS
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.2.0-pre7/linux/drivers/net/Makefile	Fri Jan  8 22:36:06 1999
+++ linux/drivers/net/Makefile	Thu Jan 14 22:58:47 1999
@@ -1060,5 +1060,5 @@
 wanpipe.o: $(WANPIPE_OBJS)
 	ld -r -o $@ $(WANPIPE_OBJS)
 
-rcpci.o: rcpci45.o rcmtl.o
-	$(LD) -r -o rcpci.o rcpci45.o rcmtl.o
+rcpci.o: rcpci45.o rclanmtl.o
+	$(LD) -r -o rcpci.o rcpci45.o rclanmtl.o
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.2.0-pre7/linux/drivers/net/Space.c	Tue Dec 22 14:16:55 1998
+++ linux/drivers/net/Space.c	Fri Jan 15 14:36:21 1999
@@ -128,7 +128,6 @@
 extern int apfddi_init(struct device *dev);
 
 /* HIPPI boards */
-extern int cern_hippi_probe(struct device *);
 extern int rr_hippi_probe(struct device *);
 
 struct devprobe
@@ -177,9 +176,6 @@
 #ifdef CONFIG_VORTEX
 	{tc59x_probe, 0},
 #endif
-#ifdef CONFIG_DEC_ELCP 
-	{tulip_probe, 0},
-#endif
 #ifdef CONFIG_NE2K_PCI
 	{ne2k_pci_probe, 0},
 #endif
@@ -189,6 +185,9 @@
 #ifdef CONFIG_EEXPRESS_PRO100	/* Intel EtherExpress Pro/100 */
 	{eepro100_probe, 0},
 #endif
+#ifdef CONFIG_DEC_ELCP 
+	{tulip_probe, 0},
+#endif
 #ifdef CONFIG_DE4X5             /* DEC DE425, DE434, DE435 adapters */
 	{de4x5_probe, 0},
 #endif
@@ -541,9 +540,6 @@
 		return 1;
 
 	if (1
-#ifdef CONFIG_CERN_HIPPI
-	    && cern_hippi_probe(dev)
-#endif
 #ifdef CONFIG_ROADRUNNER
 	    && rr_hippi_probe(dev)
 #endif
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/acenic.c linux/drivers/net/acenic.c
--- v2.2.0-pre7/linux/drivers/net/acenic.c	Fri Jan  8 22:36:06 1999
+++ linux/drivers/net/acenic.c	Sun Jan 17 18:28:06 1999
@@ -121,7 +121,6 @@
  * TODO:
  *
  * - Add multicast support.
- * - The Tigon II firmware fails to run in some PCs.
  * - NIC dump support.
  * - More tuning parameters.
  */
@@ -133,15 +132,16 @@
 static int max_tx_desc[8] = {0, };
 static int max_rx_desc[8] = {0, };
 
-static const char *version = "acenic.c: v0.22 01/07/99  Jes Sorensen (Jes.Sorensen@cern.ch)\n";
+static const char *version = "acenic.c: v0.24 01/13/99  Jes Sorensen (Jes.Sorensen@cern.ch)\n";
 
 static struct device *root_dev = NULL;
 
 static int ace_load_firmware(struct device *dev);
 
+static int probed __initdata = 0;
+
 __initfunc(int acenic_probe (struct device *dev))
 {
-	static int i = 0;
 	int boards_found = 0;
 	int version_disp;
 	struct ace_private *ap;
@@ -154,6 +154,10 @@
 #endif
 	struct pci_dev *pdev = NULL;
 
+	if (probed)
+		return -ENODEV;
+	probed ++;
+
 	if (!pci_present())		/* is PCI support present? */
 		return -ENODEV;
 
@@ -183,8 +187,6 @@
 		ap->pdev = pdev;
 		ap->vendor = pdev->vendor;
 
-		pci_set_master(pdev);
-
 		dev->irq = pdev->irq;
 #ifdef __SMP__
 		spin_lock_init(&ap->lock);
@@ -221,6 +223,8 @@
 					      pci_latency);
 		}
 
+		pci_set_master(pdev);
+
 		switch(ap->vendor){
 		case PCI_VENDOR_ID_ALTEON:
 			sprintf(ap->name, "AceNIC Gigabit Ethernet");
@@ -246,7 +250,8 @@
 						      0x4000);
 		if (!ap->regs){
 			printk(KERN_ERR "%s:  Unable to map I/O register, "
-			       "AceNIC %i will be disabled.\n", dev->name, i);
+			       "AceNIC %i will be disabled.\n",
+			       dev->name, boards_found);
 			break;
 		}
 
@@ -401,13 +406,6 @@
 			  ((CLR_INT | WORD_SWAP) << 24));
 #endif
 
-#ifdef __LITTLE_ENDIAN
-	regs->ModeStat = ACE_BYTE_SWAP_DATA | ACE_WARN | ACE_FATAL
-			| ACE_WORD_SWAP;
-#else
-#error "this driver doesn't run on big-endian machines yet!"
-#endif
-
 	/*
 	 * Stop the NIC CPU and clear pending interrupts
 	 */
@@ -438,6 +436,20 @@
 		       tig_ver);
 		return -ENODEV;
 	}
+
+	/*
+	 * ModeStat _must_ be set after the SRAM settings as this change
+	 * seems to corrupt the ModeStat and possible other registers.
+	 * The SRAM settings survive resets and setting it to the same
+	 * value a second time works as well. This is what caused the
+	 * `Firmware not running' problem on the Tigon II.
+	 */
+#ifdef __LITTLE_ENDIAN
+	regs->ModeStat = ACE_BYTE_SWAP_DATA | ACE_WARN | ACE_FATAL
+			| ACE_WORD_SWAP;
+#else
+#error "this driver doesn't run on big-endian machines yet!"
+#endif
 
 	mac1 = 0;
 	for(i = 0; i < 4; i++){
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c
--- v2.2.0-pre7/linux/drivers/net/eepro100.c	Sun Nov  8 14:03:00 1998
+++ linux/drivers/net/eepro100.c	Fri Jan 15 14:36:21 1999
@@ -1110,6 +1110,7 @@
 				/* Free the original skb. */
 				if (sp->tx_skbuff[entry]) {
 					sp->stats.tx_packets++;	/* Count only user packets. */
+					sp->stats.tx_bytes += sp->tx_skbuff[entry]->len; /* Count transmitted bytes */
 					dev_free_skb(sp->tx_skbuff[entry]);
 					sp->tx_skbuff[entry] = 0;
 				} else if ((sp->tx_ring[entry].status&0x70000) == CmdNOp << 16)
@@ -1228,6 +1229,7 @@
 			skb->protocol = eth_type_trans(skb, dev);
 			netif_rx(skb);
 			sp->stats.rx_packets++;
+			sp->stats.rx_bytes += pkt_len; /* Count received bytes */
 		}
 		entry = (++sp->cur_rx) % RX_RING_SIZE;
 	}
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- v2.2.0-pre7/linux/drivers/net/eexpress.c	Fri Jan  8 22:36:07 1999
+++ linux/drivers/net/eexpress.c	Thu Jan 14 10:31:41 1999
@@ -97,6 +97,7 @@
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
+#include <asm/irq.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/init.h>
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/hamradio/baycom_ser_fdx.c linux/drivers/net/hamradio/baycom_ser_fdx.c
--- v2.2.0-pre7/linux/drivers/net/hamradio/baycom_ser_fdx.c	Tue Jun  9 11:57:29 1998
+++ linux/drivers/net/hamradio/baycom_ser_fdx.c	Sun Jan 17 18:28:06 1999
@@ -83,6 +83,7 @@
 #include <linux/netdevice.h>
 #include <linux/hdlcdrv.h>
 #include <linux/baycom.h>
+#include <linux/version.h>
 
 /* --------------------------------------------------------------------- */
 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/hamradio/baycom_ser_hdx.c linux/drivers/net/hamradio/baycom_ser_hdx.c
--- v2.2.0-pre7/linux/drivers/net/hamradio/baycom_ser_hdx.c	Tue Jun  9 11:57:29 1998
+++ linux/drivers/net/hamradio/baycom_ser_hdx.c	Sun Jan 17 18:28:06 1999
@@ -72,6 +72,7 @@
 #include <linux/netdevice.h>
 #include <linux/hdlcdrv.h>
 #include <linux/baycom.h>
+#include <linux/version.h>
 
 /* --------------------------------------------------------------------- */
 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/hamradio/soundmodem/sm.c linux/drivers/net/hamradio/soundmodem/sm.c
--- v2.2.0-pre7/linux/drivers/net/hamradio/soundmodem/sm.c	Fri Nov 27 13:09:24 1998
+++ linux/drivers/net/hamradio/soundmodem/sm.c	Sun Jan 17 18:28:06 1999
@@ -61,6 +61,7 @@
 #include <asm/bitops.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
+#include <linux/version.h>
 #include "sm.h"
 
 /* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/ltpc.c linux/drivers/net/ltpc.c
--- v2.2.0-pre7/linux/drivers/net/ltpc.c	Tue Dec 22 14:16:55 1998
+++ linux/drivers/net/ltpc.c	Fri Jan 15 14:36:21 1999
@@ -1335,7 +1335,7 @@
 		/* if it's in process, wait a bit for it to finish */
 		timeout = jiffies+HZ; 
 		add_timer(&ltpc_timer);
-		while(del_timer(&ltpc_timer) && (timeout > jiffies))
+		while(del_timer(&ltpc_timer) && time_after(timeout, jiffies))
 		{
 			add_timer(&ltpc_timer);
 			schedule();
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c
--- v2.2.0-pre7/linux/drivers/net/pcnet32.c	Sat Sep  5 16:46:40 1998
+++ linux/drivers/net/pcnet32.c	Sun Jan 17 18:28:06 1999
@@ -1,6 +1,6 @@
 /* pcnet32.c: An AMD PCnet32 ethernet driver for linux. */
 /*
- *      Copyright 1996,97,98 Thomas Bogendoerfer
+ *      Copyright 1996-1999 Thomas Bogendoerfer
  * 
  * 	Derived from the lance driver written 1993,1994,1995 by Donald Becker.
  * 
@@ -13,7 +13,7 @@
  * 	This driver is for PCnet32 and PCnetPCI based ethercards
  */
 
-static const char *version = "pcnet32.c:v1.02 3.9.98 tsbogend@alpha.franken.de\n";
+static const char *version = "pcnet32.c:v1.11 17.1.99 tsbogend@alpha.franken.de\n";
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -51,6 +51,17 @@
 static const int max_interrupt_work = 20;
 static const int rx_copybreak = 200;
 
+#define PORT_AUI      0x00
+#define PORT_10BT     0x01
+#define PORT_GPSI     0x02
+#define PORT_MII      0x03
+
+#define PORT_PORTSEL  0x03
+#define PORT_ASEL     0x04
+#define PORT_FD       0x80
+
+static int options = PORT_ASEL;  /* port selection */
+
 /*
  * 				Theory of Operation
  * 
@@ -101,6 +112,10 @@
  * v1.01:  do ring dumps, only when debugging the driver
  *         increased the transmit timeout
  * v1.02:  fixed memory leak in pcnet32_init_ring()
+ * v1.10:  workaround for stopped transmitter
+ *         added port selection for modules
+ *         detect special T1/E1 WAN card and setup port selection
+ * v1.11:  fixed wrong checking of Tx errors
  */
 
 
@@ -176,8 +191,9 @@
 	int dirty_rx, dirty_tx;		        /* The ring entries to be free()ed. */
 	struct net_device_stats stats;
 	char tx_full;
-	unsigned long lock;
-        char shared_irq;                         /* shared irq possible */
+        int  options;
+        int  shared_irq:1,                      /* shared irq possible */
+             full_duplex:1;                     /* full duplex possible */
 #ifdef MODULE
         struct device *next;
 #endif    
@@ -250,11 +266,13 @@
 	unsigned int ioaddr = *port;
 	
 	if ( check_region(ioaddr, PCNET32_TOTAL_SIZE) == 0) {
-	    if (pcnet32_probe1(dev, ioaddr, 0, 0) == 0)
+    	    /* check if there is really a pcnet chip on that ioaddr */
+    	    if ((inb(ioaddr + 14) == 0x57) &&
+		(inb(ioaddr + 15) == 0x57) &&
+	        (pcnet32_probe1(dev, ioaddr, 0, 0) == 0))
 	      return 0;
 	}
     }
-    
     return ENODEV;
 }
 
@@ -263,13 +281,9 @@
 __initfunc(static int pcnet32_probe1(struct device *dev, unsigned int ioaddr, unsigned char irq_line, int shared))
 {
     struct pcnet32_private *lp;
-    int i;
+    int i,full_duplex = 0;
     char *chipname;
 
-    /* check if there is really a pcnet chip on that ioaddr */
-    if ((inb(ioaddr + 14) != 0x57) || (inb(ioaddr + 15) != 0x57))
-      return ENODEV; 
-    
     inw(ioaddr+PCNET32_RESET); /* Reset the PCNET32 */
 
     outw(0x0000, ioaddr+PCNET32_ADDR); /* Switch to window 0 */
@@ -295,16 +309,22 @@
 	    chipname = "PCnet/PCI 79C970";
 	    break;
 	 case 0x2430:
-	    chipname = "PCnet32";
+	    if (shared)
+		chipname = "PCnet/PCI 79C970"; /* 970 gives the wrong chip id back */
+	    else
+		chipname = "PCnet/32 79C965";
 	    break;
 	 case 0x2621:
 	    chipname = "PCnet/PCI II 79C970A";
+	    full_duplex = 1;
 	    break;
 	 case 0x2623:
 	    chipname = "PCnet/FAST 79C971";
+	    full_duplex = 1;
 	    break;
 	 case 0x2624:
 	    chipname = "PCnet/FAST+ 79C972";
+	    full_duplex = 1;
 	    break;
 	 default:
 	    printk("pcnet32: PCnet version %#x, no PCnet32 chip.\n",chip_version);
@@ -332,8 +352,14 @@
     dev->priv = lp;
     lp->name = chipname;
     lp->shared_irq = shared;
+    lp->full_duplex = full_duplex;
+    lp->options = options;
+    
+    /* detect special T1/E1 WAN card by checking for MAC address */
+    if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 && dev->dev_addr[2] == 0x75)
+	lp->options = PORT_FD | PORT_GPSI;
 
-    lp->init_block.mode = le16_to_cpu(0x0003);	/* Disable Rx and Tx. */
+    lp->init_block.mode = le16_to_cpu(0x0003); 	/* Disable Rx and Tx. */
     lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); 
     for (i = 0; i < 6; i++)
       lp->init_block.phys_addr[i] = dev->dev_addr[i];
@@ -382,11 +408,6 @@
 	}
     }
 
-    outw(0x0002, ioaddr+PCNET32_ADDR);
-    /* only touch autoselect bit */
-    outw(inw(ioaddr+PCNET32_BUS_IF) | 0x0002, ioaddr+PCNET32_BUS_IF);
-
-
     if (pcnet32_debug > 0)
       printk(version);
     
@@ -413,6 +434,7 @@
 {
 	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
 	unsigned int ioaddr = dev->base_addr;
+        unsigned short val;
 	int i;
 
 	if (dev->irq == 0 ||
@@ -428,19 +450,40 @@
 	outw(0x0014, ioaddr+PCNET32_ADDR);
 	outw(0x0002, ioaddr+PCNET32_BUS_IF);
 
-	/* Turn on auto-select of media (AUI, BNC). */
-	outw(0x0002, ioaddr+PCNET32_ADDR);
-	/* only touch autoselect bit */
-	outw(inw(ioaddr+PCNET32_BUS_IF) | 0x0002, ioaddr+PCNET32_BUS_IF);
-
 	if (pcnet32_debug > 1)
 		printk("%s: pcnet32_open() irq %d tx/rx rings %#x/%#x init %#x.\n",
 			   dev->name, dev->irq,
 		           (u32) virt_to_bus(lp->tx_ring),
 		           (u32) virt_to_bus(lp->rx_ring),
 			   (u32) virt_to_bus(&lp->init_block));
-
-	lp->init_block.mode = 0x0000;
+    
+        /* set/reset autoselect bit */
+    	outw(0x0002, ioaddr+PCNET32_ADDR);
+        val = inw(ioaddr+PCNET32_BUS_IF) & ~2;
+        if (lp->options & PORT_ASEL)
+	        val |= 2;
+        outw(val, ioaddr+PCNET32_BUS_IF);
+
+        /* handle full duplex setting */
+        if (lp->full_duplex) {
+	    outw (0x0009, ioaddr+PCNET32_ADDR);
+	    val = inw(ioaddr+PCNET32_BUS_IF) & ~3;
+	    if (lp->options & PORT_FD) {
+		val |= 1;
+		if (lp->options == (PORT_FD | PORT_AUI))
+		    val |= 2;
+	    }
+	    outw(val, ioaddr+PCNET32_BUS_IF);
+	}
+    
+        /* set/reset GPSI bit in test register */
+        outw (0x007c, ioaddr+PCNET32_ADDR);
+        val = inw(ioaddr+PCNET32_DATA) & ~0x10;
+        if ((lp->options & PORT_PORTSEL) == PORT_GPSI)
+	        val |= 0x10;
+    	outw(val, ioaddr+PCNET32_DATA);
+	    
+	lp->init_block.mode = le16_to_cpu((lp->options & PORT_PORTSEL) << 7);
 	lp->init_block.filter[0] = 0x00000000;
 	lp->init_block.filter[1] = 0x00000000;
 	if (pcnet32_init_ring(dev))
@@ -515,7 +558,7 @@
 	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
 	int i;
 
-	lp->lock = 0, lp->tx_full = 0;
+	lp->tx_full = 0;
 	lp->cur_rx = lp->cur_tx = 0;
 	lp->dirty_rx = lp->dirty_tx = 0;
 
@@ -613,7 +656,6 @@
 		outw(0x0000, ioaddr+PCNET32_ADDR);
 		printk("%s: pcnet32_start_xmit() called, csr0 %4.4x.\n", dev->name,
 			   inw(ioaddr+PCNET32_DATA));
-		outw(0x0000, ioaddr+PCNET32_DATA);
 	}
 
 	/* Block a timer-based transmit from overlapping.  This could better be
@@ -623,12 +665,8 @@
 		return 1;
 	}
 
-	if (test_and_set_bit(0, (void*)&lp->lock) != 0) {
-		if (pcnet32_debug > 0)
-			printk("%s: tx queue lock!.\n", dev->name);
-		/* don't clear dev->tbusy flag. */
-		return 1;
-	}
+	save_flags (flags);
+	cli ();
 
 	/* Fill in a Tx ring entry */
 
@@ -655,15 +693,11 @@
 
 	dev->trans_start = jiffies;
 
-	save_flags(flags);
-	cli();
-	lp->lock = 0;
 	if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
 		clear_bit (0, (void *)&dev->tbusy);
 	else
 		lp->tx_full = 1;
 	restore_flags(flags);
-
 	return 0;
 }
 
@@ -675,6 +709,7 @@
 	struct pcnet32_private *lp;
 	unsigned int csr0, ioaddr;
 	int boguscnt =  max_interrupt_work;
+	int must_restart;
 
 	if (dev == NULL) {
 		printk ("pcnet32_interrupt(): irq %d for unknown device.\n", irq);
@@ -693,6 +728,8 @@
 		/* Acknowledge all of the current interrupt sources ASAP. */
 		outw(csr0 & ~0x004f, dev->base_addr + PCNET32_DATA);
 
+		must_restart = 0;
+
 		if (pcnet32_debug > 5)
 			printk("%s: interrupt  csr0=%#2.2x new csr=%#2.2x.\n",
 				   dev->name, csr0, inw(dev->base_addr + PCNET32_DATA));
@@ -714,7 +751,7 @@
 
 				if (status & 0x4000) {
 					/* There was an major error, log it. */
-					int err_status = le16_to_cpu(lp->tx_ring[entry].misc);
+					int err_status = le32_to_cpu(lp->tx_ring[entry].misc);
 					lp->stats.tx_errors++;
 					if (err_status & 0x04000000) lp->stats.tx_aborted_errors++;
 					if (err_status & 0x08000000) lp->stats.tx_carrier_errors++;
@@ -725,10 +762,7 @@
 						/* Remove this verbosity later! */
 						printk("%s: Tx FIFO error! Status %4.4x.\n",
 							   dev->name, csr0);
-					        /* stop the chip to clear the error condition, then restart */
-					        outw(0x0000, dev->base_addr + PCNET32_ADDR);
-					        outw(0x0004, dev->base_addr + PCNET32_DATA);
-					        pcnet32_restart(dev, 0x0002);
+						must_restart = 1;
 					}
 				} else {
 					if (status & 0x1800)
@@ -782,6 +816,13 @@
 				   dev->name, csr0);
 		        /* unlike for the lance, there is no restart needed */
 		}
+
+		if (must_restart) {
+			/* stop the chip to clear the error condition, then restart */
+		        outw(0x0000, dev->base_addr + PCNET32_ADDR);
+		        outw(0x0004, dev->base_addr + PCNET32_DATA);
+		        pcnet32_restart(dev, 0x0002);
+		}
 	}
 
     /* Clear any other interrupt, and set interrupt enable. */
@@ -1014,9 +1055,9 @@
 	if (dev->flags&IFF_PROMISC) {
 		/* Log any net taps. */
 		printk("%s: Promiscuous mode enabled.\n", dev->name);
-	        lp->init_block.mode = le16_to_cpu(0x8000);
+	        lp->init_block.mode = le16_to_cpu(0x8000 | (lp->options & PORT_PORTSEL) << 7);
 	} else {
-	        lp->init_block.mode = 0x0000;
+	    	lp->init_block.mode = le16_to_cpu((lp->options & PORT_PORTSEL) << 7);
 	        pcnet32_load_multicast (dev);
 	}
     
@@ -1028,6 +1069,7 @@
 
 #ifdef MODULE
 MODULE_PARM(debug, "i");
+MODULE_PARM(options, "i");
 MODULE_PARM(max_interrupt_work, "i");
 MODULE_PARM(rx_copybreak, "i");
 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v2.2.0-pre7/linux/drivers/net/ppp.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/net/ppp.c	Thu Jan 14 22:56:06 1999
@@ -4,7 +4,7 @@
  *  Al Longyear <longyear@netcom.com>
  *  Extensively rewritten by Paul Mackerras <paulus@cs.anu.edu.au>
  *
- *  ==FILEVERSION 981004==
+ *  ==FILEVERSION 990114==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the number above to the
@@ -1216,6 +1216,7 @@
 } ppp_proto_type;
 
 static int rcv_proto_ip		(struct ppp *, struct sk_buff *);
+static int rcv_proto_ipv6	(struct ppp *, struct sk_buff *);
 static int rcv_proto_ipx	(struct ppp *, struct sk_buff *);
 static int rcv_proto_at		(struct ppp *, struct sk_buff *);
 static int rcv_proto_vjc_comp	(struct ppp *, struct sk_buff *);
@@ -1226,6 +1227,7 @@
 static
 ppp_proto_type proto_list[] = {
 	{ PPP_IP,	  rcv_proto_ip	       },
+	{ PPP_IPV6,	  rcv_proto_ipv6       },
 	{ PPP_IPX,	  rcv_proto_ipx	       },
 	{ PPP_AT,	  rcv_proto_at	       },
 	{ PPP_VJC_COMP,	  rcv_proto_vjc_comp   },
@@ -2003,6 +2005,19 @@
 }
 
 /*
+ * Process the receipt of an IPv6 frame
+ */
+static int
+rcv_proto_ipv6(struct ppp *ppp, struct sk_buff *skb)
+{
+	CHECK_PPP(0);
+	if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0)
+	    && ppp->sc_npmode[NP_IPV6] == NPMODE_PASS)
+		return ppp_rcv_rx(ppp, ETH_P_IPV6, skb);
+	return 0;
+}
+
+/*
  * Process the receipt of an IPX frame
  */
 static int
@@ -2382,6 +2397,10 @@
 	case ETH_P_IP:
 		proto = PPP_IP;
 		npmode = ppp->sc_npmode[NP_IP];
+		break;
+	case ETH_P_IPV6:
+		proto = PPP_IPV6;
+		npmode = ppp->sc_npmode[NP_IPV6];
 		break;
 	case ETH_P_IPX:
 		proto = PPP_IPX;
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/rcif.h linux/drivers/net/rcif.h
--- v2.2.0-pre7/linux/drivers/net/rcif.h	Tue Dec 22 14:16:56 1998
+++ linux/drivers/net/rcif.h	Thu Jan 14 22:58:47 1999
@@ -8,7 +8,7 @@
 **  RedCreek InterFace include file.
 **
 **  ---------------------------------------------------------------------
-**  ---     Copyright (c) 1998, RedCreek Communications Inc.          ---
+**  ---     Copyright (c) 1998-1999, RedCreek Communications Inc.     ---
 **  ---                   All rights reserved.                        ---
 **  ---------------------------------------------------------------------
 **
@@ -38,7 +38,7 @@
 
 /* The following protocol revision # should be incremented every time
    a new protocol or new structures are used in this file. */
-int USER_PROTOCOL_REV = 1;     /* used to track different protocol revisions */
+int USER_PROTOCOL_REV = 2;     /* used to track different protocol revisions */
 
 /* define a single TCB & buffer */
 typedef struct                  /* a single buffer */
@@ -124,6 +124,31 @@
             U32 LinkSpeedCode;
         } RCgetspeed;                   /* <---- RCgetspeed */
   
+        /* SETSPEED structure */
+        struct RCsetspeed_tag {
+            U16 LinkSpeedCode;
+        } RCsetspeed;                   /* <---- RCsetspeed */
+
+        /* GETPROM structure */
+        struct  RCgetprom_tag {
+            U32 PromMode;
+        } RCgetprom;                   /* <---- RCgetprom */
+  
+        /* SETPROM structure */
+        struct RCsetprom_tag {
+            U16 PromMode;
+        } RCsetprom;                   /* <---- RCsetprom */
+
+        /* GETBROADCAST structure */
+        struct  RCgetbroadcast_tag {
+            U32 BroadcastMode;
+        } RCgetbroadcast;                   /* <---- RCgetbroadcast */
+  
+        /* SETBROADCAST structure */
+        struct RCsetbroadcast_tag {
+            U16 BroadcastMode;
+        } RCsetbroadcast;                   /* <---- RCsetbroadcast */
+
         /* GETFIRMWAREVER structure */
         #define FirmStringLen 80
         struct RCgetfwver_tag {
@@ -136,12 +161,23 @@
             U32 NetMask;
         } RCgetipandmask;               /* <---- RCgetipandmask */
 
+        /* SETIPANDMASK structure */
+        struct RCsetipnmask_tag {
+            U32 IpAddr;
+            U32 NetMask;
+        } RCsetipandmask;               /* <---- RCsetipandmask */
+
         /* GETMAC structure */
         #define MAC_SIZE 10
         struct RCgetmac_tag {
             U8 mac[MAC_SIZE];
         } RCgetmac;                     /* <---- RCgetmac */
 
+        /* SETMAC structure */
+        struct RCsetmac_tag {
+            U8 mac[MAC_SIZE];
+        } RCsetmac;                     /* <---- RCsetmac */
+
         /* GETLINKSTATUS structure */
         struct RCgetlnkstatus_tag {
             U32 ReturnStatus;
@@ -166,35 +202,56 @@
 union RC_user_data_tag {        /* structure tags used are taken from RC_user_tag structure above */
     struct RCgetinfo_tag      *getinfo;
     struct RCgetspeed_tag     *getspeed;
+    struct RCgetprom_tag *getprom;
+    struct RCgetbroadcast_tag   *getbroadcast;
     struct RCgetfwver_tag     *getfwver;
     struct RCgetipnmask_tag   *getipandmask;
     struct RCgetmac_tag       *getmac;
     struct RCgetlnkstatus_tag *getlinkstatus;
     struct RCgetlinkstats_tag *getlinkstatistics;
     struct RCdefault_tag      *rcdefault;
+    struct RCsetspeed_tag     *setspeed;
+    struct RCsetprom_tag     *setprom;
+    struct RCsetbroadcast_tag     *setbroadcast;
+    struct RCsetipnmask_tag   *setipandmask;
+    struct RCsetmac_tag       *setmac;
 } _RC_user_data;  /* declare as a global, so the defines below will work */
 
 /* 3) Structure short-cut entry */
 /* define structure short-cuts */   /* structure names are taken from RC_user_tag structure above */
 #define RCUS_GETINFO           data.RCgetinfo;
 #define RCUS_GETSPEED          data.RCgetspeed;
+#define RCUS_GETPROM           data.RCgetprom;
+#define RCUS_GETBROADCAST      data.RCgetbroadcast;
 #define RCUS_GETFWVER          data.RCgetfwver;
 #define RCUS_GETIPANDMASK      data.RCgetipandmask;
 #define RCUS_GETMAC            data.RCgetmac;
 #define RCUS_GETLINKSTATUS     data.RCgetlnkstatus;
 #define RCUS_GETLINKSTATISTICS data.RCgetlinkstats;
 #define RCUS_DEFAULT           data.RCdefault;
+#define RCUS_SETSPEED          data.RCsetspeed;
+#define RCUS_SETPROM           data.RCsetprom;
+#define RCUS_SETBROADCAST      data.RCsetbroadcast;
+#define RCUS_SETIPANDMASK      data.RCsetipandmask;
+#define RCUS_SETMAC            data.RCsetmac;
 
 /* 4) Data short-cut entry */
 /* define data short-cuts */    /* pointer names are from RC_user_data_tag union (just below RC_user_tag) */
 #define RCUD_GETINFO           _RC_user_data.getinfo
 #define RCUD_GETSPEED          _RC_user_data.getspeed
+#define RCUD_GETPROM           _RC_user_data.getprom
+#define RCUD_GETBROADCAST      _RC_user_data.getbroadcast
 #define RCUD_GETFWVER          _RC_user_data.getfwver
 #define RCUD_GETIPANDMASK      _RC_user_data.getipandmask
 #define RCUD_GETMAC            _RC_user_data.getmac
 #define RCUD_GETLINKSTATUS     _RC_user_data.getlinkstatus
 #define RCUD_GETLINKSTATISTICS _RC_user_data.getlinkstatistics
 #define RCUD_DEFAULT           _RC_user_data.rcdefault
+#define RCUD_SETSPEED          _RC_user_data.setspeed
+#define RCUD_SETPROM           _RC_user_data.setprom
+#define RCUD_SETBROADCAST      _RC_user_data.setbroadcast
+#define RCUD_SETIPANDMASK      _RC_user_data.setipandmask
+#define RCUD_SETMAC            _RC_user_data.setmac
 
 /* 5) Command identifier entry */
 /* define command identifiers */
@@ -205,7 +262,14 @@
 #define RCUC_GETMAC             0x05
 #define RCUC_GETLINKSTATUS      0x06
 #define RCUC_GETLINKSTATISTICS  0x07
+#define RCUC_GETPROM            0x14
+#define RCUC_GETBROADCAST       0x15
 #define RCUC_DEFAULT            0xff
+#define RCUC_SETSPEED           0x08
+#define RCUC_SETIPANDMASK       0x09
+#define RCUC_SETMAC             0x0a
+#define RCUC_SETPROM            0x16
+#define RCUC_SETBROADCAST       0x17
 
 /* define ioctl commands to use, when talking to RC 45/PCI driver */
 #define RCU_PROTOCOL_REV         SIOCDEVPRIVATE
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/rclanmtl.c linux/drivers/net/rclanmtl.c
--- v2.2.0-pre7/linux/drivers/net/rclanmtl.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/rclanmtl.c	Thu Jan 14 22:58:47 1999
@@ -0,0 +1,2307 @@
+/*
+** *************************************************************************
+**
+**
+**     R C L A N M T L . C             $Revision: 5 $
+**
+**
+**  RedCreek I2O LAN Message Transport Layer program module.
+**
+**  ---------------------------------------------------------------------
+**  ---     Copyright (c) 1997-1999, RedCreek Communications Inc.     ---
+**  ---                   All rights reserved.                        ---
+**  ---------------------------------------------------------------------
+**
+**  File Description:
+**
+**  Host side I2O (Intelligent I/O) LAN message transport layer.
+**
+**  This program is free software; you can redistribute it and/or modify
+**  it under the terms of the GNU General Public License as published by
+**  the Free Software Foundation; either version 2 of the License, or
+**  (at your option) any later version.
+
+**  This program is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**  GNU General Public License for more details.
+
+**  You should have received a copy of the GNU General Public License
+**  along with this program; if not, write to the Free Software
+**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+** *************************************************************************
+*/
+
+#undef DEBUG
+
+#define RC_LINUX_MODULE
+#include "rclanmtl.h"
+
+#define dprintf kprintf
+
+extern int printk(const char * fmt, ...);
+
+ /* RedCreek LAN device Target ID */
+#define RC_LAN_TARGET_ID  0x10 
+ /* RedCreek's OSM default LAN receive Initiator */
+#define DEFAULT_RECV_INIT_CONTEXT  0xA17  
+
+
+/*
+** I2O message structures
+*/
+
+#define    I2O_TID_SZ                                  12
+#define    I2O_FUNCTION_SZ                             8
+
+/* Transaction Reply Lists (TRL) Control Word structure */
+
+#define    I2O_TRL_FLAGS_SINGLE_FIXED_LENGTH           0x00
+#define    I2O_TRL_FLAGS_SINGLE_VARIABLE_LENGTH        0x40
+#define    I2O_TRL_FLAGS_MULTIPLE_FIXED_LENGTH         0x80
+
+/* LAN Class specific functions */
+
+#define    I2O_LAN_PACKET_SEND                         0x3B
+#define    I2O_LAN_SDU_SEND                            0x3D
+#define    I2O_LAN_RECEIVE_POST                        0x3E
+#define    I2O_LAN_RESET                               0x35
+#define    I2O_LAN_SHUTDOWN                            0x37
+
+/* Private Class specfic function */
+#define    I2O_PRIVATE                                 0xFF
+
+/*  I2O Executive Function Codes.  */
+
+#define    I2O_EXEC_ADAPTER_ASSIGN                     0xB3
+#define    I2O_EXEC_ADAPTER_READ                       0xB2
+#define    I2O_EXEC_ADAPTER_RELEASE                    0xB5
+#define    I2O_EXEC_BIOS_INFO_SET                      0xA5
+#define    I2O_EXEC_BOOT_DEVICE_SET                    0xA7
+#define    I2O_EXEC_CONFIG_VALIDATE                    0xBB
+#define    I2O_EXEC_CONN_SETUP                         0xCA
+#define    I2O_EXEC_DEVICE_ASSIGN                      0xB7
+#define    I2O_EXEC_DEVICE_RELEASE                     0xB9
+#define    I2O_EXEC_HRT_GET                            0xA8
+#define    I2O_EXEC_IOP_CLEAR                          0xBE
+#define    I2O_EXEC_IOP_CONNECT                        0xC9
+#define    I2O_EXEC_IOP_RESET                          0xBD
+#define    I2O_EXEC_LCT_NOTIFY                         0xA2
+#define    I2O_EXEC_OUTBOUND_INIT                      0xA1
+#define    I2O_EXEC_PATH_ENABLE                        0xD3
+#define    I2O_EXEC_PATH_QUIESCE                       0xC5
+#define    I2O_EXEC_PATH_RESET                         0xD7
+#define    I2O_EXEC_STATIC_MF_CREATE                   0xDD
+#define    I2O_EXEC_STATIC_MF_RELEASE                  0xDF
+#define    I2O_EXEC_STATUS_GET                         0xA0
+#define    I2O_EXEC_SW_DOWNLOAD                        0xA9
+#define    I2O_EXEC_SW_UPLOAD                          0xAB
+#define    I2O_EXEC_SW_REMOVE                          0xAD
+#define    I2O_EXEC_SYS_ENABLE                         0xD1
+#define    I2O_EXEC_SYS_MODIFY                         0xC1
+#define    I2O_EXEC_SYS_QUIESCE                        0xC3
+#define    I2O_EXEC_SYS_TAB_SET                        0xA3
+
+
+ /* Init Outbound Q status */
+#define    I2O_EXEC_OUTBOUND_INIT_IN_PROGRESS          0x01
+#define    I2O_EXEC_OUTBOUND_INIT_REJECTED             0x02
+#define    I2O_EXEC_OUTBOUND_INIT_FAILED               0x03
+#define    I2O_EXEC_OUTBOUND_INIT_COMPLETE             0x04
+
+
+#define    I2O_UTIL_NOP                                0x00
+
+
+/* I2O Get Status State values */
+
+#define    I2O_IOP_STATE_INITIALIZING                  0x01
+#define    I2O_IOP_STATE_RESET                         0x02
+#define    I2O_IOP_STATE_HOLD                          0x04
+#define    I2O_IOP_STATE_READY                         0x05
+#define    I2O_IOP_STATE_OPERATIONAL                   0x08
+#define    I2O_IOP_STATE_FAILED                        0x10
+#define    I2O_IOP_STATE_FAULTED                       0x11
+
+
+/* Defines for Request Status Codes:  Table 3-1 Reply Status Codes.  */
+
+#define    I2O_REPLY_STATUS_SUCCESS                    0x00
+#define    I2O_REPLY_STATUS_ABORT_DIRTY                0x01
+#define    I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER     0x02
+#define    I2O_REPLY_STATUS_ABORT_PARTIAL_TRANSFER     0x03
+#define    I2O_REPLY_STATUS_ERROR_DIRTY                0x04
+#define    I2O_REPLY_STATUS_ERROR_NO_DATA_TRANSFER     0x05
+#define    I2O_REPLY_STATUS_ERROR_PARTIAL_TRANSFER     0x06
+#define    I2O_REPLY_STATUS_PROCESS_ABORT_DIRTY        0x07
+#define    I2O_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER   0x08
+#define    I2O_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER   0x09
+#define    I2O_REPLY_STATUS_TRANSACTION_ERROR          0x0A
+#define    I2O_REPLY_STATUS_PROGRESS_REPORT            0x80
+
+
+/* DetailedStatusCode defines for ALL messages: Table 3-2 Detailed Status Codes.*/
+
+#define    I2O_DETAIL_STATUS_SUCCESS                        0x0000
+#define    I2O_DETAIL_STATUS_BAD_KEY                        0x0001
+#define    I2O_DETAIL_STATUS_CHAIN_BUFFER_TOO_LARGE         0x0002
+#define    I2O_DETAIL_STATUS_DEVICE_BUSY                    0x0003
+#define    I2O_DETAIL_STATUS_DEVICE_LOCKED                  0x0004
+#define    I2O_DETAIL_STATUS_DEVICE_NOT_AVAILABLE           0x0005
+#define    I2O_DETAIL_STATUS_DEVICE_RESET                   0x0006
+#define    I2O_DETAIL_STATUS_INAPPROPRIATE_FUNCTION         0x0007
+#define    I2O_DETAIL_STATUS_INSUFFICIENT_RESOURCE_HARD     0x0008
+#define    I2O_DETAIL_STATUS_INSUFFICIENT_RESOURCE_SOFT     0x0009
+#define    I2O_DETAIL_STATUS_INVALID_INITIATOR_ADDRESS      0x000A
+#define    I2O_DETAIL_STATUS_INVALID_MESSAGE_FLAGS          0x000B
+#define    I2O_DETAIL_STATUS_INVALID_OFFSET                 0x000C
+#define    I2O_DETAIL_STATUS_INVALID_PARAMETER              0x000D
+#define    I2O_DETAIL_STATUS_INVALID_REQUEST                0x000E
+#define    I2O_DETAIL_STATUS_INVALID_TARGET_ADDRESS         0x000F
+#define    I2O_DETAIL_STATUS_MESSAGE_TOO_LARGE              0x0010
+#define    I2O_DETAIL_STATUS_MESSAGE_TOO_SMALL              0x0011
+#define    I2O_DETAIL_STATUS_MISSING_PARAMETER              0x0012
+#define    I2O_DETAIL_STATUS_NO_SUCH_PAGE                   0x0013
+#define    I2O_DETAIL_STATUS_REPLY_BUFFER_FULL              0x0014
+#define    I2O_DETAIL_STATUS_TCL_ERROR                      0x0015
+#define    I2O_DETAIL_STATUS_TIMEOUT                        0x0016
+#define    I2O_DETAIL_STATUS_UNKNOWN_ERROR                  0x0017
+#define    I2O_DETAIL_STATUS_UNKNOWN_FUNCTION               0x0018
+#define    I2O_DETAIL_STATUS_UNSUPPORTED_FUNCTION           0x0019
+#define    I2O_DETAIL_STATUS_UNSUPPORTED_VERSION            0x001A
+
+ /* I2O msg header defines for VersionOffset */
+#define I2OMSGVER_1_5   0x0001
+#define SGL_OFFSET_0    I2OMSGVER_1_5
+#define SGL_OFFSET_4    (0x0040 | I2OMSGVER_1_5)
+#define TRL_OFFSET_5    (0x0050 | I2OMSGVER_1_5)
+#define TRL_OFFSET_6    (0x0060 | I2OMSGVER_1_5)
+
+ /* I2O msg header defines for MsgFlags */
+#define MSG_STATIC      0x0100
+#define MSG_64BIT_CNTXT 0x0200
+#define MSG_MULTI_TRANS 0x1000
+#define MSG_FAIL        0x2000
+#define MSG_LAST        0x4000
+#define MSG_REPLY       0x8000
+
+  /* normal LAN request message MsgFlags and VersionOffset (0x1041) */
+#define LAN_MSG_REQST  (MSG_MULTI_TRANS | SGL_OFFSET_4)
+
+ /* minimum size msg */
+#define THREE_WORD_MSG_SIZE 0x00030000
+#define FOUR_WORD_MSG_SIZE  0x00040000
+#define FIVE_WORD_MSG_SIZE  0x00050000
+#define SIX_WORD_MSG_SIZE   0x00060000
+#define SEVEN_WORD_MSG_SIZE 0x00070000
+#define EIGHT_WORD_MSG_SIZE 0x00080000
+#define NINE_WORD_MSG_SIZE  0x00090000
+
+/* Special TID Assignments */
+
+#define I2O_IOP_TID   0
+#define I2O_HOST_TID  1
+
+ /* RedCreek I2O private message codes */
+#define RC_PRIVATE_GET_MAC_ADDR     0x0001/**/ /* OBSOLETE */
+#define RC_PRIVATE_SET_MAC_ADDR     0x0002
+#define RC_PRIVATE_GET_NIC_STATS    0x0003
+#define RC_PRIVATE_GET_LINK_STATUS  0x0004
+#define RC_PRIVATE_SET_LINK_SPEED   0x0005
+#define RC_PRIVATE_SET_IP_AND_MASK  0x0006
+/* #define RC_PRIVATE_GET_IP_AND_MASK  0x0007 */ /* OBSOLETE */
+#define RC_PRIVATE_GET_LINK_SPEED   0x0008
+#define RC_PRIVATE_GET_FIRMWARE_REV 0x0009
+/* #define RC_PRIVATE_GET_MAC_ADDR     0x000A *//**/
+#define RC_PRIVATE_GET_IP_AND_MASK  0x000B /**/
+#define RC_PRIVATE_DEBUG_MSG        0x000C
+#define RC_PRIVATE_REPORT_DRIVER_CAPABILITY  0x000D
+#define RC_PRIVATE_SET_PROMISCUOUS_MODE  0x000e
+#define RC_PRIVATE_GET_PROMISCUOUS_MODE  0x000f
+#define RC_PRIVATE_SET_BROADCAST_MODE    0x0010
+#define RC_PRIVATE_GET_BROADCAST_MODE    0x0011
+
+#define RC_PRIVATE_REBOOT           0x00FF
+
+
+/* I2O message header */
+typedef struct _I2O_MESSAGE_FRAME 
+{
+    U8                          VersionOffset;
+    U8                          MsgFlags;
+    U16                         MessageSize;
+    BF                          TargetAddress:I2O_TID_SZ;
+    BF                          InitiatorAddress:I2O_TID_SZ;
+    BF                          Function:I2O_FUNCTION_SZ;
+    U32                         InitiatorContext;
+    /* SGL[] */ 
+}
+I2O_MESSAGE_FRAME, *PI2O_MESSAGE_FRAME;
+
+
+ /* assumed a 16K minus 256 byte space for outbound queue message frames */
+#define MSG_FRAME_SIZE  512
+#define NMBR_MSG_FRAMES 30
+
+/*
+**  Message Unit CSR definitions for RedCreek PCI45 board
+*/
+typedef struct tag_rcatu 
+{
+    volatile unsigned long APICRegSel;  /* APIC Register Select */
+    volatile unsigned long reserved0;
+    volatile unsigned long APICWinReg;  /* APIC Window Register */
+    volatile unsigned long reserved1;
+    volatile unsigned long InMsgReg0;   /* inbound message register 0 */
+    volatile unsigned long InMsgReg1;   /* inbound message register 1 */
+    volatile unsigned long OutMsgReg0;  /* outbound message register 0 */
+    volatile unsigned long OutMsgReg1;  /* outbound message register 1 */
+    volatile unsigned long InDoorReg;   /* inbound doorbell register */
+    volatile unsigned long InIntStat;   /* inbound interrupt status register */
+    volatile unsigned long InIntMask;   /* inbound interrupt mask register */
+    volatile unsigned long OutDoorReg;  /* outbound doorbell register */
+    volatile unsigned long OutIntStat;  /* outbound interrupt status register */
+    volatile unsigned long OutIntMask;  /* outbound interrupt mask register */
+    volatile unsigned long reserved2;
+    volatile unsigned long reserved3;
+    volatile unsigned long InQueue;     /* inbound queue port */
+    volatile unsigned long OutQueue;    /* outbound queue port */
+    volatile unsigned long reserved4;
+    volatile unsigned long reserver5;
+    /* RedCreek extension */
+    volatile unsigned long EtherMacLow;
+    volatile unsigned long EtherMacHi;
+    volatile unsigned long IPaddr;
+    volatile unsigned long IPmask;
+}
+ATU, *PATU;
+
+ /* 
+ ** typedef PAB
+ **
+ ** PCI Adapter Block - holds instance specific information and is located
+ ** in a reserved space at the start of the message buffer allocated by user.
+ */
+typedef struct
+{
+    PATU             p_atu;                /* ptr to  ATU register block */
+    PU8              pPci45LinBaseAddr;
+    PU8              pLinOutMsgBlock;
+    U32              outMsgBlockPhyAddr; 
+    PFNTXCALLBACK    pTransCallbackFunc;
+    PFNRXCALLBACK    pRecvCallbackFunc;
+    PFNCALLBACK      pRebootCallbackFunc;
+    PFNCALLBACK      pCallbackFunc;
+    U16              IOPState;
+    U16              InboundMFrameSize;
+}
+PAB, *PPAB;
+
+ /* 
+ ** in reserved space right after PAB in host memory is area for returning
+ ** values from card 
+ */
+
+ /* 
+ ** Array of pointers to PCI Adapter Blocks.
+ ** Indexed by a zero based (0-31) interface number.
+ */ 
+#define MAX_ADAPTERS 32
+static PPAB  PCIAdapterBlock[MAX_ADAPTERS] = 
+{
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+
+/*
+** typedef NICSTAT
+**
+** Data structure for NIC statistics retruned from PCI card.  Data copied from
+** here to user allocated RCLINKSTATS (see rclanmtl.h) structure.
+*/
+typedef struct tag_NicStat 
+{
+    unsigned long   TX_good; 
+    unsigned long   TX_maxcol;
+    unsigned long   TX_latecol;
+    unsigned long   TX_urun;
+    unsigned long   TX_crs;         /* lost carrier sense */
+    unsigned long   TX_def;         /* transmit deferred */
+    unsigned long   TX_singlecol;   /* single collisions */
+    unsigned long   TX_multcol;
+    unsigned long   TX_totcol;
+    unsigned long   Rcv_good;
+    unsigned long   Rcv_CRCerr;
+    unsigned long   Rcv_alignerr;
+    unsigned long   Rcv_reserr;     /* rnr'd pkts */
+    unsigned long   Rcv_orun;
+    unsigned long   Rcv_cdt;
+    unsigned long   Rcv_runt;
+    unsigned long   dump_status;    /* last field directly from the chip */
+} 
+NICSTAT, *P_NICSTAT;
+ 
+
+#define DUMP_DONE   0x0000A005      /* completed statistical dump */
+#define DUMP_CLEAR  0x0000A007      /* completed stat dump and clear counters */
+
+
+static volatile int msgFlag;
+
+
+/* local function prototypes */
+static void ProcessOutboundI2OMsg(PPAB pPab, U32 phyMsgAddr);
+static int FillI2OMsgSGLFromTCB(PU32 pMsg, PRCTCB pXmitCntrlBlock);
+static int GetI2OStatus(PPAB pPab);
+static int SendI2OOutboundQInitMsg(PPAB pPab);
+static int SendEnableSysMsg(PPAB pPab);
+
+
+/* 1st 100h bytes of message block is reserved for messenger instance */
+#define ADAPTER_BLOCK_RESERVED_SPACE 0x100
+
+/*
+** =========================================================================
+** RCInitI2OMsgLayer()
+**
+** Initialize the RedCreek I2O Module and adapter.
+**
+** Inputs:  AdapterID - interface number from 0 to 15
+**          pciBaseAddr - virual base address of PCI (set by BIOS)
+**          p_msgbuf - virual address to private message block (min. 16K)
+**          p_phymsgbuf - physical address of private message block
+**          TransmitCallbackFunction - address of transmit callback function
+**          ReceiveCallbackFunction  - address of receive  callback function
+**
+** private message block is allocated by user.  It must be in locked pages.
+** p_msgbuf and p_phymsgbuf point to the same location.  Must be contigous
+** memory block of a minimum of 16K byte and long word aligned.
+** =========================================================================
+*/
+RC_RETURN
+RCInitI2OMsgLayer(U16 AdapterID, U32 pciBaseAddr, 
+                  PU8 p_msgbuf,  PU8 p_phymsgbuf,
+                  PFNTXCALLBACK  TransmitCallbackFunction,
+                  PFNRXCALLBACK  ReceiveCallbackFunction,
+                  PFNCALLBACK    RebootCallbackFunction)
+{
+    int result;
+    PPAB pPab; 
+    
+#ifdef DEBUG
+    kprintf("InitI2O: Adapter:0x%04.4ux ATU:0x%08.8ulx msgbuf:0x%08.8ulx phymsgbuf:0x%08.8ulx\n"
+            "TransmitCallbackFunction:0x%08.8ulx  ReceiveCallbackFunction:0x%08.8ulx\n",
+            AdapterID, pciBaseAddr, p_msgbuf, p_phymsgbuf, TransmitCallbackFunction, ReceiveCallbackFunction);
+#endif /* DEBUG */
+
+    
+    /* Check if this interface already initialized - if so, shut it down */
+    if (PCIAdapterBlock[AdapterID] != NULL)
+    {
+        printk("PCIAdapterBlock[%d]!=NULL\n", AdapterID);
+//        RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
+        PCIAdapterBlock[AdapterID] = NULL;
+    }
+
+    /* 
+    ** store adapter instance values in adapter block.
+    ** Adapter block is at beginning of message buffer
+    */  
+    pPab = (PPAB)p_msgbuf;
+    
+    pPab->p_atu = (PATU)pciBaseAddr;
+    pPab->pPci45LinBaseAddr =  (PU8)pciBaseAddr;
+    
+    /* Set outbound message frame addr - skip over Adapter Block */
+    pPab->outMsgBlockPhyAddr = (U32)(p_phymsgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
+    pPab->pLinOutMsgBlock    = (PU8)(p_msgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
+
+    /* store callback function addresses */
+    pPab->pTransCallbackFunc = TransmitCallbackFunction;
+    pPab->pRecvCallbackFunc  = ReceiveCallbackFunction;
+    pPab->pRebootCallbackFunc  = RebootCallbackFunction;
+    pPab->pCallbackFunc  = (PFNCALLBACK)NULL;
+
+    /*
+    ** Initialize I2O IOP
+    */
+    result = GetI2OStatus(pPab);
+    
+    if (result != RC_RTN_NO_ERROR)
+        return result;
+
+    if (pPab->IOPState == I2O_IOP_STATE_OPERATIONAL)
+    {
+        printk("pPab->IOPState == op: resetting adapter\n");
+        RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
+    }
+        
+    result = SendI2OOutboundQInitMsg(pPab);
+    
+    if (result != RC_RTN_NO_ERROR)
+        return result;
+
+    result = SendEnableSysMsg(pPab);
+   
+    if (result != RC_RTN_NO_ERROR)
+        return result;
+   
+    PCIAdapterBlock[AdapterID] = pPab;
+    return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** Disable and Enable I2O interrupts.  I2O interrupts are enabled at Init time
+** but can be disabled and re-enabled through these two function calls.
+** Packets will still be put into any posted received buffers and packets will
+** be sent through RCI2OSendPacket() functions.  Disabling I2O interrupts
+** will prevent hardware interrupt to host even though the outbound I2O msg
+** queue is not emtpy.
+** =========================================================================
+*/
+#define i960_OUT_POST_Q_INT_BIT        0x0008 /* bit set masks interrupts */
+
+RC_RETURN RCDisableI2OInterrupts(U16 AdapterID)
+{
+    PPAB pPab;
+
+
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+        
+    pPab->p_atu->OutIntMask |= i960_OUT_POST_Q_INT_BIT;
+
+    return RC_RTN_NO_ERROR;
+}
+
+RC_RETURN RCEnableI2OInterrupts(U16 AdapterID)
+{
+    PPAB pPab;
+
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    pPab->p_atu->OutIntMask &= ~i960_OUT_POST_Q_INT_BIT;
+    
+    return RC_RTN_NO_ERROR;
+
+}
+
+
+/*
+** =========================================================================
+** RCI2OSendPacket()
+** =========================================================================
+*/
+RC_RETURN
+RCI2OSendPacket(U16 AdapterID, U32 InitiatorContext, PRCTCB pTransCtrlBlock)
+{
+    U32 msgOffset;
+    PU32 pMsg;
+    int size;
+    PPAB pPab;
+
+#ifdef DEBUG
+    kprintf("RCI2OSendPacket()...\n");
+#endif /* DEBUG */
+    
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    /* get Inbound free Q entry - reading from In Q gets free Q entry */
+    /* offset to Msg Frame in PCI msg block */
+
+    msgOffset = pPab->p_atu->InQueue; 
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("RCI2OSendPacket(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+        
+    /* calc virual address of msg - virual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    size = FillI2OMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);
+
+    if (size == -1) /* error processing TCB - send NOP msg */
+    {
+#ifdef DEBUG
+        kprintf("RCI2OSendPacket(): Error Rrocess TCB!\n");
+#endif /* DEBUG */
+        pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
+        pMsg[1] = I2O_UTIL_NOP << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+        return RC_RTN_TCB_ERROR;
+    }
+    else /* send over msg header */
+    {    
+        pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
+        pMsg[1] = I2O_LAN_PACKET_SEND << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+        pMsg[2] = InitiatorContext;
+        pMsg[3] = 0;  /* batch reply */
+        /* post to Inbound Post Q */   
+        pPab->p_atu->InQueue = msgOffset;
+        return RC_RTN_NO_ERROR;
+    }
+}
+ 
+
+/*
+** =========================================================================
+** RCI2OPostRecvBuffer()
+**
+** inputs:  pBufrCntrlBlock - pointer to buffer control block
+**
+** returns TRUE if successful in sending message, else FALSE.
+** =========================================================================
+*/
+RC_RETURN
+RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransCtrlBlock)
+{
+    U32 msgOffset;
+    PU32 pMsg;
+    int size;
+    PPAB pPab;
+
+#ifdef DEBUG
+    kprintf("RCPostRecvBuffers()...\n");
+#endif /* DEBUG */
+    
+    /* search for DeviceHandle */
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+
+    /* get Inbound free Q entry - reading from In Q gets free Q entry */
+    /* offset to Msg Frame in PCI msg block */
+    msgOffset = pPab->p_atu->InQueue; 
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("RCPostRecvBuffers(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+   
+    }
+    /* calc virual address of msg - virual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    size = FillI2OMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);
+
+    if (size == -1) /* error prcessing TCB - send 3 DWORD private msg == NOP */
+    {
+#ifdef DEBUG
+        kprintf("RCPostRecvBuffers(): Error Processing TCB! size = %d\n", size);
+#endif /* DEBUG */
+        pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
+        pMsg[1] = I2O_UTIL_NOP << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+        /* post to Post Q */   
+        pPab->p_atu->InQueue = msgOffset;
+        return RC_RTN_TCB_ERROR;
+    }
+    else /* send over size msg header */
+    {    
+        pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
+        pMsg[1] = I2O_LAN_RECEIVE_POST << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+        pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+        pMsg[3] = *(PU32)pTransCtrlBlock; /* number of packet buffers */
+        /* post to Post Q */   
+        pPab->p_atu->InQueue = msgOffset;
+        return RC_RTN_NO_ERROR;
+    }
+}
+
+
+/*
+** =========================================================================
+** RCProcI2OMsgQ()
+**
+** Process I2O outbound message queue until empty.
+** =========================================================================
+*/
+void 
+RCProcI2OMsgQ(U16 AdapterID)
+{
+    U32 phyAddrMsg;
+    PU8 p8Msg;
+    PU32 p32;
+    U16 count;
+    PPAB pPab;
+    unsigned char debug_msg[20];
+    
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return;
+    
+    phyAddrMsg = pPab->p_atu->OutQueue;
+
+    while (phyAddrMsg != 0xFFFFFFFF)
+    {
+        p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
+        p32 = (PU32)p8Msg;
+        
+        //printk(" msg: 0x%x  0x%x \n", p8Msg[7], p32[5]);
+
+        /* 
+        ** Send Packet Reply Msg
+        */
+        if (I2O_LAN_PACKET_SEND == p8Msg[7])  /* function code byte */
+        {
+            count = *(PU16)(p8Msg+2);
+            count -= p8Msg[0] >> 4;
+            /* status, count, context[], adapter */
+            (*pPab->pTransCallbackFunc)(p8Msg[19], count, p32+5, AdapterID);
+        }             
+        /* 
+        ** Receive Packet Reply Msg */
+        else if (I2O_LAN_RECEIVE_POST == p8Msg[7])
+        {
+#ifdef DEBUG    
+            kprintf("I2O_RECV_REPLY pPab:0x%08.8ulx p8Msg:0x%08.8ulx p32:0x%08.8ulx\n", pPab, p8Msg, p32);
+            kprintf("msg: 0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+                    p32[0], p32[1], p32[2], p32[3]);
+            kprintf("     0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+                    p32[4], p32[5], p32[6], p32[7]);
+            kprintf("     0x%08.8ulx:0X%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+                    p32[8], p32[9], p32[10], p32[11]);
+#endif
+            /*  status, count, buckets remaining, packetParmBlock, adapter */
+            (*pPab->pRecvCallbackFunc)(p8Msg[19], p8Msg[12], p32[5], p32+6, AdapterID);
+      
+      
+        }
+        else if (I2O_LAN_RESET == p8Msg[7] || I2O_LAN_SHUTDOWN == p8Msg[7])
+        {
+            if (pPab->pCallbackFunc)
+            {
+                (*pPab->pCallbackFunc)(p8Msg[19],0,0,AdapterID);
+            }
+            else
+            {
+                pPab->pCallbackFunc = (PFNCALLBACK) 1;
+            }
+            //PCIAdapterBlock[AdapterID] = 0;
+        }
+        else if (I2O_PRIVATE == p8Msg[7])
+        {
+            //printk("i2o private 0x%x, 0x%x \n", p8Msg[7], p32[5]);
+            switch (p32[5])
+            {
+            case RC_PRIVATE_DEBUG_MSG:
+                msgFlag = 1;
+                /*printk("Received I2O_PRIVATE msg\n");*/
+                debug_msg[15]  = (p32[6]&0xff000000) >> 24;
+                debug_msg[14]  = (p32[6]&0x00ff0000) >> 16;
+                debug_msg[13]  = (p32[6]&0x0000ff00) >> 8;
+                debug_msg[12]  = (p32[6]&0x000000ff);
+
+                debug_msg[11]  = (p32[7]&0xff000000) >> 24;
+                debug_msg[10]  = (p32[7]&0x00ff0000) >> 16;
+                debug_msg[ 9]  = (p32[7]&0x0000ff00) >> 8;
+                debug_msg[ 8]  = (p32[7]&0x000000ff);
+
+                debug_msg[ 7]  = (p32[8]&0xff000000) >> 24;
+                debug_msg[ 6]  = (p32[8]&0x00ff0000) >> 16;
+                debug_msg[ 5] = (p32[8]&0x0000ff00) >> 8;
+                debug_msg[ 4] = (p32[8]&0x000000ff);
+
+                debug_msg[ 3] = (p32[9]&0xff000000) >> 24;
+                debug_msg[ 2] = (p32[9]&0x00ff0000) >> 16;
+                debug_msg[ 1] = (p32[9]&0x0000ff00) >> 8;
+                debug_msg[ 0] = (p32[9]&0x000000ff);
+
+                debug_msg[16] = '\0';
+                printk (debug_msg);
+                break;
+            case RC_PRIVATE_REBOOT:
+                printk("Adapter reboot initiated...\n");
+                if (pPab->pRebootCallbackFunc)
+                {
+                    (*pPab->pRebootCallbackFunc)(0,0,0,AdapterID);
+                }
+                break;
+            default:
+                printk("Unknown private I2O msg received: 0x%x\n",
+                       p32[5]);
+                break;
+            }
+        }
+
+        /* 
+        ** Process other Msg's
+        */
+        else
+        {
+            ProcessOutboundI2OMsg(pPab, phyAddrMsg);
+        }
+        
+        /* return MFA to outbound free Q*/
+        pPab->p_atu->OutQueue = phyAddrMsg;
+    
+        /* any more msgs? */
+        phyAddrMsg = pPab->p_atu->OutQueue;
+    }
+}
+
+
+/*
+** =========================================================================
+**  Returns LAN interface statistical counters to space provided by caller at
+**  StatsReturnAddr.  Returns 0 if success, else RC_RETURN code.
+**  This function will call the WaitCallback function provided by
+**  user while waiting for card to respond.
+** =========================================================================
+*/
+RC_RETURN
+RCGetLinkStatistics(U16 AdapterID, 
+                    P_RCLINKSTATS StatsReturnAddr,
+                    PFNWAITCALLBACK WaitCallback)
+{
+    U32 msgOffset;
+    volatile U32 timeout;
+    volatile PU32 pMsg;
+    volatile PU32 p32, pReturnAddr;
+    P_NICSTAT pStats;
+    int i;
+    PPAB pPab;
+
+/*kprintf("Get82558Stats() StatsReturnAddr:0x%08.8ulx\n", StatsReturnAddr);*/
+
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    msgOffset = pPab->p_atu->InQueue;
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("Get8255XStats(): Inbound Free Q empty!\n");
+#endif
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virual address of msg - virual already mapped to physical */
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+/*dprintf("Get82558Stats - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
+/*dprintf("Get82558Stats - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
+
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = 0x112; /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_NIC_STATS;
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+
+    pStats = (P_NICSTAT)p32;
+    pStats->dump_status = 0xFFFFFFFF;
+
+    /* post to Inbound Post Q */
+    pPab->p_atu->InQueue = msgOffset;
+
+    timeout = 100000;
+    while (1)
+    {
+        if (WaitCallback)
+            (*WaitCallback)();
+        
+        for (i = 0; i < 1000; i++)
+            ;
+
+        if (pStats->dump_status != 0xFFFFFFFF)
+            break;
+
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("RCGet82558Stats() Timeout waiting for NIC statistics\n");
+#endif
+            return RC_RTN_MSG_REPLY_TIMEOUT;
+        }
+    }
+    
+    pReturnAddr = (PU32)StatsReturnAddr;
+    
+    /* copy Nic stats to user's structure */
+    for (i = 0; i < (int) sizeof(RCLINKSTATS) / 4; i++)
+        pReturnAddr[i] = p32[i];
+   
+    return RC_RTN_NO_ERROR;     
+}
+
+
+/*
+** =========================================================================
+** Get82558LinkStatus()
+** =========================================================================
+*/
+RC_RETURN
+RCGetLinkStatus(U16 AdapterID, PU32 ReturnAddr, PFNWAITCALLBACK WaitCallback)
+{
+    U32 msgOffset;
+    volatile U32 timeout;
+    volatile PU32 pMsg;
+    volatile PU32 p32;
+    PPAB pPab;
+
+/*kprintf("Get82558LinkStatus() ReturnPhysAddr:0x%08.8ulx\n", ReturnAddr);*/
+
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    msgOffset = pPab->p_atu->InQueue;
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        dprintf("Get82558LinkStatus(): Inbound Free Q empty!\n");
+#endif
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virual address of msg - virual already mapped to physical */
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+/*dprintf("Get82558LinkStatus - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
+/*dprintf("Get82558LinkStatus - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
+
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = 0x112; /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_STATUS;
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    *p32 = 0xFFFFFFFF;
+    
+    /* post to Inbound Post Q */
+    pPab->p_atu->InQueue = msgOffset;
+
+    timeout = 100000;
+    while (1)
+    {
+        U32 i;
+
+        if (WaitCallback)
+            (*WaitCallback)();
+        
+        for (i = 0; i < 1000; i++)
+            ;
+
+        if (*p32 != 0xFFFFFFFF)
+            break;
+
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("Timeout waiting for link status\n");
+#endif    
+            return RC_RTN_MSG_REPLY_TIMEOUT;
+        }
+    }
+    
+    *ReturnAddr = *p32; /* 1 = up 0 = down */
+    
+    return RC_RTN_NO_ERROR;
+    
+}
+
+/*
+** =========================================================================
+** RCGetMAC()
+**
+** get the MAC address the adapter is listening for in non-promiscous mode.
+** MAC address is in media format.
+** =========================================================================
+*/
+RC_RETURN
+RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback)
+{
+    unsigned i, timeout;
+    U32      off;
+    PU32     p;
+    U32      temp[2];
+    PPAB     pPab;
+    PATU     p_atu;
+    
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    p_atu = pPab->p_atu;
+
+    p_atu->EtherMacLow = 0;     /* first zero return data */
+    p_atu->EtherMacHi = 0;
+    
+    off = p_atu->InQueue;   /* get addresss of message */
+ 
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+
+    p = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+#ifdef RCDEBUG
+    printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", 
+           (uint)p_atu, (uint)off, (uint)p);
+#endif /* RCDEBUG */
+    /* setup private message */
+    p[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
+    p[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    p[2] = 0;               /* initiator context */
+    p[3] = 0x218;           /* transaction context */
+    p[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_MAC_ADDR;
+
+
+    p_atu->InQueue = off;   /* send it to the I2O device */
+#ifdef RCDEBUG
+    printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", 
+           (uint)p_atu, (uint)off, (uint)p);
+#endif /* RCDEBUG */
+
+    /* wait for the rcpci45 board to update the info */
+    timeout = 1000000;
+    while (0 == p_atu->EtherMacLow) 
+    {
+        if (WaitCallback)
+            (*WaitCallback)();
+    
+        for (i = 0; i < 1000; i++)
+            ;
+
+        if (!timeout--)
+        {
+            printk("rc_getmac: Timeout\n");
+            return RC_RTN_MSG_REPLY_TIMEOUT;
+        }
+    }
+    
+    /* read the mac address  */
+    temp[0] = p_atu->EtherMacLow;
+    temp[1] = p_atu->EtherMacHi;
+    memcpy((char *)mac, (char *)temp, 6);
+
+
+#ifdef RCDEBUG
+//    printk("rc_getmac: 0x%X\n", ptr);
+#endif /* RCDEBUG */
+
+    return RC_RTN_NO_ERROR;
+}
+
+ 
+/*
+** =========================================================================
+** RCSetMAC()
+**
+** set MAC address the adapter is listening for in non-promiscous mode.
+** MAC address is in media format.
+** =========================================================================
+*/
+RC_RETURN
+RCSetMAC(U16 AdapterID, PU8 mac)
+{
+    U32  off;
+    PU32 pMsg;
+    PPAB pPab;
+
+
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_MAC_ADDR;
+    pMsg[5] = *(unsigned *)mac;  /* first four bytes */
+    pMsg[6] = *(unsigned *)(mac + 4); /* last two bytes */
+    
+    pPab->p_atu->InQueue = off;   /* send it to the I2O device */
+
+    return RC_RTN_NO_ERROR ;
+}
+
+
+/*
+** =========================================================================
+** RCSetLinkSpeed()
+**
+** set ethernet link speed. 
+** input: speedControl - determines action to take as follows
+**          0 = reset and auto-negotiate (NWay)
+**          1 = Full Duplex 100BaseT
+**          2 = Half duplex 100BaseT
+**          3 = Full Duplex  10BaseT
+**          4 = Half duplex  10BaseT
+**          all other values are ignore (do nothing)
+** =========================================================================
+*/
+RC_RETURN
+RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode)
+{
+    U32  off;
+    PU32 pMsg;
+    PPAB pPab;
+
+    
+    pPab =PCIAdapterBlock[AdapterID];
+     
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_LINK_SPEED;
+    pMsg[5] = LinkSpeedCode;     /* link speed code */
+
+    pPab->p_atu->InQueue = off;   /* send it to the I2O device */
+
+    return RC_RTN_NO_ERROR ;
+}
+/*
+** =========================================================================
+** RCSetPromiscuousMode()
+**
+** Defined values for Mode:
+**  0 - turn off promiscuous mode
+**  1 - turn on  promiscuous mode
+**
+** =========================================================================
+*/
+RC_RETURN
+RCSetPromiscuousMode(U16 AdapterID, U16 Mode)
+{
+    U32  off;
+    PU32 pMsg;
+    PPAB pPab;
+    
+    pPab =PCIAdapterBlock[AdapterID];
+     
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_PROMISCUOUS_MODE;
+    pMsg[5] = Mode;     /* promiscuous mode setting */
+
+    pPab->p_atu->InQueue = off;   /* send it to the device */
+
+    return RC_RTN_NO_ERROR ;
+}
+/*
+** =========================================================================
+** RCGetPromiscuousMode()
+**
+** get promiscuous mode setting
+**
+** Possible return values placed in pMode:
+**  0 = promisuous mode not set
+**  1 = promisuous mode is set
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetPromiscuousMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback)
+{
+    U32 msgOffset, timeout;
+    PU32 pMsg;
+    volatile PU32 p32;
+    PPAB pPab;
+
+    pPab =PCIAdapterBlock[AdapterID];
+
+
+    msgOffset = pPab->p_atu->InQueue;
+
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+        kprintf("RCGetLinkSpeed(): Inbound Free Q empty!\n");
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virtual address of msg - virtual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    /* virtual pointer to return buffer - clear first two dwords */
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0xff;
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_PROMISCUOUS_MODE;
+    /* phys address to return status - area right after PAB */
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+   
+    /* post to Inbound Post Q */   
+
+    pPab->p_atu->InQueue = msgOffset;
+
+    /* wait for response */
+    timeout = 1000000;
+    while(1)
+    {
+        int i;
+        
+        if (WaitCallback)
+            (*WaitCallback)();
+
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0] != 0xff)
+            break;
+            
+        if (!timeout--)
+        {
+            kprintf("Timeout waiting for promiscuous mode from adapter\n");
+            kprintf("0x%08.8ulx\n", p32[0]);
+            return RC_RTN_NO_LINK_SPEED;
+        }
+    }
+
+    /* get mode */
+    *pMode = (U8)((volatile PU8)p32)[0] & 0x0f;
+
+    return RC_RTN_NO_ERROR;
+}
+/*
+** =========================================================================
+** RCSetBroadcastMode()
+**
+** Defined values for Mode:
+**  0 - turn off promiscuous mode
+**  1 - turn on  promiscuous mode
+**
+** =========================================================================
+*/
+RC_RETURN
+RCSetBroadcastMode(U16 AdapterID, U16 Mode)
+{
+    U32  off;
+    PU32 pMsg;
+    PPAB pPab;
+    
+    pPab =PCIAdapterBlock[AdapterID];
+     
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_BROADCAST_MODE;
+    pMsg[5] = Mode;     /* promiscuous mode setting */
+
+    pPab->p_atu->InQueue = off;   /* send it to the device */
+
+    return RC_RTN_NO_ERROR ;
+}
+/*
+** =========================================================================
+** RCGetBroadcastMode()
+**
+** get promiscuous mode setting
+**
+** Possible return values placed in pMode:
+**  0 = promisuous mode not set
+**  1 = promisuous mode is set
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetBroadcastMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback)
+{
+    U32 msgOffset, timeout;
+    PU32 pMsg;
+    volatile PU32 p32;
+    PPAB pPab;
+
+    pPab =PCIAdapterBlock[AdapterID];
+
+
+    msgOffset = pPab->p_atu->InQueue;
+
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+        kprintf("RCGetLinkSpeed(): Inbound Free Q empty!\n");
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virtual address of msg - virtual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    /* virtual pointer to return buffer - clear first two dwords */
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0xff;
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_BROADCAST_MODE;
+    /* phys address to return status - area right after PAB */
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+   
+    /* post to Inbound Post Q */   
+
+    pPab->p_atu->InQueue = msgOffset;
+
+    /* wait for response */
+    timeout = 1000000;
+    while(1)
+    {
+        int i;
+        
+        if (WaitCallback)
+            (*WaitCallback)();
+
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0] != 0xff)
+            break;
+            
+        if (!timeout--)
+        {
+            kprintf("Timeout waiting for promiscuous mode from adapter\n");
+            kprintf("0x%08.8ulx\n", p32[0]);
+            return RC_RTN_NO_LINK_SPEED;
+        }
+    }
+
+    /* get mode */
+    *pMode = (U8)((volatile PU8)p32)[0] & 0x0f;
+
+    return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** RCGetLinkSpeed()
+**
+** get ethernet link speed. 
+**
+** 0 = Unknown
+** 1 = Full Duplex 100BaseT
+** 2 = Half duplex 100BaseT
+** 3 = Full Duplex  10BaseT
+** 4 = Half duplex  10BaseT
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback)
+{
+    U32 msgOffset, timeout;
+    PU32 pMsg;
+    volatile PU32 p32;
+    U8 IOPLinkSpeed;
+    PPAB pPab;
+
+    pPab =PCIAdapterBlock[AdapterID];
+
+
+    msgOffset = pPab->p_atu->InQueue;
+
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+        kprintf("RCGetLinkSpeed(): Inbound Free Q empty!\n");
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virtual address of msg - virtual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    /* virtual pointer to return buffer - clear first two dwords */
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0xff;
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_SPEED;
+    /* phys address to return status - area right after PAB */
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+   
+    /* post to Inbound Post Q */   
+
+    pPab->p_atu->InQueue = msgOffset;
+
+    /* wait for response */
+    timeout = 1000000;
+    while(1)
+    {
+        int i;
+        
+        if (WaitCallback)
+            (*WaitCallback)();
+
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0] != 0xff)
+            break;
+            
+        if (!timeout--)
+        {
+            kprintf("Timeout waiting for link speed from IOP\n");
+            kprintf("0x%08.8ulx\n", p32[0]);
+            return RC_RTN_NO_LINK_SPEED;
+        }
+    }
+
+    /* get Link speed */
+    IOPLinkSpeed = (U8)((volatile PU8)p32)[0] & 0x0f;
+
+    *pLinkSpeedCode= IOPLinkSpeed;
+
+    return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** RCReportDriverCapability(U16 AdapterID, U32 capability)
+**
+** Currently defined bits:
+** WARM_REBOOT_CAPABLE   0x01
+**
+** =========================================================================
+*/
+RC_RETURN
+RCReportDriverCapability(U16 AdapterID, U32 capability)
+{
+    U32  off;
+    PU32 pMsg;
+    PPAB pPab;
+
+    pPab =PCIAdapterBlock[AdapterID];
+     
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_REPORT_DRIVER_CAPABILITY;
+    pMsg[5] = capability;
+
+    pPab->p_atu->InQueue = off;   /* send it to the I2O device */
+
+    return RC_RTN_NO_ERROR ;
+}
+
+/*
+** =========================================================================
+** RCGetFirmwareVer()
+**
+** Return firmware version in the form "SoftwareVersion : Bt BootVersion"
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback)
+{
+    U32 msgOffset, timeout;
+    PU32 pMsg;
+    volatile PU32 p32;
+    PPAB pPab;
+
+    pPab =PCIAdapterBlock[AdapterID];
+
+    msgOffset = pPab->p_atu->InQueue;
+
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+        kprintf("RCGetFirmwareVer(): Inbound Free Q empty!\n");
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virtual address of msg - virtual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    /* virtual pointer to return buffer - clear first two dwords */
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0xff;
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_FIRMWARE_REV;
+    /* phys address to return status - area right after PAB */
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+
+   
+    /* post to Inbound Post Q */   
+
+    pPab->p_atu->InQueue = msgOffset;
+
+    
+    /* wait for response */
+    timeout = 1000000;
+    while(1)
+    {
+        int i;
+        
+        if (WaitCallback)
+            (*WaitCallback)();
+            
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+
+        if (p32[0] != 0xff)
+            break;
+            
+        if (!timeout--)
+        {
+            kprintf("Timeout waiting for link speed from IOP\n");
+            return RC_RTN_NO_FIRM_VER;
+        }
+    }
+
+    strcpy(pFirmString, (PU8)p32);
+    return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** RCResetLANCard()
+**
+** ResourceFlags indicates whether to return buffer resource explicitly
+** to host or keep and reuse.
+** CallbackFunction (if not NULL) is the function to be called when 
+** reset is complete.
+** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when
+** reset is done (if not NULL).
+**
+** =========================================================================
+*/
+RC_RETURN 
+RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
+{
+    unsigned long off;
+    unsigned long *pMsg;
+    PPAB pPab;
+    int i;
+    long timeout = 0;
+
+    
+    pPab =PCIAdapterBlock[AdapterID];
+     
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+    
+    pPab->pCallbackFunc = CallbackFunction;
+
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup message */
+    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_LAN_RESET << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = ResourceFlags << 16;   /* resource flags */
+
+    pPab->p_atu->InQueue = off;   /* send it to the I2O device */
+
+    if (CallbackFunction == (PFNCALLBACK)NULL)
+    {
+        /* call RCProcI2OMsgQ() until something in pPab->pCallbackFunc
+           or until timer goes off */
+        while (pPab->pCallbackFunc == (PFNCALLBACK)NULL)
+        {
+            RCProcI2OMsgQ(AdapterID);
+            for (i = 0; i < 100000; i++)     /* please don't hog the bus!!! */
+                ;
+            timeout++;
+            if (timeout > 10000)
+            {
+                break;
+            }
+        }
+        if (ReturnAddr != (PU32)NULL)
+            *ReturnAddr = (U32)pPab->pCallbackFunc;
+    }
+
+    return RC_RTN_NO_ERROR ;
+}
+/*
+** =========================================================================
+** RCResetIOP()
+**
+** Send StatusGet Msg, wait for results return directly to buffer.
+**
+** =========================================================================
+*/
+RC_RETURN 
+RCResetIOP(U16 AdapterID)
+{
+    U32 msgOffset, timeout;
+    PU32 pMsg;
+    PPAB pPab;
+    volatile PU32 p32;
+    
+    pPab = PCIAdapterBlock[AdapterID];
+    msgOffset = pPab->p_atu->InQueue;
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virtual address of msg - virtual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_EXEC_IOP_RESET << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID;
+    pMsg[2] = 0; /* universal context */
+    pMsg[3] = 0; /* universal context */
+    pMsg[4] = 0; /* universal context */
+    pMsg[5] = 0; /* universal context */
+    /* phys address to return status - area right after PAB */
+    pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+    pMsg[7] = 0;
+    pMsg[8] = 1;  /*  return 1 byte */
+
+    /* virual pointer to return buffer - clear first two dwords */
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0;
+    p32[1] = 0;
+
+    /* post to Inbound Post Q */   
+
+    pPab->p_atu->InQueue = msgOffset;
+
+    /* wait for response */
+    timeout = 1000000;
+    while(1)
+    {
+        int i;
+        
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0] || p32[1])
+            break;
+            
+        if (!timeout--)
+        {
+            printk("RCResetIOP timeout\n");
+            return RC_RTN_MSG_REPLY_TIMEOUT;
+        }
+    }
+    return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** RCShutdownLANCard()
+**
+** ResourceFlags indicates whether to return buffer resource explicitly
+** to host or keep and reuse.
+** CallbackFunction (if not NULL) is the function to be called when 
+** shutdown is complete.
+** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when
+** shutdown is done (if not NULL).
+**
+** =========================================================================
+*/
+RC_RETURN 
+RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
+{
+    volatile PU32 pMsg;
+    U32 off;
+    PPAB pPab;
+    int i;
+    long timeout = 0;
+
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+
+    pPab->pCallbackFunc = CallbackFunction;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup message */
+    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_LAN_SHUTDOWN << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = ResourceFlags << 16;   /* resource flags */
+
+    pPab->p_atu->InQueue = off;   /* send it to the I2O device */
+
+    if (CallbackFunction == (PFNCALLBACK)NULL)
+    {
+        /* call RCProcI2OMsgQ() until something in pPab->pCallbackFunc
+           or until timer goes off */
+        while (pPab->pCallbackFunc == (PFNCALLBACK)NULL)
+        {
+            RCProcI2OMsgQ(AdapterID);
+            for (i = 0; i < 100000; i++)     /* please don't hog the bus!!! */
+                ;
+            timeout++;
+            if (timeout > 10000)
+            {
+				printk("RCShutdownLANCard(): timeout\n");
+                break;
+            }
+        }
+        if (ReturnAddr != (PU32)NULL)
+            *ReturnAddr = (U32)pPab->pCallbackFunc;
+    }
+    return RC_RTN_NO_ERROR ;
+}
+
+
+/*
+** =========================================================================
+** RCSetRavlinIPandMask()
+**
+** Set the Ravlin 45/PCI cards IP address and network mask.
+**
+** IP address and mask must be in network byte order.
+** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be
+** 0x04030201 and 0x00FFFFFF on a little endian machine.
+**
+** =========================================================================
+*/
+RC_RETURN
+RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask)
+{
+    volatile PU32 pMsg;
+    U32 off;
+    PPAB pPab;
+
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_IP_AND_MASK;
+    pMsg[5] = ipAddr; 
+    pMsg[6] = netMask;
+
+
+    pPab->p_atu->InQueue = off;   /* send it to the I2O device */
+    return RC_RTN_NO_ERROR ;
+
+}
+
+/*
+** =========================================================================
+** RCGetRavlinIPandMask()
+**
+** get the IP address and MASK from the card
+** 
+** =========================================================================
+*/
+RC_RETURN
+RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, 
+                     PFNWAITCALLBACK WaitCallback)
+{
+    unsigned i, timeout;
+    U32      off;
+    PU32     pMsg, p32;
+    PPAB     pPab;
+    PATU     p_atu;
+    
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr);
+#endif /* DEBUG */
+
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    p_atu = pPab->p_atu;
+    off = p_atu->InQueue;   /* get addresss of message */
+ 
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    *p32 = 0xFFFFFFFF;
+
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
+#endif /* DEBUG */
+    /* setup private message */
+    pMsg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;               /* initiator context */
+    pMsg[3] = 0x218;           /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_IP_AND_MASK;
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+    p_atu->InQueue = off;   /* send it to the I2O device */
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
+#endif /* DEBUG */
+
+    /* wait for the rcpci45 board to update the info */
+    timeout = 100000;
+    while (0xffffffff == *p32)
+    {
+        if (WaitCallback)
+            (*WaitCallback)();
+    
+        for (i = 0; i < 1000; i++)
+            ;
+
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("RCGetRavlinIPandMask: Timeout\n");
+#endif /* DEBUG */
+            return RC_RTN_MSG_REPLY_TIMEOUT;
+        }
+    }
+
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: after time out\n", \
+            "p32[0] (IpAddr) 0x%08.8ulx, p32[1] (IPmask) 0x%08.8ulx\n", p32[0], p32[1]);
+#endif /* DEBUG */
+    
+    /* send IP and mask to user's space  */
+    *pIpAddr  = p32[0];
+    *pNetMask = p32[1];
+
+
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr);
+#endif /* DEBUG */
+
+    return RC_RTN_NO_ERROR;
+}
+
+/* 
+** /////////////////////////////////////////////////////////////////////////
+** /////////////////////////////////////////////////////////////////////////
+**
+**                        local functions
+**
+** /////////////////////////////////////////////////////////////////////////
+** /////////////////////////////////////////////////////////////////////////
+*/
+
+/*
+** =========================================================================
+** SendI2OOutboundQInitMsg()
+**
+** =========================================================================
+*/
+static int 
+SendI2OOutboundQInitMsg(PPAB pPab)
+{
+    U32 msgOffset, timeout, phyOutQFrames, i;
+    volatile PU32 pMsg;
+    volatile PU32 p32;
+    
+    
+    
+    msgOffset = pPab->p_atu->InQueue;
+
+    
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("SendI2OOutboundQInitMsg(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+    
+    
+    /* calc virual address of msg - virual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+#ifdef DEBUG
+    kprintf("SendI2OOutboundQInitMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
+#endif /* DEBUG */
+
+    pMsg[0] = EIGHT_WORD_MSG_SIZE | TRL_OFFSET_6;
+    pMsg[1] = I2O_EXEC_OUTBOUND_INIT << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = 0x106; /* transaction context */
+    pMsg[4] = 4096; /* Host page frame size */
+    pMsg[5] = MSG_FRAME_SIZE  << 16 | 0x80; /* outbound msg frame size and Initcode */
+    pMsg[6] = 0xD0000004;       /* simple sgl element LE, EOB */
+    /* phys address to return status - area right after PAB */
+    pMsg[7] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+    /* virual pointer to return buffer - clear first two dwords */
+    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0;
+    
+    /* post to Inbound Post Q */   
+    pPab->p_atu->InQueue = msgOffset;
+    
+    /* wait for response */
+    timeout = 100000;
+    while(1)
+    {
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0])
+            break;
+            
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("Timeout wait for InitOutQ InPrgress status from IOP\n");
+#endif /* DEBUG */
+            return RC_RTN_NO_I2O_STATUS;
+        }
+    }
+
+    timeout = 100000;
+    while(1)
+    {
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0] == I2O_EXEC_OUTBOUND_INIT_COMPLETE)
+            break;
+
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("Timeout wait for InitOutQ Complete status from IOP\n");
+#endif /* DEBUG */
+            return RC_RTN_NO_I2O_STATUS;
+        }
+    }
+
+    /* load PCI outbound free Q with MF physical addresses */
+    phyOutQFrames = pPab->outMsgBlockPhyAddr;
+
+    for (i = 0; i < NMBR_MSG_FRAMES; i++)
+    {
+        pPab->p_atu->OutQueue = phyOutQFrames;
+        phyOutQFrames += MSG_FRAME_SIZE;
+    }
+    return RC_RTN_NO_ERROR;
+}
+
+
+/*
+** =========================================================================
+** GetI2OStatus()
+**
+** Send StatusGet Msg, wait for results return directly to buffer.
+**
+** =========================================================================
+*/
+static int 
+GetI2OStatus(PPAB pPab)
+{
+    U32 msgOffset, timeout;
+    PU32 pMsg;
+    volatile PU32 p32;
+    
+    
+    msgOffset = pPab->p_atu->InQueue; 
+#ifdef DEBUG
+    printk("GetI2OStatus: msg offset = 0x%x\n", msgOffset);
+#endif /* DEBUG */
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("GetI2OStatus(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virual address of msg - virual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_EXEC_STATUS_GET << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID;
+    pMsg[2] = 0; /* universal context */
+    pMsg[3] = 0; /* universal context */
+    pMsg[4] = 0; /* universal context */
+    pMsg[5] = 0; /* universal context */
+    /* phys address to return status - area right after PAB */
+    pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+    pMsg[7] = 0;
+    pMsg[8] = 88;  /*  return 88 bytes */
+
+    /* virual pointer to return buffer - clear first two dwords */
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0;
+    p32[1] = 0;
+
+#ifdef DEBUG
+    kprintf("GetI2OStatus - pMsg:0x%08.8ulx, msgOffset:0x%08.8ulx, [1]:0x%08.8ulx, [6]:0x%08.8ulx\n",
+            pMsg, msgOffset, pMsg[1], pMsg[6]);
+#endif /* DEBUG */
+   
+    /* post to Inbound Post Q */   
+    pPab->p_atu->InQueue = msgOffset;
+    
+#ifdef DEBUG
+    kprintf("Return status to p32 = 0x%08.8ulx\n", p32);
+#endif /* DEBUG */
+    
+    /* wait for response */
+    timeout = 1000000;
+    while(1)
+    {
+        int i;
+        
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0] && p32[1])
+            break;
+            
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("Timeout waiting for status from IOP\n");
+            kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
+            kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
+            kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
+#endif /* DEBUG */
+            return RC_RTN_NO_I2O_STATUS;
+        }
+    }
+            
+#ifdef DEBUG
+    kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
+    kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
+    kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
+#endif /* DEBUG */
+    /* get IOP state */
+    pPab->IOPState = ((volatile PU8)p32)[10];
+    pPab->InboundMFrameSize  = ((volatile PU16)p32)[6];
+    
+#ifdef DEBUG
+    kprintf("IOP state 0x%02.2x InFrameSize = 0x%04.4x\n", 
+            pPab->IOPState, pPab->InboundMFrameSize);
+#endif /* DEBUG */
+    return RC_RTN_NO_ERROR;
+}
+
+
+/*
+** =========================================================================
+** SendEnableSysMsg()
+**
+**
+** =========================================================================
+*/
+static int 
+SendEnableSysMsg(PPAB pPab)
+{
+    U32 msgOffset; // timeout;
+    volatile PU32 pMsg;
+
+    msgOffset = pPab->p_atu->InQueue;
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("SendEnableSysMsg(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virual address of msg - virual already mapped to physical */
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+#ifdef DEBUG
+    kprintf("SendEnableSysMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
+#endif /* DEBUG */
+
+    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_EXEC_SYS_ENABLE << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = 0x110; /* transaction context */
+    pMsg[4] = 0x50657465; /*  RedCreek Private */
+
+    /* post to Inbound Post Q */
+    pPab->p_atu->InQueue = msgOffset;
+
+    return RC_RTN_NO_ERROR;
+}
+
+
+/*
+** =========================================================================
+** FillI2OMsgFromTCB()
+**
+** inputs   pMsgU32 - virual pointer (mapped to physical) of message frame
+**          pXmitCntrlBlock - pointer to caller buffer control block.
+**
+** fills in LAN SGL after Transaction Control Word or Bucket Count.
+** =========================================================================
+*/
+static int 
+FillI2OMsgSGLFromTCB(PU32 pMsgFrame, PRCTCB pTransCtrlBlock)
+{
+    unsigned int nmbrBuffers, nmbrSeg, nmbrDwords, context, flags;
+    PU32 pTCB, pMsg;
+
+    /* SGL element flags */   
+#define EOB        0x40000000
+#define LE         0x80000000
+#define SIMPLE_SGL 0x10000000
+#define BC_PRESENT 0x01000000
+
+    pTCB = (PU32)pTransCtrlBlock;
+    pMsg = pMsgFrame;
+    nmbrDwords = 0;
+
+#ifdef DEBUG
+    kprintf("FillI2OMsgSGLFromTCBX\n");
+    kprintf("TCB  0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+            pTCB[0], pTCB[1], pTCB[2], pTCB[3], pTCB[4]);
+    kprintf("pTCB 0x%08.8ulx, pMsg 0x%08.8ulx\n", pTCB, pMsg);
+#endif /* DEBUG */
+    
+    nmbrBuffers = *pTCB++;
+    
+    if (!nmbrBuffers)
+    {
+        return -1;
+    }
+
+    do
+    {
+        context = *pTCB++; /* buffer tag (context) */
+        nmbrSeg = *pTCB++; /* number of segments */
+
+        if (!nmbrSeg)
+        {
+            return -1;
+        }
+        
+        flags = SIMPLE_SGL | BC_PRESENT;
+
+        if (1 == nmbrSeg)
+        {
+            flags |= EOB;
+        
+            if (1 == nmbrBuffers)
+                flags |= LE;
+        }    
+
+        /* 1st SGL buffer element has context */
+        pMsg[0] = pTCB[0] | flags ; /* send over count (segment size) */
+        pMsg[1] = context;
+        pMsg[2] = pTCB[1]; /* send buffer segment physical address */
+        nmbrDwords += 3;
+        pMsg += 3;
+        pTCB += 2;
+
+        
+        if (--nmbrSeg)
+        {
+            do
+            {
+                flags = SIMPLE_SGL;
+                
+                if (1 == nmbrSeg)
+                {
+                    flags |= EOB;
+                
+                    if (1 == nmbrBuffers)
+                        flags |= LE;
+                }    
+                
+                pMsg[0] = pTCB[0] | flags;  /* send over count */
+                pMsg[1] = pTCB[1];   /* send buffer segment physical address */
+                nmbrDwords += 2;
+                pTCB += 2;
+                pMsg += 2;
+        
+            } while (--nmbrSeg);
+        }
+        
+    } while (--nmbrBuffers);
+    
+    return nmbrDwords;
+}
+
+
+/*
+** =========================================================================
+** ProcessOutboundI2OMsg()
+**
+** process I2O reply message
+** * change to msg structure *
+** =========================================================================
+*/
+static void 
+ProcessOutboundI2OMsg(PPAB pPab, U32 phyAddrMsg)
+{
+    PU8 p8Msg;
+    PU32 p32;
+    //  U16 count;
+    
+    
+    p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
+    p32 = (PU32)p8Msg;
+    
+#ifdef DEBUG
+    kprintf("VXD: ProcessOutboundI2OMsg - pPab 0x%08.8ulx, phyAdr 0x%08.8ulx, linAdr 0x%08.8ulx\n", pPab, phyAddrMsg, p8Msg);
+    kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
+    kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
+#endif /* DEBUG */
+
+    if (p32[4] >> 24 != I2O_REPLY_STATUS_SUCCESS)
+    {
+#ifdef DEBUG
+        kprintf("Message reply status not success\n");
+#endif /* DEBUG */
+        return;
+    }
+    
+    switch (p8Msg[7] )  /* function code byte */
+    {
+    case I2O_EXEC_SYS_TAB_SET:
+        msgFlag = 1;
+#ifdef DEBUG
+        kprintf("Received I2O_EXEC_SYS_TAB_SET reply\n");
+#endif /* DEBUG */
+        break;
+
+    case I2O_EXEC_HRT_GET:
+        msgFlag = 1;
+#ifdef DEBUG
+        kprintf("Received I2O_EXEC_HRT_GET reply\n");
+#endif /* DEBUG */
+        break;
+        
+    case I2O_EXEC_LCT_NOTIFY:
+        msgFlag = 1;
+#ifdef DEBUG
+        kprintf("Received I2O_EXEC_LCT_NOTIFY reply\n");
+#endif /* DEBUG */
+        break;
+        
+    case I2O_EXEC_SYS_ENABLE:
+        msgFlag = 1;
+#ifdef DEBUG
+        kprintf("Received I2O_EXEC_SYS_ENABLE reply\n");
+#endif /* DEBUG */
+        break;
+
+    default:    
+#ifdef DEBUG
+        kprintf("Received UNKNOWN reply\n");
+#endif /* DEBUG */
+        break;
+    }
+}
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/rclanmtl.h linux/drivers/net/rclanmtl.h
--- v2.2.0-pre7/linux/drivers/net/rclanmtl.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/rclanmtl.h	Thu Jan 14 22:58:47 1999
@@ -0,0 +1,637 @@
+/*
+** *************************************************************************
+**
+**
+**     R C L A N M T L . H             $Revision: 5 $
+**
+**
+**  RedCreek I2O LAN Message Transport Layer header file.
+**
+**  ---------------------------------------------------------------------
+**  ---     Copyright (c) 1997-1999, RedCreek Communications Inc.     ---
+**  ---                   All rights reserved.                        ---
+**  ---------------------------------------------------------------------
+**
+**  File Description:
+**
+**  Header file for host I2O (Intelligent I/O) LAN message transport layer 
+**  API and data types.
+**
+**  This program is free software; you can redistribute it and/or modify
+**  it under the terms of the GNU General Public License as published by
+**  the Free Software Foundation; either version 2 of the License, or
+**  (at your option) any later version.
+
+**  This program is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**  GNU General Public License for more details.
+
+**  You should have received a copy of the GNU General Public License
+**  along with this program; if not, write to the Free Software
+**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** *************************************************************************
+*/
+
+#ifndef RCLANMTL_H
+#define RCLANMTL_H
+
+/* Linux specific includes */
+#define kprintf printk
+#ifdef RC_LINUX_MODULE     /* linux modules need non-library version of string functions */
+#include <linux/string.h>
+#else
+#include <string.h>
+#endif
+
+/* PCI/45 Configuration space values */
+#define RC_PCI45_VENDOR_ID  0x4916
+#define RC_PCI45_DEVICE_ID  0x1960
+
+
+ /* RedCreek API function return values */
+#define RC_RTN_NO_ERROR             0
+#define RC_RTN_I2O_NOT_INIT         1
+#define RC_RTN_FREE_Q_EMPTY         2
+#define RC_RTN_TCB_ERROR            3
+#define RC_RTN_TRANSACTION_ERROR    4
+#define RC_RTN_ADAPTER_ALREADY_INIT 5
+#define RC_RTN_MALLOC_ERROR         6
+#define RC_RTN_ADPTR_NOT_REGISTERED 7
+#define RC_RTN_MSG_REPLY_TIMEOUT    8
+#define RC_RTN_NO_I2O_STATUS        9
+#define RC_RTN_NO_FIRM_VER         10
+#define RC_RTN_NO_LINK_SPEED       11
+
+/* Driver capability flags */
+#define WARM_REBOOT_CAPABLE      0x01
+
+ /* scalar data types */
+typedef unsigned char   U8;
+typedef unsigned char*  PU8;
+typedef unsigned short  U16;
+typedef unsigned short* PU16;
+typedef unsigned long   U32;
+typedef unsigned long*  PU32;
+typedef unsigned long   BF;
+typedef int             RC_RETURN;
+
+
+ /* 
+ ** type PFNWAITCALLBACK
+ **
+ ** pointer to void function - type used for WaitCallback in some functions 
+ */
+typedef void (*PFNWAITCALLBACK)(void);  /* void argument avoids compiler complaint */
+
+ /*
+ ** type PFNTXCALLBACK 
+ **
+ ** Pointer to user's transmit callback function.  This user function is
+ ** called from RCProcI2OMsgQ() when packet have been transmitted from buffers
+ ** given in the RCI2OSendPacket() function.  BufferContext is a pointer to
+ ** an array of 32 bit context values.  These are the values the user assigned
+ ** and passed in the TCB to the RCI2OSendPacket() function.  PcktCount
+ ** indicates the number of buffer context values in the BufferContext[] array.
+ ** The User's TransmitCallbackFunction should recover (put back in free queue)
+ ** the packet buffers associated with the buffer context values.
+ */
+typedef void (*PFNTXCALLBACK)(U32  Status,
+                              U16  PcktCount,
+                              PU32 BufferContext,
+                              U16  AdaterID);
+
+ /* 
+ ** type PFNRXCALLBACK 
+ **
+ ** Pointer to user's receive callback function.  This user function
+ ** is called from RCProcI2OMsgQ() when packets have been received into
+ ** previously posted packet buffers throught the RCPostRecvBuffers() function.
+ ** The received callback function should process the Packet Descriptor Block
+ ** pointed to by PacketDescBlock. See Packet Decription Block below.
+ */
+typedef void (*PFNRXCALLBACK)(U32  Status,
+                              U8   PktCount,
+                              U32  BucketsRemain,
+                              PU32 PacketDescBlock,
+                              U16  AdapterID);
+
+ /* 
+ ** type PFNCALLBACK 
+ **
+ ** Pointer to user's generic callback function.  This user function
+ ** can be passed to LANReset or LANShutdown and is called when the 
+ ** the reset or shutdown is complete.
+ ** Param1 and Param2 are invalid for LANReset and LANShutdown.
+ */
+typedef void (*PFNCALLBACK)(U32  Status,
+                              U32  Param1,
+                              U32  Param2,
+                              U16  AdapterID);
+
+/*
+** Status - Transmit and Receive callback status word 
+**
+** A 32 bit Status is returned to the TX and RX callback functions.  This value
+** contains both the reply status and the detailed status as follows:
+**
+**  32    24     16            0
+**  +------+------+------------+
+**  | Reply|      |  Detailed  |
+**  |Status|   0  |   Status   |
+**  +------+------+------------+
+**
+** Reply Status and Detailed Status of zero indicates No Errors.
+*/
+ /* reply message status defines */
+#define    I2O_REPLY_STATUS_SUCCESS                    0x00
+#define    I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER     0x02
+#define    I2O_REPLY_STATUS_TRANSACTION_ERROR          0x0A
+
+
+/* DetailedStatusCode defines */
+#define    I2O_LAN_DSC_SUCCESS                         0x0000
+#define    I2O_LAN_DSC_DEVICE_FAILURE                  0x0001
+#define    I2O_LAN_DSC_DESTINATION_NOT_FOUND           0x0002
+#define    I2O_LAN_DSC_TRANSMIT_ERROR                  0x0003
+#define    I2O_LAN_DSC_TRANSMIT_ABORTED                0x0004
+#define    I2O_LAN_DSC_RECEIVE_ERROR                   0x0005
+#define    I2O_LAN_DSC_RECEIVE_ABORTED                 0x0006
+#define    I2O_LAN_DSC_DMA_ERROR                       0x0007
+#define    I2O_LAN_DSC_BAD_PACKET_DETECTED             0x0008
+#define    I2O_LAN_DSC_OUT_OF_MEMORY                   0x0009
+#define    I2O_LAN_DSC_BUCKET_OVERRUN                  0x000A
+#define    I2O_LAN_DSC_IOP_INTERNAL_ERROR              0x000B
+#define    I2O_LAN_DSC_CANCELED                        0x000C
+#define    I2O_LAN_DSC_INVALID_TRANSACTION_CONTEXT     0x000D
+#define    I2O_LAN_DSC_DESTINATION_ADDRESS_DETECTED    0x000E
+#define    I2O_LAN_DSC_DESTINATION_ADDRESS_OMITTED     0x000F
+#define    I2O_LAN_DSC_PARTIAL_PACKET_RETURNED         0x0010
+
+
+/*
+** Packet Description Block   (Received packets)
+**
+** A pointer to this block structure is returned to the ReceiveCallback 
+** function.  It contains the list of packet buffers which have either been
+** filled with a packet or returned to host due to a LANReset function. 
+** Currently there will only be one packet per receive bucket (buffer) posted. 
+**
+**   32   24               0     
+**  +-----------------------+  -\
+**  |   Buffer 1 Context    |    \
+**  +-----------------------+     \
+**  |      0xC0000000       |     / First Bucket Descriptor
+**  +-----+-----------------+    /
+**  |  0  | packet 1 length |   / 
+**  +-----------------------+  -\
+**  |   Buffer 2 Context    |    \
+**  +-----------------------+     \
+**  |      0xC0000000       |     / Second Bucket Descriptor
+**  +-----+-----------------+    /
+**  |  0  | packet 2 length |   / 
+**  +-----+-----------------+  -
+**  |         ...           |  ----- more bucket descriptors
+**  +-----------------------+  -\
+**  |   Buffer n Context    |    \
+**  +-----------------------+     \
+**  |      0xC0000000       |     / Last Bucket Descriptor
+**  +-----+-----------------+    /
+**  |  0  | packet n length |   / 
+**  +-----+-----------------+  -
+**
+** Buffer Context values are those given to adapter in the TCB on calls to
+** RCPostRecvBuffers().
+**  
+*/
+
+
+
+/*
+** Transaction Control Block (TCB) structure
+**
+** A structure like this is filled in by the user and passed by reference to 
+** RCI2OSendPacket() and RCPostRecvBuffers() functions.  Minimum size is five
+** 32-bit words for one buffer with one segment descriptor.  
+** MAX_NMBR_POST_BUFFERS_PER_MSG defines the maximum single segment buffers
+** that can be described in a given TCB.
+**
+**   32                    0
+**  +-----------------------+
+**  |   Buffer Count        |  Number of buffers in the TCB
+**  +-----------------------+
+**  |   Buffer 1 Context    |  first buffer reference
+**  +-----------------------+
+**  |   Buffer 1 Seg Count  |  number of segments in buffer
+**  +-----------------------+
+**  |   Buffer 1 Seg Desc 1 |  first segment descriptor (size, physical address)
+**  +-----------------------+
+**  |         ...           |  more segment descriptors (size, physical address)
+**  +-----------------------+
+**  |   Buffer 1 Seg Desc n |  last segment descriptor (size, physical address)
+**  +-----------------------+
+**  |   Buffer 2 Context    |  second buffer reference
+**  +-----------------------+
+**  |   Buffer 2 Seg Count  |  number of segments in buffer
+**  +-----------------------+
+**  |   Buffer 2 Seg Desc 1 |  segment descriptor (size, physical address)
+**  +-----------------------+
+**  |         ...           |  more segment descriptors (size, physical address)
+**  +-----------------------+
+**  |   Buffer 2 Seg Desc n |
+**  +-----------------------+
+**  |         ...           |  more buffer descriptor blocks ...
+**  +-----------------------+
+**  |   Buffer n Context    |
+**  +-----------------------+
+**  |   Buffer n Seg Count  |
+**  +-----------------------+
+**  |   Buffer n Seg Desc 1 |
+**  +-----------------------+
+**  |         ...           |
+**  +-----------------------+
+**  |   Buffer n Seg Desc n |
+**  +-----------------------+
+**
+**
+** A TCB for one contigous packet buffer would look like the following:
+**
+**   32                    0
+**  +-----------------------+
+**  |         1             |  one buffer in the TCB
+**  +-----------------------+
+**  |  <user's Context>     |  user's buffer reference
+**  +-----------------------+
+**  |         1             |  one segment buffer
+**  +-----------------------+                            _
+**  |    <buffer size>      |  size                       \ 
+**  +-----------------------+                              \ segment descriptor
+**  |  <physical address>   |  physical address of buffer  /
+**  +-----------------------+                            _/
+**
+*/
+
+ /* Buffer Segment Descriptor */
+typedef struct
+{
+    U32 size;
+    U32 phyAddress;
+}
+ BSD, *PBSD;
+ 
+typedef PU32 PRCTCB;
+/*
+** -------------------------------------------------------------------------
+** Exported functions comprising the API to the LAN I2O message transport layer
+** -------------------------------------------------------------------------
+*/
+
+
+ /*
+ ** InitRCI2OMsgLayer()
+ ** 
+ ** Called once prior to using the I2O LAN message transport layer.  User 
+ ** provides both the physical and virual address of a locked page buffer 
+ ** that is used as a private buffer for the RedCreek I2O message
+ ** transport layer.  This buffer must be a contigous memory block of a 
+ ** minimum of 16K bytes and long word aligned.  The user also must provide
+ ** the base address of the RedCreek PCI adapter assigned by BIOS or operating
+ ** system.  The user provided value AdapterID is a zero based index of the
+ ** Ravlin 45/PCI adapter.  This interface number is used in all subsequent API
+ ** calls to identify which adpapter for which the function is intended.  
+ ** Up to sixteen interfaces are supported with this API.
+ **
+ ** Inputs:  AdapterID - interface number from 0 to 15
+ **          pciBaseAddr - virual base address of PCI (set by BIOS)
+ **          p_msgbuf - virual address to private message block (min. 16K)
+ **          p_phymsgbuf - physical address of private message block
+ **          TransmitCallbackFunction - address of user's TX callback function
+ **          ReceiveCallbackFunction  - address of user's RX callback function
+ **
+ */
+RC_RETURN RCInitI2OMsgLayer(U16 AdapterID, U32 pciBaseAddr, 
+                            PU8 p_msgbuf,  PU8 p_phymsgbuf,
+                            PFNTXCALLBACK TransmitCallbackFunction,
+                            PFNRXCALLBACK ReceiveCallbackFunction,
+                            PFNCALLBACK   RebootCallbackFunction);
+
+ /*
+ ** RCSetRavlinIPandMask()
+ **
+ ** Set the Ravlin 45/PCI cards IP address and network mask.
+ **
+ ** IP address and mask must be in network byte order.
+ ** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be
+ ** 0x04030201 and 0x00FFFFFF on a little endian machine.
+ **
+ */
+RC_RETURN RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask);
+
+
+/*
+** =========================================================================
+** RCGetRavlinIPandMask()
+**
+** get the IP address and MASK from the card
+** 
+** =========================================================================
+*/
+RC_RETURN
+RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, 
+                        PFNWAITCALLBACK WaitCallback);
+
+ /* 
+ ** RCProcI2OMsgQ()
+ ** 
+ ** Called from user's polling loop or Interrupt Service Routine for a PCI 
+ ** interrupt from the RedCreek PCI adapter.  User responsible for determining
+ ** and hooking the PCI interrupt. This function will call the registered
+ ** callback functions, TransmitCallbackFunction or ReceiveCallbackFunction,
+ ** if a TX or RX transaction has completed.
+ */
+void RCProcI2OMsgQ(U16 AdapterID);
+
+
+ /*
+ ** Disable and Enable I2O interrupts.  I2O interrupts are enabled at Init time
+ ** but can be disabled and re-enabled through these two function calls.
+ ** Packets will still be put into any posted recieved buffers and packets will
+ ** be sent through RCI2OSendPacket() functions.  Disabling I2O interrupts
+ ** will prevent hardware interrupt to host even though the outbound I2O msg
+ ** queue is not emtpy.
+ */
+RC_RETURN RCEnableI2OInterrupts(U16 adapterID);
+RC_RETURN RCDisableI2OInterrupts(U16 AdapterID);
+
+
+ /* 
+ ** RCPostRecvBuffers()
+ ** 
+ ** Post user's page locked buffers for use by the PCI adapter to
+ ** return ethernet packets received from the LAN.  Transaction Control Block,
+ ** provided by user, contains buffer descriptor(s) which includes a buffer
+ ** context number along with buffer size and physical address.  See TCB above.
+ ** The buffer context and actual packet length are returned to the 
+ ** ReceiveCallbackFunction when packets have been received.  Buffers posted
+ ** to the RedCreek adapter are considered owned by the adapter until the
+ ** context is return to user through the ReceiveCallbackFunction.
+ */
+RC_RETURN RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransactionCtrlBlock);
+#define MAX_NMBR_POST_BUFFERS_PER_MSG 32
+
+ /*
+ ** RCI2OSendPacket()
+ ** 
+ ** Send user's ethernet packet from a locked page buffer.  
+ ** Packet must have full MAC header, however without a CRC.  
+ ** Initiator context is a user provided value that is returned 
+ ** to the TransmitCallbackFunction when packet buffer is free.
+ ** Transmit buffer are considered owned by the adapter until context's
+ ** returned to user through the TransmitCallbackFunction.
+ */
+RC_RETURN RCI2OSendPacket(U16 AdapterID, 
+                          U32 context, 
+                          PRCTCB pTransactionCtrlBlock);
+
+
+ /* Ethernet Link Statistics structure */
+typedef struct tag_RC_link_stats
+{
+    U32 TX_good;      /* good transmit frames */
+    U32 TX_maxcol;    /* frames not TX due to MAX collisions */
+    U32 TX_latecol;   /* frames not TX due to late collisions */
+    U32 TX_urun;      /* frames not TX due to DMA underrun */
+    U32 TX_crs;       /* frames TX with lost carrier sense */
+    U32 TX_def;       /* frames deferred due to activity on link */
+    U32 TX_singlecol; /* frames TX with one and only on collision */
+    U32 TX_multcol;   /* frames TX with more than one collision */
+    U32 TX_totcol;    /* total collisions detected during TX */
+    U32 Rcv_good;     /* good frames received */
+    U32 Rcv_CRCerr;   /* frames RX and discarded with CRC errors */
+    U32 Rcv_alignerr; /* frames RX with alignment and CRC errors */
+    U32 Rcv_reserr;   /* good frames discarded due to no RX buffer */
+    U32 Rcv_orun;     /* RX frames lost due to FIFO overrun */
+    U32 Rcv_cdt;      /* RX frames with collision during RX */
+    U32 Rcv_runt;     /* RX frames shorter than 64 bytes */
+}
+ RCLINKSTATS, *P_RCLINKSTATS;
+
+ /*
+ ** RCGetLinkStatistics()
+ **
+ ** Returns link statistics in user's structure at address StatsReturnAddr
+ ** If given, not NULL, the function WaitCallback is called during the wait
+ ** loop while waiting for the adapter to respond.
+ */
+RC_RETURN RCGetLinkStatistics(U16 AdapterID,
+                              P_RCLINKSTATS StatsReturnAddr,
+                              PFNWAITCALLBACK WaitCallback);
+
+ /*
+ ** RCGetLinkStatus()
+ **
+ ** Return link status, up or down, to user's location addressed by ReturnAddr.
+ ** If given, not NULL, the function WaitCallback is called during the wait
+ ** loop while waiting for the adapter to respond.
+ */
+RC_RETURN RCGetLinkStatus(U16 AdapterID, 
+                          PU32 pReturnStatus,
+                          PFNWAITCALLBACK WaitCallback);
+                               
+ /* Link Status defines - value returned in pReturnStatus */
+#define RC_LAN_LINK_STATUS_DOWN     0
+#define RC_LAN_LINK_STATUS_UP       1
+
+ /*
+ ** RCGetMAC()
+ **
+ ** Get the current MAC address assigned to user.  RedCreek Ravlin 45/PCI 
+ ** has two MAC addresses.  One which is private to the PCI Card, and 
+ ** another MAC which is given to the user as its link layer MAC address. The
+ ** adapter runs in promiscous mode because of the dual address requirement.
+ ** The MAC address is returned to the unsigned char array pointer to by mac.
+ */
+RC_RETURN RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback);
+
+ /*
+ ** RCSetMAC()
+ **
+ ** Set a new user port MAC address.  This address will be returned on
+ ** subsequent RCGetMAC() calls.
+ */
+RC_RETURN RCSetMAC(U16 AdapterID, PU8 mac);
+
+ /*
+ ** RCSetLinkSpeed()
+ **
+ ** set adapter's link speed based on given input code.
+ */
+RC_RETURN RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode);
+ /* Set link speed codes */
+#define LNK_SPD_AUTO_NEG_NWAY   0
+#define LNK_SPD_100MB_FULL      1
+#define LNK_SPD_100MB_HALF      2
+#define LNK_SPD_10MB_FULL       3
+#define LNK_SPD_10MB_HALF       4
+
+
+
+
+ /*
+ ** RCGetLinkSpeed()
+ **
+ ** Return link speed code.
+ */
+ /* Return link speed codes */
+#define LNK_SPD_UNKNOWN         0
+#define LNK_SPD_100MB_FULL      1
+#define LNK_SPD_100MB_HALF      2
+#define LNK_SPD_10MB_FULL       3
+#define LNK_SPD_10MB_HALF       4
+
+RC_RETURN
+RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback);
+/*
+** =========================================================================
+** RCSetPromiscuousMode(U16 AdapterID, U16 Mode)
+**
+** Defined values for Mode:
+**  0 - turn off promiscuous mode
+**  1 - turn on  promiscuous mode
+**
+** =========================================================================
+*/
+#define PROMISCUOUS_MODE_OFF 0
+#define PROMISCUOUS_MODE_ON  1
+RC_RETURN
+RCSetPromiscuousMode(U16 AdapterID, U16 Mode);
+/*
+** =========================================================================
+** RCGetPromiscuousMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback)
+**
+** get promiscuous mode setting
+**
+** Possible return values placed in pMode:
+**  0 = promisuous mode not set
+**  1 = promisuous mode is set
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetPromiscuousMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback);
+
+/*
+** =========================================================================
+** RCSetBroadcastMode(U16 AdapterID, U16 Mode)
+**
+** Defined values for Mode:
+**  0 - turn off promiscuous mode
+**  1 - turn on  promiscuous mode
+**
+** =========================================================================
+*/
+#define BROADCAST_MODE_OFF 0
+#define BROADCAST_MODE_ON  1
+RC_RETURN
+RCSetBroadcastMode(U16 AdapterID, U16 Mode);
+/*
+** =========================================================================
+** RCGetBroadcastMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback)
+**
+** get broadcast mode setting
+**
+** Possible return values placed in pMode:
+**  0 = broadcast mode not set
+**  1 = broadcast mode is set
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetBroadcastMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback);
+/*
+** =========================================================================
+** RCReportDriverCapability(U16 AdapterID, U32 capability)
+**
+** Currently defined bits:
+** WARM_REBOOT_CAPABLE   0x01
+**
+** =========================================================================
+*/
+RC_RETURN
+RCReportDriverCapability(U16 AdapterID, U32 capability);
+
+/*
+** RCGetFirmwareVer()
+**
+** Return firmware version in the form "SoftwareVersion : Bt BootVersion"
+**
+** WARNING: user's space pointed to by pFirmString should be at least 60 bytes.
+*/
+RC_RETURN
+RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback);
+
+/*
+** ----------------------------------------------
+** LAN adapter Reset and Shutdown functions
+** ----------------------------------------------
+*/
+ /* resource flag bit assignments for RCResetLANCard() & RCShutdownLANCard() */
+#define RC_RESOURCE_RETURN_POSTED_RX_BUCKETS  0x0001 
+#define RC_RESOURCE_RETURN_PEND_TX_BUFFERS    0x0002
+
+ /*
+ ** RCResetLANCard()
+ **
+ ** Reset LAN card operation.  Causes a software reset of the ethernet
+ ** controller and restarts the command and receive units. Depending on 
+ ** the ResourceFlags given, the buffers are either returned to the
+ ** host with reply status of I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER and
+ ** detailed status of I2O_LAN_DSC_CANCELED (new receive buffers must be
+ ** posted after issuing this) OR the buffers are kept and reused by
+ ** the ethernet controller. If CallbackFunction is not NULL, the function
+ ** will be called when the reset is complete.  If the CallbackFunction is
+ ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset 
+ ** to complete (please disable I2O interrupts during this method).
+ ** Any outstanding transmit or receive buffers that are complete will be
+ ** returned via the normal reply messages before the requested resource
+ ** buffers are returned.
+ ** A call to RCPostRecvBuffers() is needed to return the ethernet to full
+ ** operation if the receive buffers were returned during LANReset.
+ ** Note: The IOP status is not affected by a LAN reset.
+ */
+RC_RETURN RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction);
+
+
+ /*
+ ** RCShutdownLANCard()
+ **
+ ** Shutdown LAN card operation and put into an idle (suspended) state.
+ ** The LAN card is restarted with RCResetLANCard() function.
+ ** Depending on the ResourceFlags given, the buffers are either returned 
+ ** to the host with reply status of I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 
+ ** and detailed status of I2O_LAN_DSC_CANCELED (new receive buffers must be
+ ** posted after issuing this) OR the buffers are kept and reused by
+ ** the ethernet controller. If CallbackFunction is not NULL, the function
+ ** will be called when the reset is complete.  If the CallbackFunction is
+ ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset 
+ ** to complete (please disable I2O interrupts during this method).
+ ** Any outstanding transmit or receive buffers that are complete will be
+ ** returned via the normal reply messages before the requested resource
+ ** buffers are returned.
+ ** Note: The IOP status is not affected by a LAN shutdown.
+ */                                      
+RC_RETURN 
+RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction);
+
+ /*
+ ** RCResetIOP();
+ **     Initializes IOPState to I2O_IOP_STATE_RESET.
+ **     Stops access to outbound message Q.
+ **     Discards any outstanding transmit or posted receive buffers.
+ **     Clears outbound message Q. 
+ */
+RC_RETURN 
+RCResetIOP(U16 AdapterID);
+
+#endif /* RCLANMTL_H */
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/rcmtl.c linux/drivers/net/rcmtl.c
--- v2.2.0-pre7/linux/drivers/net/rcmtl.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/net/rcmtl.c	Wed Dec 31 16:00:00 1969
@@ -1,2058 +0,0 @@
-/*
-** *************************************************************************
-**
-**
-**     R C M T L . C             $Revision: 1.1 $
-**
-**
-**  RedCreek Message Transport Layer program module.
-**
-**  ---------------------------------------------------------------------
-**  ---     Copyright (c) 1997-1998, RedCreek Communications Inc.     ---
-**  ---                   All rights reserved.                        ---
-**  ---------------------------------------------------------------------
-**
-** File Description:
-**
-** Host side message transport layer.
-**
-**  This program is free software; you can redistribute it and/or modify
-**  it under the terms of the GNU General Public License as published by
-**  the Free Software Foundation; either version 2 of the License, or
-**  (at your option) any later version.
-
-**  This program is distributed in the hope that it will be useful,
-**  but WITHOUT ANY WARRANTY; without even the implied warranty of
-**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-**  GNU General Public License for more details.
-
-**  You should have received a copy of the GNU General Public License
-**  along with this program; if not, write to the Free Software
-**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-**
-***************************************************************************/
-
-#undef DEBUG
-
-#define RC_LINUX_MODULE
-#include "rcmtl.h"
-
-#define dprintf kprintf
-
-extern int printk(const char * fmt, ...);
-
- /* RedCreek LAN device Target ID */
-#define LAN_TARGET_ID  0x10 
- /* RedCreek's OSM default LAN receive Initiator */
-#define DEFAULT_RECV_INIT_CONTEXT  0xA17  
-
-
-/*
-** message structures
-*/
-
-#define    TID_SZ                                  12
-#define    FUNCTION_SZ                             8
-
-/* Transaction Reply Lists (TRL) Control Word structure */
-
-#define    TRL_SINGLE_FIXED_LENGTH           0x00
-#define    TRL_SINGLE_VARIABLE_LENGTH        0x40
-#define    TRL_MULTIPLE_FIXED_LENGTH         0x80
-
-/* LAN Class specific functions */
-
-#define    LAN_PACKET_SEND                         0x3B
-#define    LAN_SDU_SEND                            0x3D
-#define    LAN_RECEIVE_POST                        0x3E
-#define    LAN_RESET                               0x35
-#define    LAN_SHUTDOWN                            0x37
-
-/* Private Class specfic function */
-#define    RC_PRIVATE                                 0xFF
-
-/*  RC Executive Function Codes.  */
-
-#define    RC_CMD_ADAPTER_ASSIGN                     0xB3
-#define    RC_CMD_ADAPTER_READ                       0xB2
-#define    RC_CMD_ADAPTER_RELEASE                    0xB5
-#define    RC_CMD_BIOS_INFO_SET                      0xA5
-#define    RC_CMD_BOOT_DEVICE_SET                    0xA7
-#define    RC_CMD_CONFIG_VALIDATE                    0xBB
-#define    RC_CMD_CONN_SETUP                         0xCA
-#define    RC_CMD_DEVICE_ASSIGN                      0xB7
-#define    RC_CMD_DEVICE_RELEASE                     0xB9
-#define    RC_CMD_HRT_GET                            0xA8
-#define    RC_CMD_ADAPTER_CLEAR                          0xBE
-#define    RC_CMD_ADAPTER_CONNECT                        0xC9
-#define    RC_CMD_ADAPTER_RESET                          0xBD
-#define    RC_CMD_LCT_NOTIFY                         0xA2
-#define    RC_CMD_OUTBOUND_INIT                      0xA1
-#define    RC_CMD_PATH_ENABLE                        0xD3
-#define    RC_CMD_PATH_QUIESCE                       0xC5
-#define    RC_CMD_PATH_RESET                         0xD7
-#define    RC_CMD_STATIC_MF_CREATE                   0xDD
-#define    RC_CMD_STATIC_MF_RELEASE                  0xDF
-#define    RC_CMD_STATUS_GET                         0xA0
-#define    RC_CMD_SW_DOWNLOAD                        0xA9
-#define    RC_CMD_SW_UPLOAD                          0xAB
-#define    RC_CMD_SW_REMOVE                          0xAD
-#define    RC_CMD_SYS_ENABLE                         0xD1
-#define    RC_CMD_SYS_MODIFY                         0xC1
-#define    RC_CMD_SYS_QUIESCE                        0xC3
-#define    RC_CMD_SYS_TAB_SET                        0xA3
-
-
- /* Init Outbound Q status */
-#define    RC_CMD_OUTBOUND_INIT_IN_PROGRESS          0x01
-#define    RC_CMD_OUTBOUND_INIT_REJECTED             0x02
-#define    RC_CMD_OUTBOUND_INIT_FAILED               0x03
-#define    RC_CMD_OUTBOUND_INIT_COMPLETE             0x04
-
-
-#define    UTIL_NOP                                0x00
-
-
-/* RC Get Status State values */
-
-#define    ADAPTER_STATE_INITIALIZING                  0x01
-#define    ADAPTER_STATE_RESET                         0x02
-#define    ADAPTER_STATE_HOLD                          0x04
-#define    ADAPTER_STATE_READY                         0x05
-#define    ADAPTER_STATE_OPERATIONAL                   0x08
-#define    ADAPTER_STATE_FAILED                        0x10
-#define    ADAPTER_STATE_FAULTED                       0x11
-
-
-/* Defines for Request Status Codes:  Table 3-1 Reply Status Codes.  */
-
-#define    RC_REPLY_STATUS_SUCCESS                    0x00
-#define    RC_REPLY_STATUS_ABORT_DIRTY                0x01
-#define    RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER     0x02
-#define    RC_REPLY_STATUS_ABORT_PARTIAL_TRANSFER     0x03
-#define    RC_REPLY_STATUS_ERROR_DIRTY                0x04
-#define    RC_REPLY_STATUS_ERROR_NO_DATA_TRANSFER     0x05
-#define    RC_REPLY_STATUS_ERROR_PARTIAL_TRANSFER     0x06
-#define    RC_REPLY_STATUS_PROCESS_ABORT_DIRTY        0x07
-#define    RC_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER   0x08
-#define    RC_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER   0x09
-#define    RC_REPLY_STATUS_TRANSACTION_ERROR          0x0A
-#define    RC_REPLY_STATUS_PROGRESS_REPORT            0x80
-
-
-/* DetailedStatusCode defines for ALL messages: Table 3-2 Detailed Status Codes.*/
-
-#define    RC_DS_SUCCESS                        0x0000
-#define    RC_DS_BAD_KEY                        0x0001
-#define    RC_DS_CHAIN_BUFFER_TOO_LARGE         0x0002
-#define    RC_DS_DEVICE_BUSY                    0x0003
-#define    RC_DS_DEVICE_LOCKED                  0x0004
-#define    RC_DS_DEVICE_NOT_AVAILABLE           0x0005
-#define    RC_DS_DEVICE_RESET                   0x0006
-#define    RC_DS_INAPPROPRIATE_FUNCTION         0x0007
-#define    RC_DS_INSUFFICIENT_RESOURCE_HARD     0x0008
-#define    RC_DS_INSUFFICIENT_RESOURCE_SOFT     0x0009
-#define    RC_DS_INVALID_INITIATOR_ADDRESS      0x000A
-#define    RC_DS_INVALID_MESSAGE_FLAGS          0x000B
-#define    RC_DS_INVALID_OFFSET                 0x000C
-#define    RC_DS_INVALID_PARAMETER              0x000D
-#define    RC_DS_INVALID_REQUEST                0x000E
-#define    RC_DS_INVALID_TARGET_ADDRESS         0x000F
-#define    RC_DS_MESSAGE_TOO_LARGE              0x0010
-#define    RC_DS_MESSAGE_TOO_SMALL              0x0011
-#define    RC_DS_MISSING_PARAMETER              0x0012
-#define    RC_DS_NO_SUCH_PAGE                   0x0013
-#define    RC_DS_REPLY_BUFFER_FULL              0x0014
-#define    RC_DS_TCL_ERROR                      0x0015
-#define    RC_DS_TIMEOUT                        0x0016
-#define    RC_DS_UNKNOWN_ERROR                  0x0017
-#define    RC_DS_UNKNOWN_FUNCTION               0x0018
-#define    RC_DS_UNSUPPORTED_FUNCTION           0x0019
-#define    RC_DS_UNSUPPORTED_VERSION            0x001A
-
- /* msg header defines for VersionOffset */
-#define RCMSGVER_1   0x0001
-#define SGL_OFFSET_0    RCMSGVER_1
-#define SGL_OFFSET_4    (0x0040 | RCMSGVER_1)
-#define TRL_OFFSET_5    (0x0050 | RCMSGVER_1)
-#define TRL_OFFSET_6    (0x0060 | RCMSGVER_1)
-
- /* msg header defines for MsgFlags */
-#define MSG_STATIC      0x0100
-#define MSG_64BIT_CNTXT 0x0200
-#define MSG_MULTI_TRANS 0x1000
-#define MSG_FAIL        0x2000
-#define MSG_LAST        0x4000
-#define MSG_REPLY       0x8000
-
-  /* normal LAN request message MsgFlags and VersionOffset (0x1041) */
-#define LAN_MSG_REQST  (MSG_MULTI_TRANS | SGL_OFFSET_4)
-
- /* minimum size msg */
-#define THREE_WORD_MSG_SIZE 0x00030000
-#define FOUR_WORD_MSG_SIZE  0x00040000
-#define FIVE_WORD_MSG_SIZE  0x00050000
-#define SIX_WORD_MSG_SIZE   0x00060000
-#define SEVEN_WORD_MSG_SIZE 0x00070000
-#define EIGHT_WORD_MSG_SIZE 0x00080000
-#define NINE_WORD_MSG_SIZE  0x00090000
-
-/* Special TID Assignments */
-
-#define ADAPTER_TID   0
-#define HOST_TID  1
-
- /* RedCreek private message codes */
-#define RC_PRIVATE_GET_MAC_ADDR     0x0001/**/ /* OBSOLETE */
-#define RC_PRIVATE_SET_MAC_ADDR     0x0002
-#define RC_PRIVATE_GET_LAN_STATS    0x0003
-#define RC_PRIVATE_GET_LINK_STATUS  0x0004
-#define RC_PRIVATE_SET_LINK_SPEED   0x0005
-#define RC_PRIVATE_SET_IP_AND_MASK  0x0006
-/* #define RC_PRIVATE_GET_IP_AND_MASK  0x0007 */ /* OBSOLETE */
-#define RC_PRIVATE_GET_LINK_SPEED   0x0008
-#define RC_PRIVATE_GET_FIRMWARE_REV 0x0009
-/* #define RC_PRIVATE_GET_MAC_ADDR     0x000A *//**/
-#define RC_PRIVATE_GET_IP_AND_MASK  0x000B /**/
-#define RC_PRIVATE_DEBUG_MSG        0x000C
-#define RC_PRIVATE_REPORT_DRIVER_CAPABILITY  0x000D
-
-#define RC_PRIVATE_REBOOT           0x00FF
-
-
-/* RC message header */
-typedef struct _RC_MSG_FRAME 
-{
-   U8                          VersionOffset;
-   U8                          MsgFlags;
-   U16                         MessageSize;
-   BF                          TargetAddress:TID_SZ;
-   BF                          InitiatorAddress:TID_SZ;
-   BF                          Function:FUNCTION_SZ;
-   U32                         InitiatorContext;
-   /* SGL[] */ 
-}
- RC_MSG_FRAME, *PRC_MSG_FRAME;
-
-
- /* assumed a 16K minus 256 byte space for outbound queue message frames */
-#define MSG_FRAME_SIZE  512
-#define NMBR_MSG_FRAMES 30
-
-/*
-**  Message Unit CSR definitions for RedCreek PCI45 board
-*/
-typedef struct tag_rcatu 
-{
-    volatile unsigned long APICRegSel;  /* APIC Register Select */
-    volatile unsigned long reserved0;
-    volatile unsigned long APICWinReg;  /* APIC Window Register */
-    volatile unsigned long reserved1;
-    volatile unsigned long InMsgReg0;   /* inbound message register 0 */
-    volatile unsigned long InMsgReg1;   /* inbound message register 1 */
-    volatile unsigned long OutMsgReg0;  /* outbound message register 0 */
-    volatile unsigned long OutMsgReg1;  /* outbound message register 1 */
-    volatile unsigned long InDoorReg;   /* inbound doorbell register */
-    volatile unsigned long InIntStat;   /* inbound interrupt status register */
-    volatile unsigned long InIntMask;   /* inbound interrupt mask register */
-    volatile unsigned long OutDoorReg;  /* outbound doorbell register */
-    volatile unsigned long OutIntStat;  /* outbound interrupt status register */
-    volatile unsigned long OutIntMask;  /* outbound interrupt mask register */
-    volatile unsigned long reserved2;
-    volatile unsigned long reserved3;
-    volatile unsigned long InQueue;     /* inbound queue port */
-    volatile unsigned long OutQueue;    /* outbound queue port */
-    volatile unsigned long reserved4;
-    volatile unsigned long reserver5;
-     /* RedCreek extension */
-    volatile unsigned long EtherMacLow;
-    volatile unsigned long EtherMacHi;
-    volatile unsigned long IPaddr;
-    volatile unsigned long IPmask;
-}
- ATU, *PATU;
-
- /* 
- ** typedef PAB
- **
- ** PCI Adapter Block - holds instance specific information and is located
- ** in a reserved space at the start of the message buffer allocated by user.
- */
-typedef struct
-{
-    PATU             p_atu;                /* ptr to  ATU register block */
-    PU8              pPci45LinBaseAddr;
-    PU8              pLinOutMsgBlock;
-    U32              outMsgBlockPhyAddr; 
-    PFNTXCALLBACK    pTransCallbackFunc;
-    PFNRXCALLBACK    pRecvCallbackFunc;
-    PFNCALLBACK      pRebootCallbackFunc;
-    PFNCALLBACK      pCallbackFunc;
-    U16              ADAPTERState;
-    U16              InboundMFrameSize;
-}
- PAB, *PPAB;
-
- /* 
- ** in reserved space right after PAB in host memory is area for returning
- ** values from card 
- */
-
- /* 
- ** Array of pointers to PCI Adapter Blocks.
- ** Indexed by a zero based (0-31) interface number.
- */ 
-#define MAX_ADAPTERS 32
-static PPAB  PCIAdapterBlock[MAX_ADAPTERS] = 
-{
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-
-/*
-** typedef NICSTAT
-**
-** Data structure for NIC statistics retruned from PCI card.  Data copied from
-** here to user allocated RCLINKSTATS (see rclanmtl.h) structure.
-*/
-typedef struct tag_NicStat 
-{
-        unsigned long   TX_good; 
-        unsigned long   TX_maxcol;
-        unsigned long   TX_latecol;
-        unsigned long   TX_urun;
-        unsigned long   TX_crs;         /* lost carrier sense */
-        unsigned long   TX_def;         /* transmit deferred */
-        unsigned long   TX_singlecol;   /* single collisions */
-        unsigned long   TX_multcol;
-        unsigned long   TX_totcol;
-        unsigned long   Rcv_good;
-        unsigned long   Rcv_CRCerr;
-        unsigned long   Rcv_alignerr;
-        unsigned long   Rcv_reserr;     /* rnr'd pkts */
-        unsigned long   Rcv_orun;
-        unsigned long   Rcv_cdt;
-        unsigned long   Rcv_runt;
-        unsigned long   dump_status;    /* last field directly from the chip */
-} 
- NICSTAT, *P_NICSTAT;
- 
-
-#define DUMP_DONE   0x0000A005      /* completed statistical dump */
-#define DUMP_CLEAR  0x0000A007      /* completed stat dump and clear counters */
-
-
-static volatile int msgFlag;
-
-
-/* local function prototypes */
-static void ProcessOutboundAdapterMsg(PPAB pPab, U32 phyMsgAddr);
-static int FillAdapterMsgSGLFromTCB(PU32 pMsg, PRCTCB pXmitCntrlBlock);
-static int GetAdapterStatus(PPAB pPab);
-static int SendAdapterOutboundQInitMsg(PPAB pPab);
-static int SendEnableSysMsg(PPAB pPab);
-
-
- /* 1st 100h bytes of message block is reserved for messenger instance */
-#define ADAPTER_BLOCK_RESERVED_SPACE 0x100
-
-/*
-** =========================================================================
-** InitRCApiMsgLayer()
-**
-** Initialize the RedCreek API Module and adapter.
-**
-** Inputs:  AdapterID - interface number from 0 to 15
-**          pciBaseAddr - virual base address of PCI (set by BIOS)
-**          p_msgbuf - virual address to private message block (min. 16K)
-**          p_phymsgbuf - physical address of private message block
-**          TransmitCallbackFunction - address of transmit callback function
-**          ReceiveCallbackFunction  - address of receive  callback function
-**
-** private message block is allocated by user.  It must be in locked pages.
-** p_msgbuf and p_phymsgbuf point to the same location.  Must be contigous
-** memory block of a minimum of 16K byte and long word aligned.
-** =========================================================================
-*/
-RC_RETURN
-InitRCApiMsgLayer(U16 AdapterID, U32 pciBaseAddr, 
-                  PU8 p_msgbuf,  PU8 p_phymsgbuf,
-                  PFNTXCALLBACK  TransmitCallbackFunction,
-                  PFNRXCALLBACK  ReceiveCallbackFunction,
-                  PFNCALLBACK    RebootCallbackFunction)
-{
-     int result;
-     PPAB pPab; 
-    
-#ifdef DEBUG
-     kprintf("InitAPI: Adapter:0x%04.4ux ATU:0x%08.8ulx msgbuf:0x%08.8ulx phymsgbuf:0x%08.8ulx\n"
-             "TransmitCallbackFunction:0x%08.8ulx  ReceiveCallbackFunction:0x%08.8ulx\n",
-             AdapterID, pciBaseAddr, p_msgbuf, p_phymsgbuf, TransmitCallbackFunction, ReceiveCallbackFunction);
-#endif /* DEBUG */
-
-    
-     /* Check if this interface already initialized - if so, shut it down */
-     if (PCIAdapterBlock[AdapterID] != NULL)
-     {
-          printk("PCIAdapterBlock[%d]!=NULL\n", AdapterID);
-//        RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
-          PCIAdapterBlock[AdapterID] = NULL;
-     }
-
-    /* 
-    ** store adapter instance values in adapter block.
-    ** Adapter block is at beginning of message buffer
-    */  
-    pPab = (PPAB)p_msgbuf;
-    
-    pPab->p_atu = (PATU)pciBaseAddr;
-    pPab->pPci45LinBaseAddr =  (PU8)pciBaseAddr;
-    
-     /* Set outbound message frame addr - skip over Adapter Block */
-    pPab->outMsgBlockPhyAddr = (U32)(p_phymsgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
-    pPab->pLinOutMsgBlock    = (PU8)(p_msgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
-
-     /* store callback function addresses */
-    pPab->pTransCallbackFunc = TransmitCallbackFunction;
-    pPab->pRecvCallbackFunc  = ReceiveCallbackFunction;
-    pPab->pRebootCallbackFunc  = RebootCallbackFunction;
-    pPab->pCallbackFunc  = (PFNCALLBACK)NULL;
-
-    /*
-    ** Initialize API 
-    */
-    result = GetAdapterStatus(pPab);
-    
-    if (result != RC_RTN_NO_ERROR)
-        return result;
-
-    if (pPab->ADAPTERState == ADAPTER_STATE_OPERATIONAL)
-    {
-         printk("pPab->ADAPTERState == op: resetting adapter\n");
-         RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
-    }
-        
-    result = SendAdapterOutboundQInitMsg(pPab);
-    
-    if (result != RC_RTN_NO_ERROR)
-         return result;
-
-    result = SendEnableSysMsg(pPab);
-   
-    if (result != RC_RTN_NO_ERROR)
-         return result;
-   
-    PCIAdapterBlock[AdapterID] = pPab;
-    return RC_RTN_NO_ERROR;
-}
-
-/*
-** =========================================================================
-** Disable and Enable Adapter interrupts.  Adapter interrupts are enabled at Init time
-** but can be disabled and re-enabled through these two function calls.
-** Packets will still be put into any posted received buffers and packets will
-** be sent through RCSendPacket() functions.  Disabling Adapter interrupts
-** will prevent hardware interrupt to host even though the outbound Adapter msg
-** queue is not emtpy.
-** =========================================================================
-*/
-#define i960_OUT_POST_Q_INT_BIT        0x0008 /* bit set masks interrupts */
-
-RC_RETURN RCDisableAdapterInterrupts(U16 AdapterID)
-{
-    PPAB pPab;
-
-
-    pPab = PCIAdapterBlock[AdapterID];
-    
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-        
-    pPab->p_atu->OutIntMask |= i960_OUT_POST_Q_INT_BIT;
-
-    return RC_RTN_NO_ERROR;
-}
-
-RC_RETURN RCEnableAdapterInterrupts(U16 AdapterID)
-{
-    PPAB pPab;
-
-    pPab = PCIAdapterBlock[AdapterID];
-    
-    if (pPab == NULL)
-         return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-    pPab->p_atu->OutIntMask &= ~i960_OUT_POST_Q_INT_BIT;
-    
-    return RC_RTN_NO_ERROR;
-
-}
-
-
-/*
-** =========================================================================
-** RCSendPacket()
-** =========================================================================
-*/
-RC_RETURN
-RCSendPacket(U16 AdapterID, U32 InitiatorContext, PRCTCB pTransCtrlBlock)
-{
-    U32 msgOffset;
-    PU32 pMsg;
-    int size;
-    PPAB pPab;
-
-#ifdef DEBUG
-kprintf("RCSendPacket()...\n");
-#endif /* DEBUG */
-    
-    pPab = PCIAdapterBlock[AdapterID];
-
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-     /* get Inbound free Q entry - reading from In Q gets free Q entry */
-     /* offset to Msg Frame in PCI msg block */
-
-    msgOffset = pPab->p_atu->InQueue; 
-
-    if (msgOffset == 0xFFFFFFFF)
-    {
-#ifdef DEBUG
-        kprintf("RCSendPacket(): Inbound Free Q empty!\n");
-#endif /* DEBUG */
-        return RC_RTN_FREE_Q_EMPTY;
-    }
-        
-     /* calc virual address of msg - virual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-    size = FillAdapterMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);
-
-    if (size == -1) /* error processing TCB - send NOP msg */
-    {
-#ifdef DEBUG
-            kprintf("RCSendPacket(): Error Rrocess TCB!\n");
-#endif /* DEBUG */
-        pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
-        pMsg[1] = UTIL_NOP << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-        return RC_RTN_TCB_ERROR;
-    }
-    else /* send over msg header */
-    {    
-        pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
-        pMsg[1] = LAN_PACKET_SEND << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-        pMsg[2] = InitiatorContext;
-        pMsg[3] = 0;  /* batch reply */
-         /* post to Inbound Post Q */   
-        pPab->p_atu->InQueue = msgOffset;
-        return RC_RTN_NO_ERROR;
-    }
-}
- 
-
-/*
-** =========================================================================
-** RCPostRecvBuffer()
-**
-** inputs:  pBufrCntrlBlock - pointer to buffer control block
-**
-** returns TRUE if successful in sending message, else FALSE.
-** =========================================================================
-*/
-RC_RETURN
-RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransCtrlBlock)
-{
-    U32 msgOffset;
-    PU32 pMsg;
-    int size;
-    PPAB pPab;
-
-#ifdef DEBUG
-kprintf("RCPostRecvBuffers()...\n");
-#endif /* DEBUG */
-    
-     /* search for DeviceHandle */
-    pPab = PCIAdapterBlock[AdapterID];
-
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-
-     /* get Inbound free Q entry - reading from In Q gets free Q entry */
-     /* offset to Msg Frame in PCI msg block */
-    msgOffset = pPab->p_atu->InQueue; 
-
-    if (msgOffset == 0xFFFFFFFF)
-    {
-#ifdef DEBUG
-            kprintf("RCPostRecvBuffers(): Inbound Free Q empty!\n");
-#endif /* DEBUG */
-        return RC_RTN_FREE_Q_EMPTY;
-   
-    }
-     /* calc virual address of msg - virual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-    size = FillAdapterMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);
-
-    if (size == -1) /* error prcessing TCB - send 3 DWORD private msg == NOP */
-    {
-#ifdef DEBUG
-        kprintf("RCPostRecvBuffers(): Error Processing TCB! size = %d\n", size);
-#endif /* DEBUG */
-        pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
-        pMsg[1] = UTIL_NOP << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-         /* post to Post Q */   
-        pPab->p_atu->InQueue = msgOffset;
-        return RC_RTN_TCB_ERROR;
-    }
-    else /* send over size msg header */
-    {    
-        pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
-        pMsg[1] = LAN_RECEIVE_POST << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-        pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-        pMsg[3] = *(PU32)pTransCtrlBlock; /* number of packet buffers */
-         /* post to Post Q */   
-        pPab->p_atu->InQueue = msgOffset;
-        return RC_RTN_NO_ERROR;
-    }
-}
-
-
-/*
-** =========================================================================
-** RCProcMsgQ()
-**
-** Process outbound message queue until empty.
-** =========================================================================
-*/
-void 
-RCProcMsgQ(U16 AdapterID)
-{
-    U32 phyAddrMsg;
-    PU8 p8Msg;
-    PU32 p32;
-    U16 count;
-    PPAB pPab;
-    unsigned char debug_msg[20];
-    
-    pPab = PCIAdapterBlock[AdapterID];
-    
-    if (pPab == NULL)
-        return;
-    
-    phyAddrMsg = pPab->p_atu->OutQueue;
-
-    while (phyAddrMsg != 0xFFFFFFFF)
-    {
-        p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
-        p32 = (PU32)p8Msg;
-        
-        //printk(" msg: 0x%x  0x%x \n", p8Msg[7], p32[5]);
-
-     /* 
-     ** Send Packet Reply Msg
-     */
-        if (LAN_PACKET_SEND == p8Msg[7])  /* function code byte */
-        {
-            count = *(PU16)(p8Msg+2);
-            count -= p8Msg[0] >> 4;
-                                      /* status, count, context[], adapter */
-           (*pPab->pTransCallbackFunc)(p8Msg[19], count, p32+5, AdapterID);
-        }             
-     /* 
-     ** Receive Packet Reply Msg */
-        else if (LAN_RECEIVE_POST == p8Msg[7])
-        {
-#ifdef DEBUG    
-    kprintf("RECV_REPLY pPab:0x%08.8ulx p8Msg:0x%08.8ulx p32:0x%08.8ulx\n", pPab, p8Msg, p32);
-    kprintf("msg: 0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
-            p32[0], p32[1], p32[2], p32[3]);
-    kprintf("     0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
-            p32[4], p32[5], p32[6], p32[7]);
-    kprintf("     0x%08.8ulx:0X%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
-            p32[8], p32[9], p32[10], p32[11]);
-#endif
-               /*  status, count, buckets remaining, packetParmBlock, adapter */
-          (*pPab->pRecvCallbackFunc)(p8Msg[19], p8Msg[12], p32[5], p32+6, AdapterID);
-      
-      
-        }
-        else if (LAN_RESET == p8Msg[7] || LAN_SHUTDOWN == p8Msg[7])
-        {
-            if (pPab->pCallbackFunc)
-            {
-                (*pPab->pCallbackFunc)(p8Msg[19],0,0,AdapterID);
-            }
-            else
-            {
-                pPab->pCallbackFunc = (PFNCALLBACK) 1;
-            }
-            //PCIAdapterBlock[AdapterID] = 0;
-        }
-        else if (RC_PRIVATE == p8Msg[7])
-        {
-            //printk("i2o private 0x%x, 0x%x \n", p8Msg[7], p32[5]);
-          switch (p32[5])
-          {
-          case RC_PRIVATE_DEBUG_MSG:
-               msgFlag = 1;
-               /*printk("Received RC_PRIVATE msg\n");*/
-               debug_msg[15]  = (p32[6]&0xff000000) >> 24;
-               debug_msg[14]  = (p32[6]&0x00ff0000) >> 16;
-               debug_msg[13]  = (p32[6]&0x0000ff00) >> 8;
-               debug_msg[12]  = (p32[6]&0x000000ff);
-
-               debug_msg[11]  = (p32[7]&0xff000000) >> 24;
-               debug_msg[10]  = (p32[7]&0x00ff0000) >> 16;
-               debug_msg[ 9]  = (p32[7]&0x0000ff00) >> 8;
-               debug_msg[ 8]  = (p32[7]&0x000000ff);
-
-               debug_msg[ 7]  = (p32[8]&0xff000000) >> 24;
-               debug_msg[ 6]  = (p32[8]&0x00ff0000) >> 16;
-               debug_msg[ 5] = (p32[8]&0x0000ff00) >> 8;
-               debug_msg[ 4] = (p32[8]&0x000000ff);
-
-               debug_msg[ 3] = (p32[9]&0xff000000) >> 24;
-               debug_msg[ 2] = (p32[9]&0x00ff0000) >> 16;
-               debug_msg[ 1] = (p32[9]&0x0000ff00) >> 8;
-               debug_msg[ 0] = (p32[9]&0x000000ff);
-
-               debug_msg[16] = '\0';
-               printk (debug_msg);
-               break;
-          case RC_PRIVATE_REBOOT:
-               printk("Adapter reboot initiated...\n");
-               if (pPab->pRebootCallbackFunc)
-               {
-                    (*pPab->pRebootCallbackFunc)(0,0,0,AdapterID);
-               }
-               break;
-          default:
-                printk("Unknown private msg received: 0x%x\n",
-                      p32[5]);
-               break;
-          }
-        }
-
-     /* 
-     ** Process other Msg's
-     */
-        else
-        {
-            ProcessOutboundAdapterMsg(pPab, phyAddrMsg);
-        }
-        
-         /* return MFA to outbound free Q*/
-        pPab->p_atu->OutQueue = phyAddrMsg;
-    
-         /* any more msgs? */
-        phyAddrMsg = pPab->p_atu->OutQueue;
-    }
-}
-
-
-/*
-** =========================================================================
-**  Returns LAN interface statistical counters to space provided by caller at
-**  StatsReturnAddr.  Returns 0 if success, else RC_RETURN code.
-**  This function will call the WaitCallback function provided by
-**  user while waiting for card to respond.
-** =========================================================================
-*/
-RC_RETURN
-RCGetLinkStatistics(U16 AdapterID, 
-                    P_RCLINKSTATS StatsReturnAddr,
-                    PFNWAITCALLBACK WaitCallback)
-{
-    U32 msgOffset;
-    volatile U32 timeout;
-    volatile PU32 pMsg;
-    volatile PU32 p32, pReturnAddr;
-    P_NICSTAT pStats;
-    int i;
-    PPAB pPab;
-
-/*kprintf("Get82558Stats() StatsReturnAddr:0x%08.8ulx\n", StatsReturnAddr);*/
-
-    pPab = PCIAdapterBlock[AdapterID];
-
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-    msgOffset = pPab->p_atu->InQueue;
-
-    if (msgOffset == 0xFFFFFFFF)
-    {
-    #ifdef DEBUG
-        kprintf("Get8255XStats(): Inbound Free Q empty!\n");
-    #endif
-        return RC_RTN_FREE_Q_EMPTY;
-    }
-
-     /* calc virual address of msg - virual already mapped to physical */
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-/*dprintf("Get82558Stats - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
-/*dprintf("Get82558Stats - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
-
-    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-    pMsg[3] = 0x112; /* transaction context */
-    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LAN_STATS;
-    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-
-    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-
-    pStats = (P_NICSTAT)p32;
-    pStats->dump_status = 0xFFFFFFFF;
-
-     /* post to Inbound Post Q */
-    pPab->p_atu->InQueue = msgOffset;
-
-    timeout = 100000;
-    while (1)
-    {
-        if (WaitCallback)
-                (*WaitCallback)();
-        
-        for (i = 0; i < 1000; i++)
-            ;
-
-        if (pStats->dump_status != 0xFFFFFFFF)
-            break;
-
-        if (!timeout--)
-        {
-        #ifdef DEBUG
-            kprintf("RCGet82558Stats() Timeout waiting for NIC statistics\n");
-        #endif
-            return RC_RTN_MSG_REPLY_TIMEOUT;
-        }
-    }
-    
-    pReturnAddr = (PU32)StatsReturnAddr;
-    
-     /* copy Nic stats to user's structure */
-    for (i = 0; i < (int) sizeof(RCLINKSTATS) / 4; i++)
-        pReturnAddr[i] = p32[i];
-   
-    return RC_RTN_NO_ERROR;     
-}
-
-
-/*
-** =========================================================================
-** Get82558LinkStatus()
-** =========================================================================
-*/
-RC_RETURN
-RCGetLinkStatus(U16 AdapterID, PU32 ReturnAddr, PFNWAITCALLBACK WaitCallback)
-{
-    U32 msgOffset;
-    volatile U32 timeout;
-    volatile PU32 pMsg;
-    volatile PU32 p32;
-    PPAB pPab;
-
-/*kprintf("Get82558LinkStatus() ReturnPhysAddr:0x%08.8ulx\n", ReturnAddr);*/
-
-    pPab = PCIAdapterBlock[AdapterID];
-
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-    msgOffset = pPab->p_atu->InQueue;
-
-    if (msgOffset == 0xFFFFFFFF)
-    {
-    #ifdef DEBUG
-        dprintf("Get82558LinkStatus(): Inbound Free Q empty!\n");
-    #endif
-        return RC_RTN_FREE_Q_EMPTY;
-    }
-
-     /* calc virual address of msg - virual already mapped to physical */
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-/*dprintf("Get82558LinkStatus - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
-/*dprintf("Get82558LinkStatus - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
-
-    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-    pMsg[3] = 0x112; /* transaction context */
-    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_STATUS;
-    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-
-    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-    *p32 = 0xFFFFFFFF;
-    
-     /* post to Inbound Post Q */
-    pPab->p_atu->InQueue = msgOffset;
-
-    timeout = 100000;
-    while (1)
-    {
-        U32 i;
-
-        if (WaitCallback)
-                (*WaitCallback)();
-        
-        for (i = 0; i < 1000; i++)
-            ;
-
-        if (*p32 != 0xFFFFFFFF)
-            break;
-
-        if (!timeout--)
-        {
-        #ifdef DEBUG
-            kprintf("Timeout waiting for link status\n");
-        #endif    
-            return RC_RTN_MSG_REPLY_TIMEOUT;
-        }
-    }
-    
-    *ReturnAddr = *p32; /* 1 = up 0 = down */
-    
-    return RC_RTN_NO_ERROR;
-    
-}
-
-/*
-** =========================================================================
-** RCGetMAC()
-**
-** get the MAC address the adapter is listening for in non-promiscous mode.
-** MAC address is in media format.
-** =========================================================================
-*/
-RC_RETURN
-RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback)
-{
-     unsigned i, timeout;
-     U32      off;
-     PU32     p;
-     U32      temp[2];
-     PPAB     pPab;
-     PATU     p_atu;
-    
-     pPab = PCIAdapterBlock[AdapterID];
-    
-     if (pPab == NULL)
-          return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-     p_atu = pPab->p_atu;
-
-     p_atu->EtherMacLow = 0;     /* first zero return data */
-     p_atu->EtherMacHi = 0;
-    
-     off = p_atu->InQueue;   /* get addresss of message */
- 
-     if (0xFFFFFFFF == off)
-          return RC_RTN_FREE_Q_EMPTY;
-
-     p = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-#ifdef RCDEBUG
-     printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", 
-            (uint)p_atu, (uint)off, (uint)p);
-#endif /* RCDEBUG */
-     /* setup private message */
-     p[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
-     p[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-     p[2] = 0;               /* initiator context */
-     p[3] = 0x218;           /* transaction context */
-     p[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_MAC_ADDR;
-
-
-     p_atu->InQueue = off;   /* send it to the device */
-#ifdef RCDEBUG
-     printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", 
-            (uint)p_atu, (uint)off, (uint)p);
-#endif /* RCDEBUG */
-
-     /* wait for the rcpci45 board to update the info */
-     timeout = 1000000;
-     while (0 == p_atu->EtherMacLow) 
-     {
-          if (WaitCallback)
-               (*WaitCallback)();
-    
-          for (i = 0; i < 1000; i++)
-               ;
-
-          if (!timeout--)
-          {
-               printk("rc_getmac: Timeout\n");
-               return RC_RTN_MSG_REPLY_TIMEOUT;
-          }
-     }
-    
-     /* read the mac address  */
-     temp[0] = p_atu->EtherMacLow;
-     temp[1] = p_atu->EtherMacHi;
-     memcpy((char *)mac, (char *)temp, 6);
-
-
-#ifdef RCDEBUG
-//    printk("rc_getmac: 0x%X\n", ptr);
-#endif /* RCDEBUG */
-
-     return RC_RTN_NO_ERROR;
-}
-
- 
-/*
-** =========================================================================
-** RCSetMAC()
-**
-** set MAC address the adapter is listening for in non-promiscous mode.
-** MAC address is in media format.
-** =========================================================================
-*/
-RC_RETURN
-RCSetMAC(U16 AdapterID, PU8 mac)
-{
-    U32  off;
-    PU32 pMsg;
-    PPAB pPab;
-
-
-    pPab = PCIAdapterBlock[AdapterID];
-    
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-    off = pPab->p_atu->InQueue; /* get addresss of message */
-    
-    if (0xFFFFFFFF == off)
-         return RC_RTN_FREE_Q_EMPTY;
-    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-    /* setup private message */
-    pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = 0;                 /* initiator context */
-    pMsg[3] = 0x219;             /* transaction context */
-    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_MAC_ADDR;
-    pMsg[5] = *(unsigned *)mac;  /* first four bytes */
-    pMsg[6] = *(unsigned *)(mac + 4); /* last two bytes */
-    
-    pPab->p_atu->InQueue = off;   /* send it to the device */
-
-    return RC_RTN_NO_ERROR ;
-}
-
-
-/*
-** =========================================================================
-** RCSetLinkSpeed()
-**
-** set ethernet link speed. 
-** input: speedControl - determines action to take as follows
-**          0 = reset and auto-negotiate (NWay)
-**          1 = Full Duplex 100BaseT
-**          2 = Half duplex 100BaseT
-**          3 = Full Duplex  10BaseT
-**          4 = Half duplex  10BaseT
-**          all other values are ignore (do nothing)
-** =========================================================================
-*/
-RC_RETURN
-RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode)
-{
-    U32  off;
-    PU32 pMsg;
-    PPAB pPab;
-
-    
-    pPab =PCIAdapterBlock[AdapterID];
-     
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-    off = pPab->p_atu->InQueue; /* get addresss of message */
-    
-    if (0xFFFFFFFF == off)
-         return RC_RTN_FREE_Q_EMPTY;
-    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-    /* setup private message */
-    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = 0;                 /* initiator context */
-    pMsg[3] = 0x219;             /* transaction context */
-    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_LINK_SPEED;
-    pMsg[5] = LinkSpeedCode;     /* link speed code */
-
-    pPab->p_atu->InQueue = off;   /* send it to the device */
-
-    return RC_RTN_NO_ERROR ;
-}
-
-/*
-** =========================================================================
-** RCGetLinkSpeed()
-**
-** get ethernet link speed. 
-**
-** 0 = Unknown
-** 1 = Full Duplex 100BaseT
-** 2 = Half duplex 100BaseT
-** 3 = Full Duplex  10BaseT
-** 4 = Half duplex  10BaseT
-**
-** =========================================================================
-*/
-RC_RETURN
-RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback)
-{
-     U32 msgOffset, timeout;
-     PU32 pMsg;
-     volatile PU32 p32;
-     U8 AdapterLinkSpeed;
-     PPAB pPab;
-
-     pPab =PCIAdapterBlock[AdapterID];
-
-
-     msgOffset = pPab->p_atu->InQueue;
-
-
-     if (msgOffset == 0xFFFFFFFF)
-     {
-          kprintf("RCGetLinkSpeed(): Inbound Free Q empty!\n");
-          return RC_RTN_FREE_Q_EMPTY;
-     }
-
-     /* calc virtual address of msg - virtual already mapped to physical */    
-     pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-     /* virtual pointer to return buffer - clear first two dwords */
-     p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-     p32[0] = 0xff;
-
-     /* setup private message */
-     pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
-     pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-     pMsg[2] = 0;                 /* initiator context */
-     pMsg[3] = 0x219;             /* transaction context */
-     pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_SPEED;
-     /* phys address to return status - area right after PAB */
-     pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-   
-     /* post to Inbound Post Q */   
-
-     pPab->p_atu->InQueue = msgOffset;
-
-     /* wait for response */
-     timeout = 1000000;
-     while(1)
-     {
-          int i;
-        
-          if (WaitCallback)
-                (*WaitCallback)();
-
-          for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
-               ;
-            
-          if (p32[0] != 0xff)
-               break;
-            
-          if (!timeout--)
-          {
-               kprintf("Timeout waiting for link speed from adapter\n");
-               kprintf("0x%08.8ulx\n", p32[0]);
-               return RC_RTN_NO_LINK_SPEED;
-          }
-     }
-
-     /* get Link speed */
-     AdapterLinkSpeed = (U8)((volatile PU8)p32)[0] & 0x0f;
-
-     *pLinkSpeedCode= AdapterLinkSpeed;
-
-     return RC_RTN_NO_ERROR;
-}
-
-/*
-** =========================================================================
-** RCReportDriverCapability(U16 AdapterID, U32 capability)
-**
-** Currently defined bits:
-** WARM_REBOOT_CAPABLE   0x01
-**
-** =========================================================================
-*/
-RC_RETURN
-RCReportDriverCapability(U16 AdapterID, U32 capability)
-{
-    U32  off;
-    PU32 pMsg;
-    PPAB pPab;
-
-    pPab =PCIAdapterBlock[AdapterID];
-     
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-    off = pPab->p_atu->InQueue; /* get addresss of message */
-    
-    if (0xFFFFFFFF == off)
-         return RC_RTN_FREE_Q_EMPTY;
-    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-    /* setup private message */
-    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = 0;                 /* initiator context */
-    pMsg[3] = 0x219;             /* transaction context */
-    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_REPORT_DRIVER_CAPABILITY;
-    pMsg[5] = capability;
-
-    pPab->p_atu->InQueue = off;   /* send it to the device */
-
-    return RC_RTN_NO_ERROR ;
-}
-
-/*
-** =========================================================================
-** RCGetFirmwareVer()
-**
-** Return firmware version in the form "SoftwareVersion : Bt BootVersion"
-**
-** =========================================================================
-*/
-RC_RETURN
-RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback)
-{
-     U32 msgOffset, timeout;
-     PU32 pMsg;
-     volatile PU32 p32;
-     PPAB pPab;
-
-     pPab =PCIAdapterBlock[AdapterID];
-
-     msgOffset = pPab->p_atu->InQueue;
-
-
-     if (msgOffset == 0xFFFFFFFF)
-     {
-          kprintf("RCGetFirmwareVer(): Inbound Free Q empty!\n");
-          return RC_RTN_FREE_Q_EMPTY;
-     }
-
-     /* calc virtual address of msg - virtual already mapped to physical */    
-     pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-     /* virtual pointer to return buffer - clear first two dwords */
-     p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-     p32[0] = 0xff;
-
-     /* setup private message */
-     pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
-     pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-     pMsg[2] = 0;                 /* initiator context */
-     pMsg[3] = 0x219;             /* transaction context */
-     pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_FIRMWARE_REV;
-     /* phys address to return status - area right after PAB */
-     pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-
-
-   
-     /* post to Inbound Post Q */   
-
-     pPab->p_atu->InQueue = msgOffset;
-
-    
-     /* wait for response */
-     timeout = 1000000;
-     while(1)
-     {
-          int i;
-        
-          if (WaitCallback)
-                (*WaitCallback)();
-            
-          for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
-               ;
-
-          if (p32[0] != 0xff)
-               break;
-            
-          if (!timeout--)
-          {
-               kprintf("Timeout waiting for link speed from adapter\n");
-               return RC_RTN_NO_FIRM_VER;
-          }
-     }
-
-     strcpy(pFirmString, (PU8)p32);
-     return RC_RTN_NO_ERROR;
-}
-
-/*
-** =========================================================================
-** RCResetLANCard()
-**
-** ResourceFlags indicates whether to return buffer resource explicitly
-** to host or keep and reuse.
-** CallbackFunction (if not NULL) is the function to be called when 
-** reset is complete.
-** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when
-** reset is done (if not NULL).
-**
-** =========================================================================
-*/
-RC_RETURN 
-RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
-{
-    unsigned long off;
-    unsigned long *pMsg;
-    PPAB pPab;
-    int i;
-    long timeout = 0;
-
-    
-    pPab =PCIAdapterBlock[AdapterID];
-     
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-
-    off = pPab->p_atu->InQueue; /* get addresss of message */
-    
-    if (0xFFFFFFFF == off)
-         return RC_RTN_FREE_Q_EMPTY;
-    
-    pPab->pCallbackFunc = CallbackFunction;
-
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-    /* setup message */
-    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = LAN_RESET << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-    pMsg[3] = ResourceFlags << 16;   /* resource flags */
-
-    pPab->p_atu->InQueue = off;   /* send it to the device */
-
-    if (CallbackFunction == (PFNCALLBACK)NULL)
-    {
-        /* call RCProcMsgQ() until something in pPab->pCallbackFunc
-           or until timer goes off */
-        while (pPab->pCallbackFunc == (PFNCALLBACK)NULL)
-        {
-            RCProcMsgQ(AdapterID);
-            for (i = 0; i < 100000; i++)     /* please don't hog the bus!!! */
-               ;
-            timeout++;
-            if (timeout > 10000)
-            {
-                break;
-            }
-        }
-        if (ReturnAddr != (PU32)NULL)
-           *ReturnAddr = (U32)pPab->pCallbackFunc;
-    }
-
-    return RC_RTN_NO_ERROR ;
-}
-/*
-** =========================================================================
-** RCResetAdapter()
-**
-** Send StatusGet Msg, wait for results return directly to buffer.
-**
-** =========================================================================
-*/
-RC_RETURN 
-RCResetAdapter(U16 AdapterID)
-{
-     U32 msgOffset, timeout;
-     PU32 pMsg;
-     PPAB pPab;
-     volatile PU32 p32;
-    
-     pPab = PCIAdapterBlock[AdapterID];
-     msgOffset = pPab->p_atu->InQueue;
-
-     if (msgOffset == 0xFFFFFFFF)
-     {
-          return RC_RTN_FREE_Q_EMPTY;
-     }
-
-     /* calc virtual address of msg - virtual already mapped to physical */    
-     pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-     pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
-     pMsg[1] = RC_CMD_ADAPTER_RESET << 24 | HOST_TID << 12 | ADAPTER_TID;
-     pMsg[2] = 0; /* universal context */
-     pMsg[3] = 0; /* universal context */
-     pMsg[4] = 0; /* universal context */
-     pMsg[5] = 0; /* universal context */
-     /* phys address to return status - area right after PAB */
-     pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-     pMsg[7] = 0;
-     pMsg[8] = 1;  /*  return 1 byte */
-
-     /* virual pointer to return buffer - clear first two dwords */
-     p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-     p32[0] = 0;
-     p32[1] = 0;
-
-     /* post to Inbound Post Q */   
-
-     pPab->p_atu->InQueue = msgOffset;
-
-     /* wait for response */
-     timeout = 1000000;
-     while(1)
-     {
-          int i;
-        
-          for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
-               ;
-            
-          if (p32[0] || p32[1])
-               break;
-            
-          if (!timeout--)
-          {
-               printk("RCResetAdapter timeout\n");
-               return RC_RTN_MSG_REPLY_TIMEOUT;
-          }
-     }
-     return RC_RTN_NO_ERROR;
-}
-
-/*
-** =========================================================================
-** RCShutdownLANCard()
-**
-** ResourceFlags indicates whether to return buffer resource explicitly
-** to host or keep and reuse.
-** CallbackFunction (if not NULL) is the function to be called when 
-** shutdown is complete.
-** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when
-** shutdown is done (if not NULL).
-**
-** =========================================================================
-*/
-RC_RETURN 
-RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
-{
-    volatile PU32 pMsg;
-    U32 off;
-    PPAB pPab;
-    int i;
-    long timeout = 0;
-
-    pPab = PCIAdapterBlock[AdapterID];
-
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-
-    off = pPab->p_atu->InQueue; /* get addresss of message */
-    
-    if (0xFFFFFFFF == off)
-         return RC_RTN_FREE_Q_EMPTY;
-
-    pPab->pCallbackFunc = CallbackFunction;
-    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-    /* setup message */
-    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = LAN_SHUTDOWN << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-    pMsg[3] = ResourceFlags << 16;   /* resource flags */
-
-    pPab->p_atu->InQueue = off;   /* send it to the device */
-
-    if (CallbackFunction == (PFNCALLBACK)NULL)
-    {
-        /* call RCProcMsgQ() until something in pPab->pCallbackFunc
-           or until timer goes off */
-        while (pPab->pCallbackFunc == (PFNCALLBACK)NULL)
-        {
-            RCProcMsgQ(AdapterID);
-            for (i = 0; i < 100000; i++)     /* please don't hog the bus!!! */
-               ;
-            timeout++;
-            if (timeout > 10000)
-            {
-                break;
-            }
-        }
-        if (ReturnAddr != (PU32)NULL)
-           *ReturnAddr = (U32)pPab->pCallbackFunc;
-    }
-    return RC_RTN_NO_ERROR ;
-}
-
-
-/*
-** =========================================================================
-** RCSetRavlinIPandMask()
-**
-** Set the Ravlin 45/PCI cards IP address and network mask.
-**
-** IP address and mask must be in network byte order.
-** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be
-** 0x04030201 and 0x00FFFFFF on a little endian machine.
-**
-** =========================================================================
-*/
-RC_RETURN
-RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask)
-{
-    volatile PU32 pMsg;
-    U32 off;
-    PPAB pPab;
-
-    pPab = PCIAdapterBlock[AdapterID];
-
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-
-    off = pPab->p_atu->InQueue; /* get addresss of message */
-    
-    if (0xFFFFFFFF == off)
-         return RC_RTN_FREE_Q_EMPTY;
-    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-    /* setup private message */
-    pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = 0;                 /* initiator context */
-    pMsg[3] = 0x219;             /* transaction context */
-    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_IP_AND_MASK;
-    pMsg[5] = ipAddr; 
-    pMsg[6] = netMask;
-
-
-    pPab->p_atu->InQueue = off;   /* send it to the device */
-    return RC_RTN_NO_ERROR ;
-
-}
-
-/*
-** =========================================================================
-** RCGetRavlinIPandMask()
-**
-** get the IP address and MASK from the card
-** 
-** =========================================================================
-*/
-RC_RETURN
-RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, 
-                        PFNWAITCALLBACK WaitCallback)
-{
-    unsigned i, timeout;
-    U32      off;
-    PU32     pMsg, p32;
-    PPAB     pPab;
-    PATU     p_atu;
-    
-#ifdef DEBUG
-    kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr);
-#endif /* DEBUG */
-
-    pPab = PCIAdapterBlock[AdapterID];
-    
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-    p_atu = pPab->p_atu;
-    off = p_atu->InQueue;   /* get addresss of message */
- 
-   if (0xFFFFFFFF == off)
-         return RC_RTN_FREE_Q_EMPTY;
-
-    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-    *p32 = 0xFFFFFFFF;
-
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-#ifdef DEBUG
-    kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
-#endif /* DEBUG */
-    /* setup private message */
-    pMsg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = 0;               /* initiator context */
-    pMsg[3] = 0x218;           /* transaction context */
-    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_IP_AND_MASK;
-    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-
-    p_atu->InQueue = off;   /* send it to the device */
-#ifdef DEBUG
-    kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
-#endif /* DEBUG */
-
-     /* wait for the rcpci45 board to update the info */
-    timeout = 100000;
-    while (0xffffffff == *p32)
-    {
-        if (WaitCallback)
-                (*WaitCallback)();
-    
-        for (i = 0; i < 1000; i++)
-            ;
-
-        if (!timeout--)
-        {
-        #ifdef DEBUG
-            kprintf("RCGetRavlinIPandMask: Timeout\n");
-        #endif /* DEBUG */
-             return RC_RTN_MSG_REPLY_TIMEOUT;
-        }
-    }
-
-#ifdef DEBUG
-    kprintf("RCGetRavlinIPandMask: after time out\n", \
-            "p32[0] (IpAddr) 0x%08.8ulx, p32[1] (IPmask) 0x%08.8ulx\n", p32[0], p32[1]);
-#endif /* DEBUG */
-    
-    /* send IP and mask to user's space  */
-    *pIpAddr  = p32[0];
-    *pNetMask = p32[1];
-
-
-#ifdef DEBUG
-    kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr);
-#endif /* DEBUG */
-
-    return RC_RTN_NO_ERROR;
-}
-
-/* 
-** /////////////////////////////////////////////////////////////////////////
-** /////////////////////////////////////////////////////////////////////////
-**
-**                        local functions
-**
-** /////////////////////////////////////////////////////////////////////////
-** /////////////////////////////////////////////////////////////////////////
-*/
-
-/*
-** =========================================================================
-** SendAdapterOutboundQInitMsg()
-**
-** =========================================================================
-*/
-static int 
-SendAdapterOutboundQInitMsg(PPAB pPab)
-{
-    U32 msgOffset, timeout, phyOutQFrames, i;
-    volatile PU32 pMsg;
-    volatile PU32 p32;
-    
-    
-    
-    msgOffset = pPab->p_atu->InQueue;
-
-    
-    if (msgOffset == 0xFFFFFFFF)
-    {
-#ifdef DEBUG
-        kprintf("SendAdapterOutboundQInitMsg(): Inbound Free Q empty!\n");
-#endif /* DEBUG */
-        return RC_RTN_FREE_Q_EMPTY;
-    }
-    
-    
-     /* calc virual address of msg - virual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-#ifdef DEBUG
-kprintf("SendAdapterOutboundQInitMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
-#endif /* DEBUG */
-
-    pMsg[0] = EIGHT_WORD_MSG_SIZE | TRL_OFFSET_6;
-    pMsg[1] = RC_CMD_OUTBOUND_INIT << 24 | HOST_TID << 12 | ADAPTER_TID;
-    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-    pMsg[3] = 0x106; /* transaction context */
-    pMsg[4] = 4096; /* Host page frame size */
-    pMsg[5] = MSG_FRAME_SIZE  << 16 | 0x80; /* outbound msg frame size and Initcode */
-    pMsg[6] = 0xD0000004;       /* simple sgl element LE, EOB */
-     /* phys address to return status - area right after PAB */
-    pMsg[7] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-
-     /* virual pointer to return buffer - clear first two dwords */
-    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-    p32[0] = 0;
-    
-     /* post to Inbound Post Q */   
-    pPab->p_atu->InQueue = msgOffset;
-    
-  /* wait for response */
-    timeout = 100000;
-    while(1)
-    {
-        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
-            ;
-            
-        if (p32[0])
-            break;
-            
-        if (!timeout--)
-        {
-#ifdef DEBUG
-            kprintf("Timeout wait for InitOutQ InPrgress status from adapter\n");
-#endif /* DEBUG */
-            return RC_RTN_NO_STATUS;
-        }
-    }
-
-    timeout = 100000;
-    while(1)
-    {
-        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
-            ;
-            
-        if (p32[0] == RC_CMD_OUTBOUND_INIT_COMPLETE)
-            break;
-
-        if (!timeout--)
-        {
-#ifdef DEBUG
-            kprintf("Timeout wait for InitOutQ Complete status from adapter\n");
-#endif /* DEBUG */
-            return RC_RTN_NO_STATUS;
-        }
-    }
-
-     /* load PCI outbound free Q with MF physical addresses */
-    phyOutQFrames = pPab->outMsgBlockPhyAddr;
-
-    for (i = 0; i < NMBR_MSG_FRAMES; i++)
-    {
-        pPab->p_atu->OutQueue = phyOutQFrames;
-        phyOutQFrames += MSG_FRAME_SIZE;
-    }
-    return RC_RTN_NO_ERROR;
-}
-
-
-/*
-** =========================================================================
-** GetAdapterStatus()
-**
-** Send StatusGet Msg, wait for results return directly to buffer.
-**
-** =========================================================================
-*/
-static int 
-GetAdapterStatus(PPAB pPab)
-{
-    U32 msgOffset, timeout;
-    PU32 pMsg;
-    volatile PU32 p32;
-    
-    
-    msgOffset = pPab->p_atu->InQueue; 
-    printk("GetAdapterStatus: msg offset = 0x%x\n", msgOffset);
-    if (msgOffset == 0xFFFFFFFF)
-    {
-#ifdef DEBUG
-        kprintf("GetAdapterStatus(): Inbound Free Q empty!\n");
-#endif /* DEBUG */
-        return RC_RTN_FREE_Q_EMPTY;
-    }
-
-     /* calc virual address of msg - virual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-    pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_CMD_STATUS_GET << 24 | HOST_TID << 12 | ADAPTER_TID;
-    pMsg[2] = 0; /* universal context */
-    pMsg[3] = 0; /* universal context */
-    pMsg[4] = 0; /* universal context */
-    pMsg[5] = 0; /* universal context */
-     /* phys address to return status - area right after PAB */
-    pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-    pMsg[7] = 0;
-    pMsg[8] = 88;  /*  return 88 bytes */
-
-     /* virual pointer to return buffer - clear first two dwords */
-    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-    p32[0] = 0;
-    p32[1] = 0;
-
-#ifdef DEBUG
-kprintf("GetAdapterStatus - pMsg:0x%08.8ulx, msgOffset:0x%08.8ulx, [1]:0x%08.8ulx, [6]:0x%08.8ulx\n",
-    pMsg, msgOffset, pMsg[1], pMsg[6]);
-#endif /* DEBUG */
-   
-     /* post to Inbound Post Q */   
-    pPab->p_atu->InQueue = msgOffset;
-    
-#ifdef DEBUG
-kprintf("Return status to p32 = 0x%08.8ulx\n", p32);
-#endif /* DEBUG */
-    
-     /* wait for response */
-    timeout = 1000000;
-    while(1)
-    {
-        int i;
-        
-        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
-            ;
-            
-        if (p32[0] && p32[1])
-            break;
-            
-        if (!timeout--)
-        {
-#ifdef DEBUG
-            kprintf("Timeout waiting for status from adapter\n");
-kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
-kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
-kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
-#endif /* DEBUG */
-            return RC_RTN_NO_STATUS;
-        }
-    }
-            
-#ifdef DEBUG
-kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
-kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
-kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
-#endif /* DEBUG */
-    /* get adapter state */
-    pPab->ADAPTERState = ((volatile PU8)p32)[10];
-    pPab->InboundMFrameSize  = ((volatile PU16)p32)[6];
-    
-#ifdef DEBUG
-    kprintf("adapter state 0x%02.2x InFrameSize = 0x%04.4x\n", 
-                        pPab->ADAPTERState, pPab->InboundMFrameSize);
-#endif /* DEBUG */
-    return RC_RTN_NO_ERROR;
-}
-
-
-/*
-** =========================================================================
-** SendEnableSysMsg()
-**
-**
-** =========================================================================
-*/
-static int 
-SendEnableSysMsg(PPAB pPab)
-{
-    U32 msgOffset; // timeout;
-    volatile PU32 pMsg;
-
-    msgOffset = pPab->p_atu->InQueue;
-
-    if (msgOffset == 0xFFFFFFFF)
-    {
-#ifdef DEBUG
-        kprintf("SendEnableSysMsg(): Inbound Free Q empty!\n");
-#endif /* DEBUG */
-        return RC_RTN_FREE_Q_EMPTY;
-    }
-
-     /* calc virual address of msg - virual already mapped to physical */
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-#ifdef DEBUG
-kprintf("SendEnableSysMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
-#endif /* DEBUG */
-
-    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_CMD_SYS_ENABLE << 24 | HOST_TID << 12 | ADAPTER_TID;
-    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-    pMsg[3] = 0x110; /* transaction context */
-    pMsg[4] = 0x50657465; /*  RedCreek Private */
-
-     /* post to Inbound Post Q */
-    pPab->p_atu->InQueue = msgOffset;
-
-    return RC_RTN_NO_ERROR;
-}
-
-
-/*
-** =========================================================================
-** FillI12OMsgFromTCB()
-**
-** inputs   pMsgU32 - virual pointer (mapped to physical) of message frame
-**          pXmitCntrlBlock - pointer to caller buffer control block.
-**
-** fills in LAN SGL after Transaction Control Word or Bucket Count.
-** =========================================================================
-*/
-static int 
-FillAdapterMsgSGLFromTCB(PU32 pMsgFrame, PRCTCB pTransCtrlBlock)
-{
-    unsigned int nmbrBuffers, nmbrSeg, nmbrDwords, context, flags;
-    PU32 pTCB, pMsg;
-
- /* SGL element flags */   
-#define EOB        0x40000000
-#define LE         0x80000000
-#define SIMPLE_SGL 0x10000000
-#define BC_PRESENT 0x01000000
-
-    pTCB = (PU32)pTransCtrlBlock;
-    pMsg = pMsgFrame;
-    nmbrDwords = 0;
-
-#ifdef DEBUG
- kprintf("FillAdapterMsgSGLFromTCBX\n");
-kprintf("TCB  0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
-               pTCB[0], pTCB[1], pTCB[2], pTCB[3], pTCB[4]);
-kprintf("pTCB 0x%08.8ulx, pMsg 0x%08.8ulx\n", pTCB, pMsg);
-#endif /* DEBUG */
-    
-    nmbrBuffers = *pTCB++;
-    
-    if (!nmbrBuffers)
-    {
-        return -1;
-    }
-
-    do
-    {
-        context = *pTCB++; /* buffer tag (context) */
-        nmbrSeg = *pTCB++; /* number of segments */
-
-        if (!nmbrSeg)
-        {
-            return -1;
-        }
-        
-        flags = SIMPLE_SGL | BC_PRESENT;
-
-        if (1 == nmbrSeg)
-        {
-            flags |= EOB;
-        
-            if (1 == nmbrBuffers)
-                flags |= LE;
-        }    
-
-         /* 1st SGL buffer element has context */
-        pMsg[0] = pTCB[0] | flags ; /* send over count (segment size) */
-        pMsg[1] = context;
-        pMsg[2] = pTCB[1]; /* send buffer segment physical address */
-        nmbrDwords += 3;
-        pMsg += 3;
-        pTCB += 2;
-
-        
-        if (--nmbrSeg)
-        {
-            do
-            {
-                flags = SIMPLE_SGL;
-                
-                if (1 == nmbrSeg)
-                {
-                    flags |= EOB;
-                
-                    if (1 == nmbrBuffers)
-                        flags |= LE;
-                }    
-                
-                pMsg[0] = pTCB[0] | flags;  /* send over count */
-                pMsg[1] = pTCB[1];   /* send buffer segment physical address */
-                nmbrDwords += 2;
-                pTCB += 2;
-                pMsg += 2;
-        
-            } while (--nmbrSeg);
-        }
-        
-    } while (--nmbrBuffers);
-    
-    return nmbrDwords;
-}
-
-
-/*
-** =========================================================================
-** ProcessOutboundAdapterMsg()
-**
-** process reply message
-** * change to msg structure *
-** =========================================================================
-*/
-static void 
-ProcessOutboundAdapterMsg(PPAB pPab, U32 phyAddrMsg)
-{
-    PU8 p8Msg;
-    PU32 p32;
-  //  U16 count;
-    
-    
-    p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
-    p32 = (PU32)p8Msg;
-    
-#ifdef DEBUG
- kprintf("VXD: ProcessOutboundAdapterMsg - pPab 0x%08.8ulx, phyAdr 0x%08.8ulx, linAdr 0x%08.8ulx\n", pPab, phyAddrMsg, p8Msg);
- kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
- kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
-#endif /* DEBUG */
-
-    if (p32[4] >> 24 != RC_REPLY_STATUS_SUCCESS)
-    {
-#ifdef DEBUG
-        kprintf("Message reply status not success\n");
-#endif /* DEBUG */
-        return;
-    }
-    
-    switch (p8Msg[7] )  /* function code byte */
-    {
-        case RC_CMD_SYS_TAB_SET:
-            msgFlag = 1;
-#ifdef DEBUG
-        kprintf("Received RC_CMD_SYS_TAB_SET reply\n");
-#endif /* DEBUG */
-            break;
-
-        case RC_CMD_HRT_GET:
-            msgFlag = 1;
-#ifdef DEBUG
-        kprintf("Received RC_CMD_HRT_GET reply\n");
-#endif /* DEBUG */
-            break;
-        
-        case RC_CMD_LCT_NOTIFY:
-            msgFlag = 1;
-#ifdef DEBUG
-        kprintf("Received RC_CMD_LCT_NOTIFY reply\n");
-#endif /* DEBUG */
-            break;
-        
-        case RC_CMD_SYS_ENABLE:
-            msgFlag = 1;
-#ifdef DEBUG
-        kprintf("Received RC_CMD_SYS_ENABLE reply\n");
-#endif /* DEBUG */
-            break;
-
-        default:    
-#ifdef DEBUG
-        kprintf("Received UNKNOWN reply\n");
-#endif /* DEBUG */
-            break;
-    }
-}
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/rcmtl.h linux/drivers/net/rcmtl.h
--- v2.2.0-pre7/linux/drivers/net/rcmtl.h	Fri Jan  8 22:36:07 1999
+++ linux/drivers/net/rcmtl.h	Wed Dec 31 16:00:00 1969
@@ -1,580 +0,0 @@
-/*
-** *************************************************************************
-**
-**
-**     R C M T L . H             $Revision: 3 $
-**
-**
-**  RedCreek Message Transport Layer header file.
-**
-**  ---------------------------------------------------------------------
-**  ---     Copyright (c) 1997-1998, RedCreek Communications Inc.     ---
-**  ---                   All rights reserved.                        ---
-**  ---------------------------------------------------------------------
-**
-**  File Description:
-**
-**  Header file for host message transport layer API and data types.
-**
-**  This program is free software; you can redistribute it and/or modify
-**  it under the terms of the GNU General Public License as published by
-**  the Free Software Foundation; either version 2 of the License, or
-**  (at your option) any later version.
-
-**  This program is distributed in the hope that it will be useful,
-**  but WITHOUT ANY WARRANTY; without even the implied warranty of
-**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-**  GNU General Public License for more details.
-
-**  You should have received a copy of the GNU General Public License
-**  along with this program; if not, write to the Free Software
-**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-**
-** *************************************************************************
-*/
-
-#ifndef RCMTL_H
-#define RCMTL_H
-
-/* Linux specific includes */
-#define kprintf printk
-#ifdef RC_LINUX_MODULE     /* linux modules need non-library version of string functions */
-#include <linux/string.h>
-#else
-#include <string.h>
-#endif
-
-/* PCI/45 Configuration space values */
-#define RC_PCI45_VENDOR_ID  0x4916
-#define RC_PCI45_DEVICE_ID  0x1960
-
-
- /* RedCreek API function return values */
-#define RC_RTN_NO_ERROR             0
-#define RC_RTN_NOT_INIT             1
-#define RC_RTN_FREE_Q_EMPTY         2
-#define RC_RTN_TCB_ERROR            3
-#define RC_RTN_TRANSACTION_ERROR    4
-#define RC_RTN_ADAPTER_ALREADY_INIT 5
-#define RC_RTN_MALLOC_ERROR         6
-#define RC_RTN_ADPTR_NOT_REGISTERED 7
-#define RC_RTN_MSG_REPLY_TIMEOUT    8
-#define RC_RTN_NO_STATUS            9
-#define RC_RTN_NO_FIRM_VER         10
-#define RC_RTN_NO_LINK_SPEED       11
-
-/* Driver capability flags */
-#define WARM_REBOOT_CAPABLE      0x01
-
- /* scalar data types */
-typedef unsigned char   U8;
-typedef unsigned char*  PU8;
-typedef unsigned short  U16;
-typedef unsigned short* PU16;
-typedef unsigned long   U32;
-typedef unsigned long*  PU32;
-typedef unsigned long   BF;
-typedef int             RC_RETURN;
-
-
- /* 
- ** type PFNWAITCALLBACK
- **
- ** pointer to void function - type used for WaitCallback in some functions 
- */
-typedef void (*PFNWAITCALLBACK)(void);  /* void argument avoids compiler complaint */
-
- /*
- ** type PFNTXCALLBACK 
- **
- ** Pointer to user's transmit callback function.  This user function is
- ** called from RCProcMsgQ() when packet have been transmitted from buffers
- ** given in the RCSendPacket() function.  BufferContext is a pointer to
- ** an array of 32 bit context values.  These are the values the user assigned
- ** and passed in the TCB to the RCSendPacket() function.  PcktCount
- ** indicates the number of buffer context values in the BufferContext[] array.
- ** The User's TransmitCallbackFunction should recover (put back in free queue)
- ** the packet buffers associated with the buffer context values.
- */
-typedef void (*PFNTXCALLBACK)(U32  Status,
-                              U16  PcktCount,
-                              PU32 BufferContext,
-                              U16  AdaterID);
-
- /* 
- ** type PFNRXCALLBACK 
- **
- ** Pointer to user's receive callback function.  This user function
- ** is called from RCProcMsgQ() when packets have been received into
- ** previously posted packet buffers throught the RCPostRecvBuffers() function.
- ** The received callback function should process the Packet Descriptor Block
- ** pointed to by PacketDescBlock. See Packet Decription Block below.
- */
-typedef void (*PFNRXCALLBACK)(U32  Status,
-                              U8   PktCount,
-                              U32  BucketsRemain,
-                              PU32 PacketDescBlock,
-                              U16  AdapterID);
-
- /* 
- ** type PFNCALLBACK 
- **
- ** Pointer to user's generic callback function.  This user function
- ** can be passed to LANReset or LANShutdown and is called when the 
- ** the reset or shutdown is complete.
- ** Param1 and Param2 are invalid for LANReset and LANShutdown.
- */
-typedef void (*PFNCALLBACK)(U32  Status,
-                              U32  Param1,
-                              U32  Param2,
-                              U16  AdapterID);
-
-/*
-** Status - Transmit and Receive callback status word 
-**
-** A 32 bit Status is returned to the TX and RX callback functions.  This value
-** contains both the reply status and the detailed status as follows:
-**
-**  32    24     16            0
-**  +------+------+------------+
-**  | Reply|      |  Detailed  |
-**  |Status|   0  |   Status   |
-**  +------+------+------------+
-**
-** Reply Status and Detailed Status of zero indicates No Errors.
-*/
- /* reply message status defines */
-#define    RC_REPLY_STATUS_SUCCESS                    0x00
-#define    RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER     0x02
-#define    RC_REPLY_STATUS_TRANSACTION_ERROR          0x0A
-
-
-/* DetailedStatusCode defines */
-#define    RC_DSC_SUCCESS                         0x0000
-#define    RC_DSC_DEVICE_FAILURE                  0x0001
-#define    RC_DSC_DESTINATION_NOT_FOUND           0x0002
-#define    RC_DSC_TRANSMIT_ERROR                  0x0003
-#define    RC_DSC_TRANSMIT_ABORTED                0x0004
-#define    RC_DSC_RECEIVE_ERROR                   0x0005
-#define    RC_DSC_RECEIVE_ABORTED                 0x0006
-#define    RC_DSC_DMA_ERROR                       0x0007
-#define    RC_DSC_BAD_PACKET_DETECTED             0x0008
-#define    RC_DSC_OUT_OF_MEMORY                   0x0009
-#define    RC_DSC_BUCKET_OVERRUN                  0x000A
-#define    RC_DSC_IOP_INTERNAL_ERROR              0x000B
-#define    RC_DSC_CANCELED                        0x000C
-#define    RC_DSC_INVALID_TRANSACTION_CONTEXT     0x000D
-#define    RC_DSC_DESTINATION_ADDRESS_DETECTED    0x000E
-#define    RC_DSC_DESTINATION_ADDRESS_OMITTED     0x000F
-#define    RC_DSC_PARTIAL_PACKET_RETURNED         0x0010
-
-
-/*
-** Packet Description Block   (Received packets)
-**
-** A pointer to this block structure is returned to the ReceiveCallback 
-** function.  It contains the list of packet buffers which have either been
-** filled with a packet or returned to host due to a LANReset function. 
-** Currently there will only be one packet per receive bucket (buffer) posted. 
-**
-**   32   24               0     
-**  +-----------------------+  -\
-**  |   Buffer 1 Context    |    \
-**  +-----------------------+     \
-**  |      0xC0000000       |     / First Bucket Descriptor
-**  +-----+-----------------+    /
-**  |  0  | packet 1 length |   / 
-**  +-----------------------+  -\
-**  |   Buffer 2 Context    |    \
-**  +-----------------------+     \
-**  |      0xC0000000       |     / Second Bucket Descriptor
-**  +-----+-----------------+    /
-**  |  0  | packet 2 length |   / 
-**  +-----+-----------------+  -
-**  |         ...           |  ----- more bucket descriptors
-**  +-----------------------+  -\
-**  |   Buffer n Context    |    \
-**  +-----------------------+     \
-**  |      0xC0000000       |     / Last Bucket Descriptor
-**  +-----+-----------------+    /
-**  |  0  | packet n length |   / 
-**  +-----+-----------------+  -
-**
-** Buffer Context values are those given to adapter in the TCB on calls to
-** RCPostRecvBuffers().
-**  
-*/
-
-
-
-/*
-** Transaction Control Block (TCB) structure
-**
-** A structure like this is filled in by the user and passed by reference to 
-** RCSendPacket() and RCPostRecvBuffers() functions.  Minimum size is five
-** 32-bit words for one buffer with one segment descriptor.  
-** MAX_NMBR_POST_BUFFERS_PER_MSG defines the maximum single segment buffers
-** that can be described in a given TCB.
-**
-**   32                    0
-**  +-----------------------+
-**  |   Buffer Count        |  Number of buffers in the TCB
-**  +-----------------------+
-**  |   Buffer 1 Context    |  first buffer reference
-**  +-----------------------+
-**  |   Buffer 1 Seg Count  |  number of segments in buffer
-**  +-----------------------+
-**  |   Buffer 1 Seg Desc 1 |  first segment descriptor (size, physical address)
-**  +-----------------------+
-**  |         ...           |  more segment descriptors (size, physical address)
-**  +-----------------------+
-**  |   Buffer 1 Seg Desc n |  last segment descriptor (size, physical address)
-**  +-----------------------+
-**  |   Buffer 2 Context    |  second buffer reference
-**  +-----------------------+
-**  |   Buffer 2 Seg Count  |  number of segments in buffer
-**  +-----------------------+
-**  |   Buffer 2 Seg Desc 1 |  segment descriptor (size, physical address)
-**  +-----------------------+
-**  |         ...           |  more segment descriptors (size, physical address)
-**  +-----------------------+
-**  |   Buffer 2 Seg Desc n |
-**  +-----------------------+
-**  |         ...           |  more buffer descriptor blocks ...
-**  +-----------------------+
-**  |   Buffer n Context    |
-**  +-----------------------+
-**  |   Buffer n Seg Count  |
-**  +-----------------------+
-**  |   Buffer n Seg Desc 1 |
-**  +-----------------------+
-**  |         ...           |
-**  +-----------------------+
-**  |   Buffer n Seg Desc n |
-**  +-----------------------+
-**
-**
-** A TCB for one contigous packet buffer would look like the following:
-**
-**   32                    0
-**  +-----------------------+
-**  |         1             |  one buffer in the TCB
-**  +-----------------------+
-**  |  <user's Context>     |  user's buffer reference
-**  +-----------------------+
-**  |         1             |  one segment buffer
-**  +-----------------------+                            _
-**  |    <buffer size>      |  size                       \ 
-**  +-----------------------+                              \ segment descriptor
-**  |  <physical address>   |  physical address of buffer  /
-**  +-----------------------+                            _/
-**
-*/
-
- /* Buffer Segment Descriptor */
-typedef struct
-{
-    U32 size;
-    U32 phyAddress;
-}
- BSD, *PBSD;
- 
-typedef PU32 PRCTCB;
-/*
-** -------------------------------------------------------------------------
-** Exported functions comprising the API to the message transport layer
-** -------------------------------------------------------------------------
-*/
-
-
- /*
- ** InitRCApiMsgLayer()
- ** 
- ** Called once prior to using the API message transport layer.  User 
- ** provides both the physical and virual address of a locked page buffer 
- ** that is used as a private buffer for the RedCreek API message
- ** transport layer.  This buffer must be a contigous memory block of a 
- ** minimum of 16K bytes and long word aligned.  The user also must provide
- ** the base address of the RedCreek PCI adapter assigned by BIOS or operating
- ** system.  The user provided value AdapterID is a zero based index of the
- ** Ravlin 45/PCI adapter.  This interface number is used in all subsequent API
- ** calls to identify which adpapter for which the function is intended.  
- ** Up to sixteen interfaces are supported with this API.
- **
- ** Inputs:  AdapterID - interface number from 0 to 15
- **          pciBaseAddr - virual base address of PCI (set by BIOS)
- **          p_msgbuf - virual address to private message block (min. 16K)
- **          p_phymsgbuf - physical address of private message block
- **          TransmitCallbackFunction - address of user's TX callback function
- **          ReceiveCallbackFunction  - address of user's RX callback function
- **
- */
-RC_RETURN InitRCApiMsgLayer(U16 AdapterID, U32 pciBaseAddr, 
-                            PU8 p_msgbuf,  PU8 p_phymsgbuf,
-                            PFNTXCALLBACK TransmitCallbackFunction,
-                            PFNRXCALLBACK ReceiveCallbackFunction,
-                            PFNCALLBACK   RebootCallbackFunction);
-
- /*
- ** RCSetRavlinIPandMask()
- **
- ** Set the Ravlin 45/PCI cards IP address and network mask.
- **
- ** IP address and mask must be in network byte order.
- ** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be
- ** 0x04030201 and 0x00FFFFFF on a little endian machine.
- **
- */
-RC_RETURN RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask);
-
-
-/*
-** =========================================================================
-** RCGetRavlinIPandMask()
-**
-** get the IP address and MASK from the card
-** 
-** =========================================================================
-*/
-RC_RETURN
-RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, 
-                        PFNWAITCALLBACK WaitCallback);
-
- /* 
- ** RCProcMsgQ()
- ** 
- ** Called from user's polling loop or Interrupt Service Routine for a PCI 
- ** interrupt from the RedCreek PCI adapter.  User responsible for determining
- ** and hooking the PCI interrupt. This function will call the registered
- ** callback functions, TransmitCallbackFunction or ReceiveCallbackFunction,
- ** if a TX or RX transaction has completed.
- */
-void RCProcMsgQ(U16 AdapterID);
-
-
- /*
- ** Disable and Enable Adapter interrupts.  Adapter interrupts are enabled at 
- ** Init time but can be disabled and re-enabled through these two function calls.
- ** Packets will still be put into any posted received buffers and packets will
- ** be sent through RCSendPacket() functions.  Disabling Adapter interrupts
- ** will prevent hardware interrupt to host even though the outbound msg
- ** queue is not emtpy.
- */
-RC_RETURN RCEnableAdapterInterrupts(U16 adapterID);
-RC_RETURN RCDisableAdapterInterrupts(U16 AdapterID);
-
-
- /* 
- ** RCPostRecvBuffers()
- ** 
- ** Post user's page locked buffers for use by the PCI adapter to
- ** return ethernet packets received from the LAN.  Transaction Control Block,
- ** provided by user, contains buffer descriptor(s) which includes a buffer
- ** context number along with buffer size and physical address.  See TCB above.
- ** The buffer context and actual packet length are returned to the 
- ** ReceiveCallbackFunction when packets have been received.  Buffers posted
- ** to the RedCreek adapter are considered owned by the adapter until the
- ** context is return to user through the ReceiveCallbackFunction.
- */
-RC_RETURN RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransactionCtrlBlock);
-#define MAX_NMBR_POST_BUFFERS_PER_MSG 32
-
- /*
- ** RCSendPacket()
- ** 
- ** Send user's ethernet packet from a locked page buffer.  
- ** Packet must have full MAC header, however without a CRC.  
- ** Initiator context is a user provided value that is returned 
- ** to the TransmitCallbackFunction when packet buffer is free.
- ** Transmit buffer are considered owned by the adapter until context's
- ** returned to user through the TransmitCallbackFunction.
- */
-RC_RETURN RCSendPacket(U16 AdapterID, 
-                          U32 context, 
-                          PRCTCB pTransactionCtrlBlock);
-
-
- /* Ethernet Link Statistics structure */
-typedef struct tag_RC_link_stats
-{
-    U32 TX_good;      /* good transmit frames */
-    U32 TX_maxcol;    /* frames not TX due to MAX collisions */
-    U32 TX_latecol;   /* frames not TX due to late collisions */
-    U32 TX_urun;      /* frames not TX due to DMA underrun */
-    U32 TX_crs;       /* frames TX with lost carrier sense */
-    U32 TX_def;       /* frames deferred due to activity on link */
-    U32 TX_singlecol; /* frames TX with one and only on collision */
-    U32 TX_multcol;   /* frames TX with more than one collision */
-    U32 TX_totcol;    /* total collisions detected during TX */
-    U32 Rcv_good;     /* good frames received */
-    U32 Rcv_CRCerr;   /* frames RX and discarded with CRC errors */
-    U32 Rcv_alignerr; /* frames RX with alignment and CRC errors */
-    U32 Rcv_reserr;   /* good frames discarded due to no RX buffer */
-    U32 Rcv_orun;     /* RX frames lost due to FIFO overrun */
-    U32 Rcv_cdt;      /* RX frames with collision during RX */
-    U32 Rcv_runt;     /* RX frames shorter than 64 bytes */
-}
- RCLINKSTATS, *P_RCLINKSTATS;
-
- /*
- ** RCGetLinkStatistics()
- **
- ** Returns link statistics in user's structure at address StatsReturnAddr
- ** If given, not NULL, the function WaitCallback is called during the wait
- ** loop while waiting for the adapter to respond.
- */
-RC_RETURN RCGetLinkStatistics(U16 AdapterID,
-                              P_RCLINKSTATS StatsReturnAddr,
-                              PFNWAITCALLBACK WaitCallback);
-
- /*
- ** RCGetLinkStatus()
- **
- ** Return link status, up or down, to user's location addressed by ReturnAddr.
- ** If given, not NULL, the function WaitCallback is called during the wait
- ** loop while waiting for the adapter to respond.
- */
-RC_RETURN RCGetLinkStatus(U16 AdapterID, 
-                          PU32 pReturnStatus,
-                          PFNWAITCALLBACK WaitCallback);
-                               
- /* Link Status defines - value returned in pReturnStatus */
-#define LAN_LINK_STATUS_DOWN     0
-#define LAN_LINK_STATUS_UP       1
-
- /*
- ** RCGetMAC()
- **
- ** Get the current MAC address assigned to user.  RedCreek Ravlin 45/PCI 
- ** has two MAC addresses.  One which is private to the PCI Card, and 
- ** another MAC which is given to the user as its link layer MAC address. The
- ** adapter runs in promiscous mode because of the dual address requirement.
- ** The MAC address is returned to the unsigned char array pointer to by mac.
- */
-RC_RETURN RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback);
-
- /*
- ** RCSetMAC()
- **
- ** Set a new user port MAC address.  This address will be returned on
- ** subsequent RCGetMAC() calls.
- */
-RC_RETURN RCSetMAC(U16 AdapterID, PU8 mac);
-
- /*
- ** RCSetLinkSpeed()
- **
- ** set adapter's link speed based on given input code.
- */
-RC_RETURN RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode);
- /* Set link speed codes */
-#define LNK_SPD_AUTO_NEG_NWAY   0
-#define LNK_SPD_100MB_FULL      1
-#define LNK_SPD_100MB_HALF      2
-#define LNK_SPD_10MB_FULL       3
-#define LNK_SPD_10MB_HALF       4
-
-
-
-
- /*
- ** RCGetLinkSpeed()
- **
- ** Return link speed code.
- */
- /* Return link speed codes */
-#define LNK_SPD_UNKNOWN         0
-#define LNK_SPD_100MB_FULL      1
-#define LNK_SPD_100MB_HALF      2
-#define LNK_SPD_10MB_FULL       3
-#define LNK_SPD_10MB_HALF       4
-
-RC_RETURN
-RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback);
-
-/*
-** =========================================================================
-** RCReportDriverCapability(U16 AdapterID, U32 capability)
-**
-** Currently defined bits:
-** WARM_REBOOT_CAPABLE   0x01
-**
-** =========================================================================
-*/
-RC_RETURN
-RCReportDriverCapability(U16 AdapterID, U32 capability);
-
-/*
-** RCGetFirmwareVer()
-**
-** Return firmware version in the form "SoftwareVersion : Bt BootVersion"
-**
-** WARNING: user's space pointed to by pFirmString should be at least 60 bytes.
-*/
-RC_RETURN
-RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback);
-
-/*
-** ----------------------------------------------
-** LAN adapter Reset and Shutdown functions
-** ----------------------------------------------
-*/
- /* resource flag bit assignments for RCResetLANCard() & RCShutdownLANCard() */
-#define RC_RESOURCE_RETURN_POSTED_RX_BUCKETS  0x0001 
-#define RC_RESOURCE_RETURN_PEND_TX_BUFFERS    0x0002
-
- /*
- ** RCResetLANCard()
- **
- ** Reset LAN card operation.  Causes a software reset of the ethernet
- ** controller and restarts the command and receive units. Depending on 
- ** the ResourceFlags given, the buffers are either returned to the
- ** host with reply status of RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER and
- ** detailed status of RC_DSC_CANCELED (new receive buffers must be
- ** posted after issuing this) OR the buffers are kept and reused by
- ** the ethernet controller. If CallbackFunction is not NULL, the function
- ** will be called when the reset is complete.  If the CallbackFunction is
- ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset 
- ** to complete (please disable adapter interrupts during this method).
- ** Any outstanding transmit or receive buffers that are complete will be
- ** returned via the normal reply messages before the requested resource
- ** buffers are returned.
- ** A call to RCPostRecvBuffers() is needed to return the ethernet to full
- ** operation if the receive buffers were returned during LANReset.
- ** Note: The IOP status is not affected by a LAN reset.
- */
-RC_RETURN RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction);
-
-
- /*
- ** RCShutdownLANCard()
- **
- ** Shutdown LAN card operation and put into an idle (suspended) state.
- ** The LAN card is restarted with RCResetLANCard() function.
- ** Depending on the ResourceFlags given, the buffers are either returned 
- ** to the host with reply status of RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 
- ** and detailed status of RC_DSC_CANCELED (new receive buffers must be
- ** posted after issuing this) OR the buffers are kept and reused by
- ** the ethernet controller. If CallbackFunction is not NULL, the function
- ** will be called when the reset is complete.  If the CallbackFunction is
- ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset 
- ** to complete (please disable adapter interrupts during this method).
- ** Any outstanding transmit or receive buffers that are complete will be
- ** returned via the normal reply messages before the requested resource
- ** buffers are returned.
- ** Note: The IOP status is not affected by a LAN shutdown.
- */                                      
-RC_RETURN 
-RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction);
-
- /*
- ** RCResetAdapter();
- **     Initializes ADAPTERState to ADAPTER_STATE_RESET.
- **     Stops access to outbound message Q.
- **     Discards any outstanding transmit or posted receive buffers.
- **     Clears outbound message Q. 
- */
-RC_RETURN 
-RCResetAdapter(U16 AdapterID);
-
-#endif /* RCMTL_H */
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/rcpci45.c linux/drivers/net/rcpci45.c
--- v2.2.0-pre7/linux/drivers/net/rcpci45.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/net/rcpci45.c	Thu Jan 14 22:58:47 1999
@@ -4,7 +4,7 @@
 **
 **
 **  ---------------------------------------------------------------------
-**  ---     Copyright (c) 1998, RedCreek Communications Inc.          ---
+**  ---     Copyright (c) 1998, 1999, RedCreek Communications Inc.    ---
 **  ---                   All rights reserved.                        ---
 **  ---------------------------------------------------------------------
 **
@@ -12,14 +12,13 @@
 **
 ** Known Problems
 ** 
-** Billions and Billions...  
-**
-** ... apparently added by Brian.  Pete knows of no bugs.
+** None known at this time.
 **
 **  TODO:
 **      -Get rid of the wait loops in the API and replace them
 **       with system independent delays ...something like
-**       "delayms(2)".
+**       "delayms(2)".  However, under normal circumstances, the 
+**       delays are very short so they're not a problem.
 **
 **  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
@@ -35,14 +34,18 @@
 **  along with this program; if not, write to the Free Software
 **  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 **
+**   
+**  Pete Popov, January 11,99: Fixed a couple of 2.1.x problems 
+**  (virt_to_bus() not called), tested it under 2.2pre5, and added a 
+**  #define to enable the use of the same file for both, the 2.0.x kernels 
+**  as well as the 2.1.x.
 **
-**  Ported to 2.1.x by Alan Cox 1998/12/9. If this doesnt work try 2.0.x
-**  and let me know.
+**  Ported to 2.1.x by Alan Cox 1998/12/9. 
 **
 ***************************************************************************/
 
 static char *version =
-"RedCreek Communications PCI linux driver version 1.32 Beta\n";
+"RedCreek Communications PCI linux driver version 2.00\n";
 
 
 #include <linux/module.h>
@@ -63,27 +66,34 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 
+#if LINUX_VERSION_CODE >= 0x020100
+#define LINUX_2_1
+#endif
+
+#ifdef LINUX_2_1
 #include <asm/uaccess.h>
+#endif
 
 #include <linux/if_ether.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
+
 #define RC_LINUX_MODULE
-#include "rcmtl.h"
+#include "rclanmtl.h"
 #include "rcif.h"
 
 #define RUN_AT(x) (jiffies + (x))
-#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
-
-#define FREE_IRQ(irqnum, dev) free_irq(irqnum)
-#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n)
-#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
 
 #define NEW_MULTICAST
 #include <linux/delay.h>
 
+#ifndef LINUX_2_1
+#define ioremap vremap
+#define iounmap vfree
+#endif
+
 /* PCI/45 Configuration space values */
 #define RC_PCI45_VENDOR_ID  0x4916
 #define RC_PCI45_DEVICE_ID  0x1960
@@ -111,25 +121,25 @@
 typedef struct
 {
 
-     /* 
-      *    pointer to the device structure which is part
-      * of the interface to the Linux kernel.
-      */
-      struct device *dev;            
+    /* 
+     *    pointer to the device structure which is part
+     * of the interface to the Linux kernel.
+     */
+    struct device *dev;            
      
-     char devname[8];                /* "ethN" string */
-     U8     id;                        /* the AdapterID */
-     U32    pci_addr;               /* the pci address of the adapter */
-     U32    bus;
-     U32    function;
-     struct timer_list timer;        /*  timer */
-     struct enet_statistics  stats; /* the statistics structure */
-     struct device *next;            /* points to the next RC adapter */
-     unsigned long numOutRcvBuffers;/* number of outstanding receive buffers*/
-     unsigned char shutdown;
-     unsigned char reboot;
-     unsigned char nexus;
-     PU8    PLanApiPA;             /* Pointer to Lan Api Private Area */
+    char devname[8];                /* "ethN" string */
+    U8     id;                        /* the AdapterID */
+    U32    pci_addr;               /* the pci address of the adapter */
+    U32    bus;
+    U32    function;
+    struct timer_list timer;        /*  timer */
+    struct enet_statistics  stats; /* the statistics structure */
+    struct device *next;            /* points to the next RC adapter */
+    unsigned long numOutRcvBuffers;/* number of outstanding receive buffers*/
+    unsigned char shutdown;
+    unsigned char reboot;
+    unsigned char nexus;
+    PU8    PLanApiPA;             /* Pointer to Lan Api Private Area */
 
 }
 DPA, *PDPA;
@@ -138,10 +148,10 @@
 
 static PDPA  PCIAdapters[MAX_ADAPTERS] = 
 {
-     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
 
@@ -173,258 +183,258 @@
 int rcpci_probe(struct netdevice *dev)
 #endif
 {
-     int cards_found;
+    int cards_found;
 
-     printk(version);
+    printk(version);
 
-     root_RCdev = NULL;
-     cards_found = RCscan();
-#ifdef MODULE     
-     return cards_found ? 0 : -ENODEV;
+    root_RCdev = NULL;
+    cards_found = RCscan();
+#ifdef MODULE
+    return cards_found ? 0 : -ENODEV;
 #else
-     return -1;
-#endif          
+    return -1;
+#endif
 }
 
-static int RCscan(void)
+static int RCscan()
 {
-     int cards_found = 0;
-     struct device *dev = 0;
+    int cards_found = 0;
+    struct device *dev = 0;
+
+    if (pcibios_present()) 
+    {
+        static int pci_index = 0;
+        unsigned char pci_bus, pci_device_fn;
+        int scan_status;
+        int board_index = 0;
 
-     if (pcibios_present()) 
-     {
-          static int pci_index = 0;
-          unsigned char pci_bus, pci_device_fn;
-          int scan_status;
-          int board_index = 0;
-
-          for (;pci_index < 0xff; pci_index++) 
-          {
-               unsigned char pci_irq_line;
-               unsigned short pci_command, vendor, device, class;
-               unsigned int pci_ioaddr;
-
-
-               scan_status =  
-                    (pcibios_find_device (RC_PCI45_VENDOR_ID, 
-                                          RC_PCI45_DEVICE_ID, 
-                                          pci_index, 
-                                          &pci_bus, 
-                                          &pci_device_fn));
-#ifdef RCDEBUG
-               printk("rc scan_status = 0x%X\n", scan_status);
-#endif
-               if (scan_status != PCIBIOS_SUCCESSFUL)
-                    break;
-               pcibios_read_config_word(pci_bus, 
-                                        pci_device_fn, 
-                                        PCI_VENDOR_ID, &vendor);
-               pcibios_read_config_word(pci_bus, 
-                                        pci_device_fn,
-                                        PCI_DEVICE_ID, &device);
-               pcibios_read_config_byte(pci_bus, 
-                                        pci_device_fn,
-                                        PCI_INTERRUPT_LINE, &pci_irq_line);
-               pcibios_read_config_dword(pci_bus, 
-                                         pci_device_fn,
-                                         PCI_BASE_ADDRESS_0, &pci_ioaddr);
-               pcibios_read_config_word(pci_bus, 
-                                        pci_device_fn,
-                                        PCI_CLASS_DEVICE, &class);
-
-               pci_ioaddr &= ~0xf;
-
-#ifdef RCDEBUG
-               printk("rc: Found RedCreek PCI adapter\n");
-               printk("rc: pci class = 0x%x  0x%x \n", class, class>>8);
-               printk("rc: pci_bus = %d,  pci_device_fn = %d\n", pci_bus, pci_device_fn);
-               printk("rc: pci_irq_line = 0x%x \n", pci_irq_line);
-               printk("rc: pci_ioaddr = 0x%x\n", pci_ioaddr);
-#endif
-
-#if 0
-               if (check_region(pci_ioaddr, 32768))
-               {
-                    printk("rc: check_region failed\n");
-                    continue;
-               }
-               else
-               {
-                    printk("rc: check_region passed\n");
-               }
+        for (;pci_index < 0xff; pci_index++) 
+        {
+            unsigned char pci_irq_line;
+            unsigned short pci_command, vendor, device, class;
+            unsigned int pci_ioaddr;
+
+
+            scan_status =  
+                (pcibios_find_device (RC_PCI45_VENDOR_ID, 
+                                      RC_PCI45_DEVICE_ID, 
+                                      pci_index, 
+                                      &pci_bus, 
+                                      &pci_device_fn));
+#ifdef RCDEBUG
+            printk("rc scan_status = 0x%X\n", scan_status);
+#endif
+            if (scan_status != PCIBIOS_SUCCESSFUL)
+                break;
+            pcibios_read_config_word(pci_bus, 
+                                     pci_device_fn, 
+                                     PCI_VENDOR_ID, &vendor);
+            pcibios_read_config_word(pci_bus, 
+                                     pci_device_fn,
+                                     PCI_DEVICE_ID, &device);
+            pcibios_read_config_byte(pci_bus, 
+                                     pci_device_fn,
+                                     PCI_INTERRUPT_LINE, &pci_irq_line);
+            pcibios_read_config_dword(pci_bus, 
+                                      pci_device_fn,
+                                      PCI_BASE_ADDRESS_0, &pci_ioaddr);
+            pcibios_read_config_word(pci_bus, 
+                                     pci_device_fn,
+                                     PCI_CLASS_DEVICE, &class);
+
+            pci_ioaddr &= ~0xf;
+
+#ifdef RCDEBUG
+            printk("rc: Found RedCreek PCI adapter\n");
+            printk("rc: pci class = 0x%x  0x%x \n", class, class>>8);
+            printk("rc: pci_bus = %d,  pci_device_fn = %d\n", pci_bus, pci_device_fn);
+            printk("rc: pci_irq_line = 0x%x \n", pci_irq_line);
+            printk("rc: pci_ioaddr = 0x%x\n", pci_ioaddr);
 #endif
-               
-                /*
-                 * Get and check the bus-master and latency values.
-                 * Some PCI BIOSes fail to set the master-enable bit.
-                 */
 
-                pcibios_read_config_word(pci_bus, 
-                                         pci_device_fn,
-                                         PCI_COMMAND, 
-                                         &pci_command);
-                if ( ! (pci_command & PCI_COMMAND_MASTER)) {
-                     printk("rc: PCI Master Bit has not been set!\n");
+#if 1
+            if (check_region(pci_ioaddr, 2*32768))
+            {
+                printk("rc: check_region failed\n");
+                continue;
+            }
+            else
+            {
+                printk("rc: check_region passed\n");
+            }
+#endif
+               
+            /*
+             * Get and check the bus-master and latency values.
+             * Some PCI BIOSes fail to set the master-enable bit.
+             */
+
+            pcibios_read_config_word(pci_bus, 
+                                     pci_device_fn,
+                                     PCI_COMMAND, 
+                                     &pci_command);
+            if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+                printk("rc: PCI Master Bit has not been set!\n");
                             
-                     pci_command |= PCI_COMMAND_MASTER;
-                     pcibios_write_config_word(pci_bus, 
-                                               pci_device_fn,
-                                               PCI_COMMAND, 
-                                               pci_command);
-                }
-                if ( ! (pci_command & PCI_COMMAND_MEMORY)) {
+                pci_command |= PCI_COMMAND_MASTER;
+                pcibios_write_config_word(pci_bus, 
+                                          pci_device_fn,
+                                          PCI_COMMAND, 
+                                          pci_command);
+            }
+            if ( ! (pci_command & PCI_COMMAND_MEMORY)) {
                 /*
                  * If the BIOS did not set the memory enable bit, what else
                  * did it not initialize?  Skip this adapter.
                  */
-                     printk("rc: Adapter %d, PCI Memory Bit has not been set!\n",
-                         cards_found);
-                     printk("rc: Bios problem? \n");
-                     continue;
-                }
+                printk("rc: Adapter %d, PCI Memory Bit has not been set!\n",
+                       cards_found);
+                printk("rc: Bios problem? \n");
+                continue;
+            }
                     
-               dev = RCfound_device(dev, pci_ioaddr, pci_irq_line,
-                                    pci_bus, pci_device_fn,
-                                    board_index++, cards_found);
-
-               if (dev) {
-                    dev = 0;
-                    cards_found++;
-               }
-          }
-     }
-     printk("rc: found %d cards \n", cards_found);
-     return cards_found;
+            dev = RCfound_device(dev, pci_ioaddr, pci_irq_line,
+                                 pci_bus, pci_device_fn,
+                                 board_index++, cards_found);
+
+            if (dev) {
+                dev = 0;
+                cards_found++;
+            }
+        }
+    }
+    printk("rc: found %d cards \n", cards_found);
+    return cards_found;
 }
 
 static struct device *
 RCfound_device(struct device *dev, int memaddr, int irq, 
                int bus, int function, int product_index, int card_idx)
 {
-     int dev_size = 32768;        
-     unsigned long *vaddr=0;
-     PDPA pDpa;
-     int init_status;
-
-     /* 
-      * Allocate and fill new device structure. 
-      * We need enough for struct device plus DPA plus the LAN API private
-      * area, which requires a minimum of 16KB.  The top of the allocated
-      * area will be assigned to struct device; the next chunk will be
-      * assigned to DPA; and finally, the rest will be assigned to the
-      * the LAN API layer.
-      */
-     dev = (struct device *) kmalloc(dev_size, GFP_DMA | GFP_KERNEL |GFP_ATOMIC);
-     memset(dev, 0, dev_size);
+    int dev_size = 32768;        
+    unsigned long *vaddr=0;
+    PDPA pDpa;
+    int init_status;
+
+    /* 
+     * Allocate and fill new device structure. 
+     * We need enough for struct device plus DPA plus the LAN API private
+     * area, which requires a minimum of 16KB.  The top of the allocated
+     * area will be assigned to struct device; the next chunk will be
+     * assigned to DPA; and finally, the rest will be assigned to the
+     * the LAN API layer.
+     */
+    dev = (struct device *) kmalloc(dev_size, GFP_DMA | GFP_KERNEL |GFP_ATOMIC);
+    memset(dev, 0, dev_size);
 #ifdef RCDEBUG
-     printk("rc: dev = 0x%08X\n", (uint)dev);
+    printk("rc: dev = 0x%08X\n", (uint)dev);
 #endif 
 
-     /*
-      * dev->priv will point to the start of DPA.
-      */
-     dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15);
-     pDpa = dev->priv;
-     dev->name = pDpa->devname;
-
-     pDpa->dev = dev;            /* this is just for easy reference */
-     pDpa->function = function;
-     pDpa->bus = bus;
-     pDpa->id = card_idx;        /* the device number */
-     pDpa->pci_addr = memaddr;
-     PCIAdapters[card_idx] = pDpa;
-#ifdef RCDEBUG
-     printk("rc: pDpa = 0x%x, id = %d \n", (uint)pDpa, (uint)pDpa->id);
-#endif
-
-     /*
-      * Save the starting address of the LAN API private area.  We'll
-      * pass that to InitRCApiMsgLayer().
-      */
-     pDpa->PLanApiPA = (void *)(((long)pDpa + sizeof(DPA) + 0xff) & ~0xff);
+    /*
+     * dev->priv will point to the start of DPA.
+     */
+    dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15);
+    pDpa = dev->priv;
+    dev->name = pDpa->devname;
+
+    pDpa->dev = dev;            /* this is just for easy reference */
+    pDpa->function = function;
+    pDpa->bus = bus;
+    pDpa->id = card_idx;        /* the device number */
+    pDpa->pci_addr = memaddr;
+    PCIAdapters[card_idx] = pDpa;
+#ifdef RCDEBUG
+    printk("rc: pDpa = 0x%x, id = %d \n", (uint)pDpa, (uint)pDpa->id);
+#endif
+
+    /*
+     * Save the starting address of the LAN API private area.  We'll
+     * pass that to RCInitI2OMsgLayer().
+     */
+    pDpa->PLanApiPA = (void *)(((long)pDpa + sizeof(DPA) + 0xff) & ~0xff);
 #ifdef RCDEBUG
-     printk("rc: pDpa->PLanApiPA = 0x%x\n", (uint)pDpa->PLanApiPA);
+    printk("rc: pDpa->PLanApiPA = 0x%x\n", (uint)pDpa->PLanApiPA);
 #endif
     
-     /* The adapter is accessable through memory-access read/write, not
-      * I/O read/write.  Thus, we need to map it to some virtual address
-      * area in order to access the registers are normal memory.
-      */
-     vaddr = (ulong *) ioremap(memaddr, 32768);
+    /* The adapter is accessable through memory-access read/write, not
+     * I/O read/write.  Thus, we need to map it to some virtual address
+     * area in order to access the registers are normal memory.
+     */
+    vaddr = (ulong *) ioremap (memaddr, 2*32768);
+#ifdef RCDEBUG
+    printk("rc: RCfound_device: 0x%x, priv = 0x%x, vaddr = 0x%x\n", 
+           (uint)dev, (uint)dev->priv, (uint)vaddr);
+#endif
+    dev->base_addr = (unsigned long)vaddr;
+    dev->irq = irq;
+    dev->interrupt = 0;
+
+    /*
+     * Request a shared interrupt line.
+     */
+    if ( request_irq(dev->irq, (void *)RCinterrupt,
+                     SA_INTERRUPT|SA_SHIRQ, "RedCreek VPN Adapter", dev) )
+    {
+        printk( "RC PCI 45: %s: unable to get IRQ %d\n", (PU8)dev->name, (uint)dev->irq );
+        iounmap(vaddr);
+        kfree(dev);
+        return 0;
+    }
+
+    init_status = RCInitI2OMsgLayer(pDpa->id, dev->base_addr, 
+                                    pDpa->PLanApiPA, (PU8)virt_to_bus((void *)pDpa->PLanApiPA),
+                                    (PFNTXCALLBACK)RCxmit_callback,
+                                    (PFNRXCALLBACK)RCrecv_callback,
+                                    (PFNCALLBACK)RCreboot_callback);
 #ifdef RCDEBUG
-     printk("rc: RCfound_device: 0x%x, priv = 0x%x, vaddr = 0x%x\n", 
-            (uint)dev, (uint)dev->priv, (uint)vaddr);
+    printk("rc: I2O msg initted: status = 0x%x\n", init_status);
 #endif
-     dev->base_addr = (unsigned long)vaddr;
-     dev->irq = irq;
-     dev->interrupt = 0;
+    if (init_status)
+    {
+        printk("rc: Unable to initialize msg layer\n");
+        free_irq(dev->irq, dev);
+        iounmap(vaddr);
+        kfree(dev);
+        return 0;
+    }
+    if (RCGetMAC(pDpa->id, dev->dev_addr, NULL))
+    {
+        printk("rc: Unable to get adapter MAC\n");
+        free_irq(dev->irq, dev);
+        iounmap(vaddr);
+        kfree(dev);
+        return 0;
+    }
 
-     /*
-      * Request a shared interrupt line.
-      */
-     if ( request_irq(dev->irq, (void *)RCinterrupt,
-                      SA_INTERRUPT|SA_SHIRQ, "RedCreek VPN Adapter", dev) )
-     {
-          printk( "RC PCI 45: %s: unable to get IRQ %d\n", (PU8)dev->name, (uint)dev->irq );
-          iounmap(vaddr);
-          kfree(dev);
-           return 0;
-     }
-
-     init_status = InitRCApiMsgLayer(pDpa->id, dev->base_addr, 
-                                     pDpa->PLanApiPA, pDpa->PLanApiPA,
-                                     (PFNTXCALLBACK)RCxmit_callback,
-                                     (PFNRXCALLBACK)RCrecv_callback,
-                                     (PFNCALLBACK)RCreboot_callback);
-#ifdef RCDEBUG
-     printk("rc: msg initted: status = 0x%x\n", init_status);
-#endif
-     if (init_status)
-     {
-          printk("rc: Unable to initialize msg layer\n");
-          free_irq(dev->irq, dev);
-          vfree(vaddr);
-          kfree(dev);
-          return 0;
-     }
-     if (RCGetMAC(pDpa->id, dev->dev_addr, NULL))
-     {
-          printk("rc: Unable to get adapter MAC\n");
-          free_irq(dev->irq, dev);
-          vfree(vaddr);
-          kfree(dev);
-          return 0;
-     }
-
-     DriverControlWord |= WARM_REBOOT_CAPABLE;
-     RCReportDriverCapability(pDpa->id, DriverControlWord);
-
-     dev->init = RCprobe1;
-     ether_setup(dev);            /* linux kernel interface */
-
-     pDpa->next = root_RCdev;
-     root_RCdev = dev;
-
-     if (register_netdev(dev) != 0) /* linux kernel interface */
-     {
-          printk("rc: unable to register device \n");
-          free_irq(dev->irq, dev);
-          vfree(vaddr);
-          kfree(dev);
-          return 0;
-     }
-     return dev;
+    DriverControlWord |= WARM_REBOOT_CAPABLE;
+    RCReportDriverCapability(pDpa->id, DriverControlWord);
+
+    dev->init = RCprobe1;
+    ether_setup(dev);            /* linux kernel interface */
+
+    pDpa->next = root_RCdev;
+    root_RCdev = dev;
+
+    if (register_netdev(dev) != 0) /* linux kernel interface */
+    {
+        printk("rc: unable to register device \n");
+        free_irq(dev->irq, dev);
+        iounmap(vaddr);
+        kfree(dev);
+        return 0;
+    }
+    return dev;
 }
 
 static int RCprobe1(struct device *dev)
 {
-     dev->open = RCopen;
-     dev->hard_start_xmit = RC_xmit_packet;
-     dev->stop = RCclose;
-     dev->get_stats = RCget_stats;
-     dev->do_ioctl = RCioctl;
-     dev->set_config = RCconfig;
-     return 0;
+    dev->open = RCopen;
+    dev->hard_start_xmit = RC_xmit_packet;
+    dev->stop = RCclose;
+    dev->get_stats = RCget_stats;
+    dev->do_ioctl = RCioctl;
+    dev->set_config = RCconfig;
+    return 0;
 }
 
 static int
@@ -438,25 +448,25 @@
 #ifdef RCDEBUG
     printk("rc: RCopen\n");
 #endif
-    RCEnableAdapterInterrupts(pDpa->id);
+    RCEnableI2OInterrupts(pDpa->id);
 
     if (pDpa->nexus)
     {
-       /* This is not the first time RCopen is called.  Thus,
-        * the interface was previously opened and later closed
-        * by RCclose().  RCclose() does a Shutdown; to wake up
-        * the adapter, a reset is mandatory before we can post
-        * receive buffers.  However, if the adapter initiated 
-        * a reboot while the interface was closed -- and interrupts
-        * were turned off -- we need will need to reinitialize
-        * the adapter, rather than simply waking it up.  
-        */
+        /* This is not the first time RCopen is called.  Thus,
+         * the interface was previously opened and later closed
+         * by RCclose().  RCclose() does a Shutdown; to wake up
+         * the adapter, a reset is mandatory before we can post
+         * receive buffers.  However, if the adapter initiated 
+         * a reboot while the interface was closed -- and interrupts
+         * were turned off -- we need will need to reinitialize
+         * the adapter, rather than simply waking it up.  
+         */
         printk("rc: Waking up adapter...\n");
         RCResetLANCard(pDpa->id,0,0,0);
     }
     else
     {
-       pDpa->nexus = 1;
+        pDpa->nexus = 1;
     }
 
     while(post_buffers)
@@ -469,19 +479,19 @@
 
         if ( count < requested )
         {
-             /*
-              * Check to see if we were able to post any buffers at all.
-              */
-             if (post_buffers == MAX_NMBR_RCV_BUFFERS)
-             {
-                  printk("rc: Error RCopen: not able to allocate any buffers\r\n");
-                  return(-ENOMEM);                    
-             }
-             printk("rc: Warning RCopen: not able to allocate all requested buffers\r\n");
-             break;            /* we'll try to post more buffers later */
+            /*
+             * Check to see if we were able to post any buffers at all.
+             */
+            if (post_buffers == MAX_NMBR_RCV_BUFFERS)
+            {
+                printk("rc: Error RCopen: not able to allocate any buffers\r\n");
+                return(-ENOMEM);                    
+            }
+            printk("rc: Warning RCopen: not able to allocate all requested buffers\r\n");
+            break;            /* we'll try to post more buffers later */
         }
         else
-             post_buffers -= count;
+            post_buffers -= count;
     }
     pDpa->numOutRcvBuffers = MAX_NMBR_RCV_BUFFERS - post_buffers;
     pDpa->shutdown = 0;        /* just in case */
@@ -496,68 +506,69 @@
 RC_xmit_packet(struct sk_buff *skb, struct device *dev)
 {
 
-     PDPA pDpa = (PDPA) dev->priv;
-     singleTCB tcb;
-     psingleTCB ptcb = &tcb;
-     RC_RETURN status = 0;
+    PDPA pDpa = (PDPA) dev->priv;
+    singleTCB tcb;
+    psingleTCB ptcb = &tcb;
+    RC_RETURN status = 0;
     
-     if (dev->tbusy || pDpa->shutdown || pDpa->reboot)
-     {
+        if (dev->tbusy || pDpa->shutdown || pDpa->reboot)
+        {
 #ifdef RCDEBUG
-          printk("rc: RC_xmit_packet: tbusy!\n");
+            printk("rc: RC_xmit_packet: tbusy!\n");
 #endif
-          return 1;
-     }
+            return 1;
+        }
       
-     if ( skb->len <= 0 ) 
-     {
-          printk("RC_xmit_packet: skb->len less than 0!\n");
-          return 0;
-     }
-
-     /*
-      * The user is free to reuse the TCB after RCSendPacket() returns, since
-      * the function copies the necessary info into its own private space.  Thus,
-      * our TCB can be a local structure.  The skb, on the other hand, will be
-      * freed up in our interrupt handler.
-      */
-     ptcb->bcount = 1;
-     /* 
-      * we'll get the context when the adapter interrupts us to tell us that
-      * the transmision is done. At that time, we can free skb.
-      */
-     ptcb->b.context = (U32)skb;    
-     ptcb->b.scount = 1;
-     ptcb->b.size = skb->len;
-     ptcb->b.addr = (U32)skb->data;
-
-#ifdef RCDEBUG
-     printk("rc: RC xmit: skb = 0x%x, pDpa = 0x%x, id = %d, ptcb = 0x%x\n", 
-            (uint)skb, (uint)pDpa, (uint)pDpa->id, (uint)ptcb);
-#endif
-     if ( (status = RCSendPacket(pDpa->id, (U32)NULL, (PRCTCB)ptcb))
-          != RC_RTN_NO_ERROR)
-     {
-#ifdef RCDEBUG
-          printk("rc: RC send error 0x%x\n", (uint)status);
-#endif
-          dev->tbusy = 1;
-     }
-     else
-     {
-          dev->trans_start = jiffies;
-          //       dev->tbusy = 0;
-     }
-     /*
-      * That's it!
-      */
-     return 0;
+    if ( skb->len <= 0 ) 
+    {
+        printk("RC_xmit_packet: skb->len less than 0!\n");
+        return 0;
+    }
+
+    /*
+     * The user is free to reuse the TCB after RCI2OSendPacket() returns, since
+     * the function copies the necessary info into its own private space.  Thus,
+     * our TCB can be a local structure.  The skb, on the other hand, will be
+     * freed up in our interrupt handler.
+     */
+    ptcb->bcount = 1;
+    /* 
+     * we'll get the context when the adapter interrupts us to tell us that
+     * the transmision is done. At that time, we can free skb.
+     */
+    ptcb->b.context = (U32)skb;    
+    ptcb->b.scount = 1;
+    ptcb->b.size = skb->len;
+    ptcb->b.addr = virt_to_bus((void *)skb->data);
+
+#ifdef RCDEBUG
+    printk("rc: RC xmit: skb = 0x%x, pDpa = 0x%x, id = %d, ptcb = 0x%x\n", 
+           (uint)skb, (uint)pDpa, (uint)pDpa->id, (uint)ptcb);
+#endif
+    if ( (status = RCI2OSendPacket(pDpa->id, (U32)NULL, (PRCTCB)ptcb))
+         != RC_RTN_NO_ERROR)
+    {
+#ifdef RCDEBUG
+        printk("rc: RC send error 0x%x\n", (uint)status);
+#endif
+        dev->tbusy = 1;
+        return 1;
+    }
+    else
+    {
+        dev->trans_start = jiffies;
+        //       dev->tbusy = 0;
+    }
+    /*
+     * That's it!
+     */
+    return 0;
 }
 
 /*
  * RCxmit_callback()
  *
- * The transmit callback routine. It's called by RCProcMsgQ()
+ * The transmit callback routine. It's called by RCProcI2OMsgQ()
  * because the adapter is done with one or more transmit buffers and
  * it's returning them to us, or we asked the adapter to return the
  * outstanding transmit buffers by calling RCResetLANCard() with 
@@ -574,80 +585,84 @@
     PDPA pDpa;
     struct device *dev;
 
-    pDpa = PCIAdapters[AdapterID];
-    if (!pDpa)
-    {
-        printk("rc: Fatal error: xmit callback, !pDpa\n");
-        return;
-    }
-    dev = pDpa->dev;
+        pDpa = PCIAdapters[AdapterID];
+        if (!pDpa)
+        {
+            printk("rc: Fatal error: xmit callback, !pDpa\n");
+            return;
+        }
+        dev = pDpa->dev;
 
         // printk("xmit_callback: Status = 0x%x\n", (uint)Status);
-    if (Status != RC_REPLY_STATUS_SUCCESS)
-    {
-        printk("rc: xmit_callback: Status = 0x%x\n", (uint)Status);
-    }
+        if (Status != I2O_REPLY_STATUS_SUCCESS)
+        {
+            printk("rc: xmit_callback: Status = 0x%x\n", (uint)Status);
+        }
 #ifdef RCDEBUG
-    if (pDpa->shutdown || pDpa->reboot)
-        printk("rc: xmit callback: shutdown||reboot\n");
+        if (pDpa->shutdown || pDpa->reboot)
+            printk("rc: xmit callback: shutdown||reboot\n");
 #endif
 
 #ifdef RCDEBUG     
-    printk("rc: xmit_callback: PcktCount = %d, BC = 0x%x\n", 
-            (uint)PcktCount, (uint)BufferContext);
+        printk("rc: xmit_callback: PcktCount = %d, BC = 0x%x\n", 
+               (uint)PcktCount, (uint)BufferContext);
 #endif
-    while (PcktCount--)
-    {
-        skb = (struct sk_buff *)(BufferContext[0]);
+        while (PcktCount--)
+        {
+            skb = (struct sk_buff *)(BufferContext[0]);
 #ifdef RCDEBUG
-        printk("rc: skb = 0x%x\n", (uint)skb);
+            printk("rc: skb = 0x%x\n", (uint)skb);
 #endif
-        BufferContext++;
-        dev_kfree_skb(skb);
-    }
-    dev->tbusy = 0;
+            BufferContext++;
+#ifdef LINUX_2_1
+            dev_kfree_skb (skb);
+#else
+            dev_kfree_skb (skb, FREE_WRITE);
+#endif
+        }
+        dev->tbusy = 0;
 
 }
 
 static void
 RCreset_callback(U32 Status, U32 p1, U32 p2, U16 AdapterID)
 {
-     PDPA pDpa;
-     struct device *dev;
+    PDPA pDpa;
+    struct device *dev;
      
-     pDpa = PCIAdapters[AdapterID];
-     dev = pDpa->dev;
+    pDpa = PCIAdapters[AdapterID];
+    dev = pDpa->dev;
 #ifdef RCDEBUG
-      printk("rc: RCreset_callback Status 0x%x\n", (uint)Status);
+    printk("rc: RCreset_callback Status 0x%x\n", (uint)Status);
 #endif
-      /*
-       * Check to see why we were called.
-       */
-      if (pDpa->shutdown)
-      {
-           printk("rc: Shutting down interface\n");
-           pDpa->shutdown = 0;
-           pDpa->reboot = 0;
-           MOD_DEC_USE_COUNT; 
-      }
-      else if (pDpa->reboot)
-      {
-           printk("rc: reboot, shutdown adapter\n");
-           /*
-            * We don't set any of the flags in RCShutdownLANCard()
-            * and we don't pass a callback routine to it.
-            * The adapter will have already initiated the reboot by
-            * the time the function returns.
-            */
-		   RCDisableAdapterInterrupts(pDpa->id);
-           RCShutdownLANCard(pDpa->id,0,0,0);
-           printk("rc: scheduling timer...\n");
-           init_timer(&pDpa->timer);
-           pDpa->timer.expires = RUN_AT((30*HZ)/10); /* 3 sec. */
-           pDpa->timer.data = (unsigned long)dev;
-           pDpa->timer.function = &rc_timer;    /* timer handler */
-           add_timer(&pDpa->timer);
-      }
+    /*
+     * Check to see why we were called.
+     */
+    if (pDpa->shutdown)
+    {
+        printk("rc: Shutting down interface\n");
+        pDpa->shutdown = 0;
+        pDpa->reboot = 0;
+        MOD_DEC_USE_COUNT; 
+    }
+    else if (pDpa->reboot)
+    {
+        printk("rc: reboot, shutdown adapter\n");
+        /*
+         * We don't set any of the flags in RCShutdownLANCard()
+         * and we don't pass a callback routine to it.
+         * The adapter will have already initiated the reboot by
+         * the time the function returns.
+         */
+        RCDisableI2OInterrupts(pDpa->id);
+        RCShutdownLANCard(pDpa->id,0,0,0);
+        printk("rc: scheduling timer...\n");
+        init_timer(&pDpa->timer);
+        pDpa->timer.expires = RUN_AT((30*HZ)/10); /* 3 sec. */
+        pDpa->timer.data = (unsigned long)dev;
+        pDpa->timer.function = &rc_timer;    /* timer handler */
+        add_timer(&pDpa->timer);
+    }
 
 
 
@@ -656,33 +671,33 @@
 static void
 RCreboot_callback(U32 Status, U32 p1, U32 p2, U16 AdapterID)
 {
-     PDPA pDpa;
+    PDPA pDpa;
     
-     pDpa = PCIAdapters[AdapterID];
+    pDpa = PCIAdapters[AdapterID];
 #ifdef RCDEBUG
-     printk("rc: RCreboot: rcv buffers outstanding = %d\n", 
-            (uint)pDpa->numOutRcvBuffers);
+    printk("rc: RCreboot: rcv buffers outstanding = %d\n", 
+           (uint)pDpa->numOutRcvBuffers);
 #endif
-     if (pDpa->shutdown)
-     {
-          printk("rc: skipping reboot sequence -- shutdown already initiated\n");
-          return;
-     }
-     pDpa->reboot = 1;
-     /*
-      * OK, we reset the adapter and ask it to return all
-      * outstanding transmit buffers as well as the posted
-      * receive buffers.  When the adapter is done returning
-      * those buffers, it will call our RCreset_callback() 
-      * routine.  In that routine, we'll call RCShutdownLANCard()
-      * to tell the adapter that it's OK to start the reboot and
-      * schedule a timer callback routine to execute 3 seconds 
-      * later; this routine will reinitialize the adapter at that time.
-      */
-     RCResetLANCard(pDpa->id, 
-                    RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | 
-                    RC_RESOURCE_RETURN_PEND_TX_BUFFERS,0,
-                    (PFNCALLBACK)RCreset_callback);
+    if (pDpa->shutdown)
+    {
+        printk("rc: skipping reboot sequence -- shutdown already initiated\n");
+        return;
+    }
+    pDpa->reboot = 1;
+    /*
+     * OK, we reset the adapter and ask it to return all
+     * outstanding transmit buffers as well as the posted
+     * receive buffers.  When the adapter is done returning
+     * those buffers, it will call our RCreset_callback() 
+     * routine.  In that routine, we'll call RCShutdownLANCard()
+     * to tell the adapter that it's OK to start the reboot and
+     * schedule a timer callback routine to execute 3 seconds 
+     * later; this routine will reinitialize the adapter at that time.
+     */
+    RCResetLANCard(pDpa->id, 
+                   RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | 
+                   RC_RESOURCE_RETURN_PEND_TX_BUFFERS,0,
+                   (PFNCALLBACK)RCreset_callback);
 }
 
 
@@ -699,7 +714,7 @@
  * RCrecv_callback()
  * 
  * The receive packet callback routine.  This is called by
- * RCProcMsgQ() after the adapter posts buffers which have been
+ * RCProcI2OMsgQ() after the adapter posts buffers which have been
  * filled (one ethernet packet per buffer).
  */
 static void
@@ -710,131 +725,144 @@
                 U16  AdapterID)
 {
 
-     U32 len, count;
-     PDPA pDpa;
-     struct sk_buff *skb;
-     struct device *dev;
-     singleTCB tcb;
-     psingleTCB ptcb = &tcb;
+    U32 len, count;
+    PDPA pDpa;
+    struct sk_buff *skb;
+    struct device *dev;
+    singleTCB tcb;
+    psingleTCB ptcb = &tcb;
 
 
-          pDpa = PCIAdapters[AdapterID];
-          dev = pDpa->dev;
+        pDpa = PCIAdapters[AdapterID];
+        dev = pDpa->dev;
 
-          ptcb->bcount = 1;
+        ptcb->bcount = 1;
 
 #ifdef RCDEBUG
-          printk("rc: RCrecv_callback: 0x%x, 0x%x, 0x%x\n",
-                 (uint)PktCount, (uint)BucketsRemain, (uint)PacketDescBlock);
+        printk("rc: RCrecv_callback: 0x%x, 0x%x, 0x%x\n",
+               (uint)PktCount, (uint)BucketsRemain, (uint)PacketDescBlock);
 #endif
         
 #ifdef RCDEBUG
-          if ((pDpa->shutdown || pDpa->reboot) && !Status)
-               printk("shutdown||reboot && !Status: PktCount = %d\n",PktCount);
+        if ((pDpa->shutdown || pDpa->reboot) && !Status)
+            printk("shutdown||reboot && !Status: PktCount = %d\n",PktCount);
 #endif
 
-          if ( (Status != RC_REPLY_STATUS_SUCCESS) || pDpa->shutdown)
-          {
-               /*
-                * Free whatever buffers the adapter returned, but don't
-                * pass them to the kernel.
-                */
+        if ( (Status != I2O_REPLY_STATUS_SUCCESS) || pDpa->shutdown)
+        {
+            /*
+             * Free whatever buffers the adapter returned, but don't
+             * pass them to the kernel.
+             */
         
-                    if (!pDpa->shutdown && !pDpa->reboot)
-                         printk("rc: RCrecv error: status = 0x%x\n", (uint)Status);
-                    else
-                         printk("rc: Returning %d buffers, status = 0x%x\n", 
-                                PktCount, (uint)Status);
-               /*
-                * TO DO: check the nature of the failure and put the adapter in
-                * failed mode if it's a hard failure.  Send a reset to the adapter
-                * and free all outstanding memory.
-                */
-               if (Status == RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER)
-               {
-#ifdef RCDEBUG
-                    printk("RCrecv status ABORT NO DATA TRANSFER\n");
-#endif
-               }
-               /* check for reset status: RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER */ 
-               if (PacketDescBlock)
-               {
-                    while(PktCount--)
-                    {
-                         skb = (struct sk_buff *)PacketDescBlock[0];
+                if (!pDpa->shutdown && !pDpa->reboot)
+                    printk("rc: RCrecv error: status = 0x%x\n", (uint)Status);
+                else
+                    printk("rc: Returning %d buffers, status = 0x%x\n", 
+                           PktCount, (uint)Status);
+            /*
+             * TO DO: check the nature of the failure and put the adapter in
+             * failed mode if it's a hard failure.  Send a reset to the adapter
+             * and free all outstanding memory.
+             */
+            if (Status == I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER)
+            {
 #ifdef RCDEBUG
-                         printk("free skb 0x%p\n", skb);
+                printk("RCrecv status ABORT NO DATA TRANSFER\n");
 #endif
-                         dev_kfree_skb(skb);
-                         pDpa->numOutRcvBuffers--;
-                         PacketDescBlock += BD_SIZE; /* point to next context field */
-                    }
-               }
-               return;
-          }
-          else
-          {
-               while(PktCount--)
-               {
+            }
+            /* check for reset status: I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER */ 
+            if (PacketDescBlock)
+            {
+                while(PktCount--)
+                {
                     skb = (struct sk_buff *)PacketDescBlock[0];
+#ifndef LINUX_2_1
+                    skb->free = 1;
+                    skb->lock = 0;    
+#endif
 #ifdef RCDEBUG
-                    if (pDpa->shutdown)
-                         printk("shutdown: skb=0x%x\n", (uint)skb);
-
-                    printk("skb = 0x%x: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", (uint)skb,
-                           (uint)skb->data[0], (uint)skb->data[1], (uint)skb->data[2],
-                           (uint)skb->data[3], (uint)skb->data[4], (uint)skb->data[5]);
+                    printk("free skb 0x%p\n", skb);
 #endif
-                    if ( (memcmp(dev->dev_addr, skb->data, 6)) &&
-                         (!broadcast_packet(skb->data)))
+#ifdef LINUX_2_1
+                    dev_kfree_skb (skb);
+#else
+                    dev_kfree_skb(skb, FREE_READ);
+#endif
+                    pDpa->numOutRcvBuffers--;
+                    PacketDescBlock += BD_SIZE; /* point to next context field */
+                }
+            }
+            return;
+        }
+        else
+        {
+            while(PktCount--)
+            {
+                skb = (struct sk_buff *)PacketDescBlock[0];
+#ifdef RCDEBUG
+                if (pDpa->shutdown)
+                    printk("shutdown: skb=0x%x\n", (uint)skb);
+
+                printk("skb = 0x%x: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", (uint)skb,
+                       (uint)skb->data[0], (uint)skb->data[1], (uint)skb->data[2],
+                       (uint)skb->data[3], (uint)skb->data[4], (uint)skb->data[5]);
+#endif
+                if ( (memcmp(dev->dev_addr, skb->data, 6)) &&
+                     (!broadcast_packet(skb->data)))
+                {
+                    /*
+                     * Re-post the buffer to the adapter.  Since the adapter usually
+                     * return 1 to 2 receive buffers at a time, it's not too inefficient
+                     * post one buffer at a time but ... may be that should be 
+                     * optimized at some point.
+                     */
+                    ptcb->b.context = (U32)skb;    
+                    ptcb->b.scount = 1;
+                    ptcb->b.size = MAX_ETHER_SIZE;
+                    ptcb->b.addr = virt_to_bus((void *)skb->data);
+
+                    if ( RCPostRecvBuffers(pDpa->id, (PRCTCB)ptcb ) != RC_RTN_NO_ERROR)
                     {
-                         /*
-                          * Re-post the buffer to the adapter.  Since the adapter usually
-                          * return 1 to 2 receive buffers at a time, it's not too inefficient
-                          * post one buffer at a time but ... may be that should be 
-                          * optimized at some point.
-                          */
-                         ptcb->b.context = (U32)skb;    
-                         ptcb->b.scount = 1;
-                         ptcb->b.size = MAX_ETHER_SIZE;
-                         ptcb->b.addr = (U32)skb->data;
-
-                         if ( RCPostRecvBuffers(pDpa->id, (PRCTCB)ptcb ) != RC_RTN_NO_ERROR)
-                         {
-                              printk("rc: RCrecv_callback: post buffer failed!\n");
-                              dev_kfree_skb(skb);
-                         }
-                         else
-                         {
-                              pDpa->numOutRcvBuffers++;
-                         }
+                        printk("rc: RCrecv_callback: post buffer failed!\n");
+#ifdef LINUX_2_1
+                        dev_kfree_skb (skb);
+#else
+                        skb->free = 1;
+                        dev_kfree_skb(skb, FREE_READ);
+#endif
                     }
                     else
                     {
-                         len = PacketDescBlock[2];
-                         skb->dev = dev;
-                         skb_put( skb, len ); /* adjust length and tail */
-                         skb->protocol = eth_type_trans(skb, dev);
-                         netif_rx(skb);    /* send the packet to the kernel */
-                         dev->last_rx = jiffies;
+                        pDpa->numOutRcvBuffers++;
                     }
-                    pDpa->numOutRcvBuffers--;
-                    PacketDescBlock += BD_SIZE; /* point to next context field */
-               }
-          } 
+                }
+                else
+                {
+                    len = PacketDescBlock[2];
+                    skb->dev = dev;
+                    skb_put( skb, len ); /* adjust length and tail */
+                    skb->protocol = eth_type_trans(skb, dev);
+                    netif_rx(skb);    /* send the packet to the kernel */
+                    dev->last_rx = jiffies;
+                }
+                pDpa->numOutRcvBuffers--;
+                PacketDescBlock += BD_SIZE; /* point to next context field */
+            }
+        } 
     
-          /*
-           * Replenish the posted receive buffers. 
-           * DO NOT replenish buffers if the driver has already
-           * initiated a reboot or shutdown!
-           */
-
-          if (!pDpa->shutdown && !pDpa->reboot)
-          {
-               count = RC_allocate_and_post_buffers(dev, 
-                                                    MAX_NMBR_RCV_BUFFERS-pDpa->numOutRcvBuffers);
-               pDpa->numOutRcvBuffers += count;
-          }
+        /*
+         * Replenish the posted receive buffers. 
+         * DO NOT replenish buffers if the driver has already
+         * initiated a reboot or shutdown!
+         */
+
+        if (!pDpa->shutdown && !pDpa->reboot)
+        {
+            count = RC_allocate_and_post_buffers(dev, 
+                                                 MAX_NMBR_RCV_BUFFERS-pDpa->numOutRcvBuffers);
+            pDpa->numOutRcvBuffers += count;
+        }
 
 }
 
@@ -843,156 +871,156 @@
  * 
  * Interrupt handler. 
  * This routine sets up a couple of pointers and calls
- * RCProcMsgQ(), which in turn process the message and
+ * RCProcI2OMsgQ(), which in turn process the message and
  * calls one of our callback functions.
  */
 static void 
 RCinterrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 
-     PDPA pDpa;
-     struct device *dev = (struct device *)(dev_id);
+    PDPA pDpa;
+    struct device *dev = (struct device *)(dev_id);
 
-     pDpa = (PDPA) (dev->priv);
+    pDpa = (PDPA) (dev->priv);
      
-     if (pDpa->shutdown)
-          printk("rc: shutdown: service irq\n");
+    if (pDpa->shutdown)
+        printk("rc: shutdown: service irq\n");
 
 #ifdef RCDEBUG
-     printk("RC irq: pDpa = 0x%x, dev = 0x%x, id = %d\n", 
-            (uint)pDpa, (uint)dev, (uint)pDpa->id);
-     printk("dev = 0x%x\n", (uint)dev);
+    printk("RC irq: pDpa = 0x%x, dev = 0x%x, id = %d\n", 
+           (uint)pDpa, (uint)dev, (uint)pDpa->id);
+    printk("dev = 0x%x\n", (uint)dev);
 #endif
-     if (dev->interrupt)
-          printk("%s: Re-entering the interrupt handler.\n", dev->name);
-     dev->interrupt = 1;
+    if (dev->interrupt)
+        printk("%s: Re-entering the interrupt handler.\n", dev->name);
+    dev->interrupt = 1;
 
-     RCProcMsgQ(pDpa->id);
-     dev->interrupt = 0;
+    RCProcI2OMsgQ(pDpa->id);
+    dev->interrupt = 0;
 
-     return;
+    return;
 }
 
 #define REBOOT_REINIT_RETRY_LIMIT 10
 static void rc_timer(unsigned long data)
 {
-     struct device *dev = (struct device *)data;
-     PDPA pDpa = (PDPA) (dev->priv);
-     int init_status;
-     static int retry = 0;
-     int post_buffers = MAX_NMBR_RCV_BUFFERS;
-     int count = 0;
-     int requested = 0;
-
-     if (pDpa->reboot)
-     {
-
-          init_status = InitRCApiMsgLayer(pDpa->id, dev->base_addr, 
-                                          pDpa->PLanApiPA, pDpa->PLanApiPA,
-                                          (PFNTXCALLBACK)RCxmit_callback,
-                                          (PFNRXCALLBACK)RCrecv_callback,
-                                          (PFNCALLBACK)RCreboot_callback);
-
-          switch(init_status)
-          {
-          case RC_RTN_NO_ERROR:
+    struct device *dev = (struct device *)data;
+    PDPA pDpa = (PDPA) (dev->priv);
+    int init_status;
+    static int retry = 0;
+    int post_buffers = MAX_NMBR_RCV_BUFFERS;
+    int count = 0;
+    int requested = 0;
+
+    if (pDpa->reboot)
+    {
+
+        init_status = RCInitI2OMsgLayer(pDpa->id, dev->base_addr, 
+                                        pDpa->PLanApiPA, pDpa->PLanApiPA,
+                                        (PFNTXCALLBACK)RCxmit_callback,
+                                        (PFNRXCALLBACK)RCrecv_callback,
+                                        (PFNCALLBACK)RCreboot_callback);
+
+        switch(init_status)
+        {
+        case RC_RTN_NO_ERROR:
  
-               pDpa->reboot = 0;
-               pDpa->shutdown = 0;        /* just in case */
-               RCReportDriverCapability(pDpa->id, DriverControlWord);
-               RCEnableAdapterInterrupts(pDpa->id);
-
-               if (dev->flags & IFF_UP)
-               {
-                    while(post_buffers)
-                    {
-                         if (post_buffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
-                              requested = MAX_NMBR_POST_BUFFERS_PER_MSG;
-                         else
-                              requested = post_buffers;
-                         count = RC_allocate_and_post_buffers(dev, requested);
-                         post_buffers -= count;
-                         if ( count < requested )
-                              break;
-                    }
-                    pDpa->numOutRcvBuffers = 
-                         MAX_NMBR_RCV_BUFFERS - post_buffers;
-                    printk("rc: posted %d buffers \r\n", 
-                           (uint)pDpa->numOutRcvBuffers);
-               }
-               printk("rc: Initialization done.\n");
-               return;
-          case RC_RTN_FREE_Q_EMPTY:
-               retry++;
-               printk("rc: inbound free q emtpy\n");
-               break;
-          default:
-               retry++;
-               printk("rc: unexpected bad status after reboot\n");    
-               break;
-          }
-
-          if (retry > REBOOT_REINIT_RETRY_LIMIT)
-          {
-               printk("rc: unable to reinitialize adapter after reboot\n");
-               printk("rc: decrementing driver and closing interface\n");
-               RCDisableAdapterInterrupts(pDpa->id);
-               dev->flags &= ~IFF_UP;
-               MOD_DEC_USE_COUNT; 
-          }
-          else
-          {
-               printk("rc: rescheduling timer...\n");
-               init_timer(&pDpa->timer);
-               pDpa->timer.expires = RUN_AT((30*HZ)/10); /* 3 sec. */
-               pDpa->timer.data = (unsigned long)dev;
-               pDpa->timer.function = &rc_timer;    /* timer handler */
-               add_timer(&pDpa->timer);
-          }
-     }
-     else
-     {
-          printk("rc: timer??\n");
-     }
+            pDpa->reboot = 0;
+            pDpa->shutdown = 0;        /* just in case */
+            RCReportDriverCapability(pDpa->id, DriverControlWord);
+            RCEnableI2OInterrupts(pDpa->id);
+
+            if (dev->flags & IFF_UP)
+            {
+                while(post_buffers)
+                {
+                    if (post_buffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
+                        requested = MAX_NMBR_POST_BUFFERS_PER_MSG;
+                    else
+                        requested = post_buffers;
+                    count = RC_allocate_and_post_buffers(dev, requested);
+                    post_buffers -= count;
+                    if ( count < requested )
+                        break;
+                }
+                pDpa->numOutRcvBuffers = 
+                    MAX_NMBR_RCV_BUFFERS - post_buffers;
+                printk("rc: posted %d buffers \r\n", 
+                       (uint)pDpa->numOutRcvBuffers);
+            }
+            printk("rc: Initialization done.\n");
+            return;
+        case RC_RTN_FREE_Q_EMPTY:
+            retry++;
+            printk("rc: inbound free q emtpy\n");
+            break;
+        default:
+            retry++;
+            printk("rc: unexpected bad status after reboot\n");    
+            break;
+        }
+
+        if (retry > REBOOT_REINIT_RETRY_LIMIT)
+        {
+            printk("rc: unable to reinitialize adapter after reboot\n");
+            printk("rc: decrementing driver and closing interface\n");
+            RCDisableI2OInterrupts(pDpa->id);
+            dev->flags &= ~IFF_UP;
+            MOD_DEC_USE_COUNT; 
+        }
+        else
+        {
+            printk("rc: rescheduling timer...\n");
+            init_timer(&pDpa->timer);
+            pDpa->timer.expires = RUN_AT((30*HZ)/10); /* 3 sec. */
+            pDpa->timer.data = (unsigned long)dev;
+            pDpa->timer.function = &rc_timer;    /* timer handler */
+            add_timer(&pDpa->timer);
+        }
+    }
+    else
+    {
+        printk("rc: timer??\n");
+    }
 }
 
 static int
 RCclose(struct device *dev)
 {
 
-     PDPA pDpa = (PDPA) dev->priv;
+    PDPA pDpa = (PDPA) dev->priv;
 
 #ifdef RCDEBUG
-     printk("rc: RCclose\r\n");
+    printk("rc: RCclose\r\n");
 #endif
-     if (pDpa->reboot)
-     {
-          printk("rc: skipping reset -- adapter already in reboot mode\n");
-          dev->flags &= ~IFF_UP;
-          pDpa->shutdown = 1;
-          return 0;
-     }
+    if (pDpa->reboot)
+    {
+        printk("rc: skipping reset -- adapter already in reboot mode\n");
+        dev->flags &= ~IFF_UP;
+        pDpa->shutdown = 1;
+        return 0;
+    }
 #ifdef RCDEBUG
-     printk("rc: receive buffers outstanding: %d\n", 
-            (uint)pDpa->numOutRcvBuffers);
+    printk("rc: receive buffers outstanding: %d\n", 
+           (uint)pDpa->numOutRcvBuffers);
 #endif
 
-     pDpa->shutdown = 1;
+    pDpa->shutdown = 1;
 
-     /*
-      * We can't allow the driver to be unloaded until the adapter returns
-      * all posted receive buffers.  It doesn't hurt to tell the adapter
-      * to return all posted receive buffers and outstanding xmit buffers,
-      * even if there are none.
-      */
+    /*
+     * We can't allow the driver to be unloaded until the adapter returns
+     * all posted receive buffers.  It doesn't hurt to tell the adapter
+     * to return all posted receive buffers and outstanding xmit buffers,
+     * even if there are none.
+     */
 
-     RCShutdownLANCard(pDpa->id, 
-                       RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | 
-                       RC_RESOURCE_RETURN_PEND_TX_BUFFERS,0,
-                       (PFNCALLBACK)RCreset_callback);
+    RCShutdownLANCard(pDpa->id, 
+                      RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | 
+                      RC_RESOURCE_RETURN_PEND_TX_BUFFERS,0,
+                      (PFNCALLBACK)RCreset_callback);
 
-     dev->flags &= ~IFF_UP;
-     return 0;
+        dev->flags &= ~IFF_UP;
+    return 0;
 }
 
 static struct enet_statistics *
@@ -1094,230 +1122,298 @@
  
     switch (cmd)  {
  
-        case RCU_PROTOCOL_REV:
-             /*
-              * Assign user protocol revision, to tell user-level
-              * controller program whether or not it's in sync.
-              */
-             rq->ifr_ifru.ifru_data = (caddr_t) USER_PROTOCOL_REV;
-             break;
+    case RCU_PROTOCOL_REV:
+        /*
+         * Assign user protocol revision, to tell user-level
+         * controller program whether or not it's in sync.
+         */
+        rq->ifr_ifru.ifru_data = (caddr_t) USER_PROTOCOL_REV;
+        break;
   
 
-        case RCU_COMMAND:
-        {
-            if(copy_from_user(&RCuser, rq->ifr_data, sizeof(RCuser)))
-            	return -EFAULT;
+    case RCU_COMMAND:
+    {
+#ifdef LINUX_2_1
+        if(copy_from_user(&RCuser, rq->ifr_data, sizeof(RCuser)))
+             return -EFAULT;
+#else
+        int error;
+        error=verify_area(VERIFY_WRITE, rq->ifr_data, sizeof(RCuser));
+        if (error)  {
+            return error;
+        }
+        memcpy_fromfs(&RCuser, rq->ifr_data, sizeof(RCuser));
+#endif
         
 #ifdef RCDEBUG
-            printk("RCioctl: RCuser_cmd = 0x%x\n", RCuser.cmd);
+        printk("RCioctl: RCuser_cmd = 0x%x\n", RCuser.cmd);
 #endif
   
-            switch(RCuser.cmd)
+        switch(RCuser.cmd)
+        {
+        case RCUC_GETFWVER:
+            printk("RC GETFWVER\n");
+            RCUD_GETFWVER = &RCuser.RCUS_GETFWVER;
+            RCGetFirmwareVer(pDpa->id, (PU8) &RCUD_GETFWVER->FirmString, NULL);
+            break;
+        case RCUC_GETINFO:
+            printk("RC GETINFO\n");
+            RCUD_GETINFO = &RCuser.RCUS_GETINFO;
+            RCUD_GETINFO -> mem_start = dev->base_addr;
+            RCUD_GETINFO -> mem_end = dev->base_addr + 2*32768;
+            RCUD_GETINFO -> base_addr = pDpa->pci_addr;
+            RCUD_GETINFO -> irq = dev->irq;
+            break;
+        case RCUC_GETIPANDMASK:
+            printk("RC GETIPANDMASK\n");
+            RCUD_GETIPANDMASK = &RCuser.RCUS_GETIPANDMASK;
+            RCGetRavlinIPandMask(pDpa->id, (PU32) &RCUD_GETIPANDMASK->IpAddr,
+                                 (PU32) &RCUD_GETIPANDMASK->NetMask, NULL);
+            break;
+        case RCUC_GETLINKSTATISTICS:
+            printk("RC GETLINKSTATISTICS\n");
+            RCUD_GETLINKSTATISTICS = &RCuser.RCUS_GETLINKSTATISTICS;
+            RCGetLinkStatistics(pDpa->id, (P_RCLINKSTATS) &RCUD_GETLINKSTATISTICS->StatsReturn, NULL);
+            break;
+        case RCUC_GETLINKSTATUS:
+            printk("RC GETLINKSTATUS\n");
+            RCUD_GETLINKSTATUS = &RCuser.RCUS_GETLINKSTATUS;
+            RCGetLinkStatus(pDpa->id, (PU32) &RCUD_GETLINKSTATUS->ReturnStatus, NULL);
+            break;
+        case RCUC_GETMAC:
+            printk("RC GETMAC\n");
+            RCUD_GETMAC = &RCuser.RCUS_GETMAC;
+            RCGetMAC(pDpa->id, (PU8) &RCUD_GETMAC->mac, NULL);
+            break;
+        case RCUC_GETPROM:
+            printk("RC GETPROM\n");
+            RCUD_GETPROM = &RCuser.RCUS_GETPROM;
+            RCGetPromiscuousMode(pDpa->id, (PU32) &RCUD_GETPROM->PromMode, NULL);
+            break;
+        case RCUC_GETBROADCAST:
+            printk("RC GETBROADCAST\n");
+            RCUD_GETBROADCAST = &RCuser.RCUS_GETBROADCAST;
+            RCGetBroadcastMode(pDpa->id, (PU32) &RCUD_GETBROADCAST->BroadcastMode, NULL);
+            break;
+        case RCUC_GETSPEED:
+            printk("RC GETSPEED\n");
+            if (!(dev->flags & IFF_UP))    
             {
-                case RCUC_GETFWVER:
-                    printk("RC GETFWVER\n");
-                    RCUD_GETFWVER = &RCuser.RCUS_GETFWVER;
-                    RCGetFirmwareVer(pDpa->id, (PU8) &RCUD_GETFWVER->FirmString, NULL);
-                    break;
-                case RCUC_GETINFO:
-                    printk("RC GETINFO\n");
-                    RCUD_GETINFO = &RCuser.RCUS_GETINFO;
-                    RCUD_GETINFO -> mem_start = dev->base_addr;
-                    RCUD_GETINFO -> mem_end = dev->base_addr + 32768;
-                    RCUD_GETINFO -> base_addr = pDpa->pci_addr;
-                    RCUD_GETINFO -> irq = dev->irq;
-                    break;
-                case RCUC_GETIPANDMASK:
-                    printk("RC GETIPANDMASK\n");
-                    RCUD_GETIPANDMASK = &RCuser.RCUS_GETIPANDMASK;
-                    RCGetRavlinIPandMask(pDpa->id, (PU32) &RCUD_GETIPANDMASK->IpAddr,
-                        (PU32) &RCUD_GETIPANDMASK->NetMask, NULL);
-                    break;
-                case RCUC_GETLINKSTATISTICS:
-                    printk("RC GETLINKSTATISTICS\n");
-                    RCUD_GETLINKSTATISTICS = &RCuser.RCUS_GETLINKSTATISTICS;
-                    RCGetLinkStatistics(pDpa->id, (P_RCLINKSTATS) &RCUD_GETLINKSTATISTICS->StatsReturn, NULL);
-                    break;
-                case RCUC_GETLINKSTATUS:
-                    printk("RC GETLINKSTATUS\n");
-                    RCUD_GETLINKSTATUS = &RCuser.RCUS_GETLINKSTATUS;
-                    RCGetLinkStatus(pDpa->id, (PU32) &RCUD_GETLINKSTATUS->ReturnStatus, NULL);
-                    break;
-                case RCUC_GETMAC:
-                    printk("RC GETMAC\n");
-                    RCUD_GETMAC = &RCuser.RCUS_GETMAC;
-                    RCGetMAC(pDpa->id, (PU8) &RCUD_GETMAC->mac, NULL);
-                    break;
-                case RCUC_GETSPEED:
-                    printk("RC GETSPEED\n");
-                    if (!(dev->flags & IFF_UP))    
-                    {
-                        printk("RCioctl, GETSPEED error: interface down\n");
-                        return -ENODATA;
-                    }
-                    RCUD_GETSPEED = &RCuser.RCUS_GETSPEED;
-                    RCGetLinkSpeed(pDpa->id, (PU32) &RCUD_GETSPEED->LinkSpeedCode, NULL);
-                    printk("RC speed = 0x%ld\n", RCUD_GETSPEED->LinkSpeedCode);
-                    break;
-                default:
-                    printk("RC command default\n");
-                    RCUD_DEFAULT = &RCuser.RCUS_DEFAULT;
-                    RCUD_DEFAULT -> rc = 0x11223344;
-                    break;
+                printk("RCioctl, GETSPEED error: interface down\n");
+                return -ENODATA;
             }
-            copy_to_user(rq->ifr_data, &RCuser, sizeof(RCuser));
+            RCUD_GETSPEED = &RCuser.RCUS_GETSPEED;
+            RCGetLinkSpeed(pDpa->id, (PU32) &RCUD_GETSPEED->LinkSpeedCode, NULL);
+            printk("RC speed = 0x%ld\n", RCUD_GETSPEED->LinkSpeedCode);
+            break;
+        case RCUC_SETIPANDMASK:
+            printk("RC SETIPANDMASK\n");
+            RCUD_SETIPANDMASK = &RCuser.RCUS_SETIPANDMASK;
+            printk ("RC New IP Addr = %d.%d.%d.%d, ", (U8) ((RCUD_SETIPANDMASK->IpAddr) & 0xff),
+                    (U8) ((RCUD_SETIPANDMASK->IpAddr >>  8) & 0xff),
+                    (U8) ((RCUD_SETIPANDMASK->IpAddr >> 16) & 0xff),
+                    (U8) ((RCUD_SETIPANDMASK->IpAddr >> 24) & 0xff));
+            printk ("RC New Mask = %d.%d.%d.%d\n", (U8) ((RCUD_SETIPANDMASK->NetMask) & 0xff),
+                    (U8) ((RCUD_SETIPANDMASK->NetMask >>  8) & 0xff),
+                    (U8) ((RCUD_SETIPANDMASK->NetMask >> 16) & 0xff),
+                    (U8) ((RCUD_SETIPANDMASK->NetMask >> 24) & 0xff));
+            RCSetRavlinIPandMask(pDpa->id, (U32) RCUD_SETIPANDMASK->IpAddr,
+                                 (U32) RCUD_SETIPANDMASK->NetMask);
+            break;
+        case RCUC_SETMAC:
+            printk("RC SETMAC\n");
+            RCUD_SETMAC = &RCuser.RCUS_SETMAC;
+            printk ("RC New MAC addr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+                    (U8) (RCUD_SETMAC->mac[0]), (U8) (RCUD_SETMAC->mac[1]), (U8) (RCUD_SETMAC->mac[2]),
+                    (U8) (RCUD_SETMAC->mac[3]), (U8) (RCUD_SETMAC->mac[4]), (U8) (RCUD_SETMAC->mac[5]));
+            RCSetMAC(pDpa->id, (PU8) &RCUD_SETMAC->mac);
+            break;
+        case RCUC_SETSPEED:
+            printk("RC SETSPEED\n");
+            RCUD_SETSPEED = &RCuser.RCUS_SETSPEED;
+            RCSetLinkSpeed(pDpa->id, (U16) RCUD_SETSPEED->LinkSpeedCode);
+            printk("RC New speed = 0x%d\n", RCUD_SETSPEED->LinkSpeedCode);
+            break;
+        case RCUC_SETPROM:
+            printk("RC SETPROM\n");
+            RCUD_SETPROM = &RCuser.RCUS_SETPROM;
+            RCSetPromiscuousMode(pDpa->id,(U16)RCUD_SETPROM->PromMode);
+            printk("RC New prom mode = 0x%d\n", RCUD_SETPROM->PromMode);
+            break;
+        case RCUC_SETBROADCAST:
+            printk("RC SETBROADCAST\n");
+            RCUD_SETBROADCAST = &RCuser.RCUS_SETBROADCAST;
+            RCSetBroadcastMode(pDpa->id,(U16)RCUD_SETBROADCAST->BroadcastMode);
+            printk("RC New broadcast mode = 0x%d\n", RCUD_SETBROADCAST->BroadcastMode);
             break;
-        }   /* RCU_COMMAND */ 
-
         default:
-            printk("RC default\n");
-            rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678;
+            printk("RC command default\n");
+            RCUD_DEFAULT = &RCuser.RCUS_DEFAULT;
+            RCUD_DEFAULT -> rc = 0x11223344;
             break;
+        }
+#ifdef LINUX_2_1
+        copy_to_user(rq->ifr_data, &RCuser, sizeof(RCuser));
+#else
+        memcpy_tofs(rq->ifr_data, &RCuser, sizeof(RCuser));
+#endif
+        break;
+    }   /* RCU_COMMAND */ 
+
+    default:
+        printk("RC default\n");
+        rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678;
+        break;
     }
     return 0;
 }
 
 static int RCconfig(struct device *dev, struct ifmap *map)
 {
-     /*
-      * To be completed ...
+    /*
+     * To be completed ...
       */
-      printk("rc: RCconfig\n");
-      return 0;
-      if (dev->flags & IFF_UP)    /* can't act on a running interface */
-           return -EBUSY;
+    printk("rc: RCconfig\n");
+    return 0;
+    if (dev->flags & IFF_UP)    /* can't act on a running interface */
+        return -EBUSY;
 
      /* Don't allow changing the I/O address */
-     if (map->base_addr != dev->base_addr) {
-          printk(KERN_WARNING "RC pci45: Change I/O address not implemented\n");
-          return -EOPNOTSUPP;
-     }
-     return 0;
+    if (map->base_addr != dev->base_addr) {
+        printk(KERN_WARNING "RC pci45: Change I/O address not implemented\n");
+        return -EOPNOTSUPP;
+    }
+    return 0;
 }
 
-#ifdef MODULE
-void cleanup_module(void)
+void
+cleanup_module(void)
 {
-     PDPA pDpa;
-     struct device *next;
+    PDPA pDpa;
+    struct device *next;
 
 
 #ifdef RCDEBUG
-     printk("rc: RC cleanup_module\n");
-     printk("rc: root_RCdev = 0x%x\n", (uint)root_RCdev);
+    printk("rc: RC cleanup_module\n");
+    printk("rc: root_RCdev = 0x%x\n", (uint)root_RCdev);
 #endif
 
 
-     while (root_RCdev)
-     {
-          pDpa = (PDPA) root_RCdev->priv;
+    while (root_RCdev)
+    {
+        pDpa = (PDPA) root_RCdev->priv;
 #ifdef RCDEBUG
-          printk("rc: cleanup 0x%08X\n", (uint)root_RCdev);
+        printk("rc: cleanup 0x%08X\n", (uint)root_RCdev);
 #endif
-          printk("Adapter reset: 0x%x\n", RCResetAdapter(pDpa->id));
-          unregister_netdev(root_RCdev);
-          next = pDpa->next;
-
-          vfree((unsigned long *)root_RCdev->base_addr); 
-          free_irq( root_RCdev->irq, root_RCdev );
-          kfree(root_RCdev);
-          root_RCdev = next;
-     }
+        printk("IOP reset: 0x%x\n", RCResetIOP(pDpa->id));
+        unregister_netdev(root_RCdev);
+        next = pDpa->next;
+
+        iounmap((unsigned long *)root_RCdev->base_addr); 
+        free_irq( root_RCdev->irq, root_RCdev );
+        kfree(root_RCdev);
+        root_RCdev = next;
+    }
 }
-#endif
-
 
 static int
 RC_allocate_and_post_buffers(struct device *dev, int numBuffers)
 {
 
-     int i;
-     PDPA pDpa = (PDPA)dev->priv;
-     PU32 p;
-     psingleB pB;
-     struct sk_buff *skb;
-     RC_RETURN status;
-
-     if (!numBuffers)
-          return 0;
-     else if (numBuffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
-     {
+    int i;
+    PDPA pDpa = (PDPA)dev->priv;
+    PU32 p;
+    psingleB pB;
+    struct sk_buff *skb;
+    RC_RETURN status;
+
+    if (!numBuffers)
+        return 0;
+    else if (numBuffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
+    {
 #ifdef RCDEBUG
-          printk("rc: Too many buffers requested!\n");
-          printk("rc: attempting to allocate only 32 buffers\n");
+        printk("rc: Too many buffers requested!\n");
+        printk("rc: attempting to allocate only 32 buffers\n");
 #endif
-          numBuffers = 32;
-     }
+        numBuffers = 32;
+    }
     
-     p = (PU32) kmalloc(sizeof(U32) + numBuffers*sizeof(singleB), GFP_ATOMIC);
+    p = (PU32) kmalloc(sizeof(U32) + numBuffers*sizeof(singleB), GFP_ATOMIC);
 
 #ifdef RCDEBUG
-     printk("rc: TCB = 0x%x\n", (uint)p);
+    printk("rc: TCB = 0x%x\n", (uint)p);
 #endif
 
-     if (!p)
-     {
-          printk("rc: RCopen: unable to allocate TCB\n");
-          return 0;
-     }
+    if (!p)
+    {
+        printk("rc: RCopen: unable to allocate TCB\n");
+        return 0;
+    }
 
-     p[0] = 0;                              /* Buffer Count */
-     pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */
+    p[0] = 0;                              /* Buffer Count */
+    pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */
 
 #ifdef RCDEBUG
-     printk("rc: p[0] = 0x%x, p = 0x%x, pB = 0x%x\n", (uint)p[0], (uint)p, (uint)pB);
-     printk("rc: pB = 0x%x\n", (uint)pB);
+    printk("rc: p[0] = 0x%x, p = 0x%x, pB = 0x%x\n", (uint)p[0], (uint)p, (uint)pB);
+    printk("rc: pB = 0x%x\n", (uint)pB);
 #endif
 
-     for (i=0; i<numBuffers; i++)
-     {
-          skb = dev_alloc_skb(MAX_ETHER_SIZE+2);
-          if (!skb)
-          {
-               printk("rc: Doh! RCopen: unable to allocate enough skbs!\n");
-               if (*p != 0)        /* did we allocate any buffers at all? */
-               {
+    for (i=0; i<numBuffers; i++)
+    {
+        skb = dev_alloc_skb(MAX_ETHER_SIZE+2);
+        if (!skb)
+        {
+            printk("rc: Doh! RCopen: unable to allocate enough skbs!\n");
+            if (*p != 0)        /* did we allocate any buffers at all? */
+            {
 #ifdef RCDEBUG
-                    printk("rc: will post only %d buffers \n", (uint)(*p));
+                printk("rc: will post only %d buffers \n", (uint)(*p));
 #endif
-                    break;
-               }
-               else 
-               {
-                    kfree(p);    /* Free the TCB */
-                    return 0;
-               }
-          }
+                break;
+            }
+            else 
+            {
+                kfree(p);    /* Free the TCB */
+                return 0;
+            }
+        }
 #ifdef RCDEBUG
-          printk("post 0x%x\n", (uint)skb);
+        printk("post 0x%x\n", (uint)skb);
 #endif
-          skb_reserve(skb, 2);    /* Align IP on 16 byte boundaries */
-          pB->context = (U32)skb;
-          pB->scount = 1;        /* segment count */
-          pB->size = MAX_ETHER_SIZE;
-          pB->addr = (U32)skb->data;
-          p[0]++;
-          pB++;
-     }
+        skb_reserve(skb, 2);    /* Align IP on 16 byte boundaries */
+        pB->context = (U32)skb;
+        pB->scount = 1;        /* segment count */
+        pB->size = MAX_ETHER_SIZE;
+        pB->addr = virt_to_bus((void *)skb->data);
+        p[0]++;
+        pB++;
+    }
 
-     if ( (status = RCPostRecvBuffers(pDpa->id, (PRCTCB)p )) != RC_RTN_NO_ERROR)
-     {
-          printk("rc: Post buffer failed with error code 0x%x!\n", status);
-          pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */
-          while(p[0])
-          {
-               skb = (struct sk_buff *)pB->context;
+    if ( (status = RCPostRecvBuffers(pDpa->id, (PRCTCB)p )) != RC_RTN_NO_ERROR)
+    {
+        printk("rc: Post buffer failed with error code 0x%x!\n", status);
+        pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */
+        while(p[0])
+        {
+            skb = (struct sk_buff *)pB->context;
+#ifndef LINUX_2_1
+            skb->free = 1;    
+#endif
 #ifdef RCDEBUG
-               printk("rc: freeing 0x%x\n", (uint)skb);
+            printk("rc: freeing 0x%x\n", (uint)skb);
 #endif
-               dev_kfree_skb(skb);
-               p[0]--;
-               pB++;
-          }
+#ifdef LINUX_2_1
+            dev_kfree_skb (skb);
+#else
+            dev_kfree_skb(skb, FREE_READ);
+#endif
+            p[0]--;
+            pB++;
+        }
 #ifdef RCDEBUG
-           printk("rc: freed all buffers, p[0] = %ld\n", p[0]);
+        printk("rc: freed all buffers, p[0] = %ld\n", p[0]);
 #endif
-     }
-     kfree(p);
-     return(p[0]);                /* return the number of posted buffers */
+    }
+    kfree(p);
+    return(p[0]);                /* return the number of posted buffers */
 }
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/net/znet.c linux/drivers/net/znet.c
--- v2.2.0-pre7/linux/drivers/net/znet.c	Fri Jan  8 22:36:09 1999
+++ linux/drivers/net/znet.c	Thu Jan 14 10:31:41 1999
@@ -207,11 +207,11 @@
 	char *p;
 
 	/* This code scans the region 0xf0000 to 0xfffff for a "NETIDBLK". */
-	for(p = phys_to_virt(0xf0000); p < phys_to_virt(0x100000); p++)
+	for(p = (char *)phys_to_virt(0xf0000); p < (char *)phys_to_virt(0x100000); p++)
 		if (*p == 'N'  &&  strncmp(p, "NETIDBLK", 8) == 0)
 			break;
 
-	if (p >= pyhs_to_virt(0x100000)) {
+	if (p >= (char *)phys_to_virt(0x100000)) {
 		if (znet_debug > 1)
 			printk(KERN_INFO "No Z-Note ethernet adaptor found.\n");
 		return ENODEV;
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c
--- v2.2.0-pre7/linux/drivers/pci/oldproc.c	Thu Jan  7 15:11:37 1999
+++ linux/drivers/pci/oldproc.c	Fri Jan 15 14:36:21 1999
@@ -162,8 +162,8 @@
 	DEVICE( SI,		SI_496,		"85C496"),
 	DEVICE( SI,		SI_601,		"85C601"),
 	DEVICE( SI,		SI_5107,	"5107"),
-	DEVICE( SI,		SI_5511,		"85C5511"),
-	DEVICE( SI,		SI_5513,		"85C5513"),
+	DEVICE( SI,		SI_5511,       	"85C5511"),
+	DEVICE( SI,		SI_5513,	"85C5513"),
 	DEVICE( SI,		SI_5571,	"5571"),
 	DEVICE( SI,		SI_5591,	"5591/5592 Host"),
 	DEVICE( SI,		SI_5597,	"5597/5598 Host"),
@@ -253,6 +253,8 @@
 	DEVICE( VISION,		VISION_QD8580,	"QD-8580"),
 	DEVICE( BROOKTREE,	BROOKTREE_848,	"Bt848"),
 	DEVICE( BROOKTREE,	BROOKTREE_849A,	"Bt849"),
+	DEVICE( BROOKTREE,      BROOKTREE_878_1,"Bt878 2nd Contr. (?)"),
+	DEVICE( BROOKTREE,      BROOKTREE_878,  "Bt878"),
 	DEVICE( BROOKTREE,	BROOKTREE_8474,	"Bt8474"),
 	DEVICE( SIERRA,		SIERRA_STB,	"STB Horizon 64"),
 	DEVICE( ACC,		ACC_2056,	"2056"),
@@ -371,9 +373,9 @@
 	DEVICE( VORTEX,		VORTEX_GDT6557RP2,"GDT 6557RP2"),
 	DEVICE( VORTEX,		VORTEX_GDT6x11RP2,"GDT 6111RP2/6511RP2"),
 	DEVICE( VORTEX,		VORTEX_GDT6x21RP2,"GDT 6121RP2/6521RP2"),
-	DEVICE( EF,		EF_ATM_FPGA,		"155P-MF1 (FPGA)"),
-	DEVICE( EF,		EF_ATM_ASIC,	"155P-MF1 (ASIC)"),
-	DEVICE( FORE,		FORE_PCA200PC, "PCA-200PC"),
+	DEVICE( EF,		EF_ATM_FPGA,   	"155P-MF1 (FPGA)"),
+	DEVICE( EF,		EF_ATM_ASIC,    "155P-MF1 (ASIC)"),
+	DEVICE( FORE,		FORE_PCA200PC,  "PCA-200PC"),
 	DEVICE( FORE,		FORE_PCA200E,	 "PCA-200E"),
 	DEVICE( IMAGINGTECH,	IMAGINGTECH_ICPCI, "MVC IC-PCI"),
 	DEVICE( PHILIPS,	PHILIPS_SAA7145,"SAA7145"),
@@ -435,6 +437,7 @@
 	DEVICE( O2,		O2_6832,	"6832"),
 	DEVICE( 3DFX,		3DFX_VOODOO,	"Voodoo"),
 	DEVICE( 3DFX,		3DFX_VOODOO2,	"Voodoo2"),
+	DEVICE( 3DFX,           3DFX_BANSHEE,   "Banshee"),
 	DEVICE( SIGMADES,	SIGMADES_6425,	"REALmagic64/GX"),
 	DEVICE( STALLION,	STALLION_ECHPCI832,"EasyConnection 8/32"),
 	DEVICE( STALLION,	STALLION_ECHPCI864,"EasyConnection 8/64"),
@@ -448,7 +451,7 @@
 	DEVICE( SATSAGEM,	SATSAGEM_TELSATTURBO,"Telsat Turbo DVB"),
 	DEVICE( HUGHES,		HUGHES_DIRECPC,	"DirecPC"),
 	DEVICE( ENSONIQ,	ENSONIQ_AUDIOPCI,"AudioPCI"),
-	DEVICE( ALTEON,		ALTEON_ACENIC,"AceNIC"),
+	DEVICE( ALTEON,		ALTEON_ACENIC,  "AceNIC"),
 	DEVICE( PICTUREL,	PICTUREL_PCIVST,"PCIVST"),
 	DEVICE( NVIDIA_SGS,	NVIDIA_SGS_RIVA128,	"Riva 128"),
 	DEVICE( CBOARDS,	CBOARDS_DAS1602_16,"DAS1602/16"),
@@ -468,7 +471,7 @@
 	DEVICE( S3,		S3_AURORA64VP,	"Aurora64V+"),
 	DEVICE( S3,		S3_TRIO64UVP,	"Trio64UV+"),
 	DEVICE( S3,		S3_ViRGE_VX,	"ViRGE/VX"),
-	DEVICE( S3,		S3_868,	"Vision 868"),
+	DEVICE( S3,		S3_868,	        "Vision 868"),
 	DEVICE( S3,		S3_928,		"Vision 928-P"),
 	DEVICE( S3,		S3_864_1,	"Vision 864-P"),
 	DEVICE( S3,		S3_864_2,	"Vision 864-P"),
@@ -584,7 +587,7 @@
 		       return 0;
 		    continue;
 	    }
-
+  	   
 	    return & dev_info[ i ];
 	}
 }
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx
--- v2.2.0-pre7/linux/drivers/scsi/ChangeLog.ncr53c8xx	Wed Dec 16 10:32:55 1998
+++ linux/drivers/scsi/ChangeLog.ncr53c8xx	Sat Jan 16 16:57:16 1999
@@ -1,3 +1,16 @@
+Sat Jan 16  17:30 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.1f
+	- Some PCI fix-ups not needed any more for PPC (from Cort).
+	- Cache line size set to 16 DWORDS for Sparc   (from DSM).
+	- Waiting list look-up didn't work for the first command of the list.
+	- Remove 2 useless lines of code.
+
+Sun Dec 13  18:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.1e
+	- Same work-around as for the 53c876 rev <= 0x15 for 53c896 rev 1:
+	  Disable overlapped arbitration. This will not make difference 
+	  since the chip has on-chip RAM.
+
 Thu Nov 26  22:00 1998 Gerard Roudier (groudier@club-internet.fr)
 	* revision 3.1d
 	- The SISL RAID change requires now remap_pci_mem() stuff to be 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/NCR53c406a.c linux/drivers/scsi/NCR53c406a.c
--- v2.2.0-pre7/linux/drivers/scsi/NCR53c406a.c	Fri Oct 23 22:01:21 1998
+++ linux/drivers/scsi/NCR53c406a.c	Fri Jan 15 14:41:04 1999
@@ -656,10 +656,10 @@
 static void wait_intr() {
     int i = jiffies + WATCHDOG;
     
-    while(i>jiffies && !(inb(STAT_REG)&0xe0)) /* wait for a pseudo-interrupt */
+    while(time_after(i,jiffies) && !(inb(STAT_REG)&0xe0)) /* wait for a pseudo-interrupt */
         barrier();
     
-    if (i <= jiffies) {		/* Timed out */
+    if (time_before_eq(i,jiffies)) {		/* Timed out */
         rtrc(0);
         current_SC->result = DID_TIME_OUT << 16;
         current_SC->SCp.phase = idle;
@@ -983,9 +983,9 @@
     
     /* Wait for the interrupt to occur */
     i = jiffies + WATCHDOG;
-    while(i > jiffies && !(inb(STAT_REG) & 0x80))
+    while(time_after(i, jiffies) && !(inb(STAT_REG) & 0x80))
         barrier();
-    if (i <= jiffies) {		/* Timed out, must be hardware trouble */
+    if (time_before_eq(i, jiffies)) {		/* Timed out, must be hardware trouble */
         probe_irq_off(irqs);
         return -1;
     }
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/aha152x.c linux/drivers/scsi/aha152x.c
--- v2.2.0-pre7/linux/drivers/scsi/aha152x.c	Wed Jan 13 15:00:42 1999
+++ linux/drivers/scsi/aha152x.c	Fri Jan 15 14:41:04 1999
@@ -575,7 +575,7 @@
 {
    unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */
 
-   while (jiffies < the_time)
+   while (time_before(jiffies, the_time))
      barrier();
 }
 
@@ -1038,7 +1038,7 @@
     SETBITS(DMACNTRL0, SWINT);
 
     the_time=jiffies+100;
-    while(!HOSTDATA(shpnt)->swint && jiffies<the_time)
+    while(!HOSTDATA(shpnt)->swint && time_before(jiffies, the_time))
       barrier();
 
     free_irq(shpnt->irq,shpnt);
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.2.0-pre7/linux/drivers/scsi/aic7xxx.c	Wed Jan 13 15:00:42 1999
+++ linux/drivers/scsi/aic7xxx.c	Sun Jan 17 18:28:06 1999
@@ -513,8 +513,8 @@
  * Make a define that will tell the driver not to use tagged queueing
  * by default.
  */
-#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0,\
-                              0, 0, 0, 0, 0, 0, 0, 0}
+#define DEFAULT_TAG_COMMANDS {255, 255, 255, 255, 255, 255, 255, 255,\
+                              255, 255, 255, 255, 255, 255, 255, 255}
 
 /*
  * Modify this as you see fit for your system.  By setting tag_commands
@@ -9017,8 +9017,8 @@
           {
             printk("aic7xxx: <%s> at PCI %d/%d\n", 
               board_names[aic_pdevs[i].board_name_index],
-              PCI_SLOT(temp_p->pdev->devfn),
-              PCI_FUNC(temp_p->pdev->devfn));
+              PCI_SLOT(temp_p->pci_device_fn),
+              PCI_FUNC(temp_p->pci_device_fn));
             printk("aic7xxx: Controller disabled by BIOS, ignoring.\n");
             kfree(temp_p);
             temp_p = NULL;
@@ -9050,8 +9050,8 @@
                  */
                 printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d\n", 
                   board_names[aic_pdevs[i].board_name_index],
-                  PCI_SLOT(temp_p->pdev->devfn),
-                  PCI_FUNC(temp_p->pdev->devfn));
+                  PCI_SLOT(temp_p->pci_device_fn),
+                  PCI_FUNC(temp_p->pci_device_fn));
                 printk(KERN_INFO "aic7xxx: MMAPed I/O failed, reverting to "
                                  "Programmed I/O.\n");
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v2.2.0-pre7/linux/drivers/scsi/hosts.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/scsi/hosts.c	Sun Jan 17 18:32:26 1999
@@ -452,6 +452,9 @@
 #ifdef CONFIG_SCSI_AIC7XXX
     AIC7XXX,
 #endif
+#ifdef CONFIG_FD_MCS
+   FD_MCS,
+#endif
 #ifdef CONFIG_SCSI_FUTURE_DOMAIN
     FDOMAIN_16X0,
 #endif
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/i91uscsi.c linux/drivers/scsi/i91uscsi.c
--- v2.2.0-pre7/linux/drivers/scsi/i91uscsi.c	Wed Jan 13 15:00:42 1999
+++ linux/drivers/scsi/i91uscsi.c	Thu Jan 14 10:31:41 1999
@@ -510,10 +510,10 @@
 		if (i91u_adpt[i].ADPT_BIOS < wBIOS)
 			continue;
 		if (i91u_adpt[i].ADPT_BIOS == wBIOS) {
-			if (i91u_adpt[i].ADPT_BASE == wBASE)
+			if (i91u_adpt[i].ADPT_BASE == wBASE) {
 				if (i91u_adpt[i].ADPT_Bus != 0xFF)
 					return (FAILURE);
-				else if (i91u_adpt[i].ADPT_BASE < wBASE)
+			} else if (i91u_adpt[i].ADPT_BASE < wBASE)
 					continue;
 		}
 		for (j = MAX_SUPPORTED_ADAPTERS - 1; j > i; j--) {
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/ide-scsi.c linux/drivers/scsi/ide-scsi.c
--- v2.2.0-pre7/linux/drivers/scsi/ide-scsi.c	Tue Jul 28 14:21:08 1998
+++ linux/drivers/scsi/ide-scsi.c	Fri Jan 15 14:41:04 1999
@@ -23,6 +23,8 @@
  * Ver 0.6   Jan 27 98   Allow disabling of SCSI command translation layer
  *                        for access through /dev/sg.
  *                       Fix MODE_SENSE_6/MODE_SELECT_6/INQUIRY translation.
+ * Ver 0.7   Dev 04 98   Ignore commands where lun != 0 to avoid multiple
+ *                        detection of devices with CONFIG_SCSI_MULTI_LUN
  */
 
 #define IDESCSI_VERSION "0.6"
@@ -726,6 +728,9 @@
 
 	if (!drive) {
 		printk (KERN_ERR "ide-scsi: drive id %d not present\n", cmd->target);
+		goto abort;
+	}
+	if (cmd->lun != 0) {		/* Only respond to LUN 0. Drop others */
 		goto abort;
 	}
 	scsi = drive->driver_data;
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/mac_NCR5380.c linux/drivers/scsi/mac_NCR5380.c
--- v2.2.0-pre7/linux/drivers/scsi/mac_NCR5380.c	Mon Aug  3 12:45:46 1998
+++ linux/drivers/scsi/mac_NCR5380.c	Fri Jan 15 14:41:04 1999
@@ -1458,9 +1458,9 @@
       unsigned long timeout = jiffies + 2*NCR_TIMEOUT;
 
       while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
-	   && jiffies < timeout && !hostdata->connected)
+	   && time_before(jiffies, timeout) && !hostdata->connected)
 	;
-      if (jiffies >= timeout)
+      if (time_after_eq(jiffies, timeout))
       {
 	printk("scsi : arbitration timeout at %d\n", __LINE__);
 	NCR5380_write(MODE_REG, MR_BASE);
@@ -1615,7 +1615,7 @@
      * only wait for BSY... (Famous german words: Der Klügere gibt nach :-)
      */
 
-    while ((jiffies < timeout) && !(NCR5380_read(STATUS_REG) & 
+    while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & 
 	(SR_BSY | SR_IO)));
 
     if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == 
@@ -1628,7 +1628,7 @@
 	    return -1;
     }
 #else
-    while ((jiffies < timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY));
+    while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY));
 #endif
 
     /* 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/mac_scsi.c linux/drivers/scsi/mac_scsi.c
--- v2.2.0-pre7/linux/drivers/scsi/mac_scsi.c	Mon Aug  3 12:45:46 1998
+++ linux/drivers/scsi/mac_scsi.c	Fri Jan 15 14:41:04 1999
@@ -662,7 +662,7 @@
 	NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
 	NCR5380_read( RESET_PARITY_INTERRUPT_REG );
 
-	for( end = jiffies + AFTER_RESET_DELAY; jiffies < end; )
+	for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); )
 		barrier();
 
 	/* switch on SCSI IRQ again */
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- v2.2.0-pre7/linux/drivers/scsi/ncr53c8xx.c	Wed Dec 16 10:32:55 1998
+++ linux/drivers/scsi/ncr53c8xx.c	Sun Jan 17 18:28:06 1999
@@ -73,7 +73,7 @@
 */
 
 /*
-**	November 26 1998, version 3.1d
+**	January 16 1998, version 3.1f
 **
 **	Supported SCSI-II features:
 **	    Synchronous negotiation
@@ -1010,7 +1010,7 @@
 #endif
 
 /*
-**	If the CPU and the NCR use same endian-ness adressing,
+**	If the CPU and the NCR use same endian-ness addressing,
 **	no byte reordering is needed for script patching.
 **	Macro cpu_to_scr() is to be used for script patching.
 **	Macro scr_to_cpu() is to be used for getting a DWORD 
@@ -1049,7 +1049,7 @@
 */
 
 /*
-**	If the CPU and the NCR use same endian-ness adressing,
+**	If the CPU and the NCR use same endian-ness addressing,
 **	no byte reordering is needed for accessing chip io 
 **	registers. Functions suffixed by '_raw' are assumed 
 **	to access the chip over the PCI without doing byte 
@@ -6182,10 +6182,14 @@
 	/*
 	**	DEL 441 - 53C876 Rev 5 - Part Number 609-0392787/2788 - ITEM 2.
 	**	Disable overlapped arbitration.
+	**	The 896 Rev 1 is also affected by this errata.
 	*/
 	if (np->device_id == PCI_DEVICE_ID_NCR_53C875 &&
 	    np->revision_id >= 0x10 && np->revision_id <= 0x15)
 		OUTB (nc_ctest0, (1<<5));
+	else if (np->device_id == PCI_DEVICE_ID_NCR_53C896 &&
+	         np->revision_id <= 0x1)
+		OUTB (nc_ccntl0, DPR);
 
 	/*
 	**	Fill in target structure.
@@ -6811,7 +6815,7 @@
 **		scntl3:	(see the manual)
 **
 **	current script command:
-**		dsp:	script adress (relative to start of script).
+**		dsp:	script address (relative to start of script).
 **		dbc:	first word of script command.
 **
 **	First 16 register of the chip:
@@ -9637,7 +9641,7 @@
 	(void) pcibios_read_config_dword(bus, device_fn,
 					PCI_BASE_ADDRESS_2, &base_2);
 
-	/* Handle 64bit base adresses for 53C896. */
+	/* Handle 64bit base addresses for 53C896. */
 	if ((base & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64)
 		(void) pcibios_read_config_dword(bus, device_fn,
 						 PCI_BASE_ADDRESS_3, &base_2);
@@ -9738,7 +9742,9 @@
 			printk("succeeded.\n");
 		}
 	}
-	
+
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,140)
 	if ( is_prep ) {
 		if (io_port >= 0x10000000) {
 			printk("ncr53c8xx: reallocating io_port (Wacky IBM)");
@@ -9756,6 +9762,7 @@
 			pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_2, base_2);
 		}
 	}
+#endif
 #endif	/* __powerpc__ */
 
 #ifdef __sparc__
@@ -9786,7 +9793,8 @@
 	}
 
 	if ((chip->features & FE_CLSE) && !cache_line_size) {
-		cache_line_size = CACHE_LINE_SIZE;
+		/* PCI_CACHE_LINE_SIZE value is in 32-bit words. */
+		cache_line_size = 64 / sizeof(u_int32);
 		if (initverbose >= 2)
 			printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fixup)\n", cache_line_size);
 		pcibios_write_config_byte(bus, device_fn,
@@ -9796,7 +9804,7 @@
 	}
 
 	if (!latency_timer) {
-		latency_timer = 248;
+		latency_timer = 128;
 		if (initverbose >= 2)
 			printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fixup)\n", latency_timer);
 		pcibios_write_config_byte(bus, device_fn,
@@ -10148,8 +10156,6 @@
 
      cmd->scsi_done     = done;
      cmd->host_scribble = NULL;
-     cmd->SCp.ptr       = NULL;
-     cmd->SCp.buffer    = NULL;
 
      NCR_LOCK_NCB(np, flags);
 
@@ -10384,13 +10390,12 @@
 
 static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd)
 {
-	Scsi_Cmnd *wcmd;
+	Scsi_Cmnd **pcmd = &np->waiting_list;
 
-	if (!(wcmd = np->waiting_list)) return 0;
-	while (wcmd->next_wcmd) {
-		if (cmd == (Scsi_Cmnd *) wcmd->next_wcmd) {
+	while (*pcmd) {
+		if (cmd == *pcmd) {
 			if (to_remove) {
-				wcmd->next_wcmd = cmd->next_wcmd;
+				*pcmd = (Scsi_Cmnd *) cmd->next_wcmd;
 				cmd->next_wcmd = 0;
 			}
 #ifdef DEBUG_WAITING_LIST
@@ -10398,6 +10403,7 @@
 #endif
 			return cmd;
 		}
+		pcmd = (Scsi_Cmnd **) &(*pcmd)->next_wcmd;
 	}
 	return 0;
 }
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h
--- v2.2.0-pre7/linux/drivers/scsi/ncr53c8xx.h	Wed Dec 16 10:32:55 1998
+++ linux/drivers/scsi/ncr53c8xx.h	Mon Jan 18 17:34:32 1999
@@ -45,7 +45,7 @@
 /*
 **	Name and revision of the driver
 */
-#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 3.1d"
+#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 3.1f"
 
 /*
 **	Check supported Linux versions
@@ -346,7 +346,7 @@
 #endif
 
 #ifdef	SCSI_NCR_BIG_ENDIAN
-#error	"The NCR in BIG ENDIAN adressing mode is not (yet) supported"
+#error	"The NCR in BIG ENDIAN addressing mode is not (yet) supported"
 #endif
 
 /*
@@ -768,7 +768,20 @@
 
 /*53*/	u_char    nc_53_;
 /*54*/  u_short   nc_sodl;	/* Lowlevel: data out to scsi data  */
-/*56*/  u_short   nc_56_;
+/*56*/	u_char    nc_ccntl0;	/* Chip Control 0 (896)             */
+	#define   ENPMJ  0x80	/* Enable Phase Mismatch Jump       */
+	#define   PMJCTL 0x40	/* Phase Mismatch Jump Control      */
+	#define   ENNDJ  0x20	/* Enable Non Data PM Jump          */
+	#define   DISFC  0x10	/* Disable Auto FIFO Clear          */
+	#define   DILS   0x02	/* Disable Internal Load/Store      */
+	#define   DPR    0x01	/* Disable Pipe Req                 */
+
+/*57*/	u_char    nc_ccntl1;	/* Chip Control 1 (896)             */
+	#define   ZMOD   0x80	/* High Impedance Mode              */
+	#define   DDAC   0x08	/* Disable Dual Address Cycle       */
+	#define   XTIMOD 0x04	/* 64-bit Table Ind. Indexing Mode  */
+	#define   EXTIBMV 0x02	/* Enable 64-bit Table Ind. BMOV    */
+	#define   EXDBMV 0x01	/* Enable 64-bit Direct BMOV        */
 /*58*/  u_short   nc_sbdl;	/* Lowlevel: data from scsi data    */
 /*5a*/  u_short   nc_5a_;
 /*5c*/  u_char    nc_scr0;	/* Working register B               */
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.2.0-pre7/linux/drivers/scsi/scsi.c	Wed Jan 13 15:00:42 1999
+++ linux/drivers/scsi/scsi.c	Sun Jan 17 18:29:54 1999
@@ -1749,7 +1749,7 @@
         host_active = NULL;
         
         /* For block devices "wake_up" is done in end_scsi_request */
-        if (!SCSI_BLK_MAJOR(SCpnt->request.rq_dev)) {
+        if (!SCSI_BLK_MAJOR(MAJOR(SCpnt->request.rq_dev))) {
             struct Scsi_Host * next;
             
             for (next = host->block; next != host; next = next->block)
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/scsi_obsolete.c linux/drivers/scsi/scsi_obsolete.c
--- v2.2.0-pre7/linux/drivers/scsi/scsi_obsolete.c	Wed Jan 13 15:00:42 1999
+++ linux/drivers/scsi/scsi_obsolete.c	Sun Jan 17 18:29:54 1999
@@ -671,7 +671,7 @@
 	    host_active = NULL;
 
 	    /* For block devices "wake_up" is done in end_scsi_request */
-	    if (!SCSI_BLK_MAJOR(SCpnt->request.rq_dev)) {
+	    if (!SCSI_BLK_MAJOR(MAJOR(SCpnt->request.rq_dev))) {
 		struct Scsi_Host * next;
 
 		for (next = host->block; next != host; next = next->block)
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v2.2.0-pre7/linux/drivers/scsi/sd.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/scsi/sd.c	Sun Jan 17 18:29:54 1999
@@ -1482,12 +1482,11 @@
     if (!rscsi_disks)
         sd_template.dev_max = sd_template.dev_noticed + SD_EXTRA_DEVS;
 
-    /* 128 disks is our current limit (8 majors, 16 disks per major) */
-    if(sd_template.dev_max > 128)
-   	sd_template.dev_max = 128;
+    if(sd_template.dev_max > N_SD_MAJORS * SCSI_DISKS_PER_MAJOR )
+	sd_template.dev_max = N_SD_MAJORS * SCSI_DISKS_PER_MAJOR;
 
     if(!sd_registered) {
-	for (i=0; i <= sd_template.dev_max / SCSI_DISKS_PER_MAJOR; i++) {
+	for (i=0; i <= (sd_template.dev_max - 1) / SCSI_DISKS_PER_MAJOR; i++) {
 	    if (register_blkdev(SD_MAJOR(i),"sd",&sd_fops)) {
 		printk("Unable to get major %d for SCSI disk\n", SD_MAJOR(i));
 		return 1;
@@ -1540,8 +1539,9 @@
 	sd_gendisks[i].real_devices =
 		 (void *) (rscsi_disks + i * SCSI_DISKS_PER_MAJOR);
     }
+    
     LAST_SD_GENDISK.max_nr =
-    	sd_template.dev_max % SCSI_DISKS_PER_MAJOR;
+    	(sd_template.dev_max -1 ) % SCSI_DISKS_PER_MAJOR + 1;
     LAST_SD_GENDISK.next = NULL;
     return 0;
 }
@@ -1559,7 +1559,7 @@
     struct gendisk *gendisk;
     int i;
 
-    for (i=0; i <= sd_template.dev_max / SCSI_DISKS_PER_MAJOR; i++) {
+    for (i=0; i <= (sd_template.dev_max - 1) / SCSI_DISKS_PER_MAJOR; i++) {
         /* FIXME: After 2.2 we should implement multiple sd queues */
         blk_dev[SD_MAJOR(i)].request_fn = DEVICE_REQUEST;
         if (i) blk_dev[SD_MAJOR(i)].queue = sd_get_queue;
@@ -1765,7 +1765,7 @@
 
     scsi_unregister_module(MODULE_SCSI_DEV, &sd_template);
 
-    for (i=0; i <= sd_template.dev_max / SCSI_DISKS_PER_MAJOR; i++) 
+    for (i=0; i <= (sd_template.dev_max - 1) / SCSI_DISKS_PER_MAJOR; i++) 
 	unregister_blkdev(SD_MAJOR(i),"sd");
     
     sd_registered--;
@@ -1794,11 +1794,11 @@
 
 	if (removed != N_USED_SD_MAJORS)
 	    printk("%s %d sd_gendisks in disk chain",
-		removed > N_USED_SD_MAJORS ? "total" : "just", removed);
+	    	removed > N_USED_SD_MAJORS ? "total" : "just", removed);
 	
     }
 
-    for (i=0; i <= sd_template.dev_max / SCSI_DISKS_PER_MAJOR; i++) {
+    for (i=0; i <= (sd_template.dev_max - 1) / SCSI_DISKS_PER_MAJOR; i++) {
         blk_dev[SD_MAJOR(i)].request_fn = NULL;
         blk_size[SD_MAJOR(i)] = NULL;
         hardsect_size[SD_MAJOR(i)] = NULL;
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v2.2.0-pre7/linux/drivers/scsi/sr.c	Thu May 14 19:47:41 1998
+++ linux/drivers/scsi/sr.c	Fri Jan 15 14:41:04 1999
@@ -63,7 +63,6 @@
 static int * sr_sizes = NULL;
 
 static int * sr_blocksizes = NULL;
-static int * sr_hardsizes = NULL;              /* Hardware sector size */
 
 static int sr_open(struct cdrom_device_info*, int);
 void get_sectorsize(int);
@@ -154,8 +153,7 @@
                   */
                 scsi_CDs[MINOR(cdi->dev)].needs_sector_size = 1;
 
-                scsi_CDs[MINOR(cdi->dev)].sector_size = 
-                  sr_hardsizes[MINOR(cdi->dev)] = 2048;
+                scsi_CDs[MINOR(cdi->dev)].sector_size = 2048;
         }
 	return retval;
 }
@@ -955,7 +953,6 @@
          * Add this so that we have the ability to correctly gauge
          * what the device is capable of.
          */
-        sr_hardsizes[i] = scsi_CDs[i].sector_size;
 	scsi_CDs[i].needs_sector_size = 0;
 	sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
     };
@@ -1040,16 +1037,12 @@
 
     sr_blocksizes = (int *) scsi_init_malloc(sr_template.dev_max *
 					 sizeof(int), GFP_ATOMIC);
-    sr_hardsizes =  (int *) scsi_init_malloc(sr_template.dev_max * 
-                                         sizeof(int), GFP_ATOMIC);
 
     /*
      * These are good guesses for the time being.
      */
     for(i=0;i<sr_template.dev_max;i++) sr_blocksizes[i] = 2048;
-    for(i=0;i<sr_template.dev_max;i++) sr_hardsizes[i] = 2048;
     blksize_size[MAJOR_NR] = sr_blocksizes;
-    hardsect_size[MAJOR_NR] = sr_hardsizes;
     return 0;
 }
 
@@ -1163,9 +1156,6 @@
 
 	scsi_init_free((char *) sr_blocksizes, sr_template.dev_max * sizeof(int));
         sr_blocksizes = NULL;
-
-	scsi_init_free((char *) sr_hardsizes,  sr_template.dev_max * sizeof(int));
-        sr_hardsizes = NULL;
     }
 
     blksize_size[MAJOR_NR] = NULL;
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.2.0-pre7/linux/drivers/sound/Config.in	Thu Jan  7 15:11:37 1999
+++ linux/drivers/sound/Config.in	Thu Jan 14 22:59:47 1999
@@ -9,20 +9,18 @@
 
 # Prompt user for primary drivers.
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  dep_tristate 'Ensoniq AudioPCI (ES1370)' CONFIG_SOUND_ES1370 $CONFIG_SOUND
-  if [ "$CONFIG_SOUND_ES1370" = "y" ]; then
-     bool 'Joystick support at boot time' CONFIG_SOUND_ES1370_JOYPORT_BOOT
-  fi  
-  dep_tristate 'Creative Ensoniq AudioPCI 97 (ES1371)' CONFIG_SOUND_ES1371 $CONFIG_SOUND
-  if [ "$CONFIG_SOUND_ES1371" = "y" ]; then
-     bool 'Joystick support at boot time' CONFIG_SOUND_ES1371_JOYPORT_BOOT
-     if [ "$CONFIG_SOUND_ES1371_JOYPORT_BOOT" = "y" ]; then
-        hex 'Gameport I/O 200,208,210,218' CONFIG_SOUND_ES1371_GAMEPORT 200
-     fi
-  fi
-  dep_tristate 'S3 SonicVibes' CONFIG_SOUND_SONICVIBES $CONFIG_SOUND
+dep_tristate 'Ensoniq AudioPCI (ES1370)' CONFIG_SOUND_ES1370 $CONFIG_SOUND
+if [ "$CONFIG_SOUND_ES1370" = "y" ]; then
+   bool 'Joystick support at boot time' CONFIG_SOUND_ES1370_JOYPORT_BOOT
+fi  
+dep_tristate 'Creative Ensoniq AudioPCI 97 (ES1371)' CONFIG_SOUND_ES1371 $CONFIG_SOUND
+if [ "$CONFIG_SOUND_ES1371" = "y" ]; then
+   bool 'Joystick support at boot time' CONFIG_SOUND_ES1371_JOYPORT_BOOT
+   if [ "$CONFIG_SOUND_ES1371_JOYPORT_BOOT" = "y" ]; then
+      hex 'Gameport I/O 200,208,210,218' CONFIG_SOUND_ES1371_GAMEPORT 200
+   fi
 fi
+dep_tristate 'S3 SonicVibes' CONFIG_SOUND_SONICVIBES $CONFIG_SOUND
 
 dep_tristate 'Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_MSNDCLAS $CONFIG_SOUND
 if [ "$CONFIG_SOUND_MSNDCLAS" = "y" -o "$CONFIG_SOUND_MSNDCLAS" = "m" ]; then
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v2.2.0-pre7/linux/drivers/sound/Makefile	Thu Jan  7 15:11:37 1999
+++ linux/drivers/sound/Makefile	Thu Jan 14 22:59:47 1999
@@ -95,7 +95,8 @@
 
 gus-objs	:= gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o
 pas2-objs	:= pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o
-sb-objs		:= sb_audio.o sb_card.o sb_common.o sb_midi.o sb_mixer.o
+sb-objs		:= sb_audio.o sb_card.o sb_common.o sb_midi.o sb_mixer.o	\
+			   sb_ess.o
 softoss2-objs	:= softoss.o softoss_rs.o
 vidc_mod-objs	:= vidc.o vidc_audio.o vidc_fill.o vidc_mixer.o vidc_synth.o
 wavefront-objs  := wavfront.o wf_midi.o yss225.o
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/README.FIRST linux/drivers/sound/README.FIRST
--- v2.2.0-pre7/linux/drivers/sound/README.FIRST	Sat Sep  5 16:46:41 1998
+++ linux/drivers/sound/README.FIRST	Sun Jan 17 18:23:01 1999
@@ -4,4 +4,3 @@
 forums for bug reporting. 
 
 Alan Cox
-
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/README.blurb linux/drivers/sound/README.blurb
--- v2.2.0-pre7/linux/drivers/sound/README.blurb	Tue Dec 23 13:52:02 1997
+++ linux/drivers/sound/README.blurb	Wed Dec 31 16:00:00 1969
@@ -1,10 +0,0 @@
-*********************************************************
-* Readme.cards (this directory) contains some card	*
-* specific instructions.				*
-* See http://www.4front-tech.com/ossfree for most up    *
-* to date info.                                         *
-* (European mirror http://personal.eunet.fi/pp/voxware) *
-*							*
-* DON'T USE PROGRAMS FROM snd-util PACKAGE EARLIER THAN *
-* snd-util-3.5 WITH THIS SOUND DRIVER VERSION.          *
-*********************************************************
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/README.wavefront linux/drivers/sound/README.wavefront
--- v2.2.0-pre7/linux/drivers/sound/README.wavefront	Sun Jul 26 11:57:16 1998
+++ linux/drivers/sound/README.wavefront	Wed Dec 31 16:00:00 1969
@@ -1,375 +0,0 @@
-	     An OSS/Free Driver for WaveFront Sound Cards
-	       (Turtle Beach Maui, Tropez, Tropez Plus)
-
-		     Paul Barton-Davis, July 1998
-
-			  VERSION 0.2.2
-
-Driver Status
--------------
-
-Requires: Kernel 2.1.106 or later
-	  
-As of 7/13/1998, this driver is currently in *BETA* state. This means
-that it compiles and runs, and that I use it on my system (Linux
-2.1.106) with some reasonably demanding applications and uses.  I
-believe the code is approaching an initial "finished" state that
-provides bug-free support for the Tropez Plus.
-
-Please note that to date, the driver has ONLY been tested on a Tropez
-Plus. I would very much like to hear (and help out) people with Tropez
-and Maui cards, since I think the driver can support those cards as
-well. 
-
-Finally, the driver has not been tested as a static (non-modular) part
-of the kernel. Alan Cox's good work in modularizing OSS/Free for Linux
-makes this rather unnecessary.
-
-Some Questions
---------------
-
-**********************************************************************
-0) What does this driver do that the maui driver did not ?
-**********************************************************************
-
-* can fully initialize a WaveFront card from cold boot - no DOS 
-          utilities needed
-* working patch/sample/program loading and unloading (the maui
-      driver didn't document how to make this work, and assumed
-      user-level preparation of the patch data for writing
-      to the board. ick.)
-* full user-level access to all WaveFront commands
-* for the Tropez Plus, (primitive) control of the YSS225 FX processor
-* Virtual MIDI mode supported - 2 MIDI devices accessible via the
-          WaveFront's MPU401/UART emulation. One
-	  accesses the WaveFront synth, the other accesses the
-	  external MIDI connector. Full MIDI read/write semantics
-	  for both devices.
-* OSS-compliant /dev/sequencer interface for the WaveFront synth,
-	  including native and GUS-format patch downloading.
-* semi-intelligent patch management (prototypical at this point)
-
-
-**********************************************************************
-1) What to do about MIDI interfaces ?
-**********************************************************************
-
-The Tropez Plus (and perhaps other WF cards) can in theory support up
-to 2 physical MIDI interfaces. One of these is connected to the
-ICS2115 chip (the WaveFront synth itself) and is controlled by
-MPU/UART-401 emulation code running as part of the WaveFront OS.  The
-other is controlled by the CS4232 chip present on the board. However,
-physical access to the CS4232 connector is difficult, and it is
-unlikely (though not impossible) that you will want to use it.
-
-An older version of this driver introduced an additional kernel config
-variable which controlled whether or not the CS4232 MIDI interface was
-configured. Because of Alan Cox's work on modularizing the sound
-drivers, and now backporting them to 2.0.34 kernels, there seems to be
-little reason to support "static" configuration variables, and so this
-has been abandoned in favor of *only* module parameters. Specifying
-"mpuio" and "mpuirq" for the cs4232 parameter will result in the
-CS4232 MIDI interface being configured; leaving them unspecified will
-leave it unconfigured (and thus unusable).
-
-BTW, I have heard from one Tropez+ user that the CS4232 interface is
-more reliable than the ICS2115 one. I have had no problems with the
-latter, and I don't have the right cable to test the former one
-out. Reports welcome.
-
-**********************************************************************
-2) Why does line XXX of the code look like this .... ?
-**********************************************************************
-
-Either because its not finished yet, or because you're a better coder
-than I am, or because you don't understand some aspect of how the card
-or the code works. 
-
-I absolutely welcome comments, criticisms and suggestions about the
-design and implementation of the driver. 
-
-**********************************************************************
-3) What files are included ?
-**********************************************************************
-
-   drivers/sound/README.wavefront       -- this file
-   drivers/sound/wavefront.patch	-- patches for the 2.1.106 sound drivers
-					   needed to make the rest of this work
-   drivers/sound/wavfront.c             -- the driver
-   drivers/sound/ys225.h                -- data declarations for FX config
-   drivers/sound/ys225.c                -- data definitions for FX config
-   drivers/sound/wf_midi.c              -- the "uart401" driver 
-   				              to support virtual MIDI mode.
-   include/wavefront.h                  -- the header file
-   Documentation/sound/Tropez+          -- short docs on configuration
-
-**********************************************************************
-4) How do I compile/install/use it ?
-**********************************************************************
-
-PART ONE: install the source code into your sound driver directory
-
-  cd <top-of-your-2.1.106-code-base-e.g.-/usr/src/linux>
-  tar -zxvf <where-you-put/wavefront.tar.gz>
-
-PART TWO: apply the patches
-
-  cd drivers/sound
-  patch < wavefront.patch
-
-PART THREE: configure your kernel
-
-  cd <top of your kernel tree>
-  make xconfig (or whichever config option you use)
-
-         - choose YES for Sound Support	      
-         - choose MODULE (M) for OSS Sound Modules
-         - choose MODULE(M) to YM3812/OPL3 support
-	 - choose MODULE(M) for WaveFront support
-	 - choose MODULE(M) for CS4232 support
-
-	 - choose "N" for everything else (unless you have other
-	      sound cards you want support for)
-
-
-   make dep
-   make boot
-   .
-   .
-   .
-   <whatever you normally do for a kernel install>
-   make modules
-   .
-   .
-   .
-   make modules_install
-
-Here's my autoconf.h SOUND section:
-
-/*
- * Sound
- */
-#define CONFIG_SOUND 1
-#undef  CONFIG_SOUND_OSS
-#define CONFIG_SOUND_OSS_MODULE 1
-#undef  CONFIG_SOUND_PAS
-#undef  CONFIG_SOUND_SB
-#undef  CONFIG_SOUND_ADLIB
-#undef  CONFIG_SOUND_GUS
-#undef  CONFIG_SOUND_MPU401
-#undef  CONFIG_SOUND_PSS
-#undef  CONFIG_SOUND_MSS
-#undef  CONFIG_SOUND_SSCAPE
-#undef  CONFIG_SOUND_TRIX
-#undef  CONFIG_SOUND_MAD16
-#undef  CONFIG_SOUND_WAVEFRONT
-#define CONFIG_SOUND_WAVEFRONT_MODULE 1
-#undef  CONFIG_SOUND_CS4232
-#define CONFIG_SOUND_CS4232_MODULE 1
-#undef  CONFIG_SOUND_MAUI
-#undef  CONFIG_SOUND_SGALAXY
-#undef  CONFIG_SOUND_OPL3SA1
-#undef  CONFIG_SOUND_SOFTOSS
-#undef  CONFIG_SOUND_YM3812
-#define CONFIG_SOUND_YM3812_MODULE 1
-#undef  CONFIG_SOUND_VMIDI
-#undef  CONFIG_SOUND_UART6850
-/*
- * Additional low level sound drivers
- */
-#undef  CONFIG_LOWLEVEL_SOUND
-
-************************************************************
-6) How do I configure my card ?
-************************************************************
-
-You need to edit /etc/conf.modules. Here's mine (edited to show the
-relevant details):
-
-  # Sound system
-  alias char-major-14 wavefront
-  alias synth0 wavefront
-  alias mixer0 cs4232
-  alias audio0 cs4232
-  pre-install wavefront modprobe "-k" "cs4232"
-  post-install wavefront modprobe "-k" "opl3"
-  options wavefront io=0x200 irq=9
-  options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0
-  options opl3 io=0x388
-
-Things to note: 
-
-       the wavefront options "io" and "irq" ***MUST*** match the "synthio"
-       and "synthirq" cs4232 options.
-
-       you can do without the adlib_card module if you don't
-       want to use the OPL/[34] synth on the sound card
-
-       the adlib_card io parameter is conventionally not adjustable.
-       In theory, any not-in-use IO port address would work, but
-       just use 0x388 and stick with the crowd.
-
-**********************************************************************
-7) What about firmware ?
-**********************************************************************
-
-Turtle Beach have not given me permission to distribute their firmware
-for the ICS2115. However, if you have a WaveFront card, then you
-almost certainly have the firmware, and if not, its freely available
-on their website, at:
-
-   http://www.tbeach.com/tbs/downloads/scardsdown.htm#tropezplus 
-
-The file is called WFOS2001.MOT (for the Tropez+).
-
-This driver, however, doesn't use the pure firmware as distributed,
-but instead relies on a somewhat processed form of it. You can
-generate this very easily. Following an idea from Andrew Veliath's
-Pinnacle driver, the following flex program will generate the
-processed version:
-
----- cut here -------------------------
-%option main
-%%
-^S[28].*\r$ printf ("%c%.*s", yyleng-1,yyleng-1,yytext);
-<<EOF>> { fputc ('\0', stdout); return; }
-\n {} 
-.  {}
----- cut here -------------------------
-
-To use it, put the above in file (say, ws.l) compile it like this:
-
-      shell> flex -ows.c ws.l
-      shell> cc -o ws ws.c
-      
-and then use it like this:
-
-    ws < my-copy-of-the-oswf.mot-file > /etc/sound/wavefront.os
-
-If you put it somewhere else, you'll always have to use the wf_ospath
-module parameter (see below) or alter the source code.
-
-**********************************************************************
-7) How do I get it working ?
-**********************************************************************
-
-Optionally, you can reboot with the "new" kernel (even though the only
-changes have really been made to a module).
-
-Then, as root do:
-
-     modprobe wavefront
-
-You should get something like this in /var/log/messages:
-
-    WaveFront: firmware 1.20 already loaded.
-
-or 
-
-    WaveFront: no response to firmware probe, assume raw.
-
-then:
-
-    WaveFront: waiting for memory configuration ...
-    WaveFront: hardware version 1.64
-    WaveFront: available DRAM 8191k
-    WaveFront: 332 samples used (266 real, 13 aliases, 53 multi), 180 empty
-    WaveFront: 128 programs slots in use
-    WaveFront: 256 patch slots filled, 142 in use
-
-The whole process takes about 16 seconds, the longest waits being
-after reporting the hardware version (during the firmware download),
-and after reporting program status (during patch status inquiry).  Its
-shorter (about 10 secs) if the firmware is already loaded (i.e. only
-warm reboots since the last firmware load).
-
-The "available DRAM" line will vary depending on how much added RAM
-your card has. Mine has 8MB.
-
-Next, check /dev/sndstat, which on my machine says:
----------------------------------------------------------------------
-OSS/Free:3.8s2++-971130
-Load type: Driver loaded as a module
-Kernel: Linux bd 2.1.106 #12 SMP Fri Jul 3 00:37:34 EDT 1998 i486
-Config options: 0
-
-Installed drivers: 
-
-Card config: 
-
-Audio devices:
-0: Crystal audio controller (CS4232) (DUPLEX)
-
-Synth devices:
-0: Turtle Beach WaveFront
-1: Yamaha OPL-3
-
-Midi devices:
-0: WaveFront Internal MIDI
-1: WaveFront External MIDI
-
-Timers:
-0: System clock
-1: Crystal audio controller (CS4232)
-
-Mixers:
-0: Crystal audio controller (CS4232)
------------------------------------------------------------
-
-To check basically functionality, use play(1) or splay(1) to send a
-.WAV or other audio file through the audio portion. Then use playmidi
-to play a General MIDI file. Try the "-D 0" to hear the
-difference between sending MIDI to the WaveFront and using the OPL/3,
-which is the default (I think ...). If you have an external synth(s)
-hooked to the sound card, you can use "-e" to route to the
-external synth(s) (in theory, -D 1 should work as well, but I think
-there is a bug in playmidi which prevents this from doing what it
-should). 
-
-**********************************************************************
-8) What are the module parameters ?
-**********************************************************************
-
-Its best to read wavefront.c for this, but here is a summary:
-
-integers: 
-	  wf_raw  - if set, ignore apparent presence of firmware
-		    loaded onto the ICS2115, reset the whole
-		    board, and initialize it from scratch. (default = 0)
-
-          fx_raw  - if set, always initialize the YSS225 processor
-		    on the Tropez plus. (default = 1)
-
-          < The next 4 are basically for kernel hackers to allow
-	    tweaking the driver for testing purposes. >		    
-
-          wf_short_wait_count - loop counter used when waiting for
-			        status conditions on the board. This
-				is CPU-specific. After this many
-			        loops, the driver will sleep.
-				The default is 5000. I have a 66Mhz 486.
-
-          wf_sleep_interval  - the driver sleeps for
-			       HZ/wf_sleep_interval seconds per sleep.
-			       The default is 50.	
-
-          wf_sleep_tries  - the number of times the driver will sleep
-			    when waiting for a status condition on the
-			    board. The default is 100 (2 secs, if 
-			    wf_sleep_interval is 50).
-
-          wf_debug_default - debugging flags. See sound/wavefront.h
-			     for WF_DEBUG_* values. Default is zero.
-			     Setting this allows you to debug the
-			     driver during module installation.
-strings:
-	  wf_ospath - path to get to the pre-processed OS firmware.
-		    (default: /etc/sound/wavefront.os)
-
-**********************************************************************
-9) Who should I contact if I have problems?
-**********************************************************************
-
-Just me: Paul Barton-Davis <pbd@op.net>
-
-
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/Readme linux/drivers/sound/Readme
--- v2.2.0-pre7/linux/drivers/sound/Readme	Fri Oct 23 22:01:21 1998
+++ linux/drivers/sound/Readme	Wed Dec 31 16:00:00 1969
@@ -1,186 +0,0 @@
-OSS/Free version 3.8 release notes
-----------------------------------
-
-Most up to date information about this driver is available from 
-http://www.opensound.com/ossfree.
-
-
-
-Please read the SOUND-HOWTO (available from sunsite.unc.edu and other Linux FTP 
-sites). It gives instructions about using sound with Linux. It's bit out of
-date but still very useful. Information about bug fixes and such things
-is available from the web page (see above).
-
-Please check http://www.opensound.com/pguide for more info about programming
-with OSS API.
-
-   ====================================================
--  THIS VERSION ____REQUIRES____ Linux 2.1.57 OR LATER.
-   ====================================================
-
-Packages "snd-util-3.8.tar.gz" and "snd-data-0.1.tar.Z"
-contain useful utilities to be used with this driver.
-See http://www.opensound.com/ossfree/getting.html for
-download instructions.
-
-If you are looking for the installation instructions, please
-look at Readme.linux.
-
-Supported sound cards
----------------------
-
-See Readme.cards.
-
-Please check http://www.opensound.com/ossfree if you don't find
-your sound card there.
-
-Contributors
-------------
-
-This driver contains code by several contributors. In addition several other
-persons have given useful suggestions. The following is a list of major
-contributors. (I could have forgotten some names.)
-
-	Craig Metz	1/2 of the PAS16 Mixer and PCM support
-	Rob Hooft	Volume computation algorithm for the FM synth.
-	Mika Liljeberg	uLaw encoding and decoding routines
-	Jeff Tranter	Linux SOUND HOWTO document
-	Greg Lee	Volume computation algorithm for the GUS and
-			lot's of valuable suggestions.
-	Andy Warner	ISC port
-	Jim Lowe,
-	Amancio Hasty Jr	FreeBSD/NetBSD port
-	Anders Baekgaard 	Bug hunting and valuable suggestions.
-	Joerg Schubert	SB16 DSP support (initial version).
-	Andrew Robinson Improvements to the GUS driver
-	Megens SA	MIDI recording for SB and SB Pro (initial version).
-	Mikael Nordqvist  Linear volume support for GUS and
-			  nonblocking /dev/sequencer.
-	Ian Hartas		SVR4.2 port
-	Markus Aroharju	and
-	Risto Kankkunen		Major contributions to the mixer support
-				of GUS v3.7.
-	Hunyue Yau	Mixer support for SG NX Pro.
-	Marc Hoffman	PSS support (initial version).
-	Rainer Vranken	Initialization for Jazz16 (initial version).
-	Peter Trattler	Initial version of loadable module support for Linux.
-	JRA Gibson	16 bit mode for Jazz16 (initial version)
-	Davor Jadrijevic MAD16 support (initial version)
-	Gregor Hoffleit	Mozart support (initial version)
-	Riccardo Facchetti Audio Excel DSP 16 (aedsp16) support
-	James Hightower Spotting a tiny but important bug in CS423x support.
-	Denis Sablic	OPTi 82C924 spesific enhancements (non PnP mode)
-	Tim MacKenzie	Full duplex support for OPTi 82C930.
-	
-	Please look at lowlevel/README for more contributors.
-
-There are probably many other names missing. If you have sent me some
-patches and your name is not in the above list, please inform me.
-
-Sending your contributions or patches
--------------------------------------
-
-First of all it's highly recommended to contact me before sending anything
-or before even starting to do any work. Tell me what you suggest to be
-changed or what you have planned to do. Also ensure you are using the
-very latest (development) version of OSS/Free since the change may already be
-implemented there. In general it's major waste of time to try to improve
-several months old version. Information about the latest version can be found
-from http://www.opensound.com/ossfree. In general there is no point in
-sending me patches relative to production kernels.
-
-Sponsors etc.
--------------
-
-The following companies have greatly helped development of this driver 
-in form of a free copy of their product:
-
-Novell, Inc.		UnixWare personal edition + SDK
-The Santa Cruz Operation, Inc. 	A SCO OpenServer + SDK
-Ensoniq Corp,		a SoundScape card and extensive amount of assistance
-MediaTrix Peripherals Inc, a AudioTrix Pro card + SDK
-Acer, Inc.		a pair of AcerMagic S23 cards.
-
-In addition the following companies have provided me sufficient amount
-of technical information at least some of their products (free or $$$):
-
-Advanced Gravis Computer Technology Ltd.
-Media Vision Inc.
-Analog Devices Inc.
-Logitech Inc.
-Aztech Labs Inc.
-Crystal Semiconductor Corporation,
-Integrated Circuit Systems Inc.
-OAK Technology
-OPTi
-Turtle Beach
-miro
-Ad Lib Inc. ($$)
-Music Quest Inc. ($$)
-Creative Labs ($$$)
-
-If you have some problems
-=========================
-
-Read the sound HOWTO (sunsite.unc.edu:/pub/Linux/docs/...?).
-Also look at the home page (http://www.opensound.com/ossfree). It may
-contain info about some recent bug fixes.
-
-It's likely that you have some problems when trying to use the sound driver
-first time. Sound cards don't have standard configuration so there are no
-good default configuration to use. Please try to use same I/O, DMA and IRQ
-values for the sound card than with DOS.
-
-If you get an error message when trying to use the driver, please look
-at /var/adm/messages for more verbose error message.
-
-
-In general the easiest way to diagnose problems is to do "cat /dev/sndstat".
-
-If you get an error message, there are some problems with the driver setup:
-
-	- "No such file or directory" tells that the device files for
-	the sound driver are missing. Use the script at the end of
-	linux/drivers/sound/Readme.linux to create them.
-
-	- "No such device" tells that the sound driver is not in the kernel.
-	You have to reconfigure and recompile the kernel to have the sound
-	driver. Compiling the driver doesn't help alone. You have to boot
-	with the newly compiled one before the driver becomes active.
-	The Linux-HOWTO should help in this step.
-
-The following errors are likely with /dev/dsp and /dev/audio.
-
-	- "No such device or address". This error message should not happen
-	with /dev/sndstat but it's possible with the other sound devices.
-	This error indicates that there are no suitable hardware for the
-	device file or the sound driver has been compiled without support for
-	this particular device. For example /dev/audio and /dev/dsp will not
-	work if "digitized voice support" was not enabled during "make config".
-	
-	- "Device or resource busy". Probably the IRQ (or DMA) channel 
-	required by the sound card is in use by some other device/driver.
-
-	- "I/O error". Almost certainly (99%) it's an IRQ or DMA conflict.
-	Look at the kernel messages in /var/adm/notice for more info.
-
-	- "Invalid argument". The application is calling ioctl()
-	with impossible parameters. Check that the application is
-	for sound driver version 2.X or later.
-
-In general the printout of /dev/sndstat should tell what is the problem.
-It's possible that there are bugs in the sound driver but 99% of the problems
-reported to me are caused by somehow incorrect setup during "make config".
-
-Best regards,
-
-Hannu
-
-Hannu Savolainen
-hannu@opensound.com	
-(Please check http://www.opensound.com/ossfree before mailing me).
-
-Snail mail:	Hannu Savolainen
-		Hiekkalaiturintie 3 A 8
-		00980 Helsinki
-		Finland
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/Readme.cards linux/drivers/sound/Readme.cards
--- v2.2.0-pre7/linux/drivers/sound/Readme.cards	Thu Jul 16 18:09:26 1998
+++ linux/drivers/sound/Readme.cards	Wed Dec 31 16:00:00 1969
@@ -1,1227 +0,0 @@
-Configuring version 3.8 (for Linux) with some common sound cards
-================================================================
-
-This document describes configuring sound cards with the freeware version of
-Open Sound Systems (OSS/Free). Information about the commercial version
-(OSS/Linux) and its configuration is available from 
-http://www.opensound.com/linux.html. Information presented here is
-not valid for OSS/Linux. 
-
-If you are unsure about how to configure OSS/Free
-you can download the free evaluation version of OSS/Linux from the above
-address. There is a chance that it can autodetect your sound card. In this case
-you can use the information included in soundon.log when configuring OSS/Free.
-
-
-IMPORTANT!	This document covers only cards that were "known" when
-		this driver version was released. Please look at
-		http://www.opensound.com/ossfree for info about
-		cards introduced recently.
-
-		When configuring the sound driver, you should carefully
-		check each sound configuration option (particularly
-		"Support for /dev/dsp and /dev/audio"). The default values
-		offered by these programs are not necessarily valid.
-
-
-THE BIGGEST MISTAKES YOU CAN MAKE
-=================================
-
-1. Assuming that the card is Sound Blaster compatible when it's not.
---------------------------------------------------------------------
-
-The number one mistake is to assume that your card is compatible with
-Sound Blaster. Only the cards made by Creative Technology or which have
-one or more chips labeled by Creative are SB compatible. In addition there
-are few sound chipsets which are SB compatible in Linux such as ESS1688 or
-Jazz16. Note that SB compatibility in DOS/Windows does _NOT_ mean anything
-in Linux. 
-
-IF YOU REALLY ARE 150% SURE YOU HAVE A SOUND BLASTER YOU CAN SKIP THE REST OF
-THIS CHAPTER.
-
-For most other "supposed to be SB compatible" cards you have to use other
-than SB drivers (see below).  It is possible to get most sound cards to work
-in SB mode but in general it's a complete waste of time. There are several
-problems which you will encounter by using SB mode with cards that are not
-truly SB compatible:
-
-- The SB emulation is at most SB Pro (DSP version 3.x) which means that 
-you get only 8 bit audio (there is always an another ("native") mode which
-gives the 16 bit capability). The 8 bit only operation is the reason why
-many users claim that sound quality in Linux is much worse than in DOS.
-In addition some applications require 16 bit mode and they produce just
-noise with a 8 bit only device.
-- The card may work only in some cases but refuse to work most of the
-time. The SB compatible mode always requires special initialization which is 
-done by the DOS/Windows drivers. This kind of cards work in Linux after
-you have warm booted it after DOS but they don't work after cold boot
-(power on or reset).
-- You get the famous "DMA timed out" messages. Usually all SB clones have
-software selectable IRQ and DMA settings. If the (power on default) values
-currently used by the card don't match configuration of the driver you will
-get the above error message whenever you try to record or play. There are
-few other reasons to the DMA timeout message but using the SB mode seems
-to be the most common cause.
-
-2. Trying to use a PnP (Plug & Play) card just like an ordinary sound card
---------------------------------------------------------------------------
-
-Plug & Play is a protocol defined by Intel and Microsoft. It lets operating
-systems to easily identify and reconfigure I/O ports, IRQs and DMAs of ISA
-cards. The problem with PnP cards is that the standard Linux doesn't currently
-(versions 2.1.x and earlier) don't support PnP. This means that you will have
-to use some special tricks (see later) to get a PnP card alive. Many PnP cards
-work after they have been initialized but this is not always the case.
-
-There are sometimes both PnP and non-PnP versions of the same sound card.
-The non-PnP version is the original model which usually has been discontinued
-more than an year ago. The PnP version has the same name but with "PnP"
-appended to it (sometimes not). This causes major confusion since the non-PnP
-model works with Linux but the PnP one doesn't.
-
-You should carefully check if "Plug & Play" or "PnP" is mentioned in the name
-of the card or in the documentation or package that came with the card. 
-Everything described in the rest of this document is not necessarily valid for
-PnP models of sound cards even you have managed to wake up the card properly.
-Many PnP cards are simply too different from their non-PnP ancestors which are
-covered by this document.
-
-
-Cards that are not (fully) supported by this driver
-===================================================
-
-See http://www.opensound.com/ossfree for information about sound cards 
-to be supported in future. 
-
-
-How to use sound without recompiling kernel and/or sound driver
-===============================================================
-
-There is a commercial sound driver which comes in precompiled form and doesn't
-require recompiling of the kernel. See http://www.4Front-tech.com/oss.html for
-more info.
-
-
-Configuring PnP cards
-=====================
-
-New versions of most sound cards use the so-called ISA PnP protocol for
-soft configuring their I/O, IRQ, DMA and shared memory resources.
-Currently at least cards made by Creative Technology (SB32 and SB32AWE
-PnP), Gravis (GUS PnP and GUS PnP Pro), Ensoniq (Soundscape PnP) and
-Aztech (some Sound Galaxy models) use PnP technology. The CS4232/4236 audio
-chip by Crystal Semiconductor (Intel Atlantis, HP Pavilion and many other
-motherboards) is also based on PnP technology but there is a "native" driver
-available for it (see information about CS4232 later in this document).
-
-PnP sound cards (as well as most other PnP ISA cards) are not supported
-by this version of the driver . Proper
-support for them should be released during 97 once the kernel level
-PnP support is available.
-
-There is a method to get most of the PnP cards to work. The basic method
-is the following:
-
-1) Boot DOS so the card's DOS drivers have a chance to initialize it.
-2) _Cold_ boot to Linux by using "loadlin.exe".  Hitting ctrl-alt-del
-works with older machines but causes a hard reset of all cards on recent
-(Pentium) machines.
-3) If you have the sound driver in Linux configured properly, the card should
-work now. "Proper" means that I/O, IRQ and DMA settings are the same as in
-DOS. The hard part is to find which settings were used. See the documentation of
-your card for more info.
-
-Windows 95 could work as well as DOS but running loadlin may be difficult.
-Probably you should "shut down" your machine to MS-DOS mode before running it.
-
-Some machines have a BIOS utility for setting PnP resources. This is a good
-way to configure some cards. In this case you don't need to boot DOS/Win95
-before starting Linux.
-
-Another way to initialize PnP cards without DOS/Win95 is a Linux based
-PnP isolation tool. When writing this there is a pre alpha test version
-of such a tool available from ftp://ftp.demon.co.uk/pub/unix/linux/utils. The
-file is called isapnptools-*. Please note that this tool is just a temporary
-solution which may be incompatible with future kernel versions having proper
-support for PnP cards. There are bugs in setting DMA channels in earlier
-versions of isapnptools so at least version 1.6 is required with sound cards.
-
-Yet another way to use PnP cards is to use (commercial) OSS/Linux drivers.  See
-http://www.opensound.com/linux.html for more info. This is probably the way you
-should do it if you don't want to spend time recompiling the kernel and 
-required tools.
-
-
-Read this before trying to configure the driver
-===============================================
-
-There are currently many cards that work with this driver. Some of the cards
-have native support while others work since they emulate some other
-card (usually SB, MSS/WSS and/or MPU401). The following cards have native
-support in the driver. Detailed instructions for configuring these cards
-will be given later in this document.
-
-Pro Audio Spectrum 16 (PAS16) and compatibles:
-	Pro Audio Spectrum 16
-	Pro Audio Studio 16
-	Logitech Sound Man 16
-	NOTE! The original Pro Audio Spectrum as well as the PAS+ are not
-	      and will not be supported by the driver.
-
-Media Vision Jazz16 based cards
-	Pro Sonic 16
-	Logitech SoundMan Wave
-	(Other Jazz based cards should work but I don't have any reports
-	about them).
-
-Sound Blasters
-	SB 1.0 to 2.0
-	SB Pro
-	SB 16
-	SB32/64/AWE
-		Configure SB32/64/AWE just like SB16. See lowlevel/README.awe
-		for information about using the wave table synth.
-	        NOTE! AWE63/Gold and 16/32/AWE "PnP" cards need to be activated
-		      using isapnptools before they work with OSS/Free.
-	SB16 compatible cards by other manufacturers than Creative.
-		You have been fooled since there are _no_ SB16 compatible
-		cards on the market (as of May 1997). It's likely that your card
-		is compatible just with SB Pro but there is also a non-SB-
-		compatible 16 bit mode. Usually it's MSS/WSS but it could also
-		be a proprietary one like MV Jazz16 or ESS ES688. OPTi
-		MAD16 chips are very common in so called "SB 16 bit cards"
-		(try with the MAD16 driver).
-
-	======================================================================
-	"Supposed to be SB compatible" cards.
-		Forget the SB compatibility and check for other alternatives
-		first. The only cards that work with the SB driver in
-		Linux have been made by Creative Technology (there is at least
-		one chip on the card with "CREATIVE" printed on it). The
-		only other SB compatible chips are ESS and Jazz16 chips
-		(maybe ALSxxx chips too but they probably don't work).
-		Most other "16 bit SB compatible" cards such as "OPTi/MAD16" or
-		"Crystal" are _NOT_ SB compatible in Linux.
-
-		Practically all sound cards have some kind of SB emulation mode
-		in addition to their native (16 bit) mode. In most cases this
-		(8 bit only) SB compatible mode doesn't work with Linux. If
-		you get it working it may cause problems with games and
-		applications which require 16 bit audio. Some 16 bit only
-		applications don't check if the card actually supports 16 bits.
-		They just dump 16 bit data to a 8 bit card which produces just
-		noise.
-
-		In most cases the 16 bit native mode is supported by Linux.
-		Use the SB mode with "clones" only if you don't find anything
-		better from the rest of this doc.
-	======================================================================
-
-Gravis Ultrasound (GUS)
-	GUS
-	GUS + the 16 bit option
-	GUS MAX
-	GUS ACE (No MIDI port and audio recording)
-	GUS PnP (with RAM)
-
-MPU-401	and compatibles
-	The driver works both with the full (intelligent mode) MPU-401
-	cards (such as MPU IPC-T and MQX-32M) and with the UART only
-	dumb MIDI ports. MPU-401 is currently the most common MIDI
-	interface. Most sound cards are compatible with it. However,
-	don't enable MPU401 mode blindly. Many cards with native support
-	in the driver have their own MPU401 driver. Enabling the standard one
-	will cause a conflict with these cards. So check if your card is
-	in the list of supported cards before enabling MPU401.
-
-Windows Sound System (MSS/WSS)
-	Even when Microsoft has discontinued their own Sound System card 
-	they managed to make it a standard. MSS compatible cards are based on 
-	a codec chip which is easily available from at least two manufacturers
-	(AD1848 by Analog Devices and CS4231/CS4248 by Crystal Semiconductor).
-	Currently most sound cards are based on one of the MSS compatible codec
-	chips. The CS4231 is used in the high quality cards such as GUS MAX,
-	MediaTrix AudioTrix Pro and TB Tropez (GUS MAX is not MSS compatible). 
-
-	Having a AD1848, CS4248 or CS4231 codec chip on the card is a good
-	sign. Even if the card is not MSS compatible, it could be easy to write
-	support for it. Note also that most MSS compatible cards
-	require special boot time initialization which may not be present
-	in the driver. Also, some MSS compatible cards have native support.
-	Enabling the MSS support with these cards is likely to
-	cause a conflict. So check if your card is listed in this file before
-	enabling the MSS support.
-
-Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4)
-	Most sound cards have a FM synthesizer chip. The OPL2 is a 2
-	operator chip used in the original AdLib card. Currently it's used
-	only in the cheapest (8 bit mono) cards. The OPL3 is a 4 operator 
-	FM chip which provides better sound quality and/or more available 
-	voices than the OPL2. The OPL4 is a new chip that has an OPL3 and
-	a wave table synthesizer packed onto the same chip. The driver supports
-	just the OPL3 mode directly. Most cards with an OPL4 (like
-	SM Wave and AudioTrix Pro) support the OPL4 mode using MPU401
-	emulation. Writing a native OPL4 support is difficult
-	since Yamaha doesn't give information about their sample ROM chip.
-
-	Enable the generic OPL2/OPL3 FM synthesizer support if your
-	card has a FM chip made by Yamaha. Don't enable it if your card
-	has a software (TRS) based FM emulator.
-
-	----------------------------------------------------------------
-	NOTE! OPL3-SA is different chip than the ordinary OPL3. In addition
-	to the FM synth this chip has also digital audio (WSS) and
-	MIDI (MPU401) capabilities. Support for OPL3-SA is described below.
-	----------------------------------------------------------------
-
-Yamaha OPL3-SA1
-
-	Yamaha OPL3-SA1 (YMF701) is an audio controller chip used on some
-	(Intel) motherboards and on cheap sound cards. It should not be
-	confused with the original OPL3 chip (YMF278) which is entirely
-        different chip. OPL3-SA1 has support for MSS, MPU401 and SB Pro
-	(not used in OSS/Free) in addition to the OPL3 FM synth.
-
-	There are also chips called OPL3-SA2, OPL3-SA3, ..., OPL3SA-N. They
-	are PnP chips and will not work with the OPL3-SA1 driver. You should 
-	use the standard MSS, MPU401 and OPL3 options with thses chips and to
-	activate the card using isapnptools.
-
-4Front Technologies SoftOSS
-
-	SoftOSS is a software based wave table emulation which works with
-	any 16 bit stereo sound card. Due to its nature a fast CPU is
-	required (P133 is minimum). Although SoftOSS does _not_ use MMX
-	instructions it has proven out that recent processors (which appear
-	to have MMX) perform significantly better with SoftOSS than earlier
-	ones. For example a P166MMX beats a PPro200. SoftOSS should not be used
-	on 486 or 386 machines.
-
-	The amount of CPU load caused by SoftOSS can be controlled by
-	selecting the CONFIG_SOFTOSS_RATE and CONFIG_SOFTOSS_VOICES
-	parameters properly (they will be prompted by make config). It's
-	recommended to set CONFIG_SOFTOSS_VOICES to 32. If you have a
-	P166MMX or faster (PPro200 is not faster) you can set
-	CONFIG_SOFTOSS_RATE to 44100 (kHz). However with slower systems it
-	recommended to use sampling rates around 22050 or even 16000 kHz.
-	Selecting too high values for these parameters may hang your
-	system when playing MIDI files with hight degree of polyphony
-	(number of concurrently playing notes). It's also possible to
-	decrease CONFIG_SOFTOSS_VOICES. This makes it possible to use
-	higher sampling rates. However using fewer voices decreases
-	playback quality more than decreasing the sampling rate.
-
-	SoftOSS keeps the samples loaded on the system's RAM so much RAM is
-	required. SoftOSS should never be used on machines with less than 16 MB
-	of RAM since this is potentially dangerous (you may accidently run out
-	of memory which probably crashes the machine). 
-
-	SoftOSS implements the wave table API originally designed for GUS. For
-	this reason all applications designed for GUS should work (at least
-	after minor modifications). For example gmod/xgmod and playmidi -g are
-	known to work.
-
-	To work SoftOSS will require GUS compatible
-	patch files to be installed on the system (in /dos/ultrasnd/midi). You
-	can use the public domain MIDIA patchset available from several ftp
-	sites.
-
-        *********************************************************************
-	IMPORTANT NOTICE! The original patch set distributed with the Gravis 
-	Ultrasound card is not in public domain (even though it's available from
-	some FTP sites). You should contact Voice Crystal (www.voicecrystal.com)
-	if you like to use these patches with SoftOSS included in OSS/Free.
-        *********************************************************************
-
-PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC)
-	Analog Devices and Echo Speech have together defined a sound card
-	architecture based on the above chips. The DSP chip is used
-	for emulation of SB Pro, FM and General MIDI/MT32.
-
-	There are several cards based on this architecture. The most known
-	ones are Orchid SW32 and Cardinal DSP16. 
-
-	The driver supports downloading DSP algorithms to these cards.
-
-	NOTE! You will have to use the "old" config script when configuring
-	PSS cards.
-
-MediaTrix AudioTrix Pro
-	The ATP card is built around a CS4231 codec and an OPL4 synthesizer
-	chips. The OPL4 mode is supported by a microcontroller running a
-	General MIDI emulator. There is also a SB 1.5 compatible playback mode.
-
-Ensoniq SoundScape and compatibles
-	Ensoniq has designed a sound card architecture based on the
-	OTTO synthesizer chip used in their professional MIDI synthesizers.
-	Several companies (including Ensoniq, Reveal and Spea) are selling
-	cards based on this architecture.
-
-	NOTE! The SoundScape PnP is not supported by OSS/Free. Ensoniq VIVO and
-	VIVO90 cards are not compatible with Soundscapes so the Soundscape
-        driver will not work with them. You may want to use OSS/Linux with these
-        cards.
-
-OPTi MAD16 and Mozart based cards
-	The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929),
-	OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface
-	chips are used in many different sound cards, including some
-	cards by Reveal miro and Turtle Beach (Tropez). The purpose of these
-	chips is to connect other audio components to the PC bus. The
-	interface chip performs address decoding for the other chips.
-	NOTE! Tropez Plus is not MAD16 but CS4232 based.
-	NOTE! MAD16 PnP cards (82C924, 82C925, 82C931) are not MAD16 compatible
-	in the PnP mode. You will have to use them in MSS mode after having
-	initialized them using isapnptools or DOS. 82C931 probably requires
-	initialization using DOS/Windows (running isapnptools is not enough).
-	It's possible to use 82C931 with OSS/Free by jumpering it to non-PnP
-	mode (provided that the card has a jumper for this). In non-PnP mode
-	82C931 is compatible with 82C930 and should work with the MAD16 driver
-	(without need to use isapnptools or DOS to initialize it). All OPTi
-	chips are supported by OSS/Linux (both in PnP and non-PnP modes).
-
-Audio Excel DSP16 
-	Support for this card was written by Riccardo Faccetti
-	(riccardo@cdc8g5.cdc.polimi.it). The AEDSP16 driver included in
-	the lowlevel/ directory. To use it you should enable the
-	"Additional low level drivers" option.
-
-Crystal CS4232 and CS4236 based cards such as AcerMagic S23, TB Tropez _Plus_ and 
-	many PC motherboards (Compaq, HP, Intel, ...)
-	CS4232 is a PnP multimedia chip which contains a CS3231A codec,
-	SB and MPU401 emulations. There is support for OPL3 too.
-	Unfortunately the MPU401 mode doesn't work (I don't know how to
-	initialize it). CS4236 is an enhanced (compatible) version of CS4232.
-	NOTE! Don't ever try to use isapnptools with CS4232 since this will just
-	freeze your machine (due to chip bugs). If you have problems in getting
-	CS4232 working you could try initializing it with DOS (CS4232C.EXE) and
-	then booting Linux using loadlin. CS4232C.EXE loads a secret firmware
-	patch which is not documented by Crystal.
-
-Turtle Beach Maui and Tropez "classic"
-	This driver version supports sample, patch and program loading commands
-	described in the Maui/Tropez User's manual. 
-	There is now full initialization support too. The audio side of
-	the Tropez is based on the MAD16 chip (see above).
-	NOTE! Tropez Plus is different card than Tropez "classic" and will not
-	work fully in Linux. You can get audio features working by configuring
-	the card as a CS4232 based card (above).
-
-
-Jumpers and software configuration
-==================================
-
-Some of the earliest sound cards were jumper configurable. You have to
-configure the driver use I/O, IRQ and DMA settings
-that match the jumpers. Just few 8 bit cards are fully jumper 
-configurable (SB 1.x/2.x, SB Pro and clones).
-Some cards made by Aztech have an EEPROM which contains the 
-config info. These cards behave much like hardware jumpered cards.
-
-Most cards have jumper for the base I/O address but other parameters
-are software configurable. Sometimes there are few other jumpers too.
-
-Latest cards are fully software configurable or they are PnP ISA
-compatible. There are no jumpers on the board.
-
-The driver handles software configurable cards automatically. Just configure
-the driver to use I/O, IRQ and DMA settings which are known to work.
-You could usually use the same values than with DOS and/or Windows.
-Using different settings is possible but not recommended since it may cause
-some trouble (for example when warm booting from an OS to another or
-when installing new hardware to the machine).
-
-Sound driver sets the soft configurable parameters of the card automatically
-during boot. Usually you don't need to run any extra initialization
-programs when booting Linux but there are some exceptions. See the
-card-specific instructions below for more info.
-
-The drawback of software configuration is that the driver needs to know
-how the card must be initialized. It cannot initialize unknown cards
-even if they are otherwise compatible with some other cards (like SB,
-MPU401 or Windows Sound System).
-
-
-What if your card was not listed above?
-=======================================
-
-The first thing to do is to look at the major IC chips on the card.
-Many of the latest sound cards are based on some standard chips. If you
-are lucky, all of them could be supported by the driver. The most common ones
-are the OPTi MAD16, Mozart, SoundScape (Ensoniq) and the PSS architectures
-listed above. Also look at the end of this file for list of unsupported
-cards and the ones which could be supported later.
-
-The last resort is to send _exact_ name and model information of the card
-to me together with a list of the major IC chips (manufactured, model) to 
-me. I could then try to check if your card looks like something familiar.
-
-There are many more cards in the world than listed above. The first thing to
-do with these cards is to check if they emulate some other card or interface
-such as SB, MSS and/or MPU401. In this case there is a chance to get the
-card to work by booting DOS before starting Linux (boot DOS, hit ctrl-alt-del
-and boot Linux without hard resetting the machine). In this method the
-DOS based driver initializes the hardware to use known I/O, IRQ and DMA
-settings. If sound driver is configured to use the same settings, everything
-should work OK.
-
-
-Configuring sound driver (with Linux)
-=====================================
-
-The sound driver is currently distributed as part of the Linux kernel. The 
-files are in /usr/src/linux/drivers/sound/. 
-
-****************************************************************************
-*	ALWAYS USE THE SOUND DRIVER VERSION WHICH IS DISTRIBUTED WITH	   *
-*	THE KERNEL SOURCE PACKAGE YOU ARE USING. SOME ALPHA AND BETA TEST  *
-*	VERSIONS CAN BE INSTALLED FROM A SEPARATELY DISTRIBUTED PACKAGE	   *
-*	BUT CHECK THAT THE PACKAGE IS NOT MUCH OLDER (OR NEWER) THAN THE   *
-*	KERNEL YOU ARE USING. IT'S POSSIBLE THAT THE KERNEL/DRIVER 	   *
-*	INTERFACE CHANGES BETWEEN KERNEL RELEASES WHICH MAY CAUSE SOME	   *
-*	INCOMPATIBILITY PROBLEMS.					   *
-*									   *
-*	IN CASE YOU INSTALL A SEPARATELY DISTRIBUTED SOUND DRIVER VERSION, *
-*	BE SURE TO REMOVE OR RENAME THE OLD SOUND DRIVER DIRECTORY BEFORE  *
-*	INSTALLING THE NEW ONE. LEAVING OLD FILES TO THE SOUND DRIVER	   *
-*	DIRECTORY _WILL_ CAUSE PROBLEMS WHEN THE DRIVER IS USED OR	   *
-*	COMPILED.							   *
-****************************************************************************
-
-To configure the driver, run "make config" in the kernel source directory
-(/usr/src/linux). Answer "y" or "m" to the question about Sound card support
-(after the questions about mouse, CD-ROM, ftape, etc. support).  Questions
-about options for sound will then be asked.
-
-After configuring the kernel and sound driver, run "make dep" and compile
-the kernel following instructions in the kernel README.
-
-The sound driver configuration dialog
--------------------------------------
-
-If you already have the sound driver installed, consult a printout of
-"cat /dev/sndstat" when configuring the driver again. It gives the I/O,
-IRQ and DMA settings you used earlier.
-
-Sound configuration starts by making some yes/no questions. Be careful
-when answering to these questions since answering y to a question may
-prevent some later ones from being asked. For example don't answer y to
-the first question (PAS16) if you don't really have a PAS16. Don't enable
-more cards than you really need since they just consume memory. Also
-some drivers (like MPU401) may conflict with your SCSI controller and
-prevent kernel from booting. If you card was in the list of supported
-cards (above), please look at the card specific config instructions
-(later in this file) before starting to configure. Some cards must be
-configured in way which is not obvious.
-
-So here is the beginning of the config dialog. Answer 'y' or 'n' to these
-questions. The default answer is shown so that (y/n) means 'y' by default and
-(n/y) means 'n'. To use the default value, just hit ENTER. But be careful
-since using the default _doesn't_ guarantee anything.
-
-Note also that all questions may not be asked. The configuration program
-may disable some questions depending on the earlier choices. It may also
-select some options automatically as well.
-
-  "ProAudioSpectrum 16 support",
-	- Answer 'y'_ONLY_ if you have a Pro Audio Spectrum _16_,
-	  Pro Audio Studio 16 or Logitech SoundMan 16 (be sure that
-	  you read the above list correctly). Don't answer 'y' if you
-	  have some other card made by Media Vision or Logitech since they
-	  are not PAS16 compatible.
-	  NOTE! Since 3.5-beta10 you need to enable SB support (next question)
-	  if you want to use the SB emulation of PAS16. It's also possible to
-   	  the emulation if you want to use a true SB card together with PAS16
-	  (there is another question about this that is asked later).
-  "Sound Blaster support",
-	- Answer 'y' if you have an original SB card made by Creative Labs
-	  or a full 100% hardware compatible clone (like Thunderboard or
-	  SM Games). If your card was in the list of supported cards (above),
-	  please look at the card specific instructions later in this file
-	  before answering this question. For an unknown card you may answer 
-	  'y' if the card claims to be SB compatible.
-	 Enable this option also with PAS16 (changed since v3.5-beta9).
-
-	 Don't enable SB if you have a MAD16 or Mozart compatible card.
-
-  "Generic OPL2/OPL3 FM synthesizer support",
-	- Answer 'y' if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4).
-	  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. However I don't currently know
-	  such cards.
-  "Gravis Ultrasound support",
-	- Answer 'y' if you have GUS or GUS MAX. Answer 'n' if you don't
-	  have GUS since the GUS driver consumes much memory.
-	  Currently I don't have experiences with the GUS ACE so I don't
-	  know what to answer with it.
-  "MPU-401 support (NOT for SB16)",
-	- Be careful with this question. The MPU401 interface is supported
-	  by almost any sound card today. However some natively supported cards
-	  have their own driver for MPU401. Enabling the 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 was in the list of supported cards (above), please look at
-	  the card specific instructions later in this file.
-
-	  In MOST cases this MPU401 driver should only be used with "true"
-	  MIDI-only MPU401 professional cards. In most other cases there
-	  is another way to get the MPU401 compatible interface of a
-	  sound card to work.
-	  Support for the MPU401 compatible MIDI port of SB16, ESS1688
-	  and MV Jazz16 cards is included in the SB driver. Use it instead
-	  of this separate MPU401 driver with these cards. As well 
-	  Soundscape, PSS and Maui drivers include their own MPU401
-	  options.
-
-	  It's safe to answer 'y' if you have a true MPU401 MIDI interface
-	  card. 
-  "6850 UART Midi support",
-	- It's safe to answer 'n' to this question in all cases. The 6850
-	  UART interface is so rarely used.
-  "PSS (ECHO-ADI2111) support",
-	- Answer 'y' only if you have Orchid SW32, Cardinal DSP16 or some
-	  other card based on the PSS chipset (AD1848 codec + ADSP-2115
-	  DSP chip + Echo ESC614 ASIC CHIP).
-  "16 bit sampling option of GUS (_NOT_ GUS MAX)",
-	- Answer 'y' if you have installed the 16 bit sampling daughtercard
-	  to your GUS. Answer 'n' if you have GUS MAX. Enabling this option
-	  disables GUS MAX support.
-  "GUS MAX support",
-	- Answer 'y' only if you have a GUS MAX.
-  "Microsoft Sound System support",
-	- Again think carefully before answering 'y' to this question. It's
-	  safe to answer 'y' in case you have the original Windows Sound 
-	  System card made by Microsoft or Aztech SG 16 Pro (or NX16 Pro).
-	  Also you may answer 'y' in case your card was not listed earlier
-	  in this file. For cards having native support in the driver, consult
-	  the card specific instructions later in this file. Some drivers
-	  have their own MSS support and enabling this option will cause a
-	  conflict. 
-	  Note! The MSS driver permits configuring two DMA channels. This is a
-	  "nonstandard" feature and works only with very few cards (if any).
-	  In most cases the second DMA channel should be disabled or set to
-	  the same channel than the first one. Trying to configure two separate
-	  channels with cards that don't support this feature will prevent
-	  audio (at least recording) from working.
-  "Ensoniq Soundscape support",
-	- 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 (note that Reveal makes other cards also).  The oldest
-	  cards made by Spea don't work properly with Linux. 
-	  Soundscape PnP as well as Ensoniq VIVO work only with the commercial
-	  OSS/Linux version.
-  "MediaTrix AudioTrix Pro support",
-	- Answer 'y' if you have the AudioTrix Pro.
-  "Support for MAD16 and/or Mozart based cards",
-	- Answer y if your card has a Mozart (OAK OTI-601) or MAD16
-	  (OPTi 82C928, 82C929, 82C924/82C925 or 82C930) audio interface chip. 
-	  These chips are
-	  currently quite common so it's possible that many no-name cards
-	  have one of them. In addition the MAD16 chip is used in some
-	  cards made by known manufacturers such as Turtle Beach (Tropez),
-	  Reveal (some models) and Diamond (some recent models).
-	  Note OPTi 82C924 and 82C925 are MAD16 compatible only in non PnP
-	  mode (jumper selectable on many cards).
-  "Support for TB Maui"
-	- This enables TB Maui specific initialization. Works with TB Maui
-	and TB Tropez (may not work with Tropez Plus).
-
-
-Then the configuration program asks some y/n questions about the higher
-level services. It's recommended to answer 'y' to each of these questions.
-Answer 'n' only if you know you will not need the option.
-
-  "MIDI interface support",
-	- Answering 'n' disables /dev/midi## devices and access to any
-	  MIDI ports using /dev/sequencer and /dev/music. This option
-	  also affects any MPU401 and/or General MIDI compatible devices.
-  "FM synthesizer (YM3812/OPL-3) support",
-	- Answer 'y' here.
-  "/dev/sequencer support",
-	- Answering 'n' disables /dev/sequencer and /dev/music.
-
-Entering the I/O, IRQ and DMA config parameters
------------------------------------------------
-
-After the above questions the configuration program prompts for the
-card specific configuration information. Usually just a set of
-I/O address, IRQ and DMA numbers are asked. With some cards the program
-asks for some files to be used during initialization of the card. For example
-many cards have a DSP chip or microprocessor which must be initialized by
-downloading a program (microcode) file to the card.
-
-Instructions for answering these questions are given in the next section.
-
-
-Card specific information
-=========================
-
-This section gives additional instructions about configuring some cards.
-Please refer manual of your card for valid I/O, IRQ and DMA numbers. Using
-the same settings with DOS/Windows and Linux is recommended. Using
-different values could cause some problems when switching between
-different operating systems.
-
-Sound Blasters (the original ones by Creative)
----------------------------------------------
-
-NOTE! Check if you have a PnP Sound Blaster (cards sold after summer 1995
-      are almost certainly PnP ones). With PnP cards you should use isapnptools
-      to activate them (see above).
-
-It's possible to configure these cards to use different I/O, IRQ and
-DMA settings. Since the possible/default settings have changed between various
-models, you have to consult manual of your card for the proper ones. It's
-a good idea to use the same values than with DOS/Windows. With SB and SB Pro
-it's the only choice. SB16 has software selectable IRQ and DMA channels but
-using different values with DOS and Linux is likely to cause troubles. The
-DOS driver is not able to reset the card properly after warm boot from Linux
-if Linux has used different IRQ or DMA values.
-
-The original (steam) Sound Blaster (versions 1.x and 2.x) use always
-DMA1. There is no way to change it.
-
-The SB16 needs two DMA channels. A 8 bit one (1 or 3) is required for
-8 bit operation and a 16 bit one (5, 6 or 7) for the 16 bit mode. In theory
-it's possible to use just one (8 bit) DMA channel by answering the 8 bit
-one when the configuration program asks for the 16 bit one. This may work
-in some systems but is likely to cause terrible noise on some other systems.
-
-It's possible to use two SB16/32/64 at the same time. To do this you should
-first configure OSS/Free for one card. Then edit local.h manually and define
-SB2_BASE, SB2_IRQ, SB2_DMA and SB2_DMA2 for the second one. You can't get
-the OPL3, MIDI and EMU8000 devices of the second card to work. If you are
-going to use two PnP Sound Blasters, ensure that they are of different model
-and have different PnP IDs. There is no way to get two cards with the same
-card ID and serial number to work. The easiest way to check this is trying 
-if isapnptools can see both cards or just one.
-
-NOTE!	Don't enable the SM Games option (asked by the configuration program)
-	if you are not 101% sure that your card is a Logitech Soundman Games
-	(not a SM Wave or SM16).
-
-SB Clones
----------
-
-First of all: There are no SB16 clones. There are SB Pro clones with a
-16 bit mode which is not SB16 compatible. The most likely alternative is that
-the 16 bit mode means MSS/WSS.
-
-There are just a few fully 100% hardware SB or SB Pro compatible cards.
-I know just Thunderboard and SM Games. Other cards require some kind of
-hardware initialization before they become SB compatible. Check if your card
-was listed in the beginning of this file. In this case you should follow
-instructions for your card later in this file.
-
-For other not fully SB clones you may try initialization using DOS in
-the following way:
-
-	- Boot DOS so that the card specific driver gets run.
-	- Hit ctrl-alt-del (or use loadlin) to boot Linux. Don't
-	  switch off power or press the reset button.
-	- If you use the same I/O, IRQ and DMA settings in Linux, the
-	  card should work.
-
-If your card is both SB and MSS compatible, I recommend using the MSS mode.
-Most cards of this kind are not able to work in the SB and the MSS mode 
-simultaneously. Using the MSS mode provides 16 bit recording and playback.
-
-ProAudioSpectrum 16 and compatibles
------------------------------------
-
-PAS16 has a SB emulation chip which can be used together with the native
-(16 bit) mode of the card. To enable this emulation you should configure 
-the driver to have SB support too (this has been changed since version
-3.5-beta9 of this driver).
-
-With current driver versions it's also possible to use PAS16 together with 
-another SB compatible card. In this case you should configure SB support
-for the other card and to disable the SB emulation of PAS16 (there is a
-separate questions about this).
-
-With PAS16 you can use two audio device files at the same time. /dev/dsp (and
-/dev/audio) is connected to the 8/16 bit native codec and the /dev/dsp1 (and
-/dev/audio1) is connected to the SB emulation (8 bit mono only).
-
-Gravis Ultrasound
------------------
-
-There are many different revisions of the Ultrasound card (GUS). The
-earliest ones (pre 3.7) don't have a hardware mixer. With these cards
-the driver uses a software emulation for synth and pcm playbacks. It's
-also possible to switch some of the inputs (line in, mic) off by setting
-mixer volume of the channel level below 10%. For recording you have
-to select the channel as a recording source and to use volume above 10%.
-
-GUS 3.7 has a hardware mixer.
-
-GUS MAX and the 16 bit sampling daughtercard have a CS4231 codec chip which 
-also contains a mixer.
-
-Configuring GUS is simple. Just enable the GUS support and GUS MAX or
-the 16 bit daughtercard if you have them. Note that enabling the daughter
-card disables GUS MAX driver.
-
-NOTE for owners of the 16 bit daughtercard: By default the daughtercard
-uses /dev/dsp (and /dev/audio). Command "ln -sf /dev/dsp1 /dev/dsp"
-selects the daughter card as the default device.
-
-With just the standard GUS enabled the configuration program prompts
-for the I/O, IRQ and DMA numbers for the card. Use the same values than
-with DOS.
-
-With the daughter card option enabled you will be prompted for the I/O,
-IRQ and DMA numbers for the daughter card. You have to use different I/O
-and DMA values than for the standard GUS. The daughter card permits
-simultaneous recording and playback. Use /dev/dsp (the daughtercard) for
-recording and /dev/dsp1 (GUS GF1) for playback.
-
-GUS MAX uses the same I/O address and IRQ settings than the original GUS
-(GUS MAX = GUS + a CS4231 codec). In addition an extra DMA channel may be used.
-Using two DMA channels permits simultaneous playback using two devices
-(dev/dsp0 and /dev/dsp1). The second DMA channel is required for
-full duplex audio.
-To enable the second DMA channels, give a valid DMA channel when the config
-program asks for the GUS MAX DMA (entering -1 disables the second DMA).
-Using 16 bit DMA channels (5,6 or 7) is recommended.
-
-If you have problems in recording with GUS MAX, you could try to use
-just one 8 bit DMA channel. Recording will not work with one DMA
-channel if it's a 16 bit one.
-
-Microphone input of GUS MAX is connected to mixer in little bit nonstandard
-way. There is actually two microphone volume controls. Normal "mic" controls
-only recording level. Mixer control "speaker" is used to control volume of
-microphone signal connected directly to line/speaker out. So just decrease
-volume of "speaker" if you have problems with microphone feedback.
-
-GUS ACE works too but any attempt to record or to use the MIDI port
-will fail.
-
-GUS PnP (with RAM) is partially supported but it needs to be initialized using
-DOS or isapnptools before starting the driver.
-
-MPU401 and Windows Sound System
--------------------------------
-
-Again. Don't enable these options in case your card is listed
-somewhere else in this file.
-
-Configuring these cards is obvious (or it should be). With MSS
-you should probably enable the OPL3 synth also since
-most MSS compatible cards have it. However check that this is true
-before enabling OPL3.
-
-Sound driver supports more than one MPU401 compatible cards at the same time
-but the config program asks config info for just the first of them.
-Adding the second or third MPU interfaces must be done manually by
-editing sound/local.h (after running the config program). Add defines for
-MPU2_BASE & MPU2_IRQ (and MPU3_BASE & MPU3_IRQ) to the file.
-
-CAUTION!
-
-The default I/O base of Adaptec AHA-1542 SCSI controller is 0x330 which
-is also the default of the MPU401 driver. Don't configure the sound driver to
-use 0x330 as the MPU401 base if you have a AHA1542. The kernel will not boot
-if you make this mistake.
-
-PSS
----
-
-Even the PSS cards are compatible with SB, MSS and MPU401, you must not
-enable these options when configuring the driver. The configuration
-program handles these options itself. (You may use the SB, MPU and MSS options
-together with PSS if you have another card on the system).
-
-The PSS driver enables MSS and MPU401 modes of the card. SB is not enabled 
-since it doesn't work concurrently with MSS. The driver loads also a
-DSP algorithm which is used to for the general MIDI emulation. The
-algorithm file (.ld) is read by the config program and written to a
-file included when the pss.c is compiled. For this reason the config
-program asks if you want to download the file. Use the genmidi.ld file
-distributed with the DOS/Windows drivers of the card (don't use the mt32.ld).
-With some cards the file is called 'synth.ld'. You must have access to
-the file when configuring the driver. The easiest way is to mount the DOS
-partition containing the file with Linux. 
-
-It's possible to load your own DSP algorithms and run them with the card.
-Look at the directory pss_test of snd-util-3.0.tar.gz for more info.
-
-AudioTrix Pro
--------------
-
-You have to enable the OPL3 and SB (not SB Pro or SB16) drivers in addition
-to the native AudioTrix driver. Don't enable MSS or MPU drivers.
-
-Configuring ATP is little bit tricky since it uses so many I/O, IRQ and
-DMA numbers. Using the same values than with DOS/Win is a good idea. Don't
-attempt to use the same IRQ or DMA channels twice.
-
-The SB mode of ATP is implemented so the ATP driver just enables SB
-in the proper address. The SB driver handles the rest. You have to configure
-both the SB driver and the SB mode of ATP to use the same IRQ, DMA and I/O
-settings.
-
-Also the ATP has a microcontroller for the General MIDI emulation (OPL4).
-For this reason the driver asks for the name of a file containing the
-microcode (TRXPRO.HEX). This file is usually located in the directory
-where the DOS drivers were installed. You must have access to this file
-when configuring the driver. 
-
-If you have the effects daughtercard, it must be initialized by running
-the setfx program of snd-util-3.0.tar.gz package. This step is not required
-when using the (future) binary distribution version of the driver.
-
-Ensoniq SoundScape
-------------------
-
-NOTE!	The new PnP SoundScape is not supported yet. Soundscape compatible
-	cards made by Reveal don't work with Linux. They use older revision
-	of the Soundscape chipset which is not fully compatible with
-	newer cards made by Ensoniq.
-
-The SoundScape driver handles initialization of MSS and MPU supports
-itself so you don't need to enable other drivers than SoundScape
-(enable also the /dev/dsp, /dev/sequencer and MIDI supports).
-
-!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!!!!!                                                                   !!!!
-!!!!! NOTE! Before version 3.5-beta6 there WERE two sets of audio 	!!!!
-!!!!!       device files (/dev/dsp0 and /dev/dsp1). The first one WAS   !!!!
-!!!!!	    used only for card initialization and the second for audio  !!!!
-!!!!!	    purposes. It WAS required to change /dev/dsp (a symlink) to !!!!
-!!!!!	    point to /dev/dsp1.						!!!!
-!!!!!                                                                   !!!!
-!!!!!	    This is not required with OSS versions 3.5-beta6 and later	!!!!
-!!!!!	    since there is now just one audio device file. Please 	!!!!
-!!!!!	    change /dev/dsp to point back to /dev/dsp0 if you are	!!!!
-!!!!!	    upgrading from an earlier driver version using		!!!!
-!!!!!	    (cd /dev;rm dsp;ln -s dsp0 dsp).				!!!!
-!!!!!                                                                   !!!!
-!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-The configuration program asks one DMA channel and two interrupts. One IRQ
-and one DMA is used by the MSS codec. The second IRQ is required for the
-MPU401 mode (you have to use different IRQs for both purposes).
-There were earlier two DMA channels for SoundScape but the current driver
-version requires just one.
-
-The SoundScape card has a Motorola microcontroller which must initialized
-_after_ boot (the driver doesn't initialize it during boot).
-The initialization is done by running the 'ssinit' program which is
-distributed in the snd-util-3.0.tar.gz package. You have to edit two
-defines in the ssinit.c and then compile the program. You may run ssinit 
-manually (after each boot) or add it to /etc/rc.d/rc.local.
-
-The ssinit program needs the microcode file that comes with the DOS/Windows
-driver of the card. You will need to use version 1.30.00 or later
-of the microcode file (sndscape.co0 or sndscape.co1 depending on
-your card model). THE OLD sndscape.cod WILL NOT WORK. IT WILL HANG YOUR
-MACHINE. The only way to get the new microcode file is to download
-and install the DOS/Windows driver from ftp://ftp.ensoniq.com/pub.
-
-Then you have to select the proper microcode file to use: soundscape.co0
-is the right one for most cards and sndscape.co1 is for few (older) cards
-made by Reveal and/or Spea. The driver has capability to detect the card
-version during boot. Look at the boot log messages in /var/adm/messages
-and locate the sound driver initialization message for the SoundScape
-card. If the driver displays string <Ensoniq Soundscape (old)>, you have
-an old card and you will need to use sndscape.co1. For other cards use
-soundscape.co0. New Soundscape revisions such as Elite and PnP use
-code files with higher numbers (.co2, .co3, etc.).
-
-NOTE!	Ensoniq Soundscape VIVO is not compatible with other Soundscape cards.
-	Currently it's possible to use it in Linux only with OSS/Linux
-	drivers.
-
-Check /var/adm/messages after running ssinit. The driver prints
-the board version after downloading the microcode file. That version
-number must match the number in the name of the microcode file (extension).
-
-Running ssinit with a wrong version of the sndscape.co? file is not
-dangerous as long as you don't try to use a file called sndscape.cod.
-If you have initialized the card using a wrong microcode file (sounds
-are terrible), just modify ssinit.c to use another microcode file and try
-again. It's possible to use an earlier version of sndscape.co[01] but it
-may sound weird.
-
-MAD16 (Pro) and Mozart
-----------------------
-
-You need to enable just the MAD16 /Mozart support when configuring
-the driver. _Don't_ enable SB, MPU401 or MSS. However you will need the
-/dev/audio, /dev/sequencer and MIDI supports.
-
-Mozart and OPTi 82C928 (the original MAD16) chips don't support
-MPU401 mode so enter just 0 when the configuration program asks the
-MPU/MIDI I/O base. The MAD16 Pro (OPTi 82C929) and 82C930 chips have MPU401
-mode.
-
-TB Tropez is based on the 82C929 chip. It has two MIDI ports.
-The one connected to the MAD16 chip is the second one (there is a second
-MIDI connector/pins somewhere??). If you have not connected the second MIDI
-port, just disable the MIDI port of MAD16. The 'Maui' compatible synth of
-Tropez is jumper configurable and not connected to the MAD16 chip (the
-Maui driver can be used with it).
-
-Some MAD16 based cards may cause feedback, whistle or terrible noise if the
-line3 mixer channel is turned too high. This happens at least with Shuttle
-Sound System. Current driver versions set volume of line3 low enough so
-this should not be a problem.
-
-If you have a MAD16 card which have an OPL4 (FM + Wave table) synthesizer
-chip (_not_ an OPL3), you have to append a line containing #define MAD16_OPL4
-to the file linux/drivers/sound/local.h (after running make config).
-
-MAD16 cards having a CS4231 codec support full duplex mode. This mode
-can be enabled by configuring the card to use two DMA channels. Possible
-DMA channel pairs are: 0&1, 1&0 and 3&0.
-
-NOTE! Cards having an OPTi 82C924/82C925 chip work with OSS/Free only in
-non-PnP mode (usually jumper selectable). The PnP mode is supported only
-by OSS/Linux.
-
-MV Jazz (ProSonic)
-------------------
-
-The Jazz16 driver is just a hack made to the SB Pro driver. However it works
-fairly well. You have to enable SB, SB Pro (_not_ SB16) and MPU401 supports
-when configuring the driver. The configuration program asks later if you
-want support for MV Jazz16 based cards (after asking SB base address). Answer
-'y' here and the driver asks the second (16 bit) DMA channel.
-
-The Jazz16 driver uses the MPU401 driver in a way which will cause
-problems if you have another MPU401 compatible card. In this case you must
-give address of the Jazz16 based MPU401 interface when the config
-program prompts for the MPU401 information. Then look at the MPU401
-specific section for instructions about configuring more than one MPU401 cards.
-
-Logitech Soundman Wave
-----------------------
-
-Read the above MV Jazz specific instructions first.
-
-The Logitech SoundMan Wave (don't confuse this with the SM16 or SM Games) is
-a MV Jazz based card which has an additional OPL4 based wave table
-synthesizer. The OPL4 chip is handled by an on board microcontroller
-which must be initialized during boot. The config program asks if
-you have a SM Wave immediately after asking the second DMA channel of jazz16.
-If you answer 'y', the config program will ask name of the file containing
-code to be loaded to the microcontroller. The file is usually called
-MIDI0001.BIN and it's located in the DOS/Windows driver directory. The file
-may also be called as TSUNAMI.BIN or something else (older cards?).
-
-The OPL4 synth will be inaccessible without loading the microcontroller code.
-
-Also remember to enable SB MPU401 support if you want to use the OPL4 mode.
-(Don't enable the 'normal' MPU401 device as with some earlier driver
-versions (pre 3.5-alpha8)).
-
-NOTE!	Don't answer 'y' when the driver asks about SM Games support
-	(the next question after the MIDI0001.BIN name). However
-	answering 'y' doesn't cause damage your computer so don't panic. 
-
-Sound Galaxies
---------------
-
-There are many different Sound Galaxy cards made by Aztech. The 8 bit
-ones are fully SB or SB Pro compatible and there should be no problems
-with them. 
-
-The older 16 bit cards (SG Pro16, SG NX Pro16, Nova and Lyra) have
-an EEPROM chip for storing the configuration data. There is a microcontroller
-which initializes the card to match the EEPROM settings when the machine
-is powered on. These cards actually behave just like they have jumpers
-for all of the settings. Configure driver for MSS, MPU, SB/SB Pro  and OPL3 
-supports with these cards. 
-
-There are some new Sound Galaxies in the market. I have no experience with
-them so read the card's manual carefully.
-
-ESS ES1688 and ES688 'AudioDrive' based cards
----------------------------------------------
-
-Support for these two ESS chips is embedded in the SB driver.
-Configure these cards just like SB. Enable the 'SB MPU401 MIDI port'
-if you want to use MIDI features of ES1688. ES688 doesn't have MPU mode
-so you don't need to enable it (the driver uses normal SB MIDI automatically
-with ES688).
-
-NOTE! ESS cards are not compatible with MSS/WSS so don't worry if MSS support
-of OSS doesn't work with it.
-
-There are some ES1688/688 based sound cards and (particularily) motherboards
-which use software configurable I/O port relocation feature of the chip.
-This ESS proprietary feature is supported only by OSS/Linux.
-
-There are ES1688 based cards which use different interrupt pin assignment than
-recommended by ESS (5, 7, 9/2 and 10). In this case all IRQ's don't work.
-At least a card called (Pearl?) Hypersound 16 supports IRQ15 but it doesn't
-work.
-
-ES1868 is a PnP chip which is (supposed to be) compatible with ESS1688
-brobably works with OSS/Free after initialization using isapnptools.
-
-Reveal cards
-------------
-
-There are several different cards made/marketed by Reveal. Some of them
-are compatible with SoundScape and some use the MAD16 chip. You may have
-to look at the card and try to identify its origin.
-
-Diamond
--------
-
-The oldest (Sierra Aria based) sound cards made by Diamond are not supported
-(they may work if the card is initialized using DOS). The recent (LX?)
-models are based on the MAD16 chip which is supported by the driver.
-
-Audio Excel DSP16
------------------
-
-Support for this card is currently not functional. A new driver for it
-should be available later this year.
-
-PCMCIA cards
-------------
-
-Sorry, can't help. Some cards may work and some don't.
-
-TI TM4000M notebooks
---------------------
-
-These computers have a built in sound support based on the Jazz chipset.
-Look at the instructions for MV Jazz (above). It's also important to note
-that there is something wrong with the mouse port and sound at least on
-some TM models. Don't enable the "C&T 82C710 mouse port support" when
-configuring Linux. Having it enabled is likely to cause mysterious problems
-and kernel failures when sound is used.
-
-miroSOUND
----------
-
-The miroSOUND PCM12 has been used successfully. This card is based on
-the MAD16, OPL4, and CS4231A chips and everything said in the section
-about MAD16 cards applies here, too. The only major difference between
-the PCM12 and other MAD16 cards is that instead of the mixer in the
-CS4231 codec a separate mixer controlled by an on-board 80C32
-microcontroller is used. Control of the mixer takes place via the ACI
-(miro's audio control interface) protocol that is implemented in a
-separate lowlevel driver. Make sure you compile this ACI driver
-together with the normal MAD16 support when you use a miroSOUND PCM12
-card. The ACI mixer is controlled by /dev/mixer and the CS4231 mixer
-by /dev/mixer2. You usually don't want to change anything on the
-CS4231 mixer.
-
-The miroSOUND PCM12 is capable of full duplex operation (simultaneous
-PCM replay and recording), which allows you to implement nice
-real-time signal processing audio effect software and network
-telephones. The ACI mixer has to be configured into a special "solo"
-mode for duplex operation in order to avoid feedback caused by the
-mixer (input hears output signal). See lowlevel/aci.c for details on
-the ioctl() for activating the "solo" mode.
-
-The following configuration parameters have worked fine for the PCM12
-in Markus Kuhn's system, many other configurations might work, too:
-CONFIG_MAD16_BASE=0x530, CONFIG_MAD16_IRQ=11, CONFIG_MAD16_DMA=3, 
-CONFIG_MAD16_DMA2=0, CONFIG_MAD16_MPU_BASE=0x330, CONFIG_MAD16_MPU_IRQ=10,
-DSP_BUFFSIZE=65536, SELECTED_SOUND_OPTIONS=0x00281000.
-
-The miroSOUND PCM1 pro and the PCM20 are very similar to the PCM12.
-Perhaps the same ACI driver also works for these cards, however this
-has never actually been tested. The PCM20 contains a radio tuner,
-which is also controlled by ACI. This radio tuner is currently not
-supported by the ACI driver, but documentation for it was provided by
-miro and ACI tuner support could easily be added if someone is really
-interested.
-
-Compaq Deskpro XL
------------------
-
-The builtin sound hardware of Compaq Deskpro XL is now supported. 
-You need to configure the driver with MSS and OPL3 supports enabled.
-In addition you need to manually edit linux/drivers/sound/local.h and
-to add a line containing "#define DESKPROXL" if you used 
-make menuconfig/xconfig.
-
-Others?
--------
-
-Since there are so many different sound cards, it's likely that I have 
-forgotten to mention many of them. Please inform me if you know yet another
-card which works with Linux, please inform me (or is anybody else
-willing to maintain a database of supported cards (just like in XF86)?).
-
-Cards not supported yet
-=======================
-
-Please check the version of sound driver you are using before 
-complaining that your card is not supported. It's possible you are 
-using a driver version which was released months before your card was
-introduced.  The driver's release date is listed after its version number in a
-"cat /dev/sndstat" printout and in the file linux/drivers/sound/soundvers.h.
-
-First of all, there is an easy way to make most sound cards work with Linux.
-Just use the DOS based driver to initialize the card to a known state, then use
-loadlin.exe to boot Linux. If Linux is configured to use the same I/O, IRQ and
-DMA numbers as DOS, the card could work.
-(ctrl-alt-del can be used in place of loadlin.exe but it doesn't work with
-new motherboards). This method works also with all/most PnP sound cards.
-
-Don't get fooled with SB compatibility. Most cards are compatible with
-SB but that may require a TSR which is not possible with Linux. If
-the card is compatible with MSS, it's a better choice. Some cards
-don't work in the SB and MSS modes at the same time.
-
-Then there are cards which are no longer manufactured and/or which
-are relatively rarely used (such as the 8 bit ProAudioSpectrum
-models). It's extremely unlikely that such cards ever get supported.
-Adding support for a new card requires much work and increases time
-required in maintaining the driver (some changes need to be done
-to all low level drivers and be tested too, maybe with multiple
-operating systems). For this reason I have made a decision to not support
-obsolete cards. It's possible that someone else makes a separately 
-distributed driver (diffs) for the card. 
-
-Writing a driver for a new card is not possible if there are no 
-programming information available about the card. If you don't
-find your new card from this file, look from the home page 
-(http://www.opensound.com/ossfree). Then please contact
-manufacturer of the card and ask if they have (or are willing to)
-released technical details of the card. Do this before contacting me. I
-can only answer 'no' if there are no programming information available.
-
-I have made decision to not accept code based on reverse engineering
-to the driver. There are three main reasons: First I don't want to break
-relationships to sound card manufacturers. The second reason is that
-maintaining and supporting a driver without any specs will be a pain.
-The third reason is that companies have freedom to refuse selling their
-products to other than Windows users.
-
-Some companies don't give low level technical information about their
-products to public or at least their require signing a NDA. It's not
-possible to implement a freeware driver for them. However it's possible
-that support for such cards become available in the commercial version
-of this driver (see http://www.4Front-tech.com/oss.html for more info).
-
-There are some common audio chipsets that are not supported yet. For example
-Sierra Aria and IBM Mwave. It's possible that these architectures
-get some support in future but I can't make any promises. Just look
-at the home page (http://www.opensound.com/ossfree/new_cards.html)
-for latest info.
-
-Information about unsupported sound cards and chipsets is welcome as well
-as free copies of sound cards, SDKs and operating systems.
-
-If you have any corrections and/or comments, please contact me.
-
-Hannu Savolainen
-hannu@opensound.com
-
-Personal home page:	   http://www.compusonic.fi/~hannu
-home page of OSS/Free: http://www.opensound.com/ossfree
-
-home page of commercial OSS
-(Open Sound System) drivers: http://www.opensound.com/oss.html
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/Readme.linux linux/drivers/sound/Readme.linux
--- v2.2.0-pre7/linux/drivers/sound/Readme.linux	Sun Jun  7 11:16:35 1998
+++ linux/drivers/sound/Readme.linux	Wed Dec 31 16:00:00 1969
@@ -1,85 +0,0 @@
-Installation
-------------
-
-IMPORTANT!	Read this if you are installing a separately
-		distributed version of this driver.
-
-		Check that your kernel version works with this
-		release of the driver (see Readme). Also verify
-		that your current kernel version doesn't have more
-		recent sound driver version than this one. IT'S HIGHLY
-		RECOMMENDED THAT YOU USE THE SOUND DRIVER VERSION THAT
-		IS DISTRIBUTED WITH KERNEL SOURCES.
-
-- When installing separately distributed sound driver you should first
-  read the above notice. Then try to find proper directory where and how
-  to install the driver sources. You should not try to install a separately
-  distributed driver version if you are not able to find the proper way
-  yourself (in this case use the version that is distributed with kernel
-  sources). Remove old version of linux/drivers/sound directory before
-  installing new files.
-
-- To build the device files you need to run the enclosed shell script 
-  (see below). You need to do this only when installing sound driver
-  first time or when upgrading to much recent version than the earlier
-  one.
-
-- Configure and compile Linux as normally (remember to include the
-  sound support during "make config"). Please refer to kernel documentation
-  for instructions about configuring and compiling kernel. File Readme.cards
-  contains card specific instructions for configuring this driver for
-  use with various sound cards.
-
-Boot time configuration (using lilo and insmod) 
------------------------------------------------
-
-This information has been removed. Too many users didn't believe
-that it's really not necessary to use this method. Please look at
-Readme of sound driver version 3.0.1 if you still want to use this method.
-
-Problems
---------
-
-If you have any kind of problems, there is a debugging feature which
-could help you to solve the problem. To use it, just execute the
-command:
-
-	cat /dev/sndstat 
-
-and look at the output. It should display some useful info about the
-driver configuration. If there is no /dev/sndstat 
-(/dev/sndstat: No such file or directory), ensure that you have executed the
-soundinstall script (at the end of this file).
-
-Common error messages:
-
-- /dev/???????: No such file or directory.
-Run the script at the end of this file.
-
-- /dev/???????: No such device.
-You are not running kernel which contains the sound driver. When using
-modularized sound driver this error means that the sound driver is not
-loaded.
-
-- /dev/????: No such device or address.
-Sound driver didn't detect suitable card when initializing. Please look at
-Readme.cards for info about configuring the driver with your card. Also
-check for possible boot (insmod) time error messages in /var/adm/messages.
-
-- Other messages or problems
-Please check http://www.opensound.com/ossfree for more info.
-
-Hannu Savolainen
-hannu@opensound.com
-
------------------ cut here ------------------------------
-SURPRISE SURPRISE!!!
-
-The device file creation script that used to be here earlier is 
-obviously not here any more.
-
-Why?
-
-Because you do not need it. All Linux distributions have the
-device files properly created (yes they are) so you should not
-try to run any scripts which create them.
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/Readme.modules linux/drivers/sound/Readme.modules
--- v2.2.0-pre7/linux/drivers/sound/Readme.modules	Thu Nov 12 16:21:21 1998
+++ linux/drivers/sound/Readme.modules	Wed Dec 31 16:00:00 1969
@@ -1,99 +0,0 @@
-Building a modular sound driver
-================================
-
-  The following information is current as of linux-2.1.85. Check the other
-readme files, especially Readme.cards, for information not specific to
-making sound modular.
-
-  First, configure your kernel. This is an idea of what you should be
-setting in the sound section:
-
-<M> Sound card support 
-
-<M> 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support 
-
-  I have SoundBlaster. Select your card from the list.
-
-<M> Generic OPL2/OPL3 FM synthesizer support 
-<M> FM synthesizer (YM3812/OPL-3) support 
-
-  If you don't set these, you will probably find you can play .wav files
-but not .midi. As the help for them says, set them unless you know your
-card does not use one of these chips for FM support.
-
-  Once you are configured, make zlilo, modules, modules_install; reboot.
-Note that it is no longer necessary or possible to configure sound in the
-drivers/sound dir. Now one simply configures and makes one's kernel and
-modules in the usual way.
-
- Then, add to your /etc/modules.conf or /etc/conf.modules something like:
-
-alias char-major-14 sb
-post-install sb /sbin/modprobe "-k" "adlib_card"
-options sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330
-options adlib_card io=0x388     # FM synthesizer
-
-  The effect of this is that the sound driver and all necessary bits and
-pieces autoload on demand, assuming you use kerneld (a sound choice) and
-autoclean when not in use. Also, options for the device drivers are
-set. They will not work without them. Change as appropriate for your card.
-If you are not yet using the very cool kerneld, you will have to "modprobe
--k sb" yourself to get things going. Eventually things may be fixed so
-that this kludgery is not necessary; for the time being, it seems to work
-well.
-
-  Replace 'sb' with the driver for your card, and give it the right
-options. To find the filename of the driver, look in
-/lib/modules/<kernel-version>/misc. Mine looks like:
-
-adlib_card.o # This is the generic OPLx driver
-opl3.o # The OPL3 driver
-sb.o # <<The SoundBlaster driver. Yours may differ.>>
-sound.o # The sound driver
-uart401.o # Used by sb, maybe other cards
-
- Whichever card you have, try feeding it the options that would be the
-default if you were making the driver wired, not as modules. You can look
-at the init_module() code for the card to see what args are expected.
-
- Note that at present there is no way to configure the io, irq and other
-parameters for the modular drivers as one does for the wired drivers.. One
-needs to pass the modules the necessary parameters as arguments, either
-with /etc/modules.conf or with command-line args to modprobe, e.g.
-
-modprobe -k sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330
-modprobe -k adlib_card io=0x388
-
- recommend using /etc/modules.conf.
-
-Persistent DMA Buffers:
-
-The sound modules normally allocate DMA buffers during open() and
-deallocate them during close(). Linux can often have problems allocating
-DMA buffers for ISA cards on machines with more than 16MB RAM. This is
-because ISA DMA buffers must exist below the 16MB boundry and it is quite
-possible that we can't find a large enough free block in this region after
-the machine has been running for any amount of time. The way to avoid this
-problem is to allocate the DMA buffers during module load and deallocate
-them when the module is unloaded. For this to be effective we need to load
-the sound modules right after the kernel boots, either manually or by an
-init script, and keep them around until we shut down. This is a little
-wasteful of RAM, but it guarantees that sound always works.
-
-To make the sound driver use persistent DMA buffers we need to pass the
-sound.o module a "dmabuf=1" command-line argument. This is normally done
-in /etc/conf.modules (or the more proper /etc/modules.conf) like so:
-
-options sound		dmabuf=1
-
-If you have 16MB or less RAM or a PCI sound card, this is wasteful and
-unnecessary. It is possible that machine with 16MB or less RAM will find
-this option useful, but if your machine is so memory-starved that it
-cannot find a 64K block free, you will be wasting even more RAM by keeping
-the sound modules loaded and the DMA buffers allocated when they are not
-needed. The proper solution is to upgrade your RAM. But you do also have
-this improper solution as well. Use it wisely.
-
-  I'm afraid I know nothing about anything but my setup, being more of a
-text-mode guy anyway. If you have options for other cards or other helpful
-hints, send them to me, Jim Bray, jb@as220.org, http://as220.org/jb.
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v2.2.0-pre7/linux/drivers/sound/ad1848.c	Fri Jan  8 22:36:10 1999
+++ linux/drivers/sound/ad1848.c	Sun Jan 17 18:23:01 1999
@@ -2298,7 +2298,8 @@
 		hw_config->card_subtype = 1;
 		return 1;
 	}
-	if ((hw_config->irq != 7)  &&
+	if ((hw_config->irq != 5)  &&
+	    (hw_config->irq != 7)  &&
 	    (hw_config->irq != 9)  &&
 	    (hw_config->irq != 10) &&
 	    (hw_config->irq != 11))
@@ -2332,7 +2333,7 @@
 {
 	static char     interrupt_bits[12] =
 	{
-		-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20
+		-1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20
 	};
 	char            bits, dma2_bit = 0;
 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/lowlevel/README linux/drivers/sound/lowlevel/README
--- v2.2.0-pre7/linux/drivers/sound/lowlevel/README	Wed Jan 13 15:00:42 1999
+++ linux/drivers/sound/lowlevel/README	Sun Jan 17 18:25:47 1999
@@ -1,12 +1,11 @@
 Additional low level sound drivers for Linux
 ============================================
 
-This directory contains additional low level sound drivers which
-are not part of OSS/Lite (Open Sound System). These drivers are
-maintained by their authors (not by Hannu Savolainen).
-
-If you like to write a new low level sound driver, please contact
-Hannu Savolainen (hannu@voxware.pp.fi) for more info.
+This directory contains some low level sound drivers.
+These drivers are used to be (when Linux sound drivers was OSS/Lite) external
+drivers, not maintained by Hannu Savolainen and not touched by him.
+Now things are changed: the new Linux sound driver code maintained by Alan Cox
+include these lowlevel drivers and they are no more neglected (thanks Alan).
 
 The following low level drivers are included:
 
@@ -15,4 +14,9 @@
 - Audio Excel DSP 16 initialization driver by Riccardo Facchetti
   (fizban@tin.it)
 - SB32/AWE synthesizer driver (Emu8000) by Takashi Iwai
-  (iwai@dragon.mm.t.u-tokyo.ac.jp). See README.awe for more info.
+  (iwai@dragon.mm.t.u-tokyo.ac.jp).
+
+You can find documentation for these drivers in the Documentation/sound
+directory.
+
+[ File edited 17.01.1999 - Riccardo Facchetti ]
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/pas2_card.c linux/drivers/sound/pas2_card.c
--- v2.2.0-pre7/linux/drivers/sound/pas2_card.c	Thu Jan  7 15:11:38 1999
+++ linux/drivers/sound/pas2_card.c	Thu Jan 14 22:59:47 1999
@@ -163,6 +163,7 @@
 	if (pas_irq < 0 || pas_irq > 15)
 	{
 		printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq);
+		hw_config->irq=-1;
 		ok = 0;
 	}
 	else
@@ -173,18 +174,23 @@
 		if (!irq_bits[pas_irq])
 		{
 			printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq);
+			hw_config->irq=-1;
 			ok = 0;
 		}
 		else
 		{
-			if (request_irq(pas_irq, pasintr, 0, "PAS16",NULL) < 0)
+			if (request_irq(pas_irq, pasintr, 0, "PAS16",hw_config) < 0) {
+				printk(KERN_ERR "PAS16: Cannot allocate IRQ %d\n",pas_irq);
+				hw_config->irq=-1;
 				ok = 0;
+			}
 		}
 	}
 
 	if (hw_config->dma < 0 || hw_config->dma > 7)
 	{
 		printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma);
+		hw_config->dma=-1;
 		ok = 0;
 	}
 	else
@@ -193,6 +199,7 @@
 		if (!dma_bits[hw_config->dma])
 		{
 			printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma);
+			hw_config->dma=-1;
 			ok = 0;
 		}
 		else
@@ -200,6 +207,7 @@
 			if (sound_alloc_dma(hw_config->dma, "PAS16"))
 			{
 				printk(KERN_ERR "pas2_card.c: Can't allocate DMA channel\n");
+				hw_config->dma=-1;
 				ok = 0;
 			}
 		}
@@ -370,14 +378,16 @@
 	extern int pas_audiodev;
 	extern int pas2_mididev;
 
-	sound_free_dma(hw_config->dma);
-	free_irq(hw_config->irq, NULL);
+	if (hw_config->dma>0)
+		sound_free_dma(hw_config->dma);
+	if (hw_config->irq>0)
+		free_irq(hw_config->irq, hw_config);
 
 	if(pas_audiodev!=-1)
 		sound_unload_mixerdev(audio_devs[pas_audiodev]->mixer_dev);
 	if(pas2_mididev!=-1)
 	        sound_unload_mididev(pas2_mididev);
-	if(pas_audiodev)
+	if(pas_audiodev!=-1)
 		sound_unload_audiodev(pas_audiodev);
 }
 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/pas2_midi.c linux/drivers/sound/pas2_midi.c
--- v2.2.0-pre7/linux/drivers/sound/pas2_midi.c	Thu Jan  7 15:11:38 1999
+++ linux/drivers/sound/pas2_midi.c	Thu Jan 14 22:59:47 1999
@@ -21,7 +21,7 @@
 static int      midi_busy = 0, input_opened = 0;
 static int      my_dev;
 
-int pas2_mididev;
+int pas2_mididev=-1;
 
 static unsigned char tmp_queue[256];
 static volatile int qlen;
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/pas2_pcm.c linux/drivers/sound/pas2_pcm.c
--- v2.2.0-pre7/linux/drivers/sound/pas2_pcm.c	Thu Jul 16 18:09:27 1998
+++ linux/drivers/sound/pas2_pcm.c	Thu Jan 14 22:59:47 1999
@@ -42,7 +42,7 @@
 static unsigned long pcm_count = 0;
 static unsigned short pcm_bitsok = 8;	/* mask of OK bits */
 static int      pcm_busy = 0;
-int             pas_audiodev = 0;
+int             pas_audiodev = -1;
 static int      open_mode = 0;
 
 static int pcm_set_speed(int arg)
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/sb.h linux/drivers/sound/sb.h
--- v2.2.0-pre7/linux/drivers/sound/sb.h	Fri Jan  8 22:36:11 1999
+++ linux/drivers/sound/sb.h	Thu Jan 14 22:59:47 1999
@@ -52,9 +52,12 @@
 #define SUBMDL_ES1868	0x11	/* Subtype ES1868 for specific handling */
 #define SUBMDL_ES1869	0x12	/* Subtype ES1869 for specific handling */
 #define SUBMDL_ES1878	0x13	/* Subtype ES1878 for specific handling */
-#define SUBMDL_ES188X	0x14	/* Subtype ES1887 for specific handling */
+#define SUBMDL_ES1887	0x14	/* Subtype ES1887 for specific handling */
+#define SUBMDL_ES1888	0x14	/* Subtype ES1888 for specific handling */
 #define SUBMDL_ALS007	42	/* ALS-007 differs from SB16 only in mixer */
 				/* register assignment */
+#define SUBMDL_ALS100	43	/* ALS-100 allows sampling rates of up */
+				/* to 48kHz */
 /*
  * Config flags
  */
@@ -123,7 +126,6 @@
 	   int      trg_restart_16;
 
 	   unsigned char tconst;
-	   int my_dev;
 	
 	/* MIDI fields */
 	   int my_mididev;
@@ -134,6 +136,7 @@
 	} sb_devc;
 
 int sb_dsp_command (sb_devc *devc, unsigned char val);
+int sb_dsp_get_byte(sb_devc * devc);
 int sb_dsp_reset (sb_devc *devc);
 void sb_setmixer (sb_devc *devc, unsigned int port, unsigned int value);
 unsigned int sb_getmixer (sb_devc *devc, unsigned int port);
@@ -146,9 +149,11 @@
 void sb_dsp_midi_init (sb_devc *devc);
 void sb_audio_init (sb_devc *devc, char *name);
 void sb_midi_interrupt (sb_devc *devc);
-int ess_write (sb_devc *devc, unsigned char reg, unsigned char data);
-int ess_read (sb_devc *devc, unsigned char reg);
-void ess_mixer_reload (sb_devc * devc, int dev);
+void sb_chgmixer (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val);
+int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right);
+
+int sb_audio_open(int dev, int mode);
+void sb_audio_close(int dev);
 
 extern int acer;
 extern sb_devc *last_sb;
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/sb_audio.c linux/drivers/sound/sb_audio.c
--- v2.2.0-pre7/linux/drivers/sound/sb_audio.c	Fri Jan  8 22:36:11 1999
+++ linux/drivers/sound/sb_audio.c	Thu Jan 14 22:59:47 1999
@@ -29,7 +29,9 @@
 #include "sb_mixer.h"
 #include "sb.h"
 
-static int sb_audio_open(int dev, int mode)
+#include "sb_ess.h"
+
+int sb_audio_open(int dev, int mode)
 {
 	sb_devc *devc = audio_devs[dev]->devc;
 	unsigned long flags;
@@ -93,7 +95,7 @@
 	return 0;
 }
 
-static void sb_audio_close(int dev)
+void sb_audio_close(int dev)
 {
 	sb_devc *devc = audio_devs[dev]->devc;
 
@@ -600,250 +602,21 @@
 }
 
 /*
- * ESS specific routines
- */
-
-static int ess_audio_set_speed(int dev, int speed)
-{
-	sb_devc *devc = audio_devs[dev]->devc;
-	int divider;
-
-	if (speed > 0)
-	{
-		if (speed < 5000)
-			speed = 5000;
-		if (speed > 48000)
-			speed = 48000;
-
-		if (speed > 22000)
-		{
-			divider = (795500 + speed / 2) / speed;
-			speed = (795500 + divider / 2) / divider;
-		}
-		else
-		{
-			divider = (397700 + speed / 2) / speed;
-			speed = (397700 + divider / 2) / divider;
-		}
-		devc->speed = speed;
-	}
-	return devc->speed;
-}
-
-static void ess_speed(sb_devc * devc)
-{
-	int divider;
-	unsigned char bits = 0;
-	int speed = devc->speed;
-
-	if (speed < 4000)
-		speed = 4000;
-	else if (speed > 48000)
-		speed = 48000;
-	
-	if (speed > 22000)
-	{
-		bits = 0x80;
-		divider = 256 - (795500 + speed / 2) / speed;
-	}
-	else
-	{
-		divider = 128 - (397700 + speed / 2) / speed;
-	}
-
-	bits |= (unsigned char) divider;
-	ess_write(devc, 0xa1, bits);
-
-	/*
-	 * Set filter divider register
-	 */
-
-	speed = (speed * 9) / 20;	/* Set filter roll-off to 90% of speed/2 */
-	divider = 256 - 7160000 / (speed * 82);
-	ess_write(devc, 0xa2, divider);
-	return;
-}
-
-static void ess_change
-	(sb_devc *devc, unsigned int reg, unsigned int mask, unsigned int val)
-{
-	int value;
-
-	value = ess_read(devc, reg);
-	value = (value & ~mask) | (val & mask);
-	ess_write(devc, reg, value);
-}
-
-struct ess_command {int cmd; int data;};
-
-static void ess_exec_commands
-	(sb_devc *devc, struct ess_command *cmdtab[])
-{
-	struct ess_command *cmd;
-
-	cmd = cmdtab [ ((devc->channels != 1) << 1) + (devc->bits != AFMT_U8) ];
-
-	while (cmd->cmd != -1) {
-		ess_write (devc, cmd->cmd, cmd->data);
-		cmd++;
-	}
-}
-
-static struct ess_command ess_i08m[] =		/* input 8 bit mono */
-	{ {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} };
-static struct ess_command ess_i16m[] =		/* input 16 bit mono */
-	{ {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} };
-static struct ess_command ess_i08s[] =		/* input 8 bit stereo */
-	{ {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} };
-static struct ess_command ess_i16s[] =		/* input 16 bit stereo */
-	{ {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} };
-
-static struct ess_command *ess_inp_cmds[] =
-	{ ess_i08m, ess_i16m, ess_i08s, ess_i16s };
-
-static int ess_audio_prepare_for_input(int dev, int bsize, int bcount)
-{
-	sb_devc *devc = audio_devs[dev]->devc;
-	ess_speed(devc);
-	sb_dsp_command(devc, DSP_CMD_SPKOFF);
-
-	ess_write(devc, 0xb8, 0x0e);	/* Auto init DMA mode */
-	ess_change (devc, 0xa8, 0x03, 3 - devc->channels);	/* Mono/stereo */
-	ess_write(devc, 0xb9, 2);	/* Demand mode (4 bytes/DMA request) */
-
-	ess_exec_commands (devc, ess_inp_cmds);
-
-	ess_change (devc, 0xb1, 0xf0, 0x50);
-	ess_change (devc, 0xb2, 0xf0, 0x50);
-
-	devc->trigger_bits = 0;
-	return 0;
-}
-
-static struct ess_command ess_o08m[] =          /* output 8 bit mono */
-        { {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} };
-static struct ess_command ess_o16m[] =          /* output 16 bit mono */
-        { {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} };
-static struct ess_command ess_o08s[] =          /* output 8 bit stereo */
-        { {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} };
-static struct ess_command ess_o16s[] =          /* output 16 bit stereo */
-        { {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} };
-
-
-static struct ess_command *ess_out_cmds[] =
-        { ess_o08m, ess_o16m, ess_o08s, ess_o16s };
-
-static int ess_audio_prepare_for_output(int dev, int bsize, int bcount)
-{
-	sb_devc *devc = audio_devs[dev]->devc;
-
-	sb_dsp_reset(devc);
-	ess_speed(devc);
-
-	ess_write(devc, 0xb8, 4);	/* Auto init DMA mode */
-	ess_change (devc, 0xa8, 0x03, 3 - devc->channels);	/* Mono/stereo */
-	ess_write(devc, 0xb9, 2);	/* Demand mode (4 bytes/request) */
-
-	ess_exec_commands (devc, ess_out_cmds);
-
-	ess_change (devc, 0xb1, 0xf0, 0x50);
-	ess_change (devc, 0xb2, 0xf0, 0x50);
-
-	sb_dsp_command(devc, DSP_CMD_SPKON);
-
-	devc->trigger_bits = 0;
-	return 0;
-}
-
-static void ess_audio_output_block(int dev, unsigned long buf, int nr_bytes,
-			int intrflag)
-{
-	int count = nr_bytes;
-	sb_devc *devc = audio_devs[dev]->devc;
-	short c = -nr_bytes;
-
-	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
-
-	if (audio_devs[dev]->dmap_out->dma > 3)
-		count >>= 1;
-	count--;
-
-	devc->irq_mode = IMODE_OUTPUT;
-
-	ess_write(devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
-	ess_write(devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
-
-	ess_change (devc, 0xb8, 0x05, 0x05);	/* Go */
-	devc->intr_active = 1;
-}
-
-static void ess_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
-	int count = nr_bytes;
-	sb_devc *devc = audio_devs[dev]->devc;
-	short c = -nr_bytes;
-
-	/*
-	 * Start a DMA input to the buffer pointed by dmaqtail
-	 */
-
-	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
-
-	if (audio_devs[dev]->dmap_out->dma > 3)
-		count >>= 1;
-	count--;
-
-	devc->irq_mode = IMODE_INPUT;
-
-	ess_write(devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
-	ess_write(devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
- 
-	ess_change (devc, 0xb8, 0x0f, 0x0f);	/* Go */
-	devc->intr_active = 1;
-}
-
-static void ess_audio_trigger(int dev, int bits)
-{
-	sb_devc *devc = audio_devs[dev]->devc;
-
-	bits &= devc->irq_mode;
-
-	if (!bits)
-		sb_dsp_command(devc, 0xd0);	/* Halt DMA */
-	else
-	{
-		switch (devc->irq_mode)
-		{
-			case IMODE_INPUT:
-				ess_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
-						  devc->trg_intrflag);
-				break;
-
-			case IMODE_OUTPUT:
-				ess_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
-					devc->trg_intrflag);
-				break;
-		}
-	}
-
-	devc->trigger_bits = bits;
-}
-
-/*
  * SB16 specific routines
  */
 
 static int sb16_audio_set_speed(int dev, int speed)
 {
 	sb_devc *devc = audio_devs[dev]->devc;
+	int	max_speed = devc->submodel == SUBMDL_ALS100 ? 48000 : 44100;
 
 	if (speed > 0)
 	{
-		if (speed < 5000)
-			speed = 4000;
+		if (speed < 5000)	/* which of these */
+			speed = 4000;	/* is correct ??? */
 
-		if (speed > 44100)
-			speed = 44100;
+		if (speed > max_speed)
+			speed = max_speed;
 
 		devc->speed = speed;
 	}
@@ -1276,26 +1049,6 @@
 	sb16_audio_mmap
 };
 
-static struct audio_driver ess_audio_driver =	/* ESS ES688/1688 */
-{
-	sb_audio_open,
-	sb_audio_close,
-	sb_set_output_parms,
-	sb_set_input_parms,
-	NULL,
-	ess_audio_prepare_for_input,
-	ess_audio_prepare_for_output,
-	sb1_audio_halt_xfer,
-	NULL,			/* local_qlen */
-	NULL,			/* copy_from_user */
-	NULL,
-	NULL,
-	ess_audio_trigger,
-	ess_audio_set_speed,
-	sb16_audio_set_bits,
-	sbpro_audio_set_channels
-};
-
 void sb_audio_init(sb_devc * devc, char *name)
 {
 	int audio_flags = 0;
@@ -1332,9 +1085,7 @@
 
 		case MDL_ESS:
 			DDB(printk("Will use ESS ES688/1688 driver\n"));
-			audio_flags = DMA_AUTOMODE;
-			format_mask |= AFMT_S16_LE;
-			driver = &ess_audio_driver;
+			driver = ess_audio_init (devc, &audio_flags, &format_mask);
 			break;
 
 		case MDL_SB16:
@@ -1355,17 +1106,17 @@
 			driver = &sbpro_audio_driver;
 	}
 
-	if ((devc->my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+	if ((devc->dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
 				name,driver, sizeof(struct audio_driver),
 				audio_flags, format_mask, devc,
 				devc->dma8,
-				devc->duplex ? devc->dma16 : devc->dma8) < 0))
+				devc->duplex ? devc->dma16 : devc->dma8)) < 0)
 	{
 		  printk(KERN_ERR "Sound Blaster:  unable to install audio.\n");
 		  return;
 	}
-	audio_devs[devc->my_dev]->mixer_dev = devc->my_mixerdev;
-	audio_devs[devc->my_dev]->min_fragment = 5;
+	audio_devs[devc->dev]->mixer_dev = devc->my_mixerdev;
+	audio_devs[devc->dev]->min_fragment = 5;
 }
 
 #endif
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.2.0-pre7/linux/drivers/sound/sb_common.c	Fri Jan  8 22:36:11 1999
+++ linux/drivers/sound/sb_common.c	Thu Jan 14 22:59:47 1999
@@ -32,6 +32,8 @@
 #include "sb_mixer.h"
 #include "sb.h"
 
+#include "sb_ess.h"
+
 static sb_devc *detected_devc = NULL;	/* For communication from probe to init */
 static sb_devc *last_devc = NULL;	/* For MPU401 initialization */
 
@@ -91,7 +93,7 @@
 	return 0;
 }
 
-static int sb_dsp_get_byte(sb_devc * devc)
+int sb_dsp_get_byte(sb_devc * devc)
 {
 	int i;
 
@@ -103,50 +105,18 @@
 	return 0xffff;
 }
 
-inline void ess_extended (sb_devc * devc)
-{
-	/* Enable extended mode */
-
-	sb_dsp_command(devc, 0xc6);
-}
-
-int ess_write(sb_devc * devc, unsigned char reg, unsigned char data)
-{
-	/* Write a byte to an extended mode register of ES1688 */
-
-	if (!sb_dsp_command(devc, reg))
-		return 0;
-
-	return sb_dsp_command(devc, data);
-}
-
-int ess_read(sb_devc * devc, unsigned char reg)
-{
-/* Read a byte from an extended mode register of ES1688 */
-	if (!sb_dsp_command(devc, 0xc0))	/* Read register command */
-		return -1;
-
-	if (!sb_dsp_command(devc, reg))
-		return -1;
-
-	return sb_dsp_get_byte(devc);
-}
-
-static void sbintr(int irq, void *dev_id, struct pt_regs *dummy)
+static void sb_intr (sb_devc *devc)
 {
 	int status;
 	unsigned char   src = 0xff;
 
-	sb_devc *devc = dev_id;
-
-	devc->irq_ok = 1;
 	if (devc->model == MDL_SB16)
 	{
 		src = sb_getmixer(devc, IRQ_STAT);	/* Interrupt source register */
 
 #if defined(CONFIG_MIDI)&& defined(CONFIG_UART401)
-		if (src & 4)
-			uart401intr(devc->irq, devc->midi_irq_cookie, NULL);	/* MPU401 interrupt */
+		if (src & 4)						/* MPU401 interrupt */
+			uart401intr(devc->irq, devc->midi_irq_cookie, NULL);
 #endif
 
 		if (!(src & 3))
@@ -209,6 +179,21 @@
 		status = inb(DSP_DATA_AVL16);
 }
 
+static void sbintr(int irq, void *dev_id, struct pt_regs *dummy)
+{
+    sb_devc *devc = dev_id;
+
+	devc->irq_ok = 1;
+
+	switch (devc->model) {
+	case MDL_ESS:
+		ess_intr (devc);
+		break;
+	default:
+		sb_intr (devc);
+		break;
+	}
+}
 
 int sb_dsp_reset(sb_devc * devc)
 {
@@ -216,10 +201,11 @@
 
 	DEB(printk("Entered sb_dsp_reset()\n"));
 
-	if (devc->model == MDL_ESS)
-		outb(3, DSP_RESET);	/* Reset FIFO too */
-	else
-		outb(1, DSP_RESET);
+	if (devc->model == MDL_ESS) return ess_dsp_reset (devc);
+
+	/* This is only for non-ESS chips */
+
+	outb(1, DSP_RESET);
 
 	udelay(10);
 	outb(0, DSP_RESET);
@@ -232,9 +218,9 @@
 		DDB(printk("sb: No response to RESET\n"));
 		return 0;	/* Sorry */
 	}
-	if (devc->model == MDL_ESS) ess_extended (devc);
 
 	DEB(printk("sb_dsp_reset() OK\n"));
+
 	return 1;
 }
 
@@ -492,206 +478,6 @@
 #endif
 }
 
-/*
- * ESS technology describes a detection scheme in their docs. It involves
- * fiddling with the bits in certain mixer registers. ess_probe is supposed
- * to help.
- */
-static unsigned int ess_identify (sb_devc * devc);
-
-static int ess_probe (sb_devc * devc, int reg, int xorval)
-{
-	int  val1, val2, val3;
-
-	val1 = sb_getmixer (devc, reg);
-	val2 = val1 ^ xorval;
-	sb_setmixer (devc, reg, val2);
-	val3 = sb_getmixer (devc, reg); 
-	sb_setmixer (devc, reg, val1);
-
-	return (val2 == val3);
-}
-
-static int ess_init(sb_devc * devc, struct address_info *hw_config)
-{
-	unsigned char cfg, irq_bits = 0, dma_bits = 0;
-	int ess_major = 0, ess_minor = 0;
-	int i;
-	static char name[100];
-
-	/*
-	 * Try to detect ESS chips.
-	 */
-
-	sb_dsp_command(devc, 0xe7);	/* Return identification */
-
-	for (i = 1000; i; i--)
-	{
-		if (inb(DSP_DATA_AVAIL) & 0x80)
-		{
-			if (ess_major == 0)
-				ess_major = inb(DSP_READ);
-			else
-			{
-				ess_minor = inb(DSP_READ);
-				break;
-			}
-		}
-	}
-
-	if (ess_major == 0)
-		return 0;
-
-	if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80)
-	{
-		sprintf(name, "ESS ES488 AudioDrive (rev %d)",
-			  ess_minor & 0x0f);
-		hw_config->name = name;
-		devc->model = MDL_SBPRO;
-		return 1;
-	}
-
-	/*
-	 * This the detection heuristic of ESS technology, though somewhat
-	 * changed to actually make it work.
-	 * This results in the following detection steps:
-	 * - distinct between ES688 and ES1688+ (as always done in this driver)
-	 *   if ES688 we're ready
-	 * - try to detect ES1868, ES1869 or ES1878 (ess_identify)
-	 *   if successful we're ready
-	 * - try to detect ES1888, ES1887 or ES1788 (aim: detect ES1887)
-	 *   if successful we're ready
-	 * - Dunno. Must be 1688. Will do in general
-	 *
-	 * This is the most BETA part of the software: Will the detection
-         * always work?
-	 */
-	devc->model = MDL_ESS;
-	devc->submodel = ess_minor & 0x0f;
-
-	if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80)
-	{
-		char *chip    = NULL;
-
-		if ((ess_minor & 0x0f) < 8) {
-			chip = "ES688";
-		};
-		if (chip == NULL) {
-			int type;
-
-			type = ess_identify (devc);
-
-			switch (type) {
-			case 0x1868:
-				chip = "ES1868";
-				devc->submodel = SUBMDL_ES1868;
-				break;
-			case 0x1869:
-				chip = "ES1869";
-				devc->submodel = SUBMDL_ES1869;
-				break;
-			case 0x1878:
-				chip = "ES1878";
-				devc->submodel = SUBMDL_ES1878;
-				break;
-			};
-		};
-		if (chip == NULL && !ess_probe(devc, 0x64, (1 << 3))) {
-			if (ess_probe (devc, 0x70, 0x7f)) {
-				if (ess_probe (devc, 0x64, (1 << 5))) {
-					chip = "ES1887";
-				} else {
-					chip = "ES1888";
-				}
-				devc->submodel = SUBMDL_ES188X;
-			} else {
-				chip = "ES1788";
-				devc->submodel = SUBMDL_ES1788;
-			}
-		};
-		if (chip == NULL) {
-			chip = "ES1688";
-		};
-
-		sprintf(name,"ESS %s AudioDrive (rev %d)",
-			chip, ess_minor & 0x0f);
-	}
-	else
-		strcpy(name, "Jazz16");
-
-	hw_config->name = name;
-	sb_dsp_reset(devc);	/* Turn on extended mode */
-
-	/*
-	 *    Set IRQ configuration register
-	 */
-
-	cfg = 0x50;		/* Enable only DMA counter interrupt */
-
-	switch (devc->irq)
-	{
-		case 2:
-		case 9:
-			irq_bits = 0;
-			break;
-
-		case 5:
-			irq_bits = 1;
-			break;
-
-		case 7:
-			irq_bits = 2;
-			break;
-
-		case 10:
-			irq_bits = 3;
-			break;
-
-		default:
-			irq_bits = 0;
-			cfg = 0x10;	/* Disable all interrupts */
-			printk(KERN_ERR "ESS1688: Invalid IRQ %d\n", devc->irq);
-			return 0;
-	}
-
-	if (!ess_write(devc, 0xb1, cfg | (irq_bits << 2)))
-		printk(KERN_ERR "ESS1688: Failed to write to IRQ config register\n");
-
-	/*
-	 *    Set DMA configuration register
-	 */
-
-	cfg = 0x50;		/* Extended mode DMA enable */
-
-	if (devc->dma8 > 3 || devc->dma8 < 0 || devc->dma8 == 2)
-	{
-		dma_bits = 0;
-		cfg = 0x00;	/* Disable all DMA */
-		printk(KERN_ERR "ESS1688: Invalid DMA %d\n", devc->dma8);
-	}
-	else
-	{
-		if (devc->dma8 == 3)
-			dma_bits = 3;
-		else
-			dma_bits = devc->dma8 + 1;
-	}
-
-	if (!ess_write(devc, 0xb2, cfg | (dma_bits << 2)))
-		printk(KERN_ERR "ESS1688: Failed to write to DMA config register\n");
-
-	/*
-	 *	Enable joystick and OPL3
-	 */
-
-	cfg = sb_getmixer(devc, 0x40);
-	sb_setmixer(devc, 0x40, cfg | 0x03);
-	if (devc->submodel >= 8)	/* ES1688 */
-		devc->caps |= SB_NO_MIDI;	/* ES1688 uses MPU401 MIDI mode */
-	sb_dsp_reset(devc);
-	return 1;
-}
-
 int sb_dsp_detect(struct address_info *hw_config)
 {
 	sb_devc sb_info;
@@ -700,7 +486,7 @@
 	memset((char *) &sb_info, 0, sizeof(sb_info));	/* Zero everything */
 	sb_info.my_mididev = -1;
 	sb_info.my_mixerdev = -1;
-	sb_info.my_dev = -1;
+	sb_info.dev = -1;
 
 	/*
 	 * Initialize variables 
@@ -905,11 +691,18 @@
 			break;
 
 		case 3:		/* SB Pro and most clones */
-			if (devc->model == 0)
-			{
+			switch (devc->model) {
+			case 0:
 				devc->model = hw_config->card_subtype = MDL_SBPRO;
 				if (hw_config->name == NULL)
 					hw_config->name = "Sound Blaster Pro (8 BIT ONLY)";
+				break;
+			case MDL_ESS:
+				if (!ess_dsp_init(devc, hw_config)) {
+					release_region (hw_config->io_base, 16);
+					return 0;
+				}
+				break;
 			}
 			break;
 
@@ -931,6 +724,7 @@
 				/* Register 0x22 & 0xf0 on ALS100 == 0xf0; on ALS007 it == 0x10.     */
 				if ((sb_getmixer(devc,0x30) != 0xff) || ((sb_getmixer(devc,0x22) & 0xf0) != 0x10)) 
 				{
+					devc->submodel = SUBMDL_ALS100;
 					if (hw_config->name == NULL)
 						hw_config->name = "Sound Blaster 16 (ALS-100)";
         			}
@@ -1068,7 +862,7 @@
 			/* But we have to do it, if UART401 is not detected */
 			if (!sbmpu)
 				sound_unload_mididev(devc->my_mididev);
-			sound_unload_audiodev(devc->my_dev);
+			sound_unload_audiodev(devc->dev);
 		}
 		kfree(devc);
 	}
@@ -1089,20 +883,16 @@
 {
 	unsigned long flags;
 
-	/* MDB(printk("ESS: write port %x: %x\n", port, value)); */
+	if (devc->model == MDL_ESS) return ess_setmixer (devc, port, value);
 
 	save_flags(flags);
 	cli();
-	if (devc->model == MDL_ESS && port >= 0xa0) {
-		/* ess_extended (devc); */
-		ess_write (devc, port, value);
-	} else {
-		outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
-
-		udelay(20);
-		outb(((unsigned char) (value & 0xff)), MIXER_DATA);
-		udelay(20);
-	};
+
+	outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+	udelay(20);
+	outb(((unsigned char) (value & 0xff)), MIXER_DATA);
+	udelay(20);
+
 	restore_flags(flags);
 }
 
@@ -1111,38 +901,29 @@
 	unsigned int val;
 	unsigned long flags;
 
+	if (devc->model == MDL_ESS) return ess_getmixer (devc, port);
+
 	save_flags(flags);
 	cli();
-	outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
 
+	outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
 	udelay(20);
 	val = inb(MIXER_DATA);
 	udelay(20);
+
 	restore_flags(flags);
 
 	return val;
 }
-/*
- * Some PnP chips can be identified by repeatedly reading mixer register 0x40.
- * This is done by ess_identify
- */
-static unsigned int ess_identify (sb_devc * devc)
-{
-	unsigned int val;
-	unsigned long flags;
 
-	save_flags(flags);
-	cli();
-	outb(((unsigned char) (0x40 & 0xff)), MIXER_ADDR);
-
-	udelay(20);
-	val  = inb(MIXER_DATA) << 8;
-	udelay(20);
-	val |= inb(MIXER_DATA);
-	udelay(20);
-	restore_flags(flags);
+void sb_chgmixer
+	(sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val)
+{
+	int value;
 
-	return val;
+	value = sb_getmixer(devc, reg);
+	value = (value & ~mask) | (val & mask);
+	sb_setmixer(devc, reg, value);
 }
 
 #ifdef CONFIG_MIDI
@@ -1285,53 +1066,6 @@
 #endif
 	outb((control | 0x03), mpu_base + 7);	/* xxxxxx11 restarts */
 	hw_config->name = "SoundMan Wave";
-	return 1;
-}
-
-static int ess_midi_init(sb_devc * devc, struct address_info *hw_config)
-{
-	unsigned char   cfg, tmp;
-
-	cfg = sb_getmixer(devc, 0x40) & 0x03;
-
-	if (devc->submodel < 8)
-	{
-		sb_setmixer(devc, 0x40, cfg | 0x03);	/* Enable OPL3 & joystick */
-		return 0;	/* ES688 doesn't support MPU401 mode */
-	}
-	tmp = (hw_config->io_base & 0x0f0) >> 4;
-
-	if (tmp > 3)
-	{
-		sb_setmixer(devc, 0x40, cfg);
-		return 0;
-	}
-	cfg |= tmp << 3;
-
-	tmp = 1;		/* MPU enabled without interrupts */
-
-	/* May be shared: if so the value is -ve */
-	
-	switch(abs(hw_config->irq))
-	{
-		case 9:
-			tmp = 0x4;
-			break;
-		case 5:
-			tmp = 0x5;
-			break;
-		case 7:
-			tmp = 0x6;
-			break;
-		case 10:
-			tmp = 0x7;
-			break;
-		default:
-			return 0;
-	}
-
-	cfg |= tmp << 5;
-	sb_setmixer(devc, 0x40, cfg | 0x03);
 	return 1;
 }
 
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/sb_ess.c linux/drivers/sound/sb_ess.c
--- v2.2.0-pre7/linux/drivers/sound/sb_ess.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/sb_ess.c	Sun Jan 17 18:25:48 1999
@@ -0,0 +1,1682 @@
+/*
+ * Created: 9-Jan-1999
+ *
+ * TODO: 	consistency speed calculations!!
+ *			what's the sample rate when duplex? Docs contradict.
+ *			I broke IRQ detection for non-SMP machines
+ * ????:	Did I break MIDI support?
+ *
+ * This files contains ESS chip specifics. It's based on the existing ESS
+ * handling as it resided in sb_common.c, sb_mixer.c and sb_audio.c. This
+ * file adds features like:
+ * - Chip Identification (as shown in /proc/sound)
+ * - RECLEV support for ES1688 and later
+ * - 6 bits playback level support chips later than ES1688
+ * - Recording level support on a per-device basis for ES1887
+ * - Full-Duplex for ES1887 (under development)
+ *
+ * Full duplex is enabled by specifying dma16. While the normal dma must
+ * be one of 0, 1 or 3, dma16 can be one of 0, 1, 3 or 5. DMA 5 is a 16 bit
+ * DMA channel, while the others are 8 bit..
+ *
+ * History:
+ *
+ * Rolf Fokkens	(Dec 20 1998):	ES188x recording level support on a per
+ *								input basis.
+ *				(Dec 24 1998):	Recognition of ES1788, ES1887, ES1888,
+ *								ES1868, ES1869 and ES1878. Could be used for
+ *								specific handling in the future. All except
+ *								ES1887 and ES1888 and ES688 are handled like
+ *								ES1688.
+ *				(Dec 27 1998):	RECLEV for all (?) ES1688+ chips. ES188x now
+ *								have the "Dec 20" support + RECLEV
+ *				(jan  2 1999):	Preparation for Full Duplex. This means
+ *								Audio 2 is now used for playback when dma16
+ *								is specified. The next step would be to use
+ *								Audio 1 and Audio 2 at the same time.
+ */
+
+#undef FKS_LOGGING
+
+/*
+ * About the documentation
+ *
+ * I don't know if the chips all are OK, but the documentation is buggy. 'cause
+ * I don't have all the cips myself, there's a lot I cannot verify. I'll try to
+ * keep track of my latest insights about his here. If you have additional info,
+ * please enlighten me (fokkensr@vertis.nl)!
+ *
+ * I had the impression that ES1688 also has 6 bit master volume control. The
+ * documentation about ES1888 (rev C, october '95) claims that ES1888 has
+ * the following features ES1688 doesn't have:
+ * - 6 bit master volume
+ * - Full Duplex
+ * So ES1688 apparently doesn't have 6 bit master volume control, but the
+ * ES1688 does have RECLEV control. Makes me wonder: does ES688 have it too?
+ * Without RECLEV ES688 won't be much fun I guess.
+ *
+ * From the ES1888 (rev C, october '95) documentation I got the impression
+ * that registers 0x68 to 0x6e don't exist which means: no recording volume
+ * controls. To my surprise the ES888 documentation (1/14/96) claims that
+ * ES888 does have these record mixer registers, but that ES1888 doesn't have
+ * 0x69 and 0x6b. So the rest should be there.
+ *
+ * I'm trying to get ES1887 Full Duplex. Audio 2 is playback only, while Audio 2
+ * is both record and playback. I think I should use Audio 2 for all playback.
+ *
+ * The documentation is an adventure: it's close but not fully accurate. I
+ * found out that after a reset some registers are *NOT* reset, though the
+ * docs say the would be. Interresting ones are 0x7f, 0x7d and 0x7a. They are
+ * related to the Audio 2 channel. I also was suprised about the consequenses
+ * of writing 0x00 to 0x7f (which should be done by reset): The ES1887 moves
+ * into ES1888 mode. This means that it claims IRQ 11, which happens to be my
+ * ISDN adapter. Needless to say it no longer worked. I now understand why
+ * after rebooting 0x7f already was 0x05, the value of my choise: the BIOS
+ * did it.
+ *
+ * Oh, and this is another trap: in ES1887 docs mixer register 0x70 is decribed
+ * as if it's exactly the same as register 0xa1. This is *NOT* true. The
+ * description of 0x70 in ES1869 docs is accurate however.
+ *
+ * When using audio 2 mixer register 0x72 seems te be meaningless. Only 0xa2
+ * has effect.
+ *
+ * Software reset not being able to reset all registers is great! Especially
+ * the fact that register 0x78 isn't reset is great when you wanna change back
+ * to single dma operation (simplex): audio 2 is still operation, and uses the
+ * same dma as audio 1: your ess changes into a funny echo machine.
+ */
+
+/*
+ * About recognition of ESS chips
+ *
+ * The distinction of ES688, ES1688, ES1788, ES1887 and ES1888 is described in
+ * a (preliminary ??) datasheet on ES1887. It's aim is to identify ES1887, but
+ * during detection the text claims that "this chip may be ..." when a step
+ * fails. This scheme is used to distinct between the above chips.
+ * It appears however that some PnP chips like ES1868 are recognized as ES1788
+ * by the ES1887 detection scheme. These PnP chips can be detected in another
+ * way however: ES1868, ES1869 and ES1878 can be recognized (full proof I think)
+ * by repeatedly reading mixer register 0x40. This is done by ess_identify in
+ * sb_common.c.
+ * This results in the following detection steps:
+ * - distinct between ES688 and ES1688+ (as always done in this driver)
+ *   if ES688 we're ready
+ * - try to detect ES1868, ES1869 or ES1878
+ *   if successful we're ready
+ * - try to detect ES1888, ES1887 or ES1788
+ *   if successful we're ready
+ * - Dunno. Must be 1688. Will do in general
+ *
+ * About RECLEV support:
+ *
+ * The existing ES1688 support didn't take care of the ES1688+ recording
+ * levels very well. Whenever a device was selected (recmask) for recording
+ * it's recording level was loud, and it couldn't be changed. The fact that
+ * internal register 0xb4 could take care of RECLEV, didn't work meaning until
+ * it's value was restored every time the chip was reset; this reset the
+ * value of 0xb4 too. I guess that's what 4front also had (have?) trouble with.
+ *
+ * About ES1887 support:
+ *
+ * The ES1887 has separate registers to control the recording levels, for all
+ * inputs. The ES1887 specific software makes these levels the same as their
+ * corresponding playback levels, unless recmask says they aren't recorded. In
+ * the latter case the recording volumes are 0.
+ * Now recording levels of inputs can be controlled, by changing the playback
+ * levels. Futhermore several devices can be recorded together (which is not
+ * possible with the ES1688.
+ * Besides the separate recording level control for each input, the common
+ * recordig level can also be controlled by RECLEV as described above.
+ *
+ * Not only ES1887 have this recording mixer. I know the following from the
+ * documentation:
+ * ES688	no
+ * ES1688	no
+ * ES1868	no
+ * ES1869	yes
+ * ES1878	no
+ * ES1879	yes
+ * ES1888	no/yes	Contradicting documentation; most recent: yes
+ * ES1946	yes		This is a PCI chip; not handled by this driver
+ */
+
+#include <linux/delay.h>
+
+#include "sound_config.h"
+#include "sb_mixer.h"
+#include "sb.h"
+
+#include "sb_ess.h"
+
+#ifdef FKS_LOGGING
+static void ess_show_mixerregs (sb_devc *devc);
+#endif
+static int ess_read (sb_devc * devc, unsigned char reg);
+static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data);
+static void ess_chgmixer
+	(sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val);
+
+/****************************************************************************
+ *																			*
+ *									ESS audio								*
+ *																			*
+ ****************************************************************************/
+
+struct ess_command {short cmd; short data;};
+
+/*
+ * Commands for initializing Audio 1 for input (record)
+ */
+static struct ess_command ess_i08m[] =		/* input 8 bit mono */
+	{ {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} };
+static struct ess_command ess_i16m[] =		/* input 16 bit mono */
+	{ {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} };
+static struct ess_command ess_i08s[] =		/* input 8 bit stereo */
+	{ {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} };
+static struct ess_command ess_i16s[] =		/* input 16 bit stereo */
+	{ {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} };
+
+static struct ess_command *ess_inp_cmds[] =
+	{ ess_i08m, ess_i16m, ess_i08s, ess_i16s };
+
+
+/*
+ * Commands for initializing Audio 1 for output (playback)
+ */
+static struct ess_command ess_o08m[] =		/* output 8 bit mono */
+	{ {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} };
+static struct ess_command ess_o16m[] =		/* output 16 bit mono */
+	{ {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} };
+static struct ess_command ess_o08s[] =		/* output 8 bit stereo */
+	{ {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} };
+static struct ess_command ess_o16s[] =		/* output 16 bit stereo */
+	{ {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} };
+
+static struct ess_command *ess_out_cmds[] =
+	{ ess_o08m, ess_o16m, ess_o08s, ess_o16s };
+
+static void ess_exec_commands
+	(sb_devc *devc, struct ess_command *cmdtab[])
+{
+	struct ess_command *cmd;
+
+	cmd = cmdtab [ ((devc->channels != 1) << 1) + (devc->bits != AFMT_U8) ];
+
+	while (cmd->cmd != -1) {
+		ess_write (devc, cmd->cmd, cmd->data);
+		cmd++;
+	}
+}
+
+static void ess_change
+	(sb_devc *devc, unsigned int reg, unsigned int mask, unsigned int val)
+{
+	int value;
+
+	value = ess_read (devc, reg);
+	value = (value & ~mask) | (val & mask);
+	ess_write (devc, reg, value);
+}
+
+static void ess_set_output_parms
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (devc->duplex) {
+		devc->trg_buf_16 = buf;
+		devc->trg_bytes_16 = nr_bytes;
+		devc->trg_intrflag_16 = intrflag;
+		devc->irq_mode_16 = IMODE_OUTPUT;
+	} else {
+		devc->trg_buf = buf;
+		devc->trg_bytes = nr_bytes;
+		devc->trg_intrflag = intrflag;
+		devc->irq_mode = IMODE_OUTPUT;
+	}
+}
+
+static void ess_set_input_parms
+	(int dev, unsigned long buf, int count, int intrflag)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	devc->trg_buf = buf;
+	devc->trg_bytes = count;
+	devc->trg_intrflag = intrflag;
+	devc->irq_mode = IMODE_INPUT;
+}
+
+static int ess_calc_div (int clock, int revert, int *speedp, int *diffp)
+{
+	int divider;
+	int speed, diff;
+	int retval;
+
+	speed   = *speedp;
+	divider = (clock + speed / 2) / *speedp;
+	retval  = revert - divider;
+	if (retval > 127) {
+		retval  = 127;
+		divider = revert - retval;
+	}
+	/* This line is suggested. Must be wrong I think
+	*speedp = (clock + divider / 2) / divider;
+	So I chose the next one */
+
+	*speedp	= clock / divider;
+	diff	= speed - *speedp;
+	if (diff < 0) diff =-diff;
+	*diffp  = diff;
+
+	return retval;
+}
+
+static int ess_calc_best_speed
+	(int clock1, int rev1, int clock2, int rev2, int *divp, int *speedp)
+{
+	int speed1 = *speedp, speed2 = *speedp;
+	int div1, div2;
+	int diff1, diff2;
+	int retval;
+
+	div1 = ess_calc_div (clock1, rev1, &speed1, &diff1);
+	div2 = ess_calc_div (clock2, rev2, &speed2, &diff2);
+
+	if (diff1 < diff2) {
+		*divp   = div1;
+		*speedp = speed1;
+		retval  = 1;
+	} else {
+		*divp   = div2;
+		*speedp = speed2;
+		retval  = 2;
+	}
+
+	return retval;
+}
+
+/*
+ * Depending on the audiochannel ESS devices can
+ * have different clock settings.
+ * callers of ess_speed only do an audionum suggestion, which means
+ * input suggests 1, output suggests 2. This suggestion is only true
+ * however when doing duplex.
+ */
+static void ess_speed (sb_devc *devc, int audionum)
+{
+	int choice;
+	int speed = devc->speed;
+	int clock1, clock2;
+	int rev1, rev2;
+	int div;
+
+	if (!devc->duplex) audionum = 1;
+
+	if (audionum == 1) {
+		rev1	= 128;
+		clock1	= 397700;
+		rev2	= 256;
+		clock2	= 795500;
+	} else {
+		rev1	= 128;
+		clock1	= 793800;
+		rev2	= 128;
+		clock2	= 768000;
+	}
+	choice = ess_calc_best_speed
+			(clock1, rev1, clock2, rev2, &div, &speed);
+
+#ifdef FKS_REG_LOGGING
+printk (KERN_INFO "FKS: ess_speed (%d) b speed = %d, div=%x\n", audionum, speed, div);
+#endif
+
+	if (choice == 2) div |= 0x80;
+
+	if (audionum == 1) {
+		/* Change behaviour of register A1 *
+		sb_chg_mixer(devc, 0x71, 0x20, 0x20)
+		* For ES1869 only??? */
+		ess_write (devc, 0xa1, div);
+	} else {
+		ess_setmixer (devc, 0x70, div);
+	}
+
+	/* Set filter roll-off to 90% of speed/2 */
+	speed = (speed * 9) / 20;
+
+	div = 256 - 7160000 / (speed * 82);
+
+	if (audionum == 1) {
+		ess_write (devc, 0xa2, div);
+	} else {
+		ess_write (devc, 0xa2, div);
+		ess_setmixer (devc, 0x72, div);
+	}
+}
+
+#if 0
+static void ess_speed(sb_devc * devc)
+{
+	int divider;
+	unsigned char bits = 0;
+	int speed = devc->speed;
+
+	if (speed < 4000)
+		speed = 4000;
+	else if (speed > 48000)
+		speed = 48000;
+
+	if (speed > 22000)
+	{
+		bits = 0x80;
+		divider = 256 - (795500 + speed / 2) / speed;
+	}
+	else
+	{
+		divider = 128 - (397700 + speed / 2) / speed;
+	}
+
+	bits |= (unsigned char) divider;
+
+	ess_write (devc, 0xa1, bits);
+
+	/*
+	* Set filter divider register
+	*/
+
+	speed = (speed * 9) / 20;	/* Set filter roll-off to 90% of speed/2 */
+	divider = 256 - 7160000 / (speed * 82);
+
+	ess_write (devc, 0xa2, divider);
+
+	return;
+}
+#endif
+
+static int ess_audio_prepare_for_input(int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	ess_speed(devc, 1);
+
+	sb_dsp_command(devc, DSP_CMD_SPKOFF);
+
+	ess_write (devc, 0xb8, 0x0e);	/* Auto init DMA mode */
+	ess_change (devc, 0xa8, 0x03, 3 - devc->channels);	/* Mono/stereo */
+	ess_write (devc, 0xb9, 2);	/* Demand mode (4 bytes/DMA request) */
+
+	ess_exec_commands (devc, ess_inp_cmds);
+
+	ess_change (devc, 0xb1, 0xf0, 0x50);
+	ess_change (devc, 0xb2, 0xf0, 0x50);
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int ess_audio_prepare_for_output_audio1 (int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	sb_dsp_reset(devc);
+	ess_speed(devc, 1);
+	ess_write (devc, 0xb8, 4);	/* Auto init DMA mode */
+	ess_change (devc, 0xa8, 0x03, 3 - devc->channels);	/* Mono/stereo */
+	ess_write (devc, 0xb9, 2);	/* Demand mode (4 bytes/request) */
+
+	ess_exec_commands (devc, ess_out_cmds);
+
+	ess_change (devc, 0xb1, 0xf0, 0x50);	/* Enable DMA */
+	ess_change (devc, 0xb2, 0xf0, 0x50);	/* Enable IRQ */
+
+	sb_dsp_command(devc, DSP_CMD_SPKON);	/* There be sound! */
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int ess_audio_prepare_for_output_audio2 (int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	unsigned char bits;
+
+	sb_dsp_reset(devc);
+
+	/*
+	 * Auto-Initialize:
+	 * DMA mode + demand mode (8 bytes/request, yes I want it all!)
+	 * But leave 16-bit DMA bit untouched!
+	 */
+	ess_chgmixer (devc, 0x78, 0xd0, 0xd0);
+
+	ess_speed(devc, 2);
+
+	/* bits 4:3 on ES1887 represent recording source. Keep them! */
+	bits = ess_getmixer (devc, 0x7a) & 0x18;
+
+	/* Set stereo/mono */
+	if (devc->channels != 1) bits |= 0x02;
+
+	/* Init DACs; UNSIGNED mode for 8 bit; SIGNED mode for 16 bit */
+	if (devc->bits != AFMT_U8) bits |= 0x05;	/* 16 bit */
+
+	/* Enable DMA, IRQ will be shared (hopefully)*/
+	bits |= 0x60;
+
+	ess_setmixer (devc, 0x7a, bits);
+
+	ess_mixer_reload (devc, SOUND_MIXER_PCM);	/* There be sound! */
+
+	devc->trigger_bits = 0;
+	return 0;
+}
+
+static int ess_audio_prepare_for_output(int dev, int bsize, int bcount)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "ess_audio_prepare_for_output: dma_out=%d,dma_in=%d\n"
+, audio_devs[dev]->dmap_out->dma, audio_devs[dev]->dmap_in->dma);
+#endif
+
+	if (devc->duplex) {
+		return ess_audio_prepare_for_output_audio2 (dev, bsize, bcount);
+	} else {
+		return ess_audio_prepare_for_output_audio1 (dev, bsize, bcount);
+	}
+}
+
+static void ess_audio_halt_xfer(int dev)
+{
+	unsigned long flags;
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	save_flags(flags);
+	cli();
+	sb_dsp_reset(devc);
+	restore_flags(flags);
+
+	/*
+	 * Audio 2 may still be operational! Creates awful sounds!
+	 */
+	if (devc->duplex) ess_chgmixer(devc, 0x78, 0x03, 0x00);
+}
+
+static void ess_audio_start_input
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+	short c = -nr_bytes;
+
+	/*
+	 * Start a DMA input to the buffer pointed by dmaqtail
+	 */
+
+	if (audio_devs[dev]->dmap_in->dma > 3) count >>= 1;
+	count--;
+
+	devc->irq_mode = IMODE_INPUT;
+
+	ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
+	ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
+
+	ess_change (devc, 0xb8, 0x0f, 0x0f);	/* Go */
+	devc->intr_active = 1;
+}
+
+static void ess_audio_output_block_audio1
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+	short c = -nr_bytes;
+
+	if (audio_devs[dev]->dmap_out->dma > 3)
+		count >>= 1;
+	count--;
+
+	devc->irq_mode = IMODE_OUTPUT;
+
+	ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
+	ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
+
+	ess_change (devc, 0xb8, 0x05, 0x05);	/* Go */
+	devc->intr_active = 1;
+}
+
+static void ess_audio_output_block_audio2
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	int count = nr_bytes;
+	sb_devc *devc = audio_devs[dev]->devc;
+	short c = -nr_bytes;
+
+	if (audio_devs[dev]->dmap_out->dma > 3) count >>= 1;
+	count--;
+
+	ess_setmixer (devc, 0x74, (unsigned char) ((unsigned short) c & 0xff));
+	ess_setmixer (devc, 0x76, (unsigned char) (((unsigned short) c >> 8) & 0xff));
+	ess_chgmixer (devc, 0x78, 0x03, 0x03);   /* Go */
+
+	devc->irq_mode_16 = IMODE_OUTPUT;
+		devc->intr_active_16 = 1;
+}
+
+static void ess_audio_output_block
+	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (devc->duplex) {
+		ess_audio_output_block_audio2 (dev, buf, nr_bytes, intrflag);
+	} else {
+		ess_audio_output_block_audio1 (dev, buf, nr_bytes, intrflag);
+	}
+}
+
+/*
+ * FKS: the if-statements for both bits and bits_16 are quite alike.
+ * Combine this...
+ */
+static void ess_audio_trigger(int dev, int bits)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	int bits_16 = bits & devc->irq_mode_16;
+	bits &= devc->irq_mode;
+
+	if (!bits && !bits_16) {
+		/* FKS oh oh.... wrong?? for dma 16? */
+		sb_dsp_command(devc, 0xd0);	/* Halt DMA */
+	}
+
+	if (bits) {
+		switch (devc->irq_mode)
+		{
+			case IMODE_INPUT:
+				ess_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
+					devc->trg_intrflag);
+				break;
+
+			case IMODE_OUTPUT:
+				ess_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
+					devc->trg_intrflag);
+				break;
+		}
+	}
+
+	if (bits_16) {
+		switch (devc->irq_mode_16) {
+		case IMODE_INPUT:
+			ess_audio_start_input(dev, devc->trg_buf_16, devc->trg_bytes_16,
+					devc->trg_intrflag_16);
+			break;
+
+		case IMODE_OUTPUT:
+			ess_audio_output_block(dev, devc->trg_buf_16, devc->trg_bytes_16,
+					devc->trg_intrflag_16);
+			break;
+		}
+	}
+
+	devc->trigger_bits = bits | bits_16;
+}
+
+/*
+ * FKS: Change this!! it's the old routine!
+ */
+static int ess_audio_set_speed(int dev, int speed)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+	int divider;
+
+	if (speed > 0)
+	{
+		if (speed < 5000)
+			speed = 5000;
+		if (speed > 48000)
+			speed = 48000;
+
+		if (speed > 22000)
+		{
+			divider = (795500 + speed / 2) / speed;
+			speed = (795500 + divider / 2) / divider;
+		}
+		else
+		{
+			divider = (397700 + speed / 2) / speed;
+			speed = (397700 + divider / 2) / divider;
+		}
+		devc->speed = speed;
+	}
+	return devc->speed;
+}
+
+/*
+ * FKS: This is a one-on-one copy of sb1_audio_set_bits
+ */
+static unsigned int ess_audio_set_bits(int dev, unsigned int bits)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (bits != 0) {
+		if (bits == AFMT_U8 || bits == AFMT_S16_LE) {
+			devc->bits = bits;
+		} else {
+			devc->bits = AFMT_U8;
+		}
+	}
+
+	return devc->bits;
+}
+
+/*
+ * FKS: This is a one-on-one copy of sbpro_audio_set_channels
+ * (*) Modified it!!
+ */
+static short ess_audio_set_channels(int dev, short channels)
+{
+	sb_devc *devc = audio_devs[dev]->devc;
+
+	if (channels == 1 || channels == 2) devc->channels = channels;
+
+	return devc->channels;
+}
+
+static struct audio_driver ess_audio_driver =   /* ESS ES688/1688 */
+{
+	sb_audio_open,
+	sb_audio_close,
+	ess_set_output_parms,
+	ess_set_input_parms,
+	NULL,
+	ess_audio_prepare_for_input,
+	ess_audio_prepare_for_output,
+	ess_audio_halt_xfer,
+	NULL,		/* local_qlen */
+	NULL,		/* copy_from_user */
+	NULL,
+	NULL,
+	ess_audio_trigger,
+	ess_audio_set_speed,
+	ess_audio_set_bits,
+	ess_audio_set_channels
+};
+
+/*
+ * ess_audio_init must be called from sb_audio_init
+ */
+struct audio_driver *ess_audio_init
+		(sb_devc *devc, int *audio_flags, int *format_mask)
+{
+	*audio_flags = DMA_AUTOMODE;
+	*format_mask |= AFMT_S16_LE;
+
+	if (devc->duplex) {
+		int tmp_dma;
+		/*
+		 * sb_audio_init thinks dma8 is for playback and
+		 * dma16 is for record. Not now! So swap them.
+		 */
+		tmp_dma		= devc->dma16;
+		devc->dma16	= devc->dma8;
+		devc->dma8	= tmp_dma;
+
+		*audio_flags |= DMA_DUPLEX;
+	}
+
+	return &ess_audio_driver;
+}
+
+/****************************************************************************
+ *																			*
+ *								ESS common									*
+ *																			*
+ ****************************************************************************/
+static void ess_handle_channel
+	(char *channel, int dev, int intr_active, unsigned char flag, int irq_mode)
+{
+	if (!intr_active || !flag) return;
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "FKS: ess_handle_channel %s irq_mode=%d\n", channel, irq_mode);
+#endif
+	switch (irq_mode) {
+		case IMODE_OUTPUT:
+			DMAbuf_outputintr (dev, 1);
+			break;
+
+		case IMODE_INPUT:
+			DMAbuf_inputintr (dev);
+			break;
+
+		case IMODE_INIT:
+			break;
+
+		default:
+			/* printk(KERN_WARN "ESS: Unexpected interrupt\n"); */
+	}
+}
+
+/*
+ * FKS: TODO!!! Finish this!
+ *
+ * I think midi stuff uses uart401, without interrupts.
+ * So IMODE_MIDI isn't a value for devc->irq_mode.
+ */
+void ess_intr (sb_devc *devc)
+{
+	int				status;
+	unsigned char	src;
+
+	if (devc->submodel == SUBMDL_ES1887) {
+		src = ess_getmixer (devc, 0x7f) >> 4;
+	} else {
+		src = 0xff;
+	}
+
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "FKS: sbintr src=%x\n",(int)src);
+#endif
+	ess_handle_channel
+		( "Audio 1"
+		, devc->dev, devc->intr_active   , src & 0x01, devc->irq_mode   );
+	ess_handle_channel
+		( "Audio 2"
+		, devc->dev, devc->intr_active_16, src & 0x02, devc->irq_mode_16);
+	/*
+	 * Acknowledge interrupts
+	 */
+	if (devc->submodel == SUBMDL_ES1887 && (src & 0x02)) {
+		ess_chgmixer (devc, 0x7a, 0x80, 0x00);
+	}
+
+	if (src & 0x01) {
+		status = inb(DSP_DATA_AVAIL);
+	}
+}
+
+static void ess_extended (sb_devc * devc)
+{
+	/* Enable extended mode */
+
+	sb_dsp_command(devc, 0xc6);
+}
+
+static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data)
+{
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "FKS: write reg %x: %x\n", reg, data);
+#endif
+	/* Write a byte to an extended mode register of ES1688 */
+
+	if (!sb_dsp_command(devc, reg))
+		return 0;
+
+	return sb_dsp_command(devc, data);
+}
+
+static int ess_read (sb_devc * devc, unsigned char reg)
+{
+	/* Read a byte from an extended mode register of ES1688 */
+
+	/* Read register command */
+	if (!sb_dsp_command(devc, 0xc0)) return -1;
+
+	if (!sb_dsp_command(devc, reg )) return -1;
+
+	return sb_dsp_get_byte(devc);
+}
+
+int ess_dsp_reset(sb_devc * devc)
+{
+	int loopc;
+
+#ifdef FKS_REG_LOGGING
+printk(KERN_INFO "FKS: ess_dsp_reset 1\n");
+ess_show_mixerregs (devc);
+#endif
+
+	DEB(printk("Entered ess_dsp_reset()\n"));
+
+	outb(3, DSP_RESET); /* Reset FIFO too */
+
+	udelay(10);
+	outb(0, DSP_RESET);
+	udelay(30);
+
+	for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++);
+
+	if (inb(DSP_READ) != 0xAA) {
+		DDB(printk("sb: No response to RESET\n"));
+		return 0;   /* Sorry */
+	}
+	ess_extended (devc);
+
+	DEB(printk("sb_dsp_reset() OK\n"));
+
+#ifdef FKS_LOGGING
+printk(KERN_INFO "FKS: dsp_reset 2\n");
+ess_show_mixerregs (devc);
+#endif
+
+	return 1;
+}
+
+static int ess_irq_bits (int irq)
+{
+	switch (irq) {
+	case 2:
+	case 9:
+		return 0;
+
+	case 5:
+		return 1;
+
+	case 7:
+		return 2;
+
+	case 10:
+		return 3;
+
+	default:
+		printk(KERN_ERR "ESS1688: Invalid IRQ %d\n", irq);
+		return -1;
+	}
+}
+
+/*
+ *	Set IRQ configuration register for all ESS models
+ */
+static int ess_common_set_irq_hw (sb_devc * devc)
+{
+	int irq_bits;
+
+	if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return 0;
+
+	if (!ess_write (devc, 0xb1, 0x50 | (irq_bits << 2))) {
+		printk(KERN_ERR "ES1688: Failed to write to IRQ config register\n");
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * I wanna use modern ES1887 mixer irq handling. Funny is the
+ * fact that my BIOS wants the same. But suppose someone's BIOS
+ * doesn't do this!
+ * This is independent of duplex. If there's a 1887 this will
+ * prevent it from going into 1888 mode.
+ */
+static void ess_es1887_set_irq_hw (sb_devc * devc)
+{
+	int irq_bits;
+
+	if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return;
+
+	ess_chgmixer (devc, 0x7f, 0x0f, 0x01 | ((irq_bits + 1) << 1));
+}
+
+static int ess_set_irq_hw (sb_devc * devc)
+{
+	if (devc->submodel == SUBMDL_ES1887) ess_es1887_set_irq_hw (devc);
+
+	return ess_common_set_irq_hw (devc);
+}
+
+static unsigned int ess_identify (sb_devc * devc)
+{
+	unsigned int val;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outb(((unsigned char) (0x40 & 0xff)), MIXER_ADDR);
+
+	udelay(20);
+	val  = inb(MIXER_DATA) << 8;
+	udelay(20);
+	val |= inb(MIXER_DATA);
+	udelay(20);
+	restore_flags(flags);
+
+	return val;
+}
+
+/*
+ * ESS technology describes a detection scheme in their docs. It involves
+ * fiddling with the bits in certain mixer registers. ess_probe is supposed
+ * to help.
+ *
+ * FKS: tracing shows ess_probe writes wrong value to 0x64. Bit 3 reads 1, but
+ * should be written 0 only. Check this.
+ */
+static int ess_probe (sb_devc * devc, int reg, int xorval)
+{
+	int  val1, val2, val3;
+
+	val1 = ess_getmixer (devc, reg);
+	val2 = val1 ^ xorval;
+	ess_setmixer (devc, reg, val2);
+	val3 = ess_getmixer (devc, reg);
+	ess_setmixer (devc, reg, val1);
+
+	return (val2 == val3);
+}
+
+int ess_init(sb_devc * devc, struct address_info *hw_config)
+{
+	unsigned char cfg;
+	int ess_major = 0, ess_minor = 0;
+	int i;
+	static char name[100];
+
+	/*
+	 * Try to detect ESS chips.
+	 */
+
+	sb_dsp_command(devc, 0xe7); /* Return identification */
+
+	for (i = 1000; i; i--) {
+		if (inb(DSP_DATA_AVAIL) & 0x80) {
+			if (ess_major == 0) {
+				ess_major = inb(DSP_READ);
+			} else {
+				ess_minor = inb(DSP_READ);
+				break;
+			}
+		}
+	}
+
+	if (ess_major == 0) return 0;
+
+	if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) {
+		sprintf(name, "ESS ES488 AudioDrive (rev %d)",
+			ess_minor & 0x0f);
+		hw_config->name = name;
+		devc->model = MDL_SBPRO;
+		return 1;
+	}
+
+	/*
+	 * This the detection heuristic of ESS technology, though somewhat
+	 * changed to actually make it work.
+	 * This results in the following detection steps:
+	 * - distinct between ES688 and ES1688+ (as always done in this driver)
+	 *   if ES688 we're ready
+	 * - try to detect ES1868, ES1869 or ES1878 (ess_identify)
+	 *   if successful we're ready
+	 * - try to detect ES1888, ES1887 or ES1788 (aim: detect ES1887)
+	 *   if successful we're ready
+	 * - Dunno. Must be 1688. Will do in general
+	 *
+	 * This is the most BETA part of the software: Will the detection
+	 * always work?
+	 */
+	devc->model = MDL_ESS;
+	devc->submodel = ess_minor & 0x0f;
+
+	if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
+		char *chip = NULL;
+
+		if ((ess_minor & 0x0f) < 8) {
+			chip = "ES688";
+		};
+#ifdef FKS_LOGGING
+printk(KERN_INFO "FKS: mixer_reset\n");
+		ess_setmixer (devc, 0x00, 0x00);
+#endif
+		if (chip == NULL) {
+			int type;
+
+			type = ess_identify (devc);
+
+			switch (type) {
+			case 0x1868:
+				chip = "ES1868";
+				devc->submodel = SUBMDL_ES1868;
+				break;
+			case 0x1869:
+				chip = "ES1869";
+				devc->submodel = SUBMDL_ES1869;
+				break;
+			case 0x1878:
+				chip = "ES1878";
+				devc->submodel = SUBMDL_ES1878;
+				break;
+			};
+		};
+		if (chip == NULL && !ess_probe(devc, 0x64, (1 << 3))) {
+			if (ess_probe (devc, 0x70, 0x7f)) {
+				if (ess_probe (devc, 0x64, (1 << 5))) {
+					chip = "ES1887";
+					devc->submodel = SUBMDL_ES1887;
+				} else {
+					chip = "ES1888";
+					devc->submodel = SUBMDL_ES1888;
+				}
+			} else {
+				chip = "ES1788";
+				devc->submodel = SUBMDL_ES1788;
+			}
+		};
+		if (chip == NULL) {
+			chip = "ES1688";
+		};
+
+		sprintf(name,"ESS %s AudioDrive (rev %d)", chip, ess_minor & 0x0f);
+	} else {
+		strcpy(name, "Jazz16");
+	}
+
+	hw_config->name = name;
+	/* FKS: sb_dsp_reset to enable extended mode???? */
+	sb_dsp_reset(devc); /* Turn on extended mode */
+
+	/*
+	 *  Enable joystick and OPL3
+	 */
+	cfg = ess_getmixer (devc, 0x40);
+	ess_setmixer (devc, 0x40, cfg | 0x03);
+	if (devc->submodel >= 8) {		/* ES1688 */
+		devc->caps |= SB_NO_MIDI;   /* ES1688 uses MPU401 MIDI mode */
+	}
+	sb_dsp_reset (devc);
+
+	/*
+	 * This is important! If it's not done, the IRQ probe in sb_dsp_init
+	 * may fail.
+	 */
+	return ess_set_irq_hw (devc);
+}
+
+static int ess_set_dma_hw(sb_devc * devc)
+{
+	unsigned char cfg, dma_bits = 0, dma16_bits;
+	int dma;
+
+#ifdef FKS_LOGGING
+printk(KERN_INFO "ess_set_dma_hw: dma8=%d,dma16=%d,dup=%d\n"
+, devc->dma8, devc->dma16, devc->duplex);
+#endif
+
+	/*
+	 * FKS: It seems as if this duplex flag isn't set yet. Check it.
+	 */
+	dma = devc->dma8;
+
+	if (dma > 3 || dma < 0 || dma == 2) {
+		dma_bits = 0;
+		printk(KERN_ERR "ESS1688: Invalid DMA8 %d\n", dma);
+		return 0;
+	} else {
+		/* Extended mode DMA enable */
+		cfg = 0x50;
+
+		if (dma == 3) {
+			dma_bits = 3;
+		} else {
+			dma_bits = dma + 1;
+		}
+	}
+
+	if (!ess_write (devc, 0xb2, cfg | (dma_bits << 2))) {
+		printk(KERN_ERR "ESS1688: Failed to write to DMA config register\n");
+		return 0;
+	}
+
+	if (devc->duplex) {
+		dma = devc->dma16;
+		dma16_bits = 0;
+
+		if (dma >= 0) {
+			switch (dma) {
+			case 0:
+				dma_bits = 0x04;
+				break;
+			case 1:
+				dma_bits = 0x05;
+				break;
+			case 3:
+				dma_bits = 0x06;
+				break;
+			case 5:
+				dma_bits   = 0x07;
+				dma16_bits = 0x20;
+				break;
+			default:
+				printk(KERN_ERR "ESS1887: Invalid DMA16 %d\n", dma);
+				return 0;
+			};
+			ess_chgmixer (devc, 0x78, 0x20, dma16_bits);
+			ess_chgmixer (devc, 0x7d, 0x07, dma_bits);
+		}
+	}
+	return 1;
+}
+
+/*
+ * This one is called from sb_dsp_init.
+ *
+ * Return values:
+ *  0: Failed
+ *  1: Succeeded or doesn't apply (not SUBMDL_ES1887)
+ */
+int ess_dsp_init (sb_devc *devc, struct address_info *hw_config)
+{
+	/*
+	 * This for ES1887 to run Full Duplex. Actually ES1888
+	 * is allowed to do so too. I have no idea yet if this
+	 * will work for ES1888 however.
+	 *
+	 * For SB16 having both dma8 and dma16 means enable
+	 * Full Duplex. Let's try this for ES1887 too
+	 *
+	 */
+	if (devc->submodel == SUBMDL_ES1887) {
+		if (hw_config->dma2 != -1) {
+			devc->dma16 = hw_config->dma2;
+		}
+		/*
+		 * devc->duplex initialization is put here, cause
+		 * ess_set_dma_hw needs it.
+		 */
+		if (devc->dma8 != devc->dma16 && devc->dma16 != -1) {
+			devc->duplex = 1;
+		}
+
+		if (!ess_set_dma_hw (devc)) {
+			free_irq(devc->irq, devc);
+			return 0;
+		}
+		return 1;
+	} else {
+		return -1;
+	}
+}
+
+/****************************************************************************
+ *																			*
+ *									ESS mixer								*
+ *																			*
+ ****************************************************************************/
+
+#define ES688_RECORDING_DEVICES	\
+			( SOUND_MASK_LINE	| SOUND_MASK_MIC	| SOUND_MASK_CD		)
+#define ES688_MIXER_DEVICES		\
+			( SOUND_MASK_SYNTH	| SOUND_MASK_PCM	| SOUND_MASK_LINE	\
+			| SOUND_MASK_MIC	| SOUND_MASK_CD		| SOUND_MASK_VOLUME	\
+			| SOUND_MASK_LINE2	| SOUND_MASK_SPEAKER					)
+
+#define ES1688_RECORDING_DEVICES	\
+			( ES688_RECORDING_DEVICES					)
+#define ES1688_MIXER_DEVICES		\
+			( ES688_MIXER_DEVICES | SOUND_MASK_RECLEV	)
+
+#define ES1887_RECORDING_DEVICES	\
+			( ES1688_RECORDING_DEVICES | SOUND_MASK_LINE2 | SOUND_MASK_SYNTH)
+#define ES1887_MIXER_DEVICES		\
+			( ES1688_MIXER_DEVICES											)
+
+/*
+ * Mixer registers of ES1887
+ *
+ * These registers specifically take care of recording levels. To make the
+ * mapping from playback devices to recording devices every recording
+ * devices = playback device + ES_REC_MIXER_RECDIFF
+ */
+#define ES_REC_MIXER_RECBASE	(SOUND_MIXER_LINE3 + 1)
+#define ES_REC_MIXER_RECDIFF	(ES_REC_MIXER_RECBASE - SOUND_MIXER_SYNTH)
+
+#define ES_REC_MIXER_RECSYNTH	(SOUND_MIXER_SYNTH	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECPCM		(SOUND_MIXER_PCM	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECSPEAKER	(SOUND_MIXER_SPEAKER + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECLINE	(SOUND_MIXER_LINE	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECMIC		(SOUND_MIXER_MIC	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECCD		(SOUND_MIXER_CD		 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECIMIX	(SOUND_MIXER_IMIX	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECALTPCM	(SOUND_MIXER_ALTPCM	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECRECLEV	(SOUND_MIXER_RECLEV	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECIGAIN	(SOUND_MIXER_IGAIN	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECOGAIN	(SOUND_MIXER_OGAIN	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECLINE1	(SOUND_MIXER_LINE1	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECLINE2	(SOUND_MIXER_LINE2	 + ES_REC_MIXER_RECDIFF)
+#define ES_REC_MIXER_RECLINE3	(SOUND_MIXER_LINE3	 + ES_REC_MIXER_RECDIFF)
+
+static mixer_tab es688_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x32, 7, 4, 0x32, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0)
+};
+
+/*
+ * The ES1688 specifics... hopefully correct...
+ * - 6 bit master volume
+ *   I was wrong, ES1888 docs say ES1688 didn't have it.
+ * - RECLEV control
+ * These may apply to ES688 too. I have no idea.
+ */
+static mixer_tab es1688_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x32, 7, 4, 0x32, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0xb4, 7, 4, 0xb4, 3, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0)
+};
+
+static mixer_tab es1688later_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x60, 5, 6, 0x62, 5, 6),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0xb4, 7, 4, 0xb4, 3, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0)
+};
+
+/*
+ * This one is for all ESS chips with a record mixer.
+ * It's not used (yet) however
+ */
+static mixer_tab es_rec_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x60, 5, 6, 0x62, 5, 6),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0xb4, 7, 4, 0xb4, 3, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECSYNTH,		0x6b, 7, 4, 0x6b, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECPCM,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECSPEAKER,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE,		0x6e, 7, 4, 0x6e, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECMIC,		0x68, 7, 4, 0x68, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECCD,			0x6a, 7, 4, 0x6a, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECIMIX,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECALTPCM,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECRECLEV,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECIGAIN,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECOGAIN,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE1,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE2,		0x6c, 7, 4, 0x6c, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECLINE3,		0x00, 0, 0, 0x00, 0, 0)
+};
+
+/*
+ * This one is for ES1887. It's little different from es_rec_mix: it
+ * has 0x7c for PCM playback level. This is because ES1887 uses
+ * Audio 2 for playback.
+ */
+static mixer_tab es1887_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,			0x60, 5, 6, 0x62, 5, 6),
+MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,			0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,			0x7c, 7, 4, 0x7c, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,		0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,			0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,			0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,				0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,			0xb4, 7, 4, 0xb4, 3, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,			0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,			0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECSYNTH,		0x6b, 7, 4, 0x6b, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECPCM,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECSPEAKER,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE,		0x6e, 7, 4, 0x6e, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECMIC,		0x68, 7, 4, 0x68, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECCD,			0x6a, 7, 4, 0x6a, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECIMIX,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECALTPCM,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECRECLEV,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECIGAIN,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECOGAIN,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE1,		0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES_REC_MIXER_RECLINE2,		0x6c, 7, 4, 0x6c, 3, 4),
+MIX_ENT(ES_REC_MIXER_RECLINE3,		0x00, 0, 0, 0x00, 0, 0)
+};
+
+static int ess_has_rec_mixer (int submodel)
+{
+	switch (submodel) {
+	case SUBMDL_ES1887:
+		return 1;
+	default:
+		return 0;
+	};
+};
+
+#ifdef FKS_LOGGING
+static int ess_mixer_mon_regs[]
+	= { 0x70, 0x71, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7d, 0x7f
+	  , 0xa1, 0xa2, 0xa4, 0xa5, 0xa8, 0xa9
+	  , 0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb7, 0xb9
+	  , 0x00};
+
+static void ess_show_mixerregs (sb_devc *devc)
+{
+	int *mp = ess_mixer_mon_regs;
+
+return;
+
+	while (*mp != 0) {
+		printk (KERN_INFO "res (%x)=%x\n", *mp, (int)(ess_getmixer (devc, *mp)));
+		mp++;
+	}
+}
+#endif
+
+void ess_setmixer (sb_devc * devc, unsigned int port, unsigned int value)
+{
+	unsigned long flags;
+
+#ifdef FKS_LOGGING
+printk(KERN_INFO "FKS: write mixer %x: %x\n", port, value);
+#endif
+
+	save_flags(flags);
+	cli();
+	if (port >= 0xa0) {
+		ess_write (devc, port, value);
+	} else {
+		outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+
+		udelay(20);
+		outb(((unsigned char) (value & 0xff)), MIXER_DATA);
+		udelay(20);
+	};
+	restore_flags(flags);
+}
+
+unsigned int ess_getmixer (sb_devc * devc, unsigned int port)
+{
+	unsigned int val;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	if (port >= 0xa0) {
+		val = ess_read (devc, port);
+	} else {
+		outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+
+		udelay(20);
+		val = inb(MIXER_DATA);
+		udelay(20);
+	}
+	restore_flags(flags);
+
+	return val;
+}
+
+static void ess_chgmixer
+	(sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val)
+{
+	int value;
+
+	value = ess_getmixer (devc, reg);
+	value = (value & ~mask) | (val & mask);
+	ess_setmixer (devc, reg, value);
+}
+
+/*
+ * ess_mixer_init must be called from sb_mixer_init
+ */
+void ess_mixer_init (sb_devc * devc)
+{
+	devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
+
+	/*
+	* Take care of ES1887 specifics...
+	*/
+	switch (devc->submodel) {
+	case SUBMDL_ES1887:
+		devc->supported_devices		= ES1887_MIXER_DEVICES;
+		devc->supported_rec_devices	= ES1887_RECORDING_DEVICES;
+#ifdef FKS_LOGGING
+printk (KERN_INFO "FKS: ess_mixer_init dup = %d\n", devc->duplex);
+#endif
+		if (devc->duplex) {
+			devc->iomap				= &es1887_mix;
+		} else {
+			devc->iomap				= &es_rec_mix;
+		}
+		break;
+	default:
+		if (devc->submodel < 8) {
+			devc->supported_devices		= ES688_MIXER_DEVICES;
+			devc->supported_rec_devices	= ES688_RECORDING_DEVICES;
+			devc->iomap					= &es688_mix;
+		} else {
+			/*
+			 * es1688 has 4 bits master vol.
+			 * later chips have 6 bits (?)
+			 */
+			devc->supported_devices		= ES1688_MIXER_DEVICES;
+			devc->supported_rec_devices	= ES1688_RECORDING_DEVICES;
+			if (devc->submodel < 0x10) {
+				devc->iomap				= &es1688_mix;
+			} else {
+				devc->iomap				= &es1688later_mix;
+			}
+		}
+	}
+}
+
+/*
+ * Changing playback levels at an ESS chip with record mixer means having to
+ * take care of recording levels of recorded inputs (devc->recmask) too!
+ */
+int ess_mixer_set(sb_devc *devc, int dev, int left, int right)
+{
+	if (ess_has_rec_mixer (devc->submodel) && (devc->recmask & (1 << dev))) {
+		sb_common_mixer_set (devc, dev + ES_REC_MIXER_RECDIFF, left, right);
+	}
+	return sb_common_mixer_set (devc, dev, left, right);
+}
+
+/*
+ * After a sb_dsp_reset extended register 0xb4 (RECLEV) is reset too. After
+ * sb_dsp_reset RECLEV has to be restored. This is where ess_mixer_reload
+ * helps.
+ */
+void ess_mixer_reload (sb_devc *devc, int dev)
+{
+	int left, right, value;
+
+	value = devc->levels[dev];
+	left  = value & 0x000000ff;
+	right = (value & 0x0000ff00) >> 8;
+
+	sb_common_mixer_set(devc, dev, left, right);
+}
+
+int es_rec_set_recmask(sb_devc * devc, int mask)
+{
+	int i, i_mask, cur_mask, diff_mask;
+	int value, left, right;
+
+#ifdef FKS_LOGGING
+printk (KERN_INFO "FKS: es_rec_set_recmask mask = %x\n", mask);
+#endif
+	/*
+	 * Changing the recmask on an ESS chip with recording mixer means:
+	 * (1) Find the differences
+	 * (2) For "turned-on"  inputs: make the recording level the playback level
+	 * (3) For "turned-off" inputs: make the recording level zero
+	 */
+	cur_mask  = devc->recmask;
+	diff_mask = (cur_mask ^ mask);
+
+	for (i = 0; i < 32; i++) {
+		i_mask = (1 << i);
+		if (diff_mask & i_mask) {	/* Difference? (1)  */
+			if (mask & i_mask) {	/* Turn it on  (2)  */
+				value = devc->levels[i];
+				left  = value & 0x000000ff;
+				right = (value & 0x0000ff00) >> 8;
+			} else {				/* Turn it off (3)  */
+				left  = 0;
+				left  = 0;
+				right = 0;
+			}
+			sb_common_mixer_set(devc, i + ES_REC_MIXER_RECDIFF, left, right);
+		}
+	}
+	return mask;
+}
+
+int ess_set_recmask(sb_devc * devc, int *mask)
+{
+	/* This applies to ESS chips with record mixers only! */
+
+	if (ess_has_rec_mixer (devc->submodel)) {
+		*mask	= es_rec_set_recmask (devc, *mask);
+		return 1;									/* Applied		*/
+	} else {
+		return 0;									/* Not applied	*/
+	}
+}
+
+/*
+ * ess_mixer_reset must be called from sb_mixer_reset
+ */
+int ess_mixer_reset (sb_devc * devc)
+{
+	/*
+	 * Separate actions for ESS chips with a record mixer:
+	 */
+	if (ess_has_rec_mixer (devc->submodel)) {
+		switch (devc->submodel) {
+		case SUBMDL_ES1887:
+			/*
+			 * Separate actions for ES1887:
+			 * Change registers 7a and 1c to make the record mixer the
+			 * actual recording source.
+			 */
+			ess_chgmixer(devc, 0x7a, 0x18, 0x08);
+			ess_chgmixer(devc, 0x1c, 0x07, 0x07);
+			break;
+		};
+		/*
+		 * Call set_recmask for proper initialization
+		 */
+		devc->recmask = devc->supported_rec_devices;
+		es_rec_set_recmask(devc, 0);
+		devc->recmask = 0;
+
+		return 1;	/* We took care of recmask.				*/
+	} else {
+		return 0;	/* We didn't take care; caller do it	*/
+	}
+}
+
+/****************************************************************************
+ *																			*
+ *								ESS midi									*
+ *																			*
+ ****************************************************************************/
+
+/*
+ * FKS: IRQ may be shared. Hm. And if so? Then What?
+ */
+int ess_midi_init(sb_devc * devc, struct address_info *hw_config)
+{
+	unsigned char   cfg, tmp;
+
+	cfg = ess_getmixer (devc, 0x40) & 0x03;
+
+	if (devc->submodel < 8) {
+		ess_setmixer (devc, 0x40, cfg | 0x03);	/* Enable OPL3 & joystick */
+		return 0;  					 /* ES688 doesn't support MPU401 mode */
+	}
+	tmp = (hw_config->io_base & 0x0f0) >> 4;
+
+	if (tmp > 3) {
+		ess_setmixer (devc, 0x40, cfg);
+		return 0;
+	}
+	cfg |= tmp << 3;
+
+	tmp = 1;		/* MPU enabled without interrupts */
+
+	/* May be shared: if so the value is -ve */
+
+	switch (abs(hw_config->irq)) {
+		case 9:
+			tmp = 0x4;
+			break;
+		case 5:
+			tmp = 0x5;
+			break;
+		case 7:
+			tmp = 0x6;
+			break;
+		case 10:
+			tmp = 0x7;
+			break;
+		default:
+			return 0;
+	}
+
+	cfg |= tmp << 5;
+	ess_setmixer (devc, 0x40, cfg | 0x03);
+
+	return 1;
+}
+
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/sb_ess.h linux/drivers/sound/sb_ess.h
--- v2.2.0-pre7/linux/drivers/sound/sb_ess.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/sb_ess.h	Thu Jan 14 22:59:47 1999
@@ -0,0 +1,34 @@
+/*
+ * Created: 9-Jan-1999 Rolf Fokkens
+ */
+
+extern void ess_intr
+		(sb_devc *devc);
+extern int ess_dsp_init
+		(sb_devc *devc, struct address_info *hw_config);
+
+extern struct audio_driver *ess_audio_init
+		(sb_devc *devc, int *audio_flags, int *format_mask);
+extern int ess_midi_init
+		(sb_devc *devc, struct address_info *hw_config);
+extern void ess_mixer_init
+		(sb_devc *devc);
+
+extern int ess_init
+		(sb_devc *devc, struct address_info *hw_config);
+extern int ess_dsp_reset
+		(sb_devc *devc);
+
+extern void ess_setmixer
+		(sb_devc *devc, unsigned int port, unsigned int value);
+extern unsigned int ess_getmixer
+		(sb_devc *devc, unsigned int port);
+extern int ess_mixer_set
+		(sb_devc *devc, int dev, int left, int right);
+extern int ess_mixer_reset
+		(sb_devc *devc);
+extern void ess_mixer_reload
+		(sb_devc * devc, int dev);
+extern int ess_set_recmask
+		(sb_devc *devc, int *mask);
+
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/sb_mixer.c linux/drivers/sound/sb_mixer.c
--- v2.2.0-pre7/linux/drivers/sound/sb_mixer.c	Fri Jan  8 22:36:11 1999
+++ linux/drivers/sound/sb_mixer.c	Thu Jan 14 22:59:47 1999
@@ -1,4 +1,3 @@
-
 /*
  * sound/sb_mixer.c
  *
@@ -12,84 +11,8 @@
  * for more info.
  *
  *
- * Thomas Sailer		: ioctl code reworked (vmalloc/vfree removed)
- * Rolf Fokkens (Dec 20 1998)	: ES188x recording level support on a per
- *				  input basis.
- *              (Dec 24 1998)	: Recognition of ES1788, ES1887, ES1888,
- *				  ES1868, ES1869 and ES1878. Could be used for
- *				  specific handling in the future. All except
- *				  ES1887 and ES1888 and ES688 are handled like
- *				  ES1688.
- *              (Dec 27 1998)	: RECLEV for all (?) ES1688+ chips. ES188x now
- *				  have the "Dec 20" support + RECLEV
- */
-
-/*
- * About the documentation
- *
- * I don't know if the chips all are OK, but the documentation is buggy. 'cause
- * I don't have all the cips myself, there's a lot I cannot verify. I'll try to
- * keep track of my latest insights about his here. If you have additional info,
- * please enlighten me (fokkensr@vertis.nl)!
- *
- * I had the impression that ES1688 also has 6 bit master volume control. The
- * documentation about ES1888 (rev C, october '95) claims that ES1888 has
- * the following features ES1688 doesn't have:
- * - 6 bit master volume
- * - Full Duplex
- * So ES1688 apparently doesn't have 6 bit master volume control, but the
- * ES1688 does have RECLEV control. Makes me wonder: does ES688 have it too?
- * Without RECLEV ES688 won't be much fun I guess.
- *
- * From the ES1888 (rev C, october '95) documentation I got the impression
- * that registers 0x68 to 0x6e don't exist which means: no recording volume
- * controls. To my surprise the ES888 documentation (1/14/96) claims that
- * ES888 does have these record mixer registers, but that ES1888 doesn't have
- * 0x69 and 0x6b. So the rest should be there.
- * 
- */
-
-/*
- * About recognition of ESS chips
- *
- * The distinction of ES688, ES1688, ES1788, ES1887 and ES1888 is described in
- * a (preliminary ??) datasheet on ES1887. It's aim is to identify ES1887, but
- * during detection the text claims that "this chip may be ..." when a step
- * fails. This scheme is used to distinct between the above chips.
- * It appears however that some PnP chips like ES1868 are recognized as ES1788
- * by the ES1887 detection scheme. These PnP chips can be detected in another
- * way however: ES1868, ES1869 and ES1878 can be recognized (full proof I think)
- * by repeatedly reading mixer register 0x40. This is done by ess_identify in
- * sb_common.c.
- * This results in the following detection steps:
- * - distinct between ES688 and ES1688+ (as always done in this driver)
- *   if ES688 we're ready
- * - try to detect ES1868, ES1869 or ES1878
- *   if successful we're ready
- * - try to detect ES1888, ES1887 or ES1788
- *   if successful we're ready
- * - Dunno. Must be 1688. Will do in general
- *
- * About RECLEV support:
- *
- * The existing ES1688 support didn't take care of the ES1688+ recording
- * levels very well. Whenever a device was selected (recmask) for recording
- * it's recording level was loud, and it couldn't be changed. The fact that
- * internal register 0xb4 could take care of RECLEV, didn't work meaning until
- * it's value was restored every time the chip was reset; this reset the
- * value of 0xb4 too. I guess that's what 4front also had (have?) trouble with.
- *
- * About ES188x support:
- *
- * The ES188x has separate registers to control the recording levels, for all
- * inputs. The ES188x specific software makes these levels the same as their
- * corresponding playback levels, unless recmask says they aren't recorded. In
- * the latter case the recording volumes are 0.
- * Now recording levels of inputs can be controlled, by changing the playback
- * levels. Futhermore several devices can be recorded together (which is not
- * possible with the ES1688.
- * Besides the separate recording level control for each input, the common
- * recordig level can also be controlled by RECLEV as described above.
+ * Thomas Sailer				: ioctl code reworked (vmalloc/vfree removed)
+ * Rolf Fokkens (Dec 20 1998)	: Moved ESS stuff into sb_ess.[ch]
  */
 
 #include <linux/config.h>
@@ -101,6 +24,8 @@
 #include "sb.h"
 #include "sb_mixer.h"
 
+#include "sb_ess.h"
+
 #define SBPRO_RECORDING_DEVICES	(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
 
 /* Same as SB Pro, unless I find otherwise */
@@ -123,16 +48,6 @@
 #define SB16_OUTFILTER_DEVICES		(SOUND_MASK_LINE | SOUND_MASK_MIC | \
 					 SOUND_MASK_CD)
 
-#define ES688_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
-#define ES688_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_LINE2|SOUND_MASK_SPEAKER)
-
-#define ES1688_RECORDING_DEVICES ES688_RECORDING_DEVICES
-#define ES1688_MIXER_DEVICES (ES688_MIXER_DEVICES|SOUND_MASK_RECLEV)
-
-#define ES188X_RECORDING_DEVICES	(ES1688_RECORDING_DEVICES | SOUND_MASK_LINE2 \
-					 |SOUND_MASK_SYNTH)
-#define ES188X_MIXER_DEVICES (ES1688_MIXER_DEVICES)
-
 #define SB16_MIXER_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
 					 SOUND_MASK_CD | \
 					 SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
@@ -147,31 +62,6 @@
 				 SOUND_MASK_CD | \
 				 SOUND_MASK_VOLUME)
 
-/*
- * Mixer registers of ES188x
- *
- * These registers specifically take care of recording levels. To make the
- * mapping from playback devices to recording devices every recording
- * devices = playback device + ES188X_MIXER_RECDIFF
- */
-#define ES188X_MIXER_RECBASE	(SOUND_MIXER_LINE3 + 1)
-#define ES188X_MIXER_RECDIFF	(ES188X_MIXER_RECBASE - SOUND_MIXER_SYNTH)
-
-#define ES188X_MIXER_RECSYNTH	(SOUND_MIXER_SYNTH	+ ES188X_MIXER_RECDIFF)
-#define ES188X_MIXER_RECPCM	(SOUND_MIXER_PCM	+ ES188X_MIXER_RECDIFF)
-#define ES188X_MIXER_RECSPEAKER	(SOUND_MIXER_SPEAKER	+ ES188X_MIXER_RECDIFF)
-#define ES188X_MIXER_RECLINE	(SOUND_MIXER_LINE	+ ES188X_MIXER_RECDIFF)
-#define ES188X_MIXER_RECMIC	(SOUND_MIXER_MIC	+ ES188X_MIXER_RECDIFF)
-#define ES188X_MIXER_RECCD	(SOUND_MIXER_CD		+ ES188X_MIXER_RECDIFF)
-#define ES188X_MIXER_RECIMIX	(SOUND_MIXER_IMIX	+ ES188X_MIXER_RECDIFF)
-#define ES188X_MIXER_RECALTPCM	(SOUND_MIXER_ALTPCM	+ ES188X_MIXER_RECDIFF)
-#define ES188X_MIXER_RECRECLEV	(SOUND_MIXER_RECLEV	+ ES188X_MIXER_RECDIFF)
-#define ES188X_MIXER_RECIGAIN	(SOUND_MIXER_IGAIN	+ ES188X_MIXER_RECDIFF)
-#define ES188X_MIXER_RECOGAIN	(SOUND_MIXER_OGAIN	+ ES188X_MIXER_RECDIFF)
-#define ES188X_MIXER_RECLINE1	(SOUND_MIXER_LINE1	+ ES188X_MIXER_RECDIFF)
-#define ES188X_MIXER_RECLINE2	(SOUND_MIXER_LINE2	+ ES188X_MIXER_RECDIFF)
-#define ES188X_MIXER_RECLINE3	(SOUND_MIXER_LINE3	+ ES188X_MIXER_RECDIFF)
-
 static mixer_tab sbpro_mix = {
 MIX_ENT(SOUND_MIXER_VOLUME,	0x22, 7, 4, 0x22, 3, 4),
 MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
@@ -187,115 +77,6 @@
 MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0)
 };
 
-static mixer_tab es688_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME,	0x32, 7, 4, 0x32, 3, 4),
-MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH,	0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM,	0x14, 7, 4, 0x14, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER,	0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE,	0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC,	0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD,		0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_IGAIN,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2,	0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3,	0x00, 0, 0, 0x00, 0, 0)
-};
-
-/*
- * The ES1688 specifics... hopefully correct...
- * - 6 bit master volume
- *   I was wrong, ES1888 docs say ES1688 didn't have it.
- * - RECLEV control
- * These may apply to ES688 too. I have no idea.
- */
-static mixer_tab es1688_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME,	0x32, 7, 4, 0x32, 3, 4),
-MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH,	0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM,	0x14, 7, 4, 0x14, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER,	0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE,	0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC,	0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD,		0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV,	0xb4, 7, 4, 0xb4, 3, 4),
-MIX_ENT(SOUND_MIXER_IGAIN,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2,	0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3,	0x00, 0, 0, 0x00, 0, 0)
-};
-
-static mixer_tab es1688later_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME,     0x60, 5, 6, 0x62, 5, 6),
-MIX_ENT(SOUND_MIXER_BASS,       0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE,     0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH,      0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM,        0x14, 7, 4, 0x14, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER,    0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE,       0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC,        0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD,         0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX,       0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM,     0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV,     0xb4, 7, 4, 0xb4, 3, 4),
-MIX_ENT(SOUND_MIXER_IGAIN,      0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN,      0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1,      0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2,      0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3,      0x00, 0, 0, 0x00, 0, 0)
-};
-
-/*
- * The ES188x specifics.
- * Note that de master volume unlike ES688 is now controlled by two 6 bit
- * registers. These seem to work OK on 1868 too.
- * Also Note that the recording levels (ES188X_MIXER_REC...) have own 
- * entries as if they were playback devices. They are used internally in the
- * driver only!
- */
-static mixer_tab es188x_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME,	0x60, 5, 6, 0x62, 5, 6),
-MIX_ENT(SOUND_MIXER_BASS,       0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE,     0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH,      0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM,        0x14, 7, 4, 0x14, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER,    0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE,       0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC,        0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD,         0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX,       0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM,     0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV,	0xb4, 7, 4, 0xb4, 3, 4),
-MIX_ENT(SOUND_MIXER_IGAIN,      0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN,      0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1,      0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2,      0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3,      0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECSYNTH,	0x6b, 7, 4, 0x6b, 3, 4),
-MIX_ENT(ES188X_MIXER_RECPCM,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECSPEAKER,0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECLINE,	0x6e, 7, 4, 0x6e, 3, 4),
-MIX_ENT(ES188X_MIXER_RECMIC,	0x68, 7, 4, 0x68, 3, 4),
-MIX_ENT(ES188X_MIXER_RECCD,	0x6a, 7, 4, 0x6a, 3, 4),
-MIX_ENT(ES188X_MIXER_RECIMIX,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECALTPCM,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECRECLEV,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECIGAIN,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECOGAIN,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECLINE1,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECLINE2,	0x6c, 7, 4, 0x6c, 3, 4),
-MIX_ENT(ES188X_MIXER_RECLINE3,	0x00, 0, 0, 0x00, 0, 0)
-};
-
 #ifdef	__SGNXPRO__
 #if 0
 static mixer_tab sgnxpro_mix = { 	/* not used anywhere */
@@ -460,20 +241,9 @@
 
 static void     sb_mixer_reset(sb_devc * devc);
 
-inline void sb_mixer_bits
-	(sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val)
-{
-	int value;
-
-	value = sb_getmixer(devc, reg);
-	value = (value & ~mask) | (val & mask);
-	sb_setmixer(devc, reg, value);
-}
-
-
 void sb_mixer_set_stereo(sb_devc * devc, int mode)
 {
-	sb_mixer_bits(devc, OUT_FILTER, STEREO_DAC, (mode ? STEREO_DAC : MONO_DAC));
+	sb_chgmixer(devc, OUT_FILTER, STEREO_DAC, (mode ? STEREO_DAC : MONO_DAC));
 }
 
 static int detect_mixer(sb_devc * devc)
@@ -520,7 +290,7 @@
 	sb_mixer_reset(devc);
 }
 
-static int common_mixer_set(sb_devc * devc, int dev, int left, int right)
+int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right)
 {
 	int regoffs;
 	unsigned char val;
@@ -558,34 +328,6 @@
 	return left | (right << 8);
 }
 
-/*
- * After a sb_dsp_reset extended register 0xb4 (RECLEV) is reset too. After
- * sb_dsp_reset RECLEV has to be restored. This is where ess_mixer_reload
- * helps.
- */
-void ess_mixer_reload (sb_devc * devc, int dev)
-{
-	int left, right, value;
-
-	value = devc->levels[dev];
-	left  = value & 0x000000ff;
-    	right = (value & 0x0000ff00) >> 8;
-
-	common_mixer_set(devc, dev, left, right);
-}
-
-/*
- * Changing playback levels at ES188x means having to take care of recording
- * levels of recorded inputs (devc->recmask) too!
- */
-static int es188x_mixer_set(sb_devc * devc, int dev, int left, int right)
-{
-	if (devc->recmask & (1 << dev)) {
-		common_mixer_set(devc, dev + ES188X_MIXER_RECDIFF, left, right);
-	}
-	return common_mixer_set(devc, dev, left, right);
-}
-
 static int smw_mixer_set(sb_devc * devc, int dev, int left, int right)
 {
 	int reg, val;
@@ -649,14 +391,10 @@
 		retval = smw_mixer_set(devc, dev, left, right);
 		break;
 	case MDL_ESS:
-		if (devc->submodel == SUBMDL_ES188X) {
-			retval = es188x_mixer_set(devc, dev, left, right);
-		} else {
-			retval = common_mixer_set(devc, dev, left, right);
-		}
+		retval = ess_mixer_set(devc, dev, left, right);
 		break;
 	default:
-		retval = common_mixer_set(devc, dev, left, right);
+		retval = sb_common_mixer_set(devc, dev, left, right);
 	}
 	if (retval >= 0) devc->levels[dev] = retval;
 
@@ -671,37 +409,6 @@
 	sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7));
 }
 
-/*
- * Changing the recmask on a ES188x means:
- * (1) Find the differences
- * (2) For "turned-on"  inputs: make the recording level the playback level
- * (3) For "turned-off" inputs: make the recording level zero
- */
-static int es188x_set_recmask(sb_devc * devc, int mask)
-{
-	int i, i_mask, cur_mask, diff_mask;
-	int value, left, right;
-
-	cur_mask  = devc->recmask;
-	diff_mask = (cur_mask ^ mask);
-
-	for (i = 0; i < 32; i++) {
-		i_mask = (1 << i);
-		if (diff_mask & i_mask) {	/* Difference? (1)	*/
-			if (mask & i_mask) {	/* Turn it on  (2)	*/
-				value = devc->levels[i];
-				left  = value & 0x000000ff;
-    				right = (value & 0x0000ff00) >> 8;
-			} else {		/* Turn it off (3)	*/
-				left  = 0;
-				right = 0;
-			}
-			common_mixer_set(devc, i + ES188X_MIXER_RECDIFF, left, right);
-		}
-	}
-	return mask;
-}
-
 static int set_recmask(sb_devc * devc, int mask)
 {
 	int devmask, i;
@@ -715,15 +422,10 @@
 		case MDL_ESS:
 		case MDL_JAZZ:
 		case MDL_SMW:
-			if (devc->model == MDL_ESS &&
-				devc->submodel == SUBMDL_ES188X) {
-				/*
-				 * ES188x needs a separate approach
-				 */
-				devmask = es188x_set_recmask(devc, devmask);
+			if (devc->model == MDL_ESS && ess_set_recmask (devc, &devmask)) {
 				break;
 			};
-
+printk (KERN_INFO "FKS: set_recmask not handled by ess_set_recmask\n");
 			if (devmask != SOUND_MASK_MIC &&
 				devmask != SOUND_MASK_LINE &&
 				devmask != SOUND_MASK_CD)
@@ -952,20 +654,9 @@
 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
 		sb_mixer_set(devc, i, devc->levels[i]);
 
-	/*
-	 * Separate actions for ES188x:
-	 * Change registers 7a and 1c to make the record mixer the
-	 * actual recording source.
-	 * Then call set_recmask twice to do extra ES188x initializations
-	 */
-	if (devc->model == MDL_ESS && devc->submodel == SUBMDL_ES188X) {
-		sb_mixer_bits(devc, 0x7a, 0x18, 0x08);
-		sb_mixer_bits(devc, 0x1c, 0x07, 0x07);
-
-		set_recmask(devc, ES188X_RECORDING_DEVICES);
-		set_recmask(devc, 0);
-	}
-	set_recmask(devc, SOUND_MASK_MIC);
+	if (devc->model != MDL_ESS || !ess_mixer_reset (devc)) {
+		set_recmask(devc, SOUND_MASK_MIC);
+	};
 }
 
 int sb_mixer_init(sb_devc * devc)
@@ -993,43 +684,7 @@
 			break;
 
 		case MDL_ESS:
-			devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
-
-			/*
-			 * Take care of ES188x specifics...
-			 */
-			switch (devc->submodel) {
-			case SUBMDL_ES188X:
-				devc->supported_devices
-					= ES188X_MIXER_DEVICES;
-				devc->supported_rec_devices
-					= ES188X_RECORDING_DEVICES;
-				devc->iomap = &es188x_mix;
-				break;
-			default:
-				if (devc->submodel < 8) {
-					devc->supported_devices
-						= ES688_MIXER_DEVICES;
-					devc->supported_rec_devices
-						= ES688_RECORDING_DEVICES;
-					devc->iomap = &es688_mix;
-				} else {
-					/*
-					 * es1688 has 4 bits master vol.
-					 * later chips have 6 bits (?)
-					 */
-					devc->supported_devices
-						= ES1688_MIXER_DEVICES;
-					devc->supported_rec_devices
-						= ES1688_RECORDING_DEVICES;
-					if (devc->submodel < 0x10) {
-						devc->iomap = &es1688_mix;
-					} else {
-						devc->iomap = &es1688later_mix;
-					}
-				}
-			}
-
+			ess_mixer_init (devc);
 			break;
 
 		case MDL_SMW:
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c
--- v2.2.0-pre7/linux/drivers/sound/sequencer.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/sound/sequencer.c	Fri Jan 15 17:46:27 1999
@@ -16,9 +16,7 @@
  */
 #include <linux/config.h>
 
-#ifdef CONFIG_KMOD
 #include <linux/kmod.h>
-#endif
 
 
 #define SEQUENCER_C
@@ -998,10 +996,8 @@
 	if (dev)		/* Patch manager device (obsolete) */
 		return -ENXIO;
 
-#ifdef CONFIG_KMOD
 	if(synth_devs[dev] == NULL)
 		request_module("synth0");
-#endif
 
 	if (mode == OPEN_READ)
 	{
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/skeleton.c linux/drivers/sound/skeleton.c
--- v2.2.0-pre7/linux/drivers/sound/skeleton.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/skeleton.c	Thu Jan 14 22:59:47 1999
@@ -0,0 +1,224 @@
+/*
+ *	PCI sound skeleton example
+ *
+ *	(c) 1998 Red Hat Software
+ *
+ *	This software may be used and distributed according to the 
+ *	terms of the GNU Public License, incorporated herein by 
+ *	reference.
+ *
+ *	This example is designed to be built in the linux/drivers/sound
+ *	directory as part of a kernel build. The example is modular only
+ *	drop me a note once you have a working modular driver and want
+ *	to integrate it with the main code.
+ *		-- Alan <alan@redhat.com>
+ *
+ *	This is a first draft. Please report any errors, corrections or
+ *	improvements to me.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include <asm/io.h>
+
+#include "sound_config.h"
+#include "soundmodule.h"
+
+/*
+ *	Define our PCI vendor ID here
+ */
+ 
+#ifndef PCI_VENDOR_MYIDENT
+#define PCI_VENDOR_MYIDENT			0x125D
+
+/*
+ *	PCI identity for the card.
+ */
+ 
+#define PCI_DEVICE_ID_MYIDENT_MYCARD1		0x1969
+#endif
+
+#define CARD_NAME	"ExampleWave 3D Pro Ultra ThingyWotsit"
+
+#define MAX_CARDS	8
+
+/*
+ *	Each address_info object holds the information about one of
+ *	our card resources. In this case the MSS emulation of our
+ *	ficticious card. Its used to manage and attach things.
+ */
+ 
+static struct address_info	mss_data[MAX_CARDS];
+static int 			cards = 0;
+
+/*
+ *	Install the actual card. This is an example
+ */
+
+static int mycard_install(struct pci_dev *pcidev)
+{
+	int iobase;
+	int mssbase;
+	int mpubase;
+	u8 x;
+	u16 w;
+	u32 v;
+	int i;
+	int dma;
+
+	/*
+	 *	Our imaginary code has its I/O on PCI address 0, a
+	 *	MSS on PCI address 1 and an MPU on address 2
+	 *
+	 *	For the example we will only initialise the MSS
+	 */
+	 	
+	iobase = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+	mssbase = pcidev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
+	mpubase = pcidev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK;
+	
+	/*
+	 *	Reset the board
+	 */
+	 
+	/*
+	 *	Wait for completion. udelay() waits in microseconds
+	 */
+	 
+	udelay(100);
+	
+	/*
+	 *	Ok card ready. Begin setup proper. You might for example
+	 *	load the firmware here
+	 */
+	
+	dma = card_specific_magic(ioaddr);
+	
+	/*
+	 *	Turn on legacy mode (example), There are also byte and
+	 *	dword (32bit) PCI configuration function calls
+	 */
+
+	pci_read_config_word(pcidev, 0x40, &w);
+	w&=~(1<<15);			/* legacy decode on */
+	w|=(1<<14);			/* Reserved write as 1 in this case */
+	w|=(1<<3)|(1<<1)|(1<<0);	/* SB on , FM on, MPU on */
+	pci_write_config_word(pcidev, 0x40, w);
+	
+	/*
+	 *	Let the user know we found his toy.
+	 */
+	 
+	printk(KERN_INFO "Programmed "CARD_NAME" at 0x%X to legacy mode.\n",
+		iobase);
+		
+	/*
+	 *	Now set it up the description of the card
+	 */
+	 
+	mss_data[cards].io_base = mssbase;
+	mss_data[cards].irq = pcidev->irq;
+	mss_data[cards].dma = dma;
+	
+	/*
+	 *	Check there is an MSS present
+	 */
+
+	if(ad1848_detect(mssbase, NULL, mss_data[cards].osp)==0)
+		return 0;
+		
+	/*
+	 *	Initialize it
+	 */
+	 
+	mss_data[cards].slots[3] = ad1848_init("MyCard MSS 16bit", 
+			mssbase, mss_data[cards].irq);
+
+	cards++;	
+	return 1;
+}
+
+
+/*
+ * 	This loop walks the PCI configuration database and finds where
+ *	the sound cards are.
+ */
+ 
+int init_mycard(void)
+{
+	struct pci_dev *pcidev=NULL;
+	int count=0;
+		
+	if(!pci_present())
+		return -ENODEV;
+	
+		
+	while((pcidev = pci_find_device(PCI_VENDOR_MYIDENT, PCI_DEVICE_ID_MYIDENT_MYCARD1, pcidev))!=NULL)
+	{
+		count+=mycard_install(pcidev);
+		if(count)
+			return 0;
+		if(count==MAX_CARDS)
+			break;
+	}
+	
+	if(count==0)
+		return -ENODEV;
+	return 0;
+}
+
+/*
+ *	This function is called when the user or kernel loads the 
+ *	module into memory.
+ */
+
+
+int init_module(void)
+{
+	if(init_mycard()<0)
+	{
+		printk(KERN_ERR "No "CARD_NAME" cards found.\n");
+		return -ENODEV;
+	}
+	/*
+	 *	Binds us to the sound subsystem	
+	 */
+	SOUND_LOCK;
+	return 0;
+}
+
+/*
+ *	This is called when it is removed. It will only be removed 
+ *	when its use count is 0. For sound the SOUND_LOCK/SOUND_UNLOCK
+ *	macros hide the entire work for this.
+ */
+ 
+void cleanup_module(void)
+{
+	for(i=0;i< cards; i++)
+	{
+		/*
+		 *	Free attached resources
+		 */
+		 
+		ad1848_unload(mss_data[i].io_base,
+			      mss_data[i].irq,
+			      mss_data[i].dma,
+			      mss_data[i].dma,
+			      0);
+		/*
+		 *	And disconnect the device from the kernel
+		 */
+		sound_unload_audiodevice(mss_data[i].slots[3]);
+	}
+	/*
+	 *	Final clean up with the sound layer
+	 */
+	SOUND_LOCK_END;
+}
+
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/sound_core.c linux/drivers/sound/sound_core.c
--- v2.2.0-pre7/linux/drivers/sound/sound_core.c	Fri Jan  8 22:36:12 1999
+++ linux/drivers/sound/sound_core.c	Fri Jan 15 17:46:27 1999
@@ -43,6 +43,7 @@
 #include <linux/fs.h>
 #include <linux/sound.h>
 #include <linux/major.h>
+#include <linux/kmod.h>
  
  
 struct sound_unit
@@ -317,7 +318,6 @@
 	
 	spin_lock(&sound_loader_lock);
 	s = __look_for_unit(chain, unit);
-#ifdef CONFIG_KMOD
 	if (s == NULL) {
 		char mod[32];
 	
@@ -336,7 +336,6 @@
 		spin_lock(&sound_loader_lock);
 		s = __look_for_unit(chain, unit);
 	}
-#endif
 	if (s) {
 		file->f_op=s->unit_fops;
 		spin_unlock(&sound_loader_lock);
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v2.2.0-pre7/linux/drivers/sound/soundcard.c	Mon Jan  4 15:08:17 1999
+++ linux/drivers/sound/soundcard.c	Fri Jan 15 17:46:27 1999
@@ -456,13 +456,11 @@
 
 	case SND_DEV_CTL:
 		dev >>= 4;
-#ifdef CONFIG_KMOD
 		if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) {
 			char modname[20];
 			sprintf(modname, "mixer%d", dev);
 			request_module(modname);
 		}
-#endif
 		if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL))
 			return -ENXIO;
 		break;
@@ -578,14 +576,12 @@
 {
  	if (mixdev < 0 || mixdev >= MAX_MIXER_DEV)
  		return -ENXIO;
-#ifdef CONFIG_KMOD
  	/* Try to load the mixer... */
  	if (mixer_devs[mixdev] == NULL) {
  		char modname[20];
  		sprintf(modname, "mixer%d", mixdev);
  		request_module(modname);
  	}
-#endif	/* CONFIG_KMOD */
  	if (mixdev >= num_mixers || !mixer_devs[mixdev])
  		return -ENXIO;
 	if (cmd == SOUND_MIXER_INFO)
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/wavfront.c linux/drivers/sound/wavfront.c
--- v2.2.0-pre7/linux/drivers/sound/wavfront.c	Wed Jan 13 15:00:43 1999
+++ linux/drivers/sound/wavfront.c	Thu Jan 14 22:59:47 1999
@@ -97,6 +97,7 @@
 #define LOOPS_PER_SEC current_cpu_data.loops_per_sec
 #endif
  
+#define _MIDI_SYNTH_C_
 #define MIDI_SYNTH_NAME	"WaveFront MIDI"
 #define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
 #include "midi_synth.h"
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/sound/wf_midi.c linux/drivers/sound/wf_midi.c
--- v2.2.0-pre7/linux/drivers/sound/wf_midi.c	Thu Jan  7 15:11:38 1999
+++ linux/drivers/sound/wf_midi.c	Thu Jan 14 22:59:47 1999
@@ -681,6 +681,7 @@
 	devc->mode = 0;
 }
 
+#define _MIDI_SYNTH_C_
 #define MIDI_SYNTH_NAME	"WaveFront (MIDI)"
 #define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
 #include "midi_synth.h"
diff -u --recursive --new-file v2.2.0-pre7/linux/drivers/video/matroxfb.c linux/drivers/video/matroxfb.c
--- v2.2.0-pre7/linux/drivers/video/matroxfb.c	Fri Jan  8 22:36:13 1999
+++ linux/drivers/video/matroxfb.c	Fri Jan 15 13:56:49 1999
@@ -543,6 +543,7 @@
 		const int*	vxres;
 		int		cross4MB;
 		int		text;
+		int		plnwt;
 			      } capable;
 	struct {
 		unsigned int	size;
@@ -1099,7 +1100,8 @@
 	mga_fifo(8);
 	mga_outl(M_PITCH, mpitch);
 	mga_outl(M_YDSTORG, curr_ydstorg(MINFO));
-	mga_outl(M_PLNWT, -1);
+	if (ACCESS_FBINFO(capable.plnwt))
+		mga_outl(M_PLNWT, -1);
 	mga_outl(M_OPMODE, mopmode);
 	mga_outl(M_CXBNDRY, 0xFFFF0000);
 	mga_outl(M_YTOP, 0);
@@ -2433,10 +2435,7 @@
 			}
 		}
 		dprintk(KERN_INFO "matroxfb: acceleration disabled\n");
-		p->dispsw = swtmp;
-		return;
-	}
-	if (p->type == FB_TYPE_TEXT) {
+	} else if (p->type == FB_TYPE_TEXT) {
 		swtmp = &matroxfb_text;
 	} else {
 		switch (p->var.bits_per_pixel) {
@@ -2473,7 +2472,6 @@
 			return;
 		}
 	}
-	dprintk(KERN_INFO "matroxfb: now accelerated\n");
 	memcpy(&ACCESS_FBINFO(dispsw), swtmp, sizeof(ACCESS_FBINFO(dispsw)));
 	p->dispsw = &ACCESS_FBINFO(dispsw);
 	if ((p->type != FB_TYPE_TEXT) && ACCESS_FBINFO(devflags.hwcursor)) {
@@ -3959,11 +3957,21 @@
 
 	DBG("MGAG100_preinit")
 	
+	/* there are some instabilities if in_div > 19 && vco < 61000 */
+	ACCESS_FBINFO(features.pll.vco_freq_min) = 62000;
+	ACCESS_FBINFO(features.pll.ref_freq)     = 27000;
+	ACCESS_FBINFO(features.pll.feed_div_min) = 7;
+	ACCESS_FBINFO(features.pll.feed_div_max) = 127;
+	ACCESS_FBINFO(features.pll.in_div_min)   = 1;
+	ACCESS_FBINFO(features.pll.in_div_max)   = 31;
+	ACCESS_FBINFO(features.pll.post_shift_max) = 3;
+	ACCESS_FBINFO(features.DAC1064.xvrefctrl) = DAC1064_XVREFCTRL_G100_DEFAULT;
 	/* ACCESS_FBINFO(capable.cfb4) = 0; ... preinitialized by 0 */
 	ACCESS_FBINFO(capable.text) = 1;
 	ACCESS_FBINFO(capable.vxres) = vxres_g100;
 	ACCESS_FBINFO(features.accel.has_cacheflush) = 1;
 	ACCESS_FBINFO(cursor.timer.function) = matroxfb_DAC1064_flashcursor;
+	ACCESS_FBINFO(capable.plnwt) = ACCESS_FBINFO(devflags.accelerator) != FB_ACCEL_MATROX_MGAG100;
 
 	if (ACCESS_FBINFO(devflags.noinit))
 		return 0;
@@ -3979,10 +3987,13 @@
 	pci_read_config_dword(ACCESS_FBINFO(pcidev), 0x50, &reg50);
 	reg50 &= ~0x3000;
 	pci_write_config_dword(ACCESS_FBINFO(pcidev), 0x50, reg50);
+
+	DAC1064_setmclk(PMINFO hw, DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PCI, 133333);
+
 	if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG100) {
-		hw->MXoptionReg |= 0x5080;
+		hw->MXoptionReg |= 0x1080;
 		pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
-		mga_outl(M_CTLWTST, 0x01032521);
+		mga_outl(M_CTLWTST, 0x00000300);
 		/* mga_outl(M_CTLWTST, 0x03258A31); */ 
 		udelay(100);
 		mga_outb(0x1C05, 0x00);
@@ -4002,9 +4013,11 @@
 		mga_writeb(ACCESS_FBINFO(video.vbase), 0x0000, 0xAA);
 		mga_writeb(ACCESS_FBINFO(video.vbase), 0x0800, 0x55);
 		mga_writeb(ACCESS_FBINFO(video.vbase), 0x4000, 0x55);
+#if 0
 		if (mga_readb(ACCESS_FBINFO(video.vbase), 0x0000) != 0xAA) {
 			hw->MXoptionReg &= ~0x1000;
 		}
+#endif
 	} else {
 		hw->MXoptionReg |= 0x00000C00;
 		if (ACCESS_FBINFO(devflags.sgram))
@@ -4028,15 +4041,6 @@
 
 	DBG("MGAG100_reset")
 
-	/* there are some instabilities if in_div > 19 && vco < 61000 */
-	ACCESS_FBINFO(features.pll.vco_freq_min) = 62000;
-	ACCESS_FBINFO(features.pll.ref_freq)     = 27000;
-	ACCESS_FBINFO(features.pll.feed_div_min) = 7;
-	ACCESS_FBINFO(features.pll.feed_div_max) = 127;
-	ACCESS_FBINFO(features.pll.in_div_min)   = 1;
-	ACCESS_FBINFO(features.pll.in_div_max)   = 31;
-	ACCESS_FBINFO(features.pll.post_shift_max) = 3;
-	ACCESS_FBINFO(features.DAC1064.xvrefctrl) = DAC1064_XVREFCTRL_G100_DEFAULT;
 	ACCESS_FBINFO(features.DAC1064.cursorimage) = ACCESS_FBINFO(video.len_usable) - 1024;
 	if (ACCESS_FBINFO(devflags.hwcursor)) 
 		ACCESS_FBINFO(video.len_usable) -= 1024;
@@ -4851,7 +4855,7 @@
 static int no_pci_retry = 0;		/* "matrox:nopciretry" */
 static int novga = 0;			/* "matrox:novga" */
 static int nobios = 0;			/* "matrox:nobios" */
-static int noinit = 1;			/* "matrox:noinit" */
+static int noinit = 1;			/* "matrox:init" */
 static int inverse = 0;			/* "matrox:inverse" */
 static int hwcursor = 1;		/* "matrox:nohwcursor" */
 static int blink = 1;			/* "matrox:noblink" */
@@ -5320,6 +5324,7 @@
 	ACCESS_FBINFO(max_pixel_clock) = b->maxclk;
 
 	printk(KERN_INFO "matroxfb: Matrox %s detected\n", b->name);
+	ACCESS_FBINFO(capable.plnwt) = 1;
 	ACCESS_FBINFO(devflags.video64bits) = b->flags & DEVF_VIDEO64BIT;
 	if (b->flags & DEVF_TEXT4B) {
 		ACCESS_FBINFO(devflags.vgastep) = 4;
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/Config.in linux/fs/Config.in
--- v2.2.0-pre7/linux/fs/Config.in	Thu Jan  7 15:11:38 1999
+++ linux/fs/Config.in	Thu Jan 14 10:29:28 1999
@@ -112,4 +112,6 @@
 fi
 endmenu
 
+source fs/nls/Config.in
+
 endmenu
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/autofs/dirhash.c linux/fs/autofs/dirhash.c
--- v2.2.0-pre7/linux/fs/autofs/dirhash.c	Wed Jan 13 15:00:43 1999
+++ linux/fs/autofs/dirhash.c	Thu Jan 14 11:43:07 1999
@@ -209,7 +209,7 @@
 void autofs_hash_dputall(struct autofs_dirhash *dh)
 {
 	int i;
-	struct autofs_dir_ent *ent, *nent;
+	struct autofs_dir_ent *ent;
 
 	for ( i = 0 ; i < AUTOFS_HASH_SIZE ; i++ ) {
 		for ( ent = dh->h[i] ; ent ; ent = ent->next ) {
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/buffer.c linux/fs/buffer.c
--- v2.2.0-pre7/linux/fs/buffer.c	Fri Jan  8 22:36:13 1999
+++ linux/fs/buffer.c	Mon Jan 18 00:03:43 1999
@@ -597,21 +597,9 @@
 struct buffer_head * get_hash_table(kdev_t dev, int block, int size)
 {
 	struct buffer_head * bh;
-	for (;;) {
-		bh = find_buffer(dev,block,size);
-		if (!bh)
-			break;
+	bh = find_buffer(dev,block,size);
+	if (bh)
 		bh->b_count++;
-		bh->b_lru_time = jiffies;
-		if (!buffer_locked(bh)) 
-			break;
-		__wait_on_buffer(bh);
-		if (bh->b_dev == dev		&&
-		    bh->b_blocknr == block	&&
-		    bh->b_size == size)
-			break;
-		bh->b_count--;
-	}
 	return bh;
 }
 
@@ -751,7 +739,6 @@
 	 * and that it's unused (b_count=0), unlocked, and clean.
 	 */
 	init_buffer(bh, dev, block, end_buffer_io_sync, NULL);
-	bh->b_lru_time	= jiffies;
 	bh->b_state=0;
 	insert_into_queues(bh);
 	return bh;
@@ -838,8 +825,6 @@
  */
 void __brelse(struct buffer_head * buf)
 {
-	wait_on_buffer(buf);
-
 	/* If dirty, mark the time this buffer should be written back. */
 	set_writetime(buf, 0);
 	refile_buffer(buf);
@@ -858,7 +843,6 @@
  */
 void __bforget(struct buffer_head * buf)
 {
-	wait_on_buffer(buf);
 	mark_buffer_clean(buf);
 	clear_bit(BH_Protected, &buf->b_state);
 	remove_from_hash_queue(buf);
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/dcache.c linux/fs/dcache.c
--- v2.2.0-pre7/linux/fs/dcache.c	Fri Jan  8 22:36:13 1999
+++ linux/fs/dcache.c	Sun Jan 17 12:43:49 1999
@@ -175,7 +175,7 @@
 	}
 
 	/*
-	 * Somebody still using it?
+	 * Somebody else still using it?
 	 *
 	 * If it's a directory, we can't drop it
 	 * for fear of somebody re-populating it
@@ -184,7 +184,7 @@
 	 * we might still populate it if it was a
 	 * working directory or similar).
 	 */
-	if (dentry->d_count) {
+	if (dentry->d_count > 1) {
 		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode))
 			return -EBUSY;
 	}
@@ -677,12 +677,11 @@
 	d_drop(dentry);
 }
 
-void d_add(struct dentry * entry, struct inode * inode)
+void d_rehash(struct dentry * entry)
 {
 	struct dentry * parent = entry->d_parent;
 
 	list_add(&entry->d_hash, d_hash(parent, entry->d_name.hash));
-	d_instantiate(entry, inode);
 }
 
 #define do_switch(x,y) do { \
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/exec.c linux/fs/exec.c
--- v2.2.0-pre7/linux/fs/exec.c	Thu Nov 19 09:56:28 1998
+++ linux/fs/exec.c	Mon Jan 18 13:47:38 1999
@@ -380,17 +380,13 @@
 
 	if (atomic_read(&current->mm->count) == 1) {
 		flush_cache_mm(current->mm);
+		mm_release();
+		release_segments(current->mm);
 		exit_mmap(current->mm);
-		clear_page_tables(current);
 		flush_tlb_mm(current->mm);
 		return 0;
 	}
 
-	/*
-	 * The clear_page_tables done later on exec does the right thing
-	 * to the page directory when shared, except for graceful abort
-	 * (the oom is wrong there, too, IMHO)
-	 */
 	retval = -ENOMEM;
 	mm = mm_alloc();
 	if (!mm)
@@ -412,6 +408,7 @@
 		goto fail_restore;
 	activate_context(current);
 	up(&mm->mmap_sem);
+	mm_release();
 	mmput(old_mm);
 	return 0;
 
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/isofs/namei.c linux/fs/isofs/namei.c
--- v2.2.0-pre7/linux/fs/isofs/namei.c	Thu Sep 17 17:53:37 1998
+++ linux/fs/isofs/namei.c	Fri Jan 15 14:41:04 1999
@@ -161,6 +161,7 @@
 
 		if (dir->i_sb->u.isofs_sb.s_rock ||
 		    dir->i_sb->u.isofs_sb.s_joliet_level || 
+		    dir->i_sb->u.isofs_sb.s_mapping == 'n' ||
 		    dir->i_sb->u.isofs_sb.s_mapping == 'a') {
 			if (! page) {
 				page = (unsigned char *)
@@ -190,12 +191,13 @@
 					break;
 				}
 				if (c == ';') c = '.';
-				dpnt[i] = c;
+				page[i] = c;
 			}
 			/* This allows us to match with and without
 			 * a trailing period. */
-			if(dpnt[dlen-1] == '.' && dentry->d_name.len == dlen-1)
+			if(page[dlen-1] == '.' && dentry->d_name.len == dlen-1)
 				dlen--;
+			dpnt = page;
 		}
 		/*
 		 * Skip hidden or associated files unless unhide is set 
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/namei.c linux/fs/namei.c
--- v2.2.0-pre7/linux/fs/namei.c	Mon Dec 28 15:00:52 1998
+++ linux/fs/namei.c	Thu Jan 14 22:11:45 1999
@@ -561,6 +561,20 @@
 }
 
 /*
+ * We need to do a check-parent every time
+ * after we have locked the parent - to verify
+ * that the parent is still our parent and
+ * that we are still hashed onto it..
+ *
+ * This is requied in case two processes race
+ * on removing (or moving) the same entry: the
+ * parent lock will serialize them, but the
+ * other process will be too late..
+ */
+#define check_parent(dir, dentry) \
+	((dir) == (dentry)->d_parent && !list_empty(&dentry->d_hash))
+
+/*
  * Locking the parent is needed to:
  *  - serialize directory operations
  *  - make sure the parent doesn't change from
@@ -577,17 +591,41 @@
 	struct dentry *dir = dget(dentry->d_parent);
 
 	down(&dir->d_inode->i_sem);
+	return dir;
+}
 
-	/* Un-hashed or moved?  Punt if so.. */
-	if (dir != dentry->d_parent || list_empty(&dentry->d_hash)) {
-		if (dir != dentry) {
-			unlock_dir(dir);
-			dir = ERR_PTR(-ENOENT);
+/*
+ * Whee.. Deadlock country. Happily there are only two VFS
+ * operations that do this..
+ */
+static inline void double_lock(struct dentry *d1, struct dentry *d2)
+{
+	struct semaphore *s1 = &d1->d_inode->i_sem;
+	struct semaphore *s2 = &d2->d_inode->i_sem;
+
+	if (s1 != s2) {
+		if ((unsigned long) s1 < (unsigned long) s2) {
+			struct semaphore *tmp = s2;
+			s2 = s1; s1 = tmp;
 		}
+		down(s1);
 	}
-	return dir;
+	down(s2);
 }
 
+static inline void double_unlock(struct dentry *d1, struct dentry *d2)
+{
+	struct semaphore *s1 = &d1->d_inode->i_sem;
+	struct semaphore *s2 = &d2->d_inode->i_sem;
+
+	up(s1);
+	if (s1 != s2)
+		up(s2);
+	dput(d1);
+	dput(d2);
+}
+
+
 /* 
  * Special case: O_CREAT|O_EXCL implies O_NOFOLLOW for security
  * reasons.
@@ -645,9 +683,20 @@
 			goto exit;
 
 		dir = lock_parent(dentry);
-		error = PTR_ERR(dir);
-		if (IS_ERR(dir))
+		if (!check_parent(dir, dentry)) {
+			/*
+			 * Really nasty race happened. What's the 
+			 * right error code? We had a dentry, but
+			 * before we could use it it was removed
+			 * by somebody else. We could just re-try
+			 * everything, I guess.
+			 *
+			 * ENOENT is definitely wrong.
+			 */
+			error = -ENOENT;
+			unlock_dir(dir);
 			goto exit;
+		}
 
 		/*
 		 * Somebody might have created the file while we
@@ -760,9 +809,9 @@
 		return dentry;
 
 	dir = lock_parent(dentry);
-	retval = dir;
-	if (IS_ERR(dir))
-		goto exit;
+	error = -ENOENT;
+	if (!check_parent(dir, dentry))
+		goto exit_lock;
 
 	error = may_create(dir->d_inode, dentry);
 	if (error)
@@ -779,7 +828,6 @@
 	if (!error)
 		retval = dget(dentry);
 	unlock_dir(dir);
-exit:
 	dput(dentry);
 	return retval;
 }
@@ -835,9 +883,9 @@
 		goto exit;
 
 	dir = lock_parent(dentry);
-	error = PTR_ERR(dir);
-	if (IS_ERR(dir))
-		goto exit_dput;
+	error = -ENOENT;
+	if (!check_parent(dir, dentry))
+		goto exit_lock;
 
 	error = may_create(dir->d_inode, dentry);
 	if (error)
@@ -853,7 +901,6 @@
 
 exit_lock:
 	unlock_dir(dir);
-exit_dput:
 	dput(dentry);
 exit:
 	return error;
@@ -875,39 +922,6 @@
 	return error;
 }
 
-/*
- * Whee.. Deadlock country. Happily there are only two VFS
- * operations that do this..
- */
-static inline void double_lock(struct dentry *d1, struct dentry *d2)
-{
-	struct semaphore *s1 = &d1->d_inode->i_sem;
-	struct semaphore *s2 = &d2->d_inode->i_sem;
-
-	if (s1 != s2) {
-		if ((unsigned long) s1 < (unsigned long) s2) {
-			struct semaphore *tmp = s2;
-			s2 = s1; s1 = tmp;
-		}
-		down(s1);
-	}
-	down(s2);
-}
-
-static inline void double_unlock(struct dentry *d1, struct dentry *d2)
-{
-	struct semaphore *s1 = &d1->d_inode->i_sem;
-	struct semaphore *s2 = &d2->d_inode->i_sem;
-
-	up(s1);
-	if (s1 != s2)
-		up(s2);
-	dput(d1);
-	dput(d2);
-}
-
-
-
 int vfs_rmdir(struct inode *dir, struct dentry *dentry)
 {
 	int error;
@@ -976,7 +990,9 @@
 	dentry->d_count++;
 	double_lock(dir, dentry);
 
-	error = vfs_rmdir(dir->d_inode, dentry);
+	error = -ENOENT;
+	if (check_parent(dir, dentry))
+		error = vfs_rmdir(dir->d_inode, dentry);
 
 	double_unlock(dentry, dir);
 exit_dput:
@@ -1032,14 +1048,11 @@
 		goto exit;
 
 	dir = lock_parent(dentry);
-	error = PTR_ERR(dir);
-	if (IS_ERR(dir))
-		goto exit_dput;
-
-	error = vfs_unlink(dir->d_inode, dentry);
+	error = -ENOENT;
+	if (check_parent(dir, dentry))
+		error = vfs_unlink(dir->d_inode, dentry);
 
         unlock_dir(dir);
-exit_dput:
 	dput(dentry);
 exit:
 	return error;
@@ -1074,9 +1087,9 @@
 		goto exit;
 
 	dir = lock_parent(dentry);
-	error = PTR_ERR(dir);
-	if (IS_ERR(dir))
-		goto exit_dput;
+	error = -ENOENT;
+	if (!check_parent(dir, dentry))
+		goto exit_lock;
 
 	error = may_create(dir->d_inode, dentry);
 	if (error)
@@ -1091,7 +1104,6 @@
 
 exit_lock:
 	unlock_dir(dir);
-exit_dput:
 	dput(dentry);
 exit:
 	return error;
@@ -1145,9 +1157,9 @@
 		goto exit_old;
 
 	dir = lock_parent(new_dentry);
-	error = PTR_ERR(dir);
-	if (IS_ERR(dir))
-		goto exit_new;
+	error = -ENOENT;
+	if (!check_parent(dir, new_dentry))
+		goto exit_lock;
 
 	error = -ENOENT;
 	inode = old_dentry->d_inode;
@@ -1178,7 +1190,6 @@
 
 exit_lock:
 	unlock_dir(dir);
-exit_new:
 	dput(new_dentry);
 exit_old:
 	dput(old_dentry);
@@ -1272,8 +1283,10 @@
 
 	double_lock(new_dir, old_dir);
 
-	error = vfs_rename(old_dir->d_inode, old_dentry,
-			   new_dir->d_inode, new_dentry);
+	error = -ENOENT;
+	if (check_parent(old_dir, old_dentry) && check_parent(new_dir, new_dentry))
+		error = vfs_rename(old_dir->d_inode, old_dentry,
+				   new_dir->d_inode, new_dentry);
 
 	double_unlock(new_dir, old_dir);
 	dput(new_dentry);
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.2.0-pre7/linux/fs/nfs/dir.c	Mon Dec 28 15:00:52 1998
+++ linux/fs/nfs/dir.c	Fri Jan 15 15:45:22 1999
@@ -402,7 +402,17 @@
 	struct nfs_fh fhandle;
 	struct nfs_fattr fattr;
 
-	if (inode && is_bad_inode(inode)) {
+	/*
+	 * If we don't have an inode, let's just assume
+	 * a 5-second "live" time for negative dentries.
+	 */
+	if (!inode) {
+		if (time < NFS_REVALIDATE_INTERVAL)
+			goto out_valid;
+		goto out_bad;
+	}
+
+	if (is_bad_inode(inode)) {
 #ifdef NFS_PARANOIA
 printk("nfs_lookup_validate: %s/%s has dud inode\n",
 parent->d_name.name, dentry->d_name.name);
@@ -410,16 +420,12 @@
 		goto out_bad;
 	}
 
-	if (time < NFS_REVALIDATE_INTERVAL)
+	if (time < NFS_ATTRTIMEO(inode))
 		goto out_valid;
-	/*
-	 * Don't bother looking up a negative dentry ...
-	 */
-	if (!inode)
-		goto out_bad;
 
 	if (IS_ROOT(dentry))
 		goto out_valid;
+
 	/*
 	 * Do a new lookup and check the dentry attributes.
 	 */
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- v2.2.0-pre7/linux/fs/nfsd/nfsfh.c	Thu Dec 31 10:29:02 1998
+++ linux/fs/nfsd/nfsfh.c	Sat Jan 16 17:05:13 1999
@@ -1055,7 +1055,7 @@
 	/*
 	 * Look up the dentry using the NFS file handle.
 	 */
-	error = nfserr_stale;
+	error = nfserr_noent;
 	dentry = find_fh_dentry(fh);
 	if (!dentry)
 		goto out;
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- v2.2.0-pre7/linux/fs/nfsd/vfs.c	Mon Jan  4 15:08:17 1999
+++ linux/fs/nfsd/vfs.c	Sat Jan 16 17:05:13 1999
@@ -282,7 +282,7 @@
 
 	/* Change the attributes. */
 	if (iap->ia_valid) {
-		kernel_cap_t	saved_cap;
+		kernel_cap_t	saved_cap = 0;
 
 		iap->ia_valid |= ATTR_CTIME;
 		iap->ia_ctime = CURRENT_TIME;
@@ -1109,6 +1109,12 @@
 	err = PTR_ERR(rdentry);
 	if (IS_ERR(rdentry))
 		goto out_nfserr;
+
+	if (!rdentry->d_inode) {
+		dput(rdentry);
+		err = nfserr_noent;
+		goto out;
+	}
 
 	if (type != S_IFDIR) {
 		/* It's UNLINK */
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.2.0-pre7/linux/fs/proc/array.c	Wed Jan 13 15:00:43 1999
+++ linux/fs/proc/array.c	Thu Jan 14 10:49:22 1999
@@ -42,6 +42,8 @@
  * Alan Cox	     :  security fixes. 
  *			<Alan.Cox@linux.org>
  *
+ * Andi Kleen	     :  Race Fixes. 	
+ *
  */
 
 #include <linux/types.h>
@@ -386,21 +388,46 @@
 	return sprintf(buffer, "%s\n", saved_command_line);
 }
 
-static unsigned long get_phys_addr(struct task_struct * p, unsigned long ptr)
+/* 
+ * Caller must release_mm the mm_struct later.
+ * You don't get any access to init_mm. 
+ */ 
+static struct mm_struct *get_mm_and_lock(int pid) 
+{ 
+	struct mm_struct *mm = NULL;  
+	struct task_struct *tsk; 
+
+	read_lock(&tasklist_lock); 
+	tsk = find_task_by_pid(pid); 
+	if (tsk && tsk->mm && tsk->mm != &init_mm)
+		mmget(mm = tsk->mm);  
+	read_unlock(&tasklist_lock);
+	if (mm != NULL) 
+		down(&mm->mmap_sem); 
+	return mm; 
+}
+
+static void release_mm(struct mm_struct *mm)
+{
+	up(&mm->mmap_sem); 
+	mmput(mm); 
+}
+
+static unsigned long get_phys_addr(struct mm_struct *mm, unsigned long ptr)
 {
 	pgd_t *page_dir;
 	pmd_t *page_middle;
 	pte_t pte;
 
-	if (!p || !p->mm || ptr >= TASK_SIZE)
+	if (ptr >= TASK_SIZE)
 		return 0;
 	/* Check for NULL pgd .. shouldn't happen! */
-	if (!p->mm->pgd) {
-		printk("get_phys_addr: pid %d has NULL pgd!\n", p->pid);
+	if (!mm->pgd) {
+		printk(KERN_DEBUG "missing pgd for mm %p\n", mm); 
 		return 0;
 	}
 
-	page_dir = pgd_offset(p->mm,ptr);
+	page_dir = pgd_offset(mm,ptr);
 	if (pgd_none(*page_dir))
 		return 0;
 	if (pgd_bad(*page_dir)) {
@@ -422,7 +449,7 @@
 	return pte_page(pte) + (ptr & ~PAGE_MASK);
 }
 
-static int get_array(struct task_struct *p, unsigned long start, unsigned long end, char * buffer)
+static int get_array(struct mm_struct *mm, unsigned long start, unsigned long end, char * buffer)
 {
 	unsigned long addr;
 	int size = 0, result = 0;
@@ -431,7 +458,7 @@
 	if (start >= end)
 		return result;
 	for (;;) {
-		addr = get_phys_addr(p, start);
+		addr = get_phys_addr(mm, start);
 		if (!addr)
 			return result;
 		do {
@@ -453,27 +480,28 @@
 
 static int get_env(int pid, char * buffer)
 {
-	struct task_struct *p;
-	
-	read_lock(&tasklist_lock);
-	p = find_task_by_pid(pid);
-	read_unlock(&tasklist_lock);	/* FIXME!! This should be done after the last use */
+	struct mm_struct *mm; 
+	int res = 0; 
 
-	if (!p || !p->mm)
-		return 0;
-	return get_array(p, p->mm->env_start, p->mm->env_end, buffer);
+	mm = get_mm_and_lock(pid); 
+	if (mm) {  
+		res = get_array(mm, mm->env_start, mm->env_end, buffer); 
+		release_mm(mm);
+	}
+	return res;
 }
 
 static int get_arg(int pid, char * buffer)
 {
-	struct task_struct *p;
+	struct mm_struct *mm; 
+	int res = 0; 
 
-	read_lock(&tasklist_lock);
-	p = find_task_by_pid(pid);
-	read_unlock(&tasklist_lock);	/* FIXME!! This should be done after the last use */
-	if (!p || !p->mm)
-		return 0;
-	return get_array(p, p->mm->arg_start, p->mm->arg_end, buffer);
+	mm = get_mm_and_lock(pid); 
+	if (mm) {
+		res = get_array(mm, mm->arg_start, mm->arg_end, buffer); 
+		release_mm(mm); 
+	} 
+	return res;
 }
 
 /*
@@ -726,11 +754,14 @@
 {
 	struct mm_struct * mm = p->mm;
 
-	if (mm && mm != &init_mm) {
-		struct vm_area_struct * vma = mm->mmap;
+	if (!mm) 
+		return buffer;
+	if (mm != &init_mm) {
+		struct vm_area_struct * vma;
 		unsigned long data = 0, stack = 0;
 		unsigned long exec = 0, lib = 0;
 
+		down(&mm->mmap_sem); 
 		for (vma = mm->mmap; vma; vma = vma->vm_next) {
 			unsigned long len = (vma->vm_end - vma->vm_start) >> 10;
 			if (!vma->vm_file) {
@@ -748,6 +779,7 @@
 				lib += len;
 			}
 		}	
+		up(&mm->mmap_sem); 
 		buffer += sprintf(buffer,
 			"VmSize:\t%8lu kB\n"
 			"VmLck:\t%8lu kB\n"
@@ -817,15 +849,35 @@
 			    cap_t(p->cap_effective));
 }
 
+static struct task_struct *grab_task(int pid, struct task_struct *dst) 
+{
+	struct task_struct *tsk = current; 
+	if (pid != tsk->pid) { 
+		read_lock(&tasklist_lock);
+		tsk = find_task_by_pid(pid);
+		if (tsk) { 
+			memcpy(dst, tsk, sizeof(struct task_struct));
+			tsk = dst;
+			if (tsk->mm && tsk->mm != &init_mm) 
+				mmget(tsk->mm);
+		}
+		read_unlock(&tasklist_lock);
+	}	
+	return tsk; 
+}
+
+static void release_task(struct task_struct *tsk)
+{
+	if (tsk != current && tsk->mm && tsk->mm != &init_mm)
+		mmput(tsk->mm); 
+}
 
 static int get_status(int pid, char * buffer)
 {
 	char * orig = buffer;
-	struct task_struct *tsk;
-
-	read_lock(&tasklist_lock);
-	tsk = find_task_by_pid(pid);
-	read_unlock(&tasklist_lock);	/* FIXME!! This should be done after the last use */
+	struct task_struct *tsk, mytask;
+	
+	tsk = grab_task(pid, &mytask); 
 	if (!tsk)
 		return 0;
 	buffer = task_name(tsk, buffer);
@@ -833,31 +885,34 @@
 	buffer = task_mem(tsk, buffer);
 	buffer = task_sig(tsk, buffer);
 	buffer = task_cap(tsk, buffer);
+	release_task(tsk); 
 	return buffer - orig;
 }
 
 static int get_stat(int pid, char * buffer)
 {
-	struct task_struct *tsk;
+	struct task_struct *tsk, mytask;
 	unsigned long vsize, eip, esp, wchan;
 	long priority, nice;
 	int tty_pgrp;
 	sigset_t sigign, sigcatch;
 	char state;
+	int res;
 
-	read_lock(&tasklist_lock);
-	tsk = find_task_by_pid(pid);
-	read_unlock(&tasklist_lock);	/* FIXME!! This should be done after the last use */
-	if (!tsk)
+	tsk = grab_task(pid, &mytask);  
+	if (!tsk) 
 		return 0;
 	state = *get_task_state(tsk);
 	vsize = eip = esp = 0;
 	if (tsk->mm && tsk->mm != &init_mm) {
-		struct vm_area_struct *vma = tsk->mm->mmap;
-		while (vma) {
+		struct vm_area_struct *vma;
+
+		down(&tsk->mm->mmap_sem);
+		for (vma = tsk->mm->mmap; vma; vma = vma->vm_next) {  
 			vsize += vma->vm_end - vma->vm_start;
-			vma = vma->vm_next;
 		}
+		up(&tsk->mm->mmap_sem); 
+		
 		eip = KSTK_EIP(tsk);
 		esp = KSTK_ESP(tsk);
 	}
@@ -878,7 +933,7 @@
 	nice = tsk->priority;
 	nice = 20 - (nice * 20 + DEF_PRIORITY / 2) / DEF_PRIORITY;
 
-	return sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \
+	res = sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \
 %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu \
 %lu %lu %lu %lu %lu %lu %lu %lu %d\n",
 		pid,
@@ -923,6 +978,9 @@
 		tsk->nswap,
 		tsk->cnswap,
 		tsk->exit_signal);
+
+	release_task(tsk);
+	return res;
 }
 		
 static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size,
@@ -1000,19 +1058,15 @@
 
 static int get_statm(int pid, char * buffer)
 {
-	struct task_struct *tsk;
 	int size=0, resident=0, share=0, trs=0, lrs=0, drs=0, dt=0;
+	struct mm_struct *mm;
 
-	read_lock(&tasklist_lock);
-	tsk = find_task_by_pid(pid);
-	read_unlock(&tasklist_lock);	/* FIXME!! This should be done after the last use */
-	if (!tsk)
-		return 0;
-	if (tsk->mm && tsk->mm != &init_mm) {
-		struct vm_area_struct * vma = tsk->mm->mmap;
+	mm = get_mm_and_lock(pid); 
+	if (mm) { 
+		struct vm_area_struct * vma = mm->mmap;
 
 		while (vma) {
-			pgd_t *pgd = pgd_offset(tsk->mm, vma->vm_start);
+			pgd_t *pgd = pgd_offset(mm, vma->vm_start);
 			int pages = 0, shared = 0, dirty = 0, total = 0;
 
 			statm_pgd_range(pgd, vma->vm_start, vma->vm_end, &pages, &shared, &dirty, &total);
@@ -1030,7 +1084,9 @@
 				drs += pages;
 			vma = vma->vm_next;
 		}
-	}
+		release_mm(mm); 
+	} else
+		return 0; 
 	return sprintf(buffer,"%d %d %d %d %d %d %d\n",
 		       size, resident, share, trs, lrs, drs, dt);
 }
@@ -1067,7 +1123,7 @@
 
 #define MAPS_LINE_MAX	MAPS_LINE_MAX8
 
-
+/* FIXME: this does not do proper mm locking */  
 static ssize_t read_maps (int pid, struct file * file, char * buf,
 			  size_t count, loff_t *ppos)
 {
@@ -1199,15 +1255,11 @@
 #ifdef __SMP__
 static int get_pidcpu(int pid, char * buffer)
 {
-	struct task_struct * tsk = current ;
+	struct task_struct * tsk, mytask;
 	int i, len;
 
-	read_lock(&tasklist_lock);
-	if (pid != tsk->pid)
-		tsk = find_task_by_pid(pid);
-	read_unlock(&tasklist_lock);	/* FIXME!! This should be done after the last use */
-
-	if (tsk == NULL)
+	tsk = grab_task(pid, &mytask);
+	if (!tsk)
 		return 0;
 
 	len = sprintf(buffer,
@@ -1221,6 +1273,7 @@
 			tsk->per_cpu_utime[cpu_logical_map(i)],
 			tsk->per_cpu_stime[cpu_logical_map(i)]);
 
+	release_task(tsk); 
 	return len;
 }
 #endif
@@ -1421,6 +1474,7 @@
 	ssize_t end;
 	unsigned int type, pid;
 	struct proc_dir_entry *dp;
+	int err;
 
 	if (count > PROC_BLOCK_SIZE)
 		count = PROC_BLOCK_SIZE;
@@ -1449,8 +1503,10 @@
 		return length;
 	}
 	if (start != NULL) {
+		if (length > count)
+			length = count;
 		/* We have had block-adjusting processing! */
-		copy_to_user(buf, start, length);
+		err = copy_to_user(buf, start, length);
 		*ppos += length;
 		count = length;
 	} else {
@@ -1462,11 +1518,11 @@
 		if (count + *ppos > length)
 			count = length - *ppos;
 		end = count + *ppos;
-		copy_to_user(buf, (char *) page + *ppos, count);
+		err = copy_to_user(buf, (char *) page + *ppos, count);
 		*ppos = end;
 	}
 	free_page(page);
-	return count;
+	return err ? -EFAULT : count;
 }
 
 static struct file_operations proc_array_operations = {
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/proc/root.c linux/fs/proc/root.c
--- v2.2.0-pre7/linux/fs/proc/root.c	Wed Jan 13 15:00:43 1999
+++ linux/fs/proc/root.c	Fri Jan 15 15:38:59 1999
@@ -348,6 +348,32 @@
 	return 0;
 }
 
+/*
+ * Kill an inode that got unregistered..
+ */
+static void proc_kill_inodes(int ino)
+{
+	struct file *filp;
+
+	/* inuse_filps is protected by the single kernel lock */
+	for (filp = inuse_filps; filp; filp = filp->f_next) {
+		struct dentry * dentry;
+		struct inode * inode;
+
+		dentry = filp->f_dentry;
+		if (!dentry)
+			continue;
+		if (dentry->d_op != &proc_dentry_operations)
+			continue;
+		inode = dentry->d_inode;
+		if (!inode)
+			continue;
+		if (inode->i_ino != ino)
+			continue;
+		filp->f_op = NULL;
+	}
+}
+
 int proc_unregister(struct proc_dir_entry * dir, int ino)
 {
 	struct proc_dir_entry **p = &dir->subdir, *dp;
@@ -362,6 +388,7 @@
 			    ino < PROC_DYNAMIC_FIRST+PROC_NDYNAMIC)
 				clear_bit(ino-PROC_DYNAMIC_FIRST, 
 					  (void *) proc_alloc_map);
+			proc_kill_inodes(ino);
 			return 0;
 		}
 		p = &dp->next;
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/select.c linux/fs/select.c
--- v2.2.0-pre7/linux/fs/select.c	Wed Jan 13 15:00:43 1999
+++ linux/fs/select.c	Fri Jan 15 18:57:33 1999
@@ -41,28 +41,35 @@
 
 static void free_wait(poll_table * p)
 {
-	struct poll_table_entry * entry = p->entry + p->nr;
+	struct poll_table_entry * entry;
+	poll_table *old;
 
-	while (p->nr > 0) {
-		p->nr--;
-		entry--;
-		remove_wait_queue(entry->wait_address,&entry->wait);
-		fput(entry->filp);
+	while (p) {
+		entry = p->entry + p->nr;
+		while (p->nr > 0) {
+			p->nr--;
+			entry--;
+			remove_wait_queue(entry->wait_address,&entry->wait);
+			fput(entry->filp);
+		}
+		old = p;
+		p = p->next;
+		free_page((unsigned long) old);
 	}
 }
 
-#define __IN(in)	(in)
-#define __OUT(in)	(in + sizeof(kernel_fd_set)/sizeof(unsigned long))
-#define __EX(in)	(in + 2*sizeof(kernel_fd_set)/sizeof(unsigned long))
-#define __RES_IN(in)	(in + 3*sizeof(kernel_fd_set)/sizeof(unsigned long))
-#define __RES_OUT(in)	(in + 4*sizeof(kernel_fd_set)/sizeof(unsigned long))
-#define __RES_EX(in)	(in + 5*sizeof(kernel_fd_set)/sizeof(unsigned long))
+#define __IN(fds, n)		(fds->in + n)
+#define __OUT(fds, n)		(fds->out + n)
+#define __EX(fds, n)		(fds->ex + n)
+#define __RES_IN(fds, n)	(fds->res_in + n)
+#define __RES_OUT(fds, n)	(fds->res_out + n)
+#define __RES_EX(fds, n)	(fds->res_ex + n)
 
-#define BITS(in)	(*__IN(in)|*__OUT(in)|*__EX(in))
+#define BITS(fds, n)		(*__IN(fds, n)|*__OUT(fds, n)|*__EX(fds, n))
 
-static int max_select_fd(unsigned long n, fd_set_buffer *fds)
+static int max_select_fd(unsigned long n, fd_set_bits *fds)
 {
-	unsigned long *open_fds, *in;
+	unsigned long *open_fds;
 	unsigned long set;
 	int max;
 
@@ -70,10 +77,9 @@
 	set = ~(~0UL << (n & (__NFDBITS-1)));
 	n /= __NFDBITS;
 	open_fds = current->files->open_fds.fds_bits+n;
-	in = fds->in+n;
 	max = 0;
 	if (set) {
-		set &= BITS(in);
+		set &= BITS(fds, n);
 		if (set) {
 			if (!(set & ~*open_fds))
 				goto get_max;
@@ -81,10 +87,9 @@
 		}
 	}
 	while (n) {
-		in--;
 		open_fds--;
 		n--;
-		set = BITS(in);
+		set = BITS(fds, n);
 		if (!set)
 			continue;
 		if (set & ~*open_fds)
@@ -111,21 +116,22 @@
 #define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
 #define POLLEX_SET (POLLPRI)
 
-int do_select(int n, fd_set_buffer *fds, long *timeout)
+int do_select(int n, fd_set_bits *fds, long *timeout)
 {
-	poll_table wait_table, *wait;
-	int retval, i;
+	poll_table *wait_table, *wait;
+	int retval, i, off;
 	long __timeout = *timeout;
 
-	wait = NULL;
+	wait = wait_table = NULL;
 	if (__timeout) {
-		struct poll_table_entry *entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
-		if (!entry)
+		wait_table = (poll_table *) __get_free_page(GFP_KERNEL);
+		if (!wait_table)
 			return -ENOMEM;
 
-		wait_table.nr = 0;
-		wait_table.entry = entry;
-		wait = &wait_table;
+		wait_table->nr = 0;
+		wait_table->entry = (struct poll_table_entry *)(wait_table + 1);
+		wait_table->next = NULL;
+		wait = wait_table;
 	}
 
 	lock_kernel();
@@ -139,11 +145,11 @@
 		current->state = TASK_INTERRUPTIBLE;
 		for (i = 0 ; i < n; i++) {
 			unsigned long bit = BIT(i);
-			unsigned long *in = MEM(i,fds->in);
 			unsigned long mask;
 			struct file *file;
 
-			if (!(bit & BITS(in)))
+			off = i / __NFDBITS;
+			if (!(bit & BITS(fds, off)))
 				continue;
 			/*
 			 * The poll_wait routine will increment f_count if
@@ -157,18 +163,18 @@
 				if (file->f_op && file->f_op->poll)
 					mask = file->f_op->poll(file, wait);
 			}
-			if ((mask & POLLIN_SET) && ISSET(bit, __IN(in))) {
-				SET(bit, __RES_IN(in));
+			if ((mask & POLLIN_SET) && ISSET(bit, __IN(fds,off))) {
+				SET(bit, __RES_IN(fds,off));
 				retval++;
 				wait = NULL;
 			}
-			if ((mask & POLLOUT_SET) && ISSET(bit, __OUT(in))) {
-				SET(bit, __RES_OUT(in));
+			if ((mask & POLLOUT_SET) && ISSET(bit, __OUT(fds,off))) {
+				SET(bit, __RES_OUT(fds,off));
 				retval++;
 				wait = NULL;
 			}
-			if ((mask & POLLEX_SET) && ISSET(bit, __EX(in))) {
-				SET(bit, __RES_EX(in));
+			if ((mask & POLLEX_SET) && ISSET(bit, __EX(fds,off))) {
+				SET(bit, __RES_EX(fds,off));
 				retval++;
 				wait = NULL;
 			}
@@ -181,10 +187,8 @@
 	current->state = TASK_RUNNING;
 
 out:
-	if (*timeout) {
-		free_wait(&wait_table);
-		free_page((unsigned long) wait_table.entry);
-	}
+	if (*timeout)
+		free_wait(wait_table);
 
 	/*
 	 * Up-to-date the caller timeout.
@@ -208,9 +212,10 @@
 asmlinkage int
 sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
 {
-	fd_set_buffer *fds;
+	fd_set_bits fds;
+	char *bits;
 	long timeout;
-	int ret;
+	int ret, size;
 
 	timeout = MAX_SCHEDULE_TIMEOUT;
 	if (tvp) {
@@ -231,24 +236,35 @@
 		}
 	}
 
+	ret = -EINVAL;
+	if (n < 0 || n > KFDS_NR)
+		goto out_nofds;
+	/*
+	 * We need 6 bitmaps (in/out/ex for both incoming and outgoing),
+	 * since we used fdset we need to allocate memory in units of
+	 * long-words. 
+	 */
 	ret = -ENOMEM;
-	fds = (fd_set_buffer *) __get_free_page(GFP_KERNEL);
-	if (!fds)
+	size = FDS_BYTES(n);
+	bits = kmalloc(6 * size, GFP_KERNEL);
+	if (!bits)
 		goto out_nofds;
-	ret = -EINVAL;
-	if (n < 0)
+	fds.in      = (unsigned long *)  bits;
+	fds.out     = (unsigned long *) (bits +   size);
+	fds.ex      = (unsigned long *) (bits + 2*size);
+	fds.res_in  = (unsigned long *) (bits + 3*size);
+	fds.res_out = (unsigned long *) (bits + 4*size);
+	fds.res_ex  = (unsigned long *) (bits + 5*size);
+
+	if ((ret = get_fd_set(n, inp, fds.in)) ||
+	    (ret = get_fd_set(n, outp, fds.out)) ||
+	    (ret = get_fd_set(n, exp, fds.ex)))
 		goto out;
-	if (n > KFDS_NR)
-		n = KFDS_NR;
-	if ((ret = get_fd_set(n, inp, fds->in)) ||
-	    (ret = get_fd_set(n, outp, fds->out)) ||
-	    (ret = get_fd_set(n, exp, fds->ex)))
-		goto out;
-	zero_fd_set(n, fds->res_in);
-	zero_fd_set(n, fds->res_out);
-	zero_fd_set(n, fds->res_ex);
+	zero_fd_set(n, fds.res_in);
+	zero_fd_set(n, fds.res_out);
+	zero_fd_set(n, fds.res_ex);
 
-	ret = do_select(n, fds, &timeout);
+	ret = do_select(n, &fds, &timeout);
 
 	if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
 		time_t sec = 0, usec = 0;
@@ -270,12 +286,12 @@
 		ret = 0;
 	}
 
-	set_fd_set(n, inp, fds->res_in);
-	set_fd_set(n, outp, fds->res_out);
-	set_fd_set(n, exp, fds->res_ex);
+	set_fd_set(n, inp, fds.res_in);
+	set_fd_set(n, outp, fds.res_out);
+	set_fd_set(n, exp, fds.res_ex);
 
 out:
-	free_page((unsigned long) fds);
+	kfree(bits);
 out_nofds:
 	return ret;
 }
@@ -327,7 +343,7 @@
 {
 	int i, fdcount, err, size;
 	struct pollfd * fds, *fds1;
-	poll_table wait_table, *wait = NULL;
+	poll_table *wait_table = NULL, *wait = NULL;
 
 	lock_kernel();
 	/* Do a sanity check on nfds ... */
@@ -338,20 +354,20 @@
 	if (timeout) {
 		/* Carefula about overflow in the intermediate values */
 		if ((unsigned long) timeout < MAX_SCHEDULE_TIMEOUT / HZ)
-			timeout = (timeout*HZ+999)/1000+1;
+			timeout = (unsigned long)(timeout*HZ+999)/1000+1;
 		else /* Negative or overflow */
 			timeout = MAX_SCHEDULE_TIMEOUT;
 	}
 
 	err = -ENOMEM;
 	if (timeout) {
-		struct poll_table_entry *entry;
-		entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
-		if (!entry)
+		wait_table = (poll_table *) __get_free_page(GFP_KERNEL);
+		if (!wait_table)
 			goto out;
-		wait_table.nr = 0;
-		wait_table.entry = entry;
-		wait = &wait_table;
+		wait_table->nr = 0;
+		wait_table->entry = (struct poll_table_entry *)(wait_table + 1);
+		wait_table->next = NULL;
+		wait = wait_table;
 	}
 
 	size = nfds * sizeof(struct pollfd);
@@ -378,10 +394,8 @@
 out_fds:
 	kfree(fds);
 out:
-	if (wait) {
-		free_wait(&wait_table);
-		free_page((unsigned long) wait->entry);
-	}
+	if (wait)
+		free_wait(wait_table);
 	unlock_kernel();
 	return err;
 }
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/ufs/balloc.c linux/fs/ufs/balloc.c
--- v2.2.0-pre7/linux/fs/ufs/balloc.c	Fri Oct 23 22:01:22 1998
+++ linux/fs/ufs/balloc.c	Sun Jan 17 18:32:26 1999
@@ -93,7 +93,7 @@
 	ufs_fragacct(sb, blkmap, ucg->cg_frsum, 1);
 
 	/*
-	 * Trying to reasembly free fragments into block
+	 * Trying to reassemble free fragments into block
 	 */
 	blkno = ufs_fragstoblks (bbase);
 	if (ubh_isblockset(UCPI_UBH, ucpi->c_freeoff, blkno)) {
@@ -436,7 +436,7 @@
 	fragsize = i - oldcount;
 	if (!SWAB32(ucg->cg_frsum[fragsize]))
 		ufs_panic (sb, "ufs_add_fragments",
-			"internal error or corruted bitmap on cg %u", cgno);
+			"internal error or corrupted bitmap on cg %u", cgno);
 	DEC_SWAB32(ucg->cg_frsum[fragsize]);
 	if (fragsize != count)
 		INC_SWAB32(ucg->cg_frsum[fragsize - count]);
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/ufs/cylinder.c linux/fs/ufs/cylinder.c
--- v2.2.0-pre7/linux/fs/ufs/cylinder.c	Fri Oct 23 22:01:22 1998
+++ linux/fs/ufs/cylinder.c	Sun Jan 17 18:32:26 1999
@@ -86,7 +86,7 @@
 }
 
 /*
- * Remove cylinder group from cache, does'n release memory
+ * Remove cylinder group from cache, doesn't release memory
  * allocated for cylinder group (this is done at ufs_put_super only).
  */
 void ufs_put_cylinder (struct super_block * sb, unsigned bitmap_nr)
@@ -132,7 +132,7 @@
  * Find cylinder group in cache and return it as pointer.
  * If cylinder group is not in cache, we will load it from disk.
  *
- * The cache is managed by LRU alghoritm. 
+ * The cache is managed by LRU algorithm. 
  */
 struct ufs_cg_private_info * ufs_load_cylinder (
 	struct super_block * sb, unsigned cgno)
@@ -161,7 +161,7 @@
 	if (uspi->s_ncg <= UFS_MAX_GROUP_LOADED) {
 		if (sb->u.ufs_sb.s_cgno[cgno] != UFS_CGNO_EMPTY) {
 			if (sb->u.ufs_sb.s_cgno[cgno] != cgno) {
-				ufs_panic (sb, "ufs_load_cylinder", "internal error, wrog number of cg in cache");
+				ufs_panic (sb, "ufs_load_cylinder", "internal error, wrong number of cg in cache");
 				UFSD(("EXIT (FAILED)\n"))
 				return NULL;
 			}
@@ -191,7 +191,7 @@
 		sb->u.ufs_sb.s_ucpi[0] = ucpi;
 	/*
 	 * Cylinder group number cg is not in cache, we will read it from disk
-	 * and put it to the first possition
+	 * and put it to the first position
 	 */
 	} else {
 		if (sb->u.ufs_sb.s_cg_loaded < UFS_MAX_GROUP_LOADED)
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/ufs/dir.c linux/fs/ufs/dir.c
--- v2.2.0-pre7/linux/fs/ufs/dir.c	Fri Jan  8 22:36:14 1999
+++ linux/fs/ufs/dir.c	Thu Jan 14 10:31:41 1999
@@ -14,6 +14,7 @@
  */
 
 #include <linux/fs.h>
+#include <linux/ufs_fs.h>
 
 #include "swab.h"
 #include "util.h"
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/ufs/file.c linux/fs/ufs/file.c
--- v2.2.0-pre7/linux/fs/ufs/file.c	Wed Aug 26 11:37:43 1998
+++ linux/fs/ufs/file.c	Thu Jan 14 10:31:41 1999
@@ -41,9 +41,6 @@
 #define MIN(a,b) (((a)<(b))?(a):(b))
 #define MAX(a,b) (((a)>(b))?(a):(b))
 
-#include <linux/fs.h>
-#include <linux/ufs_fs.h>
-
 static long long ufs_file_lseek(struct file *, long long, int);
 static ssize_t ufs_file_write (struct file *, const char *, size_t, loff_t *);
 static int ufs_release_file (struct inode *, struct file *);
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/ufs/ialloc.c linux/fs/ufs/ialloc.c
--- v2.2.0-pre7/linux/fs/ufs/ialloc.c	Fri Oct 23 22:01:22 1998
+++ linux/fs/ufs/ialloc.c	Sun Jan 17 18:32:26 1999
@@ -150,7 +150,7 @@
  * the groups with above-average free space, that group with the fewest
  * directories already is chosen.
  *
- * For other inodes, search forward from the parent directory\'s block
+ * For other inodes, search forward from the parent directory's block
  * group to find a free inode.
  */
 struct inode * ufs_new_inode (const struct inode * dir,	int mode, int * err )
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/ufs/inode.c linux/fs/ufs/inode.c
--- v2.2.0-pre7/linux/fs/ufs/inode.c	Fri Oct 23 22:01:22 1998
+++ linux/fs/ufs/inode.c	Sun Jan 17 18:32:26 1999
@@ -1,5 +1,5 @@
 /*
- *  linux/ufs/ufs/inode.c
+ *  linux/fs/ufs/inode.c
  *
  * Copyright (C) 1998
  * Daniel Pirkl <daniel.pirkl@email.cz>
@@ -254,7 +254,7 @@
 			SWAB32(*p), required +  (blockoff - lastblockoff), err);
 	}
 	/*
-	 * We will allocated new block before last allocat block
+	 * We will allocate new block before last allocated block
 	 */
 	else /* (lastblock > block) */ {
 		if (lastblock && (tmp = SWAB32(inode->u.ufs_i.i_u1.i_data[lastblock-1])))
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/ufs/namei.c linux/fs/ufs/namei.c
--- v2.2.0-pre7/linux/fs/ufs/namei.c	Wed Jan 13 15:00:43 1999
+++ linux/fs/ufs/namei.c	Sun Jan 17 18:32:26 1999
@@ -58,8 +58,7 @@
 /*
  * NOTE! unlike strncmp, ufs_match returns 1 for success, 0 for failure.
  *
- * len <= UFS_MAXNAMLEN' is guaranteed by caller.
- * de != NULL' is guaranteed by caller.
+ * len <= UFS_MAXNAMLEN and de != NULL are guaranteed by caller.
  */
 static inline int ufs_match (int len, const char * const name,
 	struct ufs_dir_entry * de, unsigned flags, unsigned swab)
@@ -144,7 +143,7 @@
 		dlimit = bh->b_data + sb->s_blocksize;
 		while ((char *) de < dlimit && offset < dir->i_size) {
 			/* this code is executed quadratically often */
-			/* do minimal checking by hand' */
+			/* do minimal checking by hand */
 			int de_len;
 
 			if ((char *) de + namelen <= dlimit &&
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/ufs/super.c linux/fs/ufs/super.c
--- v2.2.0-pre7/linux/fs/ufs/super.c	Wed Jan 13 15:00:43 1999
+++ linux/fs/ufs/super.c	Sun Jan 17 18:32:26 1999
@@ -6,7 +6,7 @@
  * Charles University, Faculty of Mathematics and Physics
  */
 
-/* Derivated from
+/* Derived from
  *
  *  linux/fs/ext2/super.c
  *
@@ -26,7 +26,7 @@
  */
  
 /*
- * Inspirated by
+ * Inspired by
  *
  *  linux/fs/ufs/super.c
  *
@@ -92,7 +92,7 @@
 
 #ifdef UFS_SUPER_DEBUG_MORE
 /*
- * Print contents of ufs_super_block, useful for debuging
+ * Print contents of ufs_super_block, useful for debugging
  */
 void ufs_print_super_stuff(struct ufs_super_block_first * usb1,
 	struct ufs_super_block_second * usb2, 
@@ -137,7 +137,7 @@
 
 
 /*
- * Print contents of ufs_cylinder_group, useful for debuging
+ * Print contents of ufs_cylinder_group, useful for debugging
  */
 void ufs_print_cylinder_stuff(struct ufs_cylinder_group *cg, unsigned swab)
 {
@@ -231,10 +231,6 @@
 	sb->s_flags |= MS_RDONLY;
 	printk (KERN_CRIT "UFS-fs panic (device %s): %s: %s\n",
 		kdevname(sb->s_dev), function, error_buf);
-/*** 
-	panic ("UFS-fs panic (device %s): %s: %s\n", 
-		kdevname(sb->s_dev), function, error_buf);
-***/
 }
 
 void ufs_warning (struct super_block * sb, const char * function,
@@ -309,7 +305,7 @@
 }
 
 /*
- * Read on-disk structures asscoiated with cylinder groups
+ * Read on-disk structures associated with cylinder groups
  */
 int ufs_read_cylinder_structures (struct super_block * sb) {
 	struct ufs_sb_private_info * uspi;
@@ -391,7 +387,7 @@
 }
 
 /*
- * Put on-disk structures associated with cylidner groups and 
+ * Put on-disk structures associated with cylinder groups and 
  * write them back to disk
  */
 void ufs_put_cylinder_structures (struct super_block * sb) {
@@ -466,10 +462,10 @@
 		goto failed;
 	}
 	if (!(sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE)) {
-		printk("You didn't specify type of your ufs file system\n\n"
+		printk("You didn't specify the type of your ufs filesystem\n\n"
 		"       mount -t ufs -o ufstype=sun|44bsd|old|next|openstep ....\n\n"
-		"!!! WARNING !!! wrong value may corrupt you file system\n"
-		"default value is ufstype=old\n");
+		">>>WARNING<<< Wrong ufstype may corrupt your filesystem, "
+		"default is ufstype=old\n");
 		ufs_set_opt (sb->u.ufs_sb.s_mount_opt, UFSTYPE_OLD);
 	}
 
@@ -480,7 +476,7 @@
 
 	switch (sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) {
 	case UFS_MOUNT_UFSTYPE_44BSD:
-		UFSD(("44bsd ufstype\n"))
+		UFSD(("ufstype=44bsd\n"))
 		uspi->s_fsize = block_size = 512;
 		uspi->s_fmask = ~(512 - 1);
 		uspi->s_fshift = 9;
@@ -490,7 +486,7 @@
 		break;
 		
 	case UFS_MOUNT_UFSTYPE_SUN:
-		UFSD(("sun  ufstype\n"))
+		UFSD(("ufstype=sun\n"))
 		uspi->s_fsize = block_size = 1024;
 		uspi->s_fmask = ~(1024 - 1);
 		uspi->s_fshift = 10;
@@ -500,7 +496,7 @@
 		break;
 
 	case UFS_MOUNT_UFSTYPE_OLD:
-		UFSD(("old ufstype\n"))
+		UFSD(("ufstype=old\n"))
 		uspi->s_fsize = block_size = 1024;
 		uspi->s_fmask = ~(1024 - 1);
 		uspi->s_fshift = 10;
@@ -508,13 +504,13 @@
 		uspi->s_sbbase = 0;
 		flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD;
 		if (!(sb->s_flags & MS_RDONLY)) {
-			printk("old type of ufs is supported read-only\n"); 
+			printk(KERN_INFO "ufstype=old is supported read-only\n"); 
 			sb->s_flags |= MS_RDONLY;
 		}
 		break;
 	
 	case UFS_MOUNT_UFSTYPE_NEXT:
-		UFSD(("next ufstype\n"))
+		UFSD(("ufstype=next\n"))
 		uspi->s_fsize = block_size = 1024;
 		uspi->s_fmask = ~(1024 - 1);
 		uspi->s_fshift = 10;
@@ -522,13 +518,13 @@
 		uspi->s_sbbase = 0;
 		flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD;
 		if (!(sb->s_flags & MS_RDONLY)) {
-			printk("nextstep type of ufs is supported read-only\n");
+			printk(KERN_INFO "ufstype=next is supported read-only\n");
 			sb->s_flags |= MS_RDONLY;
 		}
 		break;
 	
 	case UFS_MOUNT_UFSTYPE_OPENSTEP:
-		UFSD(("openstep ufstype\n"))
+		UFSD(("ufstype=openstep\n"))
 		uspi->s_fsize = block_size = 1024;
 		uspi->s_fmask = ~(1024 - 1);
 		uspi->s_fshift = 10;
@@ -536,13 +532,13 @@
 		uspi->s_sbbase = 0;
 		flags |= UFS_DE_44BSD | UFS_UID_44BSD | UFS_ST_44BSD | UFS_CG_44BSD;
 		if (!(sb->s_flags & MS_RDONLY)) {
-			printk("openstep type of ufs is supported read-only\n");
+			printk(KERN_INFO "ufstype=openstep is supported read-only\n");
 			sb->s_flags |= MS_RDONLY;
 		}
 		break;
 	
 	default:
-		printk("this fs type of ufs is not supported\n");
+		printk("unknown ufstype\n");
 		goto failed;
 	}
 	
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/ufs/symlink.c linux/fs/ufs/symlink.c
--- v2.2.0-pre7/linux/fs/ufs/symlink.c	Mon Sep 28 10:51:35 1998
+++ linux/fs/ufs/symlink.c	Thu Jan 14 10:31:41 1999
@@ -1,5 +1,5 @@
 /*
- *  linux/ufs/ufs/symlink.c
+ *  linux/fs/ufs/symlink.c
  *
  * Copyright (C) 1998
  * Daniel Pirkl <daniel.pirkl@emai.cz>
@@ -27,7 +27,7 @@
 
 #include <linux/errno.h>
 #include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include <linux/ufs_fs.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/stat.h>
diff -u --recursive --new-file v2.2.0-pre7/linux/fs/vfat/namei.c linux/fs/vfat/namei.c
--- v2.2.0-pre7/linux/fs/vfat/namei.c	Thu Jan  7 15:11:40 1999
+++ linux/fs/vfat/namei.c	Fri Jan 15 11:52:40 1999
@@ -395,7 +395,7 @@
 static char replace_chars[] = "[];,+=";
 
 static int vfat_find(struct inode *dir,struct qstr* name,
-		      int find_long,int new_filename,int is_dir,
+		      int new_filename,int is_dir,
 		      struct vfat_slot_info *sinfo_out);
 
 /* Checks the validity of a long MS-DOS filename */
@@ -446,12 +446,11 @@
 static int vfat_valid_shortname(const char *name,int len,
 				int dot_dirs, int utf8)
 {
-	const char *walk, **reserved;
+	const char *walk;
 	unsigned char c;
 	int space;
 	int baselen;
 
-	if (IS_FREE(name)) return -EINVAL;
 	if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
 		if (!dot_dirs) return -EEXIST;
 		return 1;
@@ -463,11 +462,9 @@
 	    	c = *walk++;
 		len--;
 		if (utf8 && (c & 0x80)) return -EINVAL;
-		if (strchr(bad_chars,c)) return -EINVAL;
 		if (strchr(replace_chars,c)) return -EINVAL;
   		if (c >= 'A' && c <= 'Z') return -EINVAL;
-		if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
-		if ((walk == name) && (c == 0xE5)) c = 0x05;
+		if (c < ' '|| c==':') return -EINVAL;
 		if (c == '.') break;
 		space = c == ' ';
 	}
@@ -477,51 +474,52 @@
 		len--;
 		if (c != '.') return -EINVAL;
 	}
-	while (c != '.' && len--) c = *walk++;
 	baselen = walk - name;
 	if (c == '.') {
 		baselen--;
 		if (len >= 4) return -EINVAL;
-		while (len > 0 && walk-name < (MSDOS_NAME+1)) {
+		while (len > 0) {
 			c = *walk++;
 			len--;
 			if (utf8 && (c & 0x80)) return -EINVAL;
-			if (strchr(bad_chars,c)) return -EINVAL;
 			if (strchr(replace_chars,c))
 				return -EINVAL;
-			if (c < ' ' || c == ':' || c == '\\' || c == '.')
+			if (c < ' ' || c == '.'|| c==':')
 				return -EINVAL;
 			if (c >= 'A' && c <= 'Z') return -EINVAL;
 			space = c == ' ';
 		}
 		if (space) return -EINVAL;
-		if (len) return -EINVAL;
-	}
-	if (baselen == 3) {
-		for (reserved = reserved3_names; *reserved; reserved++)
-			if (!strnicmp(name,*reserved,baselen)) return -EINVAL;
-	} else if (baselen == 4) {
-		for (reserved = reserved4_names; *reserved; reserved++)
-			if (!strnicmp(name,*reserved,baselen)) return -EINVAL;
 	}
 
 	return 0;
 }
 
-/* Takes a short filename and converts it to a formatted MS-DOS filename.
- * If the short filename is not a valid MS-DOS filename, an error is 
- * returned.  The formatted short filename is returned in 'res'.
- */
+static int vfat_find_form(struct inode *dir,char *name)
+{
+	struct msdos_dir_entry *de;
+	struct buffer_head *bh = NULL;
+	loff_t pos = 0;
+
+	while(fat_get_entry(dir, &pos, &bh, &de) >= 0) {
+		if (de->attr == ATTR_EXT)
+			continue;
+		if (memcmp(de->name,name,MSDOS_NAME))
+			continue;
+		brelse(bh);
+		return 0;
+	}
+	brelse(bh);
+	return -ENOENT;
+}
 
 static int vfat_format_name(const char *name,int len,char *res,
 			    int dot_dirs,int utf8)
 {
 	char *walk;
-	const char **reserved;
 	unsigned char c;
 	int space;
 
-	if (IS_FREE(name)) return -EINVAL;
 	if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
 		if (!dot_dirs) return -EEXIST;
 		memset(res+1,' ',10);
@@ -530,36 +528,25 @@
 	}
 
 	space = 1; /* disallow names starting with a dot */
-	c = 0;
-	for (walk = res; len && walk-res < 8; walk++) {
-	    	c = *name++;
-		len--;
+	for (walk = res; len-- && (c=*name++)!='.' ; walk++) {
+		if (walk-res == 8) return -EINVAL;
 		if (utf8 && (c & 0x80)) return -EINVAL;
-		if (strchr(bad_chars,c)) return -EINVAL;
 		if (strchr(replace_chars,c)) return -EINVAL;
 		if (c >= 'A' && c <= 'Z') return -EINVAL;
-		if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
-		if (c == '.') break;
+		if (c < ' '|| c==':') return -EINVAL;
 		space = c == ' ';
 		*walk = c >= 'a' && c <= 'z' ? c-32 : c;
 	}
 	if (space) return -EINVAL;
-	if (len && c != '.') {
-		c = *name++;
-		len--;
-		if (c != '.') return -EINVAL;
-	}
-	while (c != '.' && len--) c = *name++;
-	if (c == '.') {
+	if (len >= 0) {
 		while (walk-res < 8) *walk++ = ' ';
 		while (len > 0 && walk-res < MSDOS_NAME) {
 			c = *name++;
 			len--;
 			if (utf8 && (c & 0x80)) return -EINVAL;
-			if (strchr(bad_chars,c)) return -EINVAL;
 			if (strchr(replace_chars,c))
 				return -EINVAL;
-			if (c < ' ' || c == ':' || c == '\\' || c == '.')
+			if (c < ' ' || c == '.'|| c==':')
 				return -EINVAL;
 			if (c >= 'A' && c <= 'Z') return -EINVAL;
 			space = c == ' ';
@@ -569,10 +556,6 @@
 		if (len) return -EINVAL;
 	}
 	while (walk-res < MSDOS_NAME) *walk++ = ' ';
-	for (reserved = reserved3_names; *reserved; reserved++)
-		if (!strnicmp(res,*reserved,8)) return -EINVAL;
-	for (reserved = reserved4_names; *reserved; reserved++)
-		if (!strnicmp(res,*reserved,8)) return -EINVAL;
 
 	return 0;
 }
@@ -587,16 +570,12 @@
 {
 	const char *ip, *ext_start, *end;
 	char *p;
-	int sz, extlen, baselen, totlen;
+	int sz, extlen, baselen;
 	char msdos_name[13];
 	char base[9], ext[4];
 	int i;
-	int res;
-	int spaces;
 	char buf[8];
-	struct vfat_slot_info sinfo;
 	const char *name_start;
-	struct qstr qname;
 
 	PRINTK2(("Entering vfat_create_shortname: name=%s, len=%d\n", name, len));
 	sz = 0;			/* Make compiler happy */
@@ -606,31 +585,25 @@
 		 * shortname if is were all capitalized.  However, do not
 		 * allow spaces in short names because Win95 scandisk does
 		 * not like that */
-		res = 0;
-		for (i = 0, p = msdos_name, ip = name; i < len; i++, p++, ip++)
-		{
-			if (*ip == ' ') {
-				res = -1;
-				break;
+		for (i = 0, p = msdos_name, ip = name; ; i++, p++, ip++) {
+			if (i == len) {
+				if (vfat_format_name(msdos_name,
+						len, name_res, 1, utf8) < 0)
+					break;
+				PRINTK3(("vfat_create_shortname 1\n"));
+				if (vfat_find_form(dir, name_res) < 0)
+					return 0;
+				return -EEXIST;
 			}
+
+			if (*ip == ' ')
+				break;
 			if (*ip >= 'A' && *ip <= 'Z') {
 				*p = *ip + 32;
 			} else {
 				*p = *ip;
 			}
 		}
-		if (res == 0) {
-			res = vfat_format_name(msdos_name, len, name_res, 1, utf8);
-		}
-		if (res > -1) {
-			PRINTK3(("vfat_create_shortname 1\n"));
-			qname.name=msdos_name;
-			qname.len=len;
-			res = vfat_find(dir, &qname, 0, 0, 0, &sinfo);
-			PRINTK3(("vfat_create_shortname 2\n"));
-			if (res > -1) return -EEXIST;
-			return 0;
-		}
 	}
 
 	PRINTK3(("vfat_create_shortname 3\n"));
@@ -675,8 +648,8 @@
 			*p++ = '_';
 			baselen++;
 		} else if (!strchr(skip_chars, *ip)) {
-			if (*ip >= 'A' && *ip <= 'Z') {
-				*p = *ip + 32;
+			if (*ip >= 'a' && *ip <= 'z') {
+				*p = *ip - 32;
 			} else {
 				*p = *ip;
 			}
@@ -689,17 +662,15 @@
 		return -EINVAL;
 	}
 		
-	spaces = 8 - baselen;
-
+	extlen = 0;
 	if (ext_start) {
-		extlen = 0;
 		for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) {
 			if (utf8 && (*ip & 0x80)) {
 				*p++ = '_';
 				extlen++;
 			} else if (!strchr(skip_chars, *ip)) {
-				if (*ip >= 'A' && *ip <= 'Z') {
-					*p = *ip + 32;
+				if (*ip >= 'a' && *ip <= 'z') {
+					*p = *ip - 32;
 				} else {
 					*p = *ip;
 				}
@@ -708,74 +679,57 @@
 				p++;
 			}
 		}
-	} else {
-		extlen = 0;
 	}
 	ext[extlen] = '\0';
 	base[baselen] = '\0';
 
-	strcpy(msdos_name, base);
-	msdos_name[baselen] = '.';
-	strcpy(&msdos_name[baselen+1], ext);
+	/* Yes, it can happen. ".\xe5" would do it. */
+	if (IS_FREE(base))
+		base[0]='_';
+
+	/* OK, at this point we know that base is not longer than 8 symbols,
+	 * ext is not longer than 3, base is nonempty, both don't contain
+	 * any bad symbols (lowercase transformed to uppercase).
+	 */
 
-	totlen = baselen + extlen + (extlen > 0);
-	res = 0;
-	if (MSDOS_SB(dir->i_sb)->options.numtail == 0) {
-		qname.name=msdos_name;
-		qname.len=totlen;
-		res = vfat_find(dir, &qname, 0, 0, 0, &sinfo);
-	}
+	memset(name_res, ' ', MSDOS_NAME);
+	memcpy(name_res,base,baselen);
+	memcpy(name_res+8,ext,extlen);
+	if (MSDOS_SB(dir->i_sb)->options.numtail == 0)
+		if (vfat_find_form(dir, name_res) < 0)
+			return 0;
 
-	if (res > -1) {
-		/*
-		 * Try to find a unique extension.  This used to
-		 * iterate through all possibilities sequentially,
-		 * but that gave extremely bad performance.  Windows
-		 * only tries a few cases before using random
-		 * values for part of the base.
-		 */
+	/*
+	 * Try to find a unique extension.  This used to
+	 * iterate through all possibilities sequentially,
+	 * but that gave extremely bad performance.  Windows
+	 * only tries a few cases before using random
+	 * values for part of the base.
+	 */
 
-		if (2 > spaces) {
-			baselen = baselen - (2 - spaces);
-			spaces = 2;
-		}
-		msdos_name[baselen]   = '~';
-		msdos_name[baselen+2] = '.';
-		strcpy(&msdos_name[baselen+3], ext);
-		totlen = baselen + 2 + extlen + (extlen > 0);
-		qname.name=msdos_name;
-		qname.len=totlen;
-		for (i = 1; res > -1 && i < 10; i++) {
-			strncpy(msdos_name, base, baselen);
-			msdos_name[baselen+1] = i + '0';
-			res = vfat_find(dir, &qname, 0, 0, 0, &sinfo);
-		}
-	}
-	if (res > -1) {
-		i = jiffies & 0xffff;
-		sz = (jiffies >> 16) & 0x7;
-		if (6 > spaces) {
-			baselen = baselen - (6 - spaces);
-			spaces = 6;
-		}
-		msdos_name[baselen+4] = '~';
-		msdos_name[baselen+5] = '1' + sz;
-		msdos_name[baselen+6] = '.';
-		strcpy(&msdos_name[baselen+7], ext);
-		totlen = baselen + 6 + extlen + (extlen > 0);
-		qname.name=msdos_name;
-		qname.len=totlen;
-		while (res > -1) {
-			sprintf(buf, "%04x", i);
-			memcpy(&msdos_name[baselen], buf, 4);
-			msdos_name[12] = 0;
-			res = vfat_find(dir, &qname, 0, 0, 0, &sinfo);
-			i -= 11;
-		}
+	if (baselen>6)
+		baselen = 6;
+	name_res[baselen] = '~';
+	for (i = 1; i < 10; i++) {
+		name_res[baselen+1] = i + '0';
+		if (vfat_find_form(dir, name_res) < 0)
+			return 0;
 	}
 
-	res = vfat_format_name(msdos_name, totlen, name_res, 1, utf8);
-	return res;
+	i = jiffies & 0xffff;
+	sz = (jiffies >> 16) & 0x7;
+	if (baselen>2)
+		baselen = 2;
+	name_res[baselen+4] = '~';
+	name_res[baselen+5] = '1' + sz;
+	while (1) {
+		sprintf(buf, "%04X", i);
+		memcpy(&name_res[baselen], buf, 4);
+		if (vfat_find_form(dir, name_res) < 0)
+			break;
+		i -= 11;
+	}
+	return 0;
 }
 
 static loff_t vfat_find_free_slots(struct inode *dir,int slots)
@@ -996,6 +950,10 @@
 		strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
 	} else {
 		PRINTK3(("vfat_build_slots 4\n"));
+		res = vfat_valid_longname(name, len, 1, xlate);
+		if (res < 0) {
+			return res;
+		}
 		res = vfat_valid_shortname(name, len, 1, utf8);
 		if (res > -1) {
 			PRINTK3(("vfat_build_slots 5a\n"));
@@ -1007,11 +965,6 @@
 				return res;
 			}
 
-			res = vfat_valid_longname(name, len, 1, xlate);
-			if (res < 0) {
-				return res;
-			}
-
 			*is_long = 1;
 
 			return vfat_fill_long_slots(ds, name, len, msdos_name,
@@ -1066,7 +1019,7 @@
 }
 
 static int vfat_find(struct inode *dir,struct qstr* qname,
-    int find_long, int new_filename,int is_dir,struct vfat_slot_info *sinfo_out)
+    int new_filename,int is_dir,struct vfat_slot_info *sinfo_out)
 {
 	struct super_block *sb = dir->i_sb;
 	struct vfat_find_info vf;
@@ -1096,7 +1049,7 @@
 	vf.found = 0;
 	vf.posix = MSDOS_SB(sb)->options.posixfs;
 	vf.anycase = (MSDOS_SB(sb)->options.name_check != 's');
-	res = fat_readdirx(dir,&fil,(void *)&vf,vfat_readdir_cb,NULL,1,find_long,0);
+	res = fat_readdirx(dir,&fil,(void *)&vf,vfat_readdir_cb,NULL,1,1,0);
 	PRINTK3(("vfat_find: Debug 1\n"));
 	if (res < 0) goto cleanup;
 	if (vf.found) {
@@ -1117,75 +1070,70 @@
 	}
 
 	PRINTK3(("vfat_find: Debug 3\n"));
-	if (!vf.found && !new_filename) {
+	if (!new_filename) {
 		res = -ENOENT;
 		goto cleanup;
 	}
 	
 	res = vfat_build_slots(dir, qname->name, vf.len, ds, 
 			       &slots, &is_long);
+	/* Here we either have is_long and slots>=0 or slots==1 */
 	if (res < 0) goto cleanup;
 
 	de = (struct msdos_dir_entry *) ds;
 
 	bh = NULL;
-	if (new_filename) {
-		PRINTK3(("vfat_find: create file 1\n"));
-		if (is_long) slots++;
-		offset = vfat_find_free_slots(dir, slots);
-		if (offset < 0) {
-			res = offset;
+
+	PRINTK3(("vfat_find: create file 1\n"));
+	if (is_long) slots++;
+	offset = vfat_find_free_slots(dir, slots);
+	if (offset < 0) {
+		res = offset;
+		goto cleanup;
+	}
+
+	PRINTK3(("vfat_find: create file 2\n"));
+	/* Now create the new entry */
+	bh = NULL;
+	for (slot = 0, ps = ds; slot < slots; slot++, ps++) {
+		PRINTK3(("vfat_find: create file 3, slot=%d\n",slot));
+		sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de);
+		if (sinfo_out->ino < 0) {
+			PRINTK3(("vfat_find: problem\n"));
+			res = sinfo_out->ino;
 			goto cleanup;
 		}
+		memcpy(de, ps, sizeof(struct msdos_dir_slot));
+		fat_mark_buffer_dirty(sb, bh, 1);
+	}
 
-		PRINTK3(("vfat_find: create file 2\n"));
-		/* Now create the new entry */
-		bh = NULL;
-		for (slot = 0, ps = ds; slot < slots; slot++, ps++) {
-			PRINTK3(("vfat_find: create file 3, slot=%d\n",slot));
-			sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de);
-			if (sinfo_out->ino < 0) {
-				PRINTK3(("vfat_find: problem\n"));
-				res = sinfo_out->ino;
-				goto cleanup;
-			}
-			memcpy(de, ps, sizeof(struct msdos_dir_slot));
-			fat_mark_buffer_dirty(sb, bh, 1);
-		}
+	PRINTK3(("vfat_find: create file 4\n"));
+	dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
+	mark_inode_dirty(dir);
 
-		PRINTK3(("vfat_find: create file 4\n"));
-		dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
-		mark_inode_dirty(dir);
-
-		PRINTK3(("vfat_find: create file 5\n"));
-
-		fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
-		de->ctime_ms = 0;
-		de->ctime = de->time;
-		de->adate = de->cdate = de->date;
-		de->start = 0;
-		de->starthi = 0;
-		de->size = 0;
-		de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
-		de->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT;
+	PRINTK3(("vfat_find: create file 5\n"));
 
+	fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
+	de->ctime_ms = 0;
+	de->ctime = de->time;
+	de->adate = de->cdate = de->date;
+	de->start = 0;
+	de->starthi = 0;
+	de->size = 0;
+	de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
+	de->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT;
 
-		fat_mark_buffer_dirty(sb, bh, 1);
-		fat_brelse(sb, bh);
 
-		sinfo_out->is_long = (slots > 1) ? 1 : 0;
-		if (sinfo_out->is_long) {
-			sinfo_out->long_slots = slots - 1;
-		} else {
-			sinfo_out->long_slots = 0;
-		}
-		sinfo_out->total_slots = slots;
-		sinfo_out->shortname_offset = offset - sizeof(struct msdos_dir_slot);
-		sinfo_out->longname_offset = offset - sizeof(struct msdos_dir_slot) * slots;
-		res = 0;
-	} else {
-		res = -ENOENT;
-	}
+	fat_mark_buffer_dirty(sb, bh, 1);
+	fat_brelse(sb, bh);
+
+	/* slots can't be less than 1 */
+	sinfo_out->is_long = (slots > 1);
+	sinfo_out->long_slots = slots - 1;
+	sinfo_out->total_slots = slots;
+	sinfo_out->shortname_offset = offset - sizeof(struct msdos_dir_slot);
+	sinfo_out->longname_offset = offset - sizeof(struct msdos_dir_slot) * slots;
+	res = 0;
 
 cleanup:
 	kfree(ds);
@@ -1206,7 +1154,7 @@
 	dentry->d_op = &vfat_dentry_ops[table];
 
 	result = NULL;
-	if ((res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo)) < 0) {
+	if ((res = vfat_find(dir,&dentry->d_name,0,0,&sinfo)) < 0) {
 		result = NULL;
 		table++;
 		goto error;
@@ -1242,7 +1190,7 @@
 
 	*result=0;
 	PRINTK1(("vfat_create_entry: Entering\n"));
-	res = vfat_find(dir, qname, 1, 1, is_dir, &sinfo);
+	res = vfat_find(dir, qname, 1, is_dir, &sinfo);
 	if (res < 0) {
 		return res;
 	}
@@ -1387,9 +1335,6 @@
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
 
-	if (dir->i_count > 1) {
-		return -EBUSY;
-	}
 	if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
 		pos = 0;
 		bh = NULL;
@@ -1410,88 +1355,50 @@
 	return 0;
 }
 
-static int vfat_rmdir_free_ino(struct inode *dir,struct buffer_head *bh,
-     struct msdos_dir_entry *de,struct dentry* dentry)
+static void vfat_free_ino(struct inode *dir,struct buffer_head *bh,
+     struct msdos_dir_entry *de,struct inode* victim)
 {
 	struct super_block *sb = dir->i_sb;
-	int res;
-
-	if (!list_empty(&dentry->d_hash))
-		return -EBUSY;
-
-	res = vfat_empty(dentry->d_inode);
-	if (res) {
-		return res;
-	}
-	dentry->d_inode->i_nlink = 0;
-	dentry->d_inode->i_mtime = dir->i_mtime = CURRENT_TIME;
-	dentry->d_inode->i_atime = dir->i_atime = CURRENT_TIME;
-	dir->i_nlink--;
-	mark_inode_dirty(dir);
-	mark_inode_dirty(dentry->d_inode);
-	de->name[0] = DELETED_FLAG;
-	fat_mark_buffer_dirty(sb, bh, 1);
-
-	return 0;
-}
-
-static int vfat_unlink_free_ino(struct inode *dir,struct buffer_head *bh,
-     struct msdos_dir_entry *de,struct dentry* dentry,int nospc)
-{
-	struct super_block *sb = dir->i_sb;
-	if ((!S_ISREG(dentry->d_inode->i_mode) && nospc) || 
-	    IS_IMMUTABLE(dentry->d_inode)) {
-		return -EPERM;
-	}
-	dentry->d_inode->i_nlink = 0;
-	dentry->d_inode->i_mtime = dir->i_mtime = CURRENT_TIME;
-	dentry->d_inode->i_atime = dir->i_atime = CURRENT_TIME;
+	victim->i_nlink = 0;
+	victim->i_mtime = dir->i_mtime = CURRENT_TIME;
+	victim->i_atime = dir->i_atime = CURRENT_TIME;
 	dir->i_version = ++event;
-	MSDOS_I(dentry->d_inode)->i_busy = 1;
+	MSDOS_I(victim)->i_busy = 1;
 	mark_inode_dirty(dir);
-	mark_inode_dirty(dentry->d_inode);
+	mark_inode_dirty(victim);
 	de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, bh, 1);
-
-	return 0;
 }
 
 static int vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo,
-     struct buffer_head **bh,struct dentry* dentry,
-     int is_dir,int nospc)
+     struct inode* victim)
 {
 	struct super_block *sb = dir->i_sb;
 	loff_t offset;
+	struct buffer_head *bh=NULL;
 	struct msdos_dir_entry *de;
 	int res, i;
 
 	/* remove the shortname */
 	offset = sinfo->shortname_offset;
-	res = fat_get_entry(dir, &offset, bh, &de);
+	res = fat_get_entry(dir, &offset, &bh, &de);
 	if (res < 0) return res;
-	if (is_dir) {
-		res = vfat_rmdir_free_ino(dir,*bh,de,dentry);
-	} else {
-		res = vfat_unlink_free_ino(dir,*bh,de,dentry,nospc);
-	}
-	if (res < 0) return res;
-		
+	vfat_free_ino(dir,bh,de,victim);
 	/* remove the longname */
 	offset = sinfo->longname_offset;
 	for (i = sinfo->long_slots; i > 0; --i) {
-		res = fat_get_entry(dir, &offset, bh, &de);
-		if (res < 0) {
+		if (fat_get_entry(dir, &offset, &bh, &de) < 0)
 			continue;
-		}
 		de->name[0] = DELETED_FLAG;
 		de->attr = 0;
-		fat_mark_buffer_dirty(sb, *bh, 1);
+		fat_mark_buffer_dirty(sb, bh, 1);
 	}
+	if (bh) fat_brelse(sb, bh);
 	return 0;
 }
 
-/* Replace inodes in alias dentries and drop all but the initial dentry */
-static void drop_replace_inodes(struct dentry *dentry, struct inode *inode)
+/* Drop all aliases */
+static void drop_aliases(struct dentry *dentry)
 {
 	struct list_head *head, *next, *tmp;
 	struct dentry *alias;
@@ -1507,12 +1414,6 @@
 			if (alias == dentry)
 				continue;
 
-			if (inode) {
-				list_del(&alias->d_alias);
-				iput(alias->d_inode);
-				d_instantiate(alias, inode);
-				inode->i_count++;
-			}
 			d_drop(alias);
 		}
 	}
@@ -1520,22 +1421,30 @@
 
 static int vfat_rmdirx(struct inode *dir,struct dentry* dentry)
 {
-	struct super_block *sb = dir->i_sb;
 	int res;
-	struct buffer_head *bh;
 	struct vfat_slot_info sinfo;
 
 	PRINTK1(("vfat_rmdirx: dentry=%p\n", dentry));
-	res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo);
+	res = vfat_find(dir,&dentry->d_name,0,0,&sinfo);
 
 	if (res >= 0 && sinfo.total_slots > 0) {
-		bh = NULL;
-		res = vfat_remove_entry(dir,&sinfo,&bh,dentry,1,0);
-		if (res > 0) {
+		if (!list_empty(&dentry->d_hash))
+			return -EBUSY;
+		/* Take care of aliases */
+		if (dentry->d_inode->i_count > 1) {
+			shrink_dcache_parent(dentry->d_parent);
+			if (dentry->d_inode->i_count > 1)
+				return -EBUSY;
+		}
+		res = vfat_empty(dentry->d_inode);
+		if (res)
+			return res;
+		
+		res = vfat_remove_entry(dir,&sinfo,dentry->d_inode);
+		if (res >= 0) {
+			dir->i_nlink--;
 			res = 0;
 		}
-		dir->i_version = ++event;
-		if (bh) fat_brelse(sb, bh);
 	}
 	return res;
 }
@@ -1549,10 +1458,7 @@
 	res = -EBUSY;
 	if (list_empty(&dentry->d_hash)) {
 		res = vfat_rmdirx(dir, dentry);
-		if (res >= 0) {
-			drop_replace_inodes(dentry, NULL);
-			d_delete(dentry);
-		}
+		/* If that went OK all aliases are already dropped */
 	}
 	return res;
 }
@@ -1562,23 +1468,22 @@
 	struct dentry* dentry,
 	int nospc)	/* Flag special file ? */
 {
-	struct super_block *sb = dir->i_sb;
 	int res;
-	struct buffer_head *bh;
 	struct vfat_slot_info sinfo;
 
 	PRINTK1(("vfat_unlinkx: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
-	bh = NULL;
-	res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo);
+	res = vfat_find(dir,&dentry->d_name,0,0,&sinfo);
 
 	if (res >= 0 && sinfo.total_slots > 0) {
-		res = vfat_remove_entry(dir,&sinfo,&bh,dentry,0,nospc);
+		if (!S_ISREG(dentry->d_inode->i_mode) && nospc) {
+			return -EPERM;
+		}
+		res = vfat_remove_entry(dir,&sinfo,dentry->d_inode);
 		if (res > 0) {
 			res = 0;
 		}
 	}
 
-	if (bh) fat_brelse(sb, bh);
 	return res;
 }
 
@@ -1586,6 +1491,7 @@
 int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode)
 {
 	struct inode *inode;
+	struct vfat_slot_info sinfo;
 	int res;
 
 	PRINTK1(("vfat_mkdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
@@ -1597,17 +1503,28 @@
 
 	dir->i_nlink++;
 	inode->i_nlink = 2; /* no need to mark them dirty */
-	MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
 
 	res = vfat_create_dotdirs(inode, dir);
+	if (res < 0)
+		goto mkdir_failed;
 	fat_unlock_creation();
-	MSDOS_I(inode)->i_busy = 0;
 	dentry->d_time = dentry->d_parent->d_inode->i_version;
 	d_instantiate(dentry,inode);
-	if (res < 0) {
-		if (vfat_rmdir(dir,dentry) < 0)
-			fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
-	}
+	return res;
+
+mkdir_failed:
+	fat_unlock_creation();
+	if (vfat_find(dir,&dentry->d_name,0,0,&sinfo) < 0)
+		goto mkdir_panic;
+	if (vfat_remove_entry(dir, &sinfo, inode) < 0)
+		goto mkdir_panic;
+	iput(inode);
+	dir->i_nlink--;
+	return res;
+
+mkdir_panic:
+	dir->i_version = ++event;
+	fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
 	return res;
 }
 
@@ -1619,7 +1536,7 @@
 	PRINTK1(("vfat_unlink: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
 	res = vfat_unlinkx (dir,dentry,1);
 	if (res >= 0) {
-		drop_replace_inodes(dentry, NULL);
+		drop_aliases(dentry);
 		d_delete(dentry);
 	}
 	return res;
@@ -1640,7 +1557,7 @@
 {
 	struct super_block *sb = old_dir->i_sb;
 	struct buffer_head *old_bh,*new_bh,*dotdot_bh;
-	struct msdos_dir_entry *old_de,*new_de,*dotdot_de;
+	struct msdos_dir_entry *old_de,*dotdot_de;
 	loff_t old_offset,new_offset,old_longname_offset;
 	int old_slots,old_ino,new_ino,dotdot_ino;
 	struct inode *old_inode, *new_inode, *dotdot_inode;
@@ -1648,21 +1565,23 @@
 	int res, is_dir, i;
 	int locked = 0;
 	struct vfat_slot_info sinfo;
-	int put_new_inode = 0;
 
 	PRINTK1(("vfat_rename: Entering: old_dentry=%p, old_inode=%p, old ino=%ld, new_dentry=%p, new_inode=%p, new ino=%ld\n",
 		 old_dentry, old_dentry->d_inode, old_dentry->d_inode->i_ino,
 		 new_dentry, new_dentry->d_inode,
 		 new_dentry->d_inode ? new_dentry->d_inode->i_ino : 0));
-	if (old_dir == new_dir && 
-	    old_dentry->d_name.len == new_dentry->d_name.len &&
-	    strncmp(old_dentry->d_name.name, new_dentry->d_name.name, 
-		    old_dentry->d_name.len) == 0)
+	/*
+	 * POSIX is braindead (surprise, surprise). It requires that rename()
+	 * should return 0 and do nothing if the target has the same inode as
+	 * the source. Somebody, get a time machine, return to '89 and tell
+	 * RMS & Co *not* to do that idiocy, FAST!
+	 */
+	if (old_dentry->d_inode == new_dentry->d_inode)
 		return 0;
 
 	old_bh = new_bh = NULL;
 	old_inode = new_inode = NULL;
-	res = vfat_find(old_dir,&old_dentry->d_name,1,0,0,&sinfo);
+	res = vfat_find(old_dir,&old_dentry->d_name,0,0,&sinfo);
 	PRINTK3(("vfat_rename 2\n"));
 	if (res < 0) goto rename_done;
 
@@ -1677,90 +1596,102 @@
 	res = -ENOENT;
 	old_inode = old_dentry->d_inode;
 	is_dir = S_ISDIR(old_inode->i_mode);
+
+	/*
+	 * Race: we can be hit by another rename after this check.
+	 * For the time being use fat_lock_creation(), but it's
+	 * ugly. FIXME.
+	 */
+
+	fat_lock_creation(); locked = 1;
+
 	if (is_dir) {
-		if ((old_dir->i_dev != new_dir->i_dev) ||
-		    (old_ino == new_dir->i_ino)) {
+		/* We can't use d_subdir() here. Arrgh. */
+		for (walk=new_dentry;walk!=walk->d_parent;walk=walk->d_parent) {
+			if (walk->d_inode != old_dentry->d_inode)
+				continue;
 			res = -EINVAL;
 			goto rename_done;
 		}
-		walk = new_dentry;
-		/* prevent moving directory below itself */
-		for (;;) {
-			if (walk == old_dentry) return -EINVAL;
-			if (walk == walk->d_parent) break;
-			walk = walk->d_parent;
-		}
 	}
 
-	res = vfat_find(new_dir,&new_dentry->d_name,1,0,is_dir,&sinfo);
-
-	PRINTK3(("vfat_rename 4\n"));
-	if (res > -1) {
-		int new_is_dir;
-
-		PRINTK3(("vfat_rename 5\n"));
-		/* Filename currently exists.  Need to delete it */
-		new_offset = sinfo.shortname_offset;
-		res = fat_get_entry(new_dir, &new_offset, &new_bh, &new_de);
-		PRINTK3(("vfat_rename 6\n"));
-		if (res < 0) goto rename_done;
+	if (new_dentry->d_inode) {
+		/*
+		 * OK, we have to remove the target. We should do it so
+		 * that nobody might go and find it negative. Actually we
+		 * should give warranties wrt preserving target over the
+		 * possible crash, but that's another story. We can't
+		 * get here with the target unhashed, so the directory entry
+		 * must exist.
+		 */
 
-		if (!(new_inode = iget(new_dir->i_sb,res)))
+		new_inode = new_dentry->d_inode;
+		res = vfat_find(new_dir,&new_dentry->d_name,0,is_dir,&sinfo);
+		if (res < 0 || new_inode->i_ino != sinfo.ino) {
+			/* WTF??? Cry and fail. */
+			printk(KERN_WARNING "vfat_rename: fs corrupted\n");
 			goto rename_done;
-		new_is_dir = S_ISDIR(new_inode->i_mode);
-		iput(new_inode);
-		if (new_is_dir) {
-			PRINTK3(("vfat_rename 7\n"));
-			res = vfat_rmdirx(new_dir,new_dentry);
-			PRINTK3(("vfat_rename 8\n"));
-			if (res < 0) goto rename_done;
-		} else {
-			/* Is this the same file, different case? */
-			if (new_inode != old_inode) {
-				PRINTK3(("vfat_rename 9\n"));
-				res = vfat_unlink(new_dir,new_dentry);
-				PRINTK3(("vfat_rename 10\n"));
-				if (res < 0) goto rename_done;
+		}
+
+		if (is_dir) {
+			/*
+			 * Target is a directory. No other owners will
+			 * be tolerated.
+			 */
+			res = -EBUSY;
+			if (d_invalidate(new_dentry) < 0)
+				goto rename_done;
+			/*
+			 * OK, let's try to get rid of other dentries.
+			 * No need to do it if i_count is 1.
+			 */
+			if (new_inode->i_count>1) {
+				shrink_dcache_parent(new_dentry->d_parent);
+				if (new_inode->i_count>1)
+					goto rename_done;
 			}
+			res = vfat_empty(new_inode);
+			if (res)
+				goto rename_done;
+		} else {
+			drop_aliases(new_dentry);
 		}
 	}
+	res = vfat_remove_entry(new_dir,&sinfo,new_inode);
+	if (res)
+		goto rename_done;
+
+	/* Serious lossage here. FAT uses braindead inode numbers scheme,
+	 * so we can't simply cannibalize the entry. It means that we have
+	 * no warranties that crash here will not make target disappear
+	 * after reboot. Lose, lose. Nothing to do with that until we'll
+	 * separate the functions of i_ino: it serves both as a search key
+	 * in icache and as a part of stat output. It would kill all the
+	 * 'busy' stuff on the spot. Later.
+	 */
 
-	PRINTK3(("vfat_rename 11\n"));
-	fat_lock_creation(); locked = 1;
-	res = vfat_find(new_dir,&new_dentry->d_name,1,1,is_dir,&sinfo);
+	if (is_dir)
+		new_dir->i_nlink--;
+
+	res = vfat_find(new_dir,&new_dentry->d_name,1,is_dir,&sinfo);
 
-	PRINTK3(("vfat_rename 12\n"));
 	if (res < 0) goto rename_done;
 
 	new_offset = sinfo.shortname_offset;
 	new_ino = sinfo.ino;
-	PRINTK3(("vfat_rename 13: new_ino=%d\n", new_ino));
-
-	if (!(new_inode = iget(new_dir->i_sb,new_ino))) goto rename_done;
-	put_new_inode = 1;
 
-	new_inode->i_mode   = old_inode->i_mode;
-	new_inode->i_size   = old_inode->i_size;
-	new_inode->i_blocks = old_inode->i_blocks;
-	new_inode->i_mtime  = old_inode->i_mtime;
-	new_inode->i_atime  = old_inode->i_atime;
-	new_inode->i_ctime  = old_inode->i_ctime;
-	new_inode->i_nlink  = old_inode->i_nlink;
-	new_inode->i_op     = old_inode->i_op;
-	MSDOS_I(new_inode)->i_ctime_ms = MSDOS_I(old_inode)->i_ctime_ms;
-
-	MSDOS_I(new_inode)->i_start = MSDOS_I(old_inode)->i_start;
-	MSDOS_I(new_inode)->i_logstart = MSDOS_I(old_inode)->i_logstart;
-	MSDOS_I(new_inode)->i_attrs = MSDOS_I(old_inode)->i_attrs;
+	/* XXX: take care of other owners */
 
+	remove_inode_hash(old_inode);
 	fat_cache_inval_inode(old_inode);
-	mark_inode_dirty(new_inode);
+	old_inode->i_ino = new_ino;
+	old_inode->i_version = ++event;
+	insert_inode_hash(old_inode);
+	mark_inode_dirty(old_inode);
 
 	old_dir->i_version = ++event;
 	new_dir->i_version = ++event;
 
-	PRINTK3(("vfat_rename 14: old_slots=%d\n",old_slots));
-
 	/* remove the old entry */
 	for (i = old_slots; i > 0; --i) {
 		res = fat_get_entry(old_dir, &old_longname_offset, &old_bh, &old_de);
@@ -1772,9 +1703,8 @@
 		old_de->attr = 0;
 		fat_mark_buffer_dirty(sb, old_bh, 1);
 	}
-	PRINTK3(("vfat_rename 15b\n"));
 
-	if (S_ISDIR(old_inode->i_mode)) {
+	if (is_dir) {
 		if ((res = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
 		    &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done;
 		if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) {
@@ -1796,26 +1726,11 @@
 		fat_brelse(sb, dotdot_bh);
 	}
 
-	/*
-	 * This convinces the VFS layer to drop the old inode,
-	 * but at the same time fools the VFAT layer to not
-	 * actually delete any of the blocks in the old file
-	 * (because they are very much used by the renamed file)
-	 */
-	MSDOS_I(old_inode)->i_start = 0;
-	MSDOS_I(old_inode)->i_logstart = 0;
-	old_inode->i_nlink = 0;
-
-	if (res > 0) res = 0;
-
-	if (res == 0) {
-		drop_replace_inodes(old_dentry, new_inode);
-		list_del(&old_dentry->d_alias);
-		iput(old_dentry->d_inode);
-		d_instantiate(old_dentry, new_inode);
-
+	if (res >= 0) {
+		if (new_inode && is_dir)
+			d_rehash(new_dentry);
 		d_move(old_dentry, new_dentry);
-		put_new_inode = 0;
+		res = 0;
 	}
 
 rename_done:
@@ -1825,9 +1740,8 @@
 		fat_brelse(sb, old_bh);
 	if (new_bh)
 		fat_brelse(sb, new_bh);
-	if (put_new_inode)
-		iput(new_inode);
 	return res;
+
 }
 
 
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-alpha/processor.h linux/include/asm-alpha/processor.h
--- v2.2.0-pre7/linux/include/asm-alpha/processor.h	Fri Oct 23 22:01:22 1998
+++ linux/include/asm-alpha/processor.h	Mon Jan 18 09:55:30 1999
@@ -65,7 +65,7 @@
 };
 
 #define INIT_MMAP { &init_mm, PAGE_OFFSET,  PAGE_OFFSET+0x10000000, \
-	PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, NULL, &init_mm.mmap }
+	NULL, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL }
 
 #define INIT_TSS  { \
 	0, 0, 0, \
@@ -113,6 +113,7 @@
 
 #define copy_segments(nr, tsk, mm)	do { } while (0)
 #define release_segments(mm)		do { } while (0)
+#define forget_segments()		do { } while (0)
 
 /* NOTE: The task struct and the stack go together!  */
 #define alloc_task_struct() \
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-alpha/semaphore.h linux/include/asm-alpha/semaphore.h
--- v2.2.0-pre7/linux/include/asm-alpha/semaphore.h	Tue May 13 22:41:15 1997
+++ linux/include/asm-alpha/semaphore.h	Sat Jan 16 17:02:51 1999
@@ -7,28 +7,80 @@
  * (C) Copyright 1996 Linus Torvalds
  */
 
+#include <asm/current.h>
+#include <asm/system.h>
 #include <asm/atomic.h>
 
+/*
+ * Semaphores are recursive: we allow the holder process to recursively do
+ * down() operations on a semaphore that the process already owns. In order
+ * to do that, we need to keep a semaphore-local copy of the owner and the
+ * "depth of ownership".
+ *
+ * NOTE! Nasty memory ordering rules:
+ *  - "owner" and "owner_count" may only be modified once you hold the lock.
+ *  - "owner_count" must be written _after_ modifying owner, and must be
+ *  read _before_ reading owner. There must be appropriate write and read
+ *  barriers to enforce this.
+ */
+
 struct semaphore {
 	atomic_t count;
 	atomic_t waking;
+	struct task_struct *owner;
+	long owner_depth;
 	struct wait_queue * wait;
 };
 
-#define MUTEX ((struct semaphore) { ATOMIC_INIT(1), ATOMIC_INIT(0), NULL })
-#define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), ATOMIC_INIT(0), NULL })
+#define MUTEX ((struct semaphore) \
+ { ATOMIC_INIT(1), ATOMIC_INIT(0), NULL, 0, NULL })
+#define MUTEX_LOCKED ((struct semaphore) \
+ { ATOMIC_INIT(0), ATOMIC_INIT(0), NULL, 1, NULL })
+
+#define semaphore_owner(sem)	((sem)->owner)
+#define sema_init(sem, val)	atomic_set(&((sem)->count), val)
 
 extern void __down(struct semaphore * sem);
 extern int  __down_interruptible(struct semaphore * sem);
 extern void __up(struct semaphore * sem);
 
-#define sema_init(sem, val)	atomic_set(&((sem)->count), val)
+/* All three have custom assembly linkages.  */
+extern void __down_failed(struct semaphore * sem);
+extern void __down_failed_interruptible(struct semaphore * sem);
+extern void __up_wakeup(struct semaphore * sem);
+
 
 /*
  * These two _must_ execute atomically wrt each other.
  *
  * This is trivially done with load_locked/store_cond,
  * which we have.  Let the rest of the losers suck eggs.
+ *
+ * Tricky bits --
+ *
+ * (1) One task does two downs, no other contention
+ *	initial state:
+ *		count = 1, waking = 0, depth = undef;
+ *	down(&sem)
+ *		count = 0, waking = 0, depth = 1;
+ *	down(&sem)
+ *		atomic dec and test sends us to waking_non_zero via __down
+ *			count = -1, waking = 0;
+ *		conditional atomic dec on waking discovers no free slots
+ *			count = -1, waking = 0;
+ *		test for owner succeeeds and we return ok.
+ *			count = -1, waking = 0, depth = 2;
+ *	up(&sem)
+ *		dec depth
+ *			count = -1, waking = 0, depth = 0;
+ *		atomic inc and test sends us to slow path
+ *			count = 0, waking = 0, depth = 0;
+ *		notice !(depth < 0) and don't call __up.
+ *	up(&sem)
+ *		dec depth
+ *			count = 0, waking = 0, depth = -1;
+ *		atomic inc and test succeeds.
+ *			count = 1, waking = 0, depth = 0;
  */
 
 static inline void wake_one_more(struct semaphore * sem)
@@ -36,48 +88,153 @@
 	atomic_inc(&sem->waking);
 }
 
-static inline int waking_non_zero(struct semaphore *sem)
+static inline int waking_non_zero(struct semaphore *sem,
+				  struct task_struct *tsk)
 {
+	long owner_depth;
 	int ret, tmp;
 
+	owner_depth = sem->owner_depth;
+	
+	/* Atomic decrement, iff the value is > 0.  */
 	__asm__ __volatile__(
 	"1:	ldl_l	%1,%2\n"
 	"	ble	%1,2f\n"
 	"	subl	%1,1,%0\n"
 	"	stl_c	%0,%2\n"
 	"	beq	%0,3f\n"
-	"2:\n"
+	"2:	mb\n"
 	".section .text2,\"ax\"\n"
 	"3:	br	1b\n"
 	".previous"
 	: "=r"(ret), "=r"(tmp), "=m"(__atomic_fool_gcc(&sem->waking))
 	: "0"(0));
 
+	ret |= ((owner_depth != 0) & (sem->owner == tsk));
+	if (ret) {
+		sem->owner = tsk;
+		wmb();
+		/* Don't use the old value, which is stale in the
+		   !owner case.  */
+		sem->owner_depth++;
+	}
+
 	return ret;
 }
 
 /*
- * This isn't quite as clever as the x86 side, but the gp register
- * makes things a bit more complicated on the alpha..
+ * Whee.  Hidden out of line code is fun.  The contention cases are
+ * handled out of line in kernel/sched.c; arch/alpha/lib/semaphore.S
+ * takes care of making sure we can call it without clobbering regs.
  */
+
 extern inline void down(struct semaphore * sem)
 {
-	if (atomic_dec_return(&sem->count) < 0)
-		__down(sem);
+	/* Given that we have to use particular hard registers to 
+	   communicate with __down_failed anyway, reuse them in 
+	   the atomic operation as well. 
+
+	   __down_failed takes the semaphore address in $24, and
+	   it's return address in $28.  The pv is loaded as usual.
+	   The gp is clobbered (in the module case) as usual.  */
+
+	__asm__ __volatile__ (
+		"/* semaphore down operation */\n"
+		"1:	ldl_l	$27,%3\n"
+		"	subl	$27,1,$27\n"
+		"	mov	$27,$28\n"
+		"	stl_c	$28,%0\n"
+		"	beq	$28,2f\n"
+		"	blt	$27,3f\n"
+		/* Got the semaphore no contention.  Set owner and depth.  */
+		"	stq	$8,%1\n"
+		"	lda	$28,1\n"
+		"	wmb\n"
+		"	stq	$28,%2\n"
+		"4:	mb\n"
+		".section .text2,\"ax\"\n"
+		"2:	br	1b\n"
+		"3:	lda	$24,%3\n"
+		"	jsr	$28,__down_failed\n"
+		"	ldgp	$29,0($28)\n"
+		"	br	4b\n"
+		".previous"
+		: "=m"(sem->count), "=m"(sem->owner), "=m"(sem->owner_depth)
+		: "m"(sem->count)
+		: "$24", "$27", "$28", "memory");
 }
 
 extern inline int down_interruptible(struct semaphore * sem)
 {
-	int ret = 0;
-	if (atomic_dec_return(&sem->count) < 0)
-		ret = __down_interruptible(sem);
+	/* __down_failed_interruptible takes the semaphore address in $24,
+	   and it's return address in $28.  The pv is loaded as usual.
+	   The gp is clobbered (in the module case) as usual.  The return
+	   value is in $24.  */
+
+	register int ret __asm__("$24");
+
+	__asm__ __volatile__ (
+		"/* semaphore down interruptible operation */\n"
+		"1:	ldl_l	$27,%4\n"
+		"	subl	$27,1,$27\n"
+		"	mov	$27,$28\n"
+		"	stl_c	$28,%1\n"
+		"	beq	$28,2f\n"
+		"	blt	$27,3f\n"
+		/* Got the semaphore no contention.  Set owner and depth.  */
+		"	stq	$8,%2\n"
+		"	lda	$28,1\n"
+		"	wmb\n"
+		"	stq	$28,%3\n"
+		"	mov	$31,$24\n"
+		"4:	mb\n"
+		".section .text2,\"ax\"\n"
+		"2:	br	1b\n"
+		"3:	lda	$24,%4\n"
+		"	jsr	$28,__down_failed_interruptible\n"
+		"	ldgp	$29,0($28)\n"
+		"	br	4b\n"
+		".previous"
+		: "=r"(ret), "=m"(sem->count), "=m"(sem->owner),
+		  "=m"(sem->owner_depth)
+		: "m"(sem->count)
+		: "$27", "$28", "memory");
+
 	return ret;
 }
 
 extern inline void up(struct semaphore * sem)
 {
-	if (atomic_inc_return(&sem->count) <= 0)
-		__up(sem);
-}	
+	/* Given that we have to use particular hard registers to 
+	   communicate with __up_wakeup anyway, reuse them in 
+	   the atomic operation as well. 
+
+	   __up_wakeup takes the semaphore address in $24, and
+	   it's return address in $28.  The pv is loaded as usual.
+	   The gp is clobbered (in the module case) as usual.  */
+
+	__asm__ __volatile__ (
+		"/* semaphore up operation */\n"
+		"	mb\n"
+		"1:	ldl_l	$27,%1\n"
+		"	addl	$27,1,$27\n"
+		"	mov	$27,$28\n"
+		"	stl_c	$28,%0\n"
+		"	beq	$28,2f\n"
+		"	mb\n"
+		"	ble	$27,3f\n"
+		"4:\n"
+		".section .text2,\"ax\"\n"
+		"2:	br	1b\n"
+		"3:	lda	$24,%1\n"
+		"	bge	%2,4b\n"
+		"	jsr	$28,__up_wakeup\n"
+		"	ldgp	$29,0($28)\n"
+		"	br	4b\n"
+		".previous"
+		: "=m"(sem->count)
+		: "m"(sem->count), "r"(--sem->owner_depth)
+		: "$24", "$27", "$28", "memory");
+}
 
 #endif
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-alpha/system.h linux/include/asm-alpha/system.h
--- v2.2.0-pre7/linux/include/asm-alpha/system.h	Thu Dec 31 10:29:02 1998
+++ linux/include/asm-alpha/system.h	Sat Jan 16 17:02:51 1999
@@ -107,6 +107,9 @@
 #define mb() \
 __asm__ __volatile__("mb": : :"memory")
 
+#define rmb() \
+__asm__ __volatile__("mb": : :"memory")
+
 #define wmb() \
 __asm__ __volatile__("wmb": : :"memory")
 
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-arm/processor.h linux/include/asm-arm/processor.h
--- v2.2.0-pre7/linux/include/asm-arm/processor.h	Wed Sep  9 14:51:12 1998
+++ linux/include/asm-arm/processor.h	Mon Jan 18 09:55:30 1999
@@ -53,11 +53,9 @@
 extern void release_thread(struct task_struct *);
 
 /* Copy and release all segment info associated with a VM */
-extern void copy_segments(int nr, struct task_struct *p, struct mm_struct * mm);
-extern void release_segments(struct mm_struct * mm);
-
 #define copy_segments(nr, tsk, mm)	do { } while (0)
 #define release_segments(mm)		do { } while (0)
+#define forget_segments()		do { } while (0)
 
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-arm/semaphore.h linux/include/asm-arm/semaphore.h
--- v2.2.0-pre7/linux/include/asm-arm/semaphore.h	Fri Jan  8 22:36:23 1999
+++ linux/include/asm-arm/semaphore.h	Thu Jan 14 15:53:25 1999
@@ -48,7 +48,7 @@
 	restore_flags(flags);
 }
 
-static inline int waking_non_zero(struct semaphore *sem)
+static inline int waking_non_zero(struct semaphore *sem, struct task_struct *tsk)
 {
 	unsigned long flags;
 	int ret = 0;
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- v2.2.0-pre7/linux/include/asm-i386/processor.h	Fri Jan  1 12:58:21 1999
+++ linux/include/asm-i386/processor.h	Mon Jan 18 17:33:46 1999
@@ -238,7 +238,7 @@
 };
 
 #define INIT_MMAP \
-{ &init_mm, 0, 0, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, NULL, &init_mm.mmap }
+{ &init_mm, 0, 0, NULL, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL }
 
 #define INIT_TSS  {						\
 	0,0, /* back_link, __blh */				\
@@ -281,6 +281,7 @@
 /* Copy and release all segment info associated with a VM */
 extern void copy_segments(int nr, struct task_struct *p, struct mm_struct * mm);
 extern void release_segments(struct mm_struct * mm);
+extern void forget_segments(void);
 
 /*
  * FPU lazy state save handling..
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-i386/semaphore.h linux/include/asm-i386/semaphore.h
--- v2.2.0-pre7/linux/include/asm-i386/semaphore.h	Wed Jan 13 15:00:43 1999
+++ linux/include/asm-i386/semaphore.h	Mon Jan 18 17:33:46 1999
@@ -23,14 +23,49 @@
 #include <asm/atomic.h>
 #include <asm/spinlock.h>
 
+/*
+ * Semaphores are recursive: we allow the holder process
+ * to recursively do down() operations on a semaphore that
+ * the process already owns. In order to do that, we need
+ * to keep a semaphore-local copy of the owner and the
+ * "depth of ownership".
+ *
+ * NOTE! Nasty memory ordering rules:
+ *  - "owner" and "owner_count" may only be modified once you hold the
+ *    lock. 
+ *  - "owner_count" must be written _after_ modifying owner, and
+ *    must be read _before_ reading owner. There must be appropriate
+ *    write and read barriers to enforce this.
+ *
+ * On an x86, writes are always ordered, so the only enformcement
+ * necessary is to make sure that the owner_depth is written after
+ * the owner value in program order.
+ *
+ * For read ordering guarantees, the semaphore wake_lock spinlock
+ * is already giving us ordering guarantees.
+ *
+ * Other (saner) architectures would use "wmb()" and "rmb()" to
+ * do this in a more obvious manner.
+ */
 struct semaphore {
 	atomic_t count;
+	unsigned long owner, owner_depth;
 	int waking;
 	struct wait_queue * wait;
 };
 
-#define MUTEX ((struct semaphore) { ATOMIC_INIT(1), 0, NULL })
-#define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), 0, NULL })
+/*
+ * Because we want the non-contention case to be
+ * fast, we save the stack pointer into the "owner"
+ * field, and to get the true task pointer we have
+ * to do the bit masking. That moves the masking
+ * operation into the slow path.
+ */
+#define semaphore_owner(sem) \
+	((struct task_struct *)((2*PAGE_MASK) & (sem)->owner))
+
+#define MUTEX ((struct semaphore) { ATOMIC_INIT(1), 0, 0, 0, NULL })
+#define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), 0, 1, 0, NULL })
 
 asmlinkage void __down_failed(void /* special register calling convention */);
 asmlinkage int  __down_failed_interruptible(void  /* params in registers */);
@@ -59,13 +94,53 @@
 	spin_unlock_irqrestore(&semaphore_wake_lock, flags);
 }
 
-static inline int waking_non_zero(struct semaphore *sem)
+/*
+ * NOTE NOTE NOTE!
+ *
+ * We read owner-count _before_ getting the semaphore. This
+ * is important, because the semaphore also acts as a memory
+ * ordering point between reading owner_depth and reading
+ * the owner.
+ *
+ * Why is this necessary? The "owner_depth" essentially protects
+ * us from using stale owner information - in the case that this
+ * process was the previous owner but somebody else is racing to
+ * aquire the semaphore, the only way we can see ourselves as an
+ * owner is with "owner_depth" of zero (so that we know to avoid
+ * the stale value).
+ *
+ * In the non-race case (where we really _are_ the owner), there
+ * is not going to be any question about what owner_depth is.
+ *
+ * In the race case, the race winner will not even get here, because
+ * it will have successfully gotten the semaphore with the locked
+ * decrement operation.
+ *
+ * Basically, we have two values, and we cannot guarantee that either
+ * is really up-to-date until we have aquired the semaphore. But we
+ * _can_ depend on a ordering between the two values, so we can use
+ * one of them to determine whether we can trust the other:
+ *
+ * Cases:
+ *  - owner_depth == zero: ignore the semaphore owner, because it
+ *    cannot possibly be us. Somebody else may be in the process
+ *    of modifying it and the zero may be "stale", but it sure isn't
+ *    going to say that "we" are the owner anyway, so who cares?
+ *  - owner_depth is non-zero. That means that even if somebody
+ *    else wrote the non-zero count value, the write ordering requriement
+ *    means that they will have written themselves as the owner, so
+ *    if we now see ourselves as an owner we can trust it to be true.
+ */
+static inline int waking_non_zero(struct semaphore *sem, struct task_struct *tsk)
 {
 	unsigned long flags;
+	unsigned long owner_depth = sem->owner_depth;
 	int ret = 0;
 
 	spin_lock_irqsave(&semaphore_wake_lock, flags);
-	if (sem->waking > 0) {
+	if (sem->waking > 0 || (owner_depth && semaphore_owner(sem) == tsk)) {
+		sem->owner = (unsigned long) tsk;
+		sem->owner_depth++;	/* Don't use the possibly stale value */
 		sem->waking--;
 		ret = 1;
 	}
@@ -86,7 +161,9 @@
 		"lock ; "
 #endif
 		"decl 0(%0)\n\t"
-		"js 2f\n"
+		"js 2f\n\t"
+		"movl %%esp,4(%0)\n"
+		"movl $1,8(%0)\n\t"
 		"1:\n"
 		".section .text.lock,\"ax\"\n"
 		"2:\tpushl $1b\n\t"
@@ -108,6 +185,8 @@
 #endif
 		"decl 0(%1)\n\t"
 		"js 2f\n\t"
+		"movl %%esp,4(%1)\n\t"
+		"movl $1,8(%1)\n\t"
 		"xorl %0,%0\n"
 		"1:\n"
 		".section .text.lock,\"ax\"\n"
@@ -131,6 +210,7 @@
 {
 	__asm__ __volatile__(
 		"# atomic up operation\n\t"
+		"decl 8(%0)\n\t"
 #ifdef __SMP__
 		"lock ; "
 #endif
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-i386/softirq.h linux/include/asm-i386/softirq.h
--- v2.2.0-pre7/linux/include/asm-i386/softirq.h	Tue Dec 22 14:16:58 1998
+++ linux/include/asm-i386/softirq.h	Mon Jan 18 17:33:46 1999
@@ -18,8 +18,9 @@
 
 extern inline void remove_bh(int nr)
 {
-	bh_base[nr] = NULL;
 	bh_mask &= ~(1 << nr);
+	mb();
+	bh_base[nr] = NULL;
 }
 
 extern inline void mark_bh(int nr)
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-i386/string.h linux/include/asm-i386/string.h
--- v2.2.0-pre7/linux/include/asm-i386/string.h	Thu Dec 31 10:29:02 1998
+++ linux/include/asm-i386/string.h	Fri Jan 15 00:39:37 1999
@@ -191,7 +191,7 @@
 #define __HAVE_ARCH_STRSTR
 extern inline char * strstr(const char * cs,const char * ct)
 {
-int d0, d1, d2, d3;
+int d0, d1, d2, d3, d4;
 register char * __res;
 __asm__ __volatile__(
 	"cld\n\t" \
@@ -213,7 +213,7 @@
 	"jne 1b\n\t"
 	"xorl %%eax,%%eax\n\t"
 	"2:"
-	:"=a" (__res), "=&c" (d0), "=&S" (d1), "=&d" (d2), "=&D" (d3) : "0" (0),"1" (0xffffffff),"2" (cs),"g" (ct));
+	:"=a" (__res), "=&c" (d0), "=&S" (d1), "=&d" (d2), "=&D" (d3), "=&g" (d4) : "0" (0),"1" (0xffffffff),"2" (cs),"4" (ct));
 return __res;
 }
 
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-m68k/processor.h linux/include/asm-m68k/processor.h
--- v2.2.0-pre7/linux/include/asm-m68k/processor.h	Tue Jun 23 10:01:28 1998
+++ linux/include/asm-m68k/processor.h	Mon Jan 18 09:55:30 1999
@@ -45,7 +45,7 @@
 	unsigned char  fpstate[FPSTATESIZE];  /* floating point state */
 };
 
-#define INIT_MMAP { &init_mm, 0, 0x40000000, __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED), VM_READ | VM_WRITE | VM_EXEC, NULL, &init_mm.mmap }
+#define INIT_MMAP { &init_mm, 0, 0x40000000, NULL, __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED), VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL }
 
 #define INIT_TSS  { \
 	sizeof(init_stack) + (unsigned long) init_stack, 0, \
@@ -74,6 +74,7 @@
 
 #define copy_segments(nr, tsk, mm)	do { } while (0)
 #define release_segments(mm)		do { } while (0)
+#define forget_segments()		do { } while (0)
 
 /*
  * Free current thread data structures etc..
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-m68k/semaphore.h linux/include/asm-m68k/semaphore.h
--- v2.2.0-pre7/linux/include/asm-m68k/semaphore.h	Tue Dec 22 14:16:58 1998
+++ linux/include/asm-m68k/semaphore.h	Thu Jan 14 15:53:25 1999
@@ -38,7 +38,7 @@
 	atomic_inc(&sem->waking);
 }
 
-static inline int waking_non_zero(struct semaphore *sem)
+static inline int waking_non_zero(struct semaphore *sem, struct task_struct *tsk)
 {
 #ifndef CONFIG_RMW_INSNS
 	unsigned long flags;
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-mips/processor.h linux/include/asm-mips/processor.h
--- v2.2.0-pre7/linux/include/asm-mips/processor.h	Fri Oct 23 22:01:23 1998
+++ linux/include/asm-mips/processor.h	Mon Jan 18 09:55:30 1999
@@ -139,8 +139,8 @@
 
 #endif /* !defined (_LANGUAGE_ASSEMBLY) */
 
-#define INIT_MMAP { &init_mm, KSEG0, KSEG1, PAGE_SHARED, \
-                    VM_READ | VM_WRITE | VM_EXEC, NULL, &init_mm.mmap }
+#define INIT_MMAP { &init_mm, KSEG0, KSEG1, NULL, PAGE_SHARED, \
+                    VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL }
 
 #define INIT_TSS  { \
         /* \
@@ -178,6 +178,7 @@
 /* Copy and release all segment info associated with a VM */
 #define copy_segments(nr, p, mm) do { } while(0)
 #define release_segments(mm) do { } while(0)
+#define forget_segments()		do { } while (0)
 
 /*
  * Return saved PC of a blocked thread.
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-mips/semaphore.h linux/include/asm-mips/semaphore.h
--- v2.2.0-pre7/linux/include/asm-mips/semaphore.h	Fri May  8 23:14:55 1998
+++ linux/include/asm-mips/semaphore.h	Thu Jan 14 15:53:25 1999
@@ -43,7 +43,7 @@
 	atomic_inc(&sem->waking);
 }
 
-static inline int waking_non_zero(struct semaphore *sem)
+static inline int waking_non_zero(struct semaphore *sem, struct task_struct *tsk)
 {
 	int ret, tmp;
 
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-ppc/processor.h linux/include/asm-ppc/processor.h
--- v2.2.0-pre7/linux/include/asm-ppc/processor.h	Mon Oct  5 13:13:43 1998
+++ linux/include/asm-ppc/processor.h	Mon Jan 18 09:55:30 1999
@@ -286,8 +286,9 @@
  * Note: the vm_start and vm_end fields here should *not*
  * be in kernel space.  (Could vm_end == vm_start perhaps?)
  */
-#define INIT_MMAP { &init_mm, 0, 0x1000, \
-		      PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC }
+#define INIT_MMAP { &init_mm, 0, 0x1000, NULL, \
+		    PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, \
+		    1, NULL, NULL }
 
 /*
  * Return saved PC of a blocked thread. For now, this is the "user" PC
@@ -299,6 +300,7 @@
 
 #define copy_segments(nr, tsk, mm)	do { } while (0)
 #define release_segments(mm)		do { } while (0)
+#define forget_segments()		do { } while (0)
 
 /*
  * NOTE! The task struct and the stack go together
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-ppc/semaphore.h linux/include/asm-ppc/semaphore.h
--- v2.2.0-pre7/linux/include/asm-ppc/semaphore.h	Sat Aug 16 09:51:09 1997
+++ linux/include/asm-ppc/semaphore.h	Thu Jan 14 15:53:25 1999
@@ -37,7 +37,7 @@
 	atomic_inc(&sem->waking);
 }
 
-static inline int waking_non_zero(struct semaphore *sem)
+static inline int waking_non_zero(struct semaphore *sem, struct task_struct *tsk)
 {
 	int ret, tmp;
 
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-sparc/processor.h linux/include/asm-sparc/processor.h
--- v2.2.0-pre7/linux/include/asm-sparc/processor.h	Thu Aug  6 14:06:34 1998
+++ linux/include/asm-sparc/processor.h	Mon Jan 18 09:55:30 1999
@@ -148,6 +148,7 @@
 
 #define copy_segments(nr, tsk, mm)	do { } while (0)
 #define release_segments(mm)		do { } while (0)
+#define forget_segments()		do { } while (0)
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-sparc/semaphore.h linux/include/asm-sparc/semaphore.h
--- v2.2.0-pre7/linux/include/asm-sparc/semaphore.h	Thu May 29 21:53:09 1997
+++ linux/include/asm-sparc/semaphore.h	Thu Jan 14 15:53:25 1999
@@ -30,7 +30,7 @@
 	restore_flags(flags);	\
 } while(0)
 
-#define waking_non_zero(sem)	\
+#define waking_non_zero(sem,tsk)\
 ({	unsigned long flags;	\
 	int ret = 0;		\
 	save_and_cli(flags);	\
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-sparc64/processor.h linux/include/asm-sparc64/processor.h
--- v2.2.0-pre7/linux/include/asm-sparc64/processor.h	Sun Nov  8 14:03:10 1998
+++ linux/include/asm-sparc64/processor.h	Mon Jan 18 09:55:31 1999
@@ -208,6 +208,7 @@
 
 #define copy_segments(nr, tsk, mm)	do { } while (0)
 #define release_segments(mm)		do { } while (0)
+#define forget_segments()		do { } while (0)
 
 #ifdef __KERNEL__
 /* Allocation and freeing of task_struct and kernel stack. */
diff -u --recursive --new-file v2.2.0-pre7/linux/include/asm-sparc64/semaphore.h linux/include/asm-sparc64/semaphore.h
--- v2.2.0-pre7/linux/include/asm-sparc64/semaphore.h	Sun Nov  8 14:03:10 1998
+++ linux/include/asm-sparc64/semaphore.h	Thu Jan 14 15:53:25 1999
@@ -24,7 +24,7 @@
 
 #define wake_one_more(sem)      atomic_inc(&sem->waking);
 
-static __inline__ int waking_non_zero(struct semaphore *sem)
+static __inline__ int waking_non_zero(struct semaphore *sem, struct task_struct *tsk)
 {
 	int ret;
 
diff -u --recursive --new-file v2.2.0-pre7/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v2.2.0-pre7/linux/include/linux/cdrom.h	Sun Nov  8 14:03:11 1998
+++ linux/include/linux/cdrom.h	Mon Jan 18 12:47:33 1999
@@ -5,8 +5,7 @@
  *               1994, 1995   Eberhard Moenkeberg, emoenke@gwdg.de
  *               1996         David van Leeuwen, david@tm.tno.nl
  *               1997, 1998   Erik Andersen, andersee@debian.org
- *               1998         Jens Axboe, axboe@image.dk and
- *                            Chris Zwilling, chris@cloudnet.com
+ *               1998, 1999   Jens Axboe, axboe@image.dk
  */
  
 #ifndef	_LINUX_CDROM_H
diff -u --recursive --new-file v2.2.0-pre7/linux/include/linux/dcache.h linux/include/linux/dcache.h
--- v2.2.0-pre7/linux/include/linux/dcache.h	Thu Dec 31 10:29:02 1998
+++ linux/include/linux/dcache.h	Fri Jan 15 15:27:04 1999
@@ -150,10 +150,18 @@
 extern int is_root_busy(struct dentry *);
 
 /*
+ * This adds the entry to the hash queues.
+ */
+extern void d_rehash(struct dentry * entry);
+/*
  * This adds the entry to the hash queues and initializes "d_inode".
  * The entry was actually filled in earlier during "d_alloc()"
  */
-extern void d_add(struct dentry * entry, struct inode * inode);
+static __inline__ void d_add(struct dentry * entry, struct inode * inode)
+{
+	d_rehash(entry);
+	d_instantiate(entry, inode);
+}
 
 /* used for rename() and baskets */
 extern void d_move(struct dentry * entry, struct dentry * newdentry);
diff -u --recursive --new-file v2.2.0-pre7/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.2.0-pre7/linux/include/linux/fs.h	Mon Jan  4 15:08:18 1999
+++ linux/include/linux/fs.h	Mon Jan 18 17:35:39 1999
@@ -208,8 +208,6 @@
 	unsigned int b_list;		/* List that this buffer appears */
 	unsigned long b_flushtime;      /* Time when this (dirty) buffer
 					 * should be written */
-	unsigned long b_lru_time;       /* Time when this buffer was 
-					 * last used. */
 	struct wait_queue * b_wait;
 	struct buffer_head ** b_pprev;		/* doubly linked list of hash-queue */
 	struct buffer_head * b_prev_free;	/* doubly linked list of buffers */
diff -u --recursive --new-file v2.2.0-pre7/linux/include/linux/hdreg.h linux/include/linux/hdreg.h
--- v2.2.0-pre7/linux/include/linux/hdreg.h	Thu Dec 31 10:29:02 1998
+++ linux/include/linux/hdreg.h	Mon Jan 18 17:33:46 1999
@@ -141,6 +141,7 @@
 #define HDIO_SET_PIO_MODE	0x0327	/* reconfig interface to new speed */
 #define HDIO_SCAN_HWIF		0x0328	/* register and (re)scan interface */
 #define HDIO_SET_NICE		0x0329	/* set nice flags */
+#define HDIO_UNREGISTER_HWIF	0x032a  /* unregister interface */
 
 /* structure returned by HDIO_GET_IDENTITY, as per ANSI ATA2 rev.2f spec */
 struct hd_driveid {
diff -u --recursive --new-file v2.2.0-pre7/linux/include/linux/i2c.h linux/include/linux/i2c.h
--- v2.2.0-pre7/linux/include/linux/i2c.h	Fri Jan  1 12:58:21 1999
+++ linux/include/linux/i2c.h	Fri Jan 15 14:36:21 1999
@@ -6,7 +6,7 @@
  * There are:
  *
  *     i2c          the basic control module        (like scsi_mod)
- *     bus driver   a driver with a i2c bus         (host adapter driver)
+ *     bus driver   a driver with a i2c bus         (hostadapter driver)
  *     chip driver  a driver for a chip connected
  *                  to a i2c bus                    (cdrom/hd driver)
  *
@@ -87,8 +87,21 @@
 
 /* needed: unsigned long flags */
 
-#define LOCK_I2C_BUS(bus)    spin_lock_irqsave(&(bus->bus_lock),flags);
-#define UNLOCK_I2C_BUS(bus)  spin_unlock_irqrestore(&(bus->bus_lock),flags);
+#if LINUX_VERSION_CODE >= 0x020100
+# if 0
+#  define LOCK_FLAGS unsigned long flags;
+#  define LOCK_I2C_BUS(bus)    spin_lock_irqsave(&(bus->bus_lock),flags);
+#  define UNLOCK_I2C_BUS(bus)  spin_unlock_irqrestore(&(bus->bus_lock),flags);
+# else
+#  define LOCK_FLAGS
+#  define LOCK_I2C_BUS(bus)    spin_lock(&(bus->bus_lock));
+#  define UNLOCK_I2C_BUS(bus)  spin_unlock(&(bus->bus_lock));
+# endif
+#else
+# define LOCK_FLAGS unsigned long flags;
+# define LOCK_I2C_BUS(bus)    { save_flags(flags); cli(); }
+# define UNLOCK_I2C_BUS(bus)  { restore_flags(flags);     }
+#endif
 
 struct i2c_bus 
 {
@@ -96,7 +109,9 @@
 	int   id;
 	void  *data;            /* free for use by the bus driver */
 
+#if LINUX_VERSION_CODE >= 0x020100
 	spinlock_t bus_lock;
+#endif
 
 	/* attach/detach inform callbacks */
 	void    (*attach_inform)(struct i2c_bus *bus, int id);
@@ -163,5 +178,5 @@
 int     i2c_write(struct i2c_bus *bus, unsigned char addr,
 		  unsigned char b1, unsigned char b2, int both);
 
-int 	i2c_init(void);
+int	i2c_init(void);
 #endif /* I2C_H */
diff -u --recursive --new-file v2.2.0-pre7/linux/include/linux/if_pppvar.h linux/include/linux/if_pppvar.h
--- v2.2.0-pre7/linux/include/linux/if_pppvar.h	Fri Oct  9 13:27:16 1998
+++ linux/include/linux/if_pppvar.h	Thu Jan 14 22:56:06 1999
@@ -42,7 +42,7 @@
  */
 
 /*
- *  ==FILEVERSION 981004==
+ *  ==FILEVERSION 990114==
  *
  *  NOTE TO MAINTAINERS:
  *   If you modify this file at all, please set the above date.
@@ -61,7 +61,8 @@
 #define NP_IP	0		/* Internet Protocol */
 #define NP_IPX	1		/* IPX protocol */
 #define NP_AT	2		/* Appletalk protocol */
-#define NUM_NP	3		/* Number of NPs. */
+#define NP_IPV6	3		/* Internet Protocol */
+#define NUM_NP	4		/* Number of NPs. */
 
 #define OBUFSIZE	256	/* # chars of output buffering */
 
diff -u --recursive --new-file v2.2.0-pre7/linux/include/linux/kmod.h linux/include/linux/kmod.h
--- v2.2.0-pre7/linux/include/linux/kmod.h	Tue Mar 17 22:18:15 1998
+++ linux/include/linux/kmod.h	Mon Jan 18 17:33:48 1999
@@ -1,4 +1,12 @@
 /*
 	kmod header
 */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_KMOD
 extern int request_module(const char * name);
+#else
+#define request_module(x) do {} while(0)
+#endif
+
diff -u --recursive --new-file v2.2.0-pre7/linux/include/linux/mm.h linux/include/linux/mm.h
--- v2.2.0-pre7/linux/include/linux/mm.h	Wed Jan 13 15:00:44 1999
+++ linux/include/linux/mm.h	Mon Jan 18 17:35:41 1999
@@ -35,10 +35,17 @@
 	struct mm_struct * vm_mm;	/* VM area parameters */
 	unsigned long vm_start;
 	unsigned long vm_end;
+
+	/* linked list of VM areas per task, sorted by address */
+	struct vm_area_struct *vm_next;
+
 	pgprot_t vm_page_prot;
 	unsigned short vm_flags;
-	struct vm_area_struct *vm_next;
-	struct vm_area_struct **vm_pprev;
+
+	/* AVL tree of VM areas per task, sorted by address */
+	short vm_avl_height;
+	struct vm_area_struct * vm_avl_left;
+	struct vm_area_struct * vm_avl_right;
 
 	/* For areas with inode, the list inode->i_mmap, for shm areas,
 	 * the list of attaches, otherwise unused.
@@ -98,7 +105,7 @@
 	unsigned long (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access);
 	unsigned long (*wppage)(struct vm_area_struct * area, unsigned long address,
 		unsigned long page);
-	int (*swapout)(struct vm_area_struct *,  unsigned long, pte_t *);
+	int (*swapout)(struct vm_area_struct *, struct page *);
 	pte_t (*swapin)(struct vm_area_struct *, unsigned long, unsigned long);
 };
 
@@ -268,7 +275,7 @@
 	unsigned long address);
 
 extern void free_page_tables(struct mm_struct * mm);
-extern void clear_page_tables(struct task_struct * tsk);
+extern void clear_page_tables(struct mm_struct *, unsigned long, int);
 extern int new_page_tables(struct task_struct * tsk);
 
 extern void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size);
@@ -293,6 +300,7 @@
 extern void vma_init(void);
 extern void merge_segments(struct mm_struct *, unsigned long, unsigned long);
 extern void insert_vm_struct(struct mm_struct *, struct vm_area_struct *);
+extern void build_mmap_avl(struct mm_struct *);
 extern void exit_mmap(struct mm_struct *);
 extern unsigned long get_unmapped_area(unsigned long, unsigned long);
 
@@ -316,6 +324,7 @@
 #define __GFP_MED	0x04
 #define __GFP_HIGH	0x08
 #define __GFP_IO	0x10
+#define __GFP_SWAP	0x20
 
 #define __GFP_DMA	0x80
 
@@ -324,7 +333,7 @@
 #define GFP_USER	(__GFP_LOW | __GFP_WAIT | __GFP_IO)
 #define GFP_KERNEL	(__GFP_MED | __GFP_WAIT | __GFP_IO)
 #define GFP_NFS		(__GFP_HIGH | __GFP_WAIT | __GFP_IO)
-#define GFP_KSWAPD	(__GFP_IO)
+#define GFP_KSWAPD	(__GFP_IO | __GFP_SWAP)
 
 /* Flag - indicates that the buffer will be suitable for DMA.  Ignored on some
    platforms, used as appropriate on others */
@@ -358,22 +367,7 @@
 }
 
 /* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
-static inline struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr)
-{
-	struct vm_area_struct *vma = NULL;
-
-	if (mm) {
-		/* Check the cache first. */
-		vma = mm->mmap_cache;
-		if(!vma || (vma->vm_end <= addr) || (vma->vm_start > addr)) {
-			vma = mm->mmap;
-			while(vma && vma->vm_end <= addr)
-				vma = vma->vm_next;
-			mm->mmap_cache = vma;
-		}
-	}
-	return vma;
-}
+extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr);
 
 /* Look up the first VMA which intersects the interval start_addr..end_addr-1,
    NULL if none.  Assume start_addr < end_addr. */
diff -u --recursive --new-file v2.2.0-pre7/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.2.0-pre7/linux/include/linux/pci.h	Thu Jan  7 15:11:40 1999
+++ linux/include/linux/pci.h	Mon Jan 18 17:33:46 1999
@@ -592,6 +592,8 @@
 #define PCI_VENDOR_ID_BROOKTREE		0x109e
 #define PCI_DEVICE_ID_BROOKTREE_848	0x0350
 #define PCI_DEVICE_ID_BROOKTREE_849A	0x0351
+#define PCI_DEVICE_ID_BROOKTREE_878_1   0x036e
+#define PCI_DEVICE_ID_BROOKTREE_878     0x0878
 #define PCI_DEVICE_ID_BROOKTREE_8474	0x8474
 
 #define PCI_VENDOR_ID_SIERRA		0x10a8
@@ -904,6 +906,7 @@
 #define PCI_VENDOR_ID_3DFX		0x121a
 #define PCI_DEVICE_ID_3DFX_VOODOO	0x0001
 #define PCI_DEVICE_ID_3DFX_VOODOO2	0x0002
+#define PCI_DEVICE_ID_3DFX_BANSHEE      0x0003
 
 #define PCI_VENDOR_ID_SIGMADES		0x1236
 #define PCI_DEVICE_ID_SIGMADES_6425	0x6401
diff -u --recursive --new-file v2.2.0-pre7/linux/include/linux/poll.h linux/include/linux/poll.h
--- v2.2.0-pre7/linux/include/linux/poll.h	Sun Nov  8 14:03:11 1998
+++ linux/include/linux/poll.h	Mon Jan 18 17:35:44 1999
@@ -7,6 +7,7 @@
 
 #include <linux/wait.h>
 #include <linux/string.h>
+#include <linux/mm.h>
 #include <asm/uaccess.h>
 
 
@@ -17,11 +18,12 @@
 };
 
 typedef struct poll_table_struct {
+	struct poll_table_struct * next;
 	unsigned int nr;
 	struct poll_table_entry * entry;
 } poll_table;
 
-#define __MAX_POLL_TABLE_ENTRIES (PAGE_SIZE / sizeof (struct poll_table_entry))
+#define __MAX_POLL_TABLE_ENTRIES ((PAGE_SIZE - sizeof (poll_table)) / sizeof (struct poll_table_entry))
 
 extern inline void poll_wait(struct file * filp, struct wait_queue ** wait_address, poll_table *p)
 {
@@ -29,8 +31,18 @@
 
 	if (!p || !wait_address)
 		return;
-	if (p->nr >= __MAX_POLL_TABLE_ENTRIES)
-		return;
+	while (p->nr >= __MAX_POLL_TABLE_ENTRIES && p->next != NULL)
+		p = p->next;
+	if (p->nr >= __MAX_POLL_TABLE_ENTRIES) {
+		poll_table *tmp = (poll_table *) __get_free_page(GFP_KERNEL);
+		if (!tmp)
+			return;
+		tmp->nr = 0;
+		tmp->entry = (struct poll_table_entry *)(tmp + 1);
+		tmp->next = NULL;
+		p->next = tmp;
+		p = tmp;
+	}
  	entry = p->entry + p->nr;
  	entry->filp = filp;
  	filp->f_count++;
@@ -59,11 +71,30 @@
 #define KFDS_NR (KFDS_64BLOCK*8 > NR_OPEN ? NR_OPEN : KFDS_64BLOCK*8)
 typedef unsigned long kernel_fd_set[KFDS_NR/__NFDBITS];
 
+/*
+ * XXX - still used by alpha osf and sparc32 compatiblity.
+ */
+
 typedef struct {
 	kernel_fd_set in, out, ex;
 	kernel_fd_set res_in, res_out, res_ex;
 } fd_set_buffer;
 
+/*
+ * Scaleable version of the fd_set.
+ */
+
+typedef struct {
+	unsigned long *in, *out, *ex;
+	unsigned long *res_in, *res_out, *res_ex;
+} fd_set_bits;
+
+/*
+ * How many longwords for "nr" bits?
+ */
+#define FDS_BITPERLONG	(8*sizeof(long))
+#define FDS_LONGS(nr)	(((nr)+FDS_BITPERLONG-1)/FDS_BITPERLONG)
+#define FDS_BYTES(nr)	(FDS_LONGS(nr)*sizeof(long))
 
 /*
  * We do a VERIFY_WRITE here even though we are only reading this time:
@@ -74,8 +105,7 @@
 static inline
 int get_fd_set(unsigned long nr, void *ufdset, unsigned long *fdset)
 {
-	/* round up nr to nearest "unsigned long" */
-	nr = (nr + 8*sizeof(long) - 1) / (8*sizeof(long)) * sizeof(long);
+	nr = FDS_BYTES(nr);
 	if (ufdset) {
 		int error;
 		error = verify_area(VERIFY_WRITE, ufdset, nr);
@@ -90,20 +120,17 @@
 static inline
 void set_fd_set(unsigned long nr, void *ufdset, unsigned long *fdset)
 {
-	if (ufdset) {
-		nr = (nr + 8*sizeof(long) - 1) / (8*sizeof(long))*sizeof(long);
-		__copy_to_user(ufdset, fdset, nr);
-	}
+	if (ufdset)
+		__copy_to_user(ufdset, fdset, FDS_BYTES(nr));
 }
 
 static inline
 void zero_fd_set(unsigned long nr, unsigned long *fdset)
 {
-	nr = (nr + 8*sizeof(long) - 1) / (8*sizeof(long)) * sizeof(long);
-	memset(fdset, 0, nr);
+	memset(fdset, 0, FDS_BYTES(nr));
 }
 
-extern int do_select(int n, fd_set_buffer *fds, long *timeout);
+extern int do_select(int n, fd_set_bits *fds, long *timeout);
 
 #endif /* KERNEL */
 
diff -u --recursive --new-file v2.2.0-pre7/linux/include/linux/ppp_defs.h linux/include/linux/ppp_defs.h
--- v2.2.0-pre7/linux/include/linux/ppp_defs.h	Sat Nov 29 10:33:21 1997
+++ linux/include/linux/ppp_defs.h	Thu Jan 14 22:56:06 1999
@@ -28,7 +28,7 @@
  */
 
 /*
- *  ==FILEVERSION 970607==
+ *  ==FILEVERSION 990114==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the above date.
@@ -75,6 +75,7 @@
 #define PPP_IPCP	0x8021	/* IP Control Protocol */
 #define PPP_ATCP	0x8029	/* AppleTalk Control Protocol */
 #define PPP_IPXCP	0x802b	/* IPX Control Protocol */
+#define PPP_IPV6CP	0x8057	/* IPv6 Control Protocol */
 #define PPP_CCP		0x80fd	/* Compression Control Protocol */
 #define PPP_LCP		0xc021	/* Link Control Protocol */
 #define PPP_PAP		0xc023	/* Password Authentication Protocol */
diff -u --recursive --new-file v2.2.0-pre7/linux/include/linux/quota.h linux/include/linux/quota.h
--- v2.2.0-pre7/linux/include/linux/quota.h	Mon Sep 28 10:51:35 1998
+++ linux/include/linux/quota.h	Thu Jan 14 22:56:06 1999
@@ -86,7 +86,7 @@
 extern int dquot_root_squash;
 
 #define NR_DQHASH 43            /* Just an arbitrary number */
-#define NR_DQUOTS 256           /* Maximum number of quotas active at one time (Configurable from /proc/sys/fs) */
+#define NR_DQUOTS 1024          /* Maximum number of quotas active at one time (Configurable from /proc/sys/fs) */
 
 /*
  * Command definitions for the 'quotactl' system call.
diff -u --recursive --new-file v2.2.0-pre7/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.2.0-pre7/linux/include/linux/sched.h	Wed Jan 13 15:00:44 1999
+++ linux/include/linux/sched.h	Mon Jan 18 17:35:41 1999
@@ -33,7 +33,7 @@
 #define CLONE_SIGHAND	0x00000800	/* set if signal handlers shared */
 #define CLONE_PID	0x00001000	/* set if pid shared */
 #define CLONE_PTRACE	0x00002000	/* set if we want to let tracing continue on the child too */
-#define CLONE_VFORK	0x00004000	/* set if the parent wants the child to wake it up on mmput */
+#define CLONE_VFORK	0x00004000	/* set if the parent wants the child to wake it up on mm_release */
 
 /*
  * These are the constant used to fake the fixed-point load-average
@@ -156,11 +156,16 @@
 /* Maximum number of active map areas.. This is a random (large) number */
 #define MAX_MAP_COUNT	(65536)
 
+/* Number of map areas at which the AVL tree is activated. This is arbitrary. */
+#define AVL_MIN_MAP_COUNT	32
+
 struct mm_struct {
-	struct vm_area_struct *mmap, *mmap_cache;
+	struct vm_area_struct *mmap;		/* list of VMAs */
+	struct vm_area_struct *mmap_avl;	/* tree of VMAs */
+	struct vm_area_struct *mmap_cache;	/* last find_vma result */
 	pgd_t * pgd;
 	atomic_t count;
-	int map_count;
+	int map_count;				/* number of VMAs */
 	struct semaphore mmap_sem;
 	unsigned long context;
 	unsigned long start_code, end_code, start_data, end_data;
@@ -177,7 +182,8 @@
 };
 
 #define INIT_MM {					\
-		&init_mmap, NULL, swapper_pg_dir, 	\
+		&init_mmap, NULL, NULL,			\
+		swapper_pg_dir, 			\
 		ATOMIC_INIT(1), 1,			\
 		MUTEX,					\
 		0,					\
@@ -321,7 +327,7 @@
 #define PF_DUMPCORE	0x00000200	/* dumped core */
 #define PF_SIGNALED	0x00000400	/* killed by a signal */
 #define PF_MEMALLOC	0x00000800	/* Allocating memory */
-#define PF_VFORK	0x00001000	/* Wake up parent in mmput */
+#define PF_VFORK	0x00001000	/* Wake up parent in mm_release */
 
 #define PF_USEDFPU	0x00100000	/* task used FPU this quantum (SMP) */
 #define PF_DTRACE	0x00200000	/* delayed trace (used on m68k, i386) */
@@ -332,7 +338,7 @@
  */
 #define _STK_LIM	(8*1024*1024)
 
-#define DEF_PRIORITY	(20*HZ/100)	/* 200 ms time slices */
+#define DEF_PRIORITY	(20*HZ/100)	/* 210 ms time slices */
 
 /*
  *  INIT_TASK is used to set up the first task table, touch at
@@ -608,6 +614,8 @@
 	atomic_inc(&mm->count);
 }
 extern void mmput(struct mm_struct *);
+/* Remove the current tasks stale references to the old mm_struct */
+extern void mm_release(void);
 
 extern int  copy_thread(int, unsigned long, unsigned long, struct task_struct *, struct pt_regs *);
 extern void flush_thread(void);
diff -u --recursive --new-file v2.2.0-pre7/linux/include/linux/videodev.h linux/include/linux/videodev.h
--- v2.2.0-pre7/linux/include/linux/videodev.h	Mon Dec 28 15:00:53 1998
+++ linux/include/linux/videodev.h	Thu Jan 14 22:53:12 1999
@@ -1,9 +1,13 @@
 #ifndef __LINUX_VIDEODEV_H
 #define __LINUX_VIDEODEV_H
 
+#include <linux/types.h>
+
 #ifdef __KERNEL__
 
+#if LINUX_VERSION_CODE >= 0x020100
 #include <linux/poll.h>
+#endif
 
 struct video_device
 {
@@ -16,7 +20,9 @@
 	long (*read)(struct video_device *, char *, unsigned long, int noblock);
 	/* Do we need a write method ? */
 	long (*write)(struct video_device *, const char *, unsigned long, int noblock);
+#if LINUX_VERSION_CODE >= 0x020100
 	unsigned int (*poll)(struct video_device *, struct file *, poll_table *);
+#endif
 	int (*ioctl)(struct video_device *, unsigned int , void *);
 	int (*mmap)(struct video_device *, const char *, unsigned long);
 	int (*initialize)(struct video_device *);	
@@ -48,7 +54,6 @@
 #define VID_TYPE_SCALES		128	/* Scalable */
 #define VID_TYPE_MONOCHROME	256	/* Monochrome only */
 #define VID_TYPE_SUBCAPTURE	512	/* Can capture subareas of the image */
-#define VID_TYPE_OUTPUT		1024	/* Can output video data */
 
 struct video_capability
 {
@@ -120,6 +125,10 @@
 #define VIDEO_PALETTE_RAW	12	/* RAW capture (BT848) */
 #define VIDEO_PALETTE_YUV422P	13	/* YUV 4:2:2 Planar */
 #define VIDEO_PALETTE_YUV411P	14	/* YUV 4:1:1 Planar */
+#define VIDEO_PALETTE_YUV420P	15	/* YUV 4:2:0 Planar */
+#define VIDEO_PALETTE_YUV410P	16	/* YUV 4:1:0 Planar */
+#define VIDEO_PALETTE_PLANAR	13	/* start of planar entries */
+#define VIDEO_PALETTE_COMPONENT 7	/* start of component entries */
 };
 
 struct video_audio
@@ -138,7 +147,7 @@
 #define VIDEO_SOUND_STEREO	2
 #define VIDEO_SOUND_LANG1	4
 #define VIDEO_SOUND_LANG2	8
-        __u16   mode;		/* detected audio carriers or one to set */
+        __u16   mode;
         __u16	balance;	/* Stereo balance */
         __u16	step;		/* Step actual volume uses */
 };
diff -u --recursive --new-file v2.2.0-pre7/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.2.0-pre7/linux/include/net/tcp.h	Sun Nov  8 14:03:12 1998
+++ linux/include/net/tcp.h	Mon Jan 18 17:35:45 1999
@@ -813,8 +813,9 @@
 /* This tells the input processing path that an ACK should go out
  * right now.
  */
-#define tcp_enter_quickack_mode(__tp)	((__tp)->ato = (HZ/100))
-#define tcp_in_quickack_mode(__tp)	((__tp)->ato == (HZ/100))
+#define tcp_enter_quickack_mode(__tp)	((__tp)->ato |= (1<<31))
+#define tcp_exit_quickack_mode(__tp)	((__tp)->ato &= ~(1<<31))
+#define tcp_in_quickack_mode(__tp)	(((__tp)->ato & (1 << 31)) != 0)
 
 /*
  * List all states of a TCP socket that can be viewed as a "connected"
diff -u --recursive --new-file v2.2.0-pre7/linux/kernel/exit.c linux/kernel/exit.c
--- v2.2.0-pre7/linux/kernel/exit.c	Fri Jan  8 22:36:25 1999
+++ linux/kernel/exit.c	Mon Jan 18 09:55:31 1999
@@ -258,6 +258,7 @@
 		tsk->mm = &init_mm;
 		tsk->swappable = 0;
 		SET_PAGE_DIR(tsk, swapper_pg_dir);
+		mm_release();
 		mmput(mm);
 	}
 }
diff -u --recursive --new-file v2.2.0-pre7/linux/kernel/fork.c linux/kernel/fork.c
--- v2.2.0-pre7/linux/kernel/fork.c	Wed Jan 13 15:00:44 1999
+++ linux/kernel/fork.c	Mon Jan 18 09:55:50 1999
@@ -16,6 +16,7 @@
 #include <linux/unistd.h>
 #include <linux/smp_lock.h>
 #include <linux/module.h>
+#include <linux/vmalloc.h>
 
 #include <asm/pgtable.h>
 #include <asm/mmu_context.h>
@@ -230,16 +231,16 @@
 		 * Link in the new vma even if an error occurred,
 		 * so that exit_mmap() can clean up the mess.
 		 */
-		if((tmp->vm_next = *pprev) != NULL)
-			(*pprev)->vm_pprev = &tmp->vm_next;
+		tmp->vm_next = *pprev;
 		*pprev = tmp;
-		tmp->vm_pprev = pprev;
 
 		pprev = &tmp->vm_next;
 		if (retval)
 			goto fail_nomem;
 	}
 	retval = 0;
+	if (mm->map_count >= AVL_MIN_MAP_COUNT)
+		build_mmap_avl(mm);
 
 fail_nomem:
 	flush_tlb_mm(current->mm);
@@ -268,7 +269,7 @@
 		 * Leave mm->pgd set to the parent's pgd
 		 * so that pgd_offset() is always valid.
 		 */
-		mm->mmap = mm->mmap_cache = NULL;
+		mm->mmap = mm->mmap_avl = mm->mmap_cache = NULL;
 
 		/* It has not run yet, so cannot be present in anyone's
 		 * cache or tlb.
@@ -278,17 +279,35 @@
 	return mm;
 }
 
-/*
- * Decrement the use count and release all resources for an mm.
+/* Please note the differences between mmput and mm_release.
+ * mmput is called whenever we stop holding onto a mm_struct,
+ * error success whatever.
+ *
+ * mm_release is called after a mm_struct has been removed
+ * from the current process.
+ *
+ * This difference is important for error handling, when we
+ * only half set up a mm_struct for a new process and need to restore
+ * the old one.  Because we mmput the new mm_struct before
+ * restoring the old one. . .
+ * Eric Biederman 10 January 1998
  */
-void mmput(struct mm_struct *mm)
+void mm_release(void)
 {
+	struct task_struct *tsk = current;
+	forget_segments();
 	/* notify parent sleeping on vfork() */
-	if (current->flags & PF_VFORK) {
-		current->flags &= ~PF_VFORK;
-		up(current->p_opptr->vfork_sem);
+	if (tsk->flags & PF_VFORK) {
+		tsk->flags &= ~PF_VFORK;
+		up(tsk->p_opptr->vfork_sem);
 	}
+}
 
+/*
+ * Decrement the use count and release all resources for an mm.
+ */
+void mmput(struct mm_struct *mm)
+{
 	if (atomic_dec_and_test(&mm->count)) {
 		release_segments(mm);
 		exit_mmap(mm);
@@ -478,6 +497,9 @@
 	int nr;
 	int retval = -ENOMEM;
 	struct task_struct *p;
+	struct semaphore sem = MUTEX_LOCKED;
+
+	current->vfork_sem = &sem;
 
 	p = alloc_task_struct();
 	if (!p)
@@ -611,9 +633,11 @@
 	}
 	++total_forks;
 bad_fork:
-	up(&current->mm->mmap_sem);
 	unlock_kernel();
+	up(&current->mm->mmap_sem);
 fork_out:
+	if ((clone_flags & CLONE_VFORK) && (retval > 0)) 
+		down(&sem);
 	return retval;
 
 bad_fork_cleanup_sighand:
diff -u --recursive --new-file v2.2.0-pre7/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.2.0-pre7/linux/kernel/ksyms.c	Thu Jan  7 15:11:41 1999
+++ linux/kernel/ksyms.c	Fri Jan 15 11:28:03 1999
@@ -121,7 +121,8 @@
 EXPORT_SYMBOL(d_alloc_root);
 EXPORT_SYMBOL(d_delete);
 EXPORT_SYMBOL(d_validate);
-EXPORT_SYMBOL(d_add);
+EXPORT_SYMBOL(d_rehash);
+EXPORT_SYMBOL(d_invalidate);	/* May be it will be better in dcache.h? */
 EXPORT_SYMBOL(d_move);
 EXPORT_SYMBOL(d_instantiate);
 EXPORT_SYMBOL(d_alloc);
diff -u --recursive --new-file v2.2.0-pre7/linux/kernel/sched.c linux/kernel/sched.c
--- v2.2.0-pre7/linux/kernel/sched.c	Thu Jan  7 15:11:41 1999
+++ linux/kernel/sched.c	Fri Jan 15 11:33:27 1999
@@ -883,7 +883,7 @@
 	 * who gets to gate through and who has to wait some more.	 \
 	 */								 \
 	for (;;) {							 \
-		if (waking_non_zero(sem))	/* are we waking up?  */ \
+		if (waking_non_zero(sem, tsk))	/* are we waking up?  */ \
 			break;			/* yes, exit loop */
 
 #define DOWN_TAIL(task_state)			\
@@ -1551,7 +1551,7 @@
 	 * do a "normalization" of the priority (traditionally
 	 * Unix nice values are -20 to 20; Linux doesn't really
 	 * use that kind of thing, but uses the length of the
-	 * timeslice instead (default 150 ms). The rounding is
+	 * timeslice instead (default 210 ms). The rounding is
 	 * why we want to avoid negative values.
 	 */
 	newprio = (newprio * DEF_PRIORITY + 10) / 20;
diff -u --recursive --new-file v2.2.0-pre7/linux/mm/filemap.c linux/mm/filemap.c
--- v2.2.0-pre7/linux/mm/filemap.c	Thu Jan  7 15:11:41 1999
+++ linux/mm/filemap.c	Thu Jan 14 11:39:54 1999
@@ -1085,22 +1085,6 @@
 	struct file * file;
 	struct dentry * dentry;
 	struct inode * inode;
-	struct buffer_head * bh;
-
-	bh = mem_map[MAP_NR(page)].buffers;
-	if (bh) {
-		/* whee.. just mark the buffer heads dirty */
-		struct buffer_head * tmp = bh;
-		do {
-			/*
-			 * WSH: There's a race here: mark_buffer_dirty()
-			 * could block, and the buffers aren't pinned down.
-			 */
-			mark_buffer_dirty(tmp, 0);
-			tmp = tmp->b_this_page;
-		} while (tmp != bh);
-		return 0;
-	}
 
 	file = vma->vm_file;
 	dentry = file->f_dentry;
@@ -1122,50 +1106,15 @@
 
 
 /*
- * Swapping to a shared file: while we're busy writing out the page
- * (and the page still exists in memory), we save the page information
- * in the page table, so that "filemap_swapin()" can re-use the page
- * immediately if it is called while we're busy swapping it out..
- *
- * Once we've written it all out, we mark the page entry "empty", which
- * will result in a normal page-in (instead of a swap-in) from the now
- * up-to-date disk file.
+ * The page cache takes care of races between somebody
+ * trying to swap something out and swap something in
+ * at the same time..
  */
-int filemap_swapout(struct vm_area_struct * vma,
-	unsigned long offset,
-	pte_t *page_table)
+int filemap_swapout(struct vm_area_struct * vma, struct page * page)
 {
-	int error;
-	unsigned long page = pte_page(*page_table);
-	unsigned long entry = SWP_ENTRY(SHM_SWP_TYPE, MAP_NR(page));
-
-	flush_cache_page(vma, (offset + vma->vm_start - vma->vm_offset));
-	set_pte(page_table, __pte(entry));
-	flush_tlb_page(vma, (offset + vma->vm_start - vma->vm_offset));
-	error = filemap_write_page(vma, offset, page);
-	if (pte_val(*page_table) == entry)
-		pte_clear(page_table);
-	return error;
+	return filemap_write_page(vma, page->offset, page_address(page));
 }
 
-/*
- * filemap_swapin() is called only if we have something in the page
- * tables that is non-zero (but not present), which we know to be the
- * page index of a page that is busy being swapped out (see above).
- * So we just use it directly..
- */
-static pte_t filemap_swapin(struct vm_area_struct * vma,
-	unsigned long offset,
-	unsigned long entry)
-{
-	unsigned long page = SWP_OFFSET(entry);
-
-	atomic_inc(&mem_map[page].count);
-	page = (page << PAGE_SHIFT) + PAGE_OFFSET;
-	return mk_pte(page,vma->vm_page_prot);
-}
-
-
 static inline int filemap_sync_pte(pte_t * ptep, struct vm_area_struct *vma,
 	unsigned long address, unsigned int flags)
 {
@@ -1306,7 +1255,7 @@
 	filemap_nopage,		/* nopage */
 	NULL,			/* wppage */
 	filemap_swapout,	/* swapout */
-	filemap_swapin,		/* swapin */
+	NULL,			/* swapin */
 };
 
 /*
diff -u --recursive --new-file v2.2.0-pre7/linux/mm/memory.c linux/mm/memory.c
--- v2.2.0-pre7/linux/mm/memory.c	Thu Jan  7 15:11:41 1999
+++ linux/mm/memory.c	Mon Jan 18 17:33:10 1999
@@ -126,48 +126,36 @@
  * This function clears all user-level page tables of a process - this
  * is needed by execve(), so that old pages aren't in the way.
  */
-void clear_page_tables(struct task_struct * tsk)
+void clear_page_tables(struct mm_struct *mm, unsigned long first, int nr)
 {
-	pgd_t * page_dir = tsk->mm->pgd;
-	int i;
-
-	if (!page_dir || page_dir == swapper_pg_dir)
-		goto out_bad;
-	for (i = 0 ; i < USER_PTRS_PER_PGD ; i++)
-		free_one_pgd(page_dir + i);
-
-	/* keep the page table cache within bounds */
-	check_pgt_cache();
-	return;
+	pgd_t * page_dir = mm->pgd;
 
-out_bad:
-	printk(KERN_ERR 
-		"clear_page_tables: %s trying to clear kernel pgd\n",
-		tsk->comm);
-	return;
+	if (page_dir && page_dir != swapper_pg_dir) {
+		page_dir += first;
+		do {
+			free_one_pgd(page_dir);
+			page_dir++;
+		} while (--nr);
+
+		/* keep the page table cache within bounds */
+		check_pgt_cache();
+	}
 }
 
 /*
- * This function frees up all page tables of a process when it exits. It
- * is the same as "clear_page_tables()", except it also frees the old
- * page table directory.
+ * This function just free's the page directory - the
+ * pages tables themselves have been freed earlier by 
+ * clear_page_tables().
  */
 void free_page_tables(struct mm_struct * mm)
 {
 	pgd_t * page_dir = mm->pgd;
-	int i;
 
-	if (!page_dir)
-		goto out;
-	if (page_dir == swapper_pg_dir)
-		goto out_bad;
-	for (i = 0 ; i < USER_PTRS_PER_PGD ; i++)
-		free_one_pgd(page_dir + i);
-	pgd_free(page_dir);
-
-	/* keep the page table cache within bounds */
-	check_pgt_cache();
-out:
+	if (page_dir) {
+		if (page_dir == swapper_pg_dir)
+			goto out_bad;
+		pgd_free(page_dir);
+	}
 	return;
 
 out_bad:
@@ -204,7 +192,7 @@
 	pgd_t * src_pgd, * dst_pgd;
 	unsigned long address = vma->vm_start;
 	unsigned long end = vma->vm_end;
-	unsigned long cow = (vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE;
+	unsigned long cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
 	
 	src_pgd = pgd_offset(src, address)-1;
 	dst_pgd = pgd_offset(dst, address)-1;
@@ -277,10 +265,15 @@
 					set_pte(dst_pte, pte);
 					goto cont_copy_pte_range;
 				}
-				if (cow)
+				/* If it's a COW mapping, write protect it both in the parent and the child */
+				if (cow) {
 					pte = pte_wrprotect(pte);
+					set_pte(src_pte, pte);
+				}
+				/* If it's a shared mapping, mark it clean in the child */
+				if (vma->vm_flags & VM_SHARED)
+					pte = pte_mkclean(pte);
 				set_pte(dst_pte, pte_mkold(pte));
-				set_pte(src_pte, pte);
 				atomic_inc(&mem_map[page_nr].count);
 			
 cont_copy_pte_range:		address += PAGE_SIZE;
diff -u --recursive --new-file v2.2.0-pre7/linux/mm/mmap.c linux/mm/mmap.c
--- v2.2.0-pre7/linux/mm/mmap.c	Fri Nov 27 13:09:30 1998
+++ linux/mm/mmap.c	Mon Jan 18 16:24:06 1999
@@ -371,6 +371,99 @@
 	}
 }
 
+#define vm_avl_empty	(struct vm_area_struct *) NULL
+
+#include "mmap_avl.c"
+
+/* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
+struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr)
+{
+	struct vm_area_struct *vma = NULL;
+
+	if (mm) {
+		/* Check the cache first. */
+		/* (Cache hit rate is typically around 35%.) */
+		vma = mm->mmap_cache;
+		if (!(vma && vma->vm_end > addr && vma->vm_start <= addr)) {
+			if (!mm->mmap_avl) {
+				/* Go through the linear list. */
+				vma = mm->mmap;
+				while (vma && vma->vm_end <= addr)
+					vma = vma->vm_next;
+			} else {
+				/* Then go through the AVL tree quickly. */
+				struct vm_area_struct * tree = mm->mmap_avl;
+				for (;;) {
+					if (tree == vm_avl_empty)
+						break;
+					if (tree->vm_end > addr) {
+						vma = tree;
+						if (tree->vm_start <= addr)
+							break;
+						tree = tree->vm_avl_left;
+					} else
+						tree = tree->vm_avl_right;
+				}
+			}
+			if (vma)
+				mm->mmap_cache = vma;
+		}
+	}
+	return vma;
+}
+
+/* Same as find_vma, but also return a pointer to the previous VMA in *pprev. */
+struct vm_area_struct * find_vma_prev(struct mm_struct * mm, unsigned long addr,
+				      struct vm_area_struct **pprev)
+{
+	if (mm) {
+		if (!mm->mmap_avl) {
+			/* Go through the linear list. */
+			struct vm_area_struct * prev = NULL;
+			struct vm_area_struct * vma = mm->mmap;
+			while (vma && vma->vm_end <= addr) {
+				prev = vma;
+				vma = vma->vm_next;
+			}
+			*pprev = prev;
+			return vma;
+		} else {
+			/* Go through the AVL tree quickly. */
+			struct vm_area_struct * vma = NULL;
+			struct vm_area_struct * last_turn_right = NULL;
+			struct vm_area_struct * prev = NULL;
+			struct vm_area_struct * tree = mm->mmap_avl;
+			for (;;) {
+				if (tree == vm_avl_empty)
+					break;
+				if (tree->vm_end > addr) {
+					vma = tree;
+					prev = last_turn_right;
+					if (tree->vm_start <= addr)
+						break;
+					tree = tree->vm_avl_left;
+				} else {
+					last_turn_right = tree;
+					tree = tree->vm_avl_right;
+				}
+			}
+			if (vma) {
+				if (vma->vm_avl_left != vm_avl_empty) {
+					prev = vma->vm_avl_left;
+					while (prev->vm_avl_right != vm_avl_empty)
+						prev = prev->vm_avl_right;
+				}
+				if ((prev ? prev->vm_next : mm->mmap) != vma)
+					printk("find_vma_prev: tree inconsistent with list\n");
+				*pprev = prev;
+				return vma;
+			}
+		}
+	}
+	*pprev = NULL;
+	return NULL;
+}
+
 /* Normal function to fix up a mapping
  * This function is the default for when an area has no specific
  * function.  This may be used as part of a more specific routine.
@@ -446,6 +539,57 @@
 	return 1;
 }
 
+/*
+ * Try to free as many page directory entries as we can,
+ * without having to work very hard at actually scanning
+ * the page tables themselves.
+ *
+ * Right now we try to free page tables if we have a nice
+ * PGDIR-aligned area that got free'd up. We could be more
+ * granular if we want to, but this is fast and simple,
+ * and covers the bad cases.
+ *
+ * "prev", if it exists, points to a vma before the one
+ * we just free'd - but there's no telling how much before.
+ */
+static void free_pgtables(struct mm_struct * mm, struct vm_area_struct *prev,
+	unsigned long start, unsigned long end)
+{
+	unsigned long first = start & PGDIR_MASK;
+	unsigned long last = (end & PGDIR_MASK) + PGDIR_SIZE;
+
+	if (!prev) {
+		prev = mm->mmap;
+		if (!prev)
+			goto no_mmaps;
+		if (prev->vm_end > start) {
+			if (last > prev->vm_end)
+				last = prev->vm_end;
+			goto no_mmaps;
+		}
+	}
+	for (;;) {
+		struct vm_area_struct *next = prev->vm_next;
+
+		if (next) {
+			if (next->vm_start < start) {
+				prev = next;
+				continue;
+			}
+			if (last > next->vm_start)
+				last = next->vm_start;
+		}
+		if (prev->vm_end > first)
+			first = prev->vm_end + PGDIR_SIZE - 1;
+		break;
+	}
+no_mmaps:
+	first = first >> PGDIR_SHIFT;
+	last = last >> PGDIR_SHIFT;
+	if (last > first)
+		clear_page_tables(mm, first, last-first);
+}
+
 /* Munmap is split into 2 main parts -- this part which finds
  * what needs doing, and the areas themselves, which do the
  * work.  This now handles partial unmappings.
@@ -454,8 +598,7 @@
 int do_munmap(unsigned long addr, size_t len)
 {
 	struct mm_struct * mm;
-	struct vm_area_struct *mpnt, *free, *extra;
-	int freed;
+	struct vm_area_struct *mpnt, *prev, **npp, *free, *extra;
 
 	if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr)
 		return -EINVAL;
@@ -469,15 +612,17 @@
 	 * on the list.  If nothing is put on, nothing is affected.
 	 */
 	mm = current->mm;
-	mpnt = mm->mmap;
-	while(mpnt && mpnt->vm_end <= addr)
-		mpnt = mpnt->vm_next;
+	mpnt = find_vma_prev(mm, addr, &prev);
 	if (!mpnt)
 		return 0;
+	/* we have  addr < mpnt->vm_end  */
+
+	if (mpnt->vm_start >= addr+len)
+		return 0;
 
 	/* If we'll make "hole", check the vm areas limit */
-	if ((mpnt->vm_start < addr && mpnt->vm_end > addr+len) &&
-	    mm->map_count > MAX_MAP_COUNT)
+	if ((mpnt->vm_start < addr && mpnt->vm_end > addr+len)
+	    && mm->map_count >= MAX_MAP_COUNT)
 		return -ENOMEM;
 
 	/*
@@ -488,18 +633,14 @@
 	if (!extra)
 		return -ENOMEM;
 
-	/* we have addr < mpnt->vm_end */
+	npp = (prev ? &prev->vm_next : &mm->mmap);
 	free = NULL;
-	for ( ; mpnt && mpnt->vm_start < addr+len; ) {
-		struct vm_area_struct *next = mpnt->vm_next;
-
-		if(mpnt->vm_next)
-			mpnt->vm_next->vm_pprev = mpnt->vm_pprev;
-		*mpnt->vm_pprev = mpnt->vm_next;
-
+	for ( ; mpnt && mpnt->vm_start < addr+len; mpnt = *npp) {
+		*npp = mpnt->vm_next;
 		mpnt->vm_next = free;
 		free = mpnt;
-		mpnt = next;
+		if (mm->mmap_avl)
+			avl_remove(mpnt, &mm->mmap_avl);
 	}
 
 	/* Ok - we have the memory areas we should free on the 'free' list,
@@ -507,15 +648,10 @@
 	 * If the one of the segments is only being partially unmapped,
 	 * it will put new vm_area_struct(s) into the address space.
 	 */
-	freed = 0;
 	while ((mpnt = free) != NULL) {
 		unsigned long st, end, size;
 
 		free = free->vm_next;
-		freed = 1;
-
-		mm->map_count--;
-		remove_shared_vm_struct(mpnt);
 
 		st = addr < mpnt->vm_start ? mpnt->vm_start : addr;
 		end = addr+len;
@@ -525,6 +661,9 @@
 		if (mpnt->vm_ops && mpnt->vm_ops->unmap)
 			mpnt->vm_ops->unmap(mpnt, st, size);
 
+		remove_shared_vm_struct(mpnt);
+		mm->map_count--;
+
 		flush_cache_range(mm, st, end);
 		zap_page_range(mm, st, size);
 		flush_tlb_range(mm, st, end);
@@ -540,8 +679,9 @@
 	if (extra)
 		kmem_cache_free(vm_area_cachep, extra);
 
-	if (freed)
-		mm->mmap_cache = NULL;	/* Kill the cache. */
+	free_pgtables(mm, prev, addr, addr+len);
+
+	mm->mmap_cache = NULL;	/* Kill the cache. */
 	return 0;
 }
 
@@ -557,13 +697,23 @@
 	return ret;
 }
 
+/* Build the AVL tree corresponding to the VMA list. */
+void build_mmap_avl(struct mm_struct * mm)
+{
+	struct vm_area_struct * vma;
+
+	mm->mmap_avl = NULL;
+	for (vma = mm->mmap; vma; vma = vma->vm_next)
+		avl_insert(vma, &mm->mmap_avl);
+}
+
 /* Release all mmaps. */
 void exit_mmap(struct mm_struct * mm)
 {
 	struct vm_area_struct * mpnt;
 
 	mpnt = mm->mmap;
-	mm->mmap = mm->mmap_cache = NULL;
+	mm->mmap = mm->mmap_avl = mm->mmap_cache = NULL;
 	mm->rss = 0;
 	mm->total_vm = 0;
 	mm->locked_vm = 0;
@@ -591,6 +741,8 @@
 	/* This is just debugging */
 	if (mm->map_count)
 		printk("exit_mmap: map count is %d\n", mm->map_count);
+
+	clear_page_tables(mm, 0, USER_PTRS_PER_PGD);
 }
 
 /* Insert vm structure into process list sorted by address
@@ -598,20 +750,26 @@
  */
 void insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vmp)
 {
-	struct vm_area_struct **pprev = &mm->mmap;
+	struct vm_area_struct **pprev;
 	struct file * file;
 
-	mm->map_count++;
-
-	/* Find where to link it in. */
-	while(*pprev && (*pprev)->vm_start <= vmp->vm_start)
-		pprev = &(*pprev)->vm_next;
-
-	/* Insert it. */
-	if((vmp->vm_next = *pprev) != NULL)
-		(*pprev)->vm_pprev = &vmp->vm_next;
+	if (!mm->mmap_avl) {
+		pprev = &mm->mmap;
+		while (*pprev && (*pprev)->vm_start <= vmp->vm_start)
+			pprev = &(*pprev)->vm_next;
+	} else {
+		struct vm_area_struct *prev, *next;
+		avl_insert_neighbours(vmp, &mm->mmap_avl, &prev, &next);
+		pprev = (prev ? &prev->vm_next : &mm->mmap);
+		if (*pprev != next)
+			printk("insert_vm_struct: tree inconsistent with list\n");
+	}
+	vmp->vm_next = *pprev;
 	*pprev = vmp;
-	vmp->vm_pprev = pprev;
+
+	mm->map_count++;
+	if (mm->map_count >= AVL_MIN_MAP_COUNT && !mm->mmap_avl)
+		build_mmap_avl(mm);
 
 	file = vmp->vm_file;
 	if (file) {
@@ -637,23 +795,17 @@
  */
 void merge_segments (struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr)
 {
-	struct vm_area_struct *prev, *mpnt, *next;
+	struct vm_area_struct *prev, *mpnt, *next, *prev1;
 
-	prev = NULL;
-	mpnt = mm->mmap;
-	while(mpnt && mpnt->vm_end <= start_addr) {
-		prev = mpnt;
-		mpnt = mpnt->vm_next;
-	}
+	mpnt = find_vma_prev(mm, start_addr, &prev1);
 	if (!mpnt)
 		return;
 
-	next = mpnt->vm_next;
-
-	/* we have prev->vm_next == mpnt && mpnt->vm_next = next */
-	if (!prev) {
+	if (prev1) {
+		prev = prev1;
+	} else {
 		prev = mpnt;
-		mpnt = next;
+		mpnt = mpnt->vm_next;
 	}
 
 	/* prev and mpnt cycle through the list, as long as
@@ -684,11 +836,10 @@
 		 * big segment can possibly merge with the next one.
 		 * The old unused mpnt is freed.
 		 */
-		if(mpnt->vm_next)
-			mpnt->vm_next->vm_pprev = mpnt->vm_pprev;
-		*mpnt->vm_pprev = mpnt->vm_next;
-
+		if (mm->mmap_avl)
+			avl_remove(mpnt, &mm->mmap_avl);
 		prev->vm_end = mpnt->vm_end;
+		prev->vm_next = mpnt->vm_next;
 		if (mpnt->vm_ops && mpnt->vm_ops->close) {
 			mpnt->vm_offset += mpnt->vm_end - mpnt->vm_start;
 			mpnt->vm_start = mpnt->vm_end;
diff -u --recursive --new-file v2.2.0-pre7/linux/mm/mmap_avl.c linux/mm/mmap_avl.c
--- v2.2.0-pre7/linux/mm/mmap_avl.c	Wed Dec 31 16:00:00 1969
+++ linux/mm/mmap_avl.c	Fri Jan 15 14:41:04 1999
@@ -0,0 +1,374 @@
+/*
+ * Searching a VMA in the linear list task->mm->mmap is horribly slow.
+ * Use an AVL (Adelson-Velskii and Landis) tree to speed up this search
+ * from O(n) to O(log n), where n is the number of VMAs of the task
+ * n is typically around 6, but may reach 3000 in some cases: object-oriented
+ * databases, persistent store, generational garbage collection (Java, Lisp),
+ * ElectricFence.
+ * Written by Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de>.
+ */
+
+/* We keep the list and tree sorted by address. */
+#define vm_avl_key	vm_end
+#define vm_avl_key_t	unsigned long	/* typeof(vma->avl_key) */
+
+/*
+ * task->mm->mmap_avl is the AVL tree corresponding to task->mm->mmap
+ * or, more exactly, its root.
+ * A vm_area_struct has the following fields:
+ *   vm_avl_left     left son of a tree node
+ *   vm_avl_right    right son of a tree node
+ *   vm_avl_height   1+max(heightof(left),heightof(right))
+ * The empty tree is represented as NULL.
+ */
+
+/* Since the trees are balanced, their height will never be large. */
+#define avl_maxheight	41	/* why this? a small exercise */
+#define heightof(tree)	((tree) == vm_avl_empty ? 0 : (tree)->vm_avl_height)
+/*
+ * Consistency and balancing rules:
+ * 1. tree->vm_avl_height == 1+max(heightof(tree->vm_avl_left),heightof(tree->vm_avl_right))
+ * 2. abs( heightof(tree->vm_avl_left) - heightof(tree->vm_avl_right) ) <= 1
+ * 3. foreach node in tree->vm_avl_left: node->vm_avl_key <= tree->vm_avl_key,
+ *    foreach node in tree->vm_avl_right: node->vm_avl_key >= tree->vm_avl_key.
+ */
+
+#ifdef DEBUG_AVL
+
+/* Look up the nodes at the left and at the right of a given node. */
+static void avl_neighbours (struct vm_area_struct * node, struct vm_area_struct * tree, struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right)
+{
+	vm_avl_key_t key = node->vm_avl_key;
+
+	*to_the_left = *to_the_right = NULL;
+	for (;;) {
+		if (tree == vm_avl_empty) {
+			printk("avl_neighbours: node not found in the tree\n");
+			return;
+		}
+		if (key == tree->vm_avl_key)
+			break;
+		if (key < tree->vm_avl_key) {
+			*to_the_right = tree;
+			tree = tree->vm_avl_left;
+		} else {
+			*to_the_left = tree;
+			tree = tree->vm_avl_right;
+		}
+	}
+	if (tree != node) {
+		printk("avl_neighbours: node not exactly found in the tree\n");
+		return;
+	}
+	if (tree->vm_avl_left != vm_avl_empty) {
+		struct vm_area_struct * node;
+		for (node = tree->vm_avl_left; node->vm_avl_right != vm_avl_empty; node = node->vm_avl_right)
+			continue;
+		*to_the_left = node;
+	}
+	if (tree->vm_avl_right != vm_avl_empty) {
+		struct vm_area_struct * node;
+		for (node = tree->vm_avl_right; node->vm_avl_left != vm_avl_empty; node = node->vm_avl_left)
+			continue;
+		*to_the_right = node;
+	}
+	if ((*to_the_left && ((*to_the_left)->vm_next != node)) || (node->vm_next != *to_the_right))
+		printk("avl_neighbours: tree inconsistent with list\n");
+}
+
+#endif
+
+/*
+ * Rebalance a tree.
+ * After inserting or deleting a node of a tree we have a sequence of subtrees
+ * nodes[0]..nodes[k-1] such that
+ * nodes[0] is the root and nodes[i+1] = nodes[i]->{vm_avl_left|vm_avl_right}.
+ */
+static void avl_rebalance (struct vm_area_struct *** nodeplaces_ptr, int count)
+{
+	for ( ; count > 0 ; count--) {
+		struct vm_area_struct ** nodeplace = *--nodeplaces_ptr;
+		struct vm_area_struct * node = *nodeplace;
+		struct vm_area_struct * nodeleft = node->vm_avl_left;
+		struct vm_area_struct * noderight = node->vm_avl_right;
+		int heightleft = heightof(nodeleft);
+		int heightright = heightof(noderight);
+		if (heightright + 1 < heightleft) {
+			/*                                                      */
+			/*                            *                         */
+			/*                          /   \                       */
+			/*                       n+2      n                     */
+			/*                                                      */
+			struct vm_area_struct * nodeleftleft = nodeleft->vm_avl_left;
+			struct vm_area_struct * nodeleftright = nodeleft->vm_avl_right;
+			int heightleftright = heightof(nodeleftright);
+			if (heightof(nodeleftleft) >= heightleftright) {
+				/*                                                        */
+				/*                *                    n+2|n+3            */
+				/*              /   \                  /    \             */
+				/*           n+2      n      -->      /   n+1|n+2         */
+				/*           / \                      |    /    \         */
+				/*         n+1 n|n+1                 n+1  n|n+1  n        */
+				/*                                                        */
+				node->vm_avl_left = nodeleftright; nodeleft->vm_avl_right = node;
+				nodeleft->vm_avl_height = 1 + (node->vm_avl_height = 1 + heightleftright);
+				*nodeplace = nodeleft;
+			} else {
+				/*                                                        */
+				/*                *                     n+2               */
+				/*              /   \                 /     \             */
+				/*           n+2      n      -->    n+1     n+1           */
+				/*           / \                    / \     / \           */
+				/*          n  n+1                 n   L   R   n          */
+				/*             / \                                        */
+				/*            L   R                                       */
+				/*                                                        */
+				nodeleft->vm_avl_right = nodeleftright->vm_avl_left;
+				node->vm_avl_left = nodeleftright->vm_avl_right;
+				nodeleftright->vm_avl_left = nodeleft;
+				nodeleftright->vm_avl_right = node;
+				nodeleft->vm_avl_height = node->vm_avl_height = heightleftright;
+				nodeleftright->vm_avl_height = heightleft;
+				*nodeplace = nodeleftright;
+			}
+		}
+		else if (heightleft + 1 < heightright) {
+			/* similar to the above, just interchange 'left' <--> 'right' */
+			struct vm_area_struct * noderightright = noderight->vm_avl_right;
+			struct vm_area_struct * noderightleft = noderight->vm_avl_left;
+			int heightrightleft = heightof(noderightleft);
+			if (heightof(noderightright) >= heightrightleft) {
+				node->vm_avl_right = noderightleft; noderight->vm_avl_left = node;
+				noderight->vm_avl_height = 1 + (node->vm_avl_height = 1 + heightrightleft);
+				*nodeplace = noderight;
+			} else {
+				noderight->vm_avl_left = noderightleft->vm_avl_right;
+				node->vm_avl_right = noderightleft->vm_avl_left;
+				noderightleft->vm_avl_right = noderight;
+				noderightleft->vm_avl_left = node;
+				noderight->vm_avl_height = node->vm_avl_height = heightrightleft;
+				noderightleft->vm_avl_height = heightright;
+				*nodeplace = noderightleft;
+			}
+		}
+		else {
+			int height = (heightleft<heightright ? heightright : heightleft) + 1;
+			if (height == node->vm_avl_height)
+				break;
+			node->vm_avl_height = height;
+		}
+	}
+}
+
+/* Insert a node into a tree. */
+static inline void avl_insert (struct vm_area_struct * new_node, struct vm_area_struct ** ptree)
+{
+	vm_avl_key_t key = new_node->vm_avl_key;
+	struct vm_area_struct ** nodeplace = ptree;
+	struct vm_area_struct ** stack[avl_maxheight];
+	int stack_count = 0;
+	struct vm_area_struct *** stack_ptr = &stack[0]; /* = &stack[stackcount] */
+	for (;;) {
+		struct vm_area_struct * node = *nodeplace;
+		if (node == vm_avl_empty)
+			break;
+		*stack_ptr++ = nodeplace; stack_count++;
+		if (key < node->vm_avl_key)
+			nodeplace = &node->vm_avl_left;
+		else
+			nodeplace = &node->vm_avl_right;
+	}
+	new_node->vm_avl_left = vm_avl_empty;
+	new_node->vm_avl_right = vm_avl_empty;
+	new_node->vm_avl_height = 1;
+	*nodeplace = new_node;
+	avl_rebalance(stack_ptr,stack_count);
+}
+
+/* Insert a node into a tree, and
+ * return the node to the left of it and the node to the right of it.
+ */
+static inline void avl_insert_neighbours (struct vm_area_struct * new_node, struct vm_area_struct ** ptree,
+	struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right)
+{
+	vm_avl_key_t key = new_node->vm_avl_key;
+	struct vm_area_struct ** nodeplace = ptree;
+	struct vm_area_struct ** stack[avl_maxheight];
+	int stack_count = 0;
+	struct vm_area_struct *** stack_ptr = &stack[0]; /* = &stack[stackcount] */
+	*to_the_left = *to_the_right = NULL;
+	for (;;) {
+		struct vm_area_struct * node = *nodeplace;
+		if (node == vm_avl_empty)
+			break;
+		*stack_ptr++ = nodeplace; stack_count++;
+		if (key < node->vm_avl_key) {
+			*to_the_right = node;
+			nodeplace = &node->vm_avl_left;
+		} else {
+			*to_the_left = node;
+			nodeplace = &node->vm_avl_right;
+		}
+	}
+	new_node->vm_avl_left = vm_avl_empty;
+	new_node->vm_avl_right = vm_avl_empty;
+	new_node->vm_avl_height = 1;
+	*nodeplace = new_node;
+	avl_rebalance(stack_ptr,stack_count);
+}
+
+/* Removes a node out of a tree. */
+static void avl_remove (struct vm_area_struct * node_to_delete, struct vm_area_struct ** ptree)
+{
+	vm_avl_key_t key = node_to_delete->vm_avl_key;
+	struct vm_area_struct ** nodeplace = ptree;
+	struct vm_area_struct ** stack[avl_maxheight];
+	int stack_count = 0;
+	struct vm_area_struct *** stack_ptr = &stack[0]; /* = &stack[stackcount] */
+	struct vm_area_struct ** nodeplace_to_delete;
+	for (;;) {
+		struct vm_area_struct * node = *nodeplace;
+#ifdef DEBUG_AVL
+		if (node == vm_avl_empty) {
+			/* what? node_to_delete not found in tree? */
+			printk("avl_remove: node to delete not found in tree\n");
+			return;
+		}
+#endif
+		*stack_ptr++ = nodeplace; stack_count++;
+		if (key == node->vm_avl_key)
+			break;
+		if (key < node->vm_avl_key)
+			nodeplace = &node->vm_avl_left;
+		else
+			nodeplace = &node->vm_avl_right;
+	}
+	nodeplace_to_delete = nodeplace;
+	/* Have to remove node_to_delete = *nodeplace_to_delete. */
+	if (node_to_delete->vm_avl_left == vm_avl_empty) {
+		*nodeplace_to_delete = node_to_delete->vm_avl_right;
+		stack_ptr--; stack_count--;
+	} else {
+		struct vm_area_struct *** stack_ptr_to_delete = stack_ptr;
+		struct vm_area_struct ** nodeplace = &node_to_delete->vm_avl_left;
+		struct vm_area_struct * node;
+		for (;;) {
+			node = *nodeplace;
+			if (node->vm_avl_right == vm_avl_empty)
+				break;
+			*stack_ptr++ = nodeplace; stack_count++;
+			nodeplace = &node->vm_avl_right;
+		}
+		*nodeplace = node->vm_avl_left;
+		/* node replaces node_to_delete */
+		node->vm_avl_left = node_to_delete->vm_avl_left;
+		node->vm_avl_right = node_to_delete->vm_avl_right;
+		node->vm_avl_height = node_to_delete->vm_avl_height;
+		*nodeplace_to_delete = node; /* replace node_to_delete */
+		*stack_ptr_to_delete = &node->vm_avl_left; /* replace &node_to_delete->vm_avl_left */
+	}
+	avl_rebalance(stack_ptr,stack_count);
+}
+
+#ifdef DEBUG_AVL
+
+/* print a list */
+static void printk_list (struct vm_area_struct * vma)
+{
+	printk("[");
+	while (vma) {
+		printk("%08lX-%08lX", vma->vm_start, vma->vm_end);
+		vma = vma->vm_next;
+		if (!vma)
+			break;
+		printk(" ");
+	}
+	printk("]");
+}
+
+/* print a tree */
+static void printk_avl (struct vm_area_struct * tree)
+{
+	if (tree != vm_avl_empty) {
+		printk("(");
+		if (tree->vm_avl_left != vm_avl_empty) {
+			printk_avl(tree->vm_avl_left);
+			printk("<");
+		}
+		printk("%08lX-%08lX", tree->vm_start, tree->vm_end);
+		if (tree->vm_avl_right != vm_avl_empty) {
+			printk(">");
+			printk_avl(tree->vm_avl_right);
+		}
+		printk(")");
+	}
+}
+
+static char *avl_check_point = "somewhere";
+
+/* check a tree's consistency and balancing */
+static void avl_checkheights (struct vm_area_struct * tree)
+{
+	int h, hl, hr;
+
+	if (tree == vm_avl_empty)
+		return;
+	avl_checkheights(tree->vm_avl_left);
+	avl_checkheights(tree->vm_avl_right);
+	h = tree->vm_avl_height;
+	hl = heightof(tree->vm_avl_left);
+	hr = heightof(tree->vm_avl_right);
+	if ((h == hl+1) && (hr <= hl) && (hl <= hr+1))
+		return;
+	if ((h == hr+1) && (hl <= hr) && (hr <= hl+1))
+		return;
+	printk("%s: avl_checkheights: heights inconsistent\n",avl_check_point);
+}
+
+/* check that all values stored in a tree are < key */
+static void avl_checkleft (struct vm_area_struct * tree, vm_avl_key_t key)
+{
+	if (tree == vm_avl_empty)
+		return;
+	avl_checkleft(tree->vm_avl_left,key);
+	avl_checkleft(tree->vm_avl_right,key);
+	if (tree->vm_avl_key < key)
+		return;
+	printk("%s: avl_checkleft: left key %lu >= top key %lu\n",avl_check_point,tree->vm_avl_key,key);
+}
+
+/* check that all values stored in a tree are > key */
+static void avl_checkright (struct vm_area_struct * tree, vm_avl_key_t key)
+{
+	if (tree == vm_avl_empty)
+		return;
+	avl_checkright(tree->vm_avl_left,key);
+	avl_checkright(tree->vm_avl_right,key);
+	if (tree->vm_avl_key > key)
+		return;
+	printk("%s: avl_checkright: right key %lu <= top key %lu\n",avl_check_point,tree->vm_avl_key,key);
+}
+
+/* check that all values are properly increasing */
+static void avl_checkorder (struct vm_area_struct * tree)
+{
+	if (tree == vm_avl_empty)
+		return;
+	avl_checkorder(tree->vm_avl_left);
+	avl_checkorder(tree->vm_avl_right);
+	avl_checkleft(tree->vm_avl_left,tree->vm_avl_key);
+	avl_checkright(tree->vm_avl_right,tree->vm_avl_key);
+}
+
+/* all checks */
+static void avl_check (struct task_struct * task, char *caller)
+{
+	avl_check_point = caller;
+/*	printk("task \"%s\", %s\n",task->comm,caller); */
+/*	printk("task \"%s\" list: ",task->comm); printk_list(task->mm->mmap); printk("\n"); */
+/*	printk("task \"%s\" tree: ",task->comm); printk_avl(task->mm->mmap_avl); printk("\n"); */
+	avl_checkheights(task->mm->mmap_avl);
+	avl_checkorder(task->mm->mmap_avl);
+}
+
+#endif
diff -u --recursive --new-file v2.2.0-pre7/linux/mm/vmalloc.c linux/mm/vmalloc.c
--- v2.2.0-pre7/linux/mm/vmalloc.c	Fri Nov 27 13:09:31 1998
+++ linux/mm/vmalloc.c	Mon Jan 18 10:19:28 1999
@@ -161,8 +161,10 @@
 	for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
 		if (size + addr < (unsigned long) tmp->addr)
 			break;
-		if (addr > VMALLOC_END-size)
+		if (addr > VMALLOC_END-size) {
+			kfree(area);
 			return NULL;
+		}
 		addr = tmp->size + (unsigned long) tmp->addr;
 	}
 	area->addr = (void *)addr;
diff -u --recursive --new-file v2.2.0-pre7/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.2.0-pre7/linux/mm/vmscan.c	Wed Jan 13 15:00:44 1999
+++ linux/mm/vmscan.c	Thu Jan 14 11:40:55 1999
@@ -126,10 +126,14 @@
 	 *
 	 * That would get rid of a lot of problems.
 	 */
+	flush_cache_page(vma, address);
 	if (vma->vm_ops && vma->vm_ops->swapout) {
 		pid_t pid = tsk->pid;
+		pte_clear(page_table);
+		flush_tlb_page(vma, address);
 		vma->vm_mm->rss--;
-		if (vma->vm_ops->swapout(vma, address - vma->vm_start + vma->vm_offset, page_table))
+		
+		if (vma->vm_ops->swapout(vma, page_map))
 			kill_proc(pid, SIGBUS, 1);
 		__free_page(page_map);
 		return 1;
@@ -147,7 +151,6 @@
 		
 	vma->vm_mm->rss--;
 	tsk->nswap++;
-	flush_cache_page(vma, address);
 	set_pte(page_table, __pte(entry));
 	flush_tlb_page(vma, address);
 	swap_duplicate(entry);	/* One for the process, one for the swap cache */
diff -u --recursive --new-file v2.2.0-pre7/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v2.2.0-pre7/linux/net/ipv4/ip_input.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ip_input.c	Sun Jan 17 10:00:00 1999
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) module.
  *
- * Version:	$Id: ip_input.c,v 1.34 1998/10/03 09:37:23 davem Exp $
+ * Version:	$Id: ip_input.c,v 1.35 1999/01/12 14:32:48 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -503,7 +503,9 @@
         {
 		int fwres;
 		u16 rport;
+#ifdef  CONFIG_IP_ROUTE_TOS
 		u8  tos = iph->tos;
+#endif
 
 		if ((fwres=call_in_firewall(PF_INET, skb->dev, iph, &rport, &skb))<FW_ACCEPT) {
 			if (fwres==FW_REJECT)
diff -u --recursive --new-file v2.2.0-pre7/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
--- v2.2.0-pre7/linux/net/ipv4/ip_masq.c	Thu Jan  7 15:11:41 1999
+++ linux/net/ipv4/ip_masq.c	Sun Jan 17 10:00:00 1999
@@ -4,7 +4,7 @@
  *
  * 	Copyright (c) 1994 Pauline Middelink
  *
- *	$Id: ip_masq.c,v 1.32 1999/01/04 20:37:05 davem Exp $
+ *	$Id: ip_masq.c,v 1.33 1999/01/15 06:45:17 davem Exp $
  *
  *
  *	See ip_fw.c for original log
@@ -1678,7 +1678,8 @@
 			return -1;
 		}
 		ciph = (struct iphdr *) (icmph + 1);
-
+		cicmph = (struct icmphdr *)((char *)ciph + 
+					    (ciph->ihl<<2));
 		/* Now we do real damage to this packet...! */
 		/* First change the dest IP address, and recalc checksum */
 		iph->daddr = ms->saddr;
@@ -1753,6 +1754,7 @@
 		return -1;
 	}
 	ciph = (struct iphdr *) (icmph + 1);
+	pptr = (__u16 *)&(((char *)ciph)[ciph->ihl*4]);
 
 	/* Now we do real damage to this packet...! */
 	/* First change the dest IP address, and recalc checksum */
diff -u --recursive --new-file v2.2.0-pre7/linux/net/ipv4/ipconfig.c linux/net/ipv4/ipconfig.c
--- v2.2.0-pre7/linux/net/ipv4/ipconfig.c	Thu Jan  7 15:11:41 1999
+++ linux/net/ipv4/ipconfig.c	Sun Jan 17 10:00:00 1999
@@ -1,5 +1,5 @@
 /*
- *  $Id: ipconfig.c,v 1.18 1999/01/04 20:14:10 davem Exp $
+ *  $Id: ipconfig.c,v 1.19 1999/01/15 06:54:00 davem Exp $
  *
  *  Automatic Configuration of IP -- use BOOTP or RARP or user-supplied
  *  information to configure own IP address and routes.
@@ -73,7 +73,14 @@
 
 #define CONFIG_IP_PNP_DYNAMIC
 
-static int ic_proto_enabled __initdata = IC_BOOTP | IC_RARP;	/* Protocols enabled */
+static int ic_proto_enabled __initdata = 0			/* Protocols enabled */
+#ifdef CONFIG_IP_PNP_BOOTP
+			| IC_BOOTP
+#endif
+#ifdef CONFIG_IP_PNP_RARP
+			| IC_RARP
+#endif
+			;
 static int ic_got_reply __initdata = 0;				/* Protocol(s) we got reply from */
 
 #else
@@ -506,10 +513,9 @@
 	h->ihl = 5;
 	h->tot_len = htons(sizeof(struct bootp_pkt));
 	h->frag_off = htons(IP_DF);
-	h->ttl = 1;
+	h->ttl = 64;
 	h->protocol = IPPROTO_UDP;
 	h->daddr = INADDR_BROADCAST;
-	h->check = 0;
 	h->check = ip_fast_csum((unsigned char *) h, h->ihl);
 
 	/* Construct UDP header */
diff -u --recursive --new-file v2.2.0-pre7/linux/net/ipv4/ipmr.c linux/net/ipv4/ipmr.c
--- v2.2.0-pre7/linux/net/ipv4/ipmr.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ipmr.c	Sun Jan 17 10:00:00 1999
@@ -9,7 +9,7 @@
  *	as published by the Free Software Foundation; either version
  *	2 of the License, or (at your option) any later version.
  *
- *	Version: $Id: ipmr.c,v 1.37 1998/10/03 09:37:39 davem Exp $
+ *	Version: $Id: ipmr.c,v 1.38 1999/01/12 14:34:40 davem Exp $
  *
  *	Fixes:
  *	Michael Chastain	:	Incorrect size of copying.
@@ -267,7 +267,6 @@
 				cache->mfc_minvif = vifi;
 			if (cache->mfc_maxvif <= vifi)
 				cache->mfc_maxvif = vifi + 1;
-			vifi++;
 		}
 	}
 	end_bh_atomic();
diff -u --recursive --new-file v2.2.0-pre7/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.2.0-pre7/linux/net/ipv4/route.c	Thu Jan  7 15:11:41 1999
+++ linux/net/ipv4/route.c	Sun Jan 17 10:00:00 1999
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.60 1999/01/04 20:14:52 davem Exp $
+ * Version:	$Id: route.c,v 1.61 1999/01/12 14:34:43 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1307,6 +1307,7 @@
 			key.dst = key.src = htonl(INADDR_LOOPBACK);
 		dev_out = &loopback_dev;
 		key.oif = loopback_dev.ifindex;
+		res.type = RTN_LOCAL;
 		flags |= RTCF_LOCAL;
 		goto make_route;
 	}
@@ -1334,6 +1335,7 @@
 
 			if (key.src == 0)
 				key.src = inet_select_addr(dev_out, 0, RT_SCOPE_LINK);
+			res.type = RTN_UNICAST;
 			goto make_route;
 		}
 		return -ENETUNREACH;
diff -u --recursive --new-file v2.2.0-pre7/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.2.0-pre7/linux/net/ipv4/tcp.c	Fri Jan  8 22:36:25 1999
+++ linux/net/ipv4/tcp.c	Sun Jan 17 10:00:00 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.133 1998/11/30 15:13:06 davem Exp $
+ * Version:	$Id: tcp.c,v 1.134 1999/01/09 08:50:09 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
diff -u --recursive --new-file v2.2.0-pre7/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.2.0-pre7/linux/net/ipv4/tcp_input.c	Thu Jan  7 15:11:41 1999
+++ linux/net/ipv4/tcp_input.c	Sun Jan 17 10:00:00 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.145 1999/01/04 20:49:11 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.150 1999/01/16 08:31:08 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -111,12 +111,12 @@
 			m = 1;
 		if(m > tp->rto)
 			tp->ato = tp->rto;
-		else
-			tp->ato = (tp->ato >> 1) + m;
-
-		/* We are not in "quick ack" mode. */
-		if(tp->ato <= (HZ/100))
-			tp->ato = ((HZ/100)*2);
+		else {
+			/* This funny shift makes sure we
+			 * clear the "quick ack mode" bit.
+			 */
+			tp->ato = ((tp->ato << 1) >> 2) + m;
+		}
 	}
 }
 
@@ -127,7 +127,10 @@
 					struct sk_buff *skb)
 {
 	tp->delayed_acks++; 
-	/* Tiny-grams with PSH set make us ACK quickly. */
+
+	/* Tiny-grams with PSH set make us ACK quickly.
+	 * Note: This also clears the "quick ack mode" bit.
+	 */
 	if(th->psh && (skb->len < (tp->mss_cache >> 1)))
 		tp->ato = HZ/50;
 } 
@@ -728,10 +731,7 @@
 	} else {
 		tcp_set_rto(tp);
 	}
-	if (should_advance_cwnd(tp, flag))
-		tcp_cong_avoid(tp);
 
-	/* NOTE: safe here so long as cong_ctl doesn't use rto */
 	tcp_bound_rto(tp);
 }
 
@@ -820,6 +820,12 @@
 	/* See if we can take anything off of the retransmit queue. */
 	flag |= tcp_clean_rtx_queue(sk, ack, &seq, &seq_rtt);
 
+	/* We must do this here, before code below clears out important
+	 * state contained in tp->fackets_out and tp->retransmits.  -DaveM
+	 */
+	if (should_advance_cwnd(tp, flag))
+		tcp_cong_avoid(tp);
+
 	/* If we have a timestamp, we always do rtt estimates. */
 	if (tp->saw_tstamp) {
 		tcp_ack_saw_tstamp(sk, tp, seq, ack, flag);
@@ -849,8 +855,6 @@
 				}
 			}
 		}
-		if (should_advance_cwnd(tp, flag))
-			tcp_cong_avoid(tp);
 	}
 
 	if (tp->packets_out) {
@@ -1302,7 +1306,7 @@
 	int num_sacks = tp->num_sacks;
 	int this_sack;
 
-	for(this_sack = 0; this_sack < num_sacks; this_sack++, tp++) {
+	for(this_sack = 0; this_sack < num_sacks; this_sack++, sp++) {
 		if(sp->end_seq == TCP_SKB_CB(old_skb)->end_seq)
 			break;
 	}
@@ -1350,7 +1354,7 @@
 
 	/*  Queue data for delivery to the user.
 	 *  Packets in sequence go to the receive queue.
-	 *  Out of sequence packets to out_of_order_queue.
+	 *  Out of sequence packets to the out_of_order_queue.
 	 */
 	if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {
 		/* Ok. In sequence. */
@@ -1398,7 +1402,7 @@
 	tp->delayed_acks++;
 	tcp_enter_quickack_mode(tp);
 
-	/* Disable header predition. */
+	/* Disable header prediction. */
 	tp->pred_flags = 0;
 
 	SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n",
@@ -1664,7 +1668,7 @@
 /* Clean the out_of_order queue if we can, trying to get
  * the socket within its memory limits again.
  *
- * Return less than zero if we should stop dropping frames
+ * Return less than zero if we should start dropping frames
  * until the socket owning process reads some of the data
  * to stabilize the situation.
  */
@@ -1677,15 +1681,22 @@
 
 	net_statistics.PruneCalled++; 
 
-	/* Clean the out_of_order queue.
-	 * Start with the end because there are probably the least
-	 * useful packets (crossing fingers).
-	 */
-	while ((skb = __skb_dequeue_tail(&tp->out_of_order_queue))) { 
-		net_statistics.OfoPruned += skb->len; 
-		kfree_skb(skb);
-		if (atomic_read(&sk->rmem_alloc) <= sk->rcvbuf)
-			return 0;
+	/* First, purge the out_of_order queue. */
+	skb = __skb_dequeue_tail(&tp->out_of_order_queue);
+	if(skb != NULL) {
+		/* Free it all. */
+		do {	net_statistics.OfoPruned += skb->len; 
+			kfree_skb(skb);
+			skb = __skb_dequeue_tail(&tp->out_of_order_queue);
+		} while((skb = __skb_dequeue_tail(&tp->out_of_order_queue)) != NULL);
+
+		/* Reset SACK state.  A conforming SACK implementation will
+		 * do the same at a timeout based retransmit.  When a connection
+		 * is in a sad state like this, we care only about integrity
+		 * of the connection not performance.
+		 */
+		if(tp->sack_ok)
+			tp->num_sacks = 0;
 	}
 	
 	/* If we are really being abused, tell the caller to silently
@@ -1766,6 +1777,7 @@
 	if (tcp_fast_parse_options(sk, th, tp)) {
 		if (tp->saw_tstamp) {
 			if (tcp_paws_discard(tp, th, len)) {
+				tcp_statistics.TcpInErrs++;
 				if (!th->rst) {
 					tcp_send_ack(sk);
 					goto discard;
@@ -2152,6 +2164,7 @@
 		 */
 		if (tp->saw_tstamp) {
 			if (tcp_paws_discard(tp, th, len)) {
+				tcp_statistics.TcpInErrs++;
 				if (!th->rst) {
 					tcp_send_ack(sk);
 					goto discard;
diff -u --recursive --new-file v2.2.0-pre7/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.2.0-pre7/linux/net/ipv4/tcp_output.c	Thu Dec 31 10:29:03 1998
+++ linux/net/ipv4/tcp_output.c	Sun Jan 17 10:00:00 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_output.c,v 1.98 1998/12/12 06:43:35 davem Exp $
+ * Version:	$Id: tcp_output.c,v 1.100 1999/01/16 08:31:06 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -49,7 +49,7 @@
 
 	tp->delayed_acks = 0;
 	if(tcp_in_quickack_mode(tp))
-		tp->ato = ((HZ/100)*2);
+		tcp_exit_quickack_mode(tp);
 	tcp_clear_xmit_timer(sk, TIME_DACK);
 }
 
@@ -99,7 +99,7 @@
 			}
 			if(sysctl_tcp_sack) {
 				sysctl_flags |= SYSCTL_FLAG_SACK;
-				if(!sysctl_tcp_timestamps)
+				if(!(sysctl_flags & SYSCTL_FLAG_TSTAMPS))
 					tcp_header_size += TCPOLEN_SACKPERM_ALIGNED;
 			}
 		} else if(tp->sack_ok && tp->num_sacks) {
@@ -997,7 +997,13 @@
 			 * (ACK is unreliable) but it's much better use of
 			 * bandwidth on slow links to send a spare ack than
 			 * resend packets.
+			 *
+			 * This is the one possible way that we can delay an
+			 * ACK and have tp->ato indicate that we are in
+			 * quick ack mode, so clear it.
 			 */
+			if(tcp_in_quickack_mode(tp))
+				tcp_exit_quickack_mode(tp);
 			tcp_send_delayed_ack(tp, HZ/2);
 			return;
 		}
diff -u --recursive --new-file v2.2.0-pre7/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.2.0-pre7/linux/net/ipv6/addrconf.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv6/addrconf.c	Sun Jan 17 10:00:00 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: addrconf.c,v 1.45 1998/08/26 12:04:41 davem Exp $
+ *	$Id: addrconf.c,v 1.46 1999/01/12 14:34:47 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -1119,7 +1119,7 @@
 		if (dev->mtu >= IPV6_MIN_MTU) {
 			struct inet6_dev *idev;
 
-			if ((idev = ipv6_find_idev(dev)) == NULL)
+			if ((idev = ipv6_get_idev(dev)) == NULL)
 				break;
 			idev->cnf.mtu6 = dev->mtu;
 			rt6_mtu_change(dev, dev->mtu);
diff -u --recursive --new-file v2.2.0-pre7/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.2.0-pre7/linux/net/ipv6/af_inet6.c	Thu Jan  7 15:11:42 1999
+++ linux/net/ipv6/af_inet6.c	Sun Jan 17 18:25:48 1999
@@ -35,6 +35,7 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <linux/init.h>
+#include <linux/version.h>
 
 #include <linux/inet.h>
 #include <linux/netdevice.h>
diff -u --recursive --new-file v2.2.0-pre7/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- v2.2.0-pre7/linux/net/ipv6/ndisc.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv6/ndisc.c	Sun Jan 17 10:00:00 1999
@@ -144,8 +144,9 @@
 	opt[1] = space>>3;
 	memcpy(opt+2, data, data_len);
 	data_len += 2;
+	opt += data_len;
 	if ((space -= data_len) > 0)
-		memset(opt + data_len, 0, space);
+		memset(opt, 0, space);
 	return opt + space;
 }
 
diff -u --recursive --new-file v2.2.0-pre7/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.2.0-pre7/linux/net/ipx/af_ipx.c	Thu Jan  7 15:11:42 1999
+++ linux/net/ipx/af_ipx.c	Sun Jan 17 18:25:48 1999
@@ -1973,8 +1973,10 @@
 		uaddr.sipx_network 	= 0;
 
 #ifdef CONFIG_IPX_INTERN
-		memcpy(uaddr.sipx_node, sk->protinfo.af_ipx.intrfc->if_node,
-		       IPX_NODE_LEN);
+		if(sk->protinfo.af_ipx.intrfc)
+			memcpy(uaddr.sipx_node, sk->protinfo.af_ipx.intrfc->if_node,IPX_NODE_LEN);
+		else
+			return -ENETDOWN;		/* Someone zonked the iface */
 #endif	/* CONFIG_IPX_INTERN */
 
 		ret = ipx_bind(sock, (struct sockaddr *)&uaddr,
@@ -2185,8 +2187,10 @@
 		uaddr.sipx_network 	= 0;
 
 #ifdef CONFIG_IPX_INTERN
-		memcpy(uaddr.sipx_node, sk->protinfo.af_ipx.intrfc->if_node,
-		       IPX_NODE_LEN);
+		if(sk->protinfo.af_ipx.intrfc)
+			memcpy(uaddr.sipx_node, sk->protinfo.af_ipx.intrfc->if_node,IPX_NODE_LEN);
+		else
+			return -ENETDOWN;		/* Someone zonked the iface */
 #endif	/* CONFIG_IPX_INTERN */
 
 		ret = ipx_bind(sock, (struct sockaddr *)&uaddr,
diff -u --recursive --new-file v2.2.0-pre7/linux/net/socket.c linux/net/socket.c
--- v2.2.0-pre7/linux/net/socket.c	Thu Jan  7 15:11:42 1999
+++ linux/net/socket.c	Sun Jan 17 10:00:00 1999
@@ -276,7 +276,7 @@
 
 	sock = socki_lookup(inode);
 
-	inode->i_mode = S_IFSOCK;
+	inode->i_mode = S_IFSOCK|S_IRWXUGO;
 	inode->i_sock = 1;
 	inode->i_uid = current->uid;
 	inode->i_gid = current->gid;
diff -u --recursive --new-file v2.2.0-pre7/linux/net/sunrpc/sched.c linux/net/sunrpc/sched.c
--- v2.2.0-pre7/linux/net/sunrpc/sched.c	Fri Jan  8 22:36:28 1999
+++ linux/net/sunrpc/sched.c	Thu Jan 14 22:56:06 1999
@@ -4,6 +4,9 @@
  * Scheduling for synchronous and asynchronous RPC requests.
  *
  * Copyright (C) 1996 Olaf Kirch, <okir@monad.swb.de>
+ * 
+ * TCP NFS related read + write fixes
+ * (C) 1999 Dave Airlie, University of Limerick, Ireland <airlied@linux.ie>
  */
 
 #include <linux/module.h>
@@ -271,8 +274,8 @@
 	if (task->tk_rpcwait != &schedq)
 		rpc_remove_wait_queue(task);
 	if (!RPC_IS_RUNNING(task)) {
-		rpc_make_runnable(task);
 		task->tk_flags |= RPC_TASK_CALLBACK;
+		rpc_make_runnable(task);
 	}
 	dprintk("RPC:      __rpc_wake_up done\n");
 }
@@ -391,10 +394,21 @@
 		 * Execute any pending callback.
 		 */
 		if (task->tk_flags & RPC_TASK_CALLBACK) {
+			/* Define a callback save pointer */
+			void (*save_callback)(struct rpc_task *);
+	
 			task->tk_flags &= ~RPC_TASK_CALLBACK;
+			/* 
+			 * If a callback exists, save it, reset it,
+			 * call it.
+			 * The save is needed to stop from resetting
+			 * another callback set within the callback handler
+			 * - Dave
+			 */
 			if (task->tk_callback) {
-				task->tk_callback(task);
-				task->tk_callback = NULL;
+				save_callback=task->tk_callback;
+				task->tk_callback=NULL;
+				save_callback(task);
 			}
 		}
 
@@ -828,11 +842,10 @@
 			rounds = 0;
 		}
 		save_flags(oldflags); cli();
+		dprintk("RPC: rpciod running checking dispatch\n");
+		rpciod_tcp_dispatcher();
+
 		if (!schedq.task) {
-			/* following two lines added by airlied@linux.ie
-				to make NFS over TCP work 5/1/99 */
-		        dprintk("RPC: rpciod running checking dispatch\n");
-		        rpciod_tcp_dispatcher();
 			dprintk("RPC: rpciod back to sleep\n");
 			interruptible_sleep_on(&rpciod_idle);
 			dprintk("RPC: switch to rpciod\n");
diff -u --recursive --new-file v2.2.0-pre7/linux/net/sunrpc/xprt.c linux/net/sunrpc/xprt.c
--- v2.2.0-pre7/linux/net/sunrpc/xprt.c	Fri Jan  8 22:36:28 1999
+++ linux/net/sunrpc/xprt.c	Thu Jan 14 22:56:06 1999
@@ -35,6 +35,8 @@
  *
  *  TCP callback races fixes (C) 1998 Red Hat Software <alan@redhat.com>
  *  TCP send fixes (C) 1998 Red Hat Software <alan@redhat.com>
+ *  TCP NFS related read + write fixes
+ *   (C) 1999 Dave Airlie, University of Limerick, Ireland <airlied@linux.ie>
  */
 
 #define __KERNEL_SYSCALLS__
@@ -272,9 +274,6 @@
 	set_fs(oldfs);
 #endif
 
-	if (!result && len)
-		result = -EAGAIN;
-
 	dprintk("RPC:      xprt_recvmsg(iov %p, len %d) = %d\n",
 						iov, len, result);
 	return result;
@@ -365,7 +364,7 @@
 	 *	TCP doesnt require the rpciod now - other things may
 	 *	but rpciod handles that not us.
 	 */
-	if(xprt->stream)
+	if(xprt->stream && !xprt->connecting)
 		rpciod_down();
 }
 
@@ -639,6 +638,11 @@
 		riov.iov_base = xprt->tcp_recm.data + offset;
 		riov.iov_len  = want;
 		result = xprt_recvmsg(xprt, &riov, 1, want);
+		if (!result)
+		{
+			dprintk("RPC: empty TCP record.\n");
+			return -ENOTCONN;
+		}
 		if (result < 0)
 			goto done;
 		offset += result;
@@ -684,6 +688,8 @@
 		dprintk("RPC: %4d TCP receiving %d bytes\n",
 					req->rq_task->tk_pid, want);
 		result = xprt_recvmsg(xprt, xprt->tcp_iovec, req->rq_rnr, want);
+		if (!result && want)
+			result = -EAGAIN;
 		if (result < 0)
 			goto done;
 		xprt->tcp_copied += result;
@@ -715,6 +721,8 @@
 		riov.iov_len  = want;
 		dprintk("RPC:      TCP skipping %d bytes\n", want);
 		result = xprt_recvmsg(xprt, &riov, 1, want);
+		if (!result && want)
+			result=-EAGAIN;
 		if (result < 0)
 			goto done;
 		offset += result;
@@ -871,14 +879,14 @@
 	if (!(xprt = xprt_from_sock(sk)))
 		return;
 	if(xprt->snd_sent && xprt->snd_task)
-		printk("write space\n");
+		dprintk("RPC: write space\n");
 	if(xprt->write_space == 0)
 	{
 		xprt->write_space = 1;
 		if (xprt->snd_task && !RPC_IS_RUNNING(xprt->snd_task))
 		{
 			if(xprt->snd_sent)
-				printk("Write wakeup snd_sent =%d\n",
+				dprintk("RPC: Write wakeup snd_sent =%d\n",
 					xprt->snd_sent);
 			rpc_wake_up_task(xprt->snd_task);			
 		}
@@ -945,8 +953,6 @@
 	struct rpc_xprt	*xprt = req->rq_xprt;
 	int status;
 
-	/*DEBUG*/int ac_debug=xprt->snd_sent;
-	
 	dprintk("RPC: %4d xprt_transmit(%x)\n", task->tk_pid, 
 				*(u32 *)(req->rq_svec[0].iov_base));
 
@@ -994,7 +1000,6 @@
 		xprt->snd_buf  = req->rq_snd_buf;
 		xprt->snd_task = task;
 		xprt->snd_sent = 0;
-		/*DEBUG*/ac_debug = 0;
 	}
 
 	/* For fast networks/servers we have to put the request on
@@ -1022,12 +1027,10 @@
 		if (xprt_transmit_some(xprt, task) != -EAGAIN) {
 			dprintk("RPC: %4d xmit complete\n", task->tk_pid);
 			xprt->snd_task = NULL;
-			if(ac_debug)
-				printk("Partial xmit finished\n");
 			return;
 		}
 
-		/*d*/printk("RPC: %4d xmit incomplete (%d left of %d)\n",
+		/*d*/dprintk("RPC: %4d xmit incomplete (%d left of %d)\n",
 				task->tk_pid, xprt->snd_buf.io_len,
 				req->rq_slen);
 		task->tk_status = 0;
diff -u --recursive --new-file v2.2.0-pre7/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.2.0-pre7/linux/net/unix/af_unix.c	Thu Jan  7 15:11:42 1999
+++ linux/net/unix/af_unix.c	Sun Jan 17 10:00:00 1999
@@ -8,7 +8,7 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  *
- * Version:	$Id: af_unix.c,v 1.72 1998/11/21 06:50:00 davem Exp $
+ * Version:	$Id: af_unix.c,v 1.73 1999/01/15 06:55:48 davem Exp $
  *
  * Fixes:
  *		Linus Torvalds	:	Assorted bug cures.
@@ -584,7 +584,7 @@
 	sk->protinfo.af_unix.addr = addr;
 	
 
-	dentry = do_mknod(sunaddr->sun_path, S_IFSOCK|S_IRWXUGO, 0);
+	dentry = do_mknod(sunaddr->sun_path, S_IFSOCK|sock->inode->i_mode, 0);
 	if (IS_ERR(dentry))
 	{
 		err = PTR_ERR(dentry);
diff -u --recursive --new-file v2.2.0-pre7/linux/scripts/header.tk linux/scripts/header.tk
--- v2.2.0-pre7/linux/scripts/header.tk	Fri Jan  8 22:36:28 1999
+++ linux/scripts/header.tk	Thu Jan 14 22:57:25 1999
@@ -1,3 +1,16 @@
+# FILE: header.tk
+# This file is boilerplate TCL/TK function definitions for 'make xconfig'.
+#
+# CHANGES
+# =======
+#
+# 8 January 1998, Michael Elizabeth Chastain, <mec@shout.net>
+# Remove unused do_cmd function (part of the 2.0 sound support).
+# Arrange buttons in three columns for better screen fitting.
+# Add CONSTANT_Y, CONSTANT_M, CONSTANT_N for commands like:
+#   dep_tristate 'foo' CONFIG_FOO m
+#
+
 #
 # This is a handy replacement for ".widget cget" that requires neither tk4
 # nor additional source code uglification.
@@ -20,9 +33,17 @@
 }
 
 #
+# Constant values used by certain dep_tristate commands.
+#
+set CONSTANT_Y 1
+set CONSTANT_M 2
+set CONSTANT_N 0
+
+#
 # Create a "reference" object to steal colors from.
 #
 button .ref
+
 #
 # On monochrome displays, -disabledforeground is blank by default; that's
 # bad.  Fill it with -foreground instead.
@@ -35,50 +56,23 @@
 #
 # Define some macros we will need to parse the config.in file.
 #
+
 proc mainmenu_name { text } {
-	message .header.message -width 400 -text "$text"
-	pack .header.message -side left -padx 15
 	wm title . "$text"
 }
 
 proc menu_option { w menu_num text } {
-	button .f0.x$menu_num -text "$text" -width 50 -command "$w .$w \"$text\""
-	pack .f0.x$menu_num -pady 0 -expand on
-}
-
-#
-# Not used at the moment, but this runs a command in a subprocess and
-# displays the result in a window with a scrollbar.
-#
-proc do_cmd { w command } {
-	catch {destroy $w}
-	toplevel $w -class Dialog
-	frame $w.tb
-	text $w.tb.text -relief raised -bd 2 -yscrollcommand "$w.tb.scroll set"
-	scrollbar $w.tb.scroll -command "$w.tb.text yview"
-	pack $w.tb.scroll -side right -fill y
-	pack $w.tb.text -side left
-
-	set oldFocus [focus]
-	frame $w.back
-	button $w.back.ok -text "OK" -width 20 \
-		-command "destroy $w; focus $oldFocus" -state disabled
-	button $w.back.ccl -text "Cancel" -width 20 \
-		-command "destroy $w; focus $oldFocus"
-	pack $w.tb -side top
-	pack $w.back.ok $w.back.ccl -side left
-	pack $w.back -side bottom -pady 10
-
-	focus $w
-	wm geometry $w +30+35
-
-	$w.tb.text delete 1.0 end
-	set f [open |$command]
-	while {![eof $f]} {
-		$w.tb.text insert end [read $f 256]
-	}
-	close $f
-	$w.back.ok configure -state normal
+	global menus_per_column
+	if { $menu_num <= $menus_per_column } then {
+	    set myframe left
+	} elseif { $menu_num <= [expr 2 * $menus_per_column] } then {
+	    set myframe middle
+	} else {
+	    set myframe right
+	} 
+	button .f0.x$menu_num -anchor w -text "$text" \
+	    -command "$w .$w \"$text\""
+	pack .f0.x$menu_num -pady 0 -side top -fill x -in .f0.$myframe
 }
 
 proc load_configfile { w title func } {
@@ -306,6 +300,10 @@
 		
 	option_name $w $mnum $line $text $variable
 		
+	global CONFIG_MODULES
+	if {($CONFIG_MODULES == 0)} then {
+		$w.x$line.m configure -state disabled
+	}
 	pack $w.x$line.n $w.x$line.m $w.x$line.y -side right -fill y
 }
 
@@ -453,7 +451,9 @@
 # Next set up the particulars for the top level menu, and define a few
 # buttons which we will stick down at the bottom.
 #
-frame .header
 
 frame .f0 
+frame .f0.left
+frame .f0.middle
+frame .f0.right
 
diff -u --recursive --new-file v2.2.0-pre7/linux/scripts/tail.tk linux/scripts/tail.tk
--- v2.2.0-pre7/linux/scripts/tail.tk	Wed Apr  1 20:11:55 1998
+++ linux/scripts/tail.tk	Fri Jan 15 14:36:20 1999
@@ -1,9 +1,12 @@
+# FILE: tail.tk
+# This file is boilerplate TCL/TK function definitions for 'make xconfig'.
+#
+# CHANGES
+# =======
+#
+# 8 January 1998, Michael Elizabeth Chastain, <mec@shout.net>
+# Arrange buttons in three columns for better screen fitting.
 #
-# Misc buttons to save/restore state and so forth.
-# 
-frame .f0_bot
-frame .f0_bot.r
-frame .f0_bot.l
 
 #
 # Read the user's settings from .config.  These will override whatever is
@@ -28,53 +31,47 @@
 
 update_mainmenu .f0
 
-button .f0_bot.r.save -text "Save and Exit" -width 25 -command {
-	writeconfig .config include/linux/autoconf.h; wrapup .wrap }
+button .f0.right.save -anchor w -text "Save and Exit" \
+    -command { writeconfig .config include/linux/autoconf.h; wrapup .wrap }
 
-button .f0_bot.r.quit -text "Quit Without Saving" -width 25 \
-	-command { maybe_exit .maybe }
+button .f0.right.quit -anchor w -text "Quit Without Saving" \
+    -command { maybe_exit .maybe }
 
-button .f0_bot.l.store -text "Store Configuration to File" -width 25 -command {
-	load_configfile .load "Save Configuration in file" write_config_file
+button .f0.right.load -anchor w -text "Load Configuration from File" \
+    -command { load_configfile .load "Load Configuration from file" read_config_file
 }
 
-button .f0_bot.l.load -text "Load Configuration from File" -width 25 -command {
-	load_configfile .load "Load Configuration from file" read_config_file
-}
+button .f0.right.store -anchor w -text "Store Configuration to File" \
+    -command { load_configfile .load "Store Configuration to file" write_config_file }
 
 #
-# Now pack everything, important things first because of small screens.
+# Now pack everything.
 #
-pack  .f0_bot.r.save .f0_bot.r.quit -padx 25 -ipadx 10 -expand on
-pack  .f0_bot.l.load .f0_bot.l.store -padx 25 -ipadx 10 -expand on
-
-pack .f0_bot.r -side left -padx 15 -expand on -fill y
-pack .f0_bot.l -side right -padx 15 -expand on -fill y
-
-pack .f0_bot -side bottom -fill both -expand on -pady 4
-pack .f0 -side bottom -padx 15 -pady 0 -fill y -expand on
-pack .header -padx 10 -pady 7 -expand on
 
+pack .f0.right.store .f0.right.load .f0.right.quit .f0.right.save \
+    -padx 0 -pady 0 -side bottom -fill x
+pack .f0.left .f0.middle .f0.right -side left -padx 5 -pady 0 -fill y
+pack .f0 -padx 5 -pady 5
 
 #
 # If we cannot write our config files, disable the write button.
 #
 if { [file exists .config] == 1 } then {
 		if { [file writable .config] == 0 } then {
-			.f0_bot.r.save configure -state disabled
+			.f0.right.save configure -state disabled
 		}
 	} else {
 		if { [file writable .] == 0 } then {
-			.f0_bot.r.save configure -state disabled
+			.f0.right.save configure -state disabled
 		}
 	}
 
 if { [file exists include/linux/autoconf.h] == 1 } then {
 		if { [file writable include/linux/autoconf.h] == 0 } then {
-			.f0_bot.r.save configure -state disabled
+			.f0.right.save configure -state disabled
 		}
 	} else {
 		if { [file writable include/linux/] == 0 } then {
-			.f0_bot.r.save configure -state disabled
+			.f0.right.save configure -state disabled
 		}
 	}
diff -u --recursive --new-file v2.2.0-pre7/linux/scripts/tkgen.c linux/scripts/tkgen.c
--- v2.2.0-pre7/linux/scripts/tkgen.c	Wed Jan 13 15:00:45 1999
+++ linux/scripts/tkgen.c	Fri Jan 15 14:36:20 1999
@@ -74,6 +74,14 @@
  * - Call clear_globalflags when writing out update_mainmenu.
  *   This fixes the missing global/vfix lines for ARCH=alpha on 2.2.0-pre4.
  *
+ * 8 January 1999, Michael Elizabeth Chastain <mec@shout.net>
+ * - Emit menus_per_column
+ *
+ * 1999 01 04
+ * Michael Elizabeth Chastain <mec@shout.net>
+ * - Call clear_globalflags when writing out update_mainmenu.
+ *   This fixes the missing global/vfix lines for ARCH=alpha on 2.2.0-pre4.
+ *
  * TO DO:
  *   - clean up - there are useless ifdef's everywhere.
  *   - better comments throughout - C code generating tcl is really cryptic.
@@ -751,8 +759,13 @@
 
   /*
    * Record this so we can set up the prev/next buttons correctly.
+   * Menus per column computation has extra button space as follows:
+   *   4 for the save/quit/load/store buttons,
+   *   1 for the blank space above save/quit/load/store
+   *   2 to make the rounding work
    */
   tot_menu_num = menu_num;
+  printf( "set menus_per_column %d\n\n", (tot_menu_num + 4 + 1 + 2) / 3 );
 
   /*
    * Now start generating the actual wish script that we will use.
diff -u --recursive --new-file v2.2.0-pre7/linux/scripts/tkparse.c linux/scripts/tkparse.c
--- v2.2.0-pre7/linux/scripts/tkparse.c	Thu Feb 12 20:56:15 1998
+++ linux/scripts/tkparse.c	Fri Jan 15 14:36:20 1999
@@ -17,7 +17,18 @@
  * and build the actual wish script.
  *
  * This file contains the code to do the first parse of config.in.
+ *
+ * Change History
+ *
+ * 7 January 1999, Michael Elizabeth Chastain, <mailto:mec@shout.net>
+ * Teach dep_tristate about a few literals, such as:
+ *   dep_tristate 'foo' CONFIG_FOO m
+ * Also have it print an error message and exit on some parse failures.
+ *
+ * 14 January 1999, Michael Elizabeth Chastain, <mailto:mec@shout.net>
+ * Don't fclose stdin.  Thanks to Tony Hoyle for nailing this one.
  */
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -467,8 +478,20 @@
       pnt = get_qstring(pnt, &kcfg->label);
       pnt = get_string(pnt, &kcfg->optionname);
       pnt = skip_whitespace(pnt);
-      if( *pnt == '$') pnt++;
-      pnt = get_string(pnt, &kcfg->depend.str);
+
+      if ( ( pnt[0] == 'y'  || pnt[0] == 'm' || pnt[0] == 'n'  )
+      &&   ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) ) {
+	if      ( pnt[0] == 'y' ) kcfg->depend.str = strdup( "CONSTANT_Y" );
+	else if ( pnt[0] == 'm' ) kcfg->depend.str = strdup( "CONSTANT_M" );
+	else                      kcfg->depend.str = strdup( "CONSTANT_N" );
+	pnt++;
+      } else if ( *pnt == '$' ) {
+	pnt++;
+	pnt = get_string(pnt, &kcfg->depend.str);
+      } else {
+	fprintf( stderr, "Can't handle dep_tristate condition\n" );
+	exit( 1 );
+      }
 
       /*
        * Create a conditional for this object's dependency.
@@ -629,8 +652,8 @@
 	  offset = 0;
 	}
     }
-  fclose(infile);
   if( infile != stdin ) {
+    fclose(infile);
     current_file = old_file;
   }
   lineno = old_lineno;
diff -u --recursive --new-file v2.2.0-pre7/linux/scripts/tkparse.h linux/scripts/tkparse.h
--- v2.2.0-pre7/linux/scripts/tkparse.h	Tue Dec 23 13:52:02 1997
+++ linux/scripts/tkparse.h	Fri Jan 15 14:36:20 1999
@@ -58,10 +58,10 @@
   struct kconfig 	* next;
   int 			  flags;
   enum 			  token tok;
-  char   		  menu_number;
-  char   		  menu_line;
-  char   		  submenu_start;
-  char   		  submenu_end;
+  int   		  menu_number;
+  int   		  menu_line;
+  int   		  submenu_start;
+  int   		  submenu_end;
   char 			* optionname;
   char 			* label;
   char 			* value;
diff -u --recursive --new-file v2.2.0-pre7/linux/scripts/ver_linux linux/scripts/ver_linux
--- v2.2.0-pre7/linux/scripts/ver_linux	Fri Jan  8 22:36:28 1999
+++ linux/scripts/ver_linux	Sun Jan 17 18:28:06 1999
@@ -2,7 +2,7 @@
 # Before running this script please ensure that your PATH is
 # typical as you use for compilation/istallation. I use
 # /bin /sbin /usr/bin /usr/sbin /usr/local/bin, but it may
-# differs on your system.
+# differ on your system.
 #
 echo '-- Versions installed: (if some fields are empty or looks'
 echo '-- unusual then possibly you have very old versions)'
@@ -25,5 +25,8 @@
 'NR==1{if ($5 != "") { n=split($5,buf,"-"); ver=buf[n]; done=1 }}
  NR==2{if (done != 1) ver=$3 }
  END{print "Net-tools             ",ver}'
-loadkeys -h 2>&1 | awk 'NR==1{print "Kbd                   ",$3}'
+loadkeys -h 2>&1 | awk \
+'(NR==1 && $3) {ver=$3}
+ (NR==2 && $1 ~ /console-tools/) {print "Console-tools         ",$3; done=1}
+ END {if (!done) print "Kbd                   ",ver}'
 expr --v | awk '{print "Sh-utils              ", $NF}'
