diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/CREDITS linux.15p1/CREDITS
--- linux.vanilla/CREDITS	Tue Jan  4 21:18:37 2000
+++ linux.15p1/CREDITS	Wed Jan 12 01:35:36 2000
@@ -253,9 +253,9 @@
 D: Configuration help text support
 D: Linux CD and Support Giveaway List
 
-N: Zoltan Boszormenyi
-E: zboszor@mol.hu
-D: MTRR emulation with Cyrix style ARR registers
+N: Zoltán Böszörményi
+E: zboszor@mail.externet.hu
+D: MTRR emulation with Cyrix style ARR registers, Athlon MTRR support
 
 N: John Boyd
 E: boyd@cis.ohio-state.edu
@@ -2263,6 +2263,13 @@
 D: Xiafs filesystem [defunct]
 S: 542 West 112th Street, 5N
 S: New York, New York 10025
+S: USA
+
+N: Victor Yodaiken
+E: yodaiken@fsmlabs.com
+D: RTLinux (RealTime Linux)
+S: POB 1822 
+S: Socorro NM, 87801
 S: USA
 
 N: Eric Youngdale
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/Documentation/Configure.help linux.15p1/Documentation/Configure.help
--- linux.vanilla/Documentation/Configure.help	Tue Jan  4 21:18:37 2000
+++ linux.15p1/Documentation/Configure.help	Wed Jan 12 01:31:00 2000
@@ -2573,7 +2573,7 @@
 
   You will need the user space program "ipmasqadm" to use these
   additional modules; you can download it from
-  http://juanjox.linuxhq.com/
+  http://juanjox.kernelnotes.org/
 
   All this additional code is still under development and so is
   currently marked EXPERIMENTAL.
@@ -2588,7 +2588,7 @@
   ftp://ftp.netis.com/pub/members/rlynch/
 
   You will also need the ipmasqadm tool available from
-  http://juanjox.linuxhq.com/ .
+  http://juanjox.kernelnotes.org/ .
 
   The ipautofw code is still under development and so is currently
   marked EXPERIMENTAL. If you want to try it, say Y.
@@ -2619,7 +2619,7 @@
   see ftp://ftp.compsoc.net/users/steve/ipportfw/linux21/
 
   You will need the user space program "ipmasqadm" which can be
-  downloaded from http://juanjox.linuxhq.com/
+  downloaded from http://juanjox.kernelnotes.org/
 
   The portfw code is still under development and so is currently
   marked EXPERIMENTAL. If you want to try it, say Y.
@@ -8976,9 +8976,12 @@
 
   The AMD K6-2 (stepping 8 and above) and K6-3 processors have two
   MTRRs. These are supported.
-  
-  The Centaur C6 (WinChip) has 8 MCRs, allowing write-combining. These
-  are supported.
+
+  The Centaur C6 (WinChip) and WinChip 2&3 processors have 8 MCRs. 
+  These are supported. Note that, due to the design of the WinChip 2&3, 
+  setting the access for normal memory to uncachable or write-combine
+  on these processors will result in instant kernel panic. It is okay 
+  to set this for non-cacheable (video) memory.
 
   Saying Y here also fixes a problem with buggy SMP BIOSes which only
   set the MTRRs for the boot CPU and not the secondary CPUs. This can
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/Documentation/networking/dmfe.txt linux.15p1/Documentation/networking/dmfe.txt
--- linux.vanilla/Documentation/networking/dmfe.txt	Thu Jan  1 01:00:00 1970
+++ linux.15p1/Documentation/networking/dmfe.txt	Wed Jan 12 18:01:03 2000
@@ -0,0 +1,55 @@
+  dmfe.c: Version 1.27C        01/07/2000
+
+        A Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux. 
+        Copyright (C) 1997  Sten Wang
+
+        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.
+
+
+  A. Compiler command:
+          "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall 
+           -Wstrict-prototypes -O6 -c dmfe.c"
+          OR
+          "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net -Wall 
+           -Wstrict-prototypes -O6 -c dmfe.c"
+
+
+  B. The following steps teach you how to active DM9102 board:
+
+        1. Used the upper compiler command to compile dmfe.c
+
+        2. Insert dmfe module into kernel
+           "insmod dmfe"        ;;Auto Detection Mode (Suggest)
+           "insmod dmfe mode=0" ;;Force 10M Half Duplex
+           "insmod dmfe mode=1" ;;Force 100M Half Duplex
+           "insmod dmfe mode=4" ;;Force 10M Full Duplex
+           "insmod dmfe mode=5" ;;Force 100M Full Duplex
+
+        3. Config a dm9102 network interface
+           "ifconfig eth0 172.22.3.18"
+                          ^^^^^^^^^^^ Your IP address
+
+        4. Active the IP routing table. For some distributions, it is not
+           necessary. You can type "route" to check.
+
+           "route add -net 172.22.3.0 eth0"
+
+
+        5. Well done. Your DM9102 adapter actived now.
+
+
+   C. Object files description:
+
+        If you can make sure your kernel version, you can rename
+        to dmfe.o and directly use it without re-compiling.
+
+
+  Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/Documentation/scsi-generic.txt linux.15p1/Documentation/scsi-generic.txt
--- linux.vanilla/Documentation/scsi-generic.txt	Sat Aug 14 02:26:45 1999
+++ linux.15p1/Documentation/scsi-generic.txt	Wed Jan 12 01:27:40 2000
@@ -1,17 +1,18 @@
-            Notes on Linux's SG driver version 2.1.34
+            Notes on Linux's SG driver version 2.1.36
             -----------------------------------------
-                                                        990606
+                                                        20000110
 
 Introduction
 ============
-Sg is one of the four "high level" SCSI device drivers along with
-sd, st and sr (disk, tape and CDROM respectively). Sg is more generalized
-(but lower level) than its siblings and tends to be used on SCSI devices
-that don't fit into the already serviced categories. Thus sg is used for
-scanners, cd writers and reading audio cds digitally amongst other things.
+The SCSI Generic driver (sg) is one of the four "high level" SCSI device
+drivers along with sd, st and sr (disk, tape and CDROM respectively). Sg
+is more generalized (but lower level) than its siblings and tends to be
+used on SCSI devices that don't fit into the already serviced categories.
+Thus sg is used for scanners, cd writers and reading audio cds digitally
+amongst other things.
 
 These are notes on the Linux SCSI generic packet device driver (sg)
-describing version 2.1.34 . The original driver was written by Lawrence
+describing version 2.1.36 . The original driver was written by Lawrence
 Foard and remained in place with minimal changes since circa 1992.
 Version 2 of this driver remains backward compatible (binary and
 source **) with the original. It adds scatter gather, command queuing,
@@ -27,12 +28,12 @@
 of the document is version 1.5 dated 7th May 1996. It can found at
 ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO-SCSI-Programming-HOWTO .
 A copy of this document can be found at:
-http://www.torque.net/sg/p/original/HOWTO-SCSI-Programming-HOWTO .
+http://www.torque.net/sg/p/original/HOWTO-SCSI-Programming-HOWTO.txt .
 
 ** It is possible to write applications that perform differently
 depending on whether they are using the original or this version of
-the sg device driver. The author is not aware of any useful 
-pre-existing applications that have problems with version 2 (yet).
+the sg device driver. The author is not aware of any useful
+pre-existing applications that have problems with version 2.
 
 
 Architecture
@@ -52,12 +53,12 @@
 system calls of open(), close(), read(), write() and ioctl(). Two other
 related system calls: poll() and fcntl() are added to this list and
 how they interact with the sg device driver is documented later.
- 
-An SG device is accessed by write()ing SCSI commands plus any associated 
+
+An SG device is accessed by write()ing SCSI commands plus any associated
 outgoing data to it; the resulting status codes and any incoming data are
 then obtained by a read() call. The device can be opened O_NONBLOCK
 (non-blocking) and poll() used to monitor its progress. The device may be
-opened O_EXCL which excludes other "sg" users from this device (but not 
+opened O_EXCL which excludes other "sg" users from this device (but not
 "sd", "st" or "sr" users). The buffer given to the write() call is made
 up as follows:
         - struct sg_header image (see below)
@@ -71,6 +72,12 @@
 The given SCSI command has its LUN field overwritten by the LUN value of
 the associated sg device that has been open()ed.
 
+SCSI commands are only attempted once (i.e. there are no internal
+retries). If appropriate (e.g. a SCSI READ) the data buffer is copied back
+to user space irrespective of the values of the various SCSI related
+error/status codes. [Some adapters that use an old error interface in
+the SCSI mid level ignore the retry count and retry certain errors.]
+
 
 sg_header
 =========
@@ -96,11 +103,11 @@
 input variable, it is not read by sg internally (only written).
 
 The 'reply_len' is the length of the data the corresponding read()
-will/should request (including the sg_header). 
+will/should request (including the sg_header).
 
 The 'pack_id' is not acted upon by the sg device driver but is conveyed
 back to the corresponding read() so it can be used for sequencing by an
-application. 
+application.
 
 The 'result' is also bizarre, turning certain types of host codes to 0 (no
 error), EBUSY or EIO. With better error reporting now available, the
@@ -138,7 +145,7 @@
     unsigned int driver_status:8;   /* [o] driver status+suggestion */
     unsigned int other_flags:10;    /* unused */
     unsigned char sense_buffer[SG_MAX_SENSE]; /* [o] Output in 3 cases:
-           when target_status is CHECK_CONDITION or 
+           when target_status is CHECK_CONDITION or
            when target_status is COMMAND_TERMINATED or
            when (driver_status & DRIVER_SENSE) is true. */
 };      /* This structure is 36 bytes long on i386 */
@@ -164,7 +171,7 @@
 values are (found in <scsi/scsi.h>):
 /* N.B. 1 bit offset from usual SCSI status values */
 #define GOOD                 0x00
-#define CHECK_CONDITION      0x01  
+#define CHECK_CONDITION      0x01
 #define CONDITION_GOOD       0x02
 #define BUSY                 0x04
 #define INTERMEDIATE_GOOD    0x08
@@ -175,7 +182,7 @@
 When the 'target_status' is CHECK_CONDITION or COMMAND_TERMINATED the
 'sense_buffer' is output. Note that when (driver_status & DRIVER_SENSE)
 is true then the 'sense_buffer' is also output (this seems to occur when
-the ide-scsi emulation is used). When the 'sense_buffer' is output the 
+the ide-scsi emulation is used). When the 'sense_buffer' is output the
 SCSI Sense Key can be found at (sense_buffer[2] & 0x0f) .
 
 The 'host_status' field is always output and has the following values
@@ -269,12 +276,12 @@
 Returns a file descriptor if >= 0 , otherwise -1 implies an error.
 
 Error codes (value in 'errno' after -1 returned):
-EACCES          Either the user doesn't have appropriate permissions on 
+EACCES          Either the user doesn't have appropriate permissions on
                 'filename' or attempted to use both O_RDONLY and O_EXCL
 EBUSY           O_NONBLOCK set and some user of this sg device has O_EXCL
                 set while someone is already using this device
 EINTR           while waiting for an "exclusive" lock to clear, a signal
-                is received, just try again ...        
+                is received, just try again ...
 ENODEV          sg not compiled into kernel or the kernel cannot find the
                 sg module (or it can't initialize itself (low memory??))
 ENOENT          given filename not found
@@ -320,12 +327,13 @@
                 This is more likely to happen when queuing commands,
                 so wait a bit (eg usleep(10000) ) before trying again
 EDOM            a) command queuing off: a packet is already queued
-                b) command queuing on: too many packets queued 
+                b) command queuing on: too many packets queued
                    (SG_MAX_QUEUE exceeded)
-                c) SCSI command length given in SG_NEXT_CMD_LEN too long
 EFAULT          'buffer' for 'count' bytes is an invalid memory range
-EIO             incoming buffer too short. It should be at least (6 +
-                sizeof(struct sg_header))==42 bytes long
+EIO             a) incoming buffer too short. It should be at least
+                   (6 + sizeof(struct sg_header))==42 bytes long
+                b) SCSI command length given in SG_NEXT_CMD_LEN too long
+                c) reply_len negative
 ENOMEM          can't get memory for DMA. Take evasive action ...
 ENXIO           either scsi sub-system is currently processing some error
                 (eg doing a device reset) or the sg driver/module removed
@@ -334,7 +342,7 @@
 
 read(int sg_fd, void * buffer, size_t count)
 --------------------------------------------
-Read() is used to receive a packet containing 1 mandatory part and 1 
+Read() is used to receive a packet containing 1 mandatory part and 1
 optional part. The mandatory part is:
   - a control block (an instance of struct sg_header)
 The optional part is:
@@ -343,7 +351,7 @@
 sufficient to accommodate this packet to avoid truncation. Truncation occurs
 if count < sg_header::replylen .
 
-By default, read() will return the oldest packet queued up. If the 
+By default, read() will return the oldest packet queued up. If the
 SG_SET_FORCE_PACK_ID,1 ioctl() is active then read() will attempt to
 fetch the packet whose pack_id (given earlier to write()) matches the
 sg_header::pack_id given to this read(). If not available it will either
@@ -356,12 +364,16 @@
 DMA-ed by the SCSI device. This driver is currently unable to provide
 such an underrun indication.
 
+If the SCSI device reports an error then a REQUEST SENSE is automatically
+done and the output is placed in the sense_buffer array which is in the
+control block. This action is sometimes called "auto-sense".
+
 Error codes (value in 'errno' after -1 returned):
 EAGAIN          either no waiting packet or requested packet is not
                 available while O_NONBLOCK flag was set
 EFAULT          'buffer' for 'count' bytes is an invalid memory range
 EINTR           while waiting for a packet, a signal is received, just
-                try again ...        
+                try again ...
 EIO             if the 'count' given to read() is < sizeof(struct sg_header)
                 and the 'result' element in sg_header is non-zero. Not a
                 recommended error reporting technique
@@ -380,11 +392,11 @@
 flight".
 
 A process that has an open file descriptor to an sg device may be aborted
-(eg by a kill signal). In this case, the kernel automatically calls close 
+(eg by a kill signal). In this case, the kernel automatically calls close
 (which is called 'sg_release()' in the version 2 driver) to facilitate
 the cleanup mentioned above.
 
-A problem persists in version 2.1.34 if the sg driver is a module and is
+A problem persists in version 2.1.36 if the sg driver is a module and is
 removed while packets are still "in flight".
 
 Returns 0 if successful, otherwise -1 implies an error.
@@ -394,7 +406,7 @@
 
 ioctl(int sg_fd, int command, ...)  [sg specific]
 -------------------------------------------------
-Ken Thompson (or perhaps some other Unix luminary) described ioctl() as 
+Ken Thompson (or perhaps some other Unix luminary) described ioctl() as
 the "garbage bin of Unix". This driver compounds the situation by adding
 more ...
 If a ioctl command is not recognized by sg (and the various lower levels
@@ -417,7 +429,8 @@
 for this file descriptor. The unit is a "jiffy". Packets that are
 already "in flight" will not be affected. The default value is set
 on open() and is SG_DEFAULT_TIMEOUT (defined in sg.h). This default is
-currently 1 minute and may not be long enough for formats.
+currently 1 minute and may not be long enough for formats. Negative
+values will yield an EIO error.
 
 SG_EMULATED_HOST:
 Assumes 3rd argument points to an int and outputs a flag indicating
@@ -426,33 +439,31 @@
 while 0 is not.
 
 SG_SET_TRANSFORM  W:
-Third argument is ignored. Only is meaningful when SG_EMULATED host has
-yielded 1 (ie the low-level is the ide-scsi device driver); otherwise
-an EINVAL error occurs. The default state is to _not_ transform SCSI
-commands to the corresponding ATAPI commands but pass them straight
-through as is. [Only certain classes of SCSI commands need to be
-transformed to their ATAPI equivalents.] Making this ioctl command causes
-transforms to occur thereafter. Subsequent calls to this ioctl command
-have no additional effect. Beware, this state will affect all devices
-(and hence all related sg file descriptors) associated with this ide-scsi
-"bus".
-The author of ide-scsi has pointed out that this is not the intended
-behaviour which is a 3rd argument of 0 to disable transforms and 1 to
-enable transforms. Note the 3rd argument is an 'int' not a 'int *'.
-Perhaps the intended behaviour will be implemented soon.
+Only is meaningful when SG_EMULATED host has yielded 1 (i.e. the low-level
+is the ide-scsi device driver); otherwise an EINVAL error occurs. The
+default state is to _not_ transform SCSI commands to the corresponding
+ATAPI commands but pass them straight through as is. [Only certain classes
+of SCSI commands need to be transformed to their ATAPI equivalents.]
+The third argument is interpreted as an integer. When it is non-zero then
+a flag is set inside the ide-scsi driver that transforms subsequent
+commands sent to this driver. When zero is passed as the 3rd argument to
+this ioctl then the flag within the ide-scsi driver is cleared and
+subsequent commands are not transformed. Beware, this state will affect
+all devices (and hence all related sg file descriptors) associated with
+this ide-scsi "bus".
 
 SG_GET_TRANSFORM:
 Third argument is ignored. Only is meaningful when SG_EMULATED host has
 yielded 1 (ie the low-level is the ide-scsi device driver); otherwise
 an EINVAL error occurs. Returns 0 to indicate _not_ transforming SCSI
-to ATAPI commands (default). Returns 1 when it is transforming.
+to ATAPI commands (default). Returns 1 when it is transforming them.
 
 SG_SET_FORCE_LOW_DMA +:
 Assumes 3rd argument points to an int containing 0 or 1. 0 (default)
 means sg decides whether to use memory above 16 Mbyte level (on i386)
 based on the host adapter being used by this SCSI device. Typically
 PCI SCSI adapters will indicate they can DMA to the whole 32 bit address
-space. 
+space.
 If 1 is given then the host adapter is overridden and only memory below
 the 16MB level is used for DMA. A requirement for this should be
 extremely rare. If the "reserved" buffer allocated on open() is not in
@@ -469,15 +480,21 @@
 
 SG_GET_SCSI_ID +:
 Assumes 3rd argument is pointing to an object of type Sg_scsi_id (see
-sg.h) and populates it. That structure contains ints for host_no, 
-channel, scsi_id, lun and scsi_type. Most of this information is 
-available from other sources (eg SCSI_IOCTL_GET_IDLUN and 
-SCSI_IOCTL_GET_BUS_NUMBER) but tends to be awkward to collect.
+sg.h) and populates it. That structure contains ints for host_no,
+channel, scsi_id, lun, scsi_type, allowable commands per lun and
+queue_depth. Most of this information is available from other sources
+(eg SCSI_IOCTL_GET_IDLUN and SCSI_IOCTL_GET_BUS_NUMBER) but tends to be
+awkward to collect.
+Allowable commands per lun and queue_depth give an insight to the
+command queuing capabilities of the adapters and the device. The latter
+overrides the former (logically) and the former is only of interest
+if it is equal to queue_depth which probably indicates the device
+does not support queueing commands (e.g. most scanners).
 
 SG_SET_FORCE_PACK_ID +:
 Assumes 3rd argument is pointing to an int. 0 (default) instructs read()
 to return the oldest (written) packet if multiple packets are
-waiting to be read (when command queuing is being used). 
+waiting to be read (when command queuing is being used).
 1 instructs read() to view the sg_header::pack_id as input and return the
 oldest packet matching that pack_id or wait until it arrives (or yield
 EAGAIN if O_NONBLOCK is in force). As a special case the pack_id of -1
@@ -524,7 +541,7 @@
 device then an EBUSY error occurs. Per device sequencing was the original
 semantics and allowed, for example different processes to "share" the
 device, one perhaps write()ing with the other one read()ing. This command
-is supplied if anyone needs those semantics. Per file descriptor 
+is supplied if anyone needs those semantics. Per file descriptor
 sequencing, perhaps with the use of the O_EXCL flag, seems more sensible.
 
 SG_GET_MERGE_FD +:
@@ -548,7 +565,7 @@
 SG_SET_UNDERRUN_FLAG +:
 Assumes 3rd argument is pointing to an int. 0 (current default, set by
 SG_DEF_UNDERRUN_FLAG in sg.h) requests underruns be ignored. 1 requests
-that underruns be flagged. [The only low level driver that acts on this 
+that underruns be flagged. [The only low level driver that acts on this
 at the moment is the aic7xxx which yields a DID_ERROR error on underrun.]
 Only the current file descriptor is affected by this command (unless
 "per device" sequencing has been selected).
@@ -571,7 +588,13 @@
 
 SG_GET_VERSION_NUM +:
 Assumes 3rd argument points to an int. The version number is then placed
-in that int. A sg version such as 2.1.34 will yield "20134" from this ioctl.
+in that int. A sg version such as 2.1.36 will yield "20136" from this ioctl.
+
+SG_SCSI_RESET +:
+Assumes 3rd argument points to an int. Unfortunately doesn't currently
+do much (may in the future after other issues are resolved). Yields an
+EBUSY error if the SCSI bus or the associated device is being reset
+when this ioctl() is called, otherwise returns 0.
 
 SG_SET_DEBUG +:
 Assumes 3rd argument is pointing to an int. 0 (default) turns debugging
@@ -641,13 +664,16 @@
 
 Utility and Test Programs
 =========================
-See the README file in the sg_utils<date>.tgz tarball. At the time of
-writing this was sg_utils990527.tgz .
+See the README file in the sg_utils<date>.tgz tarball. Look on the
+http://www.torque.net/sg website for the latest version.
 
 Briefly, that tarball contains the following utilities:
 sg_dd512        'dd' like program that assumes 512 byte blocks size
 sg_dd2048       'dd' like program that assumes 2048 byte blocks size
+sg_dd2352       'dd' like program that assumes 2352 byte blocks size
 sgq_dd512       like 'sg_dd512' but does command queuing on "if"
+sgp_dd          probably the most flexible 'dd' variant. It uses POSIX
+                threads, block size set by "bs=..." plus other options.
 sg_scan         outputs information (optionally Inquiry) on SCSI devices
 sg_rbuf         tests SCSI bus transfer speed (without physical IO)
 sg_whoami       outputs info (optionally capacity) of given SCSI device
@@ -657,6 +683,9 @@
 It also contains the following test programs:
 sg_debug        outputs sg driver state to console/log file
 sg_poll         tests asynchronous notification
+sg_runt_ex      example run time selection program for application authors
+sg_simple1      example program first time users
+sg_simple2      like sg_simple1 but with more primitive error processing
 sg_inquiry      does a SCSI Inquiry command (from original HOWTO)
 sg_tst_med      checks presence of media (from original HOWTO)
 
@@ -664,36 +693,40 @@
 SCSI 2 errors and warnings. This code is used by most of the above
 utility and test programs.
 
-The following programs: sg_dd512, sg_dd2048, sg_scan, sg_rbuf, sg_tst_med, 
-sg_inquiry and sginfo, can be compiled either for this new sg driver _or_
-the original sg driver.
+The following programs: sg_dd512, sg_dd2048, sg_dd2352, sg_scan, sg_runt_ex,
+sg_rbuf, sg_tst_med, sg_inquiry and sginfo, can be compiled either for this
+new sg driver _or_ the original sg driver (in 2.0 or 2.2 series kernels).
+sg_runt_ex can be run on 2.0, 2.2 or 2.3 series kernels even if it is
+compiled on a different series (eg compiled on 2.0, run on 2.2).
 
 
 Header files
 ============
 User applications need to find the correct "sg.h" header file matching
-their kernel in order to write code using the sg device driver. This is 
+their kernel in order to write code using the sg device driver. This is
 sometimes more difficult than it should be. The correct "sg.h" will usually
-be found at /usr/src/linux/include/scsi/sg.h . Another important header 
+be found at /usr/src/linux/include/scsi/sg.h . Another important header
 file is "scsi.h" which will be in the same directory.
 
-Several distributions have taken their own copies of these files and placed
-them in /usr/include/scsi which is where "#include <scsi/sg.h>" would go
-looking. The directory /usr/include/scsi _should_ be a symbolic link to
-/usr/src/linux/include/scsi/ . It was is Redhat 5.1 and 5.2 but it is
-not is Redhat 6.0 . Some other distributions have the same problem. To
-solve this (as root) do the following:
-
-# cd /usr/include
-# mv scsi scsi_orig
-# ln -s ../src/linux/include/scsi scsi
-
-This doesn't seem to be a problem with /usr/include/linux (at least in
-Redhat where it is a symbolic link) so it is hard to understand why
-/usr/include/scsi is defined the way it is. The fact the
-/usr/include/linux is a symbolic link opens up the following solution
-proposed by the author of cdparanoia (Monty):
-#include <linux/../scsi/sg.h>
+When "#include <scsi/sg.h>" is written in an application then this refers
+to the file /usr/include/scsi/sg.h . A problem sometimes arises because
+the files in the /usr/include/scsi directory are controlled by the GNU
+library people who maintain glibc. Unfortunately these 2 versions of
+the sg.h header file are not always in sync. [This was the case in Redhat
+6.0 and 6.1 .] Glibc 2.1.3 and later versions should get this right.
+
+If this is a problem, the user may need to copy sg.h (and scsi.h) from
+the kernel source includes to /usr/include scsi. If the user can change
+the effected source code then another approach is to rely on the fact that
+/usr/src/linux is a symbolic link to /usr/src/linux/include/linux and
+change the sg.h include to look like:
+    #include <linux/../scsi/sg.h>
+This solution is used by the author of cdparanoia (Monty) in his application.
+
+[Former scsi generic documents suggested adding a symbolic link to
+bypass this problem but that is not popular with the glibc maintainers.
+I would like to thank Andreas Jaeger <aj@suse.de> for his contributions
+on this subject.]
 
 
 Extra information in scsi-generic_long.txt
@@ -709,6 +742,27 @@
    - shortcomings
    - future directions
    - an appendix with some SCSI 2 information in it
+
+
+References
+==========
+http://www.t10.org      Very important site for SCSI related information.
+                        Contains SCSI 2 and 3 draft standards.
+http://www.andante.org/scsi.html
+                        This is Eric Youngdale's site. Eric is primarily
+                        responsible for the Linux SCSI architecture and
+                        its mid-level implementation.
+http://www.kernel.dk    Jens Axboe's site for Linux cdrom matters including
+                        the SCSI "sr" driver.
+http://www.torque.net/sg
+                        My site with sg related information.
+newsgroup:linux-scsi@vger.rutgers.edu
+                        Newsgroup for Linux related SCSI matters
+/usr/src/linux/MAINTAINERS
+                        This is a file in the Linux kernel source that
+                        contains up to date information about who maintains
+                        what and where information can be found. Links to
+                        SCSI adapter information are also here.
 
 
 Conclusion
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/MAINTAINERS linux.15p1/MAINTAINERS
--- linux.vanilla/MAINTAINERS	Tue Jan  4 21:18:38 2000
+++ linux.15p1/MAINTAINERS	Wed Jan 12 01:34:23 2000
@@ -712,6 +712,13 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
+RTLINUX  REALTIME  LINUX
+P:	Victor Yodaiken
+M:	yodaiken@fsmlabs.com
+L:	rtl@rtlinux.org
+W:	www.rtlinux.org
+S:	Maintained
+
 SBPCD CDROM DRIVER
 P:	Eberhard Moenkeberg
 M:	emoenke@gwdg.de
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/boot/compressed/head.S linux.15p1/arch/i386/boot/compressed/head.S
--- linux.vanilla/arch/i386/boot/compressed/head.S	Sun Mar 29 20:31:16 1998
+++ linux.15p1/arch/i386/boot/compressed/head.S	Wed Jan 12 01:29:43 2000
@@ -53,7 +53,7 @@
 	xorl %eax,%eax			# Back to 0
 	mov  %cx,%ax			# SP low 16 bits
 	movl %eax,%esp
-	pushl 0				# Clear NT
+	pushl $0			# Clear NT
 	popfl
 	ljmp $(__KERNEL_CS), $0x100000    # Into C and sanity
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/boot/setup.S linux.15p1/arch/i386/boot/setup.S
--- linux.vanilla/arch/i386/boot/setup.S	Sat Aug 14 02:27:13 1999
+++ linux.15p1/arch/i386/boot/setup.S	Wed Jan 12 01:07:14 2000
@@ -343,7 +343,6 @@
 	mov ax,cs		! aka #SETUPSEG
 	sub ax,#DELTA_INITSEG	! aka #INITSEG
 	mov ds,ax
-	mov ds,ax
 	xor ax,ax
 	mov [0xa0], ax		! set table length to 0
 	mov ah, #0xc0
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/kernel/mtrr.c linux.15p1/arch/i386/kernel/mtrr.c
--- linux.vanilla/arch/i386/kernel/mtrr.c	Sat Aug 28 20:00:53 1999
+++ linux.15p1/arch/i386/kernel/mtrr.c	Wed Jan 12 01:35:36 2000
@@ -139,41 +139,41 @@
 	       Changed locking to spin with reschedule.
 	       Made use of new <smp_call_function>.
   v1.28
-    19990201   Zoltan Boszormenyi <zboszor@mol.hu>
+    19990201   Zoltán Böszörményi <zboszor@mail.externet.hu>
 	       Extended the driver to be able to use Cyrix style ARRs.
     19990204   Richard Gooch <rgooch@atnf.csiro.au>
 	       Restructured Cyrix support.
   v1.29
-    19990204   Zoltan Boszormenyi <zboszor@mol.hu>
+    19990204   Zoltán Böszörményi <zboszor@mail.externet.hu>
 	       Refined ARR support: enable MAPEN in set_mtrr_prepare()
 	       and disable MAPEN in set_mtrr_done().
     19990205   Richard Gooch <rgooch@atnf.csiro.au>
 	       Minor cleanups.
   v1.30
-    19990208   Zoltan Boszormenyi <zboszor@mol.hu>
+    19990208   Zoltán Böszörményi <zboszor@mail.externet.hu>
                Protect plain 6x86s (and other processors without the
                Page Global Enable feature) against accessing CR4 in
                set_mtrr_prepare() and set_mtrr_done().
     19990210   Richard Gooch <rgooch@atnf.csiro.au>
 	       Turned <set_mtrr_up> and <get_mtrr> into function pointers.
   v1.31
-    19990212   Zoltan Boszormenyi <zboszor@mol.hu>
+    19990212   Zoltán Böszörményi <zboszor@mail.externet.hu>
                Major rewrite of cyrix_arr_init(): do not touch ARRs,
                leave them as the BIOS have set them up.
                Enable usage of all 8 ARRs.
                Avoid multiplications by 3 everywhere and other
                code clean ups/speed ups.
-    19990213   Zoltan Boszormenyi <zboszor@mol.hu>
+    19990213   Zoltán Böszörményi <zboszor@mail.externet.hu>
                Set up other Cyrix processors identical to the boot cpu.
                Since Cyrix don't support Intel APIC, this is l'art pour l'art.
                Weigh ARRs by size:
                If size <= 32M is given, set up ARR# we were given.
                If size >  32M is given, set up ARR7 only if it is free,
                fail otherwise.
-    19990214   Zoltan Boszormenyi <zboszor@mol.hu>
+    19990214   Zoltán Böszörményi <zboszor@mail.externet.hu>
                Also check for size >= 256K if we are to set up ARR7,
                mtrr_add() returns the value it gets from set_mtrr()
-    19990218   Zoltan Boszormenyi <zboszor@mol.hu>
+    19990218   Zoltán Böszörményi <zboszor@mail.externet.hu>
                Remove Cyrix "coma bug" workaround from here.
                Moved to linux/arch/i386/kernel/setup.c and
                linux/include/asm-i386/bugs.h
@@ -187,7 +187,7 @@
     19990305   Richard Gooch <rgooch@atnf.csiro.au>
 	       Temporarily disable AMD support now MTRR capability flag is set.
   v1.32
-    19990308   Zoltan Boszormenyi <zboszor@mol.hu>
+    19990308   Zoltán Böszörményi <zboszor@mail.externet.hu>
 	       Adjust my changes (19990212-19990218) to Richard Gooch's
 	       latest changes. (19990228-19990305)
   v1.33
@@ -197,7 +197,7 @@
 	       Support K6-II/III based on Alan Cox's <alan@redhat.com> patches.
   v1.34
     19990511   Bart Hartgers <bart@etpmod.phys.tue.nl>
-	       Support Centaur C6 MCR's.
+	       Support Centaur C6 MCRs.
     19990512   Richard Gooch <rgooch@atnf.csiro.au>
 	       Minor cleanups.
   v1.35
@@ -210,6 +210,9 @@
     19990819   Alan Cox <alan@redhat.com>
     	       Tested Zoltan's changes on a pre production Athlon - 100%
     	       success. Fixed one fall through check to be Intel only.
+    19991116   Bart Hartgers <bart@etpmod.phys.tue.nl>
+               Changed Centaur/IDT WinChip support to include WinChip 2.
+               (WC 2 kindly provided by IDT).
 */
 
 #include <linux/types.h>
@@ -581,18 +584,31 @@
     return;
 }   /*  End Function amd_get_mtrr  */
 
-static struct
+static struct CENTAUR_MCR_CTX
 {
-    unsigned long high;
-    unsigned long low;
-} centaur_mcr[8];
+   unsigned type_bits[MTRR_NUM_TYPES];
+   struct {
+      u32 low;
+      u32 high;
+   } mcr[8];
+} *centaur_ctx=NULL;
 
 static void centaur_get_mcr (unsigned int reg, unsigned long *base,
 			     unsigned long *size, mtrr_type *type)
 {
-    *base = centaur_mcr[reg].high & 0xfffff000;
-    *size = (~(centaur_mcr[reg].low & 0xfffff000))+1;
-    *type = MTRR_TYPE_WRCOMB;	/*  If it is there, it is write-combining  */
+    unsigned i;
+    u32 tb;
+    tb = centaur_ctx->mcr[reg].low & 0xfff;
+    *base = centaur_ctx->mcr[reg].high & 0xfffff000;
+    *size = (~(centaur_ctx->mcr[reg].low & 0xfffff000))+1;
+    if (*size) {
+        for( i=0; i<MTRR_NUM_TYPES; ++i)
+	    if (centaur_ctx->type_bits[i]==tb) {
+	        *type = (mtrr_type) i;
+	        return;
+	    }
+        *size = 0;
+    }
 }   /*  End Function centaur_get_mcr  */
 
 static void (*get_mtrr) (unsigned int reg, unsigned long *base,
@@ -716,6 +732,7 @@
     unsigned long low, high;
 
     if (do_safe) set_mtrr_prepare( &ctxt );
+   
     if (size == 0)
     {
         /*  Disable  */
@@ -724,12 +741,13 @@
     else
     {
         high = base & 0xfffff000; /* base works on 4K pages... */
-        low = ((~(size-1))&0xfffff000);
-        low |= 0x1f;		  /* only support write-combining... */
+        low = ((~(size-1))&0xfffff000)|(centaur_ctx->type_bits[type]);
     }
-    centaur_mcr[reg].high = high;
-    centaur_mcr[reg].low = low;
+    centaur_ctx->mcr[reg].high = high;
+    centaur_ctx->mcr[reg].low = low;
+   
     wrmsr (0x110 + reg, low, high);
+   
     if (do_safe) set_mtrr_done( &ctxt );
 }   /*  End Function centaur_set_mtrr_up  */
 
@@ -1078,6 +1096,12 @@
     unsigned long lbase, lsize, last;
 
     if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV;
+    if (type >= MTRR_NUM_TYPES)
+    {
+	printk ("mtrr: type: %u illegal\n", type);
+	return -EINVAL;
+    }
+   
     switch (boot_cpu_data.x86_vendor)
     {
       case X86_VENDOR_AMD:
@@ -1113,13 +1137,12 @@
 	}
         if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR)
 	{
-	    if (type != MTRR_TYPE_WRCOMB)
-	    {
-		printk ("mtrr: only write-combining is supported\n");
+	    if (centaur_ctx->type_bits[type]==0) {
+		printk ("mtrr: type not supported\n");
 		return -EINVAL;
 	    }
 	}
-	else if (base + size < 0x100000)
+	else if (base + size < 0x100000) /* Cyrix */
 	{
 	    printk ("mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n",
 		    base, size);
@@ -1141,11 +1164,6 @@
 	return -EINVAL;
 	/*break;*/
     }
-    if (type >= MTRR_NUM_TYPES)
-    {
-	printk ("mtrr: type: %u illegal\n", type);
-	return -EINVAL;
-    }
     /*  If the type is WC, check that this processor supports it  */
     if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () )
     {
@@ -1172,7 +1190,8 @@
 	/*  New region is enclosed by an existing region  */
 	if (ltype != type)
 	{
-	    if (type == MTRR_TYPE_UNCACHABLE) continue;
+	    if ((boot_cpu_data.x86_vendor != X86_VENDOR_CENTAUR) && 
+	        (type == MTRR_TYPE_UNCACHABLE)) continue;
 	    spin_unlock (&main_lock);
 	    printk ( "mtrr: type mismatch for %lx,%lx old: %s new: %s\n",
 		     base, size, attrib_to_str (ltype), attrib_to_str (type) );
@@ -1652,7 +1671,51 @@
 {
     unsigned i;
     struct set_mtrr_context ctxt;
-
+    u32 low,high;
+    u32 mcr_ctrl_value;
+    unsigned mcr_type;
+    /* Deduce the MCR traits type for this processor.
+     * The documentation of the WinChip 2 suggests that we can read the
+     * MCR_CTRL register for all WinChips, but this hangs on my C6.
+     * -> Work around this.
+     */
+    if ((boot_cpu_data.x86 == 5) && (boot_cpu_data.x86_model == 4)) {
+	/* C6 */
+	mcr_type = 0;
+    } else {
+	rdmsr (0x120, low, high );
+	mcr_type = (low>>17)&0x7;
+    }
+    centaur_ctx = kmalloc( sizeof( struct CENTAUR_MCR_CTX ), GFP_KERNEL );
+    if (centaur_ctx == NULL) {
+        printk("mtrr: Could not allocate memory. Disabling MTRR.\n");
+        boot_cpu_data.x86_capability &= ~X86_FEATURE_MTRR;
+        return;
+    }
+    for( i=0; i<MTRR_NUM_TYPES; ++i) {
+        centaur_ctx->type_bits[i] = 0;
+    }
+    switch( mcr_type ) {
+      case 0:
+        centaur_ctx->type_bits[ MTRR_TYPE_WRCOMB ] = 0x0f;
+        centaur_ctx->type_bits[ MTRR_TYPE_WRBACK ] = 0x1f;
+        mcr_ctrl_value = 0x01f0001f;
+        break;
+      case 1:
+        centaur_ctx->type_bits[ MTRR_TYPE_UNCACHABLE ] = 0x02;
+        centaur_ctx->type_bits[ MTRR_TYPE_WRCOMB ]     = 0x13;
+        centaur_ctx->type_bits[ MTRR_TYPE_WRTHROUGH ]  = 0x11;
+        centaur_ctx->type_bits[ MTRR_TYPE_WRBACK ]     = 0x19;
+        mcr_ctrl_value = 0x01f2005f;
+        break;
+      default:
+        kfree( centaur_ctx );
+        centaur_ctx = NULL;
+        printk ("mtrr: Centaur MCR traits version %u not supported. "
+		"Disabling MTRR.\n", mcr_type);
+	boot_cpu_data.x86_capability &= ~X86_FEATURE_MTRR;
+        return;
+    }
     set_mtrr_prepare (&ctxt);
     /* Unfortunately, MCR's are read-only, so there is no way to
      * find out what the bios might have done.
@@ -1661,14 +1724,16 @@
      * This way we are sure that the centaur_mcr array contains the actual
      * values. The disadvantage is that any BIOS tweaks are thus undone.
      */
+    /* switch off MCR's first */
+    wrmsr (0x120, 0, 0 );
     for (i = 0; i < 8; ++i)
     {
-        centaur_mcr[i].high = 0;
-	centaur_mcr[i].low = 0;
+        centaur_ctx->mcr[i].high = 0;
+	centaur_ctx->mcr[i].low = 0;
 	wrmsr (0x110 + i , 0, 0);
     }
     /*  Throw the main write-combining switch...  */
-    wrmsr (0x120, 0x01f0001f, 0);
+    wrmsr (0x120, mcr_ctrl_value, 0);
     set_mtrr_done (&ctxt);
 }   /*  End Function centaur_mcr_init  */
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/kernel/setup.c linux.15p1/arch/i386/kernel/setup.c
--- linux.vanilla/arch/i386/kernel/setup.c	Tue Jan  4 21:18:39 2000
+++ linux.15p1/arch/i386/kernel/setup.c	Wed Jan 12 01:35:36 2000
@@ -7,8 +7,8 @@
  *  and Martin Mares, November 1997.
  *
  *  Force Cyrix 6x86(MX) and M II processors to report MTRR capability
- *  and fix against Cyrix "coma bug" by
- *      Zoltan Boszormenyi <zboszor@mol.hu> February 1999.
+ *  and Cyrix "coma bug" recognition by
+ *      Zoltán Böszörményi <zboszor@mail.externet.hu> February 1999.
  * 
  *  Force Centaur C6 processors to report MTRR capability.
  *      Bart Hartgers <bart@etpmod.phys.tue.nl>, May 199.
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/vmlinux.lds linux.15p1/arch/i386/vmlinux.lds
--- linux.vanilla/arch/i386/vmlinux.lds	Thu Jan  1 01:00:00 1970
+++ linux.15p1/arch/i386/vmlinux.lds	Wed Jan 12 02:01:12 2000
@@ -0,0 +1,69 @@
+/* ld script to make i386 Linux kernel
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
+ */
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+SECTIONS
+{
+  . = 0xC0000000  + 0x100000;
+  _text = .;			/* Text and read-only data */
+  .text : {
+	*(.text)
+	*(.fixup)
+	*(.gnu.warning)
+	} = 0x9090
+  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
+  .rodata : { *(.rodata) }
+  .kstrtab : { *(.kstrtab) }
+
+  . = ALIGN(16);		/* Exception table */
+  __start___ex_table = .;
+  __ex_table : { *(__ex_table) }
+  __stop___ex_table = .;
+
+  __start___ksymtab = .;	/* Kernel symbol table */
+  __ksymtab : { *(__ksymtab) }
+  __stop___ksymtab = .;
+
+  _etext = .;			/* End of text section */
+
+  .data : {			/* Data */
+	*(.data)
+	CONSTRUCTORS
+	}
+
+  _edata = .;			/* End of data section */
+
+  . = ALIGN(8192);		/* init_task */
+  .data.init_task : { *(.data.init_task) }
+
+  . = ALIGN(4096);		/* Init code and data */
+  __init_begin = .;
+  .text.init : { *(.text.init) }
+  .data.init : { *(.data.init) }
+  . = ALIGN(4096);
+  __init_end = .;
+
+  . = ALIGN(32);
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+  . = ALIGN(4096);
+  .data.page_aligned : { *(.data.idt) }
+
+
+  __bss_start = .;		/* BSS */
+  .bss : {
+	*(.bss)
+	}
+  _end = . ;
+
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+}
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/ppc/kernel/pmac_setup.c linux.15p1/arch/ppc/kernel/pmac_setup.c
--- linux.vanilla/arch/ppc/kernel/pmac_setup.c	Tue Jan  4 21:18:40 2000
+++ linux.15p1/arch/ppc/kernel/pmac_setup.c	Wed Jan 12 01:11:15 2000
@@ -47,6 +47,7 @@
 #include <asm/pgtable.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
+#include <asm/dma.h>
 #include <asm/pci-bridge.h>
 #include <asm/adb.h>
 #include <asm/cuda.h>
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/s390/kernel/head.S linux.15p1/arch/s390/kernel/head.S
--- linux.vanilla/arch/s390/kernel/head.S	Tue Jan  4 21:18:41 2000
+++ linux.15p1/arch/s390/kernel/head.S	Wed Jan 12 01:09:50 2000
@@ -24,7 +24,34 @@
 
 #ifndef CONFIG_IPL
         .org   0
-        .long  0x00080000,0x80000000+start     # Just a restart PSW
+        .long  0x00080000,0x80000000+iplstart  # Just a restart PSW
+
+iplstart:
+        l     %r12,.Lparm                      # pointer to parameter area
+#
+# find out memory size
+#
+        mvc   104(8,0),.Lpcmem0          # setup program check handler
+        slr   %r2,%r2
+        lhi   %r3,1
+        sll   %r3,20
+.Lloop0:
+        l     %r0,0(%r2)                 # test page
+        ar    %r2,%r3                    # add 1M
+        jnm   .Lloop0                    # r1 < 0x80000000 -> loop
+.Lchkmem0:
+        n     %r2,.L4malign0             # align to multiples of 4M
+        st    %r2,MEMORY_SIZE-PARMAREA(%r12)  # store memory size
+        slr   %r2,%r2
+        st    %r2,INITRD_SIZE-PARMAREA(%r12)  # null ramdisk
+        st    %r2,INITRD_START-PARMAREA(%r12)
+        j     start
+
+.Lparm: .long  PARMAREA
+.L4malign0:.long 0xffc00000
+        .align 8
+.Lpcmem0:.long  0x00080000,0x80000000 + .Lchkmem0
+
 #else
 #ifdef CONFIG_IPL_TAPE
 #define IPL_BS 1024
@@ -52,6 +79,26 @@
         lhi   %r2,IPL_BS                       # load start address
         bras  %r14,.Lloader                    # load rest of ipl image
         l     %r12,.Lparm                      # pointer to parameter area
+
+#
+# find out memory size
+#
+        mvc   104(8,0),.Lpcmem0          # setup program check handler
+        slr   %r2,%r2
+        lhi   %r3,1
+        sll   %r3,20
+.Lloop0:
+        l     %r0,0(%r2)                 # test page
+        ar    %r2,%r3                    # add 1M
+        jnm   .Lloop0                    # r1 < 0x80000000 -> loop
+.Lchkmem0:
+        n     %r2,.L4malign0             # align to multiples of 4M
+        st    %r2,MEMORY_SIZE-PARMAREA(%r12)  # store memory size
+        c     %r2,.Lbigmem               # more than 64 MB of memory ?
+        jl    .Lmemok                    # if yes load ramdisk to 32 MB
+        mvc   INITRD_START-PARMAREA(4,%r12),.Lrdstart
+.Lmemok:
+
 #
 # load parameter file from tape
 #
@@ -73,12 +120,12 @@
         tr    0(256,%r4),0(%r3)                # convert parameters to ascii
         tr    256(256,%r4),0(%r3)
         tr    512(256,%r4),0(%r3)
-        tr    768(128,%r4),0(%r3)
+        tr    768(122,%r4),0(%r3)
 .Lnocv: la    %r3,COMMAND_LINE-PARMAREA(%r12)  # load adr. of command line
 	mvc   0(256,%r3),0(%r4)
 	mvc   256(256,%r3),256(%r4)
 	mvc   512(256,%r3),512(%r4)
-	mvc   768(128,%r3),768(%r4)
+	mvc   768(122,%r3),768(%r4)
         slr   %r0,%r0
         j     .Lcntlp
 .Ldelspc:
@@ -197,7 +244,11 @@
 .Lcr6:  .long  0xff000000
         .align 8
 .Lcrash:.long  0x000a0000,0x00000000
+.Lpcmem0:.long  0x00080000,0x80000000 + .Lchkmem0
 .Lparm: .long  PARMAREA
+.L4malign0:.long 0xffc00000
+.Lbigmem:.long 0x04000000
+.Lrdstart:.long 0x02000000
 .Lldret:.long  0
 .Lsnsret: .long 0
 .Lebcdic:.long 0xc5c2c3c4,0xc9c30000
@@ -235,31 +286,57 @@
 
         .org   0xf0
 iplstart:
-        stidp __LC_CPUID                       # store cpuid
-	tm    __LC_CPUID,0xff                  # running under VM ?
-	jno   start                            #   no -> skip loader part
 	l     %r1,0xb8                         # load ipl subchannel number
         lhi   %r2,0x730                        # load start address
         bras  %r14,.Lloader                    # load rest of ipl image
-
 	l     %r12,.Lparm                      # pointer to parameter area
 
 #
+# find out memory size
+#
+        mvc   104(8,0),.Lpcmem0           # setup program check handler
+        slr   %r2,%r2
+        lhi   %r3,1
+        sll   %r3,20
+.Lloop0:
+        l     %r0,0(%r2)                 # test page
+        ar    %r2,%r3                    # add 1M
+        jnm   .Lloop0                    # r1 < 0x80000000 -> loop
+.Lchkmem0:
+        n     %r2,.L4malign0             # align to multiples of 4M
+        st    %r2,MEMORY_SIZE-PARMAREA(%r12)  # store memory size
+        c     %r2,.Lbigmem               # more than 64 MB of memory ?
+        jl    .Lmemok                    # if yes load ramdisk to 32 MB
+        mvc   INITRD_START-PARMAREA(4,%r12),.Lrdstart
+.Lmemok:
+
+#
 # load parameter file from reader
 #
-        la    %r2,COMMAND_LINE-PARMAREA(%r12)  # load adr. of command line
+        l     %r2,INITRD_START-PARMAREA(%r12)  # use ramdisk location as temp
         bras  %r14,.Lloader                    # load parameter file
         ltr   %r2,%r2                          # got anything ?
         jz    .Lnopf
-        la    %r3,COMMAND_LINE-PARMAREA(%r12)  # load adr. of command line
-        l     %r4,.Lcvtab                 
-        tr    0(256,3),0(4)                    # convert parameters to ascii
-        l     %r4,.Lcvtab                 
-        tr    256(256,3),0(4)
-        l     %r4,.Lcvtab                 	
-        tr    512(256,3),0(4)
-        l     %r4,.Lcvtab                 	
-        tr    768(128,3),0(4)            
+        chi   %r2,896
+        jnh   .Lnotrunc
+        lhi   %r2,896
+.Lnotrunc:
+        l     %r4,INITRD_START-PARMAREA(%r12)
+        clc   0(6,%r4),.Lebcdic                # prefix EBCDIC in parm file ?
+        jne   .Lnocv
+        ahi   %r4,6                            # skip EBCDIC
+        ahi   %r2,-6
+        jnp   .Lnopf
+        l     %r3,.Lcvtab
+        tr    0(256,%r4),0(%r3)                # convert parameters to ascii
+        tr    256(256,%r4),0(%r3)
+        tr    512(256,%r4),0(%r3)
+        tr    768(122,%r4),0(%r3)
+.Lnocv: la    %r3,COMMAND_LINE-PARMAREA(%r12)  # load adr. of command line
+        mvc   0(256,%r3),0(%r4)
+        mvc   256(256,%r3),256(%r4)
+        mvc   512(256,%r3),512(%r4)
+        mvc   768(122,%r3),768(%r4)
         slr   %r0,%r0
         j     .Lcntlp
 .Ldelspc:
@@ -289,6 +366,10 @@
 #
 # everything loaded, reset files in reader, then go for it
 #
+        stidp __LC_CPUID                       # store cpuid
+        lh    %r0,__LC_CPUID+4                 # get cpu version
+        chi   %r0,0x7490                       # running on P/390 ?
+        je   start                             #   no -> skip reset
         la    %r2,.Lreset              
         lhi   %r3,26
         .long 0x83230008
@@ -358,13 +439,17 @@
 .Lcr6:  .long  0xff000000
 .Lloadp:.long  0,0
 .Lparm:	.long  PARMAREA
+.Lebcdic:.long 0xc5c2c3c4,0xc9c30000
+.L4malign0:.long 0xffc00000
+.Lbigmem:.long 0x04000000
+.Lrdstart:.long 0x02000000
 .Lcvtab:.long  _ebcasc                         # ebcdic to ascii table
 .Lreset:.byte  0xc3,0xc8,0xc1,0xd5,0xc7,0xc5,0x40,0xd9,0xc4,0xd9,0x40
         .byte  0xc1,0xd3,0xd3,0x40,0xd2,0xc5,0xc5,0xd7,0x40,0xd5,0xd6
         .byte  0xc8,0xd6,0xd3,0xc4             # "change rdr all keep nohold"
         .align 8
-.Lcrash:
-        .long  0x000a0000,0x00000000
+.Lpcmem0:.long  0x00080000,0x80000000 + .Lchkmem0
+.Lcrash:.long  0x000a0000,0x00000000
 .Lnewpsw:
         .long  0x00080000,0x80000000+.Lioint
 .Lwaitpsw:
@@ -395,8 +480,11 @@
 	l     %r12,.Lparm1-.LPG1(%r13)   # pointer to parameter area
 
 #
-# find out memory size
+# find out memory size. That is done in the ipl loader too but for
+# ipl from dasd the size of the memory has to be detected too...
 #
+        icm   %r0,15,MEMORY_SIZE-PARMAREA(%r12)
+        jnz   .Lsizeok
 	mvc   104(8,0),.Lpcmem-.LPG1(%r13) # setup program check handler
         slr   %r1,%r1
         lhi   %r2,1
@@ -408,6 +496,7 @@
 .Lchkmem:
 	n     %r1,.L4malign-.LPG1(%r13)  # align to multiples of 4M
 	st    %r1,MEMORY_SIZE-PARMAREA(%r12)  # store memory size
+.Lsizeok:
 
 #
 # find out if we are running under VM
@@ -417,6 +506,11 @@
 	jno    .Lnovm
         oi     MACHINE_FLAGS+3-PARMAREA(%r12),1    # set VM flag
 .Lnovm:
+        lh     %r0,__LC_CPUID+4         # get cpu version
+        chi    %r0,0x7490               # running on a P/390 ?
+        jne    .Lnop390
+        oi     MACHINE_FLAGS+3-PARMAREA(%r12),4    # set P/390 flag
+.Lnop390:
 
 #
 # find out if we have an IEEE fpu
@@ -428,23 +522,6 @@
         oi     MACHINE_FLAGS+3-PARMAREA(%r12),2    # set IEEE fpu flag
 .Lchkfpu:
 
-#
-# move ramdisk to the end of storage, out of the way of the paging tables
-#
-        icm   %r2,15,INITRD_START-PARMAREA(%r12)  # load adr. of ramdisk
-        jz    .Lnocprd
-        icm   %r3,15,INITRD_SIZE-PARMAREA(%r12)   # store size of ramdisk
-        jz    .Lnocprd
-        l     %r4,MEMORY_SIZE-PARMAREA(%r12)
-        sr    %r4,%r3
-        srl   %r4,12
-        sll   %r4,12
-        st    %r4,INITRD_START-PARMAREA(%r12)
-        lr    %r5,%r3
-.Lcprd:
-        mvcle %r4,%r2,0
-        jo    .Lcprd
-.Lnocprd:
         lpsw  .Lentry-.LPG1(13)         # jump to _stext in primary-space,
                                         # virtual and never return ...
         .align 8
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/sparc/config.in linux.15p1/arch/sparc/config.in
--- linux.vanilla/arch/sparc/config.in	Wed Oct 20 01:12:33 1999
+++ linux.15p1/arch/sparc/config.in	Wed Jan 12 01:26:28 2000
@@ -103,6 +103,25 @@
 
 if [ "$CONFIG_NET" = "y" ]; then
 	source net/Config.in
+	mainmenu_option next_comment
+	comment 'Amateur Radio support'
+	bool 'Amateur Radio support' CONFIG_HAMRADIO
+	if [ "$CONFIG_HAMRADIO" != "n" ] ; then
+		comment 'Packet Radio protocols'
+		tristate 'Amateur Radio AX.25 Level 2 protocol' CONFIG_AX25
+		if [ "$CONFIG_AX25" != "n" ]; then
+			bool '   AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE
+#			bool '   AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER
+			dep_tristate '   Amateur Radio NET/ROM protocol' CONFIG_NETROM $CONFIG_AX25
+			dep_tristate '   Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25
+		fi
+	fi
+	if [ "$CONFIG_AX25" != "n" ]; then
+		comment 'AX.25 network device drivers'
+		dep_tristate 'Serial port KISS driver' CONFIG_MKISS $CONFIG_AX25
+		dep_tristate 'Serial port 6PACK driver' CONFIG_6PACK $CONFIG_AX25
+	fi
+	endmenu
 fi
 
 mainmenu_option next_comment
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/dmfe.c linux.15p1/dmfe.c
--- linux.vanilla/dmfe.c	Thu Jan  1 01:00:00 1970
+++ linux.15p1/dmfe.c	Wed Jan 12 16:58:27 2000
@@ -0,0 +1,1594 @@
+/*
+   dmfe.c: Version 1.27C 01/07/2000
+
+   A Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux. 
+   Copyright (C) 1997  Sten Wang
+
+   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.
+
+
+   Compiler command:
+   "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall 
+   -Wstrict-prototypes -O6 -c dmfe.c"
+   OR
+   "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net -Wall 
+   -Wstrict-prototypes -O6 -c dmfe.c"
+
+   The following steps teach you how to active DM9102 board:
+   1. Used the upper compiler command to compile dmfe.c
+   2. insert dmfe module into kernel
+   "insmod dmfe"        ;;Auto Detection Mode
+   "insmod dmfe mode=0" ;;Force 10M Half Duplex
+   "insmod dmfe mode=1" ;;Force 100M Half Duplex
+   "insmod dmfe mode=4" ;;Force 10M Full Duplex
+   "insmod dmfe mode=5" ;;Force 100M Full Duplex
+   3. config a dm9102 network interface
+   "ifconfig eth0 172.22.3.18"
+   ^^^^^^^^^^^ your IP address
+   4. active the IP routing table
+   "route add -net 172.22.3.0 eth0"
+   5. Well done. Your DM9102 adapter actived now.
+
+   Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw
+
+   Date:   10/28,1998
+
+   (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved.
+   
+   Stripped out < 2.2 support, made io address an unsigned long,
+   		Alan Cox
+   		
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/processor.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/version.h>
+
+/* Board/System/Debug information/definition ---------------- */
+#define PCI_DM9132_ID   0x91321282	/* Davicom DM9132 ID */
+#define PCI_DM9102_ID   0x91021282	/* Davicom DM9102 ID */
+#define PCI_DM9100_ID   0x91001282	/* Davicom DM9100 ID */
+
+#define DMFE_SUCC       0
+#define DM9102_IO_SIZE  0x80
+#define DM9102A_IO_SIZE 0x100
+#define TX_FREE_DESC_CNT 0x1	/* Tx packet count */
+#define TX_DESC_CNT     0x10	/* Allocated Tx descriptors */
+#define RX_DESC_CNT     0x10	/* Allocated Rx descriptors */
+#define DESC_ALL_CNT    TX_DESC_CNT+RX_DESC_CNT
+#define TX_BUF_ALLOC    0x600
+#define RX_ALLOC_SIZE   0x620
+#define DM910X_RESET    1
+#define CR6_DEFAULT     0x00280000	/* SF, HD */
+#define CR7_DEFAULT     0x1a2cd
+#define CR15_DEFAULT    0x06	/* TxJabber RxWatchdog */
+#define TDES0_ERR_MASK  0x4302	/* TXJT, LC, EC, FUE */
+#define MAX_PACKET_SIZE 1514
+#define DMFE_MAX_MULTICAST 14
+#define RX_MAX_TRAFFIC  0x14000
+#define MAX_CHECK_PACKET 0x8000
+
+#define DMFE_10MHF      0
+#define DMFE_100MHF     1
+#define DMFE_10MFD      4
+#define DMFE_100MFD     5
+#define DMFE_AUTO       8
+
+#define DMFE_TIMER_WUT  jiffies+(HZ*2)/2	/* timer wakeup time : 1 second */
+#define DMFE_TX_TIMEOUT HZ*2	/* tx packet time-out time 2 s" */
+
+#define DMFE_DBUG(dbug_now, msg, vaule) if (dmfe_debug || dbug_now) printk("DBUG: %s %x\n", msg, vaule)
+
+#define DELAY_5US udelay(5)	/* udelay scale 1 usec */
+
+#define DELAY_1US udelay(1)	/* udelay scale 1 usec */
+
+#define SHOW_MEDIA_TYPE(mode) printk("\n<WARN> Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
+
+
+/* CR9 definition: SROM/MII */
+#define CR9_SROM_READ   0x4800
+#define CR9_SRCS        0x1
+#define CR9_SRCLK       0x2
+#define CR9_CRDOUT      0x8
+#define SROM_DATA_0     0x0
+#define SROM_DATA_1     0x4
+#define PHY_DATA_1      0x20000
+#define PHY_DATA_0      0x00000
+#define MDCLKH          0x10000
+
+#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;
+
+#define CHK_IO_SIZE(pci_id, dev_rev) ( (pci_id==PCI_DM9132_ID) || (dev_rev >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE
+
+
+/* Structure/enum declaration ------------------------------- */
+struct tx_desc {
+	u32 tdes0, tdes1, tdes2, tdes3;
+	u32 tx_skb_ptr;
+	u32 tx_buf_ptr;
+	u32 next_tx_desc;
+	u32 reserved;
+};
+
+struct rx_desc {
+	u32 rdes0, rdes1, rdes2, rdes3;
+	u32 rx_skb_ptr;
+	u32 rx_buf_ptr;
+	u32 next_rx_desc;
+	u32 reserved;
+};
+
+struct dmfe_board_info {
+	u32 chip_id;		/* Chip vendor/Device ID */
+	u32 chip_revesion;	/* Chip revesion */
+	struct device *next_dev;	/* next device */
+
+	struct pci_dev *net_dev;/* PCI device */
+
+	unsigned long ioaddr;	/* I/O base address */
+	u32 cr0_data;
+	u32 cr5_data;
+	u32 cr6_data;
+	u32 cr7_data;
+	u32 cr15_data;
+
+/* descriptor pointer */
+	unsigned char *buf_pool_ptr;	/* Tx buffer pool memory */
+	unsigned char *buf_pool_start;	/* Tx buffer pool align dword */
+	unsigned char *desc_pool_ptr;	/* descriptor pool memory */
+	struct tx_desc *first_tx_desc;
+	struct tx_desc *tx_insert_ptr;
+	struct tx_desc *tx_remove_ptr;
+	struct rx_desc *first_rx_desc;
+	struct rx_desc *rx_insert_ptr;
+	struct rx_desc *rx_ready_ptr;	/* packet come pointer */
+	u32 tx_packet_cnt;	/* transmitted packet count */
+	u32 rx_avail_cnt;	/* available rx descriptor count */
+	u32 interval_rx_cnt;	/* rx packet count a callback time */
+
+	u16 phy_id2;		/* Phyxcer ID2 */
+
+	u8 media_mode;		/* user specify media mode */
+	u8 op_mode;		/* real work media mode */
+	u8 phy_addr;
+	u8 link_failed;		/* Ever link failed */
+	u8 wait_reset;		/* Hardware failed, need to reset */
+	u8 in_reset_state;	/* Now driver in reset routine */
+	u8 rx_error_cnt;	/* recievd abnormal case count */
+	u8 dm910x_chk_mode;	/* Operating mode check */
+	struct timer_list timer;
+	struct enet_statistics stats;	/* statistic counter */
+	unsigned char srom[128];
+};
+
+enum dmfe_offsets {
+	DCR0 = 0, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20, DCR5 = 0x28,
+	DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48, DCR10 = 0x50, DCR11 = 0x58,
+	DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70, DCR15 = 0x78
+};
+
+enum dmfe_CR6_bits {
+	CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, CR6_FDM = 0x200,
+	CR6_TXSC = 0x2000, CR6_STI = 0x100000, CR6_SFT = 0x200000, CR6_RXA = 0x40000000,
+	CR6_NO_PURGE = 0x20000000
+};
+
+/* Global variable declaration ----------------------------- */
+static int dmfe_debug = 0;
+static unsigned char dmfe_media_mode = 8;
+static struct device *dmfe_root_dev = NULL;	/* First device */
+static u32 dmfe_cr6_user_set = 0;
+
+/* For module input parameter */
+static int debug = 0;
+static u32 cr6set = 0;
+static unsigned char mode = 8;
+static u8 chkmode = 1;
+
+unsigned long CrcTable[256] =
+{
+	0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
+	0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
+	0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L,
+	0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L,
+	0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL,
+	0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L,
+	0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL,
+	0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L,
+	0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L,
+	0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL,
+	0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L,
+	0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L,
+	0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L,
+	0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL,
+	0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L,
+	0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL,
+	0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL,
+	0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L,
+	0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L,
+	0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L,
+	0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL,
+	0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L,
+	0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL,
+	0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L,
+	0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L,
+	0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL,
+	0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L,
+	0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L,
+	0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L,
+	0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL,
+	0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L,
+	0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL,
+	0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL,
+	0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L,
+	0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L,
+	0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L,
+	0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL,
+	0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L,
+	0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL,
+	0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L,
+	0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L,
+	0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL,
+	0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L,
+	0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L,
+	0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L,
+	0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL,
+	0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L,
+	0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL,
+	0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL,
+	0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L,
+	0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L,
+	0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L,
+	0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL,
+	0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L,
+	0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL,
+	0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L,
+	0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L,
+	0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL,
+	0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L,
+	0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L,
+	0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L,
+	0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL,
+	0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L,
+	0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL
+};
+
+/* function declaration ------------------------------------- */
+static int dmfe_probe(struct device *);
+static int dmfe_open(struct device *);
+static int dmfe_start_xmit(struct sk_buff *, struct device *);
+static int dmfe_stop(struct device *);
+static struct enet_statistics *dmfe_get_stats(struct device *);
+static void dmfe_set_filter_mode(struct device *);
+static int dmfe_do_ioctl(struct device *, struct ifreq *, int);
+static u16 read_srom_word(long, int);
+static void dmfe_interrupt(int, void *, struct pt_regs *);
+static void dmfe_descriptor_init(struct dmfe_board_info *, u32);
+static void allocated_rx_buffer(struct dmfe_board_info *);
+static void update_cr6(u32, u32);
+static void send_filter_frame(struct device *, int);
+static void dm9132_id_table(struct device *, int);
+static u16 phy_read(u32, u8, u8, u32);
+static void phy_write(u32, u8, u8, u16, u32);
+static void phy_write_1bit(u32, u32);
+static u16 phy_read_1bit(u32);
+static void dmfe_sense_speed(struct dmfe_board_info *);
+static void dmfe_process_mode(struct dmfe_board_info *);
+static void dmfe_timer(unsigned long);
+static void dmfe_rx_packet(struct device *, struct dmfe_board_info *);
+static void dmfe_reused_skb(struct dmfe_board_info *, struct sk_buff *);
+static void dmfe_dynamic_reset(struct device *);
+static void dmfe_free_rxbuffer(struct dmfe_board_info *);
+static void dmfe_init_dm910x(struct device *);
+static unsigned long cal_CRC(unsigned char *, unsigned int, u8);
+
+/* DM910X network baord routine ---------------------------- */
+
+/*
+   Search DM910X board ,allocate space and register it
+ */
+int dmfe_probe(struct device *dev)
+{
+	u32 pci_iobase;
+	u16 dm9102_count = 0;
+	u8 pci_irqline;
+	static int index = 0;	/* For multiple call */
+	struct dmfe_board_info *db;	/* Point a board information structure */
+	int i;
+	struct pci_dev *net_dev = NULL;
+
+	DMFE_DBUG(0, "dmfe_probe()", 0);
+
+	if (!pci_present())
+		return -ENODEV;
+
+	index = 0;
+	while ((net_dev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, net_dev)))
+	{
+		u32 pci_id;
+		u32 dev_rev;
+		u8 pci_cmd;
+
+		index++;
+		if (pci_read_config_dword(net_dev, PCI_VENDOR_ID, &pci_id) != DMFE_SUCC)
+			continue;
+
+		if ((pci_id != PCI_DM9102_ID) && (pci_id != PCI_DM9132_ID))
+			continue;
+
+		/* read PCI IO base address and IRQ to check */
+		pci_read_config_dword(net_dev, PCI_BASE_ADDRESS_0, &pci_iobase);
+		pci_read_config_byte(net_dev, PCI_INTERRUPT_LINE, &pci_irqline);
+		pci_iobase &= ~0x7f;	/* mask off bit0~6 */
+
+		/* Enable Master/IO access, Disable memory access */
+		pci_read_config_byte(net_dev, PCI_COMMAND, &pci_cmd);
+		pci_cmd |= PCI_COMMAND_IO + PCI_COMMAND_MASTER;
+		pci_cmd &= ~PCI_COMMAND_MEMORY;
+		pci_write_config_byte(net_dev, PCI_COMMAND, pci_cmd);
+
+		/* Set Latency Timer 80h */
+		pci_write_config_byte(net_dev, PCI_LATENCY_TIMER, 0x80);
+
+		/* Read Chip revesion */
+		pci_read_config_dword(net_dev, 8, &dev_rev);
+
+		/* IO range check */
+		if (check_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev))) {
+			printk(KERN_ERR "dmfe: I/O conflict : IO=%x Range=%x\n", pci_iobase, CHK_IO_SIZE(pci_id, dev_rev));
+			continue;
+		}
+		/* Interrupt check */
+		if (pci_irqline == 0) {
+			printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n", pci_irqline);
+			continue;
+		}
+		/* Found DM9102 card and PCI resource allocated OK */
+		dm9102_count++;	/* Found a DM9102 card */
+
+		/* Init network device */
+		dev = init_etherdev(dev, 0);
+		
+		if(dev==NULL)
+		{
+			printk(KERN_ERR "dmfe: out of memory.\n");
+			continue;
+		}
+
+		/* Allocated board information structure */
+		db = (void *) (kmalloc(sizeof(*db), GFP_KERNEL | GFP_DMA));
+		
+		if(db==NULL)
+		{
+			printk(KERN_ERR "dmfe: out of memory.\n");
+			continue;
+		}
+		
+		memset(db, 0, sizeof(*db));
+		dev->priv = db;	/* link device and board info */
+		db->next_dev = dmfe_root_dev;
+		dmfe_root_dev = dev;
+
+		db->chip_id = pci_id;	/* keep Chip vandor/Device ID */
+		db->ioaddr = pci_iobase;
+		db->chip_revesion = dev_rev;
+
+		db->net_dev = net_dev;
+
+		dev->base_addr = pci_iobase;
+		dev->irq = pci_irqline;
+		dev->open = &dmfe_open;
+		dev->hard_start_xmit = &dmfe_start_xmit;
+		dev->stop = &dmfe_stop;
+		dev->get_stats = &dmfe_get_stats;
+		dev->set_multicast_list = &dmfe_set_filter_mode;
+		dev->do_ioctl = &dmfe_do_ioctl;
+
+		request_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev), dev->name);
+
+		/* read 64 word srom data */
+		for (i = 0; i < 64; i++)
+			((u16 *) db->srom)[i] = read_srom_word(pci_iobase, i);
+
+		/* Set Node address */
+		for (i = 0; i < 6; i++)
+			dev->dev_addr[i] = db->srom[20 + i];
+
+		dev = 0;	/* NULL device */
+	}
+
+	if (!dm9102_count)
+		printk(KERN_WARNING "Can't find DM910X board or resource error\n");
+
+	return dm9102_count ? 0 : -ENODEV;
+}
+
+/*
+   Open the interface.
+   The interface is opened whenever "ifconfig" actives it.
+ */
+static int dmfe_open(struct device *dev)
+{
+	struct dmfe_board_info *db = dev->priv;
+
+	DMFE_DBUG(0, "dmfe_open", 0);
+
+	if (request_irq(dev->irq, &dmfe_interrupt, SA_SHIRQ, dev->name, dev))
+		return -EAGAIN;
+
+	/* Allocated Tx/Rx descriptor memory */
+	db->desc_pool_ptr = kmalloc(sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, GFP_KERNEL | GFP_DMA);
+	if (db->desc_pool_ptr == NULL)
+		return -ENOMEM;
+	if ((u32) db->desc_pool_ptr & 0x1f)
+		db->first_tx_desc = (struct tx_desc *) (((u32) db->desc_pool_ptr & ~0x1f) + 0x20);
+	else
+		db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr;
+
+	/* Allocated Tx buffer memory */
+	db->buf_pool_ptr = kmalloc(TX_BUF_ALLOC * TX_DESC_CNT + 4, GFP_KERNEL | GFP_DMA);
+	if (db->buf_pool_ptr == NULL) {
+		kfree(db->desc_pool_ptr);
+		return -ENOMEM;
+	}
+	if ((u32) db->buf_pool_ptr & 0x3)
+		db->buf_pool_start = (char *) (((u32) db->buf_pool_ptr & ~0x3) + 0x4);
+	else
+		db->buf_pool_start = db->buf_pool_ptr;
+
+	/* system variable init */
+	db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set;
+	db->tx_packet_cnt = 0;
+	db->rx_avail_cnt = 0;
+	db->link_failed = 0;
+	db->wait_reset = 0;
+	db->in_reset_state = 0;
+	db->rx_error_cnt = 0;
+
+	if (!chkmode || (db->chip_id == PCI_DM9132_ID) || (db->chip_revesion >= 0x02000030)) {
+		//db->cr6_data &= ~CR6_SFT;         /* Used Tx threshold */
+		//db->cr6_data |= CR6_NO_PURGE;     /* No purge if rx unavailable */
+		db->cr0_data = 0xc00000;	/* TX/RX desc burst mode */
+		db->dm910x_chk_mode = 4;	/* Enter the normal mode */
+	} else {
+		db->cr0_data = 0;
+		db->dm910x_chk_mode = 1;	/* Enter the check mode */
+	}
+
+	/* Initilize DM910X board */
+	dmfe_init_dm910x(dev);
+
+	/* Active System Interface */
+	dev->tbusy = 0;		/* Can transmit packet */
+	dev->start = 1;		/* interface ready */
+	MOD_INC_USE_COUNT;
+
+	/* set and active a timer process */
+	init_timer(&db->timer);
+	db->timer.expires = DMFE_TIMER_WUT;
+	db->timer.data = (unsigned long) dev;
+	db->timer.function = &dmfe_timer;
+	add_timer(&db->timer);
+
+	return 0;
+}
+
+/* Initilize DM910X board
+   Reset DM910X board
+   Initilize TX/Rx descriptor chain structure
+   Send the set-up frame
+   Enable Tx/Rx machine
+ */
+static void dmfe_init_dm910x(struct device *dev)
+{
+	struct dmfe_board_info *db = dev->priv;
+	unsigned long ioaddr = db->ioaddr;
+
+	DMFE_DBUG(0, "dmfe_init_dm910x()", 0);
+
+	/* Reset DM910x board : need 32 PCI clock to complete */
+	outl(DM910X_RESET, ioaddr + DCR0);	/* RESET MAC */
+	DELAY_5US;
+	outl(db->cr0_data, ioaddr + DCR0);
+
+	outl(0x180, ioaddr + DCR12);	/* Let bit 7 output port */
+	outl(0x80, ioaddr + DCR12);	/* RESET DM9102 phyxcer */
+	outl(0x0, ioaddr + DCR12);	/* Clear RESET signal */
+
+	/* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */
+	db->phy_addr = 1;
+
+/* Media Mode Check */
+	db->media_mode = dmfe_media_mode;
+	if (db->media_mode & DMFE_AUTO)
+		dmfe_sense_speed(db);
+	else
+		db->op_mode = db->media_mode;
+	dmfe_process_mode(db);
+
+	/* Initiliaze Transmit/Receive decriptor and CR3/4 */
+	dmfe_descriptor_init(db, ioaddr);
+
+	/* Init CR6 to program DM910x operation */
+	update_cr6(db->cr6_data, ioaddr);
+
+	/* Send setup frame */
+	if (db->chip_id == PCI_DM9132_ID)
+		dm9132_id_table(dev, dev->mc_count);	/* DM9132 */
+	else
+		send_filter_frame(dev, dev->mc_count);	/* DM9102/DM9102A */
+
+	/* Init CR5/CR7, interrupt active bit */
+	outl(0xffffffff, ioaddr + DCR5);	/* clear all CR5 status */
+	db->cr7_data = CR7_DEFAULT;
+	outl(db->cr7_data, ioaddr + DCR7);
+
+/* Init CR15, Tx jabber and Rx watchdog timer */
+	db->cr15_data = CR15_DEFAULT;
+	outl(db->cr15_data, ioaddr + DCR15);
+
+	/* Enable DM910X Tx/Rx function */
+	db->cr6_data |= CR6_RXSC | CR6_TXSC;
+	update_cr6(db->cr6_data, ioaddr);
+
+}
+
+
+/*
+   Hardware start transmission.
+   Send a packet to media from the upper layer.
+ */
+static int dmfe_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct dmfe_board_info *db = dev->priv;
+	struct tx_desc *txptr;
+
+	DMFE_DBUG(0, "dmfe_start_xmit", 0);
+
+	if ((dev->tbusy == 1) && (db->tx_packet_cnt != 0))
+		return 1;
+	else
+		dev->tbusy = 0;
+
+	/* Too large packet check */
+	if (skb->len > MAX_PACKET_SIZE) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+	/* No Tx resource check, it never happen nromally */
+	if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) {
+		printk(KERN_ERR "dmfe: No Tx resource, enter xmit() again \n");
+		dev_kfree_skb(skb);
+		dev->tbusy = 1;
+		return -EBUSY;
+	}
+	/* transmit this packet */
+	txptr = db->tx_insert_ptr;
+	memcpy((char *) txptr->tx_buf_ptr, (char *) skb->data, skb->len);
+	txptr->tdes1 = 0xe1000000 | skb->len;
+	txptr->tdes0 = 0x80000000;	/* set owner bit to DM910X */
+
+	/* Point to next transmit free descriptor */
+	db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
+
+	/* transmit counter increase 1 */
+	db->tx_packet_cnt++;
+	db->stats.tx_packets++;
+
+	/* issue Tx polling command */
+	outl(0x1, dev->base_addr + DCR1);
+
+	/* Tx resource check */
+	if (db->tx_packet_cnt >= TX_FREE_DESC_CNT)
+		dev->tbusy = 1;
+
+	/* Set transmit time stamp */
+	dev->trans_start = jiffies;	/* saved the time stamp */
+
+	/* free this SKB */
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+/*
+   Stop the interface.
+   The interface is stopped when it is brought.
+ */
+
+static int dmfe_stop(struct device *dev)
+{
+	struct dmfe_board_info *db = dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+
+	DMFE_DBUG(0, "dmfe_stop", 0);
+
+	/* disable system */
+	dev->start = 0;		/* interface disable */
+	dev->tbusy = 1;		/* can't transmit */
+
+	/* Reset & stop DM910X board */
+	outl(DM910X_RESET, ioaddr + DCR0);
+	DELAY_5US;
+
+	/* deleted timer */
+	del_timer(&db->timer);
+
+	/* free interrupt */
+	free_irq(dev->irq, dev);
+
+	/* free allocated rx buffer */
+	dmfe_free_rxbuffer(db);
+
+	/* free all descriptor memory and buffer memory */
+	kfree(db->desc_pool_ptr);
+	kfree(db->buf_pool_ptr);
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+/*
+   DM9102 insterrupt handler
+   receive the packet to upper layer, free the transmitted packet
+ */
+
+static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = dev_id;
+	struct tx_desc *txptr;
+	struct dmfe_board_info *db;
+	unsigned long ioaddr;
+
+	if (!dev) {
+		DMFE_DBUG(1, "dmfe_interrupt() without device arg", 0);
+		return;
+	}
+	if (dev->interrupt) {
+		DMFE_DBUG(1, "dmfe_interrupt() re-entry ", 0);
+		return;
+	}
+	/* A real interrupt coming */
+	dev->interrupt = 1;	/* Lock interrupt */
+	db = (struct dmfe_board_info *) dev->priv;
+	ioaddr = dev->base_addr;
+
+	DMFE_DBUG(0, "dmfe_interrupt()", 0);
+
+/* Disable all interrupt in CR7 to solve the interrupt edge problem */
+	outl(0, ioaddr + DCR7);
+
+/* Got DM910X status */
+	db->cr5_data = inl(ioaddr + DCR5);
+	outl(db->cr5_data, ioaddr + DCR5);
+	/* printk("CR5=%x\n", db->cr5_data); */
+
+	/* Check system status */
+	if (db->cr5_data & 0x2000) {
+		/* system bus error happen */
+		DMFE_DBUG(1, "System bus error happen. CR5=", db->cr5_data);
+		dev->tbusy = 1;
+		db->wait_reset = 1;	/* Need to RESET */
+		outl(0, ioaddr + DCR7);		/* disable all interrupt */
+		dev->interrupt = 0;	/* unlock interrupt */
+		return;
+	}
+	/* Free the transmitted descriptor */
+	txptr = db->tx_remove_ptr;
+	while (db->tx_packet_cnt) {
+		/* printk("tdes0=%x\n", txptr->tdes0); */
+		if (txptr->tdes0 & 0x80000000)
+			break;
+
+		/* Transmit statistic counter */
+		if (txptr->tdes0 != 0x7fffffff) {
+			/* printk("tdes0=%x\n", txptr->tdes0); */
+			db->stats.collisions += (txptr->tdes0 >> 3) & 0xf;
+			db->stats.tx_bytes += txptr->tdes1 & 0x7ff;
+			if (txptr->tdes0 & TDES0_ERR_MASK)
+				db->stats.tx_errors++;
+		}
+		txptr = (struct tx_desc *) txptr->next_tx_desc;
+		db->tx_packet_cnt--;
+	}
+	db->tx_remove_ptr = (struct tx_desc *) txptr;
+
+	if (dev->tbusy && (db->tx_packet_cnt < TX_FREE_DESC_CNT)) {
+		dev->tbusy = 0;	/* free a resource */
+		mark_bh(NET_BH);	/* active bottom half */
+	}
+	/* Received the coming packet */
+	if (db->rx_avail_cnt)
+		dmfe_rx_packet(dev, db);
+
+	/* reallocated rx descriptor buffer */
+	if (db->rx_avail_cnt < RX_DESC_CNT)
+		allocated_rx_buffer(db);
+
+	/* Mode Check */
+	if (db->dm910x_chk_mode & 0x2) {
+		db->dm910x_chk_mode = 0x4;
+		db->cr6_data |= 0x100;
+		update_cr6(db->cr6_data, db->ioaddr);
+	}
+	dev->interrupt = 0;	/* release interrupt lock */
+
+	/* Restore CR7 to enable interrupt mask */
+	if (db->interval_rx_cnt > RX_MAX_TRAFFIC)
+		db->cr7_data = 0x1a28d;
+	else
+		db->cr7_data = 0x1a2cd;
+	outl(db->cr7_data, ioaddr + DCR7);
+}
+
+/*
+   Receive the come packet and pass to upper layer
+ */
+ 
+static void dmfe_rx_packet(struct device *dev, struct dmfe_board_info *db)
+{
+	struct rx_desc *rxptr;
+	struct sk_buff *skb;
+	int rxlen;
+
+	rxptr = db->rx_ready_ptr;
+
+	while (db->rx_avail_cnt) {
+		if (rxptr->rdes0 & 0x80000000)	/* packet owner check */
+			break;
+
+		db->rx_avail_cnt--;
+		db->interval_rx_cnt++;
+
+		if ((rxptr->rdes0 & 0x300) != 0x300) {
+			/* A packet without First/Last flag */
+			/* reused this SKB */
+			DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
+			dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
+			/* db->rx_error_cnt++; */
+		} else {
+			/* A packet with First/Last flag */
+			rxlen = ((rxptr->rdes0 >> 16) & 0x3fff) - 4;	/* skip CRC */
+
+			if (rxptr->rdes0 & 0x8000) {	/* error summary bit check */
+				/* This is a error packet */
+				/* printk("rdes0 error : %x \n", rxptr->rdes0); */
+				db->stats.rx_errors++;
+				if (rxptr->rdes0 & 1)
+					db->stats.rx_fifo_errors++;
+				if (rxptr->rdes0 & 2)
+					db->stats.rx_crc_errors++;
+				if (rxptr->rdes0 & 0x80)
+					db->stats.rx_length_errors++;
+			}
+			if (!(rxptr->rdes0 & 0x8000) ||
+			    ((db->cr6_data & CR6_PM) && (rxlen > 6))) {
+				skb = (struct sk_buff *) rxptr->rx_skb_ptr;
+
+				/* Received Packet CRC check need or not */
+				if ((db->dm910x_chk_mode & 1) && (cal_CRC(skb->tail, rxlen, 1) != (*(unsigned long *) (skb->tail + rxlen)))) {
+					/* Found a error received packet */
+					dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
+					db->dm910x_chk_mode = 3;
+				} else {
+					/* A good packet coming, send to upper layer */
+					skb->dev = dev;
+					skb_put(skb, rxlen);
+					skb->protocol = eth_type_trans(skb, dev);
+					netif_rx(skb);	/* Send to upper layer */
+					dev->last_rx = jiffies;
+					db->stats.rx_packets++;
+					db->stats.rx_bytes += rxlen;
+				}
+			} else {
+				/* Reuse SKB buffer when the packet is error */
+				DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
+				dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
+			}
+		}
+
+		rxptr = (struct rx_desc *) rxptr->next_rx_desc;
+	}
+
+	db->rx_ready_ptr = rxptr;
+
+}
+
+/*
+   Get statistics from driver.
+ */
+static struct enet_statistics *dmfe_get_stats(struct device *dev)
+{
+	struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv;
+	DMFE_DBUG(0, "dmfe_get_stats", 0);
+	return &db->stats;
+}
+
+/*
+   Set DM910X multicast address
+ */
+
+static void dmfe_set_filter_mode(struct device *dev)
+{
+	struct dmfe_board_info *db = dev->priv;
+
+	DMFE_DBUG(0, "dmfe_set_filter_mode()", 0);
+
+	if (dev->flags & IFF_PROMISC) {
+		DMFE_DBUG(0, "Enable PROM Mode", 0);
+		db->cr6_data |= CR6_PM | CR6_PBF;
+		update_cr6(db->cr6_data, db->ioaddr);
+		return;
+	}
+	if (dev->flags & IFF_ALLMULTI || dev->mc_count > DMFE_MAX_MULTICAST) {
+		DMFE_DBUG(0, "Pass all multicast address", dev->mc_count);
+		db->cr6_data &= ~(CR6_PM | CR6_PBF);
+		db->cr6_data |= CR6_PAM;
+		return;
+	}
+	DMFE_DBUG(0, "Set multicast address", dev->mc_count);
+	if (db->chip_id == PCI_DM9132_ID)
+		dm9132_id_table(dev, dev->mc_count);	/* DM9132 */
+	else
+		send_filter_frame(dev, dev->mc_count);	/* DM9102/DM9102A */
+}
+
+/*
+   Process the upper socket ioctl command
+ */
+
+static int dmfe_do_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	DMFE_DBUG(0, "dmfe_do_ioctl()", 0);
+	return -EINVAL;
+}
+
+/*
+   A periodic timer routine
+   Dynamic media sense, allocated Rx buffer...
+ */
+ 
+static void dmfe_timer(unsigned long data)
+{
+	u32 tmp_cr8;
+	unsigned char tmp_cr12;
+	struct device *dev = (struct device *) data;
+	struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv;
+
+	DMFE_DBUG(0, "dmfe_timer()", 0);
+
+	/* Do reset now */
+	if (db->in_reset_state)
+		return;
+
+	/* Operating Mode Check */
+	if ((db->dm910x_chk_mode & 0x1) && (db->stats.rx_packets > MAX_CHECK_PACKET)) {
+		db->dm910x_chk_mode = 0x4;
+	}
+	/* Dynamic reset DM910X : system error or transmit time-out */
+	tmp_cr8 = inl(db->ioaddr + DCR8);
+	if ((db->interval_rx_cnt == 0) && (tmp_cr8)) {
+		db->wait_reset = 1;
+		/* printk("CR8 %x, Interval Rx %x\n", tmp_cr8, db->interval_rx_cnt); */
+	}
+	/* Receiving Traffic check */
+	if (db->interval_rx_cnt > RX_MAX_TRAFFIC)
+		db->cr7_data = 0x1a28d;
+	else
+		db->cr7_data = 0x1a2cd;
+	outl(db->cr7_data, db->ioaddr + DCR7);
+
+	db->interval_rx_cnt = 0;
+
+	if (db->wait_reset | (db->tx_packet_cnt &&
+			      ((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) | (db->rx_error_cnt > 3)) {
+		/*
+		   printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start);
+		 */
+		DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx moniotr step1", db->tx_packet_cnt);
+		dmfe_dynamic_reset(dev);
+		db->timer.expires = DMFE_TIMER_WUT;
+		add_timer(&db->timer);
+		return;
+	}
+	db->rx_error_cnt = 0;	/* Clear previos counter */
+
+	/* Link status check, Dynamic media type change */
+	if (db->chip_id == PCI_DM9132_ID)
+		tmp_cr12 = inb(db->ioaddr + DCR9 + 3);	/* DM9132 */
+	else
+		tmp_cr12 = inb(db->ioaddr + DCR12);	/* DM9102/DM9102A */
+
+	if (((db->chip_id == PCI_DM9102_ID) && (db->chip_revesion == 0x02000030)) ||
+	    ((db->chip_id == PCI_DM9132_ID) && (db->chip_revesion == 0x02000010))) {
+		/* DM9102A Chip */
+		if (tmp_cr12 & 2)
+			tmp_cr12 = 0x0;		/* Link failed */
+		else
+			tmp_cr12 = 0x3;		/* Link OK */
+	}
+	if (!(tmp_cr12 & 0x3) && !db->link_failed) {
+		/* Link Failed */
+		DMFE_DBUG(0, "Link Failed", tmp_cr12);
+		db->link_failed = 1;
+		phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id);	/* reset Phy */
+
+		/* 10/100M link failed, used 1M Home-Net */
+		db->cr6_data |= 0x00040000;	/* CR6 bit18 = 1, select Home-Net */
+		db->cr6_data &= ~0x00000200;	/* CR6 bit9 =0, half duplex mode */
+		update_cr6(db->cr6_data, db->ioaddr);
+
+		/* For DM9801 : PHY ID1 0181h, PHY ID2 B900h */
+		db->phy_id2 = phy_read(db->ioaddr, db->phy_addr, 3, db->chip_id);
+		if (db->phy_id2 == 0xb900)
+			phy_write(db->ioaddr, db->phy_addr, 25, 0x7e08, db->chip_id);
+	} else if ((tmp_cr12 & 0x3) && db->link_failed) {
+		DMFE_DBUG(0, "Link link OK", tmp_cr12);
+		db->link_failed = 0;
+
+		/* CR6 bit18=0, select 10/100M */
+		db->cr6_data &= ~0x00040000;
+		update_cr6(db->cr6_data, db->ioaddr);
+
+		/* Auto Sense Speed */
+		if (db->media_mode & DMFE_AUTO)
+			dmfe_sense_speed(db);
+		dmfe_process_mode(db);
+		update_cr6(db->cr6_data, db->ioaddr);
+		/* SHOW_MEDIA_TYPE(db->op_mode); */
+	}
+	/* reallocated rx descriptor buffer */
+	if (db->rx_avail_cnt < RX_DESC_CNT)
+		allocated_rx_buffer(db);
+
+	/* Timer active again */
+	db->timer.expires = DMFE_TIMER_WUT;
+	add_timer(&db->timer);
+}
+
+/*
+   Dynamic reset the DM910X board
+   Stop DM910X board
+   Free Tx/Rx allocated memory
+   Reset DM910X board
+   Re-initialize DM910X board
+ */
+
+static void dmfe_dynamic_reset(struct device *dev)
+{
+	struct dmfe_board_info *db = dev->priv;
+
+	DMFE_DBUG(0, "dmfe_dynamic_reset()", 0);
+
+	/* Enter dynamic reset route */
+	db->in_reset_state = 1;
+
+	/* Disable upper layer interface */
+	dev->tbusy = 1;		/* transmit packet disable */
+	dev->start = 0;		/* interface not ready */
+
+	db->cr6_data &= ~(CR6_RXSC | CR6_TXSC);		/* Disable Tx/Rx */
+	update_cr6(db->cr6_data, dev->base_addr);
+
+	/* Free Rx Allocate buffer */
+	dmfe_free_rxbuffer(db);
+
+	/* system variable init */
+	db->tx_packet_cnt = 0;
+	db->rx_avail_cnt = 0;
+	db->link_failed = 0;
+	db->wait_reset = 0;
+	db->rx_error_cnt = 0;
+
+	/* Re-initilize DM910X board */
+	dmfe_init_dm910x(dev);
+
+	/* Restart upper layer interface */
+	dev->tbusy = 0;		/* Can transmit packet */
+	dev->start = 1;		/* interface ready */
+
+	/* Leave dynamic reser route */
+	db->in_reset_state = 0;
+}
+
+/*
+   free all allocated rx buffer 
+ */
+static void dmfe_free_rxbuffer(struct dmfe_board_info *db)
+{
+	DMFE_DBUG(0, "dmfe_free_rxbuffer()", 0);
+
+	/* free allocated rx buffer */
+	while (db->rx_avail_cnt) {
+		dev_kfree_skb((void *) (db->rx_ready_ptr->rx_skb_ptr));
+		db->rx_ready_ptr = (struct rx_desc *) db->rx_ready_ptr->next_rx_desc;
+		db->rx_avail_cnt--;
+	}
+}
+
+/*
+   Reused the SK buffer
+ */
+
+static void dmfe_reused_skb(struct dmfe_board_info *db, struct sk_buff *skb)
+{
+	struct rx_desc *rxptr = db->rx_insert_ptr;
+
+	if (!(rxptr->rdes0 & 0x80000000)) {
+		rxptr->rx_skb_ptr = (u32) skb;
+		rxptr->rdes2 = virt_to_bus(skb->tail);
+		rxptr->rdes0 = 0x80000000;
+		db->rx_avail_cnt++;
+		db->rx_insert_ptr = (struct rx_desc *) rxptr->next_rx_desc;
+	} else
+		DMFE_DBUG(0, "SK Buffer reused method error", db->rx_avail_cnt);
+}
+
+/*
+   Initialize transmit/Receive descriptor 
+   Using Chain structure, and allocated Tx/Rx buffer
+ */
+static void dmfe_descriptor_init(struct dmfe_board_info *db, unsigned long ioaddr)
+{
+	struct tx_desc *tmp_tx;
+	struct rx_desc *tmp_rx;
+	unsigned char *tmp_buf;
+	int i;
+
+	DMFE_DBUG(0, "dmfe_descriptor_init()", 0);
+
+	/* tx descriptor start pointer */
+	db->tx_insert_ptr = db->first_tx_desc;
+	db->tx_remove_ptr = db->first_tx_desc;
+	outl(virt_to_bus(db->first_tx_desc), ioaddr + DCR4);	/* Init CR4 */
+
+	/* rx descriptor start pointer */
+	db->first_rx_desc = (struct rx_desc *)
+	    ((u32) db->first_tx_desc + sizeof(struct rx_desc) * TX_DESC_CNT);
+	db->rx_insert_ptr = db->first_rx_desc;
+	db->rx_ready_ptr = db->first_rx_desc;
+	outl(virt_to_bus(db->first_rx_desc), ioaddr + DCR3);	/* Init CR3 */
+
+	/* Init Transmit chain */
+	tmp_buf = db->buf_pool_start;
+	for (tmp_tx = db->first_tx_desc, i = 0; i < TX_DESC_CNT; i++, tmp_tx++) {
+		tmp_tx->tx_buf_ptr = (u32) tmp_buf;
+		tmp_tx->tdes0 = 0;
+		tmp_tx->tdes1 = 0x81000000;	/* IC, chain */
+		tmp_tx->tdes2 = (u32) virt_to_bus(tmp_buf);
+		tmp_tx->tdes3 = (u32) virt_to_bus(tmp_tx) + sizeof(struct tx_desc);
+		tmp_tx->next_tx_desc = (u32) ((u32) tmp_tx + sizeof(struct tx_desc));
+		tmp_buf = (unsigned char *) ((u32) tmp_buf + TX_BUF_ALLOC);
+	}
+	(--tmp_tx)->tdes3 = (u32) virt_to_bus(db->first_tx_desc);
+	tmp_tx->next_tx_desc = (u32) db->first_tx_desc;
+
+	/* Init Receive descriptor chain */
+	for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT; i++, tmp_rx++) {
+		tmp_rx->rdes0 = 0;
+		tmp_rx->rdes1 = 0x01000600;
+		tmp_rx->rdes3 = (u32) virt_to_bus(tmp_rx) + sizeof(struct rx_desc);
+		tmp_rx->next_rx_desc = (u32) ((u32) tmp_rx + sizeof(struct rx_desc));
+	}
+	(--tmp_rx)->rdes3 = (u32) virt_to_bus(db->first_rx_desc);
+	tmp_rx->next_rx_desc = (u32) db->first_rx_desc;
+
+	/* pre-allocated Rx buffer */
+	allocated_rx_buffer(db);
+}
+
+/*
+   Update CR6 vaule
+   Firstly stop DM910X , then written value and start
+ */
+
+static void update_cr6(u32 cr6_data, unsigned long ioaddr)
+{
+	u32 cr6_tmp;
+
+	cr6_tmp = cr6_data & ~0x2002;	/* stop Tx/Rx */
+	outl(cr6_tmp, ioaddr + DCR6);
+	DELAY_5US;
+	outl(cr6_data, ioaddr + DCR6);
+	cr6_tmp = inl(ioaddr + DCR6);
+	/* printk("CR6 update %x ", cr6_tmp); */
+}
+
+/* Send a setup frame for DM9132
+   This setup frame initilize DM910X addres filter mode
+ */
+
+static void dm9132_id_table(struct device *dev, int mc_cnt)
+{
+	struct dev_mc_list *mcptr;
+	u16 *addrptr;
+	unsigned long ioaddr = dev->base_addr + 0xc0;	/* ID Table */
+	u32 hash_val;
+	u16 i, hash_table[4];
+
+	DMFE_DBUG(0, "dm9132_id_table()", 0);
+
+	/* Node address */
+	addrptr = (u16 *) dev->dev_addr;
+	outw(addrptr[0], ioaddr);
+	ioaddr += 4;
+	outw(addrptr[1], ioaddr);
+	ioaddr += 4;
+	outw(addrptr[2], ioaddr);
+	ioaddr += 4;
+
+	/* Clear Hash Table */
+	for (i = 0; i < 4; i++)
+		hash_table[i] = 0x0;
+
+	/* broadcast address */
+	hash_table[3] = 0x8000;
+
+	/* the multicast address in Hash Table : 64 bits */
+	for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
+		hash_val = cal_CRC((char *) mcptr->dmi_addr, 6, 0) & 0x3f;
+		hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
+	}
+
+	/* Write the hash table to MAC MD table */
+	for (i = 0; i < 4; i++, ioaddr += 4) {
+		outw(hash_table[i], ioaddr);
+	}
+}
+
+/* Send a setup frame for DM9102/DM9102A
+   This setup frame initilize DM910X addres filter mode
+ */
+
+static void send_filter_frame(struct device *dev, int mc_cnt)
+{
+	struct dmfe_board_info *db = dev->priv;
+	struct dev_mc_list *mcptr;
+	struct tx_desc *txptr;
+	u16 *addrptr;
+	u32 *suptr;
+	int i;
+
+	DMFE_DBUG(0, "send_filetr_frame()", 0);
+
+	txptr = db->tx_insert_ptr;
+	suptr = (u32 *) txptr->tx_buf_ptr;
+
+	/* Node address */
+	addrptr = (u16 *) dev->dev_addr;
+	*suptr++ = addrptr[0];
+	*suptr++ = addrptr[1];
+	*suptr++ = addrptr[2];
+
+	/* broadcast address */
+	*suptr++ = 0xffff;
+	*suptr++ = 0xffff;
+	*suptr++ = 0xffff;
+
+	/* fit the multicast address */
+	for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
+		addrptr = (u16 *) mcptr->dmi_addr;
+		*suptr++ = addrptr[0];
+		*suptr++ = addrptr[1];
+		*suptr++ = addrptr[2];
+	}
+
+	for (; i < 14; i++) {
+		*suptr++ = 0xffff;
+		*suptr++ = 0xffff;
+		*suptr++ = 0xffff;
+	}
+
+	/* prepare the setup frame */
+	db->tx_packet_cnt++;
+	dev->tbusy = 1;
+	txptr->tdes1 = 0x890000c0;
+	txptr->tdes0 = 0x80000000;
+	db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
+
+	update_cr6(db->cr6_data | 0x2000, dev->base_addr);
+	outl(0x1, dev->base_addr + DCR1);
+	update_cr6(db->cr6_data, dev->base_addr);
+	dev->trans_start = jiffies;
+
+}
+
+/*
+   Allocate rx buffer,
+   As possible as allocated maxiumn Rx buffer
+ */
+
+static void allocated_rx_buffer(struct dmfe_board_info *db)
+{
+	struct rx_desc *rxptr;
+	struct sk_buff *skb;
+
+	rxptr = db->rx_insert_ptr;
+
+	while (db->rx_avail_cnt < RX_DESC_CNT) {
+		if ((skb = alloc_skb(RX_ALLOC_SIZE, GFP_ATOMIC)) == NULL)
+			break;
+		rxptr->rx_skb_ptr = (u32) skb;
+		rxptr->rdes2 = virt_to_bus(skb->tail);
+		rxptr->rdes0 = 0x80000000;
+		rxptr = (struct rx_desc *) rxptr->next_rx_desc;
+		db->rx_avail_cnt++;
+	}
+
+	db->rx_insert_ptr = rxptr;
+}
+
+/*
+   Read one word data from the serial ROM
+ */
+
+static u16 read_srom_word(unsigned long ioaddr, int offset)
+{
+	int i;
+	u16 srom_data = 0;
+	unsigned long cr9_ioaddr = ioaddr + DCR9;
+
+	outl(CR9_SROM_READ, cr9_ioaddr);
+	outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+
+	/* Send the Read Command 110b */
+	SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr);
+	SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr);
+	SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr);
+
+	/* Send the offset */
+	for (i = 5; i >= 0; i--) {
+		srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0;
+		SROM_CLK_WRITE(srom_data, cr9_ioaddr);
+	}
+
+	outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+
+	for (i = 16; i > 0; i--) {
+		outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr);
+		DELAY_5US;
+		srom_data = (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1 : 0);
+		outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+		DELAY_5US;
+	}
+
+	outl(CR9_SROM_READ, cr9_ioaddr);
+	return srom_data;
+}
+
+/*
+   Auto sense the media mode
+ */
+static void dmfe_sense_speed(struct dmfe_board_info *db)
+{
+	int i;
+	u16 phy_mode;
+
+	for (i = 1000; i; i--) {
+		DELAY_5US;
+		phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id);
+		if ((phy_mode & 0x24) == 0x24)
+			break;
+	}
+
+	if (i) {
+		if (db->chip_id == PCI_DM9132_ID)	/* DM9132 */
+			phy_mode = phy_read(db->ioaddr, db->phy_addr, 7, db->chip_id) & 0xf000;
+		else		/* DM9102/DM9102A */
+			phy_mode = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0xf000;
+		/* printk("Phy_mode %x ",phy_mode); */
+		switch (phy_mode) {
+		case 0x1000:
+			db->op_mode = DMFE_10MHF;
+			break;
+		case 0x2000:
+			db->op_mode = DMFE_10MFD;
+			break;
+		case 0x4000:
+			db->op_mode = DMFE_100MHF;
+			break;
+		case 0x8000:
+			db->op_mode = DMFE_100MFD;
+			break;
+		default:
+			db->op_mode = DMFE_10MHF;
+			DMFE_DBUG(0, "Media Type error, phy reg17", phy_mode);
+			break;
+		}
+	} else {
+		db->op_mode = DMFE_10MHF;
+		DMFE_DBUG(0, "Link Failed :", phy_mode);
+	}
+}
+
+/*
+   Process op-mode
+   AUTO mode : PHY controller in Auto-negotiation Mode
+   Force mode: PHY controller in force mode with HUB
+   N-way force capability with SWITCH
+ */
+static void dmfe_process_mode(struct dmfe_board_info *db)
+{
+	u16 phy_reg;
+
+	/* Full Duplex Mode Check */
+	db->cr6_data &= ~CR6_FDM;	/* Clear Full Duplex Bit */
+	if (db->op_mode & 0x4)
+		db->cr6_data |= CR6_FDM;
+
+	if (!(db->media_mode & DMFE_AUTO)) {	/* Force Mode Check */
+		/* User force the media type */
+		phy_reg = phy_read(db->ioaddr, db->phy_addr, 5, db->chip_id);
+		/* printk("Nway phy_reg5 %x ",phy_reg); */
+		if (phy_reg & 0x1) {
+			/* parter own the N-Way capability */
+			phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x1e0;
+			switch (db->op_mode) {
+			case DMFE_10MHF:
+				phy_reg |= 0x20;
+				break;
+			case DMFE_10MFD:
+				phy_reg |= 0x40;
+				break;
+			case DMFE_100MHF:
+				phy_reg |= 0x80;
+				break;
+			case DMFE_100MFD:
+				phy_reg |= 0x100;
+				break;
+			}
+			phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id);
+		} else {
+			/* parter without the N-Way capability */
+			switch (db->op_mode) {
+			case DMFE_10MHF:
+				phy_reg = 0x0;
+				break;
+			case DMFE_10MFD:
+				phy_reg = 0x100;
+				break;
+			case DMFE_100MHF:
+				phy_reg = 0x2000;
+				break;
+			case DMFE_100MFD:
+				phy_reg = 0x2100;
+				break;
+			}
+			phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id);
+		}
+	}
+}
+
+/*
+   Write a word to Phy register
+ */
+static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, u16 phy_data, u32 chip_id)
+{
+	u16 i;
+	unsigned long ioaddr;
+
+	if (chip_id == PCI_DM9132_ID) {
+		ioaddr = iobase + 0x80 + offset * 4;
+		outw(phy_data, ioaddr);
+	} else {
+		/* DM9102/DM9102A Chip */
+		ioaddr = iobase + DCR9;
+
+		/* Send 33 synchronization clock to Phy controller */
+		for (i = 0; i < 35; i++)
+			phy_write_1bit(ioaddr, PHY_DATA_1);
+
+		/* Send start command(01) to Phy */
+		phy_write_1bit(ioaddr, PHY_DATA_0);
+		phy_write_1bit(ioaddr, PHY_DATA_1);
+
+		/* Send write command(01) to Phy */
+		phy_write_1bit(ioaddr, PHY_DATA_0);
+		phy_write_1bit(ioaddr, PHY_DATA_1);
+
+		/* Send Phy addres */
+		for (i = 0x10; i > 0; i = i >> 1)
+			phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
+
+		/* Send register addres */
+		for (i = 0x10; i > 0; i = i >> 1)
+			phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
+
+		/* written trasnition */
+		phy_write_1bit(ioaddr, PHY_DATA_1);
+		phy_write_1bit(ioaddr, PHY_DATA_0);
+
+		/* Write a word data to PHY controller */
+		for (i = 0x8000; i > 0; i >>= 1)
+			phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0);
+	}
+}
+
+/*
+   Read a word data from phy register
+ */
+static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset, u32 chip_id)
+{
+	int i;
+	u16 phy_data;
+	unsigned long ioaddr;
+
+	if (chip_id == PCI_DM9132_ID) {
+		/* DM9132 Chip */
+		ioaddr = iobase + 0x80 + offset * 4;
+		phy_data = inw(ioaddr);
+	} else {
+		/* DM9102/DM9102A Chip */
+
+		ioaddr = iobase + DCR9;
+		/* Send 33 synchronization clock to Phy controller */
+		for (i = 0; i < 35; i++)
+			phy_write_1bit(ioaddr, PHY_DATA_1);
+
+		/* Send start command(01) to Phy */
+		phy_write_1bit(ioaddr, PHY_DATA_0);
+		phy_write_1bit(ioaddr, PHY_DATA_1);
+
+		/* Send read command(10) to Phy */
+		phy_write_1bit(ioaddr, PHY_DATA_1);
+		phy_write_1bit(ioaddr, PHY_DATA_0);
+
+		/* Send Phy addres */
+		for (i = 0x10; i > 0; i = i >> 1)
+			phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
+
+		/* Send register addres */
+		for (i = 0x10; i > 0; i = i >> 1)
+			phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
+
+		/* Skip transition state */
+		phy_read_1bit(ioaddr);
+
+		/* read 16bit data */
+		for (phy_data = 0, i = 0; i < 16; i++) {
+			phy_data <<= 1;
+			phy_data |= phy_read_1bit(ioaddr);
+		}
+	}
+
+	return phy_data;
+}
+
+/*
+   Write one bit data to Phy Controller
+ */
+static void phy_write_1bit(unsigned long ioaddr, u32 phy_data)
+{
+	outl(phy_data, ioaddr);	/* MII Clock Low */
+	DELAY_1US;
+	outl(phy_data | MDCLKH, ioaddr);	/* MII Clock High */
+	DELAY_1US;
+	outl(phy_data, ioaddr);	/* MII Clock Low */
+	DELAY_1US;
+}
+
+/*
+   Read one bit phy data from PHY controller
+ */
+static u16 phy_read_1bit(unsigned long ioaddr)
+{
+	u16 phy_data;
+
+	outl(0x50000, ioaddr);
+	DELAY_1US;
+	phy_data = (inl(ioaddr) >> 19) & 0x1;
+	outl(0x40000, ioaddr);
+	DELAY_1US;
+
+	return phy_data;
+}
+
+/*
+   Calculate the CRC valude of the Rx packet
+   flag = 1 : return the reverse CRC (for the received packet CRC)
+   0 : return the normal CRC (for Hash Table index)
+ */
+unsigned long cal_CRC(unsigned char *Data, unsigned int Len, u8 flag)
+{
+	unsigned long Crc = 0xffffffff;
+
+	while (Len--) {
+		Crc = CrcTable[(Crc ^ *Data++) & 0xFF] ^ (Crc >> 8);
+	}
+
+	if (flag)
+		return ~Crc;
+	else
+		return Crc;
+}
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw");
+MODULE_DESCRIPTION("Davicom DM910X fast ethernet driver");
+MODULE_PARM(debug, "i");
+MODULE_PARM(mode, "i");
+MODULE_PARM(cr6set, "i");
+MODULE_PARM(chkmode, "i");
+
+/* Description: 
+   when user used insmod to add module, system invoked init_module()
+   to initilize and register.
+ */
+int init_module(void)
+{
+	DMFE_DBUG(0, "init_module() ", debug);
+
+	if (debug)
+		dmfe_debug = debug;	/* set debug flag */
+	if (cr6set)
+		dmfe_cr6_user_set = cr6set;
+
+	switch (mode) {
+	case 0:
+	case 1:
+	case 4:
+	case 5:
+		dmfe_media_mode = mode;
+		break;
+	default:
+		dmfe_media_mode = 8;
+		break;
+	}
+
+	return dmfe_probe(0);	/* search board and register */
+}
+
+/* Description: 
+   when user used rmmod to delete module, system invoked clean_module()
+   to  un-register device.
+ */
+void cleanup_module(void)
+{
+	struct device *next_dev;
+	struct dmfe_board_info *db;
+
+	DMFE_DBUG(0, "clean_module()", 0);
+
+	while (dmfe_root_dev) {
+		next_dev = ((struct dmfe_board_info *) dmfe_root_dev->priv)->next_dev;
+		unregister_netdev(dmfe_root_dev);
+		db = dmfe_root_dev->priv;
+		release_region(dmfe_root_dev->base_addr, CHK_IO_SIZE(db->chip_id, db->chip_revesion));
+		kfree(db);	/* free board information */
+		kfree(dmfe_root_dev);	/* free device structure */
+		dmfe_root_dev = next_dev;
+	}
+
+	DMFE_DBUG(0, "clean_module() exit", 0);
+}
+
+#endif				/* MODULE */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/char/random.c linux.15p1/drivers/char/random.c
--- linux.vanilla/drivers/char/random.c	Wed Oct 20 01:12:35 1999
+++ linux.15p1/drivers/char/random.c	Wed Jan 12 01:14:43 2000
@@ -1328,6 +1328,7 @@
 			schedule();
 			continue;
 		}
+		current->state = TASK_RUNNING;
 		n = extract_entropy(&random_state, buf, n, 1);
 		if (n < 0) {
 			retval = n;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/82596.c linux.15p1/drivers/net/82596.c
--- linux.vanilla/drivers/net/82596.c	Thu Jan  7 16:46:59 1999
+++ linux.15p1/drivers/net/82596.c	Wed Jan 12 01:16:34 2000
@@ -283,7 +283,7 @@
 		volatile u32 i = *(volatile u32 *) (dev->base_addr);
 	}
 #endif
-#ifdef CONFIG_APRICOT_i596
+#ifdef CONFIG_APRICOT
 	if (MACH_IS_APRICOT) {
 		outw(0, (short) (dev->base_addr) + 4);
 	}
@@ -444,7 +444,7 @@
 	if (MACH_IS_BVME6000)
 		lp->scp.sysbus = 0x0000004c;
 #endif
-#ifdef CONFIG_APRICOT_i596
+#ifdef CONFIG_APRICOT
 	if (MACH_IS_APRICOT)
 		lp->scp.sysbus = 0x00440000;
 #endif
@@ -914,7 +914,7 @@
 		dev->irq = (unsigned) BVME_IRQ_I596;
 	}
 #endif
-#ifdef CONFIG_APRICOT_INTEL
+#ifdef CONFIG_APRICOT
 	int checksum = 0;
 	int ioaddr = 0x300;
 
@@ -1163,7 +1163,7 @@
 		*ethirq = 3;
 	}
 #endif
-#ifdef CONFIG_APRICOT_INTEL
+#ifdef CONFIG_APRICOT
 	(void) inb(ioaddr + 0x10);
 	outb(4, ioaddr + 0xf);
 #endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/eepro100.c linux.15p1/drivers/net/eepro100.c
--- linux.vanilla/drivers/net/eepro100.c	Wed Oct 20 01:12:39 1999
+++ linux.15p1/drivers/net/eepro100.c	Wed Jan 12 01:39:50 2000
@@ -1,14 +1,14 @@
-/* drivers/net/eepro100.c: An Intel i82557 Ethernet driver for Linux. */
+/* drivers/net/eepro100.c: An Intel i82557-559 Ethernet driver for Linux. */
 /*
    NOTICE: this version tested with kernels 1.3.72 and later only!
-	Written 1996-1998 by Donald Becker.
+	Written 1996-1999 by Donald Becker.
 
 	This software may be used and distributed according to the terms
 	of the GNU Public License, incorporated herein by reference.
 
-	This driver is for the Intel EtherExpress Pro 100B boards.
-	It should work with other i82557 and i82558 boards.
-	To use a built-in driver, install as drivers/net/eepro100.c.
+	This driver is for the Intel EtherExpress Pro100 (Speedo3) design.
+	It should work with all i82557/558/559 boards.
+
 	To use as a module, use the compile-command at the end of the file.
 
 	The author may be reached as becker@CESDIS.usra.edu, or C/O
@@ -16,15 +16,18 @@
 	   Code 930.5, NASA Goddard Space Flight Center, Greenbelt MD 20771
 	For updates see
 		http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html
-	There is also a mailing list based at
+	For installation instructions
+		http://cesdis.gsfc.nasa.gov/linux/misc/modules.html
+	There is a Majordomo mailing list based at
 		linux-eepro100@cesdis.gsfc.nasa.gov
 */
 
 static const char *version =
-"eepro100.c:v1.06 10/16/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n";
+"eepro100.c:v1.09j-t 9/29/99 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n"
+"eepro100.c: $Revision: 1.18 $ 1999/12/29 Modified by Andrey V. Savochkin <saw@msu.ru>\n";
 
 /* A few user-configurable values that apply to all boards.
-   First set are undocumented and spelled per Intel recommendations. */
+   First set is undocumented and spelled per Intel recommendations. */
 
 static int congenb = 0;		/* Enable congestion control in the DP83840. */
 static int txfifo = 8;		/* Tx FIFO threshold in 4 byte units, 0-15 */
@@ -38,14 +41,52 @@
 static int rx_copybreak = 200;
 
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 200;
+static int max_interrupt_work = 20;
 
 /* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */
 static int multicast_filter_limit = 64;
 
-#include <linux/module.h>
+/* 'options' is used to pass a transceiver override or full-duplex flag
+   e.g. "options=16" for FD, "options=32" for 100mbps-only. */
+static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+#ifdef MODULE
+static int debug = -1;			/* The debug level */
+#endif
+
+/* A few values that may be tweaked. */
+/* The ring sizes should be a power of two for efficiency. */
+#define TX_RING_SIZE	32
+#define RX_RING_SIZE	32
+/* How much slots multicast filter setup may take.
+   Do not descrease without changing set_rx_mode() implementaion. */
+#define TX_MULTICAST_SIZE   2
+#define TX_MULTICAST_RESERV (TX_MULTICAST_SIZE*2)
+/* Actual number of TX packets queued, must be
+   <= TX_RING_SIZE-TX_MULTICAST_RESERV. */
+#define TX_QUEUE_LIMIT  (TX_RING_SIZE-TX_MULTICAST_RESERV)
+/* Hysteresis marking queue as no longer full. */
+#define TX_QUEUE_UNFULL (TX_QUEUE_LIMIT-4)
+
+/* Operational parameters that usually are not changed. */
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT		(2*HZ)
+/* Size of an pre-allocated Rx buffer: <Ethernet MTU> + slack.*/
+#define PKT_BUF_SZ		1536
+
+#if !defined(__OPTIMIZE__)  ||  !defined(__KERNEL__)
+#warning  You must compile this file with the correct options!
+#warning  See the last lines of the source file.
+#error You must compile this driver with "-O".
+#endif
 
 #include <linux/version.h>
+#include <linux/module.h>
+#if defined(MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/timer.h>
@@ -53,19 +94,27 @@
 #include <linux/ioport.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
+#ifdef HAS_PCI_NETIF
+#include "pci-netif.h"
+#else
 #include <linux/pci.h>
+#endif
+#include <linux/pci.h>
+#if LINUX_VERSION_CODE >= 0x20312
+#include <linux/spinlock.h>
+#else
+#include <asm/spinlock.h>
+#endif
+
+#include <asm/bitops.h>
+#include <asm/io.h>
+
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/delay.h>
 
-#include <asm/spinlock.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-
-/*
- * Module documentation
- */
+#if defined(MODULE)
 MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
 MODULE_DESCRIPTION("Intel i82557/i82558 PCI EtherExpressPro driver");
 MODULE_PARM(debug, "i");
@@ -79,16 +128,35 @@
 MODULE_PARM(rx_copybreak, "i");
 MODULE_PARM(max_interrupt_work, "i");
 MODULE_PARM(multicast_filter_limit, "i");
+#endif
 
 #define RUN_AT(x) (jiffies + (x))
+/* Condensed bus+endian portability operations. */
+#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))
+
+#if LINUX_VERSION_CODE < 0x020314
+#define net_device              device
+#define pci_base_address(p, n)  (p)->base_address[n]
+#else
+#define pci_base_address(p, n)  (p)->resource[n].start
+#endif
 
-#define dev_free_skb(skb) dev_kfree_skb(skb);
+#define dev_free_skb(skb)       dev_kfree_skb(skb);
+#if ! defined(HAS_NETIF_QUEUE)
+#define netif_wake_queue(dev)   do { \
+									clear_bit(0, (void*)&dev->tbusy); \
+									mark_bh(NET_BH); \
+								} while(0)
+#define netif_start_queue(dev)  clear_bit(0, (void*)&dev->tbusy)
+#define netif_stop_queue(dev)   set_bit(0, (void*)&dev->tbusy)
+#endif
 
 /* The total I/O port extent of the board.
    The registers beyond 0x18 only exist on the i82558. */
 #define SPEEDO3_TOTAL_SIZE 0x20
 
-int speedo_debug = 0;
+int speedo_debug = 1;
 
 /*
 				Theory of Operation
@@ -196,24 +264,6 @@
 is non-trivial, and the larger copy might flush the cache of useful data, so
 we pass up the skbuff the packet was received into.
 
-IIID. Synchronization
-The driver runs as two independent, single-threaded flows of control.  One
-is the send-packet routine, which enforces single-threaded use by the
-dev->tbusy flag.  The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-The send packet thread has partial control over the Tx ring and 'dev->tbusy'
-flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished otherwise it sets
-the 'sp->tx_full' flag.
-
-The interrupt handler has exclusive control over the Rx ring and records stats
-from the Tx ring.  (The Tx-done interrupt can't be selectively turned off, so
-we can't avoid the interrupt overhead by having the Tx routine reap the Tx
-stats.)	 After reaping the stats, it marks the queue entry as empty by setting
-the 'base' to zero.	 Iff the 'sp->tx_full' flag is set, it clears both the
-tx_full and tbusy flags.
-
 IV. Notes
 
 Thanks to Steve Williams of Intel for arranging the non-disclosure agreement
@@ -222,15 +272,55 @@
 
 */
 
-/* A few values that may be tweaked. */
-/* The ring sizes should be a power of two for efficiency. */
-#define TX_RING_SIZE	16		/* Effectively 2 entries fewer. */
-#define RX_RING_SIZE	16
-/* Size of an pre-allocated Rx buffer: <Ethernet MTU> + slack.*/
-#define PKT_BUF_SZ		1536
+/* This table drives the PCI probe routines. */
+static struct net_device *speedo_found1(int pci_bus, int pci_devfn, long ioaddr, int irq, int chip_idx, int fnd_cnt);
 
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  ((800*HZ)/1000)
+#ifdef USE_IO
+#define SPEEDO_IOTYPE   PCI_USES_MASTER|PCI_USES_IO|PCI_ADDR1
+#define SPEEDO_SIZE		32
+#else
+#define SPEEDO_IOTYPE   PCI_USES_MASTER|PCI_USES_MEM|PCI_ADDR0
+#define SPEEDO_SIZE		0x1000
+#endif
+
+#if defined(HAS_PCI_NETIF)
+struct pci_id_info static pci_tbl[] = {
+	{ "Intel PCI EtherExpress Pro100",
+	  { 0x12298086, 0xffffffff,}, SPEEDO_IOTYPE, SPEEDO_SIZE,
+	  0, speedo_found1 },
+	{0,},						/* 0 terminated list. */
+};
+#else
+enum pci_flags_bit {
+	PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+	PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
+};
+struct pci_id_info {
+	const char *name;
+	u16	vendor_id, device_id, device_id_mask, flags;
+	int io_size;
+	struct net_device *(*probe1)(int pci_bus, int pci_devfn, long ioaddr, int irq, int chip_idx, int fnd_cnt);
+} static pci_tbl[] = {
+	{ "Intel PCI EtherExpress Pro100",
+	  0x8086, 0x1229, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 32, speedo_found1 },
+	{0,},						/* 0 terminated list. */
+};
+#endif
+
+#ifndef USE_IO
+#undef inb
+#undef inw
+#undef inl
+#undef outb
+#undef outw
+#undef outl
+#define inb readb
+#define inw readw
+#define inl readl
+#define outb writeb
+#define outw writew
+#define outl writel
+#endif
 
 /* How to wait for the command unit to accept a command.
    Typically this takes 0 ticks. */
@@ -241,10 +331,6 @@
 	while(inb(cmd_ioaddr) && --wait >= 0);
 }
 
-/* Operational parameter that usually are not changed. */
-
-/* The rest of these values should never change. */
-
 /* Offsets to the various registers.
    All accesses need not be longword aligned. */
 enum speedo_offsets {
@@ -257,34 +343,48 @@
 };
 /* Commands that can be put in a command list entry. */
 enum commands {
-	CmdNOp = 0, CmdIASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
-	CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7,
-	CmdSuspend = 0x4000,		/* Suspend after completion. */
-	CmdIntr = 0x2000,			/* Interrupt after completion. */
-	CmdTxFlex = 0x0008,			/* Use "Flexible mode" for CmdTx command. */
+	CmdNOp = 0, CmdIASetup = 0x10000, CmdConfigure = 0x20000,
+	CmdMulticastList = 0x30000, CmdTx = 0x40000, CmdTDR = 0x50000,
+	CmdDump = 0x60000, CmdDiagnose = 0x70000,
+	CmdSuspend = 0x40000000,	/* Suspend after completion. */
+	CmdIntr = 0x20000000,		/* Interrupt after completion. */
+	CmdTxFlex = 0x00080000,		/* Use "Flexible mode" for CmdTx command. */
 };
+/* Clear CmdSuspend (1<<30) atomically.
+   Otherwise the command status in the lower 16 bits may be reset after
+   an asynchronous change.  Previous driver version used separate 16 bit fields
+   for commands and statuses.  --SAW
+ */
+#ifdef __i386__
+#define speedo_fool_gcc(x) (*(volatile struct { int a[100]; } *)x)
+#define speedo_clear_mask(mask, addr) \
+__asm__ __volatile__("lock; andl %0,%1" \
+: : "r" (~(mask)),"m" (speedo_fool_gcc(addr)) : "memory")
+#define clear_suspend(cmd)  speedo_clear_mask(CmdSuspend, &(cmd)->cmd_status)
+#else
+#define clear_suspend(cmd)	(cmd)->cmd_status &= cpu_to_le32(~CmdSuspend)
+#endif
 
-/* The SCB accepts the following controls for the Tx and Rx units: */
-#define	 CU_START		0x0010
-#define	 CU_RESUME		0x0020
-#define	 CU_STATSADDR	0x0040
-#define	 CU_SHOWSTATS	0x0050	/* Dump statistics counters. */
-#define	 CU_CMD_BASE	0x0060	/* Base address to add to add CU commands. */
-#define	 CU_DUMPSTATS	0x0070	/* Dump then reset stats counters. */
-
-#define	 RX_START	0x0001
-#define	 RX_RESUME	0x0002
-#define	 RX_ABORT	0x0004
-#define	 RX_ADDR_LOAD	0x0006
-#define	 RX_RESUMENR	0x0007
-#define INT_MASK	0x0100
-#define DRVR_INT	0x0200		/* Driver generated interrupt. */
+enum SCBCmdBits {
+     SCBMaskCmdDone=0x8000, SCBMaskRxDone=0x4000, SCBMaskCmdIdle=0x2000,
+     SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800, SCBMaskFlowCtl=0x0400,
+     SCBTriggerIntr=0x0200, SCBMaskAll=0x0100,
+     /* The rest are Rx and Tx commands. */
+     CUStart=0x0010, CUResume=0x0020, CUStatsAddr=0x0040, CUShowStats=0x0050,
+     CUCmdBase=0x0060,  /* CU Base address (set to zero) . */
+     CUDumpStats=0x0070, /* Dump then reset stats counters. */
+     RxStart=0x0001, RxResume=0x0002, RxAbort=0x0004, RxAddrLoad=0x0006,
+     RxResumeNoResources=0x0007,
+};
+
+enum SCBPort_cmds {
+	PortReset=0, PortSelfTest=1, PortPartialReset=2, PortDump=3,
+};
 
 /* The Speedo3 Rx and Tx frame/buffer descriptors. */
-struct descriptor {			/* A generic descriptor. */
-	s16 status;		/* Offset 0. */
-	s16 command;		/* Offset 2. */
-	u32 link;					/* struct descriptor *  */
+struct descriptor {			    /* A generic descriptor. */
+	s32 cmd_status;				/* All command and status fields. */
+	u32 link;				    /* struct descriptor *  */
 	unsigned char params[0];
 };
 
@@ -293,8 +393,7 @@
 	s32 status;
 	u32 link;					/* struct RxFD * */
 	u32 rx_buf_addr;			/* void * */
-	u16 count;
-	u16 size;
+	u32 count;
 };
 
 /* Selected elements of the Tx/RxFD.status word. */
@@ -302,7 +401,7 @@
 	RxComplete=0x8000, RxOK=0x2000,
 	RxErrCRC=0x0800, RxErrAlign=0x0400, RxErrTooBig=0x0200, RxErrSymbol=0x0010,
 	RxEth2Type=0x0020, RxNoMatch=0x0004, RxNoIAMatch=0x0002,
-	StatusComplete=0x8000,
+	TxUnderrun=0x1000,  StatusComplete=0x8000,
 };
 
 struct TxFD {					/* Transmit frame descriptor set. */
@@ -317,6 +416,14 @@
 	s32 tx_buf_size1;			/* Length of Tx frame. */
 };
 
+/* Multicast filter setting block.  --SAW */
+struct speedo_mc_block {
+	struct speedo_mc_block *next;
+	unsigned int tx;
+	char fill[16 - sizeof(struct speedo_mc_block *) - sizeof(unsigned int)];
+	struct descriptor frame;
+};
+
 /* Elements of the dump_statistics block. This block must be lword aligned. */
 struct speedo_stats {
 	u32 tx_good_frames;
@@ -338,49 +445,60 @@
 	u32 done_marker;
 };
 
+enum Rx_ring_state_bits {
+	RrNoMem=1, RrPostponed=2, RrNoResources=4, RrOOMReported=8,
+};
+
+/* Do not change the position (alignment) of the first few elements!
+   The later elements are grouped for cache locality. */
 struct speedo_private {
-	char devname[8];			/* Used only for kernel debugging. */
-	const char *product_name;
-	struct device *next_module;
-	spinlock_t lock;
-	struct TxFD	tx_ring[TX_RING_SIZE] 	/* Commands (usually CmdTxPacket). */
-				__attribute__ ((aligned (L1_CACHE_BYTES)));;
-	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
+	struct TxFD	tx_ring[TX_RING_SIZE];	/* Commands (usually CmdTxPacket). */
+	struct RxFD *rx_ringp[RX_RING_SIZE];	/* Rx descriptor, used as ring. */
+	/* The addresses of a Tx/Rx-in-place packets/buffers. */
 	struct sk_buff* tx_skbuff[TX_RING_SIZE];
-	struct descriptor  *last_cmd;	/* Last command sent. */
-	/* Rx descriptor ring & addresses of receive-in-place skbuffs. */
-	struct RxFD *rx_ringp[RX_RING_SIZE];
 	struct sk_buff* rx_skbuff[RX_RING_SIZE];
+	struct descriptor  *last_cmd;	/* Last command sent. */
+	unsigned int cur_tx, dirty_tx;	/* The ring entries to be free()ed. */
+	spinlock_t lock;				/* Group with Tx control cache line. */
+	u32 tx_threshold;					/* The value for txdesc.count. */
 	struct RxFD *last_rxf;	/* Last command sent. */
+	unsigned int cur_rx, dirty_rx;		/* The next free ring entry */
+	long last_rx_time;			/* Last Rx, in jiffies, to handle Rx hang. */
+	const char *product_name;
+	struct net_device *next_module;
+	void *priv_addr;					/* Unaligned address for kfree */
 	struct enet_statistics stats;
 	struct speedo_stats lstats;
+	int chip_id;
+	unsigned char pci_bus, pci_devfn, acpi_pwr;
 	struct timer_list timer;	/* Media selection timer. */
-	long last_rx_time;			/* Last Rx, in jiffies, to handle Rx hang. */
-	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
-	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
-	int mc_setup_frm_len;			 	/* The length of an allocated.. */
-	struct descriptor *mc_setup_frm; 	/* ..multicast setup frame. */
-	int mc_setup_busy;					/* Avoid double-use of setup frame. */
+	struct speedo_mc_block *mc_setup_head;/* Multicast setup frame list head. */
+	struct speedo_mc_block *mc_setup_tail;/* Multicast setup frame list tail. */
+	int in_interrupt;					/* Word-aligned dev->interrupt */
 	char rx_mode;						/* Current PROMISC/ALLMULTI setting. */
 	unsigned int tx_full:1;				/* The Tx queue is full. */
 	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
-	unsigned int default_port:1;		/* Last dev->if_port value. */
+	unsigned int flow_ctrl:1;			/* Use 802.3x flow control. */
 	unsigned int rx_bug:1;				/* Work around receiver hang errata. */
 	unsigned int rx_bug10:1;			/* Receiver might hang at 10mbps. */
 	unsigned int rx_bug100:1;			/* Receiver might hang at 100mbps. */
+	unsigned char default_port:8;		/* Last dev->if_port value. */
+	unsigned char rx_ring_state;		/* RX ring status flags. */
 	unsigned short phy[2];				/* PHY media interfaces available. */
+	unsigned short advertising;			/* Current PHY advertised caps. */
+	unsigned short partner;				/* Link partner caps. */
 };
 
 /* The parameters for a CmdConfigure operation.
    There are so many options that it would be difficult to document each bit.
    We mostly use the default or recommended settings. */
 const char i82557_config_cmd[22] = {
-	22, 0x08, 0, 0,  0, 0x80, 0x32, 0x03,  1, /* 1=Use MII  0=Use AUI */
+	22, 0x08, 0, 0,  0, 0, 0x32, 0x03,  1, /* 1=Use MII  0=Use AUI */
 	0, 0x2E, 0,  0x60, 0,
 	0xf2, 0x48,   0, 0x40, 0xf2, 0x80, 		/* 0x40=Force full-duplex */
 	0x3f, 0x05, };
 const char i82558_config_cmd[22] = {
-	22, 0x08, 0, 1,  0, 0x80, 0x22, 0x03,  1, /* 1=Use MII  0=Use AUI */
+	22, 0x08, 0, 1,  0, 0, 0x22, 0x03,  1, /* 1=Use MII  0=Use AUI */
 	0, 0x2E, 0,  0x60, 0x08, 0x88,
 	0x68, 0, 0x40, 0xf2, 0xBD, 		/* 0xBD->0xFD=Force full-duplex */
 	0x31, 0x05, };
@@ -394,35 +512,29 @@
 enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
 					 S80C24, I82555, DP83840A=10, };
 static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
+#define EE_READ_CMD		(6)
 
-static void speedo_found1(struct device *dev, long ioaddr, int irq,
-						  int card_idx);
-
-static int read_eeprom(long ioaddr, int location, int addr_len);
+static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len);
 static int mdio_read(long ioaddr, int phy_id, int location);
 static int mdio_write(long ioaddr, int phy_id, int location, int value);
-static int speedo_open(struct device *dev);
+static int speedo_open(struct net_device *dev);
+static void speedo_resume(struct net_device *dev);
 static void speedo_timer(unsigned long data);
-static void speedo_init_rx_ring(struct device *dev);
-static int speedo_start_xmit(struct sk_buff *skb, struct device *dev);
-static int speedo_rx(struct device *dev);
+static void speedo_init_rx_ring(struct net_device *dev);
+static void speedo_tx_timeout(struct net_device *dev);
+static int speedo_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void speedo_refill_rx_buffers(struct net_device *dev, int force);
+static int speedo_rx(struct net_device *dev);
+static void speedo_tx_buffer_gc(struct net_device *dev);
 static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-static int speedo_close(struct device *dev);
-static struct enet_statistics *speedo_get_stats(struct device *dev);
-static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd);
-static void set_rx_mode(struct device *dev);
+static int speedo_close(struct net_device *dev);
+static struct enet_statistics *speedo_get_stats(struct net_device *dev);
+static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void set_rx_mode(struct net_device *dev);
+static void speedo_show_state(struct net_device *dev);
 
 
 
-/* The parameters that may be passed in... */
-/* 'options' is used to pass a transceiver override or full-duplex flag
-   e.g. "options=16" for FD, "options=32" for 100mbps-only. */
-static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-#ifdef MODULE
-static int debug = -1;			/* The debug level */
-#endif
-
 #ifdef honor_default_port
 /* Optional driver feature to allow forcing the transceiver setting.
    Not recommended. */
@@ -431,9 +543,10 @@
 #endif
 
 /* A list of all installed Speedo devices, for removing the driver module. */
-static struct device *root_speedo_dev = NULL;
+static struct net_device *root_speedo_dev = NULL;
 
-int eepro100_init(struct device *dev)
+#if ! defined(HAS_PCI_NETIF)
+int eepro100_init(void)
 {
 	int cards_found = 0;
 	static int pci_index = 0;
@@ -443,6 +556,7 @@
 
 	for (; pci_index < 8; pci_index++) {
 		unsigned char pci_bus, pci_device_fn, pci_latency;
+		u32 pciaddr;
 		long ioaddr;
 		int irq;
 
@@ -455,11 +569,23 @@
 			break;
 		{
 			struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
-			ioaddr = pdev->base_address[1];		/* Use [0] to mem-map */
+#ifdef USE_IO
+			pciaddr = pci_base_address(pdev, 1);	/* Use [0] to mem-map */
+#else
+			pciaddr = pci_base_address(pdev, 0);
+#endif
 			irq = pdev->irq;
 		}
 		/* Remove I/O space marker in bit 0. */
-		ioaddr &= ~3UL;
+		if (pciaddr & 1) {
+			ioaddr = pciaddr & ~3UL;
+			if (check_region(ioaddr, 32))
+				continue;
+		} else if ((ioaddr = (long)ioremap(pciaddr & ~0xfUL, 0x1000)) == 0) {
+			printk(KERN_INFO "Failed to map PCI address %#x.\n",
+				   pciaddr);
+			continue;
+		}
 		if (speedo_debug > 2)
 			printk("Found Intel i82557 PCI Speedo at I/O %#lx, IRQ %d.\n",
 				   ioaddr, irq);
@@ -485,27 +611,30 @@
 		} else if (speedo_debug > 1)
 			printk("  PCI latency timer (CFLT) is %#x.\n", pci_latency);
 
-		speedo_found1(dev, ioaddr, irq, cards_found);
-		dev = NULL;
-		cards_found++;
+		if (speedo_found1(pci_bus, pci_device_fn, ioaddr, irq, 0, cards_found))
+			cards_found++;
 	}
 
 	return cards_found;
 }
+#endif
 
-static void speedo_found1(struct device *dev, long ioaddr, int irq,
-						  int card_idx)
+static struct net_device *speedo_found1(int pci_bus, int pci_devfn, 
+			  long ioaddr, int irq, int chip_idx, int card_idx)
 {
-	static int did_version = 0;			/* Already printed version info. */
+	struct net_device *dev;
 	struct speedo_private *sp;
-	char *product;
+	const char *product;
 	int i, option;
-	u16 eeprom[0x40];
-
+	u16 eeprom[0x100];
+	int acpi_idle_state = 0;
+#ifndef MODULE
+	static int did_version = 0;			/* Already printed version info. */
 	if (speedo_debug > 0  &&  did_version++ == 0)
 		printk(version);
+#endif
 
-	dev = init_etherdev(dev, sizeof(struct speedo_private));
+	dev = init_etherdev(NULL, sizeof(struct speedo_private));
 
 	if (dev->mem_start > 0)
 		option = dev->mem_start;
@@ -514,16 +643,31 @@
 	else
 		option = 0;
 
+#if defined(HAS_PCI_NETIF)
+	acpi_idle_state = acpi_set_pwr_state(pci_bus, pci_devfn, ACPI_D0);
+#endif
+
 	/* Read the station address EEPROM before doing the reset.
-	   Perhaps this should even be done before accepting the device,
-	   then we wouldn't have a device name with which to report the error. */
+	   Nominally his should even be done before accepting the device, but
+	   then we wouldn't have a device name with which to report the error.
+	   The size test is for 6 bit vs. 8 bit address serial EEPROMs.
+	*/
 	{
 		u16 sum = 0;
 		int j;
-		int addr_len = read_eeprom(ioaddr, 0, 6) == 0xffff ? 8 : 6;
+		int read_cmd, ee_size;
+
+		if ((do_eeprom_cmd(ioaddr, EE_READ_CMD << 24, 27) & 0xffe0000)
+			== 0xffe0000) {
+			ee_size = 0x100;
+			read_cmd = EE_READ_CMD << 24;
+		} else {
+			ee_size = 0x40;
+			read_cmd = EE_READ_CMD << 22;
+		}
 
-		for (j = 0, i = 0; i < 0x40; i++) {
-			u16 value = read_eeprom(ioaddr, i, addr_len);
+		for (j = 0, i = 0; i < ee_size; i++) {
+			u16 value = do_eeprom_cmd(ioaddr, read_cmd | (i << 16), 27);
 			eeprom[i] = value;
 			sum += value;
 			if (i < 3) {
@@ -542,12 +686,12 @@
 	/* Reset the chip: stop Tx and Rx processes and clear counters.
 	   This takes less than 10usec and will easily finish before the next
 	   action. */
-	outl(0, ioaddr + SCBPort);
+	outl(PortReset, ioaddr + SCBPort);
 
 	if (eeprom[3] & 0x0100)
 		product = "OEM i82557/i82558 10/100 Ethernet";
 	else
-		product = "Intel EtherExpress Pro 10/100";
+		product = pci_tbl[chip_idx].name;
 
 	printk(KERN_INFO "%s: %s at %#3lx, ", dev->name, product, ioaddr);
 
@@ -555,7 +699,7 @@
 		printk("%2.2X:", dev->dev_addr[i]);
 	printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq);
 
-#ifndef kernel_bloat
+#if 1 || defined(kernel_bloat)
 	/* OK, this is pure kernel bloat.  I don't like it when other drivers
 	   waste non-pageable kernel space to emit similar messages, but I need
 	   them for bug reports. */
@@ -600,7 +744,7 @@
 		self_test_results = (s32*) ((((long) str) + 15) & ~0xf);
 		self_test_results[0] = 0;
 		self_test_results[1] = -1;
-		outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort);
+		outl(virt_to_bus(self_test_results) | PortSelfTest, ioaddr + SCBPort);
 		do {
 			udelay(10);
 		} while (self_test_results[1] == -1  &&  --boguscnt >= 0);
@@ -624,19 +768,33 @@
 	}
 #endif  /* kernel_bloat */
 
+	outl(PortReset, ioaddr + SCBPort);
+#if defined(HAS_PCI_NETIF)
+	/* Return the chip to its original power state. */
+	acpi_set_pwr_state(pci_bus, pci_devfn, acpi_idle_state);
+#endif
+
 	/* We do a request_region() only to register /proc/ioports info. */
 	request_region(ioaddr, SPEEDO3_TOTAL_SIZE, "Intel Speedo3 Ethernet");
 
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
 
-	if (dev->priv == NULL)
-		dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL);
 	sp = dev->priv;
+	if (dev->priv == NULL) {
+		void *mem = kmalloc(sizeof(*sp), GFP_KERNEL);
+		dev->priv = sp = mem;		/* Cache align here if kmalloc does not. */
+		sp->priv_addr = mem;
+	}
 	memset(sp, 0, sizeof(*sp));
 	sp->next_module = root_speedo_dev;
 	root_speedo_dev = dev;
 
+	sp->pci_bus = pci_bus;
+	sp->pci_devfn = pci_devfn;
+	sp->chip_id = chip_idx;
+	sp->acpi_pwr = acpi_idle_state;
+
 	sp->full_duplex = option >= 0 && (option & 0x10) ? 1 : 0;
 	if (card_idx >= 0) {
 		if (full_duplex[card_idx] >= 0)
@@ -654,12 +812,16 @@
 	/* The Speedo-specific entries in the device structure. */
 	dev->open = &speedo_open;
 	dev->hard_start_xmit = &speedo_start_xmit;
+#if defined(HAS_NETIF_QUEUE)
+	dev->tx_timeout = &speedo_tx_timeout;
+	dev->watchdog_timeo = TX_TIMEOUT;
+#endif
 	dev->stop = &speedo_close;
 	dev->get_stats = &speedo_get_stats;
 	dev->set_multicast_list = &set_rx_mode;
 	dev->do_ioctl = &speedo_ioctl;
 
-	return;
+	return dev;
 }
 
 /* Serial EEPROM section.
@@ -668,47 +830,33 @@
 #define EE_SHIFT_CLK	0x01	/* EEPROM shift clock. */
 #define EE_CS			0x02	/* EEPROM chip select. */
 #define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
-#define EE_WRITE_0		0x01
-#define EE_WRITE_1		0x05
 #define EE_DATA_READ	0x08	/* EEPROM chip data out. */
 #define EE_ENB			(0x4800 | EE_CS)
+#define EE_WRITE_0		0x4802
+#define EE_WRITE_1		0x4806
+#define EE_OFFSET		SCBeeprom
 
 /* Delay between EEPROM clock transitions.
-   This will actually work with no delay on 33Mhz PCI.  */
-#define eeprom_delay(nanosec)		udelay(1);
+   The code works with no delay on 33Mhz PCI.  */
+#define eeprom_delay()	inw(ee_addr)
 
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD	(5 << addr_len)
-#define EE_READ_CMD		(6 << addr_len)
-#define EE_ERASE_CMD	(7 << addr_len)
-
-static int read_eeprom(long ioaddr, int location, int addr_len)
+static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len)
 {
-	unsigned short retval = 0;
+	unsigned retval = 0;
 	long ee_addr = ioaddr + SCBeeprom;
-	int read_cmd = location | EE_READ_CMD;
-	int i;
 
-	outw(EE_ENB & ~EE_CS, ee_addr);
-	outw(EE_ENB, ee_addr);
+	outw(EE_ENB | EE_SHIFT_CLK, ee_addr);
 
-	/* Shift the read command bits out. */
-	for (i = 12; i >= 0; i--) {
-		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
-		outw(EE_ENB | dataval, ee_addr);
-		eeprom_delay(100);
-		outw(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
-		eeprom_delay(150);
-	}
-	outw(EE_ENB, ee_addr);
-
-	for (i = 15; i >= 0; i--) {
-		outw(EE_ENB | EE_SHIFT_CLK, ee_addr);
-		eeprom_delay(100);
+	/* Shift the command bits out. */
+	do {
+		short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0;
+		outw(dataval, ee_addr);
+		eeprom_delay();
+		outw(dataval | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay();
 		retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0);
-		outw(EE_ENB, ee_addr);
-		eeprom_delay(100);
-	}
+	} while (--cmd_len >= 0);
+	outw(EE_ENB, ee_addr);
 
 	/* Terminate the EEPROM access. */
 	outw(EE_ENB & ~EE_CS, ee_addr);
@@ -723,6 +871,7 @@
 		val = inl(ioaddr + SCBCtrlMDI);
 		if (--boguscnt < 0) {
 			printk(KERN_ERR " mdio_read() timed out with val = %8.8x.\n", val);
+			break;
 		}
 	} while (! (val & 0x10000000));
 	return val & 0xffff;
@@ -737,6 +886,7 @@
 		val = inl(ioaddr + SCBCtrlMDI);
 		if (--boguscnt < 0) {
 			printk(KERN_ERR" mdio_write() timed out with val = %8.8x.\n", val);
+			break;
 		}
 	} while (! (val & 0x10000000));
 	return val & 0xffff;
@@ -744,24 +894,34 @@
 
 
 static int
-speedo_open(struct device *dev)
+speedo_open(struct net_device *dev)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
 	long ioaddr = dev->base_addr;
 
-#ifdef notdef
-	/* We could reset the chip, but should not need to. */
-	/* In fact we MUST NOT, unless we also re-do the init */
-	outl(0, ioaddr + SCBPort);
-	udelay(10);
+#if defined(HAS_PCI_NETIF)
+	acpi_set_pwr_state(sp->pci_bus, sp->pci_devfn, ACPI_D0);
 #endif
 
-	/* This had better be initialized before we initialize the interrupt! */
-	sp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED;
-
 	if (speedo_debug > 1)
 		printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq);
 
+	/* Set up the Tx queue early.. */
+	sp->cur_tx = 0;
+	sp->dirty_tx = 0;
+	sp->last_cmd = 0;
+	sp->tx_full = 0;
+	sp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED;
+	sp->in_interrupt = 0;
+
+	/* .. we can safely take handler calls during init. */
+	if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ, dev->name, dev)) {
+		return -EAGAIN;
+	}
+	MOD_INC_USE_COUNT;
+
+	dev->if_port = sp->default_port;
+
 #ifdef oh_no_you_dont_unless_you_honour_the_options_passed_in_to_us
 	/* Retrigger negotiation to reset previous errors. */
 	if ((sp->phy[0] & 0x8000) == 0) {
@@ -780,88 +940,30 @@
 	}
 #endif
 
-	/* Load the statistics block address. */
-	wait_for_cmd_done(ioaddr + SCBCmd);
-	outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer);
-	outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd);
-	sp->lstats.done_marker = 0;
-
 	speedo_init_rx_ring(dev);
-	wait_for_cmd_done(ioaddr + SCBCmd);
-	outl(0, ioaddr + SCBPointer);
-	outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
-
-	/* Todo: verify that we must wait for previous command completion. */
-	wait_for_cmd_done(ioaddr + SCBCmd);
-	outl(virt_to_bus(sp->rx_ringp[0]), ioaddr + SCBPointer);
-	outw(INT_MASK | RX_START, ioaddr + SCBCmd);
 
-	/* Fill the first command with our physical address. */
-	{
-		u16 *eaddrs = (u16 *)dev->dev_addr;
-		u16 *setup_frm = (u16 *)&(sp->tx_ring[0].tx_desc_addr);
+	/* Fire up the hardware. */
+	outw(SCBMaskAll, ioaddr + SCBCmd);
+	speedo_resume(dev);
 
-		/* Avoid a bug(?!) here by marking the command already completed. */
-		sp->tx_ring[0].status = cpu_to_le32(((CmdSuspend | CmdIASetup) << 16) | 0xa000);
-		sp->tx_ring[0].link = cpu_to_le32(virt_to_bus(&(sp->tx_ring[1])));
-		*setup_frm++ = eaddrs[0];
-		*setup_frm++ = eaddrs[1];
-		*setup_frm++ = eaddrs[2];
-	}
-	sp->last_cmd = (struct descriptor *)&sp->tx_ring[0];
-	sp->cur_tx = 1;
-	sp->dirty_tx = 0;
-	sp->tx_full = 0;
-
-	wait_for_cmd_done(ioaddr + SCBCmd);
-	outl(0, ioaddr + SCBPointer);
-	outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd);
-
-	dev->if_port = sp->default_port;
-
-	dev->tbusy = 0;
 	dev->interrupt = 0;
 	dev->start = 1;
-
-	/*
-	 * Request the IRQ last, after we have set up all data structures.
-	 * It would be bad to get an interrupt before we're ready.
-	 *
-	 * Register ourself first before turn on the interrupt. Someone
-	 * else may share the same interrupt. H.J.
-	 */
-	if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ,
-					"Intel EtherExpress Pro 10/100 Ethernet", dev)) {
-		return -EAGAIN;
-	}
-
-	/* Start the chip's Tx process and unmask interrupts. */
-	/* Todo: verify that we must wait for previous command completion. */
-	wait_for_cmd_done(ioaddr + SCBCmd);
-	outl(virt_to_bus(&sp->tx_ring[0]), ioaddr + SCBPointer);
-	outw(CU_START, ioaddr + SCBCmd);
+	netif_start_queue(dev);
 
 	/* Setup the chip and configure the multicast list. */
-	sp->mc_setup_frm = NULL;
-	sp->mc_setup_frm_len = 0;
-	sp->mc_setup_busy = 0;
+	sp->mc_setup_head = NULL;
+	sp->mc_setup_tail = NULL;
+	sp->flow_ctrl = sp->partner = 0;
 	sp->rx_mode = -1;			/* Invalid -> always reset the mode. */
 	set_rx_mode(dev);
+	if ((sp->phy[0] & 0x8000) == 0)
+		sp->advertising = mdio_read(ioaddr, sp->phy[0] & 0x1f, 4);
 
 	if (speedo_debug > 2) {
 		printk(KERN_DEBUG "%s: Done speedo_open(), status %8.8x.\n",
 			   dev->name, inw(ioaddr + SCBStatus));
 	}
 
-	wait_for_cmd_done(ioaddr + SCBCmd);
-	outw(CU_DUMPSTATS, ioaddr + SCBCmd);
-
-	/* No need to wait for the command unit to accept here. */
-	if ((sp->phy[0] & 0x8000) == 0)
-		mdio_read(ioaddr, sp->phy[0] & 0x1f, 0);
-
-	MOD_INC_USE_COUNT;
-
 	/* Set the timer.  The timer serves a dual purpose:
 	   1) to monitor the media interface (e.g. link beat) and perhaps switch
 	   to an alternate media type
@@ -873,17 +975,100 @@
 	sp->timer.function = &speedo_timer;					/* timer handler */
 	add_timer(&sp->timer);
 
+	/* No need to wait for the command unit to accept here. */
+	if ((sp->phy[0] & 0x8000) == 0)
+		mdio_read(ioaddr, sp->phy[0] & 0x1f, 0);
+
 	return 0;
 }
 
+/* Start the chip hardware after a full reset. */
+static void speedo_resume(struct net_device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+
+	/* Start with a Tx threshold of 256 (0x..20.... 8 byte units). */
+	sp->tx_threshold = 0x01208000;
+
+	/* Set the segment registers to '0'. */
+	wait_for_cmd_done(ioaddr + SCBCmd);
+	outl(0, ioaddr + SCBPointer);
+	outb(RxAddrLoad, ioaddr + SCBCmd);
+	wait_for_cmd_done(ioaddr + SCBCmd);
+	outb(CUCmdBase, ioaddr + SCBCmd);
+	wait_for_cmd_done(ioaddr + SCBCmd);
+
+	/* Load the statistics block and rx ring addresses. */
+	outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer);
+	outb(CUStatsAddr, ioaddr + SCBCmd);
+	sp->lstats.done_marker = 0;
+	wait_for_cmd_done(ioaddr + SCBCmd);
+
+	if (sp->rx_ringp[sp->cur_rx % RX_RING_SIZE] == NULL) {
+		if (speedo_debug > 2)
+			printk(KERN_DEBUG "%s: NULL cur_rx in speedo_resume().\n",
+					dev->name);
+	} else {
+		outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]),
+			 ioaddr + SCBPointer);
+		outb(RxStart, ioaddr + SCBCmd);
+		wait_for_cmd_done(ioaddr + SCBCmd);
+	}
+
+	outb(CUDumpStats, ioaddr + SCBCmd);
+
+	/* Fill the first command with our physical address. */
+	{
+		struct descriptor *ias_cmd;
+
+		ias_cmd =
+			(struct descriptor *)&sp->tx_ring[sp->cur_tx++ % TX_RING_SIZE];
+		/* Avoid a bug(?!) here by marking the command already completed. */
+		ias_cmd->cmd_status = cpu_to_le32((CmdSuspend | CmdIASetup) | 0xa000);
+		ias_cmd->link =
+			virt_to_le32desc(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
+		memcpy(ias_cmd->params, dev->dev_addr, 6);
+		sp->last_cmd = ias_cmd;
+	}
+
+	/* Start the chip's Tx process and unmask interrupts. */
+	wait_for_cmd_done(ioaddr + SCBCmd);
+	outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]),
+		 ioaddr + SCBPointer);
+	outw(CUStart, ioaddr + SCBCmd);
+}
+
 /* Media monitoring and control. */
 static void speedo_timer(unsigned long data)
 {
-	struct device *dev = (struct device *)data;
+	struct net_device *dev = (struct net_device *)data;
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int phy_num = sp->phy[0] & 0x1f;
 
+	/* We have MII and lost link beat. */
+	if ((sp->phy[0] & 0x8000) == 0) {
+		int partner = mdio_read(ioaddr, phy_num, 5);
+		if (partner != sp->partner) {
+			int flow_ctrl = sp->advertising & partner & 0x0400 ? 1 : 0;
+			if (speedo_debug > 2)
+				printk(KERN_DEBUG "%s: Link status change.\n", dev->name);
+			sp->partner = partner;
+			if (flow_ctrl != sp->flow_ctrl) {
+				sp->flow_ctrl = flow_ctrl;
+				sp->rx_mode = -1;	/* Trigger a reload. */
+			}
+			/* Clear sticky bit. */
+			mdio_read(ioaddr, phy_num, 1);
+			/* If link beat has returned... */
+			if (mdio_read(ioaddr, phy_num, 1) & 0x0004)
+				dev->flags |= IFF_RUNNING;
+			else
+				dev->flags &= ~IFF_RUNNING;
+		}
+	}
 	if (speedo_debug > 3) {
-		long ioaddr = dev->base_addr;
 		printk(KERN_DEBUG "%s: Media control tick, status %4.4x.\n",
 			   dev->name, inw(ioaddr + SCBStatus));
 	}
@@ -892,6 +1077,9 @@
 		/* We haven't received a packet in a Long Time.  We might have been
 		   bitten by the receiver hang bug.  This can be cleared by sending
 		   a set multicast list command. */
+		if (speedo_debug > 2)
+			printk(KERN_DEBUG "%s: Sending a multicast list set command"
+				   " from a timer routine.\n", dev->name);
 		set_rx_mode(dev);
 	}
 	/* We must continue to monitor the media. */
@@ -899,9 +1087,48 @@
 	add_timer(&sp->timer);
 }
 
+static void speedo_show_state(struct net_device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int phy_num = sp->phy[0] & 0x1f;
+	int i;
+
+	/* Print a few items for debugging. */
+	if (speedo_debug > 0) {
+		int i;
+		printk(KERN_DEBUG "%s: Tx ring dump,  Tx queue %u / %u:\n", dev->name,
+			   sp->cur_tx, sp->dirty_tx);
+		for (i = 0; i < TX_RING_SIZE; i++)
+			printk(KERN_DEBUG "%s:  %c%c%2d %8.8x.\n", dev->name,
+				   i == sp->dirty_tx % TX_RING_SIZE ? '*' : ' ',
+				   i == sp->cur_tx % TX_RING_SIZE ? '=' : ' ',
+				   i, sp->tx_ring[i].status);
+	}
+	printk(KERN_DEBUG "%s: Printing Rx ring"
+		   " (next to receive into %u, dirty index %u).\n",
+		   dev->name, sp->cur_rx, sp->dirty_rx);
+
+	for (i = 0; i < RX_RING_SIZE; i++)
+		printk(KERN_DEBUG "%s: %c%c%c%2d %8.8x.\n", dev->name,
+			   sp->rx_ringp[i] == sp->last_rxf ? 'l' : ' ',
+			   i == sp->dirty_rx % RX_RING_SIZE ? '*' : ' ',
+			   i == sp->cur_rx % RX_RING_SIZE ? '=' : ' ',
+			   i, (sp->rx_ringp[i] != NULL) ?
+					   (unsigned)sp->rx_ringp[i]->status : 0);
+
+	for (i = 0; i < 16; i++) {
+		/* FIXME: what does it mean?  --SAW */
+		if (i == 6) i = 21;
+		printk(KERN_DEBUG "%s:  PHY index %d register %d is %4.4x.\n",
+			   dev->name, phy_num, i, mdio_read(ioaddr, phy_num, i));
+	}
+
+}
+
 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
 static void
-speedo_init_rx_ring(struct device *dev)
+speedo_init_rx_ring(struct net_device *dev)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
 	struct RxFD *rxf, *last_rxf = NULL;
@@ -920,139 +1147,264 @@
 		sp->rx_ringp[i] = rxf;
 		skb_reserve(skb, sizeof(struct RxFD));
 		if (last_rxf)
-			last_rxf->link = cpu_to_le32(virt_to_bus(rxf));
+			last_rxf->link = virt_to_le32desc(rxf);
 		last_rxf = rxf;
-		rxf->status = cpu_to_le32(0x00000001); 			/* '1' is flag value only. */
+		rxf->status = cpu_to_le32(0x00000001);	/* '1' is flag value only. */
 		rxf->link = 0;						/* None yet. */
 		/* This field unused by i82557, we use it as a consistency check. */
 #ifdef final_version
 		rxf->rx_buf_addr = 0xffffffff;
 #else
-		rxf->rx_buf_addr = cpu_to_le32(virt_to_bus(skb->tail));
+		rxf->rx_buf_addr = virt_to_bus(skb->tail);
 #endif
-		rxf->count = 0;
-		rxf->size = cpu_to_le16(PKT_BUF_SZ);
+		rxf->count = cpu_to_le32(PKT_BUF_SZ << 16);
 	}
 	sp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
 	/* Mark the last entry as end-of-list. */
-	last_rxf->status = cpu_to_le32(0xC0000002); 			/* '2' is flag value only. */
+	last_rxf->status = cpu_to_le32(0xC0000002);	/* '2' is flag value only. */
 	sp->last_rxf = last_rxf;
 }
 
-static void speedo_tx_timeout(struct device *dev)
+static void speedo_purge_tx(struct net_device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int entry;
+
+	while ((int)(sp->cur_tx - sp->dirty_tx) > 0) {
+		entry = sp->dirty_tx % TX_RING_SIZE;
+		if (sp->tx_skbuff[entry]) {
+			sp->stats.tx_errors++;
+			dev_free_skb(sp->tx_skbuff[entry]);
+			sp->tx_skbuff[entry] = 0;
+		}
+		sp->dirty_tx++;
+	}
+	while (sp->mc_setup_head != NULL) {
+		struct speedo_mc_block *t;
+		if (speedo_debug > 1)
+			printk(KERN_DEBUG "%s: freeing mc frame.\n", dev->name);
+		t = sp->mc_setup_head->next;
+		kfree(sp->mc_setup_head);
+		sp->mc_setup_head = t;
+	}
+	sp->mc_setup_tail = NULL;
+	sp->tx_full = 0;
+	netif_wake_queue(dev);
+}
+
+static void speedo_tx_timeout(struct net_device *dev)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
 	long ioaddr = dev->base_addr;
+	int status = inw(ioaddr + SCBStatus);
+	unsigned long flags;
 
 	printk(KERN_WARNING "%s: Transmit timed out: status %4.4x "
 		   " %4.4x at %d/%d command %8.8x.\n",
-		   dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd),
+		   dev->name, status, inw(ioaddr + SCBCmd),
 		   sp->dirty_tx, sp->cur_tx,
-		   le32_to_cpu(sp->tx_ring[sp->dirty_tx % TX_RING_SIZE].status));
-	if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) {
+		   sp->tx_ring[sp->dirty_tx % TX_RING_SIZE].status);
+
+	/* Trigger a stats dump to give time before the reset. */
+	speedo_get_stats(dev);
+
+	speedo_show_state(dev);
+#if 0
+	if ((status & 0x00C0) != 0x0080
+		&&  (status & 0x003C) == 0x0010) {
+		/* Only the command unit has stopped. */
 		printk(KERN_WARNING "%s: Trying to restart the transmitter...\n",
 			   dev->name);
 		outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]),
 			 ioaddr + SCBPointer);
-		outw(CU_START, ioaddr + SCBCmd);
+		outw(CUStart, ioaddr + SCBCmd);
 	} else {
-		outw(DRVR_INT, ioaddr + SCBCmd);
+#else
+	{
+#endif
+		start_bh_atomic();
+		/* Ensure that timer routine doesn't run! */
+		del_timer(&sp->timer);
+		end_bh_atomic();
+		/* Reset the Tx and Rx units. */
+		outl(PortReset, ioaddr + SCBPort);
+		/* We may get spurious interrupts here.  But I don't think that they
+		   may do much harm.  1999/12/09 SAW */
+		udelay(10);
+		/* Disable interrupts. */
+		outw(SCBMaskAll, ioaddr + SCBCmd);
+		synchronize_irq();
+		speedo_tx_buffer_gc(dev);
+		/* Free as much as possible.
+		   It helps to recover from a hang because of out-of-memory.
+		   It also simplifies speedo_resume() in case TX ring is full or
+		   close-to-be full. */
+		speedo_purge_tx(dev);
+		speedo_refill_rx_buffers(dev, 1);
+		spin_lock_irqsave(&sp->lock, flags);
+		speedo_resume(dev);
+		sp->rx_mode = -1;
+		dev->trans_start = jiffies;
+		spin_unlock_irqrestore(&sp->lock, flags);
+		set_rx_mode(dev); /* it takes the spinlock itself --SAW */
+		sp->timer.expires = RUN_AT(2*HZ);
+		add_timer(&sp->timer);
 	}
-#ifdef oh_no_you_dont_unless_you_honour_the_options_passed_in_to_us
 	/* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */
 	if ((sp->phy[0] & 0x8000) == 0) {
 		int phy_addr = sp->phy[0] & 0x1f;
+		int advertising = mdio_read(ioaddr, phy_addr, 4);
+		int mii_bmcr = mdio_read(ioaddr, phy_addr, 0);
 		mdio_write(ioaddr, phy_addr, 0, 0x0400);
 		mdio_write(ioaddr, phy_addr, 1, 0x0000);
 		mdio_write(ioaddr, phy_addr, 4, 0x0000);
 		mdio_write(ioaddr, phy_addr, 0, 0x8000);
 #ifdef honor_default_port
 		mdio_write(ioaddr, phy_addr, 0, mii_ctrl[dev->default_port & 7]);
+#else
+		mdio_read(ioaddr, phy_addr, 0);
+		mdio_write(ioaddr, phy_addr, 0, mii_bmcr);
+		mdio_write(ioaddr, phy_addr, 4, advertising);
 #endif
 	}
-#endif
-	sp->stats.tx_errors++;
-	dev->trans_start = jiffies;
 	return;
 }
 
 static int
-speedo_start_xmit(struct sk_buff *skb, struct device *dev)
+speedo_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
 	long ioaddr = dev->base_addr;
 	int entry;
 
-	/* Block a timer-based transmit from overlapping.  This could better be
-	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
-	   If this ever occurs the queue layer is doing something evil! */
-	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+#if ! defined(HAS_NETIF_QUEUE)
+	if (test_bit(0, (void*)&dev->tbusy) != 0) {
 		int tickssofar = jiffies - dev->trans_start;
 		if (tickssofar < TX_TIMEOUT - 2)
 			return 1;
 		if (tickssofar < TX_TIMEOUT) {
 			/* Reap sent packets from the full Tx queue. */
-			outw(DRVR_INT, ioaddr + SCBCmd);
+			outw(SCBTriggerIntr, ioaddr + SCBCmd);
 			return 1;
 		}
 		speedo_tx_timeout(dev);
 		return 1;
 	}
-
-	/* Caution: the write order is important here, set the base address
-	   with the "ownership" bits last. */
+#endif
 
 	{	/* Prevent interrupts from changing the Tx ring from underneath us. */
 		unsigned long flags;
 
 		spin_lock_irqsave(&sp->lock, flags);
 
+		/* Check if there are enough space. */
+		if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
+			printk(KERN_ERR "%s: incorrect tbusy state, fixed.\n", dev->name);
+			netif_stop_queue(dev);
+			sp->tx_full = 1;
+			spin_unlock_irqrestore(&sp->lock, flags);
+			return 1;
+		}
+
 		/* Calculate the Tx descriptor entry. */
 		entry = sp->cur_tx++ % TX_RING_SIZE;
 
 		sp->tx_skbuff[entry] = skb;
-		/* Todo: be a little more clever about setting the interrupt bit. */
 		sp->tx_ring[entry].status =
-			cpu_to_le32((CmdSuspend | CmdTx | CmdTxFlex) << 16);
+			cpu_to_le32(CmdSuspend | CmdTx | CmdTxFlex);
+		if (!(entry & ((TX_RING_SIZE>>2)-1)))
+			sp->tx_ring[entry].status |= cpu_to_le32(CmdIntr);
 		sp->tx_ring[entry].link =
-		  cpu_to_le32(virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
+			virt_to_le32desc(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
 		sp->tx_ring[entry].tx_desc_addr =
-		  cpu_to_le32(virt_to_bus(&sp->tx_ring[entry].tx_buf_addr0));
-		/* The data region is always in one buffer descriptor, Tx FIFO
-		   threshold of 256. */
-		sp->tx_ring[entry].count = cpu_to_le32(0x01208000);
-		sp->tx_ring[entry].tx_buf_addr0 = cpu_to_le32(virt_to_bus(skb->data));
+			virt_to_le32desc(&sp->tx_ring[entry].tx_buf_addr0);
+		/* The data region is always in one buffer descriptor. */
+		sp->tx_ring[entry].count = cpu_to_le32(sp->tx_threshold);
+		sp->tx_ring[entry].tx_buf_addr0 = virt_to_le32desc(skb->data);
 		sp->tx_ring[entry].tx_buf_size0 = cpu_to_le32(skb->len);
-		/* Todo: perhaps leave the interrupt bit set if the Tx queue is more
-		   than half full.  Argument against: we should be receiving packets
-		   and scavenging the queue.  Argument for: if so, it shouldn't
-		   matter. */
-		sp->last_cmd->command &= cpu_to_le16(~(CmdSuspend | CmdIntr));
-		sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
-
 		/* Trigger the command unit resume. */
 		wait_for_cmd_done(ioaddr + SCBCmd);
-		outw(CU_RESUME, ioaddr + SCBCmd);
+		clear_suspend(sp->last_cmd);
+		/* We want the time window between clearing suspend flag on the previous
+		   command and resuming CU to be as small as possible.
+		   Interrupts in between are very undesired.  --SAW */
+		outb(CUResume, ioaddr + SCBCmd);
+		sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
+
+		/* Leave room for set_rx_mode(). If there is no more space than reserved
+		   for multicast filter mark the ring as full. */
+		if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
+			netif_stop_queue(dev);
+			sp->tx_full = 1;
+		}
 
 		spin_unlock_irqrestore(&sp->lock, flags);
 	}
 
-	/* Leave room for set_rx_mode() to fill two entries. */
-	if (sp->cur_tx - sp->dirty_tx > TX_RING_SIZE - 3)
-		sp->tx_full = 1;
-	else
-		clear_bit(0, (void*)&dev->tbusy);
-
 	dev->trans_start = jiffies;
 
 	return 0;
 }
 
+static void speedo_tx_buffer_gc(struct net_device *dev)
+{
+	unsigned int dirty_tx;
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+
+	dirty_tx = sp->dirty_tx;
+	while ((int)(sp->cur_tx - dirty_tx) > 0) {
+		int entry = dirty_tx % TX_RING_SIZE;
+		int status = le32_to_cpu(sp->tx_ring[entry].status);
+
+		if (speedo_debug > 5)
+			printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n",
+				   entry, status);
+		if ((status & StatusComplete) == 0)
+			break;			/* It still hasn't been processed. */
+		if (status & TxUnderrun)
+			if (sp->tx_threshold < 0x01e08000) {
+				if (speedo_debug > 2)
+					printk(KERN_DEBUG "%s: TX underrun, threshold adjusted.\n",
+						   dev->name);
+				sp->tx_threshold += 0x00040000;
+			}
+		/* 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;
+			dev_free_skb(sp->tx_skbuff[entry]);
+			sp->tx_skbuff[entry] = 0;
+		}
+		dirty_tx++;
+	}
+
+	if (speedo_debug && (int)(sp->cur_tx - dirty_tx) > TX_RING_SIZE) {
+		printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d,"
+			   " full=%d.\n",
+			   dirty_tx, sp->cur_tx, sp->tx_full);
+		dirty_tx += TX_RING_SIZE;
+	}
+
+	while (sp->mc_setup_head != NULL
+		   && (int)(dirty_tx - sp->mc_setup_head->tx - 1) > 0) {
+		struct speedo_mc_block *t;
+		if (speedo_debug > 1)
+			printk(KERN_DEBUG "%s: freeing mc frame.\n", dev->name);
+		t = sp->mc_setup_head->next;
+		kfree(sp->mc_setup_head);
+		sp->mc_setup_head = t;
+	}
+	if (sp->mc_setup_head == NULL)
+		sp->mc_setup_tail = NULL;
+
+	sp->dirty_tx = dirty_tx;
+}
+
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
 static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
 {
-	struct device *dev = (struct device *)dev_instance;
+	struct net_device *dev = (struct net_device *)dev_instance;
 	struct speedo_private *sp;
 	long ioaddr, boguscnt = max_interrupt_work;
 	unsigned short status;
@@ -1066,9 +1418,15 @@
 
 	ioaddr = dev->base_addr;
 	sp = (struct speedo_private *)dev->priv;
-	spin_lock(&sp->lock);
 
 #ifndef final_version
+	/* A lock to prevent simultaneous entry on SMP machines. */
+	if (test_and_set_bit(0, (void*)&sp->in_interrupt)) {
+		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
+			   dev->name);
+		sp->in_interrupt = 0;	/* Avoid halting machine. */
+		return;
+	}
 	dev->interrupt = 1;
 #endif
 
@@ -1084,63 +1442,90 @@
 		if ((status & 0xfc00) == 0)
 			break;
 
-		if (status & 0x4000)	 /* Packet received. */
+		/* Always check if all rx buffers are allocated.  --SAW */
+		speedo_refill_rx_buffers(dev, 0);
+
+		if ((status & 0x5000) ||	/* Packet received, or Rx error. */
+			(sp->rx_ring_state&(RrNoMem|RrPostponed)) == RrPostponed)
+									/* Need to gather the postponed packet. */
 			speedo_rx(dev);
 
 		if (status & 0x1000) {
-		  if ((status & 0x003c) == 0x0028) /* No more Rx buffers. */
-			outw(RX_RESUMENR, ioaddr + SCBCmd);
-		  else if ((status & 0x003c) == 0x0008) { /* No resources (why?!) */
-			/* No idea of what went wrong.  Restart the receiver. */
+			spin_lock(&sp->lock);
+			if ((status & 0x003c) == 0x0028) {		/* No more Rx buffers. */
+				struct RxFD *rxf;
+				printk(KERN_WARNING "%s: card reports no RX buffers.\n",
+						dev->name);
+				rxf = sp->rx_ringp[sp->cur_rx % RX_RING_SIZE];
+				if (rxf == NULL) {
+					if (speedo_debug > 2)
+						printk(KERN_DEBUG
+								"%s: NULL cur_rx in speedo_interrupt().\n",
+								dev->name);
+					sp->rx_ring_state |= RrNoMem|RrNoResources;
+				} else if (rxf == sp->last_rxf) {
+					if (speedo_debug > 2)
+						printk(KERN_DEBUG
+								"%s: cur_rx is last in speedo_interrupt().\n",
+								dev->name);
+					sp->rx_ring_state |= RrNoMem|RrNoResources;
+				} else
+					outb(RxResumeNoResources, ioaddr + SCBCmd);
+			} else if ((status & 0x003c) == 0x0008) { /* No resources. */
+				struct RxFD *rxf;
+				printk(KERN_WARNING "%s: card reports no resources.\n",
+						dev->name);
+				rxf = sp->rx_ringp[sp->cur_rx % RX_RING_SIZE];
+				if (rxf == NULL) {
+					if (speedo_debug > 2)
+						printk(KERN_DEBUG
+								"%s: NULL cur_rx in speedo_interrupt().\n",
+								dev->name);
+					sp->rx_ring_state |= RrNoMem|RrNoResources;
+				} else if (rxf == sp->last_rxf) {
+					if (speedo_debug > 2)
+						printk(KERN_DEBUG
+								"%s: cur_rx is last in speedo_interrupt().\n",
+								dev->name);
+					sp->rx_ring_state |= RrNoMem|RrNoResources;
+				} else {
+					/* Restart the receiver. */
+					outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]),
+					   ioaddr + SCBPointer);
+					outb(RxStart, ioaddr + SCBCmd);
+				}
+			}
+			sp->stats.rx_errors++;
+			spin_unlock(&sp->lock);
+		}
+
+		if ((sp->rx_ring_state&(RrNoMem|RrNoResources)) == RrNoResources) {
+			printk(KERN_WARNING
+					"%s: restart the receiver after a possible hang.\n",
+					dev->name);
+			spin_lock(&sp->lock);
+			/* Restart the receiver.
+			   I'm not sure if it's always right to restart the receiver
+			   here but I don't know another way to prevent receiver hangs.
+			   1999/12/25 SAW */
 			outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]),
-				 ioaddr + SCBPointer);
-			outw(RX_START, ioaddr + SCBCmd);
-		  }
-		  sp->stats.rx_errors++;
+			   ioaddr + SCBPointer);
+			outb(RxStart, ioaddr + SCBCmd);
+			sp->rx_ring_state &= ~RrNoResources;
+			spin_unlock(&sp->lock);
 		}
 
 		/* User interrupt, Command/Tx unit interrupt or CU not active. */
 		if (status & 0xA400) {
-			unsigned int dirty_tx = sp->dirty_tx;
-
-			while (sp->cur_tx - dirty_tx > 0) {
-				int entry = dirty_tx % TX_RING_SIZE;
-				int status = le32_to_cpu(sp->tx_ring[entry].status);
-
-				if (speedo_debug > 5)
-					printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n",
-						   entry, status);
-				if ((status & StatusComplete) == 0)
-					break;			/* It still hasn't been processed. */
-				/* 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 ((status&0x70000) == CmdNOp << 16)
-					sp->mc_setup_busy = 0;
-				dirty_tx++;
-			}
-
-#ifndef final_version
-			if (sp->cur_tx - dirty_tx > TX_RING_SIZE) {
-				printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d,"
-					   " full=%d.\n",
-					   dirty_tx, sp->cur_tx, sp->tx_full);
-				dirty_tx += TX_RING_SIZE;
-			}
-#endif
-
-			if (sp->tx_full && dev->tbusy
-				&& dirty_tx > sp->cur_tx - TX_RING_SIZE + 2) {
-				/* The ring is no longer full, clear tbusy. */
+			spin_lock(&sp->lock);
+			speedo_tx_buffer_gc(dev);
+			if (sp->tx_full
+				&& (int)(sp->cur_tx - sp->dirty_tx) < TX_QUEUE_UNFULL) {
+				/* The ring is no longer full. */
 				sp->tx_full = 0;
-				clear_bit(0, (void*)&dev->tbusy);
-				mark_bh(NET_BH);
+				netif_wake_queue(dev); /* Attention: under a spinlock.  --SAW */
 			}
-
-			sp->dirty_tx = dirty_tx;
+			spin_unlock(&sp->lock);
 		}
 
 		if (--boguscnt < 0) {
@@ -1157,34 +1542,131 @@
 			   dev->name, inw(ioaddr + SCBStatus));
 
 	dev->interrupt = 0;
-	spin_unlock(&sp->lock);
+	clear_bit(0, (void*)&sp->in_interrupt);
 	return;
 }
 
+static inline struct RxFD *speedo_rx_alloc(struct net_device *dev, int entry)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	struct RxFD *rxf;
+	struct sk_buff *skb;
+	/* Get a fresh skbuff to replace the consumed one. */
+	skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
+	sp->rx_skbuff[entry] = skb;
+	if (skb == NULL) {
+		sp->rx_ringp[entry] = NULL;
+		return NULL;
+	}
+	rxf = sp->rx_ringp[entry] = (struct RxFD *)skb->tail;
+	skb->dev = dev;
+	skb_reserve(skb, sizeof(struct RxFD));
+	rxf->rx_buf_addr = virt_to_bus(skb->tail);
+	return rxf;
+}
+
+static inline void speedo_rx_link(struct net_device *dev, int entry,
+								  struct RxFD *rxf)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	rxf->status = cpu_to_le32(0xC0000001); 	/* '1' for driver use only. */
+	rxf->link = 0;			/* None yet. */
+	rxf->count = cpu_to_le32(PKT_BUF_SZ << 16);
+	sp->last_rxf->link = virt_to_le32desc(rxf);
+	sp->last_rxf->status &= cpu_to_le32(~0xC0000000);
+	sp->last_rxf = rxf;
+}
+
+static int speedo_refill_rx_buf(struct net_device *dev, int force)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int entry;
+	struct RxFD *rxf;
+
+	entry = sp->dirty_rx % RX_RING_SIZE;
+	if (sp->rx_skbuff[entry] == NULL) {
+		rxf = speedo_rx_alloc(dev, entry);
+		if (rxf == NULL) {
+			unsigned int forw;
+			int forw_entry;
+			if (speedo_debug > 2 || !(sp->rx_ring_state & RrOOMReported)) {
+				printk(KERN_WARNING "%s: can't fill rx buffer (force %d)!\n",
+						dev->name, force);
+				speedo_show_state(dev);
+				sp->rx_ring_state |= RrOOMReported;
+			}
+			if (!force)
+				return -1;	/* Better luck next time!  */
+			/* Borrow an skb from one of next entries. */
+			for (forw = sp->dirty_rx + 1; forw != sp->cur_rx; forw++)
+				if (sp->rx_skbuff[forw % RX_RING_SIZE] != NULL)
+					break;
+			if (forw == sp->cur_rx)
+				return -1;
+			forw_entry = forw % RX_RING_SIZE;
+			sp->rx_skbuff[entry] = sp->rx_skbuff[forw_entry];
+			sp->rx_skbuff[forw_entry] = NULL;
+			rxf = sp->rx_ringp[forw_entry];
+			sp->rx_ringp[forw_entry] = NULL;
+			sp->rx_ringp[entry] = rxf;
+		}
+	} else {
+		rxf = sp->rx_ringp[entry];
+	}
+	speedo_rx_link(dev, entry, rxf);
+	sp->dirty_rx++;
+	sp->rx_ring_state &= ~(RrNoMem|RrOOMReported); /* Mark the progress. */
+	return 0;
+}
+
+static void speedo_refill_rx_buffers(struct net_device *dev, int force)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+
+	/* Refill the RX ring. */
+	while ((int)(sp->cur_rx - sp->dirty_rx) > 0 &&
+			speedo_refill_rx_buf(dev, force) != -1);
+}
+
 static int
-speedo_rx(struct device *dev)
+speedo_rx(struct net_device *dev)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
 	int entry = sp->cur_rx % RX_RING_SIZE;
 	int status;
 	int rx_work_limit = sp->dirty_rx + RX_RING_SIZE - sp->cur_rx;
+	int alloc_ok = 1;
 
 	if (speedo_debug > 4)
 		printk(KERN_DEBUG " In speedo_rx().\n");
 	/* If we own the next entry, it's a new packet. Send it up. */
 	while (sp->rx_ringp[entry] != NULL &&
 		   (status = le32_to_cpu(sp->rx_ringp[entry]->status)) & RxComplete) {
+		int pkt_len = le32_to_cpu(sp->rx_ringp[entry]->count) & 0x3fff;
 
 		if (--rx_work_limit < 0)
 			break;
+
+		/* Check for a rare out-of-memory case: the current buffer is
+		   the last buffer allocated in the RX ring.  --SAW */
+		if (sp->last_rxf == sp->rx_ringp[entry]) {
+			/* Postpone the packet.  It'll be reaped at an interrupt when this
+			   packet is no longer the last packet in the ring. */
+			if (speedo_debug > 2)
+				printk(KERN_DEBUG "%s: RX packet postponed!\n",
+					   dev->name);
+			sp->rx_ring_state |= RrPostponed;
+			break;
+		}
+
 		if (speedo_debug > 4)
 			printk(KERN_DEBUG "  speedo_rx() status %8.8x len %d.\n", status,
-				   le16_to_cpu(sp->rx_ringp[entry]->count) & 0x3fff);
-		if ((status & (RxErrTooBig|RxOK)) != RxOK) {
+				   pkt_len);
+		if ((status & (RxErrTooBig|RxOK|0x0f90)) != RxOK) {
 			if (status & RxErrTooBig)
 				printk(KERN_ERR "%s: Ethernet frame overran the Rx buffer, "
 					   "status %8.8x!\n", dev->name, status);
-			else if ( ! (status & 0x2000)) {
+			else if (! (status & RxOK)) {
 				/* There was a fatal error.  This *should* be impossible. */
 				sp->stats.rx_errors++;
 				printk(KERN_ERR "%s: Anomalous event in speedo_rx(), "
@@ -1192,7 +1674,6 @@
 					   dev->name, status);
 			}
 		} else {
-			int pkt_len = le16_to_cpu(sp->rx_ringp[entry]->count) & 0x3fff;
 			struct sk_buff *skb;
 
 			/* Check if the packet is long enough to just accept without
@@ -1204,13 +1685,11 @@
 				/* 'skb_put()' points to the start of sk_buff data area. */
 #if 1 || USE_IP_CSUM
 				/* Packet is in one chunk -- we can copy + cksum. */
-				eth_copy_and_sum(skb,
-								 bus_to_virt(le32_to_cpu(sp->rx_ringp[entry]->rx_buf_addr)),
-								 pkt_len, 0);
+				eth_copy_and_sum(skb, sp->rx_skbuff[entry]->tail, pkt_len, 0);
 				skb_put(skb, pkt_len);
 #else
-				memcpy(skb_put(skb, pkt_len),
-					   bus_to_virt(le32_to_cpu(sp->rx_ringp[entry]->rx_buf_addr)), pkt_len);
+				memcpy(skb_put(skb, pkt_len), sp->rx_skbuff[entry]->tail,
+					   pkt_len);
 #endif
 			} else {
 				void *temp;
@@ -1223,64 +1702,44 @@
 				}
 				sp->rx_skbuff[entry] = NULL;
 				temp = skb_put(skb, pkt_len);
-				if (bus_to_virt(le32_to_cpu(sp->rx_ringp[entry]->rx_buf_addr)) != temp)
+				if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
 					printk(KERN_ERR "%s: Rx consistency error -- the skbuff "
 						   "addresses do not match in speedo_rx: %p vs. %p "
 						   "/ %p.\n", dev->name,
-						   bus_to_virt(le32_to_cpu(sp->rx_ringp[entry]->rx_buf_addr)),
+						   bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
 						   skb->head, temp);
 				sp->rx_ringp[entry] = NULL;
 			}
 			skb->protocol = eth_type_trans(skb, dev);
 			netif_rx(skb);
 			sp->stats.rx_packets++;
-			sp->stats.rx_bytes += pkt_len; /* Count received bytes */
+			sp->stats.rx_bytes += pkt_len;
 		}
 		entry = (++sp->cur_rx) % RX_RING_SIZE;
+		sp->rx_ring_state &= ~RrPostponed;
+		/* Refill the recently taken buffers.
+		   Do it one-by-one to handle traffic bursts better. */
+		if (alloc_ok && speedo_refill_rx_buf(dev, 0) == -1)
+			alloc_ok = 0;
 	}
 
-	/* Refill the Rx ring buffers. */
-	for (; sp->dirty_rx < sp->cur_rx; sp->dirty_rx++) {
-		struct RxFD *rxf;
-		entry = sp->dirty_rx % RX_RING_SIZE;
-		if (sp->rx_skbuff[entry] == NULL) {
-			struct sk_buff *skb;
-			/* Get a fresh skbuff to replace the consumed one. */
-			skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
-			sp->rx_skbuff[entry] = skb;
-			if (skb == NULL) {
-				sp->rx_ringp[entry] = NULL;
-				break;			/* Better luck next time!  */
-			}
-			rxf = sp->rx_ringp[entry] = (struct RxFD *)skb->tail;
-			skb->dev = dev;
-			skb_reserve(skb, sizeof(struct RxFD));
-			rxf->rx_buf_addr = cpu_to_le32(virt_to_bus(skb->tail));
-		} else {
-			rxf = sp->rx_ringp[entry];
-		}
-		rxf->status = cpu_to_le32(0xC0000001); 	/* '1' for driver use only. */
-		rxf->link = 0;			/* None yet. */
-		rxf->count = 0;
-		rxf->size = cpu_to_le16(PKT_BUF_SZ);
-		sp->last_rxf->link = cpu_to_le32(virt_to_bus(rxf));
-		sp->last_rxf->status &= cpu_to_le32(~0xC0000000);
-		sp->last_rxf = rxf;
-	}
+	/* Try hard to refill the recently taken buffers. */
+	speedo_refill_rx_buffers(dev, 1);
 
 	sp->last_rx_time = jiffies;
+
 	return 0;
 }
 
 static int
-speedo_close(struct device *dev)
+speedo_close(struct net_device *dev)
 {
 	long ioaddr = dev->base_addr;
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
 	int i;
 
 	dev->start = 0;
-	dev->tbusy = 1;
+	netif_stop_queue(dev);
 
 	if (speedo_debug > 1)
 		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
@@ -1289,13 +1748,16 @@
 	/* Shut off the media monitoring timer. */
 	del_timer(&sp->timer);
 
-	/* Disable interrupts, and stop the chip's Rx process. */
-	outw(INT_MASK, ioaddr + SCBCmd);
-	outw(INT_MASK | RX_ABORT, ioaddr + SCBCmd);
+	/* Shutting down the chip nicely fails to disable flow control. So.. */
+	outl(PortPartialReset, ioaddr + SCBPort);
 
 	free_irq(dev->irq, dev);
 
-	/* Free all the skbuffs in the Rx and Tx queues. */
+	/* Print a few items for debugging. */
+	if (speedo_debug > 3)
+		speedo_show_state(dev);
+
+    /* Free all the skbuffs in the Rx and Tx queues. */
 	for (i = 0; i < RX_RING_SIZE; i++) {
 		struct sk_buff *skb = sp->rx_skbuff[i];
 		sp->rx_skbuff[i] = 0;
@@ -1311,28 +1773,22 @@
 		if (skb)
 			dev_free_skb(skb);
 	}
-	if (sp->mc_setup_frm) {
-		kfree(sp->mc_setup_frm);
-		sp->mc_setup_frm_len = 0;
-	}
 
-	/* Print a few items for debugging. */
-	if (speedo_debug > 3) {
-		int phy_num = sp->phy[0] & 0x1f;
-		printk(KERN_DEBUG "%s:Printing Rx ring (next to receive into %d).\n",
-			   dev->name, sp->cur_rx);
-
-		for (i = 0; i < RX_RING_SIZE; i++)
-			printk(KERN_DEBUG "  Rx ring entry %d  %8.8x.\n",
-				   i, (int)le32_to_cpu(sp->rx_ringp[i]->status));
-
-		for (i = 0; i < 5; i++)
-			printk(KERN_DEBUG "  PHY index %d register %d is %4.4x.\n",
-				   phy_num, i, mdio_read(ioaddr, phy_num, i));
-		for (i = 21; i < 26; i++)
-			printk(KERN_DEBUG "  PHY index %d register %d is %4.4x.\n",
-				   phy_num, i, mdio_read(ioaddr, phy_num, i));
-	}
+	/* Free multicast setting blocks. */
+	for (i = 0; sp->mc_setup_head != NULL; i++) {
+		struct speedo_mc_block *t;
+		t = sp->mc_setup_head->next;
+		kfree(sp->mc_setup_head);
+		sp->mc_setup_head = t;
+	}
+	sp->mc_setup_tail = NULL;
+	if (speedo_debug > 0)
+		printk(KERN_DEBUG "%s: %d multicast blocks dropped.\n", dev->name, i);
+
+#if defined(HAS_PCI_NETIF)
+	/* Alt: acpi_set_pwr_state(pci_bus, pci_devfn, sp->acpi_pwr); */
+	acpi_set_pwr_state(sp->pci_bus, sp->pci_devfn, ACPI_D2);
+#endif
 	MOD_DEC_USE_COUNT;
 
 	return 0;
@@ -1351,12 +1807,13 @@
    Oh, and incoming frames are dropped while executing dump-stats!
    */
 static struct enet_statistics *
-speedo_get_stats(struct device *dev)
+speedo_get_stats(struct net_device *dev)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
 	long ioaddr = dev->base_addr;
 
-	if (le32_to_cpu(sp->lstats.done_marker) == 0xA007) {	/* Previous dump finished */
+	/* Update only if the previous dump finished. */
+	if (sp->lstats.done_marker == le32_to_cpu(0xA007)) {
 		sp->stats.tx_aborted_errors += le32_to_cpu(sp->lstats.tx_coll16_errs);
 		sp->stats.tx_window_errors += le32_to_cpu(sp->lstats.tx_late_colls);
 		sp->stats.tx_fifo_errors += le32_to_cpu(sp->lstats.tx_underruns);
@@ -1370,30 +1827,50 @@
 		sp->stats.rx_length_errors += le32_to_cpu(sp->lstats.rx_runt_errs);
 		sp->lstats.done_marker = 0x0000;
 		if (dev->start) {
+			unsigned long flags;
+			/* Take a spinlock to make wait_for_cmd_done and sending the
+			 * command atomic.  --SAW */
+			spin_lock_irqsave(&sp->lock, flags);
 			wait_for_cmd_done(ioaddr + SCBCmd);
-			outw(CU_DUMPSTATS, ioaddr + SCBCmd);
+			outb(CUDumpStats, ioaddr + SCBCmd);
+			spin_unlock_irqrestore(&sp->lock, flags);
 		}
 	}
 	return &sp->stats;
 }
 
-static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
 	long ioaddr = dev->base_addr;
 	u16 *data = (u16 *)&rq->ifr_data;
 	int phy = sp->phy[0] & 0x1f;
+#if defined(HAS_PCI_NETIF)
+	int saved_acpi;
+#endif
 
     switch(cmd) {
 	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
 		data[0] = phy;
 	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
+#if defined(HAS_PCI_NETIF)
+		saved_acpi = acpi_set_pwr_state(sp->pci_bus, sp->pci_devfn, ACPI_D0);
+		data[3] = mdio_read(ioaddr, data[0], data[1]);
+		acpi_set_pwr_state(sp->pci_bus, sp->pci_devfn, saved_acpi);
+#else
 		data[3] = mdio_read(ioaddr, data[0], data[1]);
+#endif
 		return 0;
 	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
 		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
+#if defined(HAS_PCI_NETIF)
+		saved_acpi = acpi_set_pwr_state(sp->pci_bus, sp->pci_devfn, ACPI_D0);
+		mdio_write(ioaddr, data[0], data[1], data[2]);
+		acpi_set_pwr_state(sp->pci_bus, sp->pci_devfn, saved_acpi);
+#else
 		mdio_write(ioaddr, data[0], data[1], data[2]);
+#endif
 		return 0;
 	default:
 		return -EOPNOTSUPP;
@@ -1409,8 +1886,7 @@
    loaded the link -- we convert the current command block, normally a Tx
    command, into a no-op and link it to the new command.
 */
-static void
-set_rx_mode(struct device *dev)
+static void set_rx_mode(struct net_device *dev)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
 	long ioaddr = dev->base_addr;
@@ -1427,9 +1903,13 @@
 	} else
 		new_rx_mode = 0;
 
-	if (sp->cur_tx - sp->dirty_tx >= TX_RING_SIZE - 1) {
-	  /* The Tx ring is full -- don't add anything!  Presumably the new mode
-		 is in config_cmd_data and will be added anyway. */
+	if (speedo_debug > 3)
+		printk(KERN_DEBUG "%s: set_rx_mode %d -> %d\n", dev->name,
+				sp->rx_mode, new_rx_mode);
+
+	if ((int)(sp->cur_tx - sp->dirty_tx) > TX_RING_SIZE - TX_MULTICAST_SIZE) {
+	    /* The Tx ring is full -- don't add anything!  Hope the mode will be
+		 * set again later. */
 		sp->rx_mode = -1;
 		return;
 	}
@@ -1443,9 +1923,9 @@
 		sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
 
 		sp->tx_skbuff[entry] = 0;			/* Redundant. */
-		sp->tx_ring[entry].status = cpu_to_le32((CmdSuspend | CmdConfigure) << 16);
+		sp->tx_ring[entry].status = cpu_to_le32(CmdSuspend | CmdConfigure);
 		sp->tx_ring[entry].link =
-			cpu_to_le32(virt_to_bus(&sp->tx_ring[(entry + 1) % TX_RING_SIZE]));
+			virt_to_le32desc(&sp->tx_ring[(entry + 1) % TX_RING_SIZE]);
 		config_cmd_data = (void *)&sp->tx_ring[entry].tx_desc_addr;
 		/* Construct a full CmdConfig frame. */
 		memcpy(config_cmd_data, i82558_config_cmd, sizeof(i82558_config_cmd));
@@ -1453,6 +1933,7 @@
 		config_cmd_data[4] = rxdmacount;
 		config_cmd_data[5] = txdmacount + 0x80;
 		config_cmd_data[15] |= (new_rx_mode & 2) ? 1 : 0;
+		config_cmd_data[19] = sp->flow_ctrl ? 0xBD : 0x80;
 		config_cmd_data[19] |= sp->full_duplex ? 0x40 : 0;
 		config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05;
 		if (sp->phy[0] & 0x8000) {			/* Use the AUI port instead. */
@@ -1460,11 +1941,13 @@
 			config_cmd_data[8] = 0;
 		}
 		/* Trigger the command unit resume. */
-		last_cmd->command &= cpu_to_le16(~CmdSuspend);
-
 		wait_for_cmd_done(ioaddr + SCBCmd);
-		outw(CU_RESUME, ioaddr + SCBCmd);
-
+		clear_suspend(last_cmd);
+		outb(CUResume, ioaddr + SCBCmd);
+		if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
+			netif_stop_queue(dev);
+			sp->tx_full = 1;
+		}
 		spin_unlock_irqrestore(&sp->lock, flags);
 	}
 
@@ -1480,9 +1963,9 @@
 		sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
 
 		sp->tx_skbuff[entry] = 0;
-		sp->tx_ring[entry].status = cpu_to_le32((CmdSuspend | CmdMulticastList) << 16);
+		sp->tx_ring[entry].status = cpu_to_le32(CmdSuspend | CmdMulticastList);
 		sp->tx_ring[entry].link =
-			cpu_to_le32(virt_to_bus(&sp->tx_ring[(entry + 1) % TX_RING_SIZE]));
+			virt_to_le32desc(&sp->tx_ring[(entry + 1) % TX_RING_SIZE]);
 		sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */
 		setup_params = (u16 *)&sp->tx_ring[entry].tx_desc_addr;
 		*setup_params++ = cpu_to_le16(dev->mc_count*6);
@@ -1495,48 +1978,40 @@
 			*setup_params++ = *eaddrs++;
 		}
 
-		last_cmd->command &= cpu_to_le16(~CmdSuspend);
-
-		/* Immediately trigger the command unit resume. */
 		wait_for_cmd_done(ioaddr + SCBCmd);
-		outw(CU_RESUME, ioaddr + SCBCmd);
+		clear_suspend(last_cmd);
+		/* Immediately trigger the command unit resume. */
+		outb(CUResume, ioaddr + SCBCmd);
 
+		if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
+			netif_stop_queue(dev);
+			sp->tx_full = 1;
+		}
 		spin_unlock_irqrestore(&sp->lock, flags);
 	} else if (new_rx_mode == 0) {
 		struct dev_mc_list *mclist;
 		u16 *setup_params, *eaddrs;
-		struct descriptor *mc_setup_frm = sp->mc_setup_frm;
+		struct speedo_mc_block *mc_blk;
+		struct descriptor *mc_setup_frm;
 		int i;
 
-		if (sp->mc_setup_frm_len < 10 + dev->mc_count*6
-			|| sp->mc_setup_frm == NULL) {
-			/* Allocate a full setup frame, 10bytes + <max addrs>. */
-			if (sp->mc_setup_frm)
-				kfree(sp->mc_setup_frm);
-			sp->mc_setup_busy = 0;
-			sp->mc_setup_frm_len = 10 + multicast_filter_limit*6;
-			sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len, GFP_ATOMIC);
-			if (sp->mc_setup_frm == NULL) {
-				printk(KERN_ERR "%s: Failed to allocate a setup frame.\n",
-					   dev->name);
-				sp->rx_mode = -1; /* We failed, try again. */
-				return;
-			}
-		}
-		/* If we are busy, someone might be quickly adding to the MC list.
-		   Try again later when the list changes stop. */
-		if (sp->mc_setup_busy) {
-			sp->rx_mode = -1;
+		mc_blk = kmalloc(sizeof(*mc_blk) + 2 + multicast_filter_limit*6,
+						 GFP_ATOMIC);
+		if (mc_blk == NULL) {
+			printk(KERN_ERR "%s: Failed to allocate a setup frame.\n",
+				   dev->name);
+			sp->rx_mode = -1; /* We failed, try again. */
 			return;
 		}
-		mc_setup_frm = sp->mc_setup_frm;
+		mc_blk->next = NULL;
+		mc_setup_frm = &mc_blk->frame;
+
 		/* Fill the setup frame. */
 		if (speedo_debug > 1)
-			printk(KERN_DEBUG "%s: Constructing a setup frame at %p, "
-				   "%d bytes.\n",
-				   dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len);
-		mc_setup_frm->status = 0;
-		mc_setup_frm->command = cpu_to_le16(CmdSuspend | CmdIntr | CmdMulticastList);
+			printk(KERN_DEBUG "%s: Constructing a setup frame at %p.\n",
+				   dev->name, mc_setup_frm);
+		mc_setup_frm->cmd_status =
+			cpu_to_le32(CmdSuspend | CmdIntr | CmdMulticastList);
 		/* Link set below. */
 		setup_params = (u16 *)&mc_setup_frm->params;
 		*setup_params++ = cpu_to_le16(dev->mc_count*6);
@@ -1552,26 +2027,35 @@
 		/* Disable interrupts while playing with the Tx Cmd list. */
 		spin_lock_irqsave(&sp->lock, flags);
 
+		if (sp->mc_setup_tail)
+			sp->mc_setup_tail->next = mc_blk;
+		else
+			sp->mc_setup_head = mc_blk;
+		sp->mc_setup_tail = mc_blk;
+		mc_blk->tx = sp->cur_tx;
+
 		entry = sp->cur_tx++ % TX_RING_SIZE;
 		last_cmd = sp->last_cmd;
 		sp->last_cmd = mc_setup_frm;
-		sp->mc_setup_busy++;
 
 		/* Change the command to a NoOp, pointing to the CmdMulti command. */
 		sp->tx_skbuff[entry] = 0;
-		sp->tx_ring[entry].status = cpu_to_le32(CmdNOp << 16);
-		sp->tx_ring[entry].link = cpu_to_le32(virt_to_bus(mc_setup_frm));
+		sp->tx_ring[entry].status = cpu_to_le32(CmdNOp);
+		sp->tx_ring[entry].link = virt_to_le32desc(mc_setup_frm);
 
 		/* Set the link in the setup frame. */
 		mc_setup_frm->link =
-			cpu_to_le32(virt_to_bus(&(sp->tx_ring[(entry+1) % TX_RING_SIZE])));
-
-		last_cmd->command &= cpu_to_le16(~CmdSuspend);
+			virt_to_le32desc(&(sp->tx_ring[(entry+1) % TX_RING_SIZE]));
 
-		/* Immediately trigger the command unit resume. */
 		wait_for_cmd_done(ioaddr + SCBCmd);
-		outw(CU_RESUME, ioaddr + SCBCmd);
+		clear_suspend(last_cmd);
+		/* Immediately trigger the command unit resume. */
+		outb(CUResume, ioaddr + SCBCmd);
 
+		if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
+			netif_stop_queue(dev);
+			sp->tx_full = 1;
+		}
 		spin_unlock_irqrestore(&sp->lock, flags);
 
 		if (speedo_debug > 5)
@@ -1584,41 +2068,65 @@
 
 #ifdef MODULE
 
-int
-init_module(void)
+int init_module(void)
 {
 	int cards_found;
 
+	if (debug >= 0 && speedo_debug != debug)
+		printk(KERN_INFO "eepro100.c: Debug level is %d.\n", debug);
 	if (debug >= 0)
 		speedo_debug = debug;
+	/* Always emit the version message. */
 	if (speedo_debug)
 		printk(KERN_INFO "%s", version);
 
-	root_speedo_dev = NULL;
-	cards_found = eepro100_init(NULL);
-	return cards_found ? 0 : -ENODEV;
+#if defined(HAS_PCI_NETIF)
+	cards_found = netif_pci_probe(pci_tbl);
+	if (cards_found < 0)
+		printk(KERN_INFO "eepro100: No cards found, driver not installed.\n");
+	return cards_found;
+#else
+	cards_found = eepro100_init();
+	if (cards_found <= 0) {
+		printk(KERN_INFO "eepro100: No cards found, driver not installed.\n");
+		return -ENODEV;
+	}
+#endif
+	return 0;
 }
 
 void
 cleanup_module(void)
 {
-	struct device *next_dev;
+	struct net_device *next_dev;
 
 	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
 	while (root_speedo_dev) {
-		next_dev = ((struct speedo_private *)root_speedo_dev->priv)->next_module;
+		struct speedo_private *sp = (void *)root_speedo_dev->priv;
 		unregister_netdev(root_speedo_dev);
+#ifdef USE_IO
 		release_region(root_speedo_dev->base_addr, SPEEDO3_TOTAL_SIZE);
+#else
+		iounmap((char *)root_speedo_dev->base_addr);
+#endif
+#if defined(HAS_PCI_NETIF)
+		acpi_set_pwr_state(sp->pci_bus, sp->pci_devfn, sp->acpi_pwr);
+#endif
+		next_dev = sp->next_module;
+		if (sp->priv_addr)
+			kfree(sp->priv_addr);
 		kfree(root_speedo_dev);
 		root_speedo_dev = next_dev;
 	}
 }
+
 #else   /* not MODULE */
-int eepro100_probe(struct device *dev)
+
+int eepro100_probe(void)
 {
 	int cards_found = 0;
 
-	cards_found = eepro100_init(dev);
+	cards_found = eepro100_init();
 
 	if (speedo_debug > 0  &&  cards_found)
 		printk(version);
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/s390/char/con3215.c linux.15p1/drivers/s390/char/con3215.c
--- linux.vanilla/drivers/s390/char/con3215.c	Tue Jan  4 21:18:53 2000
+++ linux.15p1/drivers/s390/char/con3215.c	Wed Jan 12 01:09:50 2000
@@ -45,6 +45,8 @@
 
 struct _raw3215_info;		      /* forward declaration ... */
 
+int raw3215_condevice = -1;           /* preset console device */
+
 /*
  * Request types for a 3215 device
  */
@@ -97,6 +99,16 @@
 #define MIN(a,b)	((a) < (b) ? (a) : (b))
 #endif
 
+__initfunc(void con3215_setup(char *str, char *ints))
+{
+        int vdev;
+
+        vdev = simple_strtoul(str,&str,16);
+        if (vdev >= 0 && vdev < 65536)
+                raw3215_condevice = vdev;
+        return;
+}
+
 /*
  * Get a request structure from the free list
  */
@@ -365,7 +377,7 @@
 	struct tty_struct *tty;
 	devstat_t *stat;
         int cstat, dstat;
-	int count;
+	int count, slen;
 
 	stat = (devstat_t *) int_parm;
 	req = (raw3215_req *) stat->intparm;
@@ -384,6 +396,7 @@
 		}
 	}
         if (dstat & 0x01) { /* we got a unit exception */
+#if 0
 		raw = raw3215_find_info(irq);
 		if (raw != NULL) {
                         raw->message = KERN_WARNING
@@ -394,7 +407,8 @@
                         queue_task(&raw->tqueue, &tq_immediate);
                         mark_bh(IMMEDIATE_BH);
 		}
-		dstat &= ~0x01;
+#endif
+		dstat &= ~0x01;  /* we can ignore it */
         }
 	switch (dstat) {
 	case 0x80:
@@ -407,6 +421,8 @@
 		req = raw3215_mk_read_req(raw);
 		raw3215_mk_read_ccw(raw, req);
 		raw->queued_read = req;
+                if (MACHINE_IS_P390)
+                        memset(raw->inbuf, 0, RAW3215_INBUF_SIZE);
 		queue_task(&raw->tqueue, &tq_immediate);
 		mark_bh(IMMEDIATE_BH);
 		break;
@@ -426,6 +442,11 @@
 		if (req->type == RAW3215_READ && raw->tty != NULL) {
 			tty = raw->tty;
 			count = 160 - req->residual;
+                        if (MACHINE_IS_P390) {
+                                slen = strnlen(raw->inbuf, RAW3215_INBUF_SIZE);
+                                if (count > slen)
+                                        count = slen;
+                        } else
 			if (count >= TTY_FLIPBUF_SIZE - tty->flip.count)
 				count = TTY_FLIPBUF_SIZE - tty->flip.count - 1;
 			EBCASC(raw->inbuf, count);
@@ -690,7 +711,8 @@
 	irq = 0;
 	count = 0;
 	while (count <= number && get_dev_info(irq, &dinfo) != -ENODEV) {
-		if (dinfo.sid_data.cu_type == 0x3215)
+		if (dinfo.devno == raw3215_condevice ||
+                    dinfo.sid_data.cu_type == 0x3215)
 			count++;
 		irq++;
 	}
@@ -710,7 +732,7 @@
 {
 	raw3215_info *raw;
 
-        if (!MACHINE_IS_VM)
+        if (!MACHINE_IS_VM && !MACHINE_IS_P390)
                 return 0;
 	raw = raw3215[0];  /* 3215 console is the first one */
 	if (raw->irq == -1) /* now console device found in con3215_init */
@@ -761,7 +783,7 @@
 	s390irq_spin_unlock_irqrestore(raw->irq, flags);
 }
 
-__initfunc(static int con3215_setup(struct console *co, char *options))
+__initfunc(static int con3215_consetup(struct console *co, char *options))
 {
 	return 0;
 }
@@ -776,7 +798,7 @@
 	con3215_device,
 	NULL,
 	con3215_unblank,
-	con3215_setup,
+	con3215_consetup,
 	CON_PRINTBUFFER,
 	0,
 	0,
@@ -1008,12 +1030,14 @@
 	raw3215_req *req;
 	int i;
 
-	if (!MACHINE_IS_VM)
+	if (!MACHINE_IS_VM && !MACHINE_IS_P390)
                 return kmem_start;
+        if (MACHINE_IS_VM) {
 	cpcmd("TERM CONMODE 3215", NULL, 0);
 	cpcmd("TERM AUTOCR OFF", NULL, 0);
 	cpcmd("TERM HOLD OFF", NULL, 0);
 	cpcmd("TERM MORE 5 5", NULL, 0);
+        }
 
 	kmem_start = (kmem_start + 7) & -8L;
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/s390/char/hwc_con.c linux.15p1/drivers/s390/char/hwc_con.c
--- linux.vanilla/drivers/s390/char/hwc_con.c	Tue Jan  4 21:18:53 2000
+++ linux.15p1/drivers/s390/char/hwc_con.c	Wed Jan 12 17:55:08 2000
@@ -72,6 +72,8 @@
 __initfunc(unsigned long hwc_console_init(unsigned long kmem_start))
 {
 
+        if (MACHINE_IS_P390)
+                return kmem_start;
 #ifdef CONFIG_3215
         if (MACHINE_IS_VM)
                 return kmem_start;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/s390/net/iucv.c linux.15p1/drivers/s390/net/iucv.c
--- linux.vanilla/drivers/s390/net/iucv.c	Tue Jan  4 21:18:53 2000
+++ linux.15p1/drivers/s390/net/iucv.c	Wed Jan 12 01:08:57 2000
@@ -596,8 +596,8 @@
 #ifdef DEBUG
         printk(  "iucv: transaction complete now.\n");
 #endif
+        iucv_next = *((unsigned short*)rcvptr);
         rcvptr = rcvptr + iucv_data_len;
-        iucv_next = iucv_data_len; 
         /* get next packet offset */  
         iucv_data_len= *((unsigned short*)rcvptr);
 	
@@ -791,7 +791,9 @@
 /*----------------*/
 int iucv_ioctl(struct device *dev, struct ifreq *rq, int cmd)
 {
+#ifdef DEBUG
     printk(  "iucv: device %s; iucv_ioctl\n",dev->name);
+#endif
     return 0;
 }
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/scsi.c linux.15p1/drivers/scsi/scsi.c
--- linux.vanilla/drivers/scsi/scsi.c	Tue Jan  4 21:18:58 2000
+++ linux.15p1/drivers/scsi/scsi.c	Wed Jan 12 01:19:47 2000
@@ -303,6 +303,7 @@
 {"iomega","jaz 1GB","J.86", BLIST_NOTQ | BLIST_NOLUN},
 {"CREATIVE","DVD-RAM RAM","*", BLIST_GHOST},
 {"MATSHITA","PD-2 LF-D100","*", BLIST_GHOST},
+{"AOpen","PD-2 DVD-520S","*", BLIST_GHOST},
 {"HITACHI","GF-1050","*", BLIST_GHOST},        /* Hitachi SCSI DVD-RAM */
 {"TOSHIBA","CDROM","*", BLIST_ISROM},
 {"TOSHIBA","DVD-RAM SD-W1101","*", BLIST_GHOST},
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/sound/es1370.c linux.15p1/drivers/sound/es1370.c
--- linux.vanilla/drivers/sound/es1370.c	Tue Jan  4 21:18:59 2000
+++ linux.15p1/drivers/sound/es1370.c	Wed Jan 12 01:26:27 2000
@@ -3,7 +3,7 @@
 /*
  *      es1370.c  --  Ensoniq ES1370/Asahi Kasei AK4531 audio driver.
  *
- *      Copyright (C) 1998-1999  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *      Copyright (C) 1998-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
  *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
@@ -52,66 +52,68 @@
  *  there are several MIDI to PCM (WAV) packages, one of them is timidity.
  *
  *  Revision history
- *    26.03.98   0.1   Initial release
- *    31.03.98   0.2   Fix bug in GETOSPACE
- *    04.04.98   0.3   Make it work (again) under 2.0.33
- *                     Fix mixer write operation not returning the actual
- *                     settings
- *    05.04.98   0.4   First attempt at using the new PCI stuff
- *    29.04.98   0.5   Fix hang when ^C is pressed on amp
- *    07.05.98   0.6   Don't double lock around stop_*() in *_release()
- *    10.05.98   0.7   First stab at a simple midi interface (no bells&whistles)
- *    14.05.98   0.8   Don't allow excessive interrupt rates
- *    08.06.98   0.9   First release using Alan Cox' soundcore instead of
- *                     miscdevice
- *    05.07.98   0.10  Fixed the driver to correctly maintin OSS style volume
- *                     settings (not sure if this should be standard)
- *                     Fixed many references: f_flags should be f_mode
- *                     -- Gerald Britton <gbritton@mit.edu>
- *    03.08.98   0.11  Now mixer behaviour can basically be selected between
- *                     "OSS documented" and "OSS actual" behaviour
- *                     Fixed mixer table thanks to Hakan.Lennestal@lu.erisoft.se
- *                     On module startup, set DAC2 to 11kSPS instead of 5.5kSPS,
- *                     as it produces an annoying ssssh in the lower sampling rate
- *                     Do not include modversions.h
- *    22.08.98   0.12  Mixer registers actually have 5 instead of 4 bits
- *                     pointed out by Itai Nahshon
- *    31.08.98   0.13  Fix realplayer problems - dac.count issues
- *    08.10.98   0.14  Joystick support fixed
- *		       -- Oliver Neukum <c188@org.chemie.uni-muenchen.de>
- *    10.12.98   0.15  Fix drain_dac trying to wait on not yet initialized DMA
- *    16.12.98   0.16  Don't wake up app until there are fragsize bytes to read/write
- *    06.01.99   0.17  remove the silly SA_INTERRUPT flag.
- *                     hopefully killed the egcs section type conflict
- *    12.03.99   0.18  cinfo.blocks should be reset after GETxPTR ioctl.
- *                     reported by Johan Maes <joma@telindus.be>
- *    22.03.99   0.19  return EAGAIN instead of EBUSY when O_NONBLOCK
- *                     read/write cannot be executed
- *    07.04.99   0.20  implemented the following ioctl's: SOUND_PCM_READ_RATE, 
- *                     SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; 
- *                     Alpha fixes reported by Peter Jones <pjones@redhat.com>
- *                     Note: joystick address handling might still be wrong on archs
- *                     other than i386
- *    10.05.99   0.21  Added support for an electret mic for SB PCI64
- *                     to the Linux kernel sound driver. This mod also straighten
- *                     out the question marks around the mic impedance setting
- *                     (micz). From Kim.Berts@fisub.mail.abb.com
- *    11.05.99   0.22  Implemented the IMIX call to mute recording monitor.
- *                     Guenter Geiger <geiger@epy.co.at>
- *    15.06.99   0.23  Fix bad allocation bug.
- *                     Thanks to Deti Fliegl <fliegl@in.tum.de>
- *    28.06.99   0.24  Add pci_set_master
- *    02.08.99   0.25  Added workaround for the "phantom write" bug first
- *                     documented by Dave Sharpless from Anchor Games
- *    03.08.99   0.26  adapt to Linus' new __setup/__initcall
- *                     added kernel command line option "es1370=joystick[,lineout[,micbias]]"
- *                     removed CONFIG_SOUND_ES1370_JOYPORT_BOOT kludge
- *    12.08.99   0.27  module_init/__setup fixes
- *    19.08.99   0.28  SOUND_MIXER_IMIX fixes, reported by Gianluca <gialluca@mail.tiscalinet.it>
- *    31.08.99   0.29  add spin_lock_init
- *                     __initlocaldata to fix gcc 2.7.x problems
- *    03.09.99   0.30  change read semantics for MIDI to match
- *                     OSS more closely; remove possible wakeup race
+ *    26.03.1998   0.1   Initial release
+ *    31.03.1998   0.2   Fix bug in GETOSPACE
+ *    04.04.1998   0.3   Make it work (again) under 2.0.33
+ *                       Fix mixer write operation not returning the actual
+ *                       settings
+ *    05.04.1998   0.4   First attempt at using the new PCI stuff
+ *    29.04.1998   0.5   Fix hang when ^C is pressed on amp
+ *    07.05.1998   0.6   Don't double lock around stop_*() in *_release()
+ *    10.05.1998   0.7   First stab at a simple midi interface (no bells&whistles)
+ *    14.05.1998   0.8   Don't allow excessive interrupt rates
+ *    08.06.1998   0.9   First release using Alan Cox' soundcore instead of
+ *                       miscdevice
+ *    05.07.1998   0.10  Fixed the driver to correctly maintin OSS style volume
+ *                       settings (not sure if this should be standard)
+ *                       Fixed many references: f_flags should be f_mode
+ *                       -- Gerald Britton <gbritton@mit.edu>
+ *    03.08.1998   0.11  Now mixer behaviour can basically be selected between
+ *                       "OSS documented" and "OSS actual" behaviour
+ *                       Fixed mixer table thanks to Hakan.Lennestal@lu.erisoft.se
+ *                       On module startup, set DAC2 to 11kSPS instead of 5.5kSPS,
+ *                       as it produces an annoying ssssh in the lower sampling rate
+ *                       Do not include modversions.h
+ *    22.08.1998   0.12  Mixer registers actually have 5 instead of 4 bits
+ *                       pointed out by Itai Nahshon
+ *    31.08.1998   0.13  Fix realplayer problems - dac.count issues
+ *    08.10.1998   0.14  Joystick support fixed
+ *		         -- Oliver Neukum <c188@org.chemie.uni-muenchen.de>
+ *    10.12.1998   0.15  Fix drain_dac trying to wait on not yet initialized DMA
+ *    16.12.1998   0.16  Don't wake up app until there are fragsize bytes to read/write
+ *    06.01.1999   0.17  remove the silly SA_INTERRUPT flag.
+ *                       hopefully killed the egcs section type conflict
+ *    12.03.1999   0.18  cinfo.blocks should be reset after GETxPTR ioctl.
+ *                       reported by Johan Maes <joma@telindus.be>
+ *    22.03.1999   0.19  return EAGAIN instead of EBUSY when O_NONBLOCK
+ *                       read/write cannot be executed
+ *    07.04.1999   0.20  implemented the following ioctl's: SOUND_PCM_READ_RATE, 
+ *                       SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; 
+ *                       Alpha fixes reported by Peter Jones <pjones@redhat.com>
+ *                       Note: joystick address handling might still be wrong on archs
+ *                       other than i386
+ *    10.05.1999   0.21  Added support for an electret mic for SB PCI64
+ *                       to the Linux kernel sound driver. This mod also straighten
+ *                       out the question marks around the mic impedance setting
+ *                       (micz). From Kim.Berts@fisub.mail.abb.com
+ *    11.05.1999   0.22  Implemented the IMIX call to mute recording monitor.
+ *                       Guenter Geiger <geiger@epy.co.at>
+ *    15.06.1999   0.23  Fix bad allocation bug.
+ *                       Thanks to Deti Fliegl <fliegl@in.tum.de>
+ *    28.06.1999   0.24  Add pci_set_master
+ *    02.08.1999   0.25  Added workaround for the "phantom write" bug first
+ *                       documented by Dave Sharpless from Anchor Games
+ *    03.08.1999   0.26  adapt to Linus' new __setup/__initcall
+ *                       added kernel command line option "es1370=joystick[,lineout[,micbias]]"
+ *                       removed CONFIG_SOUND_ES1370_JOYPORT_BOOT kludge
+ *    12.08.1999   0.27  module_init/__setup fixes
+ *    19.08.1999   0.28  SOUND_MIXER_IMIX fixes, reported by Gianluca <gialluca@mail.tiscalinet.it>
+ *    31.08.1999   0.29  add spin_lock_init
+ *                       __initlocaldata to fix gcc 2.7.x problems
+ *                       replaced current->state = x with set_current_state(x)
+ *    03.09.1999   0.30  change read semantics for MIDI to match
+ *                       OSS more closely; remove possible wakeup race
+ *    28.10.1999   0.31  More waitqueue races fixed
  *
  * some important things missing in Ensoniq documentation:
  *
@@ -160,6 +162,26 @@
 
 /* --------------------------------------------------------------------- */
 
+#ifdef MODULE
+#define __exit
+#define module_exit(x) void cleanup_module(void) { x(); }
+#define module_init(x) int init_module(void) { return x(); }
+#else
+#define __exit __attribute__ ((unused, __section__ (".text.init")))
+#define module_exit(x) /* nothing */
+#define module_init(x) /* nothing */
+#endif
+
+#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL
+#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL}
+#define wait_queue_head_t struct wait_queue *
+#define init_waitqueue_head(w) *(w) = 0
+#define init_MUTEX(m) *(m) = MUTEX
+#define __set_current_state(x) do { current->state = (x); } while (0)
+#define set_current_state(x) __set_current_state(x)
+
+/* --------------------------------------------------------------------- */
+
 #ifndef PCI_VENDOR_ID_ENSONIQ
 #define PCI_VENDOR_ID_ENSONIQ        0x1274    
 #endif
@@ -332,7 +354,7 @@
 	spinlock_t lock;
 	struct semaphore open_sem;
 	mode_t open_mode;
-	struct wait_queue *open_wait;
+	wait_queue_head_t open_wait;
 
 	struct dmabuf {
 		void *rawbuf;
@@ -343,7 +365,7 @@
 		unsigned total_bytes;
 		int count;
 		unsigned error; /* over/underrun */
-		struct wait_queue *wait;
+		wait_queue_head_t wait;
 		/* redundant, but makes calculations easier */
 		unsigned fragsize;
 		unsigned dmasize;
@@ -361,8 +383,8 @@
 	struct {
 		unsigned ird, iwr, icnt;
 		unsigned ord, owr, ocnt;
-		struct wait_queue *iwait;
-		struct wait_queue *owait;
+		wait_queue_head_t iwait;
+		wait_queue_head_t owait;
 		unsigned char ibuf[MIDIINBUF];
 		unsigned char obuf[MIDIOUTBUF];
 	} midi;
@@ -1057,15 +1079,15 @@
 
 static int drain_dac1(struct es1370_state *s, int nonblock)
 {
-	struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	unsigned long flags;
 	int count, tmo;
 	
 	if (s->dma_dac1.mapped || !s->dma_dac1.ready)
 		return 0;
-        current->state = TASK_INTERRUPTIBLE;
         add_wait_queue(&s->dma_dac1.wait, &wait);
         for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
                 spin_lock_irqsave(&s->lock, flags);
 		count = s->dma_dac1.count;
                 spin_unlock_irqrestore(&s->lock, flags);
@@ -1075,7 +1097,7 @@
                         break;
                 if (nonblock) {
                         remove_wait_queue(&s->dma_dac1.wait, &wait);
-                        current->state = TASK_RUNNING;
+                        set_current_state(TASK_RUNNING);
                         return -EBUSY;
                 }
 		tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2
@@ -1085,7 +1107,7 @@
 			DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");)
         }
         remove_wait_queue(&s->dma_dac1.wait, &wait);
-        current->state = TASK_RUNNING;
+        set_current_state(TASK_RUNNING);
         if (signal_pending(current))
                 return -ERESTARTSYS;
         return 0;
@@ -1093,15 +1115,15 @@
 
 static int drain_dac2(struct es1370_state *s, int nonblock)
 {
-        struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	unsigned long flags;
 	int count, tmo;
 
 	if (s->dma_dac2.mapped || !s->dma_dac2.ready)
 		return 0;
-        current->state = TASK_INTERRUPTIBLE;
         add_wait_queue(&s->dma_dac2.wait, &wait);
         for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
                 spin_lock_irqsave(&s->lock, flags);
 		count = s->dma_dac2.count;
                 spin_unlock_irqrestore(&s->lock, flags);
@@ -1111,7 +1133,7 @@
                         break;
                 if (nonblock) {
                         remove_wait_queue(&s->dma_dac2.wait, &wait);
-                        current->state = TASK_RUNNING;
+                        set_current_state(TASK_RUNNING);
                         return -EBUSY;
                 }
 		tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2
@@ -1121,7 +1143,7 @@
 			DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");)
         }
         remove_wait_queue(&s->dma_dac2.wait, &wait);
-        current->state = TASK_RUNNING;
+        set_current_state(TASK_RUNNING);
         if (signal_pending(current))
                 return -ERESTARTSYS;
         return 0;
@@ -1132,6 +1154,7 @@
 static ssize_t es1370_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned swptr;
@@ -1147,26 +1170,38 @@
 	if (!access_ok(VERIFY_WRITE, buffer, count))
 		return -EFAULT;
 	ret = 0;
+        add_wait_queue(&s->dma_adc.wait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		swptr = s->dma_adc.swptr;
 		cnt = s->dma_adc.dmasize-swptr;
 		if (s->dma_adc.count < cnt)
 			cnt = s->dma_adc.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
 		if (cnt <= 0) {
 			start_adc(s);
-			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EAGAIN;
-			interruptible_sleep_on(&s->dma_adc.wait);
-			if (signal_pending(current))
-				return ret ? ret : -ERESTARTSYS;
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		swptr = (swptr + cnt) % s->dma_adc.dmasize;
 		spin_lock_irqsave(&s->lock, flags);
 		s->dma_adc.swptr = swptr;
@@ -1177,12 +1212,15 @@
 		ret += cnt;
 		start_adc(s);
 	}
+        remove_wait_queue(&s->dma_adc.wait, &wait);
+	set_current_state(TASK_RUNNING);
 	return ret;
 }
 
 static ssize_t es1370_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned swptr;
@@ -1198,6 +1236,7 @@
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
 	ret = 0;
+        add_wait_queue(&s->dma_dac2.wait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		if (s->dma_dac2.count < 0) {
@@ -1208,20 +1247,31 @@
 		cnt = s->dma_dac2.dmasize-swptr;
 		if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize)
 			cnt = s->dma_dac2.dmasize - s->dma_dac2.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
 		if (cnt <= 0) {
 			start_dac2(s);
-			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EAGAIN;
-			interruptible_sleep_on(&s->dma_dac2.wait);
-			if (signal_pending(current))
-				return ret ? ret : -ERESTARTSYS;
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		swptr = (swptr + cnt) % s->dma_dac2.dmasize;
 		spin_lock_irqsave(&s->lock, flags);
 		s->dma_dac2.swptr = swptr;
@@ -1233,6 +1283,8 @@
 		ret += cnt;
 		start_dac2(s);
 	}
+        remove_wait_queue(&s->dma_dac2.wait, &wait);
+	set_current_state(TASK_RUNNING);
 	return ret;
 }
 
@@ -1607,6 +1659,7 @@
 static int es1370_open(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
 	struct es1370_state *s = devs;
 	unsigned long flags;
 
@@ -1623,8 +1676,12 @@
 			up(&s->open_sem);
 			return -EBUSY;
 		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
 		up(&s->open_sem);
-		interruptible_sleep_on(&s->open_wait);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
 		if (signal_pending(current))
 			return -ERESTARTSYS;
 		down(&s->open_sem);
@@ -1675,8 +1732,8 @@
 		dealloc_dmabuf(&s->dma_adc);
 	}
 	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
-	up(&s->open_sem);
 	wake_up(&s->open_wait);
+	up(&s->open_sem);
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -1704,6 +1761,7 @@
 static ssize_t es1370_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret = 0;
 	unsigned long flags;
 	unsigned swptr;
@@ -1718,6 +1776,7 @@
 		return ret;
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
+        add_wait_queue(&s->dma_dac1.wait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		if (s->dma_dac1.count < 0) {
@@ -1728,20 +1787,31 @@
 		cnt = s->dma_dac1.dmasize-swptr;
 		if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize)
 			cnt = s->dma_dac1.dmasize - s->dma_dac1.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
 		if (cnt <= 0) {
 			start_dac1(s);
-			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EAGAIN;
-			interruptible_sleep_on(&s->dma_dac1.wait);
-			if (signal_pending(current))
-				return ret ? ret : -ERESTARTSYS;
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		swptr = (swptr + cnt) % s->dma_dac1.dmasize;
 		spin_lock_irqsave(&s->lock, flags);
 		s->dma_dac1.swptr = swptr;
@@ -1753,6 +1823,8 @@
 		ret += cnt;
 		start_dac1(s);
 	}
+        remove_wait_queue(&s->dma_dac1.wait, &wait);
+	set_current_state(TASK_RUNNING);
 	return ret;
 }
 
@@ -1990,6 +2062,7 @@
 static int es1370_open_dac(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
 	struct es1370_state *s = devs;
 	unsigned long flags;
 
@@ -2013,8 +2086,12 @@
 			up(&s->open_sem);
 			return -EBUSY;
 		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
 		up(&s->open_sem);
-		interruptible_sleep_on(&s->open_wait);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
 		if (signal_pending(current))
 			return -ERESTARTSYS;
 		down(&s->open_sem);
@@ -2046,8 +2123,8 @@
 	stop_dac1(s);
 	dealloc_dmabuf(&s->dma_dac1);
 	s->open_mode &= ~FMODE_DAC;
-	up(&s->open_sem);
 	wake_up(&s->open_wait);
+	up(&s->open_sem);
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -2075,7 +2152,7 @@
 static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct es1370_state *s = (struct es1370_state *)file->private_data;
-	struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -2096,6 +2173,8 @@
 		cnt = MIDIINBUF - ptr;
 		if (s->midi.icnt < cnt)
 			cnt = s->midi.icnt;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
@@ -2105,7 +2184,6 @@
 					ret = -EAGAIN;
 				break;
 			}
-			current->state = TASK_INTERRUPTIBLE;
 			schedule();
 			if (signal_pending(current)) {
 				if (!ret)
@@ -2129,7 +2207,7 @@
 		ret += cnt;
 		break;
 	}
-	current->state = TASK_RUNNING;
+	__set_current_state(TASK_RUNNING);
         remove_wait_queue(&s->midi.iwait, &wait);
 	return ret;
 }
@@ -2137,7 +2215,7 @@
 static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct es1370_state *s = (struct es1370_state *)file->private_data;
-	struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -2158,8 +2236,10 @@
 		cnt = MIDIOUTBUF - ptr;
 		if (s->midi.ocnt + cnt > MIDIOUTBUF)
 			cnt = MIDIOUTBUF - s->midi.ocnt;
-		if (cnt <= 0)
+		if (cnt <= 0) {
+			__set_current_state(TASK_INTERRUPTIBLE);
 			es1370_handle_midi(s);
+		}
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
@@ -2169,7 +2249,6 @@
 					ret = -EAGAIN;
 				break;
 			}
-			current->state = TASK_INTERRUPTIBLE;
 			schedule();
 			if (signal_pending(current)) {
 				if (!ret)
@@ -2195,7 +2274,7 @@
 		es1370_handle_midi(s);
 		spin_unlock_irqrestore(&s->lock, flags);
 	}
-	current->state = TASK_RUNNING;
+	__set_current_state(TASK_RUNNING);
         remove_wait_queue(&s->midi.owait, &wait);
 	return ret;
 }
@@ -2227,6 +2306,7 @@
 static int es1370_midi_open(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
 	struct es1370_state *s = devs;
 	unsigned long flags;
 
@@ -2243,8 +2323,12 @@
 			up(&s->open_sem);
 			return -EBUSY;
 		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
 		up(&s->open_sem);
-		interruptible_sleep_on(&s->open_wait);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
 		if (signal_pending(current))
 			return -ERESTARTSYS;
 		down(&s->open_sem);
@@ -2276,16 +2360,16 @@
 static int es1370_midi_release(struct inode *inode, struct file *file)
 {
 	struct es1370_state *s = (struct es1370_state *)file->private_data;
-	struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	unsigned long flags;
 	unsigned count, tmo;
 
 	VALIDATE_STATE(s);
 
 	if (file->f_mode & FMODE_WRITE) {
-		current->state = TASK_INTERRUPTIBLE;
 		add_wait_queue(&s->midi.owait, &wait);
 		for (;;) {
+			__set_current_state(TASK_INTERRUPTIBLE);
 			spin_lock_irqsave(&s->lock, flags);
 			count = s->midi.ocnt;
 			spin_unlock_irqrestore(&s->lock, flags);
@@ -2295,7 +2379,7 @@
 				break;
 			if (file->f_flags & O_NONBLOCK) {
 				remove_wait_queue(&s->midi.owait, &wait);
-				current->state = TASK_RUNNING;
+				set_current_state(TASK_RUNNING);
 				return -EBUSY;
 			}
 			tmo = (count * HZ) / 3100;
@@ -2303,7 +2387,7 @@
 				DBG(printk(KERN_DEBUG "es1370: midi timed out??\n");)
 		}
 		remove_wait_queue(&s->midi.owait, &wait);
-		current->state = TASK_RUNNING;
+		set_current_state(TASK_RUNNING);
 	}
 	down(&s->open_sem);
 	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
@@ -2313,8 +2397,8 @@
 		outl(s->ctrl, s->io+ES1370_REG_CONTROL);
 	}
 	spin_unlock_irqrestore(&s->lock, flags);
-	up(&s->open_sem);
 	wake_up(&s->open_wait);
+	up(&s->open_sem);
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -2380,7 +2464,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "es1370: version v0.30 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "es1370: version v0.31 time " __TIME__ " " __DATE__ "\n");
 	while (index < NR_DEVICE && 
 	       (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) {
 		if (pcidev->base_address[0] == 0 || 
@@ -2393,13 +2477,13 @@
 			continue;
 		}
 		memset(s, 0, sizeof(struct es1370_state));
-		init_waitqueue(&s->dma_adc.wait);
-		init_waitqueue(&s->dma_dac1.wait);
-		init_waitqueue(&s->dma_dac2.wait);
-		init_waitqueue(&s->open_wait);
-		init_waitqueue(&s->midi.iwait);
-		init_waitqueue(&s->midi.owait);
-		s->open_sem = MUTEX;
+		init_waitqueue_head(&s->dma_adc.wait);
+		init_waitqueue_head(&s->dma_dac1.wait);
+		init_waitqueue_head(&s->dma_dac2.wait);
+		init_waitqueue_head(&s->open_wait);
+		init_waitqueue_head(&s->midi.iwait);
+		init_waitqueue_head(&s->midi.owait);
+		init_MUTEX(&s->open_sem);
 		spin_lock_init(&s->lock);
 		s->magic = ES1370_MAGIC;
 		s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/sound/es1371.c linux.15p1/drivers/sound/es1371.c
--- linux.vanilla/drivers/sound/es1371.c	Tue Jan  4 21:18:59 2000
+++ linux.15p1/drivers/sound/es1371.c	Wed Jan 12 01:26:27 2000
@@ -3,7 +3,7 @@
 /*
  *      es1371.c  --  Creative Ensoniq ES1371.
  *
- *      Copyright (C) 1998-1999  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *      Copyright (C) 1998-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
  *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
@@ -38,57 +38,64 @@
  *  there are several MIDI to PCM (WAV) packages, one of them is timidity.
  *
  *  Revision history
- *    04.06.98   0.1   Initial release
- *                     Mixer stuff should be overhauled; especially optional AC97 mixer bits
- *                     should be detected. This results in strange behaviour of some mixer
- *                     settings, like master volume and mic.
- *    08.06.98   0.2   First release using Alan Cox' soundcore instead of miscdevice
- *    03.08.98   0.3   Do not include modversions.h
- *                     Now mixer behaviour can basically be selected between
- *                     "OSS documented" and "OSS actual" behaviour
- *    31.08.98   0.4   Fix realplayer problems - dac.count issues
- *    27.10.98   0.5   Fix joystick support
- *                     -- Oliver Neukum (c188@org.chemie.uni-muenchen.de)
- *    10.12.98   0.6   Fix drain_dac trying to wait on not yet initialized DMA
- *    23.12.98   0.7   Fix a few f_file & FMODE_ bugs
- *                     Don't wake up app until there are fragsize bytes to read/write
- *    06.01.99   0.8   remove the silly SA_INTERRUPT flag.
- *                     hopefully killed the egcs section type conflict
- *    12.03.99   0.9   cinfo.blocks should be reset after GETxPTR ioctl.
- *                     reported by Johan Maes <joma@telindus.be>
- *    22.03.99   0.10  return EAGAIN instead of EBUSY when O_NONBLOCK
- *                     read/write cannot be executed
- *    07.04.99   0.11  implemented the following ioctl's: SOUND_PCM_READ_RATE, 
- *                     SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; 
- *                     Alpha fixes reported by Peter Jones <pjones@redhat.com>
- *                     Another Alpha fix (wait_src_ready in init routine)
- *                     reported by "Ivan N. Kokshaysky" <ink@jurassic.park.msu.ru>
- *                     Note: joystick address handling might still be wrong on archs
- *                     other than i386
- *    15.06.99   0.12  Fix bad allocation bug.
- *                     Thanks to Deti Fliegl <fliegl@in.tum.de>
- *    28.06.99   0.13  Add pci_set_master
- *    03.08.99   0.14  adapt to Linus' new __setup/__initcall
- *                     added kernel command line option "es1371=joystickaddr"
- *                     removed CONFIG_SOUND_ES1371_JOYPORT_BOOT kludge
- *    10.08.99   0.15  (Re)added S/PDIF module option for cards revision >= 4.
- *                     Initial version by Dave Platt <dplatt@snulbug.mtview.ca.us>.
- *                     module_init/__setup fixes
- *    08.16.99   0.16  Joe Cotellese <joec@ensoniq.com>
- *                     Added detection for ES1371 revision ID so that we can
- *                     detect the ES1373 and later parts.
- *                     added AC97 #defines for readability
- *                     added a /proc file system for dumping hardware state
- *                     updated SRC and CODEC w/r functions to accomodate bugs
- *                     in some versions of the ES137x chips.
- *    31.08.99   0.17  add spin_lock_init
- *                     __initlocaldata to fix gcc 2.7.x problems
- *                     replaced current->state = x with set_current_state(x)
- *    03.09.99   0.18  change read semantics for MIDI to match
- *                     OSS more closely; remove possible wakeup race
- *    21.10.99   0.19  Round sampling rates, requested by
- *                     Kasamatsu Kenichi <t29w0267@ip.media.kyoto-u.ac.jp>
- *
+ *    04.06.1998   0.1   Initial release
+ *                       Mixer stuff should be overhauled; especially optional AC97 mixer bits
+ *                       should be detected. This results in strange behaviour of some mixer
+ *                       settings, like master volume and mic.
+ *    08.06.1998   0.2   First release using Alan Cox' soundcore instead of miscdevice
+ *    03.08.1998   0.3   Do not include modversions.h
+ *                       Now mixer behaviour can basically be selected between
+ *                       "OSS documented" and "OSS actual" behaviour
+ *    31.08.1998   0.4   Fix realplayer problems - dac.count issues
+ *    27.10.1998   0.5   Fix joystick support
+ *                       -- Oliver Neukum (c188@org.chemie.uni-muenchen.de)
+ *    10.12.1998   0.6   Fix drain_dac trying to wait on not yet initialized DMA
+ *    23.12.1998   0.7   Fix a few f_file & FMODE_ bugs
+ *                       Don't wake up app until there are fragsize bytes to read/write
+ *    06.01.1999   0.8   remove the silly SA_INTERRUPT flag.
+ *                       hopefully killed the egcs section type conflict
+ *    12.03.1999   0.9   cinfo.blocks should be reset after GETxPTR ioctl.
+ *                       reported by Johan Maes <joma@telindus.be>
+ *    22.03.1999   0.10  return EAGAIN instead of EBUSY when O_NONBLOCK
+ *                       read/write cannot be executed
+ *    07.04.1999   0.11  implemented the following ioctl's: SOUND_PCM_READ_RATE, 
+ *                       SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; 
+ *                       Alpha fixes reported by Peter Jones <pjones@redhat.com>
+ *                       Another Alpha fix (wait_src_ready in init routine)
+ *                       reported by "Ivan N. Kokshaysky" <ink@jurassic.park.msu.ru>
+ *                       Note: joystick address handling might still be wrong on archs
+ *                       other than i386
+ *    15.06.1999   0.12  Fix bad allocation bug.
+ *                       Thanks to Deti Fliegl <fliegl@in.tum.de>
+ *    28.06.1999   0.13  Add pci_set_master
+ *    03.08.1999   0.14  adapt to Linus' new __setup/__initcall
+ *                       added kernel command line option "es1371=joystickaddr"
+ *                       removed CONFIG_SOUND_ES1371_JOYPORT_BOOT kludge
+ *    10.08.1999   0.15  (Re)added S/PDIF module option for cards revision >= 4.
+ *                       Initial version by Dave Platt <dplatt@snulbug.mtview.ca.us>.
+ *                       module_init/__setup fixes
+ *    08.16.1999   0.16  Joe Cotellese <joec@ensoniq.com>
+ *                       Added detection for ES1371 revision ID so that we can
+ *                       detect the ES1373 and later parts.
+ *                       added AC97 #defines for readability
+ *                       added a /proc file system for dumping hardware state
+ *                       updated SRC and CODEC w/r functions to accomodate bugs
+ *                       in some versions of the ES137x chips.
+ *    31.08.1999   0.17  add spin_lock_init
+ *                       __initlocaldata to fix gcc 2.7.x problems
+ *                       replaced current->state = x with set_current_state(x)
+ *    03.09.1999   0.18  change read semantics for MIDI to match
+ *                       OSS more closely; remove possible wakeup race
+ *    21.10.1999   0.19  Round sampling rates, requested by
+ *                       Kasamatsu Kenichi <t29w0267@ip.media.kyoto-u.ac.jp>
+ *    27.10.1999   0.20  Added SigmaTel 3D enhancement string
+ *                       Codec ID printing changes
+ *    28.10.1999   0.21  More waitqueue races fixed
+ *                       Joe Cotellese <joec@ensoniq.com>
+ *                       Changed PCI detection routine so we can more easily
+ *                       detect ES137x chip and derivatives.
+ *    05.01.2000   0.22  Should now work with rev7 boards; patch by
+ *                       Eric Lemar, elemar@cs.washington.edu
  */
 
 /*****************************************************************************/
@@ -118,16 +125,51 @@
 
 #undef OSS_DOCUMENTED_MIXER_SEMANTICS
 #undef ES1371_DEBUG
+#define DBG(x) {}
+/*#define DBG(x) {x}*/
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+#define __exit
+#define module_exit(x) void cleanup_module(void) { x(); }
+#define module_init(x) int init_module(void) { return x(); }
+#else
+#define __exit __attribute__ ((unused, __section__ (".text.init")))
+#define module_exit(x) /* nothing */
+#define module_init(x) /* nothing */
+#endif
+
+#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL
+#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL}
+#define wait_queue_head_t struct wait_queue *
+#define init_waitqueue_head(w) *(w) = 0
+#define init_MUTEX(m) *(m) = MUTEX
+#define __set_current_state(x) do { current->state = (x); } while (0)
+#define set_current_state(x) __set_current_state(x)
 
 /* --------------------------------------------------------------------- */
 
 #ifndef PCI_VENDOR_ID_ENSONIQ
 #define PCI_VENDOR_ID_ENSONIQ        0x1274    
 #endif
+
+#ifndef PCI_VENDOR_ID_ECTIVA
+#define PCI_VENDOR_ID_ECTIVA         0x1102
+#endif
+
 #ifndef PCI_DEVICE_ID_ENSONIQ_ES1371
 #define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371
 #endif
 
+#ifndef PCI_DEVICE_ID_ENSONIQ_CT5880
+#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880
+#endif
+
+#ifndef PCI_DEVICE_ID_ECTIVA_EV1938
+#define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938
+#endif
+
 /* ES1371 chip ID */
 /* This is a little confusing because all ES1371 compatible chips have the
    same DEVICE_ID, the only thing differentiating them is the REV_ID field.
@@ -137,8 +179,9 @@
 #define ES1371REV_ES1373_A  0x04
 #define ES1371REV_ES1373_B  0x06
 #define ES1371REV_CT5880_A  0x07
+#define CT5880REV_CT5880_C  0x02
 #define ES1371REV_ES1371_B  0x09
-
+#define EV1938REV_EV1938_A  0x00
 
 #define ES1371_MAGIC  ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1371)
 
@@ -146,7 +189,7 @@
 #define JOY_EXTENT                8
 
 #define ES1371_REG_CONTROL        0x00
-#define ES1371_REG_STATUS         0x04
+#define ES1371_REG_STATUS         0x04 /* on the 5880 it is control/status */
 #define ES1371_REG_UART_DATA      0x08
 #define ES1371_REG_UART_STATUS    0x09
 #define ES1371_REG_UART_CONTROL   0x09
@@ -214,6 +257,7 @@
 
 
 #define STAT_INTR       0x80000000  /* wired or of all interrupt bits */
+#define CSTAT_5880_AC97_RST 0x20000000 /* CT5880 Reset bit */
 #define STAT_EN_SPDIF   0x00040000  /* enable S/PDIF circuitry */
 #define STAT_TS_SPDIF   0x00020000  /* test S/PDIF circuitry */
 #define STAT_TESTMODE   0x00010000  /* test ASIC */
@@ -372,7 +416,13 @@
 	"NVidea 3D Stereo Enhancement",
 	"Philips Incredible Sound",
 	"Texas Instruments 3D Stereo Enhancement",
-	"VLSI Technology 3D Stereo Enhancement"
+	"VLSI Technology 3D Stereo Enhancement",
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	"SigmaTel SS3D"
 };
 
 /* --------------------------------------------------------------------- */
@@ -393,8 +443,12 @@
 	/* hardware resources */
 	unsigned long io; /* long for SPARC */
 	unsigned int irq;
+
+	/* PCI ID's */
+	u16 vendor;
+	u16 device;
         u8 rev; /* the chip revision */
-        
+
 #ifdef ES1371_DEBUG
         /* debug /proc entry */
 	struct proc_dir_entry *ps;
@@ -416,7 +470,7 @@
 	spinlock_t lock;
 	struct semaphore open_sem;
 	mode_t open_mode;
-	struct wait_queue *open_wait;
+	wait_queue_head_t open_wait;
 
 	struct dmabuf {
 		void *rawbuf;
@@ -427,7 +481,7 @@
 		unsigned total_bytes;
 		int count;
 		unsigned error; /* over/underrun */
-		struct wait_queue *wait;
+		wait_queue_head_t wait;
 		/* redundant, but makes calculations easier */
 		unsigned fragsize;
 		unsigned dmasize;
@@ -445,8 +499,8 @@
 	struct {
 		unsigned ird, iwr, icnt;
 		unsigned ord, owr, ocnt;
-		struct wait_queue *iwait;
-		struct wait_queue *owait;
+		wait_queue_head_t iwait;
+		wait_queue_head_t owait;
 		unsigned char ibuf[MIDIINBUF];
 		unsigned char obuf[MIDIOUTBUF];
 	} midi;
@@ -1228,7 +1282,7 @@
 		
 	case SOUND_MIXER_SPEAKER:
 		j = rdcodec(s, AC97_PCBEEP_VOL);
-		if (j & AC97_MUTE
+		if (j & AC97_MUTE)
 			return put_user(0, (int *)arg);
 		return put_user(0x6464 - ((j >> 1) & 0xf) * 0x606, (int *)arg);
 		
@@ -1607,15 +1661,15 @@
 
 static int drain_dac1(struct es1371_state *s, int nonblock)
 {
-        struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	unsigned long flags;
 	int count, tmo;
 	
 	if (s->dma_dac1.mapped || !s->dma_dac1.ready)
 		return 0;
-        current->state = TASK_INTERRUPTIBLE;
         add_wait_queue(&s->dma_dac1.wait, &wait);
         for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
                 spin_lock_irqsave(&s->lock, flags);
 		count = s->dma_dac1.count;
                 spin_unlock_irqrestore(&s->lock, flags);
@@ -1625,16 +1679,16 @@
                         break;
                 if (nonblock) {
                         remove_wait_queue(&s->dma_dac1.wait, &wait);
-                        current->state = TASK_RUNNING;
+                        set_current_state(TASK_RUNNING);
                         return -EBUSY;
                 }
 		tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 / s->dac1rate;
 		tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
 		if (!schedule_timeout(tmo + 1))
-			printk(KERN_DEBUG "es1371: dac1 dma timed out??\n");
+			DBG(printk(KERN_DEBUG "es1371: dac1 dma timed out??\n");)
         }
         remove_wait_queue(&s->dma_dac1.wait, &wait);
-        current->state = TASK_RUNNING;
+        set_current_state(TASK_RUNNING);
         if (signal_pending(current))
                 return -ERESTARTSYS;
         return 0;
@@ -1642,15 +1696,15 @@
 
 static int drain_dac2(struct es1371_state *s, int nonblock)
 {
-        struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	unsigned long flags;
 	int count, tmo;
 
 	if (s->dma_dac2.mapped || !s->dma_dac2.ready)
 		return 0;
-        current->state = TASK_INTERRUPTIBLE;
         add_wait_queue(&s->dma_dac2.wait, &wait);
         for (;;) {
+		__set_current_state(TASK_UNINTERRUPTIBLE);
                 spin_lock_irqsave(&s->lock, flags);
 		count = s->dma_dac2.count;
                 spin_unlock_irqrestore(&s->lock, flags);
@@ -1660,16 +1714,16 @@
                         break;
                 if (nonblock) {
                         remove_wait_queue(&s->dma_dac2.wait, &wait);
-                        current->state = TASK_RUNNING;
+                        set_current_state(TASK_RUNNING);
                         return -EBUSY;
                 }
 		tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 / s->dac2rate;
 		tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
 		if (!schedule_timeout(tmo + 1))
-			printk(KERN_DEBUG "es1371: dac2 dma timed out??\n");
+			DBG(printk(KERN_DEBUG "es1371: dac2 dma timed out??\n");)
         }
         remove_wait_queue(&s->dma_dac2.wait, &wait);
-        current->state = TASK_RUNNING;
+        set_current_state(TASK_RUNNING);
         if (signal_pending(current))
                 return -ERESTARTSYS;
         return 0;
@@ -1680,6 +1734,7 @@
 static ssize_t es1371_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned swptr;
@@ -1695,26 +1750,38 @@
 	if (!access_ok(VERIFY_WRITE, buffer, count))
 		return -EFAULT;
 	ret = 0;
+	add_wait_queue(&s->dma_adc.wait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		swptr = s->dma_adc.swptr;
 		cnt = s->dma_adc.dmasize-swptr;
 		if (s->dma_adc.count < cnt)
 			cnt = s->dma_adc.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
 		if (cnt <= 0) {
 			start_adc(s);
-			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EAGAIN;
-			interruptible_sleep_on(&s->dma_adc.wait);
-			if (signal_pending(current))
-				return ret ? ret : -ERESTARTSYS;
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		swptr = (swptr + cnt) % s->dma_adc.dmasize;
 		spin_lock_irqsave(&s->lock, flags);
 		s->dma_adc.swptr = swptr;
@@ -1725,12 +1792,15 @@
 		ret += cnt;
 		start_adc(s);
 	}
+	remove_wait_queue(&s->dma_adc.wait, &wait);
+	set_current_state(TASK_RUNNING);
 	return ret;
 }
 
 static ssize_t es1371_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned swptr;
@@ -1746,6 +1816,7 @@
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
 	ret = 0;
+	add_wait_queue(&s->dma_dac2.wait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		if (s->dma_dac2.count < 0) {
@@ -1756,20 +1827,31 @@
 		cnt = s->dma_dac2.dmasize-swptr;
 		if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize)
 			cnt = s->dma_dac2.dmasize - s->dma_dac2.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
 		if (cnt <= 0) {
 			start_dac2(s);
-			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EAGAIN;
-			interruptible_sleep_on(&s->dma_dac2.wait);
-			if (signal_pending(current))
-				return ret ? ret : -ERESTARTSYS;
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		swptr = (swptr + cnt) % s->dma_dac2.dmasize;
 		spin_lock_irqsave(&s->lock, flags);
 		s->dma_dac2.swptr = swptr;
@@ -1781,6 +1863,8 @@
 		ret += cnt;
 		start_dac2(s);
 	}
+	remove_wait_queue(&s->dma_dac2.wait, &wait);
+	set_current_state(TASK_RUNNING);
 	return ret;
 }
 
@@ -2150,6 +2234,7 @@
 static int es1371_open(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
 	struct es1371_state *s = devs;
 	unsigned long flags;
 
@@ -2166,8 +2251,12 @@
 			up(&s->open_sem);
 			return -EBUSY;
 		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
 		up(&s->open_sem);
-		interruptible_sleep_on(&s->open_wait);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
 		if (signal_pending(current))
 			return -ERESTARTSYS;
 		down(&s->open_sem);
@@ -2249,6 +2338,7 @@
 static ssize_t es1371_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret = 0;
 	unsigned long flags;
 	unsigned swptr;
@@ -2263,6 +2353,7 @@
 		return ret;
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
+	add_wait_queue(&s->dma_dac1.wait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		if (s->dma_dac1.count < 0) {
@@ -2273,20 +2364,31 @@
 		cnt = s->dma_dac1.dmasize-swptr;
 		if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize)
 			cnt = s->dma_dac1.dmasize - s->dma_dac1.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
 		if (cnt <= 0) {
 			start_dac1(s);
-			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EAGAIN;
-			interruptible_sleep_on(&s->dma_dac1.wait);
-			if (signal_pending(current))
-				return ret ? ret : -ERESTARTSYS;
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		swptr = (swptr + cnt) % s->dma_dac1.dmasize;
 		spin_lock_irqsave(&s->lock, flags);
 		s->dma_dac1.swptr = swptr;
@@ -2298,6 +2400,8 @@
 		ret += cnt;
 		start_dac1(s);
 	}
+	remove_wait_queue(&s->dma_dac1.wait, &wait);
+	set_current_state(TASK_RUNNING);
 	return ret;
 }
 
@@ -2526,6 +2630,7 @@
 static int es1371_open_dac(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
 	struct es1371_state *s = devs;
 	unsigned long flags;
 
@@ -2549,8 +2654,12 @@
 			up(&s->open_sem);
 			return -EBUSY;
 		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
 		up(&s->open_sem);
-		interruptible_sleep_on(&s->open_wait);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
 		if (signal_pending(current))
 			return -ERESTARTSYS;
 		down(&s->open_sem);
@@ -2610,7 +2719,7 @@
 static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct es1371_state *s = (struct es1371_state *)file->private_data;
-	struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -2631,6 +2740,8 @@
 		cnt = MIDIINBUF - ptr;
 		if (s->midi.icnt < cnt)
 			cnt = s->midi.icnt;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
@@ -2640,7 +2751,6 @@
 					ret = -EAGAIN;
 				break;
 			}
-			current->state = TASK_INTERRUPTIBLE;
 			schedule();
 			if (signal_pending(current)) {
 				if (!ret)
@@ -2664,7 +2774,7 @@
 		ret += cnt;
 		break;
 	}
-	current->state = TASK_RUNNING;
+	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(&s->midi.iwait, &wait);
 	return ret;
 }
@@ -2672,7 +2782,7 @@
 static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct es1371_state *s = (struct es1371_state *)file->private_data;
-	struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -2693,8 +2803,10 @@
 		cnt = MIDIOUTBUF - ptr;
 		if (s->midi.ocnt + cnt > MIDIOUTBUF)
 			cnt = MIDIOUTBUF - s->midi.ocnt;
-		if (cnt <= 0)
+		if (cnt <= 0) {
+			__set_current_state(TASK_INTERRUPTIBLE);
 			es1371_handle_midi(s);
+		}
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
@@ -2704,7 +2816,6 @@
 					ret = -EAGAIN;
 				break;
 			}
-			current->state = TASK_INTERRUPTIBLE;
 			schedule();
 			if (signal_pending(current)) {
 				if (!ret)
@@ -2730,7 +2841,7 @@
 		es1371_handle_midi(s);
 		spin_unlock_irqrestore(&s->lock, flags);
 	}
-	current->state = TASK_RUNNING;
+	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(&s->midi.owait, &wait);
 	return ret;
 }
@@ -2762,6 +2873,7 @@
 static int es1371_midi_open(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
 	struct es1371_state *s = devs;
 	unsigned long flags;
 
@@ -2778,8 +2890,12 @@
 			up(&s->open_sem);
 			return -EBUSY;
 		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
 		up(&s->open_sem);
-		interruptible_sleep_on(&s->open_wait);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
 		if (signal_pending(current))
 			return -ERESTARTSYS;
 		down(&s->open_sem);
@@ -2811,15 +2927,15 @@
 static int es1371_midi_release(struct inode *inode, struct file *file)
 {
 	struct es1371_state *s = (struct es1371_state *)file->private_data;
-        struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	unsigned long flags;
 	unsigned count, tmo;
 
 	VALIDATE_STATE(s);
 	if (file->f_mode & FMODE_WRITE) {
-		current->state = TASK_INTERRUPTIBLE;
 		add_wait_queue(&s->midi.owait, &wait);
 		for (;;) {
+			__set_current_state(TASK_INTERRUPTIBLE);
 			spin_lock_irqsave(&s->lock, flags);
 			count = s->midi.ocnt;
 			spin_unlock_irqrestore(&s->lock, flags);
@@ -2829,7 +2945,7 @@
 				break;
 			if (file->f_flags & O_NONBLOCK) {
 				remove_wait_queue(&s->midi.owait, &wait);
-				current->state = TASK_RUNNING;
+				set_current_state(TASK_RUNNING);
 				return -EBUSY;
 			}
 			tmo = (count * HZ) / 3100;
@@ -2837,7 +2953,7 @@
 				printk(KERN_DEBUG "es1371: midi timed out??\n");
 		}
 		remove_wait_queue(&s->midi.owait, &wait);
-		current->state = TASK_RUNNING;
+		set_current_state(TASK_RUNNING);
 	}
 	down(&s->open_sem);
 	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
@@ -2882,7 +2998,7 @@
 static int proc_es1371_dump (char *buf, char **start, off_t fpos, int length, int *eof, void *data)
 {
         int len = 0;
-
+        
         struct es1371_state *s = devs;
         int cnt;
 
@@ -2944,166 +3060,214 @@
 	{ SOUND_MIXER_WRITE_IGAIN, 0x4040 }
 };
 
+__initfunc(static int probe_chip(struct pci_dev *pcidev, int index))
+{
+	struct es1371_state *s;
+	mm_segment_t fs;
+	int i, val, val2;
+	unsigned char id[4];
+	unsigned long tmo;
+	signed long tmo2;
+	unsigned int cssr;
+
+	if (pcidev->base_address[0] == 0 || 
+	    (pcidev->base_address[0] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
+		return -1;
+	if (pcidev->irq == 0) 
+		return -1;
+	if (!(s = kmalloc(sizeof(struct es1371_state), GFP_KERNEL))) {
+		printk(KERN_WARNING "es1371: out of memory\n");
+		return -1;
+	}
+	memset(s, 0, sizeof(struct es1371_state));
+	init_waitqueue_head(&s->dma_adc.wait);
+	init_waitqueue_head(&s->dma_dac1.wait);
+	init_waitqueue_head(&s->dma_dac2.wait);
+	init_waitqueue_head(&s->open_wait);
+	init_waitqueue_head(&s->midi.iwait);
+	init_waitqueue_head(&s->midi.owait);
+	init_MUTEX(&s->open_sem);
+	spin_lock_init(&s->lock);
+	s->magic = ES1371_MAGIC;
+	s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+	s->irq = pcidev->irq;
+	s->vendor = pcidev->vendor;
+	s->device = pcidev->device;
+	pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev);
+	printk(KERN_INFO "es1371: found chip, vendor id 0x%04x device id 0x%04x revision 0x%02x\n",
+	       s->vendor, s->device, s->rev);
+	if (check_region(s->io, ES1371_EXTENT)) {
+		printk(KERN_ERR "es1371: io ports %#lx-%#lx in use\n", s->io, s->io+ES1371_EXTENT-1);
+		goto err_region;
+	}
+	request_region(s->io, ES1371_EXTENT, "es1371");
+	if (request_irq(s->irq, es1371_interrupt, SA_SHIRQ, "es1371", s)) {
+		printk(KERN_ERR "es1371: irq %u in use\n", s->irq);
+		goto err_irq;
+	}
+	printk(KERN_INFO "es1371: found es1371 rev %d at io %#lx irq %u\n"
+	       KERN_INFO "es1371: features: joystick 0x%x\n", s->rev, s->io, s->irq, joystick[index]);
+	/* register devices */
+	if ((s->dev_audio = register_sound_dsp(&es1371_audio_fops, -1)) < 0)
+		goto err_dev1;
+	if ((s->dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1)) < 0)
+		goto err_dev2;
+	if ((s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1)) < 0)
+		goto err_dev3;
+	if ((s->dev_midi = register_sound_midi(&es1371_midi_fops, -1)) < 0)
+		goto err_dev4;
+#ifdef ES1371_DEBUG
+	/* intialize the debug proc device */
+	s->ps = create_proc_read_entry("es1371",0,NULL,proc_es1371_dump,NULL);
+#endif /* ES1371_DEBUG */
+	
+	/* initialize codec registers */
+	s->ctrl = 0;
+	if ((joystick[index] & ~0x18) == 0x200) {
+		if (check_region(joystick[index], JOY_EXTENT))
+			printk(KERN_ERR "es1371: joystick address 0x%x already in use\n", joystick[index]);
+		else {
+			s->ctrl |= CTRL_JYSTK_EN | (((joystick[index] >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT);
+		}
+	}
+	s->sctrl = 0;
+	cssr = 0;
+	/* check to see if s/pdif mode is being requested */
+	if (spdif[index]) {
+		if (s->rev >= 4) {
+			printk(KERN_INFO "es1371: enabling S/PDIF output\n");
+			cssr |= STAT_EN_SPDIF;
+			s->ctrl |= CTRL_SPDIFEN_B;
+		} else {
+			printk(KERN_ERR "es1371: revision %d does not support S/PDIF\n", s->rev);
+		}
+	}
+	/* initialize the chips */
+	outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+	outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+	outl(0, s->io+ES1371_REG_LEGACY);
+	pci_set_master(pcidev);  /* enable bus mastering */
+	/* if we are a 5880 turn on the AC97 */
+	if (s->vendor == PCI_VENDOR_ID_ENSONIQ &&
+	    ((s->device == PCI_DEVICE_ID_ENSONIQ_CT5880 && s->rev == CT5880REV_CT5880_C) || 
+	     (s->device == PCI_DEVICE_ID_ENSONIQ_ES1371 && s->rev == ES1371REV_CT5880_A))) { 
+		cssr |= CSTAT_5880_AC97_RST;
+		outl(cssr, s->io+ES1371_REG_STATUS);
+		/* need to delay around 20ms(bleech) to give
+		   some CODECs enough time to wakeup */
+		tmo = jiffies + (HZ / 50) + 1;
+		for (;;) {
+			tmo2 = tmo - jiffies;
+			if (tmo2 <= 0)
+				break;
+			schedule_timeout(tmo2);
+		}
+	}
+	/* AC97 warm reset to start the bitclk */
+	outl(s->ctrl | CTRL_SYNCRES, s->io+ES1371_REG_CONTROL);
+	udelay(2);
+	outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+	/* init the sample rate converter */
+	src_init(s);
+	/* codec init */
+	wrcodec(s, AC97_RESET, 0); /* reset codec */
+	s->mix.codec_id = rdcodec(s, AC97_RESET);  /* get codec ID */
+	val = rdcodec(s, AC97_VENDOR_ID1);
+	val2 = rdcodec(s, AC97_VENDOR_ID2);
+	id[0] = val >> 8;
+	id[1] = val;
+	id[2] = val2 >> 8;
+	id[3] = 0;
+	if (id[0] <= ' ' || id[0] > 0x7f)
+		id[0] = ' ';
+	if (id[1] <= ' ' || id[1] > 0x7f)
+		id[1] = ' ';
+	if (id[2] <= ' ' || id[2] > 0x7f)
+		id[2] = ' ';
+	printk(KERN_INFO "es1371: codec vendor %s (0x%04x%02x) revision %d (0x%02x)\n", 
+	       id, val & 0xffff, (val2 >> 8) & 0xff, val2 & 0xff, val2 & 0xff);
+	printk(KERN_INFO "es1371: codec features");
+	if (s->mix.codec_id & CODEC_ID_DEDICATEDMIC)
+		printk(" dedicated MIC PCM in");
+	if (s->mix.codec_id & CODEC_ID_MODEMCODEC)
+		printk(" Modem Line Codec");
+	if (s->mix.codec_id & CODEC_ID_BASSTREBLE)
+		printk(" Bass & Treble");
+	if (s->mix.codec_id & CODEC_ID_SIMULATEDSTEREO)
+		printk(" Simulated Stereo");
+	if (s->mix.codec_id & CODEC_ID_HEADPHONEOUT)
+		printk(" Headphone out");
+	if (s->mix.codec_id & CODEC_ID_LOUDNESS)
+		printk(" Loudness");
+	if (s->mix.codec_id & CODEC_ID_18BITDAC)
+		printk(" 18bit DAC");
+	if (s->mix.codec_id & CODEC_ID_20BITDAC)
+		printk(" 20bit DAC");
+	if (s->mix.codec_id & CODEC_ID_18BITADC)
+		printk(" 18bit ADC");
+	if (s->mix.codec_id & CODEC_ID_20BITADC)
+		printk(" 20bit ADC");
+	printk("%s\n", (s->mix.codec_id & 0x3ff) ? "" : " none");
+	val = (s->mix.codec_id >> CODEC_ID_SESHIFT) & CODEC_ID_SEMASK;
+	printk(KERN_INFO "es1371: stereo enhancement: %s\n", 
+	       (val <= 26 && stereo_enhancement[val]) ? stereo_enhancement[val] : "unknown");
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	val = SOUND_MASK_LINE;
+	mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
+	for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) {
+		val = initvol[i].vol;
+		mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
+	}
+	set_fs(fs);
+	/* turn on S/PDIF output driver if requested */
+	outl(cssr, s->io+ES1371_REG_STATUS);
+	/* queue it for later freeing */
+	s->next = devs;
+	devs = s;
+       	return 0;
+
+ err_dev4:
+	unregister_sound_dsp(s->dev_dac);
+ err_dev3:
+	unregister_sound_mixer(s->dev_mixer);
+ err_dev2:
+	unregister_sound_dsp(s->dev_audio);
+ err_dev1:
+	printk(KERN_ERR "es1371: cannot register misc device\n");
+	free_irq(s->irq, s);
+ err_irq:
+	release_region(s->io, ES1371_EXTENT);
+ err_region:
+	kfree_s(s, sizeof(struct es1371_state));
+	return -1;
+}
+
 #ifdef MODULE
 __initfunc(int init_module(void))
 #else
 __initfunc(int init_es1371(void))
 #endif
 {
-	struct es1371_state *s;
-	struct pci_dev *pcidev = NULL;
-	mm_segment_t fs;
-	int i, val, val2, index = 0;
-	unsigned cssr;
+	struct pci_dev *pcidev;
+	int index = 0;
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "es1371: version v0.19 time " __TIME__ " " __DATE__ "\n");
-	while (index < NR_DEVICE && 
-	       (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) {
-		if (pcidev->base_address[0] == 0 || 
-		    (pcidev->base_address[0] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
-			continue;
-		if (pcidev->irq == 0) 
-			continue;
-		if (!(s = kmalloc(sizeof(struct es1371_state), GFP_KERNEL))) {
-			printk(KERN_WARNING "es1371: out of memory\n");
+	printk(KERN_INFO "es1371: version v0.22 time " __TIME__ " " __DATE__ "\n");
+	for (pcidev = pci_devices; pcidev && index < NR_DEVICE; pcidev = pcidev->next) {
+		if (pcidev->vendor == PCI_VENDOR_ID_ENSONIQ) {
+			if (pcidev->device != PCI_DEVICE_ID_ENSONIQ_ES1371 &&
+			    pcidev->device != PCI_DEVICE_ID_ENSONIQ_CT5880)
+				continue;
+		} else if (pcidev->vendor == PCI_VENDOR_ID_ECTIVA) {
+			if (pcidev->device != PCI_DEVICE_ID_ECTIVA_EV1938)
+				continue;
+		} else
 			continue;
-		}
-		memset(s, 0, sizeof(struct es1371_state));
-		init_waitqueue(&s->dma_adc.wait);
-		init_waitqueue(&s->dma_dac1.wait);
-		init_waitqueue(&s->dma_dac2.wait);
-		init_waitqueue(&s->open_wait);
-		init_waitqueue(&s->midi.iwait);
-		init_waitqueue(&s->midi.owait);
-		s->open_sem = MUTEX;
-		spin_lock_init(&s->lock);
-		s->magic = ES1371_MAGIC;
-		s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
-		s->irq = pcidev->irq;
-		pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev);
-		if (check_region(s->io, ES1371_EXTENT)) {
-			printk(KERN_ERR "es1371: io ports %#lx-%#lx in use\n", s->io, s->io+ES1371_EXTENT-1);
-			goto err_region;
-		}
-		request_region(s->io, ES1371_EXTENT, "es1371");
-		if (request_irq(s->irq, es1371_interrupt, SA_SHIRQ, "es1371", s)) {
-			printk(KERN_ERR "es1371: irq %u in use\n", s->irq);
-			goto err_irq;
-		}
-		printk(KERN_INFO "es1371: found adapter at io %#lx irq %u\n"
-		       KERN_INFO "es1371: features: joystick 0x%x\n", s->io, s->irq, joystick[index]);
-		/* register devices */
-		if ((s->dev_audio = register_sound_dsp(&es1371_audio_fops, -1)) < 0)
-			goto err_dev1;
-		if ((s->dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1)) < 0)
-			goto err_dev2;
-		if ((s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1)) < 0)
-			goto err_dev3;
-		if ((s->dev_midi = register_sound_midi(&es1371_midi_fops, -1)) < 0)
-			goto err_dev4;
-#ifdef ES1371_DEBUG
-                /* intialize the debug proc device */
-                s->ps = create_proc_entry("es1371", S_IFREG | S_IRUGO, NULL);
-                if (s->ps)
-                        s->ps->read_proc = proc_es1371_dump;
-#endif /* ES1371_DEBUG */
-
-		/* initialize codec registers */
-		s->ctrl = 0;
-		if ((joystick[index] & ~0x18) == 0x200) {
-			if (check_region(joystick[index], JOY_EXTENT))
-				printk(KERN_ERR "es1371: joystick address 0x%x already in use\n", joystick[index]);
-			else {
-				s->ctrl |= CTRL_JYSTK_EN | (((joystick[index] >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT);
-			}
-		}
-		s->sctrl = 0;
-		cssr = 0;
-		/* check to see if s/pdif mode is being requested */
-		if (spdif[index]) {
-			if (s->rev >= 4) {
-				printk(KERN_INFO "es1371: enabling S/PDIF output\n");
-				cssr |= STAT_EN_SPDIF;
-				s->ctrl |= CTRL_SPDIFEN_B;
-			} else {
-				printk(KERN_ERR "es1371: revision %d does not support S/PDIF\n", s->rev);
-			}
-		}
-		/* initialize the chips */
-		outl(s->ctrl, s->io+ES1371_REG_CONTROL);
-		outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
-		outl(0, s->io+ES1371_REG_LEGACY);
-		pci_set_master(pcidev);  /* enable bus mastering */
-		/* AC97 warm reset to start the bitclk */
-		outl(s->ctrl | CTRL_SYNCRES, s->io+ES1371_REG_CONTROL);
-		udelay(2);
-		outl(s->ctrl, s->io+ES1371_REG_CONTROL);
-		/* init the sample rate converter */
-                src_init(s);
-		/* codec init */
-		wrcodec(s, AC97_RESET, 0); /* reset codec */
-		s->mix.codec_id = rdcodec(s, AC97_RESET);  /* get codec ID */
-		val = rdcodec(s, AC97_VENDOR_ID1);
-		val2 = rdcodec(s, AC97_VENDOR_ID2);
-		printk(KERN_INFO "es1371: codec vendor %c%c%c revision %d\n", 
-		       (val >> 8) & 0xff, val & 0xff, (val2 >> 8) & 0xff, val2 & 0xff);
-		printk(KERN_INFO "es1371: codec features");
-		if (s->mix.codec_id & CODEC_ID_DEDICATEDMIC)
-			printk(" dedicated MIC PCM in");
-		if (s->mix.codec_id & CODEC_ID_MODEMCODEC)
-			printk(" Modem Line Codec");
-		if (s->mix.codec_id & CODEC_ID_BASSTREBLE)
-			printk(" Bass & Treble");
-		if (s->mix.codec_id & CODEC_ID_SIMULATEDSTEREO)
-			printk(" Simulated Stereo");
-		if (s->mix.codec_id & CODEC_ID_HEADPHONEOUT)
-			printk(" Headphone out");
-		if (s->mix.codec_id & CODEC_ID_LOUDNESS)
-			printk(" Loudness");
-		if (s->mix.codec_id & CODEC_ID_18BITDAC)
-			printk(" 18bit DAC");
-		if (s->mix.codec_id & CODEC_ID_20BITDAC)
-			printk(" 20bit DAC");
-		if (s->mix.codec_id & CODEC_ID_18BITADC)
-			printk(" 18bit ADC");
-		if (s->mix.codec_id & CODEC_ID_20BITADC)
-			printk(" 20bit ADC");
-		printk("%s\n", (s->mix.codec_id & 0x3ff) ? "" : " none");
-		val = (s->mix.codec_id >> CODEC_ID_SESHIFT) & CODEC_ID_SEMASK;
-		printk(KERN_INFO "es1371: stereo enhancement: %s\n", (val <= 20) ? stereo_enhancement[val] : "unknown");
-
-		fs = get_fs();
-		set_fs(KERNEL_DS);
-		val = SOUND_MASK_LINE;
-		mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
-		for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) {
-			val = initvol[i].vol;
-			mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
-		}
-		set_fs(fs);
-		/* turn on S/PDIF output driver if requested */
-		outl(cssr, s->io+ES1371_REG_STATUS);
-		/* queue it for later freeing */
-		s->next = devs;
-		devs = s;
-		index++;
-		continue;
-
-	err_dev4:
-		unregister_sound_dsp(s->dev_dac);
-	err_dev3:
-		unregister_sound_mixer(s->dev_mixer);
-	err_dev2:
-		unregister_sound_dsp(s->dev_audio);
-	err_dev1:
-		printk(KERN_ERR "es1371: cannot register misc device\n");
-		free_irq(s->irq, s);
-	err_irq:
-		release_region(s->io, ES1371_EXTENT);
-	err_region:
-		kfree_s(s, sizeof(struct es1371_state));
+		if (!probe_chip(pcidev, index))
+			index++;
 	}
 	if (!devs)
 		return -ENODEV;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/sound/esssolo1.c linux.15p1/drivers/sound/esssolo1.c
--- linux.vanilla/drivers/sound/esssolo1.c	Tue Jan  4 21:18:59 2000
+++ linux.15p1/drivers/sound/esssolo1.c	Wed Jan 12 01:26:27 2000
@@ -3,7 +3,7 @@
 /*
  *      esssolo1.c  --  ESS Technology Solo1 (ES1946) audio driver.
  *
- *      Copyright (C) 1998-1999  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *      Copyright (C) 1998-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
  *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
@@ -28,38 +28,41 @@
  *  /dev/midi   simple MIDI UART interface, no ioctl
  *
  *  Revision history
- *    10.11.98   0.1   Initial release (without any hardware)
- *    22.03.99   0.2   cinfo.blocks should be reset after GETxPTR ioctl.
- *                     reported by Johan Maes <joma@telindus.be>
- *                     return EAGAIN instead of EBUSY when O_NONBLOCK
- *                     read/write cannot be executed
- *    07.04.99   0.3   implemented the following ioctl's: SOUND_PCM_READ_RATE, 
- *                     SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; 
- *                     Alpha fixes reported by Peter Jones <pjones@redhat.com>
- *    15.06.99   0.4   Fix bad allocation bug.
- *                     Thanks to Deti Fliegl <fliegl@in.tum.de>
- *    28.06.99   0.5   Add pci_set_master
- *    12.08.99   0.6   Fix MIDI UART crashing the driver
- *                     Changed mixer semantics from OSS documented
- *                     behaviour to OSS "code behaviour".
- *                     Recording might actually work now.
- *                     The real DDMA controller address register is at PCI config
- *                     0x60, while the register at 0x18 is used as a placeholder
- *                     register for BIOS address allocation. This register
- *                     is supposed to be copied into 0x60, according
- *                     to the Solo1 datasheet. When I do that, I can access
- *                     the DDMA registers except the mask bit, which
- *                     is stuck at 1. When I copy the contents of 0x18 +0x10
- *                     to the DDMA base register, everything seems to work.
- *                     The fun part is that the Windows Solo1 driver doesn't
- *                     seem to do these tricks.
- *                     Bugs remaining: plops and clicks when starting/stopping playback
- *    31.08.99   0.7   add spin_lock_init
- *                     replaced current->state = x with set_current_state(x)
- *    03.09.99   0.8   change read semantics for MIDI to match
- *                     OSS more closely; remove possible wakeup race
- *    07.10.99   0.9   Fix initialization; complain if sequencer writes time out
- *                     Revised resource grabbing for the FM synthesizer
+ *    10.11.1998   0.1   Initial release (without any hardware)
+ *    22.03.1999   0.2   cinfo.blocks should be reset after GETxPTR ioctl.
+ *                       reported by Johan Maes <joma@telindus.be>
+ *                       return EAGAIN instead of EBUSY when O_NONBLOCK
+ *                       read/write cannot be executed
+ *    07.04.1999   0.3   implemented the following ioctl's: SOUND_PCM_READ_RATE, 
+ *                       SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; 
+ *                       Alpha fixes reported by Peter Jones <pjones@redhat.com>
+ *    15.06.1999   0.4   Fix bad allocation bug.
+ *                       Thanks to Deti Fliegl <fliegl@in.tum.de>
+ *    28.06.1999   0.5   Add pci_set_master
+ *    12.08.1999   0.6   Fix MIDI UART crashing the driver
+ *                       Changed mixer semantics from OSS documented
+ *                       behaviour to OSS "code behaviour".
+ *                       Recording might actually work now.
+ *                       The real DDMA controller address register is at PCI config
+ *                       0x60, while the register at 0x18 is used as a placeholder
+ *                       register for BIOS address allocation. This register
+ *                       is supposed to be copied into 0x60, according
+ *                       to the Solo1 datasheet. When I do that, I can access
+ *                       the DDMA registers except the mask bit, which
+ *                       is stuck at 1. When I copy the contents of 0x18 +0x10
+ *                       to the DDMA base register, everything seems to work.
+ *                       The fun part is that the Windows Solo1 driver doesn't
+ *                       seem to do these tricks.
+ *                       Bugs remaining: plops and clicks when starting/stopping playback
+ *    31.08.1999   0.7   add spin_lock_init
+ *                       replaced current->state = x with set_current_state(x)
+ *    03.09.1999   0.8   change read semantics for MIDI to match
+ *                       OSS more closely; remove possible wakeup race
+ *    07.10.1999   0.9   Fix initialization; complain if sequencer writes time out
+ *                       Revised resource grabbing for the FM synthesizer
+ *    28.10.1999   0.10  More waitqueue races fixed
+ *    09.12.1999   0.11  Work around stupid Alpha port issue (virt_to_bus(kmalloc(GFP_DMA)) > 16M)
+ *                       Disabling recording on Alpha
  *
  */
 
@@ -442,6 +445,14 @@
 				break;
 		if (!db->rawbuf)
 			return -ENOMEM;
+		/* work around a problem of the alpha port */
+		if ((gfp_mask & GFP_DMA) && (virt_to_bus(db->rawbuf) & (~0xffffffUL))) {
+			printk(KERN_ERR "solo1: requested DMA buffer below 16M but got 0x%lx, Alpha bug?\n", 
+			       (unsigned long)virt_to_bus(db->rawbuf));
+			kfree(db->rawbuf);
+			db->rawbuf = NULL;
+			return -ENOMEM;
+		}
 		db->buforder = order;
 		/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
 		mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
@@ -963,9 +974,9 @@
 	
 	if (s->dma_dac.mapped)
 		return 0;
-        __set_current_state(TASK_INTERRUPTIBLE);
         add_wait_queue(&s->dma_dac.wait, &wait);
         for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
                 spin_lock_irqsave(&s->lock, flags);
 		count = s->dma_dac.count;
                 spin_unlock_irqrestore(&s->lock, flags);
@@ -998,6 +1009,7 @@
 static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned swptr;
@@ -1013,12 +1025,15 @@
 	if (!access_ok(VERIFY_WRITE, buffer, count))
 		return -EFAULT;
 	ret = 0;
+	add_wait_queue(&s->dma_adc.wait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		swptr = s->dma_adc.swptr;
 		cnt = s->dma_adc.dmasize-swptr;
 		if (s->dma_adc.count < cnt)
 			cnt = s->dma_adc.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
@@ -1039,9 +1054,12 @@
 #endif
 			if (inb(s->ddmabase+15) & 1)
 				printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n");
-			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EAGAIN;
-			interruptible_sleep_on(&s->dma_adc.wait);
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
 #ifdef DEBUGREC
 			printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x  A2: 0x%02x  A4: 0x%02x  A5: 0x%02x  A8: 0x%02x\n"
 			       KERN_DEBUG "solo1_read: regs: B1: 0x%02x  B2: 0x%02x  B7: 0x%02x  B8: 0x%02x  B9: 0x%02x\n"
@@ -1051,12 +1069,18 @@
 			       read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), 
 			       inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt);
 #endif
-			if (signal_pending(current))
-				return ret ? ret : -ERESTARTSYS;
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		swptr = (swptr + cnt) % s->dma_adc.dmasize;
 		spin_lock_irqsave(&s->lock, flags);
 		s->dma_adc.swptr = swptr;
@@ -1071,12 +1095,15 @@
 		       read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc));
 #endif
 	}
+	remove_wait_queue(&s->dma_adc.wait, &wait);
+	set_current_state(TASK_RUNNING);
 	return ret;
 }
 
 static ssize_t solo1_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct solo1_state *s = (struct solo1_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned swptr;
@@ -1100,6 +1127,7 @@
 	       read_mixer(s, 0x78), read_mixer(s, 0x7a), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc));
 #endif
 	ret = 0;
+	add_wait_queue(&s->dma_dac.wait, &wait);	
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		if (s->dma_dac.count < 0) {
@@ -1110,20 +1138,31 @@
 		cnt = s->dma_dac.dmasize-swptr;
 		if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
 			cnt = s->dma_dac.dmasize - s->dma_dac.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
 		if (cnt <= 0) {
 			start_dac(s);
-			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EAGAIN;
-			interruptible_sleep_on(&s->dma_dac.wait);
-			if (signal_pending(current))
-				return ret ? ret : -ERESTARTSYS;
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			schedule();
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		swptr = (swptr + cnt) % s->dma_dac.dmasize;
 		spin_lock_irqsave(&s->lock, flags);
 		s->dma_dac.swptr = swptr;
@@ -1135,6 +1174,8 @@
 		ret += cnt;
 		start_dac(s);
 	}
+	remove_wait_queue(&s->dma_dac.wait, &wait);
+	set_current_state(TASK_RUNNING);
 	return ret;
 }
 
@@ -1410,6 +1451,12 @@
 		if (s->dma_dac.mapped)
 			s->dma_dac.count &= s->dma_dac.fragsize-1;
 		spin_unlock_irqrestore(&s->lock, flags);
+#if 0
+		printk(KERN_DEBUG "esssolo1: GETOPTR: bytes %u blocks %u ptr %u, buforder %u numfrag %u fragshift %u\n"
+		       KERN_DEBUG "esssolo1: swptr %u count %u fragsize %u dmasize %u fragsamples %u\n",
+		       cinfo.bytes, cinfo.blocks, cinfo.ptr, s->dma_dac.buforder, s->dma_dac.numfrag, s->dma_dac.fragshift,
+		       s->dma_dac.swptr, s->dma_dac.count, s->dma_dac.fragsize, s->dma_dac.dmasize, s->dma_dac.fragsamples);
+#endif
                 return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
 
         case SNDCTL_DSP_GETBLKSIZE:
@@ -1497,8 +1544,8 @@
 		dealloc_dmabuf(&s->dma_adc);
 	}
 	s->open_mode &= ~(FMODE_READ | FMODE_WRITE);
-	up(&s->open_sem);
 	wake_up(&s->open_wait);
+	up(&s->open_sem);
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -1506,6 +1553,7 @@
 static int solo1_open(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
 	struct solo1_state *s = devs;
 
 	while (s && ((s->dev_audio ^ minor) & ~0xf))
@@ -1521,8 +1569,12 @@
 			up(&s->open_sem);
 			return -EBUSY;
 		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
 		up(&s->open_sem);
-		interruptible_sleep_on(&s->open_wait);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
 		if (signal_pending(current))
 			return -ERESTARTSYS;
 		down(&s->open_sem);
@@ -1537,10 +1589,6 @@
 	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
 	up(&s->open_sem);
 	MOD_INC_USE_COUNT;
-	if (prog_dmabuf_dac(s) || prog_dmabuf_adc(s)) {
-		solo1_release(inode, file);
-		return -ENOMEM;
-	}
 	prog_codec(s);
 	return 0;
 }
@@ -1654,6 +1702,8 @@
 		cnt = MIDIINBUF - ptr;
 		if (s->midi.icnt < cnt)
 			cnt = s->midi.icnt;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
@@ -1663,7 +1713,6 @@
 					ret = -EAGAIN;
 				break;
 			}
-			__set_current_state(TASK_INTERRUPTIBLE);
 			schedule();
 			if (signal_pending(current)) {
 				if (!ret)
@@ -1716,8 +1765,10 @@
 		cnt = MIDIOUTBUF - ptr;
 		if (s->midi.ocnt + cnt > MIDIOUTBUF)
 			cnt = MIDIOUTBUF - s->midi.ocnt;
-		if (cnt <= 0)
+		if (cnt <= 0) {
+			__set_current_state(TASK_INTERRUPTIBLE);
 			solo1_handle_midi(s);
+		}
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
@@ -1727,7 +1778,6 @@
 					ret = -EAGAIN;
 				break;
 			}
-			__set_current_state(TASK_INTERRUPTIBLE);
 			schedule();
 			if (signal_pending(current)) {
 				if (!ret)
@@ -1785,6 +1835,7 @@
 static int solo1_midi_open(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
 	struct solo1_state *s = devs;
 	unsigned long flags;
 
@@ -1801,8 +1852,12 @@
 			up(&s->open_sem);
 			return -EBUSY;
 		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
 		up(&s->open_sem);
-		interruptible_sleep_on(&s->open_wait);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
 		if (signal_pending(current))
 			return -ERESTARTSYS;
 		down(&s->open_sem);
@@ -1846,9 +1901,9 @@
 	VALIDATE_STATE(s);
 
 	if (file->f_mode & FMODE_WRITE) {
-		current->state = TASK_INTERRUPTIBLE;
 		add_wait_queue(&s->midi.owait, &wait);
 		for (;;) {
+			__set_current_state(TASK_INTERRUPTIBLE);
 			spin_lock_irqsave(&s->lock, flags);
 			count = s->midi.ocnt;
 			spin_unlock_irqrestore(&s->lock, flags);
@@ -1858,7 +1913,7 @@
 				break;
 			if (file->f_flags & O_NONBLOCK) {
 				remove_wait_queue(&s->midi.owait, &wait);
-				current->state = TASK_RUNNING;
+				set_current_state(TASK_RUNNING);
 				return -EBUSY;
 			}
 			tmo = (count * HZ) / 3100;
@@ -1866,7 +1921,7 @@
 				printk(KERN_DEBUG "solo1: midi timed out??\n");
 		}
 		remove_wait_queue(&s->midi.owait, &wait);
-		current->state = TASK_RUNNING;
+		set_current_state(TASK_RUNNING);
 	}
 	down(&s->open_sem);
 	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
@@ -1876,8 +1931,8 @@
 		del_timer(&s->midi.timer);		
 	}
 	spin_unlock_irqrestore(&s->lock, flags);
-	up(&s->open_sem);
 	wake_up(&s->open_wait);
+	up(&s->open_sem);
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -2002,6 +2057,7 @@
 static int solo1_dmfm_open(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
 	struct solo1_state *s = devs;
 
 	while (s && s->dev_dmfm != minor)
@@ -2017,8 +2073,12 @@
 			up(&s->open_sem);
 			return -EBUSY;
 		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
 		up(&s->open_sem);
-		interruptible_sleep_on(&s->open_wait);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
 		if (signal_pending(current))
 			return -ERESTARTSYS;
 		down(&s->open_sem);
@@ -2057,8 +2117,8 @@
 		outb(0, s->sbbase+3);
 	}
 	release_region(s->sbbase, FMSYNTH_EXTENT);
-	up(&s->open_sem);
 	wake_up(&s->open_wait);
+	up(&s->open_sem);
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -2113,7 +2173,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "solo1: version v0.9 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "solo1: version v0.11 time " __TIME__ " " __DATE__ "\n");
 	while (index < NR_DEVICE && 
 	       (pcidev = pci_find_device(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, pcidev))) {
 		if (pcidev->base_address[0] == 0 ||
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/sound/sonicvibes.c linux.15p1/drivers/sound/sonicvibes.c
--- linux.vanilla/drivers/sound/sonicvibes.c	Tue Jan  4 21:18:59 2000
+++ linux.15p1/drivers/sound/sonicvibes.c	Wed Jan 12 01:26:28 2000
@@ -3,7 +3,7 @@
 /*
  *      sonicvibes.c  --  S3 Sonic Vibes audio driver.
  *
- *      Copyright (C) 1998-1999  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *      Copyright (C) 1998-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
  *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
@@ -35,50 +35,53 @@
  *  out first how to drive them...
  *
  *  Revision history
- *    06.05.98   0.1   Initial release
- *    10.05.98   0.2   Fixed many bugs, esp. ADC rate calculation
- *                     First stab at a simple midi interface (no bells&whistles)
- *    13.05.98   0.3   Fix stupid cut&paste error: set_adc_rate was called instead of
- *                     set_dac_rate in the FMODE_WRITE case in sv_open
- *                     Fix hwptr out of bounds (now mpg123 works)
- *    14.05.98   0.4   Don't allow excessive interrupt rates
- *    08.06.98   0.5   First release using Alan Cox' soundcore instead of miscdevice
- *    03.08.98   0.6   Do not include modversions.h
- *                     Now mixer behaviour can basically be selected between
- *                     "OSS documented" and "OSS actual" behaviour
- *    31.08.98   0.7   Fix realplayer problems - dac.count issues
- *    10.12.98   0.8   Fix drain_dac trying to wait on not yet initialized DMA
- *    16.12.98   0.9   Fix a few f_file & FMODE_ bugs
- *    06.01.99   0.10  remove the silly SA_INTERRUPT flag.
- *                     hopefully killed the egcs section type conflict
- *    12.03.99   0.11  cinfo.blocks should be reset after GETxPTR ioctl.
- *                     reported by Johan Maes <joma@telindus.be>
- *    22.03.99   0.12  return EAGAIN instead of EBUSY when O_NONBLOCK
- *                     read/write cannot be executed
- *    05.04.99   0.13  added code to sv_read and sv_write which should detect
- *                     lockups of the sound chip and revive it. This is basically
- *                     an ugly hack, but at least applications using this driver
- *                     won't hang forever. I don't know why these lockups happen,
- *                     it might well be the motherboard chipset (an early 486 PCI
- *                     board with ALI chipset), since every busmastering 100MB
- *                     ethernet card I've tried (Realtek 8139 and Macronix tulip clone)
- *                     exhibit similar behaviour (they work for a couple of packets
- *                     and then lock up and can be revived by ifconfig down/up).
- *    07.04.99   0.14  implemented the following ioctl's: SOUND_PCM_READ_RATE, 
- *                     SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; 
- *                     Alpha fixes reported by Peter Jones <pjones@redhat.com>
- *                     Note: dmaio hack might still be wrong on archs other than i386
- *    15.06.99   0.15  Fix bad allocation bug.
- *                     Thanks to Deti Fliegl <fliegl@in.tum.de>
- *    28.06.99   0.16  Add pci_set_master
- *    03.08.99   0.17  adapt to Linus' new __setup/__initcall
- *                     added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr"
- *    12.08.99   0.18  module_init/__setup fixes
- *    24.08.99   0.19  get rid of the dmaio kludge, replace with allocate_resource
- *    31.08.99   0.20  add spin_lock_init
- *                     __initlocaldata to fix gcc 2.7.x problems
- *    03.09.99   0.21  change read semantics for MIDI to match
- *                     OSS more closely; remove possible wakeup race
+ *    06.05.1998   0.1   Initial release
+ *    10.05.1998   0.2   Fixed many bugs, esp. ADC rate calculation
+ *                       First stab at a simple midi interface (no bells&whistles)
+ *    13.05.1998   0.3   Fix stupid cut&paste error: set_adc_rate was called instead of
+ *                       set_dac_rate in the FMODE_WRITE case in sv_open
+ *                       Fix hwptr out of bounds (now mpg123 works)
+ *    14.05.1998   0.4   Don't allow excessive interrupt rates
+ *    08.06.1998   0.5   First release using Alan Cox' soundcore instead of miscdevice
+ *    03.08.1998   0.6   Do not include modversions.h
+ *                       Now mixer behaviour can basically be selected between
+ *                       "OSS documented" and "OSS actual" behaviour
+ *    31.08.1998   0.7   Fix realplayer problems - dac.count issues
+ *    10.12.1998   0.8   Fix drain_dac trying to wait on not yet initialized DMA
+ *    16.12.1998   0.9   Fix a few f_file & FMODE_ bugs
+ *    06.01.1999   0.10  remove the silly SA_INTERRUPT flag.
+ *                       hopefully killed the egcs section type conflict
+ *    12.03.1999   0.11  cinfo.blocks should be reset after GETxPTR ioctl.
+ *                       reported by Johan Maes <joma@telindus.be>
+ *    22.03.1999   0.12  return EAGAIN instead of EBUSY when O_NONBLOCK
+ *                       read/write cannot be executed
+ *    05.04.1999   0.13  added code to sv_read and sv_write which should detect
+ *                       lockups of the sound chip and revive it. This is basically
+ *                       an ugly hack, but at least applications using this driver
+ *                       won't hang forever. I don't know why these lockups happen,
+ *                       it might well be the motherboard chipset (an early 486 PCI
+ *                       board with ALI chipset), since every busmastering 100MB
+ *                       ethernet card I've tried (Realtek 8139 and Macronix tulip clone)
+ *                       exhibit similar behaviour (they work for a couple of packets
+ *                       and then lock up and can be revived by ifconfig down/up).
+ *    07.04.1999   0.14  implemented the following ioctl's: SOUND_PCM_READ_RATE, 
+ *                       SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; 
+ *                       Alpha fixes reported by Peter Jones <pjones@redhat.com>
+ *                       Note: dmaio hack might still be wrong on archs other than i386
+ *    15.06.1999   0.15  Fix bad allocation bug.
+ *                       Thanks to Deti Fliegl <fliegl@in.tum.de>
+ *    28.06.1999   0.16  Add pci_set_master
+ *    03.08.1999   0.17  adapt to Linus' new __setup/__initcall
+ *                       added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr"
+ *    12.08.1999   0.18  module_init/__setup fixes
+ *    24.08.1999   0.19  get rid of the dmaio kludge, replace with allocate_resource
+ *    31.08.1999   0.20  add spin_lock_init
+ *                       __initlocaldata to fix gcc 2.7.x problems
+ *                       use new resource allocation to allocate DDMA IO space
+ *                       replaced current->state = x with set_current_state(x)
+ *    03.09.1999   0.21  change read semantics for MIDI to match
+ *                       OSS more closely; remove possible wakeup race
+ *    28.10.1999   0.22  More waitqueue races fixed
  *
  */
 
@@ -110,6 +113,26 @@
 
 /* --------------------------------------------------------------------- */
 
+#ifdef MODULE
+#define __exit
+#define module_exit(x) void cleanup_module(void) { x(); }
+#define module_init(x) int init_module(void) { return x(); }
+#else
+#define __exit __attribute__ ((unused, __section__ (".text.init")))
+#define module_exit(x) /* nothing */
+#define module_init(x) /* nothing */
+#endif
+
+#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL
+#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL}
+#define wait_queue_head_t struct wait_queue *
+#define init_waitqueue_head(w) *(w) = 0
+#define init_MUTEX(m) *(m) = MUTEX
+#define __set_current_state(x) do { current->state = (x); } while (0)
+#define set_current_state(x) __set_current_state(x)
+
+/* --------------------------------------------------------------------- */
+
 #ifndef PCI_VENDOR_ID_S3
 #define PCI_VENDOR_ID_S3             0x5333
 #endif
@@ -297,7 +320,7 @@
 	spinlock_t lock;
 	struct semaphore open_sem;
 	mode_t open_mode;
-	struct wait_queue *open_wait;
+	wait_queue_head_t open_wait;
 
 	struct dmabuf {
 		void *rawbuf;
@@ -308,7 +331,7 @@
 		unsigned total_bytes;
 		int count;
 		unsigned error; /* over/underrun */
-		struct wait_queue *wait;
+		wait_queue_head_t wait;
 		/* redundant, but makes calculations easier */
 		unsigned fragsize;
 		unsigned dmasize;
@@ -326,8 +349,8 @@
 	struct {
 		unsigned ird, iwr, icnt;
 		unsigned ord, owr, ocnt;
-		struct wait_queue *iwait;
-		struct wait_queue *owait;
+		wait_queue_head_t iwait;
+		wait_queue_head_t owait;
 		struct timer_list timer;
 		unsigned char ibuf[MIDIINBUF];
 		unsigned char obuf[MIDIOUTBUF];
@@ -512,8 +535,9 @@
 static unsigned setpll(struct sv_state *s, unsigned char reg, unsigned rate)
 {
 	unsigned long flags;
-	unsigned char r, m=0, n=0;
+	unsigned char r, m, n;
 	unsigned xm, xn, xr, xd, metric = ~0U;
+	/* the warnings about m and n used uninitialized are bogus and may safely be ignored */
 
 	if (rate < 625000/ADCMULT)
 		rate = 625000/ADCMULT;
@@ -1252,15 +1276,15 @@
 
 static int drain_dac(struct sv_state *s, int nonblock)
 {
-        struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	unsigned long flags;
 	int count, tmo;
 
 	if (s->dma_dac.mapped || !s->dma_dac.ready)
 		return 0;
-        current->state = TASK_INTERRUPTIBLE;
         add_wait_queue(&s->dma_dac.wait, &wait);
         for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
                 spin_lock_irqsave(&s->lock, flags);
 		count = s->dma_dac.count;
                 spin_unlock_irqrestore(&s->lock, flags);
@@ -1270,7 +1294,7 @@
                         break;
                 if (nonblock) {
                         remove_wait_queue(&s->dma_dac.wait, &wait);
-                        current->state = TASK_RUNNING;
+                        set_current_state(TASK_RUNNING);
                         return -EBUSY;
                 }
 		tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac;
@@ -1279,7 +1303,7 @@
 			printk(KERN_DEBUG "sv: dma timed out??\n");
         }
         remove_wait_queue(&s->dma_dac.wait, &wait);
-        current->state = TASK_RUNNING;
+        set_current_state(TASK_RUNNING);
         if (signal_pending(current))
                 return -ERESTARTSYS;
         return 0;
@@ -1290,6 +1314,7 @@
 static ssize_t sv_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct sv_state *s = (struct sv_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned swptr;
@@ -1306,24 +1331,30 @@
 		return -EFAULT;
 	ret = 0;
 #if 0
-   spin_lock_irqsave(&s->lock, flags);
-   sv_update_ptr(s);
-   spin_unlock_irqrestore(&s->lock, flags);
+	spin_lock_irqsave(&s->lock, flags);
+	sv_update_ptr(s);
+	spin_unlock_irqrestore(&s->lock, flags);
 #endif
+        add_wait_queue(&s->dma_adc.wait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		swptr = s->dma_adc.swptr;
 		cnt = s->dma_adc.dmasize-swptr;
 		if (s->dma_adc.count < cnt)
 			cnt = s->dma_adc.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
 		if (cnt <= 0) {
 			start_adc(s);
-			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EAGAIN;
-			if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) {
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			if (!schedule_timeout(HZ)) {
 				printk(KERN_DEBUG "sv: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
 				       s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, 
 				       s->dma_adc.hwptr, s->dma_adc.swptr);
@@ -1336,12 +1367,18 @@
 				s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0;
 				spin_unlock_irqrestore(&s->lock, flags);
 			}
-			if (signal_pending(current))
-				return ret ? ret : -ERESTARTSYS;
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		swptr = (swptr + cnt) % s->dma_adc.dmasize;
 		spin_lock_irqsave(&s->lock, flags);
 		s->dma_adc.swptr = swptr;
@@ -1352,12 +1389,15 @@
 		ret += cnt;
 		start_adc(s);
 	}
+        remove_wait_queue(&s->dma_adc.wait, &wait);
+	set_current_state(TASK_RUNNING);
 	return ret;
 }
 
 static ssize_t sv_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct sv_state *s = (struct sv_state *)file->private_data;
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned swptr;
@@ -1374,10 +1414,11 @@
 		return -EFAULT;
 	ret = 0;
 #if 0
-   spin_lock_irqsave(&s->lock, flags);
-   sv_update_ptr(s);
-   spin_unlock_irqrestore(&s->lock, flags);
+	spin_lock_irqsave(&s->lock, flags);
+	sv_update_ptr(s);
+	spin_unlock_irqrestore(&s->lock, flags);
 #endif
+        add_wait_queue(&s->dma_dac.wait, &wait);
 	while (count > 0) {
 		spin_lock_irqsave(&s->lock, flags);
 		if (s->dma_dac.count < 0) {
@@ -1388,14 +1429,19 @@
 		cnt = s->dma_dac.dmasize-swptr;
 		if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
 			cnt = s->dma_dac.dmasize - s->dma_dac.count;
+		if (cnt <= 0)
+			__set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
 		if (cnt <= 0) {
 			start_dac(s);
-			if (file->f_flags & O_NONBLOCK)
-				return ret ? ret : -EAGAIN;
-			if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) {
+			if (file->f_flags & O_NONBLOCK) {
+				if (!ret)
+					ret = -EAGAIN;
+				break;
+			}
+			if (!schedule_timeout(HZ)) {
 				printk(KERN_DEBUG "sv: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
 				       s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, 
 				       s->dma_dac.hwptr, s->dma_dac.swptr);
@@ -1408,12 +1454,18 @@
 				s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0;
 				spin_unlock_irqrestore(&s->lock, flags);
 			}
-			if (signal_pending(current))
-				return ret ? ret : -ERESTARTSYS;
+			if (signal_pending(current)) {
+				if (!ret)
+					ret = -ERESTARTSYS;
+				break;
+			}
 			continue;
 		}
-		if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt))
-			return ret ? ret : -EFAULT;
+		if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
 		swptr = (swptr + cnt) % s->dma_dac.dmasize;
 		spin_lock_irqsave(&s->lock, flags);
 		s->dma_dac.swptr = swptr;
@@ -1425,6 +1477,8 @@
 		ret += cnt;
 		start_dac(s);
 	}
+        remove_wait_queue(&s->dma_dac.wait, &wait);
+	set_current_state(TASK_RUNNING);
 	return ret;
 }
 
@@ -1789,6 +1843,7 @@
 static int sv_open(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
 	struct sv_state *s = devs;
 	unsigned char fmtm = ~0, fmts = 0;
 
@@ -1805,8 +1860,12 @@
 			up(&s->open_sem);
 			return -EBUSY;
 		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
 		up(&s->open_sem);
-		interruptible_sleep_on(&s->open_wait);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
 		if (signal_pending(current))
 			return -ERESTARTSYS;
 		down(&s->open_sem);
@@ -1849,8 +1908,8 @@
 		dealloc_dmabuf(&s->dma_adc);
 	}
 	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
-	up(&s->open_sem);
 	wake_up(&s->open_wait);
+	up(&s->open_sem);
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -1878,7 +1937,7 @@
 static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct sv_state *s = (struct sv_state *)file->private_data;
-	struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -1899,6 +1958,8 @@
 		cnt = MIDIINBUF - ptr;
 		if (s->midi.icnt < cnt)
 			cnt = s->midi.icnt;
+		if (cnt <= 0)
+                      __set_current_state(TASK_INTERRUPTIBLE);
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
@@ -1908,7 +1969,6 @@
                                       ret = -EAGAIN;
                               break;
                       }
-                      current->state = TASK_INTERRUPTIBLE;
                       schedule();
                       if (signal_pending(current)) {
                               if (!ret)
@@ -1932,7 +1992,7 @@
 		ret += cnt;
 		break;
 	}
-	current->state = TASK_RUNNING;
+	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(&s->midi.iwait, &wait);
 	return ret;
 }
@@ -1940,7 +2000,7 @@
 static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 {
 	struct sv_state *s = (struct sv_state *)file->private_data;
-	struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	ssize_t ret;
 	unsigned long flags;
 	unsigned ptr;
@@ -1961,8 +2021,10 @@
 		cnt = MIDIOUTBUF - ptr;
 		if (s->midi.ocnt + cnt > MIDIOUTBUF)
 			cnt = MIDIOUTBUF - s->midi.ocnt;
-		if (cnt <= 0)
+		if (cnt <= 0) {
+			__set_current_state(TASK_INTERRUPTIBLE);
 			sv_handle_midi(s);
+		}
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (cnt > count)
 			cnt = count;
@@ -1972,7 +2034,6 @@
 					ret = -EAGAIN;
 				break;
 			}
-			current->state = TASK_INTERRUPTIBLE;
 			schedule();
 			if (signal_pending(current)) {
 				if (!ret)
@@ -1998,7 +2059,7 @@
 		sv_handle_midi(s);
 		spin_unlock_irqrestore(&s->lock, flags);
 	}
-	current->state = TASK_RUNNING;
+	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(&s->midi.owait, &wait);
 	return ret;
 }
@@ -2030,6 +2091,7 @@
 static int sv_midi_open(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
 	struct sv_state *s = devs;
 	unsigned long flags;
 
@@ -2046,8 +2108,12 @@
 			up(&s->open_sem);
 			return -EBUSY;
 		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
 		up(&s->open_sem);
-		interruptible_sleep_on(&s->open_wait);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
 		if (signal_pending(current))
 			return -ERESTARTSYS;
 		down(&s->open_sem);
@@ -2087,16 +2153,16 @@
 static int sv_midi_release(struct inode *inode, struct file *file)
 {
 	struct sv_state *s = (struct sv_state *)file->private_data;
-        struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	unsigned long flags;
 	unsigned count, tmo;
 
 	VALIDATE_STATE(s);
 
 	if (file->f_mode & FMODE_WRITE) {
-		current->state = TASK_INTERRUPTIBLE;
 		add_wait_queue(&s->midi.owait, &wait);
 		for (;;) {
+			__set_current_state(TASK_INTERRUPTIBLE);
 			spin_lock_irqsave(&s->lock, flags);
 			count = s->midi.ocnt;
 			spin_unlock_irqrestore(&s->lock, flags);
@@ -2106,7 +2172,7 @@
 				break;
 			if (file->f_flags & O_NONBLOCK) {
 				remove_wait_queue(&s->midi.owait, &wait);
-				current->state = TASK_RUNNING;
+				set_current_state(TASK_RUNNING);
 				return -EBUSY;
 			}
 			tmo = (count * HZ) / 3100;
@@ -2114,7 +2180,7 @@
 				printk(KERN_DEBUG "sv: midi timed out??\n");
 		}
 		remove_wait_queue(&s->midi.owait, &wait);
-		current->state = TASK_RUNNING;
+		set_current_state(TASK_RUNNING);
 	}
 	down(&s->open_sem);
 	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
@@ -2124,8 +2190,8 @@
 		del_timer(&s->midi.timer);		
 	}
 	spin_unlock_irqrestore(&s->lock, flags);
-	up(&s->open_sem);
 	wake_up(&s->open_wait);
+	up(&s->open_sem);
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -2250,6 +2316,7 @@
 static int sv_dmfm_open(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
+	DECLARE_WAITQUEUE(wait, current);
 	struct sv_state *s = devs;
 
 	while (s && s->dev_dmfm != minor)
@@ -2265,8 +2332,12 @@
 			up(&s->open_sem);
 			return -EBUSY;
 		}
+		add_wait_queue(&s->open_wait, &wait);
+		__set_current_state(TASK_INTERRUPTIBLE);
 		up(&s->open_sem);
-		interruptible_sleep_on(&s->open_wait);
+		schedule();
+		remove_wait_queue(&s->open_wait, &wait);
+		set_current_state(TASK_RUNNING);
 		if (signal_pending(current))
 			return -ERESTARTSYS;
 		down(&s->open_sem);
@@ -2298,8 +2369,8 @@
 		outb(regb, s->iosynth+2);
 		outb(0, s->iosynth+3);
 	}
-	up(&s->open_sem);
 	wake_up(&s->open_wait);
+	up(&s->open_sem);
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -2365,7 +2436,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "sv: version v0.21 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "sv: version v0.22 time " __TIME__ " " __DATE__ "\n");
 #if 0
 	if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
 		printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
@@ -2388,12 +2459,12 @@
 			continue;
 		}
 		memset(s, 0, sizeof(struct sv_state));
-		init_waitqueue(&s->dma_adc.wait);
-		init_waitqueue(&s->dma_dac.wait);
-		init_waitqueue(&s->open_wait);
-		init_waitqueue(&s->midi.iwait);
-		init_waitqueue(&s->midi.owait);
-		s->open_sem = MUTEX;
+		init_waitqueue_head(&s->dma_adc.wait);
+		init_waitqueue_head(&s->dma_dac.wait);
+		init_waitqueue_head(&s->open_wait);
+		init_waitqueue_head(&s->midi.iwait);
+		init_waitqueue_head(&s->midi.owait);
+		init_MUTEX(&s->open_sem);
 		spin_lock_init(&s->lock);
 		s->magic = SV_MAGIC;
 		s->iosb = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/telephony/ixj.c linux.15p1/drivers/telephony/ixj.c
--- linux.vanilla/drivers/telephony/ixj.c	Tue Jan  4 21:18:59 2000
+++ linux.15p1/drivers/telephony/ixj.c	Wed Jan 12 01:17:32 2000
@@ -1974,7 +1974,7 @@
 
 		ixj_WriteDSPCommand(0x0700, board);
 	}
-	if (ixj[board].play_mode != -1 && ixj[board].rec_mode != -1);
+	if (ixj[board].play_mode != -1 && ixj[board].rec_mode != -1)
 	{
 		ixj_WriteDSPCommand(0xB002, board);	// AEC Stop
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/block_dev.c linux.15p1/fs/block_dev.c
--- linux.vanilla/fs/block_dev.c	Tue Jan  4 21:19:00 2000
+++ linux.15p1/fs/block_dev.c	Wed Jan 12 01:06:21 2000
@@ -92,6 +92,7 @@
 		      blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9) / 2;
 		      if (block + blocks > size) blocks = size - block;
 		      if (blocks > NBUF) blocks=NBUF;
+		      if (!blocks) blocks = 1;
 		      for(i=1; i<blocks; i++)
 		      {
 		        bhlist[i] = getblk (dev, block+i, blocksize);
@@ -105,8 +106,10 @@
 		    ll_rw_block(READ, blocks, bhlist);
 		    for(i=1; i<blocks; i++) brelse(bhlist[i]);
 		    wait_on_buffer(bh);
-		    if (!buffer_uptodate(bh))
+		    if (!buffer_uptodate(bh)) {
+			  brelse(bh);
 			  return written ? written : -EIO;
+		    }
 		  };
 		};
 #endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/fat/misc.c linux.15p1/fs/fat/misc.c
--- linux.vanilla/fs/fat/misc.c	Sat Aug 14 02:27:37 1999
+++ linux.15p1/fs/fat/misc.c	Wed Jan 12 01:22:48 2000
@@ -222,9 +222,10 @@
 	sector = MSDOS_SB(sb)->data_start+(nr-2)*cluster_size;
 	last_sector = sector + cluster_size;
 	if (MSDOS_SB(sb)->cvf_format &&
-	    MSDOS_SB(sb)->cvf_format->zero_out_cluster)
+	    MSDOS_SB(sb)->cvf_format->zero_out_cluster) {
 		MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode,nr);
-	else
+		res=fat_bread(sb,fat_smap(inode,inode->i_blocks));
+	}else
 	for ( ; sector < last_sector; sector++) {
 		#ifdef DEBUG
 			printk("zeroing sector %d\n",sector);
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/nfs/inode.c linux.15p1/fs/nfs/inode.c
--- linux.vanilla/fs/nfs/inode.c	Tue Jan  4 21:19:00 2000
+++ linux.15p1/fs/nfs/inode.c	Wed Jan 12 01:23:39 2000
@@ -212,7 +212,12 @@
 	if (!data)
 		goto out_miss_args;
 
-	if (data->version != NFS_MOUNT_VERSION) {
+	/* No NFS V3. */
+	if (data->flags & NFS_MOUNT_VER3)
+		goto out_fail;
+
+	/* Don't complain if "mount" is newer. */
+	if (data->version < NFS_MOUNT_VERSION) {
 		printk("nfs warning: mount version %s than kernel\n",
 			data->version < NFS_MOUNT_VERSION ? "older" : "newer");
 		if (data->version < 2)
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/nfsd/nfsctl.c linux.15p1/fs/nfsd/nfsctl.c
--- linux.vanilla/fs/nfsd/nfsctl.c	Tue Jan  4 21:19:00 2000
+++ linux.15p1/fs/nfsd/nfsctl.c	Wed Jan 12 01:23:39 2000
@@ -345,7 +345,7 @@
 int
 init_module(void)
 {
-	printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
+	printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de)\n");
 	do_nfsservctl = handle_sys_nfsservctl;
 	return 0;
 }
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/nfsd/nfsfh.c linux.15p1/fs/nfsd/nfsfh.c
--- linux.vanilla/fs/nfsd/nfsfh.c	Tue Jan  4 21:19:00 2000
+++ linux.15p1/fs/nfsd/nfsfh.c	Wed Jan 12 01:23:39 2000
@@ -455,13 +455,27 @@
 			break;
 		}
 
-		result = ERR_PTR(-ENOENT);
-		dir = iget_in_use(sb, dirino);
-		if (!dir)
-			goto out_root;
-		dentry = d_alloc_root(dir, NULL);
-		if (!dentry)
-			goto out_iput;
+		/*
+		 *  Fix for /// bad export bug: if dirino is the root,
+		 *  get the real root dentry rather than creating a temporary
+		 *  "root" dentry.  XXX We could extend this to use
+		 *  any existing dentry for the located 'dir', but all
+		 *  of this code is going to be completely rewritten soon,
+		 *  so I won't bother. 
+		 */
+
+		if (dirino == root_ino) {
+			dentry = dget(root);
+		}
+		else {
+			result = ERR_PTR(-ENOENT);
+			dir = iget_in_use(sb, dirino);
+			if (!dir)
+				goto out_root;
+			dentry = d_alloc_root(dir, NULL);
+			if (!dentry)
+				goto out_iput;
+		}
 
 		/*
 		 * Get the name for this inode and the next parent inode.
@@ -1330,7 +1344,7 @@
 	fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
 	fhp->fh_handle.fh_generation = inode->i_generation;
 	fhp->fh_handle.fh_dcookie = (struct dentry *)0xfeebbaca;
- out:
+out:
 	return;
 
 out_bad:
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/nfsd/nfsproc.c linux.15p1/fs/nfsd/nfsproc.c
--- linux.vanilla/fs/nfsd/nfsproc.c	Tue Jan  4 21:19:00 2000
+++ linux.15p1/fs/nfsd/nfsproc.c	Wed Jan 12 01:23:39 2000
@@ -237,6 +237,9 @@
 	if (nfserr)
 		goto done;
 	inode = newfhp->fh_dentry->d_inode;
+	if (inode && newfhp->fh_handle.fh_ino == 0)
+		 /* inode might have been instantiated while we slept */
+		 fh_update(newfhp);
 
 	/* Unfudge the mode bits */
 	if (attr->ia_valid & ATTR_MODE) { 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/asm-s390/setup.h linux.15p1/include/asm-s390/setup.h
--- linux.vanilla/include/asm-s390/setup.h	Tue Jan  4 21:19:00 2000
+++ linux.15p1/include/asm-s390/setup.h	Wed Jan 12 01:09:50 2000
@@ -40,6 +40,7 @@
  */
 #define MACHINE_IS_VM    (MACHINE_FLAGS & 1)
 #define MACHINE_HAS_IEEE (MACHINE_FLAGS & 2)
+#define MACHINE_IS_P390  (MACHINE_FLAGS & 4)
 
 #define RAMDISK_ORIGIN            0x800000
 #define RAMDISK_BLKSIZE           0x1000
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/init/main.c linux.15p1/init/main.c
--- linux.vanilla/init/main.c	Tue Jan  4 21:19:03 2000
+++ linux.15p1/init/main.c	Wed Jan 12 01:09:50 2000
@@ -151,6 +151,9 @@
 #ifdef CONFIG_CTC  
 extern void ctc_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_IUCV
+extern void iucv_setup(char *str, int *ints);
+#endif
 #ifdef CONFIG_ARCNET_COM90xxIO
 extern void com90io_setup(char *str, int *ints);
 #endif
@@ -182,6 +185,9 @@
 #ifdef CONFIG_PARIDE_PCD
 extern void pcd_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_3215
+extern void con3215_setup(char *str, int *ints);
+#endif
 #ifdef CONFIG_MDISK
 extern void mdisk_setup(char *str, int *ints);
 #endif
@@ -638,6 +644,10 @@
 #ifdef CONFIG_CTC
         { "ctc=", ctc_setup } ,
 #endif
+#ifdef CONFIG_IUCV
+        { "iucv=", iucv_setup } ,
+#endif
+
 #endif
 
 #ifdef CONFIG_FB
@@ -980,6 +990,9 @@
 #endif
 #ifdef CONFIG_APM
 	{ "apm=", apm_setup },
+#endif
+#ifdef CONFIG_3215
+	{ "condev=", con3215_setup },
 #endif
 #ifdef CONFIG_MDISK
         { "mdisk=", mdisk_setup },
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/ipv4/tcp_output.c linux.15p1/net/ipv4/tcp_output.c
--- linux.vanilla/net/ipv4/tcp_output.c	Sat Aug 14 02:26:52 1999
+++ linux.15p1/net/ipv4/tcp_output.c	Wed Jan 12 01:11:52 2000
@@ -1004,7 +1004,7 @@
 	unsigned long timeout;
 
 	/* Stay within the limit we were given */
-	timeout = tp->ato;
+	timeout = (tp->ato << 1) >> 1;
 	if (timeout > max_timeout)
 		timeout = max_timeout;
 	timeout += jiffies;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/x25/x25_link.c linux.15p1/net/x25/x25_link.c
--- linux.vanilla/net/x25/x25_link.c	Mon Jul 27 07:35:58 1998
+++ linux.15p1/net/x25/x25_link.c	Wed Jan 12 01:03:47 2000
@@ -84,8 +84,9 @@
 	switch (frametype) {
 		case X25_RESTART_REQUEST:
 			x25_stop_t20timer(neigh);
+			if (neigh->state!=X25_LINK_STATE_2)
+				x25_transmit_restart_confirmation(neigh);
 			neigh->state = X25_LINK_STATE_3;
-			x25_transmit_restart_confirmation(neigh);
 			break;
 
 		case X25_RESTART_CONFIRMATION:
