diff -u --recursive --new-file v2.2.0-pre4/linux/CREDITS linux/CREDITS
--- v2.2.0-pre4/linux/CREDITS	Thu Dec 31 10:28:58 1998
+++ linux/CREDITS	Mon Jan  4 15:07:27 1999
@@ -1150,8 +1150,8 @@
 
 N: Mark Lord
 E: mlord@pobox.com
-D: Author of IDE driver (ide.c), hd.c support
-D: Triton Bus Master IDE driver
+D: EIDE driver, hd.c support
+D: EIDE PCI and bus-master DMA support
 D: Hard Disk Parameter (hdparm) utility
 S: 33 Ridgefield Cr
 S: Nepean, Ontario
diff -u --recursive --new-file v2.2.0-pre4/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.2.0-pre4/linux/Documentation/Configure.help	Mon Jan  4 15:08:16 1999
+++ linux/Documentation/Configure.help	Mon Jan  4 12:57:44 1999
@@ -3604,17 +3604,25 @@
 Adaptec AIC7xxx chipset SCSI controller support
 CONFIG_SCSI_AIC7XXX
   This is support for the various aic7xxx based Adaptec SCSI
-  controllers. These include the 274x EISA cards, 284x VLB cards, 294x
-  PCI cards, 394x PCI cards, 3985 PCI card, and several versions of
-  the Adaptec built-in SCSI controllers on various PC motherboards.
+  controllers. These include the 274x EISA cards; 284x VLB cards; 2902,
+  2910, 293x, 294x, 394x, 3985 and several other PCI and motherboard based
+  SCSI controllers from Adaptec.  It does not support the AAA-13x RAID
+  controllers from Adaptec, nor will it likely ever support them.  It
+  does not support the 2920 cards from Adaptec that use the Future Domain
+  SCSI controller chip.  For those cards, you need the "Future Domain
+  16xx SCSI support" driver.
+
+  In general, if the controller is based on an Adaptec SCSI controller
+  chip from the aic777x series or the aic78xx series, it should work.  The
+  only exception is the 7810 which is specifically not supported (that's the
+  RAID controller chip on the AAA-13x cards).
+
   Information on the configuration options for this controller can be
   found by checking the help file for each of the available
-  configuration options. You also want to read
-  drivers/scsi/README.aic7xxx and the SCSI-HOWTO, available via FTP
-  (user: anonymous) at ftp://metalab.unc.edu/pub/Linux/docs/HOWTO.
-  Note that the AHA2920 SCSI host adapter is *not* supported by this
-  driver; choose "Future Domain 16xx SCSI support" instead if you have
-  one of those.
+  configuration options. You should read drivers/scsi/README.aic7xxx
+  at a minimum before contacting the maintainer with any questions.  
+  The SCSI-HOWTO, available via FTP (user: anonymous) at
+  ftp://metalab.unc.edu/pub/Linux/docs/HOWTO can also be of great help.
 
   If you want to compile this driver as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
@@ -3626,15 +3634,9 @@
   Say Y here if you want to override the default maximum number of
   commands that a single device on the aic7xxx controller is allowed
   to have active at one time. This option only affects tagged queueing
-  capable devices. The driver uses a "failsafe" value of 8 by default.
-  This is much lower than many devices can handle, but left in place
-  for safety's sake. If you say Y here, you can adjust the number of
-  commands per LUN with the following configuration option.
-  
-  NOTE: This does not actually enable tagged queueing on any
-  particular device. The driver has changed in this respect. Please
-  see the file drivers/scsi/README.aic7xxx for more information on how
-  to get particular devices to use tagged command queueing.
+  capable devices. The driver uses a value of 24 by default.
+  If you say Y here, you can adjust the number of commands per LUN
+  with the following configuration option.
   
   If unsure, say N.
 
@@ -3771,16 +3773,21 @@
 
 enable elevator sorting
 CONFIG_SCSI_U14_34F_LINKED_COMMANDS
-  This is a feature of SCSI-2 which improves performance: the host
-  adapter can send a whole list of commands to a device in one
-  batch. Some SCSI devices might not implement this properly, so the
-  safe answer is N.
+  This option enables elevator sorting for all probed SCSI disks and 
+  CDROMs. It definetly reduces the average seek distance when doing
+  random seeks, but this does not necessarily results in a noticeable
+  performance improvement: your mileage may vary...
+  The safe answer is N.
 
 maximum number of queued commands
 CONFIG_SCSI_U14_34F_MAX_TAGS
-  This specifies how many SCSI commands can be maximally queued for a
-  given SCSI device. Go with the default unless you know what you're
-  doing. Minimum is 2 and maximum is 8.
+  This specifies how many SCSI commands can be maximally queued for each
+  probed SCSI device. You should reduce the default value of 8 only if
+  you have disks with buggy or limited tagged command support.
+  Minimum is 2 and maximum is 14. This value is also the window size
+  used by the elevator sorting option above.
+  The effective value used by the driver for each probed SCSI device is 
+  reported at boot time.
 
 Future Domain 16xx SCSI/AHA-2920A support
 CONFIG_SCSI_FUTURE_DOMAIN
@@ -4237,13 +4244,13 @@
 
 EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support
 CONFIG_SCSI_EATA
-  This driver supports all the EATA/DMA-compliant SCSI host adapters
-  and does not need any BIOS32 service. DPT ISA and all EISA i/o
-  addresses are probed looking for the "EATA" signature. If you said Y
-  to "PCI BIOS support", the addresses of all the PCI SCSI controllers
-  reported by BIOS32 are probed as well. You want to read the start of
-  drivers/scsi/eata.c and the SCSI-HOWTO, available via FTP (user:
-  anonymous) at ftp://metalab.unc.edu/pub/Linux/docs/HOWTO.
+  This driver supports all the EATA/DMA-compliant SCSI host adapters.
+  DPT ISA and all EISA i/o addresses are probed looking for the "EATA"
+  signature. If you said Y to "PCI support", the addresses of all the 
+  PCI SCSI controllers reported by the PCI subsystem are probed as well.
+  You want to read the start of drivers/scsi/eata.c and the SCSI-HOWTO,
+  available via FTP (user: anonymous) at 
+  ftp://metalab.unc.edu/pub/Linux/docs/HOWTO.
 
   Note that there is also another driver for the same hardware
   available: "EATA-DMA support". You should say Y to only one of them.
@@ -4257,22 +4264,27 @@
 CONFIG_SCSI_EATA_TAGGED_QUEUE
   This is a feature of SCSI-2 which improves performance: the host
   adapter can send several SCSI commands to a device's queue even if
-  previous commands haven't finished yet. Some SCSI devices don't
-  implement this properly, so the safe answer is N.
+  previous commands haven't finished yet. Most EATA adapters negotiate
+  this feature automatically with the device, even if your answer is N.
+  The safe answer is N.
         
 enable elevator sorting
 CONFIG_SCSI_EATA_LINKED_COMMANDS
-  This is a feature of SCSI-2 which improves performance: the host
-  adapter can send a whole list of commands to a device in one
-  batch. Some SCSI devices might not implement this properly, so the
-  safe answer is N.
+  This option enables elevator sorting for all probed SCSI disks and 
+  CDROMs. It definetly reduces the average seek distance when doing
+  random seeks, but this does not necessarily results in a noticeable
+  performance improvement: your mileage may vary...
+  The safe answer is N.
 
 maximum number of queued commands
 CONFIG_SCSI_EATA_MAX_TAGS
-  This specifies how many SCSI commands can be maximally queued for a
-  given SCSI device. Go with the default unless you know what you're
-  doing. Minimum is 2 and maximum is 16. This number will only have an
-  effect if you said Y to "enable tagged command queuing", above.
+  This specifies how many SCSI commands can be maximally queued for each
+  probed SCSI device. You should reduce the default value of 16 only if
+  you have disks with buggy or limited tagged command support.
+  Minimum is 2 and maximum is 62. This value is also the window size
+  used by the elevator sorting option above.
+  The effective value used by the driver for each probed SCSI device is 
+  reported at boot time.
 
 NCR53c406a SCSI support
 CONFIG_SCSI_NCR53C406A
diff -u --recursive --new-file v2.2.0-pre4/linux/Documentation/ide.txt linux/Documentation/ide.txt
--- v2.2.0-pre4/linux/Documentation/ide.txt	Sun Jun  7 11:16:25 1998
+++ linux/Documentation/ide.txt	Mon Jan  4 15:07:27 1999
@@ -1,9 +1,5 @@
-ide.txt -- Information regarding the Enhanced IDE drive in Linux 2.1.68+
+ide.txt -- Information regarding the Enhanced IDE drive in Linux 2.1/2.2
 ===============================================================================
-Supported by:
-	Mark Lord    <mlord@pobox.com>		-- disks, interfaces, probing
-	Gadi Oxman   <gadio@netvision.net.il>	-- tapes, disks, whatever
-	Scott Snyder <snyder@fnald0.fnal.gov>	-- cdroms, ATAPI, audio
 
    +-----------------------------------------------------------------+
    |  The hdparm utility for controlling various IDE features is     |
@@ -12,7 +8,7 @@
 
 See description later on below for handling BIG IDE drives with >1024 cyls.
 
-Major features of the 2.1.xx IDE driver ("NEW!" marks changes since 2.0.xx):
+Major features of the 2.1/2.2 IDE driver ("NEW!" marks changes since 2.0.xx):
 
 NEW!	- support for IDE ATAPI *floppy* drives
 	- support for IDE ATAPI *tape* drives, courtesy of Gadi Oxman
@@ -69,10 +65,9 @@
 NEW!		- works with most Pentium PCI systems, chipsets, add-on cards
 NEW!		- works with regular DMA as well as Ultra DMA
 NEW!		- automatically probes for all PCI IDE interfaces
+NEW!	- generic support for using BIOS-configured Ultra-DMA (UDMA) transfers
 
 
-For work in progress, see the comments in ide.c, ide-cd.c, triton.c, ...
-
 ***  IMPORTANT NOTICES:  BUGGY IDE CHIPSETS CAN CORRUPT DATA!!
 ***  =================
 ***  PCI versions of the CMD640 and RZ1000 interfaces are now detected
@@ -98,24 +93,26 @@
 ***  Use of the "serialize" option is no longer necessary.
 
 This is the multiple IDE interface driver, as evolved from hd.c.
-It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
+It supports up to six IDE interfaces, on one or more IRQs (usually 14 & 15).
 There can be up to two drives per interface, as per the ATA-2 spec.
 
 Primary:    ide0, port 0x1f0; major=3;  hda is minor=0; hdb is minor=64
 Secondary:  ide1, port 0x170; major=22; hdc is minor=0; hdd is minor=64
 Tertiary:   ide2, port 0x1e8; major=33; hde is minor=0; hdf is minor=64
 Quaternary: ide3, port 0x168; major=34; hdg is minor=0; hdh is minor=64
+fifth..     ide4, usually PCI, probed
+sixth..     ide5, usually PCI, probed
 
-To access devices on the 2nd/3rd/4th interfaces, device entries must first be
+To access devices on interfaces > ide0, device entries must first be
 created in /dev for them.  To create such entries, simply run the included
 shell script:   /usr/src/linux/scripts/MAKEDEV.ide
 
-Apparently many releases of Slackware 2.2/2.3 have incorrect entries
+Apparently many older releases of Slackware had incorrect entries
 in /dev for hdc* and hdd* -- this can also be corrected by running MAKEDEV.ide
 
-ide.c automatically probes for the standard four IDE interfaces,
+ide.c automatically probes for most IDE interfaces (including all PCI ones),
 for the drives/geometries attached to those interfaces, and for the
-IRQ numbers being used by the interfaces (normally 14, 15, 11 and 10).
+IRQ numbers being used by the interfaces (normally 14, 15 for ide0/ide1).
 
 For special cases, interfaces may be specified using kernel "command line"
 options.  For example,
@@ -179,7 +176,7 @@
 so ide.c now probes for both units, though success is more likely
 when the drive is jumpered correctly.
 
-Courtesy of Scott Snyder, the driver supports ATAPI cdrom drives
+Courtesy of Scott Snyder and others, the driver supports ATAPI cdrom drives
 such as the NEC-260 and the new MITSUMI triple/quad speed drives.
 Such drives will be identified at boot time, just like a hard disk.
 
@@ -227,8 +224,8 @@
 The kernel is able to execute binaries directly off of the cdrom,
 provided it is mounted with the default block size of 1024 (as above).
 
-Please pass on any feedback on the cdrom stuff to the author & maintainer,
-Scott Snyder (snyder@fnald0.fnal.gov).
+Please pass on any feedback on any of this stuff to the maintainer,
+whose address can be found in linux/MAINTAINERS.
 
 Note that if BOTH hd.c and ide.c are configured into the kernel,
 hd.c will normally be allowed to control the primary IDE interface.
@@ -256,8 +253,7 @@
 
 	insmod ide.o options="ide0=serialize ide2=0x1e8;0x3ee;11"
 
-mlord@pobox.com
-snyder@fnald0.fnal.gov
+
 ================================================================================
 
 Summary of ide driver parameters for kernel "command line":
@@ -307,6 +303,7 @@
 				except the cmd640.
  "idex=serialize"	: do not overlap operations on idex and ide(x^1)
  "idex=reset"		: reset interface after probe
+ "idex=dma"		: automatically configure/use DMA if possible.
 
  The following are valid ONLY on ide0,
  and the defaults for the base,ctl ports must not be altered.
@@ -319,6 +316,8 @@
  "ide0=ali14xx"		: probe/support ali14xx chipsets (ALI M1439/M1445)
  "ide0=umc8672"		: probe/support umc8672 chipsets
 
+There may be more options than shown -- use the source, Luke!
+
 Everything else is rejected with a "BAD OPTION" message.
 
 ================================================================================
@@ -488,19 +487,19 @@
 
         - buy a motherboard that uses the Intel Triton chipset -- very common.
         - use IDE for the first two drives, placing them on separate interfaces.
+		- very fast 7200rpm drives are now available
+		(though many problems have been reported with Seagate ones).
 	- place the IDE cdrom drive as slave on either interface.
         - if additional disks are to be connected, consider your needs:
                 - fileserver?  Buy a SC200 SCSI adaptor for the next few drives.
                 - personal system?  Use IDE for the next two drives.
                 - still not enough?  Keep adding SC200 SCSI cards as needed.
 
-Most manufacturers make both IDE and SCSI-2 versions of each of their drives.
-The IDE ones are usually faster and cheaper, due to the higher data transfer
-speed of PIO mode4 (ATA2), 16.6MBytes/sec versus 10Mbytes/sec for SCSI-2.
-
-In particular, I recommend Quantum FireBalls as cheap and exceptionally fast.
-The new WD1.6GB models are also cheap screamers.
-
-For really high end systems, go for fast/wide 7200rpm SCSI.  But it'll cost ya!
+Most manufacturers make both IDE and SCSI versions of each of their drives.
+The IDE ones are usually as fast and cheaper, due to lower command overhead
+and the higher data transfer speed of UDMA2.  But fast/ultrawide/superlative
+SCSI is still king of the heap, especially for servers, if you've got the bucks.
 
 mlord@pobox.com
+--
+For current maintainers of this stuff, see the linux/MAINTAINERS file.
diff -u --recursive --new-file v2.2.0-pre4/linux/Documentation/kernel-docs.txt linux/Documentation/kernel-docs.txt
--- v2.2.0-pre4/linux/Documentation/kernel-docs.txt	Thu Dec 31 10:28:58 1998
+++ linux/Documentation/kernel-docs.txt	Mon Jan  4 11:37:29 1999
@@ -28,7 +28,7 @@
    
    PLEASE, if you know any paper not listed here or write a new document,
    send me an e-mail, and I'll include a reference to it here. Any
-   corrections, ideas or comments are also wellcomed.
+   corrections, ideas or comments are also welcomed.
    
    The papers that follow are listed in no particular order. All are
    catalogued with the following fields: the document's "Title", the
diff -u --recursive --new-file v2.2.0-pre4/linux/Documentation/networking/ip-sysctl.txt linux/Documentation/networking/ip-sysctl.txt
--- v2.2.0-pre4/linux/Documentation/networking/ip-sysctl.txt	Tue Dec 22 14:16:53 1998
+++ linux/Documentation/networking/ip-sysctl.txt	Mon Jan  4 15:31:35 1999
@@ -211,4 +211,4 @@
 Updated by:
 Andi Kleen
 ak@muc.de
-$Id: ip-sysctl.txt,v 1.7 1998/05/02 12:05:00 davem Exp $
+$Id: ip-sysctl.txt,v 1.8 1999/01/02 16:37:06 davem Exp $
diff -u --recursive --new-file v2.2.0-pre4/linux/Documentation/oops-tracing.txt linux/Documentation/oops-tracing.txt
--- v2.2.0-pre4/linux/Documentation/oops-tracing.txt	Mon Jan  4 15:08:16 1999
+++ linux/Documentation/oops-tracing.txt	Tue Jan  5 11:14:24 1999
@@ -1,9 +1,9 @@
 Quick Summary
 -------------
 
-cd /usr/src/linux/scripts
-g++ -o ksymoops ksymoops.cc
-./ksymoops ../System.map < the_oops.txt
+cd /usr/src/linux/scripts/ksymoops
+make ksymoops
+./ksymoops < the_oops.txt
 
 and send the output the maintainer of the kernel area that seems to be
 involved with the problem. Don't worry too much about getting the wrong
diff -u --recursive --new-file v2.2.0-pre4/linux/Documentation/sound/AWE32 linux/Documentation/sound/AWE32
--- v2.2.0-pre4/linux/Documentation/sound/AWE32	Mon Sep 28 10:51:32 1998
+++ linux/Documentation/sound/AWE32	Mon Jan  4 12:01:19 1999
@@ -6,21 +6,20 @@
 1) Make sure you have an ORIGINAL Creative SB32, AWE32 or AWE64 card. This is 
 important, because the driver works only with real Creative cards.
 
-2) If your card is NOT "Plug-n-Play" (I myself don't know Creative AWE non 
-plug'n'play cards however) then go to 5th step now. In the other case
+2) If your card is NOT "Plug-n-Play" then go to 5th step now. In the other case
 proceed to step 3.
 
 3) You should obtain isapnptools. I looked through other PnP packages 
 for Linux, but all they are either in deep unstable beta/alpha releases or 
 they are much worse than isapnptools. In my case isapnptools were included in 
-a Linux distribution (Red Hat 5.0). If you also already have them then go to 
+a Linux distribution (Red Hat 5.x). If you also already have them then go to 
 step 4.
 
-The latest copy of isapnptools-1.15 is available from 
-ftp://ftp.demon.co.uk/pub/unix/linux/utils/ (I tested isapnptools-1.15.tgz)
+The latest copy of isapnptools-1.17 is available from 
+ftp://sunsite.unc.edu/pub/Linux/system/hardware/isapnptools-1.17.tgz
 You should gunzip/untar it to something like /usr/local/ 
-(cp isapnptools-1.15.tgz /usr/local/; cd /usr/local/;
-tar -xzf isapnptools-1.15.tgz). 
+(cp isapnptools-1.17.tgz /usr/local/; cd /usr/local/;
+tar -xzf isapnptools-1.17.tgz). 
 
 Compile the package (make) and install it (make install).
 If something goes wrong check the INSTALL file in isapnptools-1.15 directory.
@@ -59,26 +58,13 @@
 Now you can execute "isapnp /etc/isapnp.conf". No errors should be reported.
 If you correctly installed isapnptools, then isapnp will run every boot time.
 
-5) Now you should recompile the kernel. I recommend using development kernels,
-because the AWE32 driver is included in them.  ATTENTION!  In kernels 2.1.102, 
-2.1.103, 2.1.104-pre1 and 2.1.104 (not the others) the lowlevel sound driver
-is not working. You should use the patch available at 
-http://members.xoom.com/yar/history.html. If you are using stable kernel
-releases 2.0.x, then get the latest version (3.8s9) of 
-OSS/Free at ftp://ftp.4front-tech.com/ossfree/ossfree38s9-linux20x.tar.gz
-and gunzip/untar it in /usr/src/ (assuming you keep your kernel source in
-/usr/src/linux).  Then go to /usr/src/linux/ and view the README file. That
-file contains info about kernel compilation and installation.
+5) Now you should recompile the kernel.
 
 In "make (x,menu)config" select in "Sound":
 "Sound card support", "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support",
 "Generic OPL2/OPL3 FM synthesizer support" and "FM synthesizer (YM3812/OPL-3) 
 support" as <M> (module).
 
-If you use kernel version 2.0.x or version 2.1.y (y <= 2.1.104) skip substep a,
-on 2.1.105 or later go through it.
-
-substep a:
 In "make (x,menu)config" select in "Sound":
 select "OSS sound modules" as <M> (module)
 
@@ -86,23 +72,23 @@
 "Additional low level sound drivers", "AWE32 synth" as <M> (module).
 Select "Additional low level sound drivers" as [y] (or [*] (yes)) (If it is not
 available as [y], select it as <M> (module))
-Now recompile the kernel (make dep; make (b)zImage; make modules; 
-make modules_install), update your boot loader and boot new kernel.
 
-6) Now download awesfx program from
-http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/index.html#Latest. Compile it.
-Copy sfxload program to /bin (or /sbin if you wish). To enable AWE midi 
+Now recompile the kernel (make dep; make (b)zImage, b(z)lilo, etc...;
+make modules; make modules_install), update your boot loader (if required) and
+boot new kernel.
+
+6) If awesfx program is not included in your distribution, then download it 
+from http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/index.html#Latest.
+Compile it. Copy sfxload program to /usr/bin. To enable AWE general midi
 synthesis you should also get the sound bank file for general midi from
-http://members.xoom.com/yar/synthgm.sbk.gz. Copy it to
-/usr and gunzip it there.
+http://members.xoom.com/yar/synthgm.sbk.gz. Copy it to /usr and gunzip it there.
 
-7) Edit /etc/rc.d/rc.local, inserting at the end of the file:
+7) Edit /etc/conf.modules, inserting at the end of the file:
 
-modprobe sound
-insmod uart401
-insmod sb io=0x220 irq=5 dma=1 dma16=5 mpu_io=0x330
-insmod awe_wave
-sfxload /usr/synthfm.sbk
+alias sound sb
+alias midi awe_wave
+post-install awe_wave /usr/bin/sfxload /usr/synthfm.sbk
+options sb io=0x220 irq=5 dma=1 dma16=5 mpu_io=0x330
 
 (on io=0xaaa irq=b.... you should use your own settings)
 That will enable the Sound Blaster and AWE wave synthesis.
@@ -110,13 +96,16 @@
 To play midi files you should get one of these programs:
 
 Playmidi 2.4 or higher: http://playmidi.openprojects.net
-Drvmidi 4.2.b: http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/index.html#Latest
+Drvmidi: http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/index.html#Latest
 
 (These are available at all major Linux FTP sites and may already be
  in your distribution)
+Remember to use -a switch if you have playmidi as a compiled binary (ex. RPM)
 
 If something goes wrong please e-mail me. All comments and suggestions are
 welcome.
 
 			    Yaroslav Rosomakho (alons55@dialup.ptt.ru)
-				    http://members.xoom.com/yar
+				    http://www.yar.opennet.ru
+
+Last Updated: 3Jan99
diff -u --recursive --new-file v2.2.0-pre4/linux/Documentation/sound/OPL3-SA2 linux/Documentation/sound/OPL3-SA2
--- v2.2.0-pre4/linux/Documentation/sound/OPL3-SA2	Tue Dec 22 14:16:53 1998
+++ linux/Documentation/sound/OPL3-SA2	Mon Jan  4 11:37:29 1999
@@ -4,7 +4,7 @@
 Scott Murray, scottm@interlog.com
 December, 1998
 
-NOTE: All trademarked terms mentioned below are properties of their
+NOTE: All trade-marked terms mentioned below are properties of their
       respective owners.
 
 This driver is for PnP soundcards based on the following Yamaha audio
@@ -14,10 +14,10 @@
 YMF715 aka OPL3-SA3
 YMF719 aka OPL3-SAx (?)
 
-I'm a little fuzzy on what is classified a SAx, as I've seen the label
-used to refer to the whole 7xx family and as a specific identifier for
-the 719 on my no-name soundcard.  To make matters worse, there seem to
-be several reversions of the 715 chipset.
+I'm a little fuzzy on what exactly is classified a SAx, as I've seen
+the label used to refer to the whole 7xx family and as a specific
+identifier for the 719 on my no-name soundcard.  To make matters
+worse, there seem to be several revisions of the 715 chipset.
 
 Anyways, all of these chipsets implement the following devices:
 
@@ -35,18 +35,20 @@
 of doing this.  The most common is to use the isapnptools package to
 initialize the card, and use the kernel module form of the sound
 subsystem and sound drivers.  Alternatively, some BIOS's allow manual
-configuration of installed PnP devices in the BIOS menus, which should
+configuration of installed PnP devices in a BIOS menu, which should
 allow using the non-modular sound drivers, i.e. built into the kernel.
 
 I personally use isapnp and modules, and do not have access to a PnP
 BIOS machine to test.  If you have such a beast, try building both the
-MSS driver and this driver into the kernel (appropiately configured,
-of course) and let me know if it works.  If it does not, then email me
-if you are willing to experiment in an effort to make it work.
+MSS driver and this driver into the kernel (appropriately configured,
+of course).  I have received reports of this working, so it should be
+possible for most people with PnP BIOS.  If it does not work for you,
+then email me if you are willing to experiment in an effort to make it
+work.
 
 If you are using isapnp, follow the directions in its documentation to
-produce a configuration file.  Here is the relevant excerpt for my SAx
-card from my isapnp.conf:
+produce a configuration file.  Here is the relevant excerpt I use for
+my SAx card from my isapnp.conf:
 
 (CONFIGURE YMH0800/-1 (LD 0
 
@@ -80,11 +82,11 @@
 insmod opl3sa2 io=0x370 mss_io=0x530 mpu_io=0x330 irq=7 dma=0 dma2=3
 insmod opl3 io=0x388
 
-Remeber that the opl3sa2 module's io argument is for it's own control
+Remember that the opl3sa2 module's io argument is for it's own control
 port, which handles the card's master mixer for volume (on all cards),
-and bass and treble (on SA3 and SAx).
+and bass and treble (on SA3 and SAx cards).
 
-If all goes well an you see no error messages, you should be able to
+If all goes well and you see no error messages, you should be able to
 start using the sound capabilities of your system.  If you get an
 error message while trying to insert the opl3sa2 module, then make
 sure that the values of the various arguments match what you specified
@@ -100,12 +102,28 @@
 that says "opl3sa2.c: chipset version = <some number>".  If you want
 me to add support for your card, send me the number from this line and
 any information you have on the make and chipset of your sound card,
-and I may be able to work up something.  If you do not see these
-messages, and any of the other messages present in the log are not
-helpful, email me some details and I'll try my best to help.
+and I should be able to work up a permanent fix.
 
-To set up automatic module loading with kmod, the kernel module loader,
-I currently use the following section in my conf.modules file:
+A temporary solution is to force the driver to act as either a SA2 or
+SA3.  If you use the modular driver, this can be done with the "force"
+option.  Using "force=2" makes the driver treat your card as a SA2,
+and "force=3" makes it treat your card as a SA3.  Note that the driver
+does not really differentiate internally between the SA3 and SAx, so
+"force=3" is actually suitable for an SAx card.
+
+If you build the driver into the kernel, a similar option is
+available, "Chipset".  Setting it to 2 or 3 will yield the same result
+as the "force" option does for the module.  I recommend trying
+auto-probing first ("Chipset" equal to the default of -1) before
+forcing compatibility with a specific chipset.
+
+If you do not see the chipset version message, and none of the other
+messages present in the system log are helpful, email me some details
+and I'll try my best to help.
+
+Lastly, if you're using modules and want to set up automatic module
+loading with kmod, the kernel module loader, here is the section I
+currently use in my conf.modules file:
 
 # Sound
 alias char-major-14 opl3sa2
diff -u --recursive --new-file v2.2.0-pre4/linux/MAINTAINERS linux/MAINTAINERS
--- v2.2.0-pre4/linux/MAINTAINERS	Thu Dec 31 10:28:58 1998
+++ linux/MAINTAINERS	Mon Jan  4 15:31:35 1999
@@ -518,9 +518,9 @@
 
 NETWORKING [IPv4/IPv6]
 P:	David S. Miller
-M:	davem@caip.rutgers.edu
-P:	Eric Schenk
-M:	Eric.Schenk@dna.lth.se
+M:	davem@dm.cobaltmicro.com
+P:	Andi Kleen
+M:	ak@muc.de
 P:	Alexey Kuznetsov
 M:	kuznet@ms2.inr.ac.ru
 L:	netdev@roxanne.nuclecu.unam.mx
diff -u --recursive --new-file v2.2.0-pre4/linux/Makefile linux/Makefile
--- v2.2.0-pre4/linux/Makefile	Mon Jan  4 15:08:16 1999
+++ linux/Makefile	Mon Jan  4 15:08:27 1999
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
 SUBLEVEL = 0
-EXTRAVERSION =-pre4
+EXTRAVERSION =-pre5
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.2.0-pre4/linux/arch/i386/config.in	Fri Jan  1 12:58:17 1999
+++ linux/arch/i386/config.in	Mon Jan  4 14:28:02 1999
@@ -15,8 +15,8 @@
 	"386			CONFIG_M386	\
 	 486/Cx486		CONFIG_M486	\
 	 586/K5/5x86/6x86	CONFIG_M586	\
-	 Pentium/TSC		CONFIG_M586TSC	\
-	 PPro/K6/6x86MX		CONFIG_M686" PPro
+	 Pentium/K6/TSC		CONFIG_M586TSC	\
+	 PPro/6x86MX		CONFIG_M686" PPro
 #
 # Define implied options from the CPU selection here
 #
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v2.2.0-pre4/linux/arch/i386/kernel/ptrace.c	Thu Dec 31 10:28:59 1998
+++ linux/arch/i386/kernel/ptrace.c	Wed Jan  6 10:09:31 1999
@@ -67,6 +67,33 @@
 	return 0;
 }
 
+extern int _stext, _etext;
+static void print_child_state(struct task_struct *task)
+{
+	unsigned int * stack = (unsigned int *) task->tss.esp0;
+	int count = 40;
+
+	printk("Process: %s (stack=%p, task=%p)\n", task->comm, stack, task);
+	for (;;) {
+		unsigned int data;
+		if ((unsigned int) stack < (unsigned int) task)
+			break;
+		if ((unsigned int) stack >= PAGE_SIZE + (unsigned int) task)
+			break;
+		data = *stack;
+		stack++;
+		if (data < (unsigned long) &_stext)
+			continue;
+		if (data >= (unsigned long) &_etext)
+			continue;
+		printk("[<%08x>] ", data);
+		if (--count)
+			continue;
+		break;
+	}	
+	printk("\n");
+}
+
 /*
  * This routine gets a long from any process space by following the page
  * tables. NOTE! You should check that the long isn't on a page boundary,
@@ -408,6 +435,8 @@
 	if (!(child->flags & PF_PTRACED))
 		goto out;
 	if (child->state != TASK_STOPPED) {
+print_child_state(child);
+goto out;
 		if (request != PTRACE_KILL)
 			goto out;
 	}
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.2.0-pre4/linux/arch/i386/kernel/smp.c	Mon Jan  4 15:08:16 1999
+++ linux/arch/i386/kernel/smp.c	Mon Jan  4 11:57:30 1999
@@ -1052,10 +1052,10 @@
 	 *  the cache size)
 	 */
 
-	if (boot_cpu_data.x86 <= 4) {
+	if (!cpu_hz) {
 		/*
 		 * this basically disables processor-affinity
-		 * scheduling on <=i486 based SMP boards.
+		 * scheduling on SMP without a TSC.
 		 */
 		cacheflush_time = 0;
 		return;
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/m68k/Makefile linux/arch/m68k/Makefile
--- v2.2.0-pre4/linux/arch/m68k/Makefile	Tue Dec 22 14:16:54 1998
+++ linux/arch/m68k/Makefile	Tue Jan  5 11:20:43 1999
@@ -116,7 +116,7 @@
 
 archclean:
 	rm -f vmlinux.gz
-	rm -f kernel/m68k_defs.h kernel/m68k_defs.d
+	rm -f arch/m68k/kernel/m68k_defs.h arch/m68k/kernel/m68k_defs.d
 
 archmrproper:
 
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/m68k/config.in linux/arch/m68k/config.in
--- v2.2.0-pre4/linux/arch/m68k/config.in	Tue Dec 22 14:16:54 1998
+++ linux/arch/m68k/config.in	Tue Jan  5 11:20:43 1999
@@ -215,8 +215,8 @@
 fi
 if [ "$CONFIG_MAC" = "y" ]; then
   bool 'Mac NS 8390 based ethernet cards' CONFIG_DAYNAPORT
-  bool 'AV Macintosh onboard MACE ethernet' CONFIG_MACMACE
-  bool 'Macintosh onboard SONIC ethernet' CONFIG_MACSONIC
+#  bool 'Macintosh (AV) onboard MACE ethernet' CONFIG_MACMACE
+  bool 'Macintosh (Quadra) onboard SONIC ethernet' CONFIG_MACSONIC
 fi
 if [ "$CONFIG_VME" = "y" -a "$CONFIG_MVME16x" = "y" ]; then
   tristate 'MVME16x Ethernet support' CONFIG_MVME16x_NET
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/m68k/kernel/m68k_defs.h linux/arch/m68k/kernel/m68k_defs.h
--- v2.2.0-pre4/linux/arch/m68k/kernel/m68k_defs.h	Thu Nov 12 16:21:18 1998
+++ linux/arch/m68k/kernel/m68k_defs.h	Tue Jan  5 11:20:43 1999
@@ -3,6 +3,6 @@
  */
 
 #define TS_MAGICKEY	0x5a5a5a5a
-#define TS_TSS 478
-#define TS_ESP0 498
-#define TS_FPU 502
+#define TS_TSS 482
+#define TS_ESP0 502
+#define TS_FPU 506
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/m68k/kernel/m68k_ksyms.c linux/arch/m68k/kernel/m68k_ksyms.c
--- v2.2.0-pre4/linux/arch/m68k/kernel/m68k_ksyms.c	Tue Dec 22 14:16:54 1998
+++ linux/arch/m68k/kernel/m68k_ksyms.c	Tue Jan  5 11:20:43 1999
@@ -45,6 +45,8 @@
 EXPORT_SYMBOL(strnlen);
 EXPORT_SYMBOL(strrchr);
 EXPORT_SYMBOL(strstr);
+EXPORT_SYMBOL(strtok);
+EXPORT_SYMBOL(strpbrk);
 EXPORT_SYMBOL(local_irq_count);
 EXPORT_SYMBOL(local_bh_count);
 EXPORT_SYMBOL(enable_irq);
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/m68k/kernel/process.c linux/arch/m68k/kernel/process.c
--- v2.2.0-pre4/linux/arch/m68k/kernel/process.c	Tue Dec 22 14:16:54 1998
+++ linux/arch/m68k/kernel/process.c	Tue Jan  5 11:20:43 1999
@@ -40,6 +40,7 @@
  */
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
+static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/m68k/kernel/ptrace.c linux/arch/m68k/kernel/ptrace.c
--- v2.2.0-pre4/linux/arch/m68k/kernel/ptrace.c	Thu Nov 12 16:21:18 1998
+++ linux/arch/m68k/kernel/ptrace.c	Tue Jan  5 11:20:43 1999
@@ -375,7 +375,9 @@
 		case PTRACE_PEEKDATA: {
 			unsigned long tmp;
 
+			down(&child->mm->mmap_sem);
 			ret = read_long(child, addr, &tmp);
+			up(&child->mm->mmap_sem);
 			if (ret >= 0)
 				ret = put_user(tmp, (unsigned long *) data);
 			goto out;
@@ -408,7 +410,9 @@
       /* when I and D space are separate, this will have to be fixed. */
 		case PTRACE_POKETEXT: /* write the word at location addr. */
 		case PTRACE_POKEDATA:
+			down(&child->mm->mmap_sem);
 			ret = write_long(child,addr,data);
+			up(&child->mm->mmap_sem);
 			goto out;
 
 		case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/m68k/mac/config.c linux/arch/m68k/mac/config.c
--- v2.2.0-pre4/linux/arch/m68k/mac/config.c	Tue Dec 22 14:16:54 1998
+++ linux/arch/m68k/mac/config.c	Tue Jan  5 11:20:43 1999
@@ -423,7 +423,7 @@
 	 */
 
 	{	MAC_MODEL_CLII, "Classic II",		MAC_ADB_IISI,	MAC_VIA_IIci,	MAC_SCSI_OLD,	MAC_IDE_NONE,	MAC_SCC_II,     MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_CCL,  "Color Classic",	MAC_ADB_IISI,	MAC_VIA_IIci,	MAC_SCSI_OLD,	MAC_IDE_NONE,	MAC_SCC_II,     MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_CCL,  "Color Classic",	MAC_ADB_CUDA,	MAC_VIA_IIci,	MAC_SCSI_OLD,	MAC_IDE_NONE,	MAC_SCC_II,     MAC_ETHER_NONE,	MAC_NUBUS},
 
 	/*
 	 *	Some Mac LC machines. Basically the same as the IIci, ADB like IIsi
@@ -475,8 +475,8 @@
 	 *	Centris - just guessing again; maybe like Quadra
 	 */
 
-	{	MAC_MODEL_C610, "Centris 610",   MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
-	{	MAC_MODEL_C650, "Centris 650",   MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
+	{	MAC_MODEL_C610, "Centris 610",   MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_SONIC, MAC_NUBUS},
+	{	MAC_MODEL_C650, "Centris 650",   MAC_ADB_II,   MAC_VIA_QUADRA, MAC_SCSI_QUADRA,  MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_SONIC, MAC_NUBUS},
 	{	MAC_MODEL_C660, "Centris 660AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_QUADRA,	MAC_ETHER_NONE,	MAC_NUBUS},
 
 	/*
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/m68k/mac/debug.c linux/arch/m68k/mac/debug.c
--- v2.2.0-pre4/linux/arch/m68k/mac/debug.c	Tue Dec 22 14:16:54 1998
+++ linux/arch/m68k/mac/debug.c	Tue Jan  5 11:20:43 1999
@@ -397,14 +397,18 @@
 	/* Mac modem port */
 	mac_init_scc_port( B9600|CS8, 0 );
 	mac_console_driver.write = mac_scca_console_write;
+#ifdef CONFIG_SERIAL_CONSOLE
 	mac_console_driver.wait_key = mac_scca_console_wait_key;
+#endif
 	scc_port = 0;
     }
     else if (!strcmp( m68k_debug_device, "ser2" )) {
 	/* Mac printer port */
 	mac_init_scc_port( B9600|CS8, 1 );
 	mac_console_driver.write = mac_sccb_console_write;
+#ifdef CONFIG_SERIAL_CONSOLE
 	mac_console_driver.wait_key = mac_sccb_console_wait_key;
+#endif
 	scc_port = 1;
     }
 #endif
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/m68k/mac/macboing.c linux/arch/m68k/mac/macboing.c
--- v2.2.0-pre4/linux/arch/m68k/mac/macboing.c	Fri Oct  9 13:27:06 1998
+++ linux/arch/m68k/mac/macboing.c	Tue Jan  5 11:20:43 1999
@@ -1,6 +1,12 @@
 /*
  *	Mac bong noise generator. Note - we ought to put a boingy noise
  *	here 8)
+ *	 
+ *	----------------------------------------------------------------------
+ *	16.11.98:
+ *	rewrote some functions, added support for Enhanced ASC (Quadras)
+ *	after the NetBSD asc.c console bell patch by Colin Wood/Frederick Bruck
+ *	Juergen Mellinger (juergen.mellinger@t-online.de)
  */
 
 #include <linux/sched.h>
@@ -9,124 +15,281 @@
 #include <asm/macintosh.h>
 #include <asm/mac_asc.h>
 
+static int mac_asc_inited = 0;
+/*
+ * dumb triangular wave table
+ */
+static __u8 mac_asc_wave_tab[ 0x800 ];
+
+/*
+ * Alan's original sine table; needs interpolating to 0x800 
+ * (hint: interpolate or hardwire [0 -> Pi/2[, it's symmetric) 
+ */
 static const signed char sine_data[] = {
 	0,  39,  75,  103,  121,  127,  121,  103,  75,  39,
 	0, -39, -75, -103, -121, -127, -121, -103, -75, -39
 };
-#define DATA_SIZE	(sizeof(sine_data)/sizeof(sine_data[0]))
 
-static void nosound( unsigned long ignored );
-static struct timer_list sound_timer = { NULL, NULL, 0, 0, nosound };
+/*
+ * where the ASC hides ...
+ */
+static volatile __u8* mac_asc_regs = ( void* )0x50F14000;
 
-static volatile unsigned char *asc_base=(void *)0x50F14000;
+/* 
+ * sample rate; is this a good default value? 
+ */
+static unsigned long mac_asc_samplespersec = 11050;  
+static int mac_bell_duration = 0;
+static unsigned long mac_bell_phase; /* 0..2*Pi -> 0..0x800 (wavetable size) */
+static unsigned long mac_bell_phasepersample;
 
+/*
+ * some function protos 
+ */
+static void mac_init_asc( void );
+static void mac_nosound( unsigned long );
+static void mac_quadra_start_bell( unsigned int, unsigned int, unsigned int );
+static void mac_quadra_ring_bell( unsigned long );
+static void mac_av_start_bell( unsigned int, unsigned int, unsigned int );
+static void ( *mac_special_bell )( unsigned int, unsigned int, unsigned int ) = NULL;
+
+/*
+ * our timer to start/continue/stop the bell
+ */
+static struct timer_list mac_sound_timer = { NULL, NULL, 0, 0, mac_nosound };
 
-void mac_mksound( unsigned int hz, unsigned int ticks )
-{
-	static int inited = 0;
-	unsigned long flags;
-	int samples=512;
-
-	if (macintosh_config->ident == MAC_MODEL_C660
-	 || macintosh_config->ident == MAC_MODEL_Q840)
-	{
-		/*
-		 * The Quadra 660AV and 840AV use the "Singer" custom ASIC for sound I/O.
-		 * It appears to be similar to the "AWACS" custom ASIC in the Power Mac 
-		 * [678]100.  Because Singer and AWACS may have a similar hardware 
-		 * interface, this would imply that the code in drivers/sound/dmasound.c 
-		 * for AWACS could be used as a basis for Singer support.  All we have to
-		 * do is figure out how to do DMA on the 660AV/840AV through the PSC and 
-		 * figure out where the Singer hardware sits in memory. (I'd look in the
-		 * vicinity of the AWACS location in a Power Mac [678]100 first, or the 
-		 * current location of the Apple Sound Chip--ASC--in other Macs.)  The 
-		 * Power Mac [678]100 info can be found in MkLinux Mach kernel sources.
-		 *
-		 * Quoted from Apple's Tech Info Library, article number 16405:
-		 *   "Among desktop Macintosh computers, only the 660AV, 840AV, and Power
-		 *   Macintosh models have 16-bit audio input and output capability 
-		 *   because of the AT&T DSP3210 hardware circuitry and the 16-bit Singer
-		 *   codec circuitry in the AVs.  The Audio Waveform Amplifier and
-		 *   Converter (AWAC) chip in the Power Macintosh performs the same 
-		 *   16-bit I/O functionality.  The PowerBook 500 series computers
-		 *   support 16-bit stereo output, but only mono input."
-		 *
-		 *   http://til.info.apple.com/techinfo.nsf/artnum/n16405
-		 *
-		 * --David Kilzer
-		 */
+/*
+ * Sort of initialize the sound chip (called from mac_mksound on the first
+ * beep).
+ */
+static void mac_init_asc( void )
+{
+	int i;
+
+	/* 
+	 * do some machine specific initialization 
+	 * BTW:
+	 * the NetBSD Quadra patch identifies the Enhanced Apple Sound Chip via
+	 * 	mac_asc_regs[ 0x800 ] & 0xF0 != 0
+	 * this makes no sense here, because we have to set the default sample
+	 * rate anyway if we want correct frequencies
+	 */
+	switch ( macintosh_config->ident )
+	{
+		case MAC_MODEL_IIFX:
+			/*
+			 * The IIfx is always special ...
+			 */
+			mac_asc_regs = ( void* )0x50010000;
+			break;
+			/* 
+			 * not sure about how correct this list is 
+			 * machines with the EASC enhanced apple sound chip 
+			 */
+		case MAC_MODEL_Q630:
+		case MAC_MODEL_P475:
+			mac_special_bell = mac_quadra_start_bell;
+			mac_asc_samplespersec = 22150;
+			break;	
+		case MAC_MODEL_Q650:
+		case MAC_MODEL_Q700:
+		case MAC_MODEL_Q800:
+		case MAC_MODEL_Q900:
+		case MAC_MODEL_Q950:
+			/*
+			 * Currently not implemented!
+			 */
+			/*
+			 * The Quadra 660AV and 840AV use the "Singer" custom ASIC for sound I/O.
+			 * It appears to be similar to the "AWACS" custom ASIC in the Power Mac 
+			 * [678]100.  Because Singer and AWACS may have a similar hardware 
+			 * interface, this would imply that the code in drivers/sound/dmasound.c 
+			 * for AWACS could be used as a basis for Singer support.  All we have to
+			 * do is figure out how to do DMA on the 660AV/840AV through the PSC and 
+			 * figure out where the Singer hardware sits in memory. (I'd look in the
+			 * vicinity of the AWACS location in a Power Mac [678]100 first, or the 
+			 * current location of the Apple Sound Chip--ASC--in other Macs.)  The 
+			 * Power Mac [678]100 info can be found in MkLinux Mach kernel sources.
+			 *
+			 * Quoted from Apple's Tech Info Library, article number 16405:
+			 *   "Among desktop Macintosh computers, only the 660AV, 840AV, and Power
+			 *   Macintosh models have 16-bit audio input and output capability
+			 *   because of the AT&T DSP3210 hardware circuitry and the 16-bit Singer
+			 *   codec circuitry in the AVs.  The Audio Waveform Amplifier and
+			 *   Converter (AWAC) chip in the Power Macintosh performs the same 
+			 *   16-bit I/O functionality.  The PowerBook 500 series computers
+			 *   support 16-bit stereo output, but only mono input."
+			 *
+			 *   http://til.info.apple.com/techinfo.nsf/artnum/n16405
+			 *
+			 * --David Kilzer
+			 */
+			mac_special_bell = mac_av_start_bell;
+			break;
+	}
+
+	/* 
+	 * init the wave table with a simple triangular wave 
+	 * A sine wave would sure be nicer here ...
+	 */
+	for ( i = 0; i < 0x400; i++ )
+	{
+		mac_asc_wave_tab[ i ] = i / 4;
+		mac_asc_wave_tab[ i + 0x400 ] = 0xFF - i / 4;
+	}
+	mac_asc_inited = 1;
+}	
 
+/*
+ * Called to make noise; current single entry to the boing driver. 
+ * Does the job for simple ASC, calls other routines else.
+ * XXX Fixme:
+ * Should be split into asc_mksound, easc_mksound, av_mksound and 
+ * function pointer set in mac_init_asc which would be called at 
+ * init time. 
+ * _This_ is rather ugly ...
+ */
+void mac_mksound( unsigned int freq, unsigned int length )
+{
+	__u32 cfreq = ( freq << 5 ) / 468;
+	__u32 flags;
+	int i;
+
+	if ( !mac_asc_inited )
+		mac_init_asc();
+
+	if ( mac_special_bell )
+	{
+		mac_special_bell( freq, length, 128 );
 		return;
 	}
-	
-	if(!inited)
+
+	if ( freq < 20 || freq > 20000 || length == 0 )
 	{
-		int i=0;
-		int j=0;
-		int k=0;
-		int l=0;
-
-		/*
-		 *	The IIfx strikes again!
-		 */
-		 
-		if(macintosh_config->ident==MAC_MODEL_IIFX)
-			asc_base=(void *)0x50010000;
+		mac_nosound( 0 );
+		return;
+	}
 
-		for(i=0;i<samples;i++)
-		{
-			asc_base[i]=sine_data[j];
-			asc_base[i+512]=sine_data[j];
-			asc_base[i+1024]=sine_data[j];
-			asc_base[i+1536]=sine_data[j];
-			j++;
-			if(j==DATA_SIZE)
-				j=0;
-			if(i&1)
-				k++;
-			if(k==DATA_SIZE)
-				k=0;
-			if((i&3)==3)
-				l++;
-			if(l==DATA_SIZE)
-				l=0;	
-		}
-		inited=1;
+	save_flags( flags );
+	cli();
+
+	del_timer( &mac_sound_timer );
+
+	for ( i = 0; i < 0x800; i++ )
+		mac_asc_regs[ i ] = 0;	
+	for ( i = 0; i < 0x800; i++ )
+		mac_asc_regs[ i ] = mac_asc_wave_tab[ i ];
+
+	for ( i = 0; i < 8; i++ )
+		*( __u32* )( ( __u32 )mac_asc_regs + ASC_CONTROL + 0x814 + 8 * i ) = cfreq;
+
+	mac_asc_regs[ 0x807 ] = 0;
+	mac_asc_regs[ ASC_VOLUME ] = 128;
+	mac_asc_regs[ 0x805 ] = 0;
+	mac_asc_regs[ 0x80F ] = 0;
+	mac_asc_regs[ ASC_MODE ] = ASC_MODE_SAMPLE;
+	mac_asc_regs[ ASC_ENABLE ] = ASC_ENABLE_SAMPLE;
+
+	mac_sound_timer.expires = jiffies + length;
+	add_timer( &mac_sound_timer );
+
+	restore_flags( flags );	
+}
+
+/*
+ * regular ASC: stop whining ..
+ */
+static void mac_nosound( unsigned long ignored )
+{
+	mac_asc_regs[ ASC_ENABLE ] = 0;
+}	
+
+/*
+ * EASC entry; init EASC, don't load wavetable, schedule 'start whining'.
+ */
+static void mac_quadra_start_bell( unsigned int freq, unsigned int length, unsigned int volume )
+{
+	__u32 flags;
+
+	/* if the bell is already ringing, ring longer */
+	if ( mac_bell_duration > 0 )
+	{
+		mac_bell_duration += length;
+		return;
 	}
-	save_flags(flags);
+
+	mac_bell_duration = length;
+	mac_bell_phase = 0;
+	mac_bell_phasepersample = ( freq * sizeof( mac_asc_wave_tab ) ) / mac_asc_samplespersec;
+	/* this is reasonably big for small frequencies */ 
+
+	save_flags( flags );
 	cli();
-	del_timer( &sound_timer );
 
-	if (hz > 20 && hz < 32767) {
-		int i;
-		u_long asc_pulses=((hz<<5)*samples)/468;
-		for(i=0;i<4;i++)
+	/* set the volume */
+	mac_asc_regs[ 0x806 ] = volume;
+
+	/* set up the ASC registers */
+	if ( mac_asc_regs[ 0x801 ] != 1 )
+	{
+		/* select mono mode */	
+		mac_asc_regs[ 0x807 ] = 0;
+		/* select sampled sound mode */
+		mac_asc_regs[ 0x802 ] = 0;
+	 	/* ??? */	
+		mac_asc_regs[ 0x801 ] = 1;
+		mac_asc_regs[ 0x803 ] |= 0x80;
+		mac_asc_regs[ 0x803 ] &= 0x7F;
+	}
+
+	mac_sound_timer.function = mac_quadra_ring_bell;
+	mac_sound_timer.expires = jiffies + 1;
+	add_timer( &mac_sound_timer );
+
+	restore_flags( flags );
+}
+
+/*
+ * EASC 'start/continue whining'; I'm not sure why the above function didn't
+ * already load the wave table, or at least call this one... 
+ * This piece keeps reloading the wave table until done.
+ */
+static void mac_quadra_ring_bell( unsigned long ignored )
+{
+	int 	i, count = mac_asc_samplespersec / HZ;
+	__u32 flags;
+
+	/*
+	 * we neither want a sound buffer overflow nor underflow, so we need to match
+	 * the number of samples per timer interrupt as exactly as possible.
+	 * using the asc interrupt will give better results in the future
+	 * ...and the possibility to use a real sample (a boingy noise, maybe...)
+	 */
+
+	save_flags( flags );
+	cli();
+	
+	del_timer( &mac_sound_timer );
+
+	if ( mac_bell_duration-- > 0 )
+	{
+		for ( i = 0; i < count; i++ )
 		{
-			asc_base[ASC_FREQ(i,0)]=0x00;
-			asc_base[ASC_FREQ(i,1)]=20;
-			asc_base[ASC_FREQ(i,2)]=0x00;
-			asc_base[ASC_FREQ(i,3)]=20;
-			asc_base[ASC_FREQ(i,4)]=(asc_pulses>>24)&0xFF;
-			asc_base[ASC_FREQ(i,5)]=(asc_pulses>>16)&0xFF;
-			asc_base[ASC_FREQ(i,6)]=(asc_pulses>>8)&0xFF;
-			asc_base[ASC_FREQ(i,7)]=(asc_pulses>>0)&0xFF;
-		}
-		asc_base[ASC_CHAN]=0x03;
-		asc_base[ASC_VOLUME]=128;
-		asc_base[ASC_MODE]=ASC_MODE_SAMPLE;
-		asc_base[ASC_ENABLE]=ASC_ENABLE_SAMPLE;
-		if (ticks) {
-			sound_timer.expires = jiffies + ticks;
-			add_timer( &sound_timer );
+			mac_bell_phase += mac_bell_phasepersample;
+			mac_asc_regs[ 0 ] = mac_asc_wave_tab[ mac_bell_phase & ( sizeof( mac_asc_wave_tab ) - 1 ) ];
 		}
-	} else {
-		nosound( 0 );
+		mac_sound_timer.expires = jiffies + 1;
+		add_timer( &mac_sound_timer );
 	}
-	restore_flags(flags);
+	else
+		mac_asc_regs[ 0x801 ] = 0;
+	
+	restore_flags( flags );
 }
 
-
-static void nosound( unsigned long ignored )
+/*
+ * AV code - please fill in.
+ */
+static void mac_av_start_bell( unsigned int freq, unsigned int length, unsigned int volume )
 {
-	asc_base[ASC_ENABLE]=0;
-}	
+} 
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/m68k/mac/macints.c linux/arch/m68k/mac/macints.c
--- v2.2.0-pre4/linux/arch/m68k/mac/macints.c	Tue Dec 22 14:16:54 1998
+++ linux/arch/m68k/mac/macints.c	Tue Jan  5 11:20:43 1999
@@ -163,6 +163,13 @@
 static unsigned long *mac_irqs[8];
 
 /*
+ * Some special nutcases ...
+ */
+
+static unsigned long mac_ide_irqs = 0;
+static unsigned long nubus_stuck_events = 0;
+
+/*
  * VIA/RBV/OSS/PSC register base pointers
  */
 
@@ -217,9 +224,13 @@
 static void via_do_nubus(int slot, void *via, struct pt_regs *regs);
 
 /* #define DEBUG_MACINTS */
-/* #define DEBUG_NUBUS_INT */
+
+#define DEBUG_SPURIOUS
+#define DEBUG_NUBUS_SPURIOUS
+#define DEBUG_NUBUS_INT
+
 /* #define DEBUG_VIA */
-/* #define DEBUG_VIA_NUBUS */
+#define DEBUG_VIA_NUBUS
 
 void mac_init_IRQ(void)
 {
@@ -351,6 +362,12 @@
 	mac_irqs[7]	 =  &nubus_irqs[0];
 
 	/*
+	 * Nubus Macs: turn off the Nubus dispatch interrupt for now
+	 */
+
+	mac_turnoff_irq(IRQ_MAC_NUBUS);
+
+	/*
 	 *	AV Macs: shutup the PSC ints
 	 */
 	if (macintosh_config->ident == MAC_MODEL_C660
@@ -430,8 +447,10 @@
 		return 0;
 	} 
 
-	/* add similar hack for Nubus pseudo-irq here - hide nubus_request_irq */
-
+	/* 
+	 * code below: only for VIA irqs currently 
+	 * add similar hack for Nubus pseudo-irq here - hide nubus_request_irq
+	 */
 	via         = (volatile unsigned char *) via_table[srcidx];
 	if (!via) 
 		return -EINVAL;
@@ -697,12 +716,18 @@
 	return (pending);
 }
 
+/*
+ * for /proc/interrupts: log interrupt stats broken down by 
+ * autovector int first, then by actual interrupt source.
+ */
+
 int mac_get_irq_list (char *buf)
 {
 	int i, len = 0;
 	int srcidx, irqidx;
 
 	for (i = VIA1_SOURCE_BASE; i < NUM_MAC_SOURCES+8; ++i) {
+	        /* XXX fixme: IRQ_SRC_MASK should cover VIA1 - Nubus */
 		srcidx = ((i & IRQ_SRC_MASK)>>3) - 1;
 		irqidx = (i & IRQ_IDX_MASK);
 
@@ -767,6 +792,32 @@
 	}
 	if (num_spurious)
 		len += sprintf(buf+len, "spurio.: %10u\n", num_spurious);
+
+	/* 
+	 * XXX Fixme: Nubus sources are never logged above ...
+	 */
+
+	len += sprintf(buf+len, "Nubus interrupts:\n");
+
+	for (i = 0; i < 7; i++) {
+		if (nubus_handler[i].handler == nubus_wtf)
+			continue;
+		len += sprintf(buf+len, "nubus %01X: %10lu ",
+			       i+9, 
+			       nubus_irqs[i]);
+		len += sprintf(buf+len, "%s\n", 
+			       nubus_param[i].devname);
+
+	}
+	len += sprintf(buf+len, "nubus spurious ints: %10lu\n",
+		       nubus_irqs[7]);
+	len += sprintf(buf+len, "nubus stuck events : %10lu\n",
+		       nubus_stuck_events);
+#ifdef CONFIG_BLK_DEV_IDE
+	len += sprintf(buf+len, "nubus/IDE interrupt: %10lu\n",
+		       mac_ide_irqs);
+#endif	
+
 	return len;
 }
 
@@ -787,8 +838,9 @@
 
 void mac_default_handler(int irq, void *dev_id, struct pt_regs *regs)
 {
-#ifdef DEBUG_VIA
-	printk("Unexpected IRQ %d on device %p\n", irq, dev_id);
+#ifdef DEBUG_SPURIOUS
+	if (console_loglevel > 6)
+		printk("Unexpected IRQ %d on device %p\n", irq, dev_id);
 #endif
 }
 
@@ -864,6 +916,18 @@
 }
 
 /*
+ *	Unexpected via interrupt
+ */
+ 
+void via_wtf(int slot, void *via, struct pt_regs *regs)
+{
+#ifdef DEBUG_SPURIOUS
+	if (console_loglevel > 6)
+		printk("Unexpected nubus event %d on via %p\n",slot,via);
+#endif
+}
+
+/*
  * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's
  * via6522.c :-), disable/pending masks added.
  * The int *viaidx etc. is just to keep the prototype happy ...
@@ -1118,17 +1182,6 @@
 }
 
 /*
- *	Unexpected via interrupt
- */
- 
-void via_wtf(int slot, void *via, struct pt_regs *regs)
-{
-#ifdef DEBUG_VIA
-	printk("Unexpected event %d on via %p\n",slot,via);
-#endif
-}
-
-/*
  *	Nubus / SCSI interrupts; OSS style
  *	The OSS is even more different than the RBV. OSS appears to stand for 
  *	Obscenely Screwed Silicon ... 
@@ -1267,8 +1320,9 @@
  
 void nubus_wtf(int slot, void *via, struct pt_regs *regs)
 {
-#ifdef DEBUG_VIA_NUBUS
-	printk("Unexpected interrupt on nubus slot %d\n",slot);
+#ifdef DEBUG_NUBUS_SPURIOUS
+	if (console_loglevel > 6)
+		printk("Unexpected interrupt on nubus slot %d\n",slot);
 #endif
 }
 
@@ -1282,9 +1336,10 @@
 	int i;
 				/* 1+2: compatibility with PSC ! */
 	for (i = 1; i < 3; i++) /* currently only these two used */
-		if (scc_handler[i].handler != mac_default_handler)
+		if (scc_handler[i].handler != mac_default_handler) {
 			(scc_handler[i].handler)(i, scc_handler[i].dev_id, regs);
-
+			scc_irqs[i]++;
+		}
 }
 
 /*
@@ -1426,6 +1481,7 @@
 	if (!nubus_active && !via2_is_oss) {
 		request_irq(IRQ_MAC_NUBUS, via_do_nubus, IRQ_FLG_LOCK, 
 			    "nubus dispatch", via_do_nubus);
+		mac_turnon_irq(IRQ_MAC_NUBUS);
 	}
 
 	nubus_active|=1<<slot;
@@ -1475,6 +1531,7 @@
  * IDE interrupt hook
  */
 extern void (*mac_ide_intr_hook)(int, void *, struct pt_regs *);
+extern int (*mac_ide_irq_p_hook)(void);
 #endif
 
 /*
@@ -1482,13 +1539,13 @@
  */
 static void via_do_nubus(int slot, void *via, struct pt_regs *regs)
 {
-	unsigned char map;
+	unsigned char map, allints;
 	int i;
 	int ct=0;
-
-/*	printk("nubus interrupt\n");*/
+	int ide_pending = 0;
 		
 	/* lock the nubus interrupt */
+	/* That's just 'clear Nubus IRQ bit in VIA2' BTW. Pretty obsolete ? */
 	if (via2_is_rbv) 
 		via_write(rbv_regp, rIFR, 0x82);
 	else
@@ -1496,37 +1553,69 @@
 	
 #ifdef CONFIG_BLK_DEV_MAC_IDE
 	/* IDE hack */
-	if (mac_ide_intr_hook)
+	if (mac_ide_intr_hook) {
 		/* 'slot' is lacking the machspec bit in 2.0 */
 		/* need to pass proper dev_id = hwgroup here */
 		mac_ide_intr_hook(IRQ_MAC_NUBUS, via, regs);
+		mac_ide_irqs++;
+	}
 #endif
 
 	while(1)
 	{
 		if (via2_is_rbv)
-			map = ~via_read(rbv_regp, rBufA);
+			allints = ~via_read(rbv_regp, rBufA);
 		else
-			map = ~via_read(via2_regp, vBufA);
+			allints = ~via_read(via2_regp, vBufA);
 		
-#ifdef DEBUG_NUBUS_INT
-		printk("nubus_irq: map %x mask %x\n", map, nubus_active);
+#ifdef CONFIG_BLK_DEV_MAC_IDE
+		if (mac_ide_irq_p_hook)
+			ide_pending = mac_ide_irq_p_hook();
 #endif
 
-		if( (map = (map&nubus_active)) ==0 ) {
-#ifdef DEBUG_NUBUS_INT
-			printk("nubus_irq: nothing pending, map %x mask %x\n", 
-				map, nubus_active);
+		if ( (map = (allints&nubus_active)) == 0 
+#ifdef CONFIG_BLK_DEV_MAC_IDE
+		      && !ide_pending 
 #endif
-			nubus_irqs[7]++;
+							) 
+		{
+			if (ct == 0) {
+#ifdef DEBUG_VIA_NUBUS
+				if (console_loglevel > 5)
+					printk("nubus_irq: nothing pending, map %x mask %x active %x\n", 
+						allints, nubus_active, map);
+#endif
+				nubus_irqs[7]++;
+			}
+			/* clear it */
+			if (allints)
+				if (via2_is_rbv)
+					via_write(rbv_regp, rIFR, 0x02);
+				else
+					via_write(via2_regp, vIFR, 0x02);
 			break;
 		}
 
+#ifdef DEBUG_VIA_NUBUS
+		if (console_loglevel > 6)
+			printk("nubus_irq: map %x mask %x active %x\n", 
+				allints, nubus_active, map);
+#endif
+
+#ifdef CONFIG_BLK_DEV_MAC_IDE
+		if (mac_ide_intr_hook && ide_pending) {
+			mac_ide_intr_hook(IRQ_MAC_NUBUS, via, regs);
+			mac_ide_irqs++;
+		}
+#endif
+
 		if(ct++>2)
 		{
-#ifdef DEBUG_NUBUS_INT
-			printk("nubus stuck events - %d/%d\n", map, nubus_active);
-#endif
+			if (console_loglevel > 5)
+				printk("nubus stuck events - %x/%x/%x ide %x\n", 
+				allints, nubus_active, map, ide_pending);
+			nubus_stuck_events++;
+
 			return;
 		}
 
@@ -1583,12 +1672,14 @@
 		printk("nubus_irq: map %x mask %x\n", map, nubus_active);
 #endif
 		if( (map = (map&nubus_active)) ==0 ) {
+			if (ct == 0) {
 #ifdef CONFIG_BLK_DEV_MAC_IDE
-			if (!mac_ide_intr_hook)
-				printk("nubus_irq: nothing pending, map %x mask %x\n", 
-					map, nubus_active);
+				if (!mac_ide_intr_hook)
+					printk("nubus_irq: nothing pending, map %x mask %x\n", 
+						map, nubus_active);
 #endif
-			nubus_irqs[7]++;
+				nubus_irqs[7]++;
+			}
 			break;
 		}
 
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/m68k/vmlinux.lds linux/arch/m68k/vmlinux.lds
--- v2.2.0-pre4/linux/arch/m68k/vmlinux.lds	Tue Feb 17 13:12:45 1998
+++ linux/arch/m68k/vmlinux.lds	Tue Jan  5 11:20:43 1999
@@ -35,6 +35,9 @@
 
   _edata = .;			/* End of data section */
 
+  . = ALIGN(16);
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
   . = ALIGN(4096);		/* Init code and data */
   __init_begin = .;
   .text.init : { *(.text.init) }
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/mips/kernel/traps.c linux/arch/mips/kernel/traps.c
--- v2.2.0-pre4/linux/arch/mips/kernel/traps.c	Fri Oct 23 22:01:19 1998
+++ linux/arch/mips/kernel/traps.c	Tue Jan  5 11:13:56 1999
@@ -6,6 +6,7 @@
  *
  * Copyright 1994, 1995, 1996, 1997, 1998 by Ralf Baechle
  * Modified for R3000 by Paul M. Antoine, 1995, 1996
+ * Complete output from die() by Ulf Carlsson, 1998
  */
 #include <linux/config.h>
 #include <linux/init.h>
@@ -80,50 +81,61 @@
  * This routine abuses get_user()/put_user() to reference pointers
  * with at least a bit of error checking ...
  */
-void show_registers(char * str, struct pt_regs * regs, long err)
+void show_stack(unsigned int *sp)
 {
-	int	i;
-	int	*stack;
-	u32	*sp, *pc, addr, module_start, module_end;
-	extern	char start_kernel, _etext;
+	int i;
+	unsigned int *stack;
 
-	sp = (u32 *)regs->regs[29];
-	pc = (u32 *)regs->cp0_epc;
+	stack = sp;
+	i = 0;
 
-	show_regs(regs);
+	printk("Stack:");
+	while ((unsigned long) stack & (PAGE_SIZE - 1)) {
+		unsigned long stackdata;
 
-	/*
-	 * Dump the stack
-	 */
-	printk("Process %s (pid: %ld, stackpage=%08lx)\nStack: ",
-		current->comm, current->pid, (unsigned long)current);
-	for(i=0;i<5;i++)
-		printk("%08x ", *sp++);
-	stack = (int *) sp;
+		if (__get_user(stackdata, stack++)) {
+			printk(" (Bad stack address)");
+			break;
+		}
 
-	for(i=0; i < kstack_depth_to_print; i++) {
-		unsigned int stackdata;
+		printk(" %08lx", stackdata);
 
-		if (((u32) stack & (PAGE_SIZE -1)) == 0)
-			break;
-		if (i && ((i % 8) == 0))
-			printk("\n       ");
-		if (get_user(stackdata, stack++) < 0) {
-			printk("(Bad stack address)");
+		if (++i > 40) {
+			printk(" ...");
 			break;
 		}
-		printk("%08x ", stackdata);
+
+		if (i % 8 == 0)
+			printk("\n      ");
 	}
-	printk("\nCall Trace: ");
-	stack = (int *)sp;
-	i = 1;
+}
+
+void show_trace(unsigned int *sp)
+{
+	int i;
+	unsigned int *stack;
+	unsigned long kernel_start, kernel_end;
+	unsigned long module_start, module_end;
+	extern char _stext, _etext;
+
+	stack = sp;
+	i = 0;
+
+	kernel_start = (unsigned long) &_stext;
+	kernel_end = (unsigned long) &_etext;
 	module_start = VMALLOC_START;
 	module_end = module_start + MODULE_RANGE;
-	while (((unsigned long)stack & (PAGE_SIZE -1)) != 0) {
-		if (get_user(addr, stack++) < 0) {
-			printk("(Bad address)\n");
+
+	printk("\nCall Trace:");
+
+	while ((unsigned long) stack & (PAGE_SIZE -1)) {
+		unsigned long addr;
+
+		if (__get_user(addr, stack++)) {
+			printk(" (Bad stack address)\n");
 			break;
 		}
+
 		/*
 		 * If the address is either in the text segment of the
 		 * kernel, or in the region which contains vmalloc'ed
@@ -132,26 +144,33 @@
 		 * down the cause of the crash will be able to figure
 		 * out the call path that was taken.
 		 */
-		if (((addr >= (u32) &start_kernel) &&
-		     (addr <= (u32) &_etext)) ||
-		    ((addr >= module_start) && (addr <= module_end))) {
-			if (i && ((i % 8) == 0))
-				printk("\n       ");
-			printk("%08x ", addr);
-			i++;
+
+		if ((addr >= kernel_start && addr < kernel_end) ||
+		    (addr >= module_start && addr < module_end)) { 
+
+			printk(" [<%08lx>]", addr);
+			if (++i > 40) {
+				printk(" ...");
+				break;
+			}
 		}
 	}
+}
 
-	printk("\nCode : ");
-	if ((KSEGX(pc) == KSEG0 || KSEGX(pc) == KSEG1) &&
-	    (((unsigned long) pc & 3) == 0))
-	{
-		for(i=0;i<5;i++)
-			printk("%08x ", *pc++);
-		printk("\n");
+void show_code(unsigned int *pc)
+{
+	long i;
+
+	printk("\nCode:");
+
+	for(i = -3 ; i < 6 ; i++) {
+		unsigned long insn;
+		if (__get_user(insn, pc + i)) {
+			printk(" (Bad address in epc)\n");
+			break;
+		}
+		printk("%c%08lx%c",(i?' ':'<'),insn,(i?' ':'>'));
 	}
-	else
-		printk("(Bad address in epc)\n");
 }
 
 void die(const char * str, struct pt_regs * regs, unsigned long err)
@@ -162,6 +181,12 @@
 	console_verbose();
 	printk("%s: %04lx\n", str, err & 0xffff);
 	show_regs(regs);
+	printk("Process %s (pid: %ld, stackpage=%08lx)\n",
+		current->comm, current->pid, (unsigned long) current);
+	show_stack((unsigned int *) regs->regs[29]);
+	show_trace((unsigned int *) regs->regs[29]);
+	show_code((unsigned int *) regs->cp0_epc);
+	printk("\n");
 	do_exit(SIGSEGV);
 }
 
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/ppc/common_defconfig linux/arch/ppc/common_defconfig
--- v2.2.0-pre4/linux/arch/ppc/common_defconfig	Mon Dec 28 15:00:52 1998
+++ linux/arch/ppc/common_defconfig	Mon Jan  4 15:32:42 1999
@@ -48,7 +48,7 @@
 # CONFIG_KGDB is not set
 # CONFIG_XMON is not set
 # CONFIG_TOTALMP is not set
-# CONFIG_BOOTX_TEXT is not set
+CONFIG_BOOTX_TEXT=y
 
 #
 # Plug and Play support
@@ -114,6 +114,10 @@
 # CONFIG_NET_FASTROUTE is not set
 # CONFIG_NET_HW_FLOWCONTROL is not set
 # CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
 # CONFIG_NET_SCHED is not set
 
 #
@@ -133,6 +137,7 @@
 # SCSI low-level drivers
 #
 # CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
 # CONFIG_SCSI_AHA152X is not set
 # CONFIG_SCSI_AHA1542 is not set
 # CONFIG_SCSI_AHA1740 is not set
@@ -140,14 +145,16 @@
 # CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_IN2000 is not set
 # CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_BUSLOGIC is not set
 # CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
 # CONFIG_SCSI_EATA_DMA is not set
 # CONFIG_SCSI_EATA_PIO is not set
-# CONFIG_SCSI_EATA is not set
 # CONFIG_SCSI_FUTURE_DOMAIN is not set
 # CONFIG_SCSI_GDTH is not set
 # CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_INITIO is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
 CONFIG_SCSI_NCR53C8XX=y
@@ -171,7 +178,7 @@
 # CONFIG_SCSI_DEBUG is not set
 CONFIG_SCSI_MESH=y
 CONFIG_SCSI_MESH_SYNC_RATE=5
-CONFIG_SCSI_MAC53C94=m
+CONFIG_SCSI_MAC53C94=y
 
 #
 # Network device support
@@ -221,6 +228,7 @@
 # CONFIG_TR is not set
 # CONFIG_SHAPER is not set
 # CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
 
 #
 # Amateur Radio support
@@ -242,25 +250,29 @@
 #
 CONFIG_DUMMY_CONSOLE=y
 CONFIG_FB_OF=y
-# CONFIG_FB_CONTROL is not set
-# CONFIG_FB_PLATINUM is not set
-# CONFIG_FB_VALKYRIE is not set
-CONFIG_FB_ATY=y
+CONFIG_FB_CONTROL=y
+CONFIG_FB_PLATINUM=y
+CONFIG_FB_VALKYRIE=y
+# CONFIG_FB_ATY is not set
 CONFIG_FB_IMSTT=y
-# CONFIG_FB_CT65550 is not set
+CONFIG_FB_CT65550=y
 # CONFIG_FB_S3TRIO is not set
 # CONFIG_FB_MATROX is not set
-CONFIG_FB_ATY=y
+# CONFIG_FB_ATY is not set
 # CONFIG_FB_VIRTUAL is not set
 # CONFIG_FBCON_ADVANCED is not set
 CONFIG_FBCON_CFB8=y
 CONFIG_FBCON_CFB16=y
-CONFIG_FBCON_CFB24=y
 CONFIG_FBCON_CFB32=y
 # CONFIG_FBCON_FONTWIDTH8_ONLY is not set
-# CONFIG_FBCON_FONTS is not set
+CONFIG_FBCON_FONTS=y
 CONFIG_FONT_8x8=y
 CONFIG_FONT_8x16=y
+# CONFIG_FONT_SUN8x16 is not set
+CONFIG_FONT_SUN12x22=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
 
 #
 # Character devices
@@ -272,6 +284,10 @@
 # CONFIG_SERIAL_NONSTANDARD is not set
 # CONFIG_UNIX98_PTYS is not set
 CONFIG_MOUSE=y
+
+#
+# Mice
+#
 # CONFIG_ATIXL_BUSMOUSE is not set
 # CONFIG_BUSMOUSE is not set
 # CONFIG_MS_BUSMOUSE is not set
@@ -280,9 +296,17 @@
 # CONFIG_PC110_PAD is not set
 # CONFIG_QIC02_TAPE is not set
 # CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
+
+#
+# Video For Linux
+#
 # CONFIG_VIDEO_DEV is not set
-# CONFIG_NVRAM is not set
+
+#
+# Joystick support
+#
 # CONFIG_JOYSTICK is not set
 
 #
@@ -294,37 +318,46 @@
 # Filesystems
 #
 # CONFIG_QUOTA is not set
-# CONFIG_MINIX_FS is not set
-CONFIG_EXT2_FS=y
-CONFIG_ISO9660_FS=y
-# CONFIG_JOLIET is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+CONFIG_HFS_FS=y
 CONFIG_FAT_FS=m
 CONFIG_MSDOS_FS=m
 # CONFIG_UMSDOS_FS is not set
 # CONFIG_VFAT_FS is not set
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
 CONFIG_NFS_FS=y
 CONFIG_NFSD=m
 # CONFIG_NFSD_SUN is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
-# CONFIG_CODA_FS is not set
 # CONFIG_SMB_FS is not set
 # CONFIG_NCP_FS is not set
-# CONFIG_HPFS_FS is not set
-# CONFIG_NTFS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_AFFS_FS is not set
-CONFIG_HFS_FS=y
-# CONFIG_ROMFS_FS is not set
-# CONFIG_AUTOFS_FS is not set
-# CONFIG_UFS_FS is not set
+
+#
+# Partition Types
+#
 # CONFIG_BSD_DISKLABEL is not set
+CONFIG_MAC_PARTITION=y
 # CONFIG_SMD_DISKLABEL is not set
 # CONFIG_SOLARIS_X86_PARTITION is not set
-# CONFIG_ADFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-CONFIG_MAC_PARTITION=y
+# CONFIG_UNIXWARE_DISKLABEL is not set
 CONFIG_NLS=y
 
 #
@@ -355,6 +388,7 @@
 # CONFIG_NLS_ISO8859_7 is not set
 # CONFIG_NLS_ISO8859_8 is not set
 # CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_15 is not set
 # CONFIG_NLS_KOI8_R is not set
 
 #
@@ -383,6 +417,7 @@
 # CONFIG_SOUND_OPL3SA2 is not set
 # CONFIG_SOUND_MAUI is not set
 # CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_AD1816 is not set
 # CONFIG_SOUND_OPL3SA1 is not set
 # CONFIG_SOUND_SOFTOSS is not set
 # CONFIG_SOUND_YM3812 is not set
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/ppc/defconfig linux/arch/ppc/defconfig
--- v2.2.0-pre4/linux/arch/ppc/defconfig	Thu Dec 31 10:28:59 1998
+++ linux/arch/ppc/defconfig	Mon Jan  4 15:32:42 1999
@@ -138,6 +138,10 @@
 # CONFIG_NET_FASTROUTE is not set
 # CONFIG_NET_HW_FLOWCONTROL is not set
 # CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
 # CONFIG_NET_SCHED is not set
 
 #
@@ -195,7 +199,6 @@
 # CONFIG_SCSI_PSI240I is not set
 # CONFIG_SCSI_QLOGIC_FAS is not set
 # CONFIG_SCSI_QLOGIC_ISP is not set
-# CONFIG_SCSI_QLOGIC_FC is not set
 # CONFIG_SCSI_SEAGATE is not set
 # CONFIG_SCSI_DC390T is not set
 # CONFIG_SCSI_T128 is not set
@@ -311,7 +314,7 @@
 #
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
-CONFIG_SERIAL=m
+# CONFIG_SERIAL is not set
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
 CONFIG_UNIX98_PTYS=y
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/ppc/kernel/process.c linux/arch/ppc/kernel/process.c
--- v2.2.0-pre4/linux/arch/ppc/kernel/process.c	Thu Dec 31 10:28:59 1998
+++ linux/arch/ppc/kernel/process.c	Tue Jan  5 11:13:56 1999
@@ -191,6 +191,19 @@
 	_enable_interrupts(s);
 }
 
+void instruction_dump (unsigned long *pc)
+{
+	int i;
+
+	if((((unsigned long) pc) & 3))
+                return;
+
+	printk("Instruction DUMP:");
+	for(i = -3; i < 6; i++)
+		printk("%c%08lx%c",i?' ':'<',pc[i],i?' ':'>');
+	printk("\n");
+}
+
 void show_regs(struct pt_regs * regs)
 {
 	int i;
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/ppc/kernel/traps.c linux/arch/ppc/kernel/traps.c
--- v2.2.0-pre4/linux/arch/ppc/kernel/traps.c	Fri May  8 23:14:45 1998
+++ linux/arch/ppc/kernel/traps.c	Tue Jan  5 11:13:56 1999
@@ -79,6 +79,7 @@
 		debugger(regs);
 #endif
 		print_backtrace((unsigned long *)regs->gpr[1]);
+		instruction_dump((unsigned long *)regs->nip);
 		panic("Exception in kernel pc %lx signal %d",regs->nip,signr);
 	}
 	force_sig(signr, current);
@@ -126,6 +127,7 @@
 		debugger(regs);
 #endif
 		print_backtrace((unsigned long *)regs->gpr[1]);
+		instruction_dump((unsigned long *)regs->nip);
 		panic("machine check");
 	}
 	_exception(SIGSEGV, regs);	
@@ -219,6 +221,7 @@
 #endif
 	show_regs(regs);
 	print_backtrace((unsigned long *)regs->gpr[1]);
+	instruction_dump((unsigned long *)regs->nip);
 	panic("kernel stack overflow");
 }
 
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/ppc/mm/fault.c linux/arch/ppc/mm/fault.c
--- v2.2.0-pre4/linux/arch/ppc/mm/fault.c	Thu Nov 19 09:56:27 1998
+++ linux/arch/ppc/mm/fault.c	Tue Jan  5 11:13:56 1999
@@ -89,6 +89,7 @@
 			printk("page fault in interrupt handler, addr=%lx\n",
 			       address);
 			show_regs(regs);
+			instruction_dump((unsigned long *)regs->nip);
 #if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
 			if (debugger_kernel_faults)
 				debugger(regs);
@@ -174,6 +175,7 @@
 	/* kernel has accessed a bad area */
 	show_regs(regs);
 	print_backtrace( (unsigned long *)regs->gpr[1] );
+	instruction_dump((unsigned long *)regs->nip);
 #if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
 	if (debugger_kernel_faults)
 		debugger(regs);
diff -u --recursive --new-file v2.2.0-pre4/linux/arch/ppc/pmac_defconfig linux/arch/ppc/pmac_defconfig
--- v2.2.0-pre4/linux/arch/ppc/pmac_defconfig	Thu Dec 31 10:28:59 1998
+++ linux/arch/ppc/pmac_defconfig	Mon Jan  4 15:32:42 1999
@@ -138,6 +138,10 @@
 # CONFIG_NET_FASTROUTE is not set
 # CONFIG_NET_HW_FLOWCONTROL is not set
 # CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
 # CONFIG_NET_SCHED is not set
 
 #
@@ -195,7 +199,6 @@
 # CONFIG_SCSI_PSI240I is not set
 # CONFIG_SCSI_QLOGIC_FAS is not set
 # CONFIG_SCSI_QLOGIC_ISP is not set
-# CONFIG_SCSI_QLOGIC_FC is not set
 # CONFIG_SCSI_SEAGATE is not set
 # CONFIG_SCSI_DC390T is not set
 # CONFIG_SCSI_T128 is not set
@@ -311,7 +314,7 @@
 #
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
-CONFIG_SERIAL=m
+# CONFIG_SERIAL is not set
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
 CONFIG_UNIX98_PTYS=y
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/block/cmd640.c linux/drivers/block/cmd640.c
--- v2.2.0-pre4/linux/drivers/block/cmd640.c	Wed Dec 17 11:11:16 1997
+++ linux/drivers/block/cmd640.c	Mon Jan  4 15:07:27 1999
@@ -5,10 +5,10 @@
  */
 
 /*
- *  Original author:	abramov@cecmow.enet.dec.com (Igor Abramov)
+ *  Original authors:	abramov@cecmow.enet.dec.com (Igor Abramov)
+ *  			mlord@pobox.com (Mark Lord)
  *
- *  Maintained by:	mlord@pobox.com (Mark Lord)
- *			with fanatical support from a legion of hackers!
+ *  See linux/MAINTAINERS for address of current maintainer.
  *
  *  This file provides support for the advanced features and bugs
  *  of IDE interfaces using the CMD Technologies 0640 IDE interface chip.
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v2.2.0-pre4/linux/drivers/block/genhd.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/block/genhd.c	Mon Jan  4 11:51:29 1999
@@ -570,6 +570,12 @@
 				bsd_kdev = MKDEV(hd->major, minor);
 				bsd_maxpart = OPENBSD_MAXPARTITIONS;
 			}
+		} else if (SYS_IND(p) == NETBSD_PARTITION) {
+			printk("!");
+			if (!bsd_kdev) {
+				bsd_kdev = MKDEV(hd->major, minor);
+				bsd_maxpart = BSD_MAXPARTITIONS;
+			}
 		}
 #endif
 #ifdef CONFIG_UNIXWARE_DISKLABEL
@@ -1336,7 +1342,7 @@
 int get_partition_list(char * page)
 {
 	struct gendisk *p;
-	char buf[8];
+	char buf[32];
 	int n, len;
 
 	len = sprintf(page, "major minor  #blocks  name\n\n");
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- v2.2.0-pre4/linux/drivers/block/ide-disk.c	Thu Dec 31 10:28:59 1998
+++ linux/drivers/block/ide-disk.c	Mon Jan  4 15:07:27 1999
@@ -5,9 +5,10 @@
  */
 
 /*
- *  Maintained by Mark Lord  <mlord@pobox.com>
- *            and Gadi Oxman <gadio@netvision.net.il>
- *            and Andre Hedrick <hedrick@astro.dyer.vanderbilt.edu>
+ *  Mostly written by Mark Lord <mlord@pobox.com>
+ *                and  Gadi Oxman <gadio@netvision.net.il>
+ *
+ *  See linux/MAINTAINERS for address of current maintainer.
  *
  * This is the IDE/ATA disk driver, as evolved from hd.c and ide.c.
  *
@@ -101,12 +102,17 @@
 		return 1;	/* lba_capacity is our only option */
 	}
 	/*
-	 * very large drives (8GB+) may lie about the number of cylinders
 	 * This is a split test for drives less than 8 Gig only.
+	 * Drives less than 8GB sometimes declare that they have 15 heads.
+	 * This is an accounting trick (0-15) == (1-16), just an initial
+	 * zero point difference.
 	 */
 	if ((id->lba_capacity < 16514064) && (lba_sects > chs_sects) &&
-	    (id->heads == 16) && (id->sectors == 63)) {
-		id->cyls = lba_sects / (16 * 63); /* correct cyls */
+	    ((id->heads == 15) || (id->heads == 16)) && (id->sectors == 63)) {
+		if (id->heads == 15)
+			id->cyls = lba_sects / (15 * 63); /* correct cyls */
+		if (id->heads == 16)
+			id->cyls = lba_sects / (16 * 63); /* correct cyls */
 		return 1;	/* lba_capacity is our only option */
 	}
 	/* perform a rough sanity check on lba_sects:  within 10% is "okay" */
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c
--- v2.2.0-pre4/linux/drivers/block/ide-probe.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/block/ide-probe.c	Mon Jan  4 15:07:27 1999
@@ -5,8 +5,10 @@
  */
 
 /*
- *  Maintained by Mark Lord  <mlord@pobox.com>
- *            and Gadi Oxman <gadio@netvision.net.il>
+ *  Mostly written by Mark Lord <mlord@pobox.com>
+ *                and Gadi Oxman <gadio@netvision.net.il>
+ *
+ *  See linux/MAINTAINERS for address of current maintainer.
  *
  * This is the IDE probe module, as evolved from hd.c and ide.c.
  *
@@ -587,7 +589,12 @@
 		drive->next = hwgroup->drive->next;
 		hwgroup->drive->next = drive;
 	}
-	hwgroup->hwif = HWIF(hwgroup->drive);
+	if (!hwgroup->hwif) {
+		hwgroup->hwif = HWIF(hwgroup->drive);
+#ifdef DEBUG
+		printk("%s : Adding missed hwif to hwgroup!!\n", hwif->name);
+#endif
+	}
 	restore_flags(flags);	/* all CPUs; safe now that hwif->hwgroup is set up */
 
 #if !defined(__mc68000__) && !defined(CONFIG_APUS) && !defined(__sparc__)
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v2.2.0-pre4/linux/drivers/block/ide-tape.c	Thu Dec 31 10:28:59 1998
+++ linux/drivers/block/ide-tape.c	Mon Jan  4 11:37:29 1999
@@ -212,7 +212,7 @@
  *                        number of tape blocks.
  *                       Add support for INTERRUPT DRQ devices.
  * Ver 1.13  Jan  2 98   Add "speed == 0" work-around for HP COLORADO 5GB
- * Ver 1.14  Dec 30 99   Partial fixes for the Sony/AIWA tape drives.
+ * Ver 1.14  Dec 30 98   Partial fixes for the Sony/AIWA tape drives.
  *                       Replace cli()/sti() with hwgroup spinlocks.
  *
  * Here are some words from the first releases of hd.c, which are quoted
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.2.0-pre4/linux/drivers/block/ide.c	Fri Jan  1 12:58:19 1999
+++ linux/drivers/block/ide.c	Mon Jan  4 15:07:27 1999
@@ -5,8 +5,10 @@
  */
 
 /*
- *  Maintained by Mark Lord  <mlord@pobox.com>
- *            and Gadi Oxman <gadio@netvision.net.il>
+ *  Mostly written by Mark Lord  <mlord@pobox.com>
+ *                and Gadi Oxman <gadio@netvision.net.il>
+ *
+ *  See linux/MAINTAINERS for address of current maintainer.
  *
  * This is the multiple IDE interface driver, as evolved from hd.c.
  * It supports up to MAX_HWIFS IDE interfaces, on one or more IRQs (usually 14 & 15).
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/block/opti621.c linux/drivers/block/opti621.c
--- v2.2.0-pre4/linux/drivers/block/opti621.c	Thu May  7 22:51:48 1998
+++ linux/drivers/block/opti621.c	Mon Jan  4 15:07:27 1999
@@ -1,20 +1,26 @@
 /*
- *  linux/drivers/block/opti621.c       Version 0.3  Nov 29, 1997
+ *  linux/drivers/block/opti621.c       Version 0.6  Jan 02, 1999
  *
- *  Copyright (C) 1996-1998  Linus Torvalds & author (see below)
+ *  Copyright (C) 1996-1998  Linus Torvalds & authors (see below)
  */
 
 /*
- * OPTi 82C621 chipset EIDE controller driver
- * Author: Jaromir Koutek (E-mail: Jaromir.Koutek@st.mff.cuni.cz)
- *
+ * Authors:
+ * Jaromir Koutek <miri@punknet.cz>,
+ * Jan Harkes <jaharkes@cwi.nl>,
+ * Mark Lord <mlord@pobox.com>
  * Some parts of code are from ali14xx.c and from rz1000.c.
+ *
+ * OPTi is trademark of OPTi, Octek is trademark of Octek.
+ *
  * I used docs from OPTi databook, from ftp.opti.com, file 9123-0002.ps
  * and disassembled/traced setupvic.exe (DOS program).
  * It increases kernel code about 2 kB.
+ * I don't have this card no more, but I hope I can get some in case
+ * of needed development.
  * My card is Octek PIDE 1.01 (on card) or OPTiViC (program).
  * It has a place for a secondary connector in circuit, but nothing
- * is there. It cost about $25. Also BIOS says no address for
+ * is there. Also BIOS says no address for
  * secondary controller (see bellow in ide_init_opti621).
  * I've only tested this on my system, which only has one disk.
  * It's Western Digital WDAC2850, with PIO mode 3. The PCI bus
@@ -26,8 +32,18 @@
  * with the third, 1GB drive: I got 3MB/s (hdparm), but sometimes
  * it slows to about 100kB/s! I don't know why and I have
  * not this drive now, so I can't try it again.
- * If you have two disk, please boot in single mode and carefully
- * (you can boot on read-only fs) try to set PIO mode 0 etc.
+ * I write this driver because I lost the paper ("manual") with
+ * settings of jumpers on the card and I have to boot Linux with
+ * Loadlin except LILO, cause I have to run the setupvic.exe program
+ * already or I get disk errors (my test: rpm -Vf
+ * /usr/X11R6/bin/XF86_SVGA - or any big file).
+ * Some numbers from hdparm -t /dev/hda:
+ * Timing buffer-cache reads:   32 MB in  3.02 seconds =10.60 MB/sec
+ * Timing buffered disk reads:  16 MB in  5.52 seconds = 2.90 MB/sec
+ * I have 4 Megs/s before, but I don't know why (maybe changes
+ * in hdparm test).
+ * After release of 0.1, I got some successful reports, so it might work.
+ *
  * The main problem with OPTi is that some timings for master
  * and slave must be the same. For example, if you have master
  * PIO 3 and slave PIO 0, driver have to set some timings of
@@ -38,25 +54,37 @@
  * for autoselect mode (you can change it to PIO 0, if you want).
  * If you then set the second drive to another PIO, the old value
  * (automatically selected) will be overrided by yours.
- * I don't know what there is a 25/33MHz switch in configuration
- * register, driver is written for use at any frequency which get
+ * There is a 25/33MHz switch in configuration
+ * register, but driver is written for use at any frequency which get
  * (use idebus=xx to select PCI bus speed).
  * Use ide0=autotune for automatical tune of the PIO modes.
  * If you get strange results, do not use this and set PIO manually
  * by hdparm.
- * I write this driver because I lost the paper ("manual") with
- * settings of jumpers on the card and I have to boot Linux with
- * Loadlin except LILO, cause I have to run the setupvic.exe program
- * already or I get disk errors (my test: rpm -Vf
- * /usr/X11R6/bin/XF86_SVGA - or any big file).
- * Some numbers from hdparm -t /dev/hda:
- * Timing buffer-cache reads:   32 MB in  3.02 seconds =10.60 MB/sec
- * Timing buffered disk reads:  16 MB in  5.52 seconds = 2.90 MB/sec
- * I have 4 Megs/s before, but I don't know why (maybe bad hdparm).
- * If you tried this driver, please send me a E-mail of your experiences.
- * My E-mail address is Jaromir.Koutek@st.mff.cuni.cz (I hope
- * till 30. 6. 2000), otherwise you can try miri@atrey.karlin.mff.cuni.cz.
- * I think OPTi is trademark of OPTi, Octek is trademark of Octek and so on.
+ *
+ * Version 0.1, Nov 8, 1996
+ * by Jaromir Koutek, for 2.1.8. 
+ * Initial version of driver.
+ * 
+ * Version 0.2
+ * Number 0.2 skipped.
+ *
+ * Version 0.3, Nov 29, 1997
+ * by Mark Lord (probably), for 2.1.68
+ * Updates for use with new IDE block driver.
+ *
+ * Version 0.4, Dec 14, 1997
+ * by Jan Harkes
+ * Fixed some errors and cleaned the code.
+ *
+ * Version 0.5, Jan 2, 1998
+ * by Jaromir Koutek
+ * Updates for use with (again) new IDE block driver.
+ * Update of documentation.
+ * 
+ * Version 0.6, Jan 2, 1999
+ * by Jaromir Koutek
+ * Reversed to version 0.3 of the driver, because
+ * 0.5 doesn't work.
  */
 
 #undef REALLY_SLOW_IO	/* most systems can safely undef this */
@@ -98,8 +126,10 @@
 
 #define READ_REG 0	/* index of Read cycle timing register */
 #define WRITE_REG 1	/* index of Write cycle timing register */
-#define MISC_REG 6	/* index of Miscellaneous register */
 #define CNTRL_REG 3	/* index of Control register */
+#define STRAP_REG 5	/* index of Strap register */
+#define MISC_REG 6	/* index of Miscellaneous register */
+
 int reg_base;
 
 #define PIO_NOT_EXIST 254
@@ -203,9 +233,10 @@
 		clks->recovery_time = 2;
 		/* minimal values */
 	}
+ 
 }
 
-/* Main tune procedure, hooked by tuneproc. */
+/* Main tune procedure, called from tuneproc. */
 static void opti621_tune_drive (ide_drive_t *drive, byte pio)
 {
 	/* primary and secondary drives share some registers,
@@ -218,7 +249,7 @@
 	byte cycle1, cycle2, misc;
 	ide_hwif_t *hwif = HWIF(drive);
 
-	/* set drive->drive_data for both drives */
+	/* sets drive->drive_data for both drives */
 	compute_pios(drive, pio);
  	pio1 = hwif->drives[0].drive_data;
  	pio2 = hwif->drives[1].drive_data;
@@ -250,7 +281,7 @@
 	outb(0xff, reg_base+5);		/* hmm, setupvic.exe does this ;-) */
 	inb(reg_base+CNTRL_REG); 	/* if reads 0xff, adapter not exist? */
 	read_reg(CNTRL_REG);		/* if reads 0xc0, no interface exist? */
-	read_reg(5);			/* read version, probably 0 */
+	read_reg(STRAP_REG);		/* read version, probably 0 */
 
 	/* program primary drive */
 	write_reg(0,      MISC_REG);	/* select Index-0 for Register-A */
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/block/rz1000.c linux/drivers/block/rz1000.c
--- v2.2.0-pre4/linux/drivers/block/rz1000.c	Tue Apr 14 14:29:20 1998
+++ linux/drivers/block/rz1000.c	Mon Jan  4 15:07:27 1999
@@ -5,7 +5,9 @@
  */
 
 /*
- *  Principal Author/Maintainer:  mlord@pobox.com (Mark Lord)
+ *  Principal Author:  mlord@pobox.com (Mark Lord)
+ *
+ *  See linux/MAINTAINERS for address of current maintainer.
  *
  *  This file provides support for disabling the buggy read-ahead
  *  mode of the RZ1000 IDE chipset, commonly used on Intel motherboards.
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- v2.2.0-pre4/linux/drivers/cdrom/cdrom.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/cdrom/cdrom.c	Mon Jan  4 11:58:44 1999
@@ -376,8 +376,10 @@
 			}
 			cdinfo(CD_OPEN, "the tray is now closed.\n"); 
 		}
-		if (ret!=CDS_DISC_OK)
+		if (ret!=CDS_DISC_OK) {
+			ret = -ENOMEDIUM;
 			goto clean_up_and_return;
+		}
 	}
 	cdrom_count_tracks(cdi, &tracks);
 	if (tracks.error == CDS_NO_DISC) {
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- v2.2.0-pre4/linux/drivers/char/cyclades.c	Fri Jan  1 12:58:19 1999
+++ linux/drivers/char/cyclades.c	Mon Jan  4 11:37:29 1999
@@ -584,10 +584,8 @@
 #include <linux/pci.h>
 #include <linux/version.h>
 
-#ifdef CONFIG_PROC_FS
 #include <linux/stat.h>
 #include <linux/proc_fs.h>
-#endif
 
 #define cy_put_user	put_user
 
@@ -781,9 +779,7 @@
 static void show_status(int);
 #endif
 
-#ifdef CONFIG_PROC_FS
 static int cyclades_get_proc_info(char *, char **, off_t , int , int *, void *);
-#endif
 
 /* The Cyclades-Z polling cycle is defined by this variable */
 static long cyz_polling_cycle = CZ_DEF_POLL;
@@ -4952,7 +4948,6 @@
 	__DATE__, __TIME__);
 } /* show_version */
 
-#ifdef CONFIG_PROC_FS
 static int 
 cyclades_get_proc_info(char *buf, char **start, off_t offset, int length,
 		       int *eof, void *data)
@@ -5009,7 +5004,6 @@
 	len = 0;
     return len;
 }
-#endif
 
 
 /* The serial driver boot-time initialization code!
@@ -5281,15 +5275,8 @@
 #endif
     }
 
-#ifdef CONFIG_PROC_FS
         ent = create_proc_entry("cyclades", S_IFREG | S_IRUGO, 0);
         ent->read_proc = cyclades_get_proc_info;
-#endif
-#if 0
-#ifdef CONFIG_PROC_FS
-    proc_register(&proc_root, &cyclades_proc_entry);
-#endif
-#endif
 
     return 0;
     
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v2.2.0-pre4/linux/drivers/char/mem.c	Tue Dec 22 14:16:55 1998
+++ linux/drivers/char/mem.c	Tue Jan  5 11:35:20 1999
@@ -138,26 +138,49 @@
 	return do_write_mem(file, __va(p), p, buf, count, ppos);
 }
 
+/*
+ * This should probably be per-architecture in <asm/pgtable.h>
+ */
+static inline unsigned long pgprot_noncached(unsigned long prot)
+{
+#if defined(__i386__)
+	if (boot_cpu_data.x86 > 3)
+		prot |= _PAGE_PCD;
+#elif defined(__powerpc__)
+	prot |= _PAGE_NO_CACHE | _PAGE_GUARDED;
+#elif defined(__mc68000__)
+	if (CPU_IS_020_OR_030)
+		prot |= _PAGE_NOCACHE030;
+	/* Use no-cache mode, serialized */
+	if (CPU_IS_040_OR_060)
+		prot = (prot & _CACHEMASK040) | _PAGE_NOCACHE_S;
+#elif defined(__mips__)
+	prot = (prot & ~_CACHE_MASK) | _CACHE_UNCACHED;
+#endif
+
+	return prot;
+}
+
 static int mmap_mem(struct file * file, struct vm_area_struct * vma)
 {
 	unsigned long offset = vma->vm_offset;
 
 	if (offset & ~PAGE_MASK)
 		return -ENXIO;
-#if defined(__i386__)
+
 	/*
-	 * hmm.. This disables high-memory caching, as the XFree86 team
-	 * wondered about that at one time.
-	 * The surround logic should disable caching for the high device
-	 * addresses anyway, but right now this seems still needed.
+	 * Accessing memory above the top the kernel knows about or
+	 * through a file pointer that was marked O_SYNC will be
+	 * done non-cached.
+	 *
+	 * Set VM_IO, as this is likely a non-cached access to an
+	 * I/O area, and we don't want to include that in a core
+	 * file.
 	 */
-	if (boot_cpu_data.x86 > 3 && offset >= __pa(high_memory))
-		pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
-#endif
-#ifdef __powerpc__
-	if (offset >= __pa(high_memory))
-		pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE|_PAGE_GUARDED;
-#endif
+	if (offset >= __pa(high_memory) || (file->f_flags & O_SYNC)) {
+		pgprot_val(vma->vm_page_prot) = pgprot_noncached(pgprot_val(vma->vm_page_prot));
+		vma->vm_flags |= VM_IO;
+	}
 	if (remap_page_range(vma->vm_start, offset, vma->vm_end-vma->vm_start,
 			     vma->vm_page_prot))
 		return -EAGAIN;
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.2.0-pre4/linux/drivers/char/serial.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/char/serial.c	Mon Jan  4 11:37:29 1999
@@ -2356,6 +2356,17 @@
 		char_time = 1;
 	if (timeout)
 	  char_time = MIN(char_time, timeout);
+	/*
+	 * If the transmitter hasn't cleared in twice the approximate
+	 * amount of time to send the entire FIFO, it probably won't
+	 * ever clear.  This assumes the UART isn't doing flow
+	 * control, which is currently the case.  Hence, if it ever
+	 * takes longer than info->timeout, this is probably due to a
+	 * UART bug of some kind.  So, we clamp the timeout parameter at
+	 * 2*info->timeout.
+	 */
+	if (!timeout || timeout > 2*info->timeout)
+		timeout = 2*info->timeout;
 #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
 	printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
 	printk("jiff=%lu...", jiffies);
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c
--- v2.2.0-pre4/linux/drivers/isdn/hisax/config.c	Wed Apr  1 20:11:50 1998
+++ linux/drivers/isdn/hisax/config.c	Mon Jan  4 11:37:29 1999
@@ -97,8 +97,10 @@
 #ifdef CONFIG_HISAX_ELSA
 #define DEFAULT_CARD ISDN_CTYPE_ELSA
 #define DEFAULT_CFG {0,0,0,0}
+#ifdef MODULE
 int elsa_init_pcmcia(void*, int, int*, int);
 EXPORT_SYMBOL(elsa_init_pcmcia);
+#endif
 #endif
 #ifdef CONFIG_HISAX_AVM_A1
 #undef DEFAULT_CARD
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/net/acenic_firmware.h linux/drivers/net/acenic_firmware.h
--- v2.2.0-pre4/linux/drivers/net/acenic_firmware.h	Tue Dec 22 14:16:55 1998
+++ linux/drivers/net/acenic_firmware.h	Wed Jan  6 09:39:23 1999
@@ -13,7 +13,7 @@
 int tigonFwBssLen = 0x2080;
 u32 tigonFwSbssAddr = 0x15090;
 int tigonFwSbssLen = 0x28;
-u32 tigonFwText[(MAX_TEXT_LEN/4) + 1] __init = {
+u32 tigonFwText[(MAX_TEXT_LEN/4) + 1] __initdata = {
 0x10000003, 0x0, 0xd, 0xd, 0x3c1d0001, 0x8fbd4f84, 
 0x3a0f021, 0x3c100000, 0x26104000, 0xc00100c, 0x0, 0xd, 
 0x27bdffd8, 0x3c1cc000, 0x3c1b0013, 0x377bd800, 0xd021, 0x3c170013, 
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v2.2.0-pre4/linux/drivers/net/plip.c	Thu Dec 31 10:29:00 1998
+++ linux/drivers/net/plip.c	Mon Jan  4 16:06:00 1999
@@ -1227,7 +1227,6 @@
 plip_init(void))
 {
 	struct parport *pb = parport_enumerate();
-	int devices=0;
 	int i=0;
 
 	if (parport[0] == -2)
@@ -1238,7 +1237,7 @@
 		timid = 0;
 	}
 
-	/* When user feeds parameters, use them */
+	/* If the user feeds parameters, use them */
 	while (pb) {
 		if ((parport[0] == -1 && (!timid || !pb->devices)) || 
 		    plip_searchfor(parport, i)) {
@@ -1266,14 +1265,13 @@
 				kfree(dev_plip[i]->name);
 				kfree(dev_plip[i]);
 			} else {
-				devices++;
+				i++;
 			}
 		}
-		i++;
 		pb = pb->next;
   	}
 
-	if (devices == 0) {
+	if (i == 0) {
 		printk(KERN_INFO "plip: no devices registered\n");
 		return -EIO;
 	}
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/net/sdla_fr.c linux/drivers/net/sdla_fr.c
--- v2.2.0-pre4/linux/drivers/net/sdla_fr.c	Fri Oct 23 22:01:21 1998
+++ linux/drivers/net/sdla_fr.c	Mon Jan  4 11:37:29 1999
@@ -1078,6 +1078,8 @@
 				++chan->if_send_bfrs_passed_to_adptr;
 				++chan->ifstats.tx_packets;
 				++card->wandev.stats.tx_packets;
+				chan->ifstats.tx_bytes += skb->len;
+				card->wandev.stats.tx_bytes += skb->len;
 			}
 		}
 	}
@@ -1501,6 +1503,8 @@
 			netif_rx(skb);
 			++chan->ifstats.rx_packets;
 			++card->wandev.stats.rx_packets;
+			chan->ifstats.rx_bytes += skb->len;
+			card->wandev.stats.rx_bytes += skb->len;
 		}
 	}
 	sdla_mapmem(&card->hw, FR_MB_VECTOR);
@@ -1621,6 +1625,8 @@
 						++chan->rx_intr_bfr_passed_to_stack;
 						++chan->ifstats.rx_packets;
 						++card->wandev.stats.rx_packets;
+						chan->ifstats.rx_bytes += skb->len;
+						card->wandev.stats.rx_bytes += skb->len;
 					}
 				}
 			}
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/net/sdla_ppp.c linux/drivers/net/sdla_ppp.c
--- v2.2.0-pre4/linux/drivers/net/sdla_ppp.c	Fri Oct 23 22:01:21 1998
+++ linux/drivers/net/sdla_ppp.c	Mon Jan  4 11:37:29 1999
@@ -698,6 +698,7 @@
 		} else {
 			++ppp_priv_area->if_send_bfr_passed_to_adptr;
 			++card->wandev.stats.tx_packets;
+			card->wandev.stats.tx_bytes += skb->len;
 		}
 	}
 tx_done:
@@ -1202,6 +1203,7 @@
 				skb->mac.raw = skb->data;
 				netif_rx(skb);
 				++card->wandev.stats.rx_packets;
+ 				card->wandev.stats.rx_bytes += skb->len;
 				++ppp_priv_area->rx_intr_bfr_passed_to_stack;
 			}
 		} else {
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/net/sdla_x25.c linux/drivers/net/sdla_x25.c
--- v2.2.0-pre4/linux/drivers/net/sdla_x25.c	Fri Jan  1 12:58:20 1999
+++ linux/drivers/net/sdla_x25.c	Mon Jan  4 11:37:29 1999
@@ -1028,6 +1028,7 @@
 		{
 			netif_rx(skb);
 			++chan->ifstats.rx_packets;
+			chan->ifstats.rx_bytes += skb->len;
 		}
 	}
 }
@@ -2122,6 +2123,7 @@
 				return 1;
 			}
 			++chan->ifstats.tx_packets;
+			chan->ifstats.tx_bytes += skb->len;
 			break;
 
 		case 0x33:	/* Tx busy */
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/net/tulip.c linux/drivers/net/tulip.c
--- v2.2.0-pre4/linux/drivers/net/tulip.c	Thu Dec 31 10:29:01 1998
+++ linux/drivers/net/tulip.c	Mon Jan  4 11:37:29 1999
@@ -75,7 +75,6 @@
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
-#include <linux/bios32.h>
 
 #include <asm/processor.h>		/* Processor type for cache alignment. */
 #include <asm/bitops.h>
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c
--- v2.2.0-pre4/linux/drivers/pci/oldproc.c	Mon Jan  4 15:08:17 1999
+++ linux/drivers/pci/oldproc.c	Mon Jan  4 12:57:44 1999
@@ -539,6 +539,7 @@
 	DEVICE( ADAPTEC,	ADAPTEC_7884,	"AIC-7884U"),
 	DEVICE( ADAPTEC,	ADAPTEC_1030,	"ABA-1030 DVB receiver"),
 	DEVICE( ADAPTEC2,	ADAPTEC2_2940U2,"AHA-2940U2"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_78902,	"AIC-7890/1"),
 	DEVICE( ADAPTEC2,	ADAPTEC2_7890,	"AIC-7890/1"),
 	DEVICE( ADAPTEC2,	ADAPTEC2_3940U2,"AHA-3940U2"),
 	DEVICE( ADAPTEC2,	ADAPTEC2_3950U2D,"AHA-3950U2D"),
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/scsi/README.aic7xxx linux/drivers/scsi/README.aic7xxx
--- v2.2.0-pre4/linux/drivers/scsi/README.aic7xxx	Fri Oct 23 22:01:21 1998
+++ linux/drivers/scsi/README.aic7xxx	Mon Jan  4 12:57:44 1999
@@ -37,8 +37,10 @@
     AHA-3940U
     AHA-3940W
     AHA-3940UW
+    AHA-3940AUW
     AHA-3940U2W
     AHA-3950U2B
+    AHA-3950U2D
     AHA-3985
     AHA-3985U
     AHA-3985W
@@ -52,6 +54,7 @@
     AIC-787x
     AIC-788x
     AIC-789x
+    AIC-3860
 
     Bus Types
     ----------------------------
@@ -69,8 +72,30 @@
     AHA-398x - PCI RAID controllers with three separate SCSI controllers
                on-board.
 
-    NOTE: The AHA-2920 is NOT an AIC-7xxx based controller, and is not
-          handled by this driver.
+  Not Supported Devices
+  ------------------------------
+    Adaptec Cards
+    ----------------------------
+    AHA-2920 (Only the cards that use the Future Domain chipset are not
+              supported, any 2920 cards based on Adaptec AIC chipsets are
+	      supported)
+    AAA-13x Raid Adapters
+    AAA-113x Raid Port Card
+
+    Motherboard Chipsets
+    ----------------------------
+    AIC-7810
+
+    Bus Types
+    ----------------------------
+    R - Raid Port busses are not supported.
+
+    The hardware RAID devices sold by Adaptec are *NOT* supported by this
+    driver (and will people please stop emailing me about them, they are
+    a totally separate beast from the bare SCSI controllers and this driver
+    can not be retrofitted in any sane manner to support the hardware RAID
+    features on those cards - Doug Ledford).
+    
 
   People
   ------------------------------
@@ -299,13 +324,12 @@
 	binary->hex conversion then send an email to the aic7xxx mailing
 	list and someone can help you out.
 
-    "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to enable
-        tagged queueing on specific devices.  As of driver version 5.0.6, we
-        now globally enable tagged queueing by default, but we also disable
-        tagged queueing on all individual devices by default.  In order to
-        enable tagged queueing for certian devices at boot time, a user may
+    "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to disable
+        tagged queueing on specific devices.  As of driver version 5.1.8, we
+        now globally enable tagged queueing by default.  In order to
+        disable tagged queueing for certian devices at boot time, a user may
         use this boot param.  The driver will then parse this message out
-        and enable the specific device entries that are present based upon
+        and disable the specific device entries that are present based upon
         the value given.  The param line is parsed in the following manner:
 
           { - first instance indicates the start of this parameter values
@@ -337,18 +361,13 @@
           commas with no value specified will simply increment to the next id
           without changing anything for the missing values.
 
-        tag_info:{{8,8},,{8,8}}
-          First adapter, scsi id 0 to 8, id 1 to 8, remainder stay at their
-          default.  Second adapter stays entirely at default.  Third
-          adapter, id 0 to 8, id 1 to 8, remainder at default (identical to
-          first adapter).
-
-        tag_info:{,,,{,,,64}}
+        tag_info:{,,,{,,,255}}
           First, second, and third adapters at default values.  Fourth
-          adapter, id 3 to 64.  Notice that leading commas simply increment
-          what the first number effects, and there are no need for trailing
-          commas.  When you close out an adapter, or the entire entry,
-          anything not explicitly set stays at the default value.
+          adapter, id 3 is disabled.  Notice that leading commas simply
+	  increment what the first number effects, and there are no need
+	  for trailing commas.  When you close out an adapter, or the
+	  entire entry, anything not explicitly set stays at the default
+	  value.
 
         A final note on this option.  The scanner I used for this isn't
         perfect or highly robust.  If you mess the line up, the worst that
@@ -449,10 +468,16 @@
     ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/
       - European Linux mirror of Teleport site
 
+  Web sites
+  ------------------------------
+    http://developer.redhat.com/aic7xxx/
+      - Primary web site maintained by Doug Ledford.  I haven't actually
+        put anything up yet....but I'm planning on it.  This information
+	is put here as an add for the vapor page :)
 
 Dean W. Gehnert
 deang@teleport.com
 
 $Revision: 3.0 $
 
-Modified by Doug Ledford 1998
+Modified by Doug Ledford 1998-9
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.2.0-pre4/linux/drivers/scsi/aic7xxx.c	Mon Jan  4 15:08:17 1999
+++ linux/drivers/scsi/aic7xxx.c	Tue Jan  5 23:58:35 1999
@@ -100,7 +100,7 @@
  *
  * Further driver modifications made by Doug Ledford <dledford@redhat.com>
  *
- * Copyright (c) 1997-1998 Doug Ledford
+ * Copyright (c) 1997-1999 Doug Ledford
  *
  * These changes are released under the same licensing terms as the FreeBSD
  * driver written by Justin Gibbs.  Please see his Copyright notice above
@@ -354,7 +354,7 @@
     0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "5.1.7"
+#define AIC7XXX_C_VERSION  "5.1.9"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
@@ -449,6 +449,8 @@
  */
 #ifdef CONFIG_AIC7XXX_CMDS_PER_LUN
 #define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN
+#else
+#define AIC7XXX_CMDS_PER_LUN 24
 #endif
 
 /* Set this to the delay in seconds after SCSI bus reset. */
@@ -511,8 +513,8 @@
  * Make a define that will tell the driver not to use tagged queueing
  * by default.
  */
-#define DEFAULT_TAG_COMMANDS {255, 255, 255, 255, 255, 255, 255, 255,\
-                              255, 255, 255, 255, 255, 255, 255, 255}
+#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0,\
+                              0, 0, 0, 0, 0, 0, 0, 0}
 
 /*
  * Modify this as you see fit for your system.  By setting tag_commands
@@ -884,6 +886,7 @@
   *  and what flags weren't.  This way, I could clean up the flag usage on
   *  a use by use basis.  Doug Ledford
   */
+        AHC_RESET_DELAY           = 0x00080000,
         AHC_A_SCANNED             = 0x00100000,
         AHC_B_SCANNED             = 0x00200000,
         AHC_MULTI_CHANNEL         = 0x00400000,
@@ -1042,6 +1045,10 @@
   unsigned long            isr_count;        /* Interrupt count */
   unsigned long            spurious_int;
   scb_data_type           *scb_data;
+  volatile unsigned short  needsdtr;
+  volatile unsigned short  sdtr_pending;
+  volatile unsigned short  needwdtr;
+  volatile unsigned short  wdtr_pending;
   struct aic7xxx_cmd_queue {
     Scsi_Cmnd *head;
     Scsi_Cmnd *tail;
@@ -1073,12 +1080,15 @@
   volatile unsigned char   dev_temp_queue_depth[MAX_TARGETS];
   unsigned char            dev_commands_sent[MAX_TARGETS];
 
+  unsigned int             dev_timer_active; /* Which devs have a timer set */
+  struct timer_list        dev_timer;
+  unsigned long            dev_expires[MAX_TARGETS];
+
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
   spinlock_t               spin_lock;
   volatile unsigned char   cpu_lock_count[NR_CPUS];
 #endif
 
-  unsigned short           dev_timer_active; /* Which devs have a timer set */
 
 #ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
   Scsi_Cmnd               *dev_wdtr_cmnd[MAX_TARGETS];
@@ -1091,12 +1101,6 @@
 
   volatile scb_queue_type  delayed_scbs[MAX_TARGETS];
 
-  unsigned long            dev_expires[MAX_TARGETS];
-  struct timer_list        dev_timer;
-
-  /*
-   * The next 64....
-   */
 
   unsigned char            msg_buf[9];       /* The message for the target */
   unsigned char            msg_type;
@@ -1123,16 +1127,11 @@
   volatile unsigned char   qoutfifo[256];
   volatile unsigned char   qinfifo[256];
   unsigned int             irq;              /* IRQ for this adapter */
-  volatile unsigned short  needsdtr;
-  volatile unsigned short  sdtr_pending;
-  volatile unsigned short  needwdtr;
-  volatile unsigned short  wdtr_pending;
   int                      instance;         /* aic7xxx instance number */
   int                      scsi_id;          /* host adapter SCSI ID */
   int                      scsi_id_b;        /* channel B for twin adapters */
   unsigned int             bios_address;
   int                      board_name_index;
-  unsigned long            reset_start;
   unsigned short           needsdtr_copy;    /* default config */
   unsigned short           needwdtr_copy;    /* default config */
   unsigned short           ultraenb;         /* Ultra mode target list */
@@ -1150,7 +1149,6 @@
   struct Scsi_Host        *host;             /* pointer to scsi host */
   int                      host_no;          /* SCSI host number */
   unsigned long            mbase;            /* I/O memory address */
-  unsigned long            last_reset;
   ahc_chip                 chip;             /* chip type */
 
   /*
@@ -1166,21 +1164,21 @@
    *
    * NOTE: Enabling this feature is likely to cause a noticeable performance
    * decrease as the accesses into the stats structures blows apart multiple
-   * cache lines and is CPU time consuming.  We keep the xfer count always
-   * for use by the aic7xxx_proc.c code, but only do the bins if the
-   * proc stats code is enabled.
+   * cache lines and is CPU time consuming.
+   *
+   * NOTE: Since it doesn't really buy us much, but consumes *tons* of RAM
+   * and blows apart all sorts of cache lines, I modified this so that we
+   * no longer look at the LUN.  All LUNs now go into the same bin on each
+   * device for stats purposes.
    */
   struct aic7xxx_xferstats {
-    long w_total;                            /* total writes */
-    long r_total;                            /* total reads */
+    long w_total;                          /* total writes */
+    long r_total;                          /* total reads */
 #ifdef AIC7XXX_PROC_STATS
-    long xfers;                              /* total xfer count */
-    long w_total512;                         /* 512 byte blocks written */
-    long r_total512;                         /* 512 byte blocks read */
-    long w_bins[10];                         /* binned write */
-    long r_bins[10];                         /* binned reads */
+    long w_bins[8];                       /* binned write */
+    long r_bins[8];                       /* binned reads */
 #endif /* AIC7XXX_PROC_STATS */
-  } stats[MAX_TARGETS][MAX_LUNS];            /* [(channel << 3)|target][lun] */
+  } stats[MAX_TARGETS];                    /* [(channel << 3)|target] */
 
 #if 0
   struct target_cmd       *targetcmds;
@@ -3092,7 +3090,7 @@
       int x;
 #endif /* AIC7XXX_PROC_STATS */
 
-      sp = &p->stats[TARGET_INDEX(cmd)][cmd->lun & 0x7];
+      sp = &p->stats[TARGET_INDEX(cmd)];
 
       /*
        * For block devices, cmd->request.cmd is always == either READ or
@@ -3109,8 +3107,6 @@
           aic7xxx_verbose &= 0xffff;
 #endif
 #ifdef AIC7XXX_PROC_STATS
-        sp->xfers++;
-        sp->w_total512 += (actual >> 9);
         ptr = sp->w_bins;
 #endif /* AIC7XXX_PROC_STATS */
       }
@@ -3122,23 +3118,27 @@
           aic7xxx_verbose &= 0xffff;
 #endif
 #ifdef AIC7XXX_PROC_STATS
-        sp->xfers++;
-        sp->r_total512 += (actual >> 9);
         ptr = sp->r_bins;
 #endif /* AIC7XXX_PROC_STATS */
       }
 #ifdef AIC7XXX_PROC_STATS
-      for (x = 9; x <= 17; x++)
+      x = -10;
+      while(actual)
       {
-        if (actual < (1 << x))
-        {
-          ptr[x - 9]++;
-          break;
-        }
+        actual >>= 1;
+        x++;
+      }
+      if (x < 0)
+      {
+        ptr[0]++;
+      }
+      else if (x > 7)
+      {
+        ptr[7]++;
       }
-      if (x > 17)
+      else
       {
-        ptr[x - 9]++;
+        ptr[x]++;
       }
 #endif /* AIC7XXX_PROC_STATS */
     }
@@ -3486,13 +3486,13 @@
             "delayed_scbs queue!\n", p->host_no, channel, i, lun);
         scbq_init(&p->delayed_scbs[i]);
       }
-      if ( !(p->dev_timer_active & (0x01 << p->scsi_id)) ||
+      if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) ||
             time_after_eq(p->dev_timer.expires, p->dev_expires[i]) )
       {
         del_timer(&p->dev_timer);
         p->dev_timer.expires = p->dev_expires[i];
         add_timer(&p->dev_timer);
-        p->dev_timer_active |= (0x01 << p->scsi_id);
+        p->dev_timer_active |= (0x01 << MAX_TARGETS);
       }
     }
   }
@@ -3958,12 +3958,6 @@
    */
   aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
 
-  /*
-   * Convince Mid Level SCSI code to leave us be for a little bit...
-   */
-  p->last_reset = jiffies;
-  p->host->last_reset = (jiffies + (HZ * AIC7XXX_RESET_DELAY));
-
   if ( !(p->features & AHC_TWIN) )
   {
     restart_sequencer(p);
@@ -4009,7 +4003,8 @@
     }
     if ( (p->dev_active_cmds[tindex] >=
           p->dev_temp_queue_depth[tindex]) ||
-         (p->dev_flags[tindex] & (DEVICE_RESET_DELAY|DEVICE_WAS_BUSY)) )
+         (p->dev_flags[tindex] & (DEVICE_RESET_DELAY|DEVICE_WAS_BUSY)) ||
+         (p->flags & AHC_RESET_DELAY) )
     {
       scbq_insert_tail(&p->delayed_scbs[tindex], scb);
     }
@@ -4128,14 +4123,17 @@
 #else
   spin_lock_irqsave(&io_request_lock, cpu_flags);
 #endif
-  p->dev_timer_active &= ~(0x01 << p->scsi_id);
+  p->dev_timer_active &= ~(0x01 << MAX_TARGETS);
+  if ( (p->dev_timer_active & (0x01 << p->scsi_id)) &&
+       time_after_eq(jiffies, p->dev_expires[p->scsi_id]) )
+  {
+    p->flags &= ~AHC_RESET_DELAY;
+    p->dev_timer_active &= ~(0x01 << p->scsi_id);
+  }
   for(i=0; i<MAX_TARGETS; i++)
   {
-    if ( i == p->scsi_id )
-    {
-      continue;
-    }
-    if ( (p->dev_timer_active & (0x01 << i)) &&
+    if ( (i != p->scsi_id) &&
+         (p->dev_timer_active & (0x01 << i)) &&
          time_after_eq(jiffies, p->dev_expires[i]) )
     {
       p->dev_timer_active &= ~(0x01 << i);
@@ -4161,7 +4159,7 @@
     }
     else if ( p->dev_timer_active & (0x01 << i) )
     {
-      if ( p->dev_timer_active & (0x01 << p->scsi_id) )
+      if ( p->dev_timer_active & (0x01 << MAX_TARGETS) )
       {
         if ( time_after_eq(p->dev_timer.expires, p->dev_expires[i]) )
         {
@@ -4171,11 +4169,11 @@
       else
       {
         p->dev_timer.expires = p->dev_expires[i];
-        p->dev_timer_active |= (0x01 << p->scsi_id);
+        p->dev_timer_active |= (0x01 << MAX_TARGETS);
       }
     }
   }
-  if ( p->dev_timer_active & (0x01 << p->scsi_id) )
+  if ( p->dev_timer_active & (0x01 << MAX_TARGETS) )
   {
     add_timer(&p->dev_timer);
   }
@@ -4901,10 +4899,10 @@
                 {
                   p->dev_expires[tindex] = jiffies + (HZ / 10);
                 }
-                if ( !(p->dev_timer_active & (0x01 << p->scsi_id)) )
+                if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) )
                 {
                   p->dev_timer.expires = p->dev_expires[tindex];
-                  p->dev_timer_active |= (0x01 << p->scsi_id);
+                  p->dev_timer_active |= (0x01 << MAX_TARGETS);
                   add_timer(&p->dev_timer);
                 }
                 else if ( time_after_eq(p->dev_timer.expires,
@@ -6398,11 +6396,7 @@
   {
     int tag_enabled = TRUE;
 
-#ifdef AIC7XXX_CMDS_PER_LUN
     default_depth = AIC7XXX_CMDS_PER_LUN;
-#else
-    default_depth = 8;  /* Not many SCBs to work with. */
-#endif
  
     if (!(p->discenable & target_mask))
     {
@@ -7448,7 +7442,6 @@
   }
 
   p->host = host;
-  p->last_reset = jiffies;
   p->host_no = host->host_no;
   host->unique_id = p->instance;
   p->isr_count = 0;
@@ -7534,10 +7527,9 @@
   }
   aic_outb(p, 0, SEQ_FLAGS);
 
-  /*
-   * Detect SCB parameters and initialize the SCB array.
-   */
   detect_maxscb(p);
+
+
   printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs);
   if (aic7xxx_verbose & VERBOSE_PROBE2)
   {
@@ -8841,6 +8833,10 @@
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7890_FE,                                      20,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_78902, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      20,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7890_FE,                                      21,
@@ -8865,9 +8861,6 @@
 
     unsigned short command;
     unsigned int  devconfig, i, oldverbose;
-#ifdef MMAPIO
-    unsigned long page_offset, base;
-#endif
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
     struct pci_dev *pdev = NULL;
 #else
@@ -9018,21 +9011,68 @@
           temp_p->mbase &= PCI_BASE_ADDRESS_MEM_MASK;
           temp_p->unpause = INTEN;
           temp_p->pause = temp_p->unpause | PAUSE;
+          if ( ((temp_p->base == 0) &&
+                (temp_p->mbase == 0)) ||
+               (temp_p->irq == 0) )
+          {
+            printk("aic7xxx: <%s> at PCI %d/%d\n", 
+              board_names[aic_pdevs[i].board_name_index],
+              PCI_SLOT(temp_p->pdev->devfn),
+              PCI_FUNC(temp_p->pdev->devfn));
+            printk("aic7xxx: Controller disabled by BIOS, ignoring.\n");
+            kfree(temp_p);
+            temp_p = NULL;
+            continue;
+          }
 
 #ifdef MMAPIO
-          base = temp_p->mbase & PAGE_MASK;
-          page_offset = temp_p->mbase - base;
+          {
+            unsigned long page_offset, base;
+
+            base = temp_p->mbase & PAGE_MASK;
+            page_offset = temp_p->mbase - base;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-          temp_p->maddr = ioremap_nocache(base, page_offset + 256);
+            temp_p->maddr = ioremap_nocache(base, page_offset + 256);
 #else
-          temp_p->maddr = vremap(base, page_offset + 256);
+            temp_p->maddr = vremap(base, page_offset + 256);
 #endif
-          if(temp_p->maddr)
-          {
-            temp_p->maddr += page_offset;
+            if(temp_p->maddr)
+            {
+              temp_p->maddr += page_offset;
+              /*
+               * We need to check the I/O with the MMAPed address.  Some machines
+               * simply fail to work with MMAPed I/O and certain controllers.
+               */
+              if(aic_inb(temp_p, HCNTRL) == 0xff)
+              {
+                /*
+                 * OK.....we failed our test....go back to programmed I/O
+                 */
+                printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d\n", 
+                  board_names[aic_pdevs[i].board_name_index],
+                  PCI_SLOT(temp_p->pdev->devfn),
+                  PCI_FUNC(temp_p->pdev->devfn));
+                printk(KERN_INFO "aic7xxx: MMAPed I/O failed, reverting to "
+                                 "Programmed I/O.\n");
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
+                iounmap((void *) (((unsigned long) temp_p->maddr) & PAGE_MASK));
+#else
+                vfree((void *) (((unsigned long) temp_p->maddr) & PAGE_MASK));
+#endif
+                temp_p->maddr = 0;
+              }
+            }
           }
 #endif
 
+          /*
+           * We HAVE to make sure the first pause_sequencer() and all other
+           * subsequent I/O that isn't PCI config space I/O takes place
+           * after the MMAPed I/O region is configured and tested.  The
+           * problem is the PowerPC architecture that doesn't support
+           * programmed I/O at all, so we have to have the MMAP I/O set up
+           * for this pause to even work on those machines.
+           */
           pause_sequencer(temp_p);
 
           /*
@@ -9067,30 +9107,20 @@
           }
 
           /*
-           * Doing a switch based upon i is really gross, but since Justin
-           * changed around the chip ID stuff, we can't use that any more.
-           * Since we don't scan the devices the same way as FreeBSD, we end
-           * up doing this gross hack in order to avoid totally splitting
-           * away from Justin's init code in ahc_pci.c
+           * We need to set the CHNL? assignments before loading the SEEPROM
+           * The 3940 and 3985 cards (original stuff, not any of the later
+           * stuff) are 7870 and 7880 class chips.  The Ultra2 stuff falls
+           * under 7896 and 7897.  The 7895 is in a class by itself :)
            */
-          switch (i)
+          switch (temp_p->chip & AHC_CHIPID_MASK)
           {
-            case 7:   /* 3940 */
-            case 12:  /* 3940-Ultra */
+            case AHC_AIC7870: /* 3840 / 3985 */
+            case AHC_AIC7880: /* 3840 UW / 3985 UW */
               switch(PCI_SLOT(temp_p->pci_device_fn))
               {
                 case 5:
                   temp_p->flags |= AHC_CHNLB;
                   break;
-                default:
-                  break;
-              }
-              break;
-
-            case 8:   /* 3985 */
-            case 13:  /* 3985-Ultra */
-              switch(PCI_SLOT(temp_p->pci_device_fn))
-              {
                 case 8:
                   temp_p->flags |= AHC_CHNLB;
                   break;
@@ -9102,10 +9132,8 @@
               }
               break;
 
-            case 15:
-            case 18:
-            case 19:
-            case 20:
+            case AHC_AIC7895: /* 7895 */
+            case AHC_AIC7896: /* 7896/7 */
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               if (PCI_FUNC(temp_p->pdev->devfn) != 0)
               {
@@ -10894,23 +10922,14 @@
     }
     action = BUS_RESET;
   }
-  if ( ((jiffies - p->last_reset) < (HZ * 3)) &&
-        (action & (HOST_RESET | BUS_RESET)) )
+  if ( (p->flags & AHC_RESET_DELAY) &&
+       (action & (HOST_RESET | BUS_RESET)) )
   {
     if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
       printk(INFO_LEAD "Reset called too soon after "
         "last bus reset, delaying.\n", p->host_no, CTL_OF_CMD(cmd));
     action = RESET_DELAY;
   }
-  if ( (action & (BUS_RESET | HOST_RESET)) && (p->flags & AHC_IN_RESET)
-        && ((jiffies - p->reset_start) > (2 * HZ * 3)) )
-  {
-    printk(KERN_ERR "(scsi%d:%d:%d:%d) Yikes!!  Card must have left to go "
-        "back to Adaptec!!\n", p->host_no, CTL_OF_CMD(cmd));
-    unpause_sequencer(p, FALSE);
-    DRIVER_UNLOCK
-    return(SCSI_RESET_SNOOZE);
-  }
 /*
  *  By this point, we want to already know what we are going to do and
  *  only have the following code implement our course of action.
@@ -10942,15 +10961,23 @@
     case BUS_RESET:
     case HOST_RESET:
     default:
-      p->reset_start = jiffies;
-      p->flags |= AHC_IN_RESET;
+      p->flags |= AHC_IN_RESET | AHC_RESET_DELAY;
+      p->dev_expires[p->scsi_id] = jiffies + (3 * HZ);
+      p->dev_timer_active |= (0x01 << p->scsi_id);
+      if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) ||
+            time_after_eq(p->dev_timer.expires, p->dev_expires[p->scsi_id]) )
+      {
+        del_timer(&p->dev_timer);
+        p->dev_timer.expires = p->dev_expires[p->scsi_id];
+        add_timer(&p->dev_timer);
+        p->dev_timer_active |= (0x01 << MAX_TARGETS);
+      }
       aic7xxx_reset_channel(p, cmd->channel, TRUE);
       if ( (p->features & AHC_TWIN) && (action & HOST_RESET) )
       {
         aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE);
         restart_sequencer(p);
       }
-      p->last_reset = jiffies;
       if (action != HOST_RESET)
         result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
       else
@@ -10974,7 +11001,7 @@
        */
       if ( flags & SCSI_RESET_SYNCHRONOUS )
       {
-        cmd->result = DID_RESET << 16;
+        cmd->result = DID_BUS_BUSY << 16;
         cmd->done(cmd);
       }
       p->flags &= ~AHC_IN_RESET;
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c
--- v2.2.0-pre4/linux/drivers/scsi/aic7xxx_proc.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/scsi/aic7xxx_proc.c	Mon Jan  4 12:57:44 1999
@@ -31,7 +31,7 @@
 
 #define	BLS	(&aic7xxx_buffer[size])
 #define HDRB \
-"        < 512 512-1K   1-2K   2-4K   4-8K  8-16K 16-32K 32-64K 64-128K >128K"
+"             < 2K      2K+     4K+     8K+    16K+    32K+    64K+   128K+"
 
 #ifdef PROC_DEBUG
 extern int vsprintf(char *, const char *, va_list);
@@ -86,7 +86,7 @@
   int    size = 0;
   unsigned char i;
   struct aic7xxx_xferstats *sp;
-  unsigned char target, lun;
+  unsigned char target;
 
   HBAptr = NULL;
 
@@ -130,15 +130,12 @@
   size = 4096;
   for (target = 0; target < MAX_TARGETS; target++)
   {
-    for (lun = 0; lun < MAX_LUNS; lun++)
-    {
-      if (p->stats[target][lun].r_total != 0)
+    if (p->dev_flags[target] & DEVICE_PRESENT)
 #ifdef AIC7XXX_PROC_STATS
-        size += 512;
+      size += 512;
 #else
-        size += 256;
+      size += 256;
 #endif
-    }
   }
   if (aic7xxx_buffer_size != size)
   {
@@ -272,88 +269,83 @@
   size += sprintf(BLS, "%d}\n", p->dev_max_queue_depth[i]);
 
   size += sprintf(BLS, "\n");
-  size += sprintf(BLS, "Statistics:\n");
+  size += sprintf(BLS, "Statistics:\n\n");
   for (target = 0; target < MAX_TARGETS; target++)
   {
-    for (lun = 0; lun < MAX_LUNS; lun++)
+    sp = &p->stats[target];
+    if ((p->dev_flags[target] & DEVICE_PRESENT) == 0)
     {
-      sp = &p->stats[target][lun];
-      if (sp->r_total == 0)
-      {
-        continue;
-      }
-      if (p->features & AHC_TWIN)
+      continue;
+    }
+    if (p->features & AHC_TWIN)
+    {
+      size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
+          p->host_no, (target >> 3), (target & 0x7), 0);
+    }
+    else
+    {
+      size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
+          p->host_no, 0, target, 0);
+    }
+    size += sprintf(BLS, "  Device using %s/%s",
+          (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ?
+          "Wide" : "Narrow",
+          (p->transinfo[target].cur_offset != 0) ?
+          "Sync transfers at " : "Async transfers.\n" );
+    if (p->transinfo[target].cur_offset != 0)
+    {
+      struct aic7xxx_syncrate *sync_rate;
+      int period = p->transinfo[target].cur_period;
+      int rate = (p->transinfo[target].cur_width ==
+                  MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
+
+      sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2);
+      if (sync_rate != NULL)
       {
-        size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
-            p->host_no, (target >> 3), (target & 0x7), lun);
+        size += sprintf(BLS, "%s MByte/sec, offset %d\n",
+                        sync_rate->rate[rate],
+                        p->transinfo[target].cur_offset );
       }
       else
       {
-        size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
-            p->host_no, 0, target, lun);
-      }
-      size += sprintf(BLS, "  Device using %s/%s\n",
-            (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ?
-            "Wide" : "Narrow",
-            (p->transinfo[target].cur_offset != 0) ?
-            "Sync transfers at" : "Async transfers." );
-      if (p->transinfo[target].cur_offset != 0)
-      {
-        struct aic7xxx_syncrate *sync_rate;
-        int period = p->transinfo[target].cur_period;
-        int rate = (p->transinfo[target].cur_width ==
-                    MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
-
-        sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2);
-        if (sync_rate != NULL)
-        {
-          size += sprintf(BLS, "  %s MByte/sec, offset %d\n",
-                          sync_rate->rate[rate],
-                          p->transinfo[target].cur_offset );
-        }
-        else
-        {
-          size += sprintf(BLS, "  3.3 MByte/sec, offset %d\n",
-                          p->transinfo[target].cur_offset );
-        }
+        size += sprintf(BLS, "3.3 MByte/sec, offset %d\n",
+                        p->transinfo[target].cur_offset );
       }
-      size += sprintf(BLS, "    Device Negotiation Settings\n");
-      size += sprintf(BLS, "        Period Offset Bus Width\n");
-      size += sprintf(BLS, "User       %03d    %03d        %d\n",
-                      p->transinfo[target].user_period,
-                      p->transinfo[target].user_offset,
-                      p->transinfo[target].user_width);
-      size += sprintf(BLS, "Goal       %03d    %03d        %d\n",
-                      p->transinfo[target].goal_period,
-                      p->transinfo[target].goal_offset,
-                      p->transinfo[target].goal_width);
-      size += sprintf(BLS, "Current    %03d    %03d        %d\n",
-                      p->transinfo[target].cur_period,
-                      p->transinfo[target].cur_offset,
-                      p->transinfo[target].cur_width);
+    }
+    size += sprintf(BLS, "  Transinfo settings: ");
+    size += sprintf(BLS, "current(%d/%d/%d), ",
+                    p->transinfo[target].cur_period,
+                    p->transinfo[target].cur_offset,
+                    p->transinfo[target].cur_width);
+    size += sprintf(BLS, "goal(%d/%d/%d), ",
+                    p->transinfo[target].goal_period,
+                    p->transinfo[target].goal_offset,
+                    p->transinfo[target].goal_width);
+    size += sprintf(BLS, "user(%d/%d/%d)\n",
+                    p->transinfo[target].user_period,
+                    p->transinfo[target].user_offset,
+                    p->transinfo[target].user_width);
 #ifdef AIC7XXX_PROC_STATS
-      size += sprintf(BLS, "    Total transfers %ld (%ld read;%ld written)\n",
-          sp->xfers, sp->r_total, sp->w_total);
-      size += sprintf(BLS, "      blks(512) rd=%ld; blks(512) wr=%ld\n",
-          sp->r_total512, sp->w_total512);
-      size += sprintf(BLS, "%s\n", HDRB);
-      size += sprintf(BLS, " Reads:");
-      for (i = 0; i < NUMBER(sp->r_bins); i++)
-      {
-        size += sprintf(BLS, "%6ld ", sp->r_bins[i]);
-      }
-      size += sprintf(BLS, "\n");
-      size += sprintf(BLS, "Writes:");
-      for (i = 0; i < NUMBER(sp->w_bins); i++)
-      {
-        size += sprintf(BLS, "%6ld ", sp->w_bins[i]);
-      }
+    size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
+        sp->r_total + sp->w_total, sp->r_total, sp->w_total);
+    size += sprintf(BLS, "%s\n", HDRB);
+    size += sprintf(BLS, "   Reads:");
+    for (i = 0; i < NUMBER(sp->r_bins); i++)
+    {
+      size += sprintf(BLS, " %7ld", sp->r_bins[i]);
+    }
+    size += sprintf(BLS, "\n");
+    size += sprintf(BLS, "  Writes:");
+    for (i = 0; i < NUMBER(sp->w_bins); i++)
+    {
+      size += sprintf(BLS, " %7ld", sp->w_bins[i]);
+    }
+    size += sprintf(BLS, "\n");
 #else
-      size += sprintf(BLS, "    Total transfers: %ld/%ld read/written)\n",
-          sp->r_total, sp->w_total);
+    size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
+        sp->r_total + sp->w_total, sp->r_total, sp->w_total);
 #endif /* AIC7XXX_PROC_STATS */
-      size += sprintf(BLS, "\n\n");
-    }
+    size += sprintf(BLS, "\n\n");
   }
 
   if (size >= aic7xxx_buffer_size)
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.2.0-pre4/linux/drivers/scsi/scsi.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/scsi/scsi.c	Mon Jan  4 12:57:44 1999
@@ -279,6 +279,7 @@
 {"MATSHITA","PD","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"YAMAHA","CDR100","1.00", BLIST_NOLUN},	/* Locks up if polled for lun != 0 */
 {"YAMAHA","CDR102","1.00", BLIST_NOLUN},	/* Locks up if polled for lun != 0 */
+{"iomega","jaz 1GB","J.86", BLIST_NOTQ | BLIST_NOLUN},
 /*
  * Must be at end of list...
  */
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.2.0-pre4/linux/drivers/sound/Config.in	Mon Dec 28 15:00:52 1998
+++ linux/drivers/sound/Config.in	Mon Jan  4 11:37:29 1999
@@ -203,6 +203,7 @@
 
   dep_tristate 'Support for Yamaha OPL3-SA2, SA3, and SAx based PnP cards' CONFIG_SOUND_OPL3SA2 $CONFIG_SOUND_OSS
   if [ "$CONFIG_SOUND_OPL3SA2" = "y" ]; then
+      int 'Chipset (-1 for autoprobe, 2, or 3)' CONFIG_OPL3SA2_CHIPSET -1
       hex 'OPL3SA2 audio I/O base (530 - F48 valid)' CONFIG_OPL3SA2_BASE 530
       int 'OPL3SA2 audio IRQ 5, 7, 9, 11, 12 or 15' CONFIG_OPL3SA2_IRQ 9
       int 'OPL3SA2 audio DMA 0, 1 or 3' CONFIG_OPL3SA2_DMA 0
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v2.2.0-pre4/linux/drivers/sound/Makefile	Tue Dec 22 14:16:56 1998
+++ linux/drivers/sound/Makefile	Mon Jan  4 11:37:29 1999
@@ -24,8 +24,7 @@
 
 export-objs	:=  ad1848.o audio_syms.o midi_syms.o mpu401.o \
 		    msnd.o opl3.o sb_card.o sequencer_syms.o \
-		    sound_core.o sound_firmware.o sound_syms.o \
-		    uart401.o ad1816.o
+		    sound_core.o sound_syms.o uart401.o ad1816.o
 
 
 
@@ -296,7 +295,7 @@
 
 ifeq ($(CONFIG_TRIX_HAVE_BOOT),y)
     trix_boot.h: $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) hex2hex
-	hex2hex -i trix_boot < $(CONFIG_TRIX_BOOT_FILE) > $@
+	./hex2hex -i trix_boot < $(CONFIG_TRIX_BOOT_FILE) > $@
 else
     trix_boot.h:
 	(							\
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v2.2.0-pre4/linux/drivers/sound/ad1848.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/sound/ad1848.c	Mon Jan  4 11:37:29 1999
@@ -1944,10 +1944,11 @@
 			if (devc->irq > 0) /* There is no point in freeing irq, if it wasn't allocated */
 				free_irq(devc->irq, (void *)devc->dev_no);
 
-			sound_free_dma(audio_devs[dev]->dmap_out->dma);
+			sound_free_dma(dma_playback);
+
+			if (dma_playback != dma_capture)
+				sound_free_dma(dma_capture);
 
-			if (audio_devs[dev]->dmap_in->dma != audio_devs[dev]->dmap_out->dma)
-				sound_free_dma(audio_devs[dev]->dmap_in->dma);
 		}
 		mixer = audio_devs[devc->dev_no]->mixer_dev;
 		if(mixer>=0)
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/dev_table.c linux/drivers/sound/dev_table.c
--- v2.2.0-pre4/linux/drivers/sound/dev_table.c	Thu Nov 12 16:21:22 1998
+++ linux/drivers/sound/dev_table.c	Mon Jan  4 11:37:29 1999
@@ -524,7 +524,7 @@
 
 int sound_alloc_audiodev(void)
 { 
-	int i = register_sound_dsp(&oss_sound_fops);
+	int i = register_sound_dsp(&oss_sound_fops, -1);
 	if(i==-1)
 		return i;
 	i>>=4;
@@ -536,7 +536,7 @@
 int sound_alloc_mididev(void)
 {
 #ifdef CONFIG_MIDI
-	int i = register_sound_midi(&oss_sound_fops);
+	int i = register_sound_midi(&oss_sound_fops, -1);
 	if(i==-1)
 		return i;
 	i>>=4;
@@ -566,7 +566,7 @@
 
 int sound_alloc_mixerdev(void)
 {
-	int i = register_sound_mixer(&oss_sound_fops);
+	int i = register_sound_mixer(&oss_sound_fops, -1);
 	if(i==-1)
 		return -1;
 	i>>=4;
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/dmasound.c linux/drivers/sound/dmasound.c
--- v2.2.0-pre4/linux/drivers/sound/dmasound.c	Thu Dec 31 10:29:01 1998
+++ linux/drivers/sound/dmasound.c	Mon Jan  4 11:37:30 1999
@@ -3853,7 +3853,7 @@
 #ifndef MODULE
 	int mixer_unit;
 #endif
-	mixer_unit = register_sound_mixer(&mixer_fops);
+	mixer_unit = register_sound_mixer(&mixer_fops, -1);
 	if (mixer_unit < 0)
 		return;
 
@@ -4258,7 +4258,7 @@
 #ifndef MODULE
 	int sq_unit;
 #endif
-	sq_unit = register_sound_dsp(&sq_fops);
+	sq_unit = register_sound_dsp(&sq_fops, -1);
 	if (sq_unit < 0)
 		return;
 
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/es1370.c linux/drivers/sound/es1370.c
--- v2.2.0-pre4/linux/drivers/sound/es1370.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/sound/es1370.c	Mon Jan  4 11:37:30 1999
@@ -2321,13 +2321,13 @@
 		       (s->ctrl & CTRL_XCTL0) ? "out" : "in",
 		       (s->ctrl & CTRL_XCTL1) ? "1" : "0");
 		/* register devices */
-		if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops)) < 0)
+		if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops, -1)) < 0)
 			goto err_dev1;
-		if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops)) < 0)
+		if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops, -1)) < 0)
 			goto err_dev2;
-		if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops)) < 0)
+		if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops, -1)) < 0)
 			goto err_dev3;
-		if ((s->dev_midi = register_sound_midi(&es1370_midi_fops)) < 0)
+		if ((s->dev_midi = register_sound_midi(&es1370_midi_fops, -1)) < 0)
 			goto err_dev4;
 		/* initialize the chips */
 		outl(s->ctrl, s->io+ES1370_REG_CONTROL);
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/es1371.c linux/drivers/sound/es1371.c
--- v2.2.0-pre4/linux/drivers/sound/es1371.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/sound/es1371.c	Mon Jan  4 11:37:30 1999
@@ -2745,13 +2745,13 @@
 		printk(KERN_INFO "es1371: found adapter at io %#06x 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)) < 0)
+		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)) < 0)
+		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)) < 0)
+		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)) < 0)
+		if ((s->dev_midi = register_sound_midi(&es1371_midi_fops, -1)) < 0)
 			goto err_dev4;
 		/* initialize codec registers */
 		s->ctrl = 0;
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/msnd_pinnacle.c linux/drivers/sound/msnd_pinnacle.c
--- v2.2.0-pre4/linux/drivers/sound/msnd_pinnacle.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/sound/msnd_pinnacle.c	Mon Jan  4 11:37:30 1999
@@ -1450,7 +1450,7 @@
 		return err;
 	}
 
-	if ((dev.dsp_minor = register_sound_dsp(&dev_fileops)) < 0) {
+	if ((dev.dsp_minor = register_sound_dsp(&dev_fileops, -1)) < 0) {
 		printk(KERN_ERR LOGNAME ": Unable to register DSP operations\n");
 		msnd_unregister(&dev);
 		release_region(dev.io, dev.numio);
@@ -1458,7 +1458,7 @@
 		return dev.dsp_minor;
 	}
 
-	if ((dev.mixer_minor = register_sound_mixer(&dev_fileops)) < 0) {
+	if ((dev.mixer_minor = register_sound_mixer(&dev_fileops, -1)) < 0) {
 		printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n");
 		unregister_sound_mixer(dev.mixer_minor);
 		msnd_unregister(&dev);
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/opl3sa.c linux/drivers/sound/opl3sa.c
--- v2.2.0-pre4/linux/drivers/sound/opl3sa.c	Sun Jul 26 11:57:16 1998
+++ linux/drivers/sound/opl3sa.c	Mon Jan  4 11:37:30 1999
@@ -260,6 +260,7 @@
 		      hw_config->dma,
 		      dma2,
 		      0);
+	sound_unload_audiodev(hw_config->slots[0]);
 }
 
 void unload_opl3sa_mpu(struct address_info *hw_config)
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/opl3sa2.c linux/drivers/sound/opl3sa2.c
--- v2.2.0-pre4/linux/drivers/sound/opl3sa2.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/sound/opl3sa2.c	Mon Jan  4 11:37:30 1999
@@ -26,6 +26,9 @@
  * Scott Murray            Original driver (Jun 14, 1998)
  * Paul J.Y. Lahaie        Changed probing / attach code order
  * Scott Murray            Added mixer support (Dec 03, 1998)
+ * Scott Murray            Changed detection code to be more forgiving,
+ *                         added force option as last resort,
+ *                         fixed ioctl return values. (Dec 30, 1998)
  *
  */
 
@@ -50,6 +53,11 @@
 #define DEFAULT_MIC    50
 #define DEFAULT_TIMBRE 0
 
+/*
+ * NOTE: CHIPSET_UNKNOWN should match the default value of
+ *       CONFIG_OPL3SA2_CHIPSET in Config.in to make everything
+ *       work right in all situations.
+ */
 #define CHIPSET_UNKNOWN -1
 #define CHIPSET_OPL3SA2  1
 #define CHIPSET_OPL3SA3  2
@@ -59,7 +67,12 @@
 #ifdef CONFIG_OPL3SA2
 
 /* What's my version? */
-static int chipset_version = CHIPSET_UNKNOWN;
+#ifdef CONFIG_OPL3SA2_CHIPSET
+/* Set chipset if compiled into the kernel */
+static int chipset = CONFIG_OPL3SA2_CHIPSET;
+#else
+static int chipset = CHIPSET_UNKNOWN;
+#endif
 
 /* Oh well, let's just cache the name */
 static char chipset_name[16];
@@ -276,43 +289,47 @@
 					return call_ad_mixer(devc, cmd, arg);
 				else
 				{
-					if(*(int *)arg != 0)
+					if(*(int*)arg != 0)
 						return -EINVAL;
 					return 0;
 				}
 
 			case SOUND_MIXER_VOLUME:
-				arg_to_volume_stereo(*(unsigned int *)arg,
+				arg_to_volume_stereo(*(unsigned int*)arg,
 						     &devc->volume_l,
 						     &devc->volume_r); 
 				opl3sa2_set_volume(devc, devc->volume_l,
 						   devc->volume_r);
-				return ret_vol_stereo(devc->volume_l,
-						      devc->volume_r);
+				*(int*)arg = ret_vol_stereo(devc->volume_l,
+							     devc->volume_r);
+				return 0;
 		  
 			case SOUND_MIXER_MIC:
-				arg_to_volume_mono(*(unsigned int *)arg,
+				arg_to_volume_mono(*(unsigned int*)arg,
 						   &devc->mic);
 				opl3sa2_set_mic(devc, devc->mic);
-				return ret_vol_mono(devc->mic);
+				*(int*)arg = ret_vol_mono(devc->mic);
+				return 0;
 		  
 			case SOUND_MIXER_BASS:
-				if(chipset_version != CHIPSET_OPL3SA2)
+				if(chipset != CHIPSET_OPL3SA2)
 				{
-					arg_to_volume_mono(*(unsigned int *)arg,
+					arg_to_volume_mono(*(unsigned int*)arg,
 							   &devc->bass);
 					opl3sa3_set_bass(devc, devc->bass);
-					return ret_vol_mono(devc->bass);
+					*(int*)arg = ret_vol_mono(devc->bass);
+					return 0;
 				}
 				return -EINVAL;
 		  
 			case SOUND_MIXER_TREBLE:
-				if(chipset_version != CHIPSET_OPL3SA2)
+				if(chipset != CHIPSET_OPL3SA2)
 				{
 					arg_to_volume_mono(*(unsigned int *)arg,
 							   &devc->treble);
 					opl3sa3_set_treble(devc, devc->treble);
-					return ret_vol_mono(devc->treble);
+					*(int*)arg = ret_vol_mono(devc->treble);
+					return 0;
 				}
 				return -EINVAL;
 		  
@@ -331,55 +348,83 @@
 				if(call_ad_mixer(devc, cmd, arg) == -EINVAL)
 					*(int*)arg = 0; /* no mixer devices */
 
-				if(chipset_version != CHIPSET_OPL3SA2)
-					return (*(int*)arg |= SOUND_MASK_VOLUME |
-						              SOUND_MASK_MIC |
-						              SOUND_MASK_BASS |
-						              SOUND_MASK_TREBLE);
+				*(int*)arg |= (SOUND_MASK_VOLUME | SOUND_MASK_MIC);
+
 				/* OPL3-SA2 has no bass and treble mixers */
-				return (*(int*)arg |= SOUND_MASK_VOLUME |
-					              SOUND_MASK_MIC);
+				if(chipset != CHIPSET_OPL3SA2)
+					*(int*)arg |= (SOUND_MASK_BASS |
+						       SOUND_MASK_TREBLE);
+				return 0;
 		  
 			case SOUND_MIXER_STEREODEVS:
 				if(call_ad_mixer(devc, cmd, arg) == -EINVAL)
 					*(int*)arg = 0; /* no stereo devices */
-				return (*(int*)arg |= SOUND_MASK_VOLUME);
+				*(int*)arg |= SOUND_MASK_VOLUME;
+				return 0;
 		  
 			case SOUND_MIXER_RECMASK:
 				if(devc->ad_mixer_dev != -1)
+				{
 					return call_ad_mixer(devc, cmd, arg);
+				}
 				else
-					return (*(int*)arg = 0); /* no record devices */
+				{
+					/* No recording devices */
+					return (*(int*)arg = 0);
+				}
 
 			case SOUND_MIXER_CAPS:
 				if(devc->ad_mixer_dev != -1)
+				{
 					return call_ad_mixer(devc, cmd, arg);
+				}
 				else
-					return (*(int*)arg = SOUND_CAP_EXCL_INPUT);
+				{
+					*(int*)arg = SOUND_CAP_EXCL_INPUT;
+					return 0;
+				}
 
 			case SOUND_MIXER_RECSRC:
 				if(devc->ad_mixer_dev != -1)
+				{
 					return call_ad_mixer(devc, cmd, arg);
+				}
 				else
-					return (*(int*)arg = 0); /* no record source */
+				{
+					/* No recording source */
+					return (*(int*)arg = 0);
+				}
 
 			case SOUND_MIXER_VOLUME:
-				return (*(int*)arg = ret_vol_stereo(devc->volume_l,
-								    devc->volume_r));
+				*(int*)arg = ret_vol_stereo(devc->volume_l,
+							    devc->volume_r);
+				return 0;
 			  
 			case SOUND_MIXER_MIC:
-				return (*(int*)arg = ret_vol_mono(devc->mic));
+				*(int*)arg = ret_vol_mono(devc->mic);
+				return 0;
 
 			case SOUND_MIXER_BASS:
-				if(chipset_version != CHIPSET_OPL3SA2)
-					return (*(int*)arg = ret_vol_mono(devc->bass));
-				return -EINVAL;
-
+				if(chipset != CHIPSET_OPL3SA2)
+				{
+					*(int*)arg = ret_vol_mono(devc->bass);
+					return 0;
+				}
+				else
+				{
+					return -EINVAL;
+				}
 			  
 			case SOUND_MIXER_TREBLE:
-				if(chipset_version != CHIPSET_OPL3SA2)
-					return (*(int*)arg = ret_vol_mono(devc->treble));
-				return -EINVAL;
+				if(chipset != CHIPSET_OPL3SA2)
+				{
+					*(int*)arg = ret_vol_mono(devc->treble);
+					return 0;
+				}
+				else
+				{
+					return -EINVAL;
+				}
 			  
 			default:
 				return -EINVAL;
@@ -481,6 +526,15 @@
 
 int probe_opl3sa2(struct address_info *hw_config)
 {
+	unsigned char chipsets[8] = { CHIPSET_UNKNOWN, /* 0 */
+				      CHIPSET_OPL3SA2, /* 1 */
+				      CHIPSET_OPL3SA3, /* 2 */
+				      CHIPSET_UNKNOWN, /* 3 */
+				      CHIPSET_OPL3SAX, /* 4 */
+				      CHIPSET_OPL3SAX, /* 5 */
+				      CHIPSET_UNKNOWN, /* 6 */
+				      CHIPSET_OPL3SA3, /* 7 */ };
+	unsigned char version = 0;
 	char tag;
 
 	/*
@@ -489,20 +543,80 @@
 	if(check_region(hw_config->io_base, 2))
 	{
 	    printk(KERN_ERR
-		   "opl3sa2.c: Control I/O port 0x%03x not free\n",
+		   "%s: Control I/O port 0x%03x not free\n",
+		   __FILE__,
 		   hw_config->io_base);
 	    return 0;
 	}
 
 	/*
-	 * Look at chipset version in lower 3 bits of index 0x0A, miscellaneous
+	 * Determine chipset type (SA2, SA3, or SAx)
+	 *
+	 * Have to handle two possible override situations:
+	 * 1) User compiled driver into the kernel and forced chipset type
+	 * 2) User built a module, but wants to override the chipset type
 	 */
-	chipset_version = 0;
-	opl3sa2_read(hw_config->io_base,
-		     OPL3SA2_MISC,
-		     (unsigned char*) &chipset_version);
-	chipset_version &= 0x0007;
-	switch(chipset_version)
+	if(chipset == CHIPSET_UNKNOWN)
+	{
+		if(hw_config->card_subtype == CHIPSET_UNKNOWN)
+		{
+			/*
+			 * Look at chipset version in lower 3 bits of index 0x0A, miscellaneous
+			 */
+			opl3sa2_read(hw_config->io_base,
+				     OPL3SA2_MISC,
+				     (unsigned char*) &version);
+			version &= 0x07;
+
+			/* Match version number to appropiate chipset */
+			chipset = chipsets[version];
+		}
+		else
+		{
+			/* Use user specified chipset */
+			switch(hw_config->card_subtype)
+			{
+				case 2:
+					chipset = CHIPSET_OPL3SA2;
+					break;
+
+				case 3:
+					chipset = CHIPSET_OPL3SA3;
+					break;
+
+				default:
+					printk(KERN_ERR "%s: Unknown chipset %d\n",
+					       __FILE__,
+					       hw_config->card_subtype);
+					chipset = CHIPSET_UNKNOWN;
+					break;
+			}
+		}
+	}
+	else
+	{
+		/* Use user compiled in chipset */
+		switch(chipset)
+		{
+			case 2:
+				chipset = CHIPSET_OPL3SA2;
+				break;
+				
+			case 3:
+				chipset = CHIPSET_OPL3SA3;
+				break;
+
+			default:
+				printk(KERN_ERR "%s: Unknown chipset %d\n",
+				       __FILE__,
+				       chipset);
+				chipset = CHIPSET_UNKNOWN;
+				break;
+		}
+	}
+
+	/* Do chipset specific stuff: */
+	switch(chipset)
 	{
 		case CHIPSET_OPL3SA2:
 			printk(KERN_INFO "Found OPL3-SA2 (YMF711)\n");
@@ -521,15 +635,23 @@
 
 		default:
 			printk(KERN_ERR "No Yamaha audio controller found\n");
-			printk(KERN_INFO
-			       "opl3sa2.c: chipset version = %x\n",
-			       chipset_version);
-			chipset_version = CHIPSET_UNKNOWN;
+
+			/* If we've actually checked the version, print it out */
+			if(version)
+			{
+				printk(KERN_INFO
+				       "%s: chipset version = %x\n",
+				       __FILE__,
+				       version);
+			}
+			
+			/* Set some sane values */
+			chipset = CHIPSET_UNKNOWN;
 			tag = '?';
 			break;
 	}
 
-	if(chipset_version != CHIPSET_UNKNOWN) {
+	if(chipset != CHIPSET_UNKNOWN) {
 		/* Generate a pretty name */
 		sprintf(chipset_name, "OPL3-SA%c", tag);
 		return 1;
@@ -565,6 +687,7 @@
 int irq     = -1;
 int dma     = -1;
 int dma2    = -1;
+int force   = -1;
 
 MODULE_PARM(io, "i");
 MODULE_PARM_DESC(io, "Set i/o base of OPL3-SA2 or SA3 card (usually 0x370)");
@@ -581,9 +704,12 @@
 MODULE_PARM(dma, "i");
 MODULE_PARM_DESC(dma, "Set MSS (audio) first DMA channel (0, 1, 3)");
 
-MODULE_PARM(dma2,"i");
+MODULE_PARM(dma2, "i");
 MODULE_PARM_DESC(dma2, "Set MSS (audio) second DMA channel (0, 1, 3)");
 
+MODULE_PARM(force, "i");
+MODULE_PARM_DESC(force, "Force audio controller chipset (2, 3)");
+
 MODULE_DESCRIPTION("Module for OPL3-SA2 and SA3 sound cards (uses AD1848 MSS driver).");
 MODULE_AUTHOR("Scott Murray <scottm@interlog.com>");
 
@@ -605,7 +731,8 @@
 
 	if(io == -1 || irq == -1 || dma == -1 || dma2 == -1 || mss_io == -1)
 	{
-		printk(KERN_ERR "opl3sa2.c: io, mss_io, irq, dma, and dma2 must be set.\n");
+		printk(KERN_ERR "%s: io, mss_io, irq, dma, and dma2 must be set.\n",
+		       __FILE__);
 		return -EINVAL;
 	}
    
@@ -614,6 +741,12 @@
 	cfg.irq     = irq;
 	cfg.dma     = dma;
 	cfg.dma2    = dma2;
+	
+	/* Does the user want to override the chipset type? */
+	if(force != -1)
+		cfg.card_subtype = force;
+	else
+		cfg.card_subtype = CHIPSET_UNKNOWN;
 
         /* The MSS config: */
 	mss_cfg.io_base      = mss_io;
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/pas2_card.c linux/drivers/sound/pas2_card.c
--- v2.2.0-pre4/linux/drivers/sound/pas2_card.c	Thu Jul 16 18:09:27 1998
+++ linux/drivers/sound/pas2_card.c	Mon Jan  4 11:37:30 1999
@@ -73,12 +73,12 @@
 
 unsigned char pas_read(int ioaddr)
 {
-	return inb(ioaddr ^ translate_code);
+	return inb(ioaddr + translate_code);
 }
 
 void pas_write(unsigned char data, int ioaddr)
 {
-	outb((data), ioaddr ^ translate_code);
+	outb((data), ioaddr + translate_code);
 }
 
 /******************* Begin of the Interrupt Handler ********************/
@@ -168,7 +168,7 @@
 	else
 	{
 		int_ptrs = pas_read(0xF38A);
-		int_ptrs |= irq_bits[pas_irq] & 0xf;
+		int_ptrs = (int_ptrs & 0xf0) | irq_bits[pas_irq];
 		pas_write(int_ptrs, 0xF38A);
 		if (!irq_bits[pas_irq])
 		{
@@ -297,7 +297,7 @@
 
 	outb((0xBC), 0x9A01);	/* Activate first board */
 	outb((hw_config->io_base >> 2), 0x9A01);	/* Set base address */
-	translate_code = 0x388 ^ hw_config->io_base;
+	translate_code = hw_config->io_base - 0x388;
 	pas_write(1, 0xBF88);	/* Select one wait states */
 
 	board_id = pas_read(0x0B8B);
@@ -347,7 +347,7 @@
 			pas_pcm_init(hw_config);
 #endif
 
-#if !defined(DISABLE_SB_EMULATION) && defined(CONFIG_SB)
+#if !defined(MODULE) && !defined(DISABLE_SB_EMULATION) && defined(CONFIG_SB)
 
 			sb_dsp_disable_midi(pas_sb_base);	/* No MIDI capability */
 #endif
@@ -367,8 +367,18 @@
 
 void unload_pas(struct address_info *hw_config)
 {
+	extern int pas_audiodev;
+	extern int pas2_mididev;
+
 	sound_free_dma(hw_config->dma);
 	free_irq(hw_config->irq, NULL);
+
+	if(pas_audiodev!=-1)
+		sound_unload_mixerdev(audio_devs[pas_audiodev]->mixer_dev);
+	if(pas2_mididev!=-1)
+	        sound_unload_mididev(pas2_mididev);
+	if(pas_audiodev)
+		sound_unload_audiodev(pas_audiodev);
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/pas2_midi.c linux/drivers/sound/pas2_midi.c
--- v2.2.0-pre4/linux/drivers/sound/pas2_midi.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/pas2_midi.c	Mon Jan  4 11:37:30 1999
@@ -21,14 +21,15 @@
 static int      midi_busy = 0, input_opened = 0;
 static int      my_dev;
 
+int pas2_mididev;
+
 static unsigned char tmp_queue[256];
 static volatile int qlen;
 static volatile unsigned char qhead, qtail;
 
 static void     (*midi_input_intr) (int dev, unsigned char data);
 
-static int
-pas_midi_open(int dev, int mode,
+static int pas_midi_open(int dev, int mode,
 	      void            (*input) (int dev, unsigned char data),
 	      void            (*output) (int dev)
 )
@@ -39,10 +40,8 @@
 
 
 	if (midi_busy)
-	  {
-		  printk("PAS16: Midi busy\n");
-		  return -EBUSY;
-	  }
+		return -EBUSY;
+
 	/*
 	 * Reset input and output FIFO pointers
 	 */
@@ -53,10 +52,10 @@
 	cli();
 
 	if ((err = pas_set_intr(0x10)) < 0)
-	  {
-		  restore_flags(flags);
-		  return err;
-	  }
+	{
+		restore_flags(flags);
+		return err;
+	}
 	/*
 	 * Enable input available and output FIFO empty interrupts
 	 */
@@ -66,14 +65,14 @@
 	midi_input_intr = input;
 
 	if (mode == OPEN_READ || mode == OPEN_READWRITE)
-	  {
-		  ctrl |= 0x04;	/* Enable input */
-		  input_opened = 1;
-	  }
+	{
+		ctrl |= 0x04;	/* Enable input */
+		input_opened = 1;
+	}
 	if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
-	  {
-		  ctrl |= 0x08 | 0x10;	/* Enable output */
-	  }
+	{
+		ctrl |= 0x08 | 0x10;	/* Enable output */
+	}
 	pas_write(ctrl, 0x178b);
 
 	/*
@@ -89,8 +88,7 @@
 	return 0;
 }
 
-static void
-pas_midi_close(int dev)
+static void pas_midi_close(int dev)
 {
 
 	/*
@@ -102,35 +100,32 @@
 	midi_busy = 0;
 }
 
-static int
-dump_to_midi(unsigned char midi_byte)
+static int dump_to_midi(unsigned char midi_byte)
 {
-	int             fifo_space, x;
+	int fifo_space, x;
 
 	fifo_space = ((x = pas_read(0x1B89)) >> 4) & 0x0f;
 
-/*
- * The MIDI FIFO space register and it's documentation is nonunderstandable.
- * There seem to be no way to differentiate between buffer full and buffer
- * empty situations. For this reason we don't never write the buffer
- * completely full. In this way we can assume that 0 (or is it 15)
- * means that the buffer is empty.
- */
+	/*
+	 * The MIDI FIFO space register and it's documentation is nonunderstandable.
+	 * There seem to be no way to differentiate between buffer full and buffer
+	 * empty situations. For this reason we don't never write the buffer
+	 * completely full. In this way we can assume that 0 (or is it 15)
+	 * means that the buffer is empty.
+	 */
 
 	if (fifo_space < 2 && fifo_space != 0)	/* Full (almost) */
-	  {
-		  return 0;	/* Ask upper layers to retry after some time */
-	  }
+		return 0;	/* Ask upper layers to retry after some time */
+
 	pas_write(midi_byte, 0x178A);
 
 	return 1;
 }
 
-static int
-pas_midi_out(int dev, unsigned char midi_byte)
+static int pas_midi_out(int dev, unsigned char midi_byte)
 {
 
-	unsigned long   flags;
+	unsigned long flags;
 
 	/*
 	 * Drain the local queue first
@@ -140,15 +135,15 @@
 	cli();
 
 	while (qlen && dump_to_midi(tmp_queue[qhead]))
-	  {
-		  qlen--;
-		  qhead++;
-	  }
+	{
+		qlen--;
+		qhead++;
+	}
 
 	restore_flags(flags);
 
 	/*
-	 * Output the byte if the local queue is empty.
+	 *	Output the byte if the local queue is empty.
 	 */
 
 	if (!qlen)
@@ -156,7 +151,7 @@
 			return 1;
 
 	/*
-	 * Put to the local queue
+	 *	Put to the local queue
 	 */
 
 	if (qlen >= 256)
@@ -174,25 +169,21 @@
 	return 1;
 }
 
-static int
-pas_midi_start_read(int dev)
+static int pas_midi_start_read(int dev)
 {
 	return 0;
 }
 
-static int
-pas_midi_end_read(int dev)
+static int pas_midi_end_read(int dev)
 {
 	return 0;
 }
 
-static void
-pas_midi_kick(int dev)
+static void pas_midi_kick(int dev)
 {
 }
 
-static int
-pas_buffer_status(int dev)
+static int pas_buffer_status(int dev)
 {
 	return qlen;
 }
@@ -218,23 +209,22 @@
 	NULL
 };
 
-void
-pas_midi_init(void)
+void pas_midi_init(void)
 {
-	int             dev = sound_alloc_mididev();
+	int dev = sound_alloc_mididev();
 
 	if (dev == -1)
-	  {
-		  printk(KERN_WARNING "pas_midi_init: Too many midi devices detected\n");
-		  return;
-	  }
+	{
+		printk(KERN_WARNING "pas_midi_init: Too many midi devices detected\n");
+		return;
+	}
 	std_midi_synth.midi_dev = my_dev = dev;
 	midi_devs[dev] = &pas_midi_operations;
+	pas2_mididev = dev;
 	sequencer_init();
 }
 
-void
-pas_midi_interrupt(void)
+void pas_midi_interrupt(void)
 {
 	unsigned char   stat;
 	int             i, incount;
@@ -243,35 +233,35 @@
 	stat = pas_read(0x1B88);
 
 	if (stat & 0x04)	/* Input data available */
-	  {
-		  incount = pas_read(0x1B89) & 0x0f;	/* Input FIFO size */
-		  if (!incount)
-			  incount = 16;
-
-		  for (i = 0; i < incount; i++)
-			  if (input_opened)
-			    {
-				    midi_input_intr(my_dev, pas_read(0x178A));
-			  } else
-				  pas_read(0x178A);	/* Flush */
-	  }
+	{
+		incount = pas_read(0x1B89) & 0x0f;	/* Input FIFO size */
+		if (!incount)
+			incount = 16;
+
+		for (i = 0; i < incount; i++)
+			if (input_opened)
+			{
+				midi_input_intr(my_dev, pas_read(0x178A));
+			} else
+				pas_read(0x178A);	/* Flush */
+	}
 	if (stat & (0x08 | 0x10))
-	  {
-		  save_flags(flags);
-		  cli();
-
-		  while (qlen && dump_to_midi(tmp_queue[qhead]))
-		    {
-			    qlen--;
-			    qhead++;
-		    }
+	{
+		save_flags(flags);
+		cli();
+
+		while (qlen && dump_to_midi(tmp_queue[qhead]))
+		{
+			qlen--;
+			qhead++;
+		}
 
-		  restore_flags(flags);
-	  }
+		restore_flags(flags);
+	}
 	if (stat & 0x40)
-	  {
-		  printk("MIDI output overrun %x,%x\n", pas_read(0x1B89), stat);
-	  }
+	{
+		printk(KERN_WARNING "MIDI output overrun %x,%x\n", pas_read(0x1B89), stat);
+	}
 	pas_write(stat, 0x1B88);	/* Acknowledge interrupts */
 }
 
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/sb.h linux/drivers/sound/sb.h
--- v2.2.0-pre4/linux/drivers/sound/sb.h	Mon Dec 28 15:00:52 1998
+++ linux/drivers/sound/sb.h	Mon Jan  4 11:37:30 1999
@@ -49,6 +49,9 @@
 #define MDL_AEDSP	15	/* Audio Excel DSP 16 */
 
 #define SUBMDL_ES188X	0x10	/* Subtype ES188X for specific handling */
+#define SUBMDL_ES1868	0x11	/* Subtype ES1868 for specific handling */
+#define SUBMDL_ES1869	0x12	/* Subtype ES1869 for specific handling */
+#define SUBMDL_ES1878	0x13	/* Subtype ES1878 for specific handling */
 #define SUBMDL_ALS007	42	/* ALS-007 differs from SB16 only in mixer */
 				/* register assignment */
 /*
@@ -144,6 +147,7 @@
 void sb_midi_interrupt (sb_devc *devc);
 int ess_write (sb_devc *devc, unsigned char reg, unsigned char data);
 int ess_read (sb_devc *devc, unsigned char reg);
+void ess_mixer_reload (sb_devc * devc, int dev);
 
 extern int acer;
 extern sb_devc *last_sb;
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/sb_audio.c linux/drivers/sound/sb_audio.c
--- v2.2.0-pre4/linux/drivers/sound/sb_audio.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/sound/sb_audio.c	Mon Jan  4 11:37:30 1999
@@ -67,6 +67,7 @@
 	devc->fullduplex = devc->duplex &&
 		((mode & OPEN_READ) && (mode & OPEN_WRITE));
 	sb_dsp_reset(devc);
+	ess_mixer_reload (devc, SOUND_MIXER_RECLEV);
 
 	/* The ALS007 seems to require that the DSP be removed from the output */
 	/* in order for recording to be activated properly.  This is done by   */
@@ -91,8 +92,10 @@
 {
 	sb_devc *devc = audio_devs[dev]->devc;
 
-	/* if we did dma juggling put the right dmap in the right place */
-	if(devc->duplex && audio_devs[dev]->dmap_out->dma != devc->dma8)
+	/* fix things if mmap turned off fullduplex */
+	if(devc->duplex
+	   && !devc->fullduplex
+	   && (devc->opened & OPEN_READ) && (devc->opened & OPEN_WRITE))
 	{
 		struct dma_buffparms *dmap_temp;
 		dmap_temp = audio_devs[dev]->dmap_out;
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.2.0-pre4/linux/drivers/sound/sb_common.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/sound/sb_common.c	Mon Jan  4 11:37:30 1999
@@ -103,6 +103,13 @@
 	return 0xffff;
 }
 
+inline void ess_extended (sb_devc * devc)
+{
+	/* Enable extended mode */
+
+	sb_dsp_command(devc, 0xc6);
+}
+
 int ess_write(sb_devc * devc, unsigned char reg, unsigned char data)
 {
 	/* Write a byte to an extended mode register of ES1688 */
@@ -225,8 +232,7 @@
 		DDB(printk("sb: No response to RESET\n"));
 		return 0;	/* Sorry */
 	}
-	if (devc->model == MDL_ESS)
-		sb_dsp_command(devc, 0xc6);	/* Enable extended mode */
+	if (devc->model == MDL_ESS) ess_extended (devc);
 
 	DEB(printk("sb_dsp_reset() OK\n"));
 	return 1;
@@ -491,6 +497,8 @@
  * fiddling with the bits in certain mixer registers. ess_probe is supposed
  * to help.
  */
+static unsigned int ess_identify (sb_devc * devc);
+
 static int ess_probe (sb_devc * devc, int reg, int xorval)
 {
 	int  val1, val2, val3;
@@ -554,18 +562,40 @@
 
 	if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80)
 	{
-		char *chip = "ES688";
+		char *chip    = NULL;
+
+		if ((ess_minor & 0x0f) < 8) {
+			chip = "ES688";
+		};
+		if (chip == NULL) {
+			int type, dummy;
+
+			type = ess_identify (devc);
 
-		if ((ess_minor & 0x0f) >= 8) {
+			switch (type) {
+			case 0x1868:
+				chip = "ES1868";
+				devc->submodel = SUBMDL_ES1868;
+				break;
+			case 0x1869:
+				chip = "ES1869";
+				devc->submodel = SUBMDL_ES1869;
+				break;
+			case 0x1878:
+				chip = "ES1878";
+				devc->submodel = SUBMDL_ES1878;
+				break;
+			};
+		};
+		if (chip == NULL) {
 			if (  !ess_probe (devc, 0x64, (1 << 3))
 			    && ess_probe (devc, 0x70, 0x7f)) {
 				chip = "ES188x";
 				devc->submodel = SUBMDL_ES188X;
-			} else {
-				chip = "ES1688";
 			};
-		} else {
-			chip = "ES688";
+		};
+		if (chip == NULL) {
+			chip = "ES1688";
 		};
 
 		sprintf(name,"ESS %s AudioDrive (rev %d)",
@@ -750,7 +780,7 @@
 		return 0;
 	}
 	memcpy((char *) detected_devc, (char *) devc, sizeof(sb_devc));
-	MDB(printk("SB %d.%d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base));
+	MDB(printk(KERN_INFO "SB %d.%d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base));
 	return 1;
 }
 
@@ -1048,7 +1078,8 @@
 
 	save_flags(flags);
 	cli();
-	if (devc->model == MDL_ESS && port >= 0xa0 && port <= 0xbf) {
+	if (devc->model == MDL_ESS && port >= 0xa0) {
+		/* ess_extended (devc); */
 		ess_write (devc, port, value);
 	} else {
 		outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
@@ -1071,6 +1102,25 @@
 
 	udelay(20);
 	val = inb(MIXER_DATA);
+	udelay(20);
+	restore_flags(flags);
+
+	return val;
+}
+
+static unsigned int ess_identify (sb_devc * devc)
+{
+	unsigned int val;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outb(((unsigned char) (0x40 & 0xff)), MIXER_ADDR);
+
+	udelay(20);
+	val  = inb(MIXER_DATA) << 8;
+	udelay(20);
+	val |= inb(MIXER_DATA);
 	udelay(20);
 	restore_flags(flags);
 
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/sb_mixer.c linux/drivers/sound/sb_mixer.c
--- v2.2.0-pre4/linux/drivers/sound/sb_mixer.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/sound/sb_mixer.c	Mon Jan  4 11:37:30 1999
@@ -12,8 +12,16 @@
  * for more info.
  *
  *
- * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
- * Rolf Fokkens    : ES188x recording level support
+ * Thomas Sailer		: ioctl code reworked (vmalloc/vfree removed)
+ * Rolf Fokkens (Dec 20 1998)	: ES188x recording level support on a per
+ *				  input basis
+ *              (Dec 24 1998)	: Recognition of ES188x (?), ES1868, ES1869
+ *				  and ES1878. Could be used for specific
+ *				  handling in the future. All except ES188x
+ *				  and ES688 are handled like ES1688
+ *              (Dec 27 1998)	: RECLEV for all (?) ES1688+ chips, see 
+ *				  ess_mixer_reload for more info. ES188x now
+ *				  has the "Dec 20" support + RECLEV
  */
 
 /*
@@ -42,14 +50,360 @@
 #include "sb.h"
 #include "sb_mixer.h"
 
+static mixer_tab sbpro_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,	0x22, 7, 4, 0x22, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,	0x26, 7, 4, 0x26, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,	0x04, 7, 4, 0x04, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	0x2e, 7, 4, 0x2e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,	0x0a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD,		0x28, 7, 4, 0x28, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0)
+};
+
+static mixer_tab es688_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,	0x32, 7, 4, 0x32, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,	0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,	0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,	0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,	0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,		0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_IGAIN,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,	0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,	0x00, 0, 0, 0x00, 0, 0)
+};
+
+/*
+ * The ES1688 specifics... hopefully correct...
+ * - 6 bit master volume
+ * - RECLEV control
+ * These may apply to ES688 too. I have no idea.
+ */
+static mixer_tab es1688_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,	0x60, 5, 6, 0x62, 5, 6),
+MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,	0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,	0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,	0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,	0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,		0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	0xb4, 7, 4, 0xb4, 3, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,	0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,	0x00, 0, 0, 0x00, 0, 0)
+};
+
+/*
+ * The ES188x specifics.
+ * Note that de master volume unlike ES688 is now controlled by two 6 bit
+ * registers. These seem to work OK on 1868 too, but I have no idea if it's
+ * compatible to 688 or 1688....
+ * Also Note that the recording levels (ES188X_MIXER_REC...) have own 
+ * entries as if they were playback devices. They are used internally in the
+ * driver only!
+ */
+static mixer_tab es188x_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,	0x60, 5, 6, 0x62, 5, 6),
+MIX_ENT(SOUND_MIXER_BASS,       0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,     0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,      0x36, 7, 4, 0x36, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,        0x14, 7, 4, 0x14, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,    0x3c, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,       0x3e, 7, 4, 0x3e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,        0x1a, 7, 4, 0x1a, 3, 4),
+MIX_ENT(SOUND_MIXER_CD,         0x38, 7, 4, 0x38, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,       0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,     0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	0xb4, 7, 4, 0xb4, 3, 4),
+MIX_ENT(SOUND_MIXER_IGAIN,      0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,      0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1,      0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE2,      0x3a, 7, 4, 0x3a, 3, 4),
+MIX_ENT(SOUND_MIXER_LINE3,      0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES188X_MIXER_RECSYNTH,	0x6b, 7, 4, 0x6b, 3, 4),
+MIX_ENT(ES188X_MIXER_RECPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES188X_MIXER_RECSPEAKER,0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES188X_MIXER_RECLINE,	0x6e, 7, 4, 0x6e, 3, 4),
+MIX_ENT(ES188X_MIXER_RECMIC,	0x68, 7, 4, 0x68, 3, 4),
+MIX_ENT(ES188X_MIXER_RECCD,	0x6a, 7, 4, 0x6a, 3, 4),
+MIX_ENT(ES188X_MIXER_RECIMIX,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES188X_MIXER_RECALTPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES188X_MIXER_RECRECLEV,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES188X_MIXER_RECIGAIN,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES188X_MIXER_RECOGAIN,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES188X_MIXER_RECLINE1,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(ES188X_MIXER_RECLINE2,	0x6c, 7, 4, 0x6c, 3, 4),
+MIX_ENT(ES188X_MIXER_RECLINE3,	0x00, 0, 0, 0x00, 0, 0)
+};
+
+#ifdef	__SGNXPRO__
+#if 0
+static mixer_tab sgnxpro_mix = { 	/* not used anywhere */
+MIX_ENT(SOUND_MIXER_VOLUME,	0x22, 7, 4, 0x22, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,	0x46, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,	0x44, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,	0x26, 7, 4, 0x26, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,	0x04, 7, 4, 0x04, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,	0x42, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	0x2e, 7, 4, 0x2e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,	0x0a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD,		0x28, 7, 4, 0x28, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_IGAIN,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,	0x00, 0, 0, 0x00, 0, 0)
+};
+#endif
+#endif
+
+static mixer_tab sb16_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME,	0x30, 7, 5, 0x31, 7, 5),
+MIX_ENT(SOUND_MIXER_BASS,	0x46, 7, 4, 0x47, 7, 4),
+MIX_ENT(SOUND_MIXER_TREBLE,	0x44, 7, 4, 0x45, 7, 4),
+MIX_ENT(SOUND_MIXER_SYNTH,	0x34, 7, 5, 0x35, 7, 5),
+MIX_ENT(SOUND_MIXER_PCM,	0x32, 7, 5, 0x33, 7, 5),
+MIX_ENT(SOUND_MIXER_SPEAKER,	0x3b, 7, 2, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	0x38, 7, 5, 0x39, 7, 5),
+MIX_ENT(SOUND_MIXER_MIC,	0x3a, 7, 5, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD,		0x36, 7, 5, 0x37, 7, 5),
+MIX_ENT(SOUND_MIXER_IMIX,	0x3c, 0, 1, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	0x3f, 7, 2, 0x40, 7, 2), /* Obsolete. Use IGAIN */
+MIX_ENT(SOUND_MIXER_IGAIN,	0x3f, 7, 2, 0x40, 7, 2),
+MIX_ENT(SOUND_MIXER_OGAIN,	0x41, 7, 2, 0x42, 7, 2)
+};
+
+static mixer_tab als007_mix = 
+{
+MIX_ENT(SOUND_MIXER_VOLUME,	0x62, 7, 4, 0x62, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH,	0x66, 7, 4, 0x66, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM,	0x64, 7, 4, 0x64, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE,	0x6e, 7, 4, 0x6e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC,	0x6a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD,		0x68, 7, 4, 0x68, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0), /* Obsolete. Use IGAIN */
+MIX_ENT(SOUND_MIXER_IGAIN,	0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN,	0x00, 0, 0, 0x00, 0, 0)
+};
+
+
+/* SM_GAMES          Master volume is lower and PCM & FM volumes
+			     higher than with SB Pro. This improves the
+			     sound quality */
+
+static int smg_default_levels[32] =
+{
+  0x2020,			/* Master Volume */
+  0x4b4b,			/* Bass */
+  0x4b4b,			/* Treble */
+  0x6464,			/* FM */
+  0x6464,			/* PCM */
+  0x4b4b,			/* PC Speaker */
+  0x4b4b,			/* Ext Line */
+  0x0000,			/* Mic */
+  0x4b4b,			/* CD */
+  0x4b4b,			/* Recording monitor */
+  0x4b4b,			/* SB PCM */
+  0x4b4b,			/* Recording level */
+  0x4b4b,			/* Input gain */
+  0x4b4b,			/* Output gain */
+  0x4040,			/* Line1 */
+  0x4040,			/* Line2 */
+  0x1515			/* Line3 */
+};
+
+static int sb_default_levels[32] =
+{
+  0x5a5a,			/* Master Volume */
+  0x4b4b,			/* Bass */
+  0x4b4b,			/* Treble */
+  0x4b4b,			/* FM */
+  0x4b4b,			/* PCM */
+  0x4b4b,			/* PC Speaker */
+  0x4b4b,			/* Ext Line */
+  0x1010,			/* Mic */
+  0x4b4b,			/* CD */
+  0x0000,			/* Recording monitor */
+  0x4b4b,			/* SB PCM */
+  0x4b4b,			/* Recording level */
+  0x4b4b,			/* Input gain */
+  0x4b4b,			/* Output gain */
+  0x4040,			/* Line1 */
+  0x4040,			/* Line2 */
+  0x1515			/* Line3 */
+};
+
+static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
+{
+	0x00,	/* SOUND_MIXER_VOLUME	*/
+	0x00,	/* SOUND_MIXER_BASS	*/
+	0x00,	/* SOUND_MIXER_TREBLE	*/
+	0x40,	/* SOUND_MIXER_SYNTH	*/
+	0x00,	/* SOUND_MIXER_PCM	*/
+	0x00,	/* SOUND_MIXER_SPEAKER	*/
+	0x10,	/* SOUND_MIXER_LINE	*/
+	0x01,	/* SOUND_MIXER_MIC	*/
+	0x04,	/* SOUND_MIXER_CD	*/
+	0x00,	/* SOUND_MIXER_IMIX	*/
+	0x00,	/* SOUND_MIXER_ALTPCM	*/
+	0x00,	/* SOUND_MIXER_RECLEV	*/
+	0x00,	/* SOUND_MIXER_IGAIN	*/
+	0x00	/* SOUND_MIXER_OGAIN	*/
+};
+
+static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
+{
+	0x00,	/* SOUND_MIXER_VOLUME	*/
+	0x00,	/* SOUND_MIXER_BASS	*/
+	0x00,	/* SOUND_MIXER_TREBLE	*/
+	0x20,	/* SOUND_MIXER_SYNTH	*/
+	0x00,	/* SOUND_MIXER_PCM	*/
+	0x00,	/* SOUND_MIXER_SPEAKER	*/
+	0x08,	/* SOUND_MIXER_LINE	*/
+	0x01,	/* SOUND_MIXER_MIC	*/
+	0x02,	/* SOUND_MIXER_CD	*/
+	0x00,	/* SOUND_MIXER_IMIX	*/
+	0x00,	/* SOUND_MIXER_ALTPCM	*/
+	0x00,	/* SOUND_MIXER_RECLEV	*/
+	0x00,	/* SOUND_MIXER_IGAIN	*/
+	0x00	/* SOUND_MIXER_OGAIN	*/
+};
+
+static char     smw_mix_regs[] =	/* Left mixer registers */
+{
+  0x0b,				/* SOUND_MIXER_VOLUME */
+  0x0d,				/* SOUND_MIXER_BASS */
+  0x0d,				/* SOUND_MIXER_TREBLE */
+  0x05,				/* SOUND_MIXER_SYNTH */
+  0x09,				/* SOUND_MIXER_PCM */
+  0x00,				/* SOUND_MIXER_SPEAKER */
+  0x03,				/* SOUND_MIXER_LINE */
+  0x01,				/* SOUND_MIXER_MIC */
+  0x07,				/* SOUND_MIXER_CD */
+  0x00,				/* SOUND_MIXER_IMIX */
+  0x00,				/* SOUND_MIXER_ALTPCM */
+  0x00,				/* SOUND_MIXER_RECLEV */
+  0x00,				/* SOUND_MIXER_IGAIN */
+  0x00,				/* SOUND_MIXER_OGAIN */
+  0x00,				/* SOUND_MIXER_LINE1 */
+  0x00,				/* SOUND_MIXER_LINE2 */
+  0x00				/* SOUND_MIXER_LINE3 */
+};
+
+#define SBPRO_RECORDING_DEVICES	(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
+
+/* Same as SB Pro, unless I find otherwise */
+#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
+
+#define SBPRO_MIXER_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD | SOUND_MASK_VOLUME)
+
+/* SG NX Pro has treble and bass settings on the mixer. The 'speaker'
+ * channel is the COVOX/DisneySoundSource emulation volume control
+ * on the mixer. It does NOT control speaker volume. Should have own
+ * mask eventually?
+ */
+#define SGNXPRO_MIXER_DEVICES	(SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \
+				 SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER )
+
+#define SB16_RECORDING_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD)
+
+#define SB16_OUTFILTER_DEVICES		(SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD)
+
+#define ES688_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
+#define ES688_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_LINE2|SOUND_MASK_SPEAKER)
+
+#define ES1688_RECORDING_DEVICES ES688_RECORDING_DEVICES
+#define ES1688_MIXER_DEVICES (ES688_MIXER_DEVICES|SOUND_MASK_RECLEV)
+
+#define ES188X_RECORDING_DEVICES	(ES1688_RECORDING_DEVICES | SOUND_MASK_LINE2 \
+					 |SOUND_MASK_SYNTH)
+#define ES188X_MIXER_DEVICES (ES1688_MIXER_DEVICES)
+
+#define SB16_MIXER_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+					 SOUND_MASK_CD | \
+					 SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
+					 SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \
+					SOUND_MASK_IMIX)
+
+/* These are the only devices that are working at the moment.  Others could
+ * be added once they are identified and a method is found to control them.
+ */
+#define ALS007_MIXER_DEVICES	(SOUND_MASK_SYNTH | SOUND_MASK_LINE | \
+				 SOUND_MASK_PCM | SOUND_MASK_MIC | \
+				 SOUND_MASK_CD | \
+				 SOUND_MASK_VOLUME)
+
+/*
+ * Mixer registers of ES188x
+ *
+ * These registers specifically take care of recording levels. To make the
+ * mapping from playback devices to recording devices every recording
+ * devices = playback device + ES188X_MIXER_RECDIFF
+ */
+#define ES188X_MIXER_RECBASE	(SOUND_MIXER_LINE3 + 1)
+#define ES188X_MIXER_RECDIFF	(ES188X_MIXER_RECBASE - SOUND_MIXER_SYNTH)
+
+#define ES188X_MIXER_RECSYNTH	(SOUND_MIXER_SYNTH	+ ES188X_MIXER_RECDIFF)
+#define ES188X_MIXER_RECPCM	(SOUND_MIXER_PCM	+ ES188X_MIXER_RECDIFF)
+#define ES188X_MIXER_RECSPEAKER	(SOUND_MIXER_SPEAKER	+ ES188X_MIXER_RECDIFF)
+#define ES188X_MIXER_RECLINE	(SOUND_MIXER_LINE	+ ES188X_MIXER_RECDIFF)
+#define ES188X_MIXER_RECMIC	(SOUND_MIXER_MIC	+ ES188X_MIXER_RECDIFF)
+#define ES188X_MIXER_RECCD	(SOUND_MIXER_CD		+ ES188X_MIXER_RECDIFF)
+#define ES188X_MIXER_RECIMIX	(SOUND_MIXER_IMIX	+ ES188X_MIXER_RECDIFF)
+#define ES188X_MIXER_RECALTPCM	(SOUND_MIXER_ALTPCM	+ ES188X_MIXER_RECDIFF)
+#define ES188X_MIXER_RECRECLEV	(SOUND_MIXER_RECLEV	+ ES188X_MIXER_RECDIFF)
+#define ES188X_MIXER_RECIGAIN	(SOUND_MIXER_IGAIN	+ ES188X_MIXER_RECDIFF)
+#define ES188X_MIXER_RECOGAIN	(SOUND_MIXER_OGAIN	+ ES188X_MIXER_RECDIFF)
+#define ES188X_MIXER_RECLINE1	(SOUND_MIXER_LINE1	+ ES188X_MIXER_RECDIFF)
+#define ES188X_MIXER_RECLINE2	(SOUND_MIXER_LINE2	+ ES188X_MIXER_RECDIFF)
+#define ES188X_MIXER_RECLINE3	(SOUND_MIXER_LINE3	+ ES188X_MIXER_RECDIFF)
+
+
 static int      sbmixnum = 1;
 
 static void     sb_mixer_reset(sb_devc * devc);
 
+inline void sb_mixer_bits
+	(sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val)
+{
+	int value;
+
+	value = sb_getmixer(devc, reg);
+	value = (value & ~mask) | (val & mask);
+	sb_setmixer(devc, reg, value);
+}
+
+
 void sb_mixer_set_stereo(sb_devc * devc, int mode)
 {
-	sb_setmixer(devc, OUT_FILTER, ((sb_getmixer(devc, OUT_FILTER) & ~STEREO_DAC)
-				       | (mode ? STEREO_DAC : MONO_DAC)));
+	sb_mixer_bits(devc, OUT_FILTER, STEREO_DAC, (mode ? STEREO_DAC : MONO_DAC));
 }
 
 static int detect_mixer(sb_devc * devc)
@@ -109,8 +463,6 @@
 	val = sb_getmixer(devc, regoffs);
 	change_bits(devc, &val, dev, LEFT_CHN, left);
 
-	devc->levels[dev] = left | (left << 8);
-
 	if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs)	/*
 								 * Change register
 								 */
@@ -133,11 +485,26 @@
 
 	sb_setmixer(devc, regoffs, val);
 
-	devc->levels[dev] = left | (right << 8);
 	return left | (right << 8);
 }
 
 /*
+ * After a sb_dsp_reset extended register 0xb4 (RECLEV) is reset too. After
+ * sb_dsp_reset RECLEV has to be restored. This is where ess_mixer_reload
+ * helps.
+ */
+void ess_mixer_reload (sb_devc * devc, int dev)
+{
+	int left, right, value;
+
+	value = devc->levels[dev];
+	left  = value & 0x000000ff;
+    	right = (value & 0x0000ff00) >> 8;
+
+	common_mixer_set(devc, dev, left, right);
+}
+
+/*
  * Changing playback levels at ES188x means having to take care of recording
  * levels of recorded inputs (devc->recmask) too!
  */
@@ -191,6 +558,7 @@
 {
 	int left = value & 0x000000ff;
 	int right = (value & 0x0000ff00) >> 8;
+	int retval;
 
 	if (left > 100)
 		left = 100;
@@ -205,19 +573,24 @@
 							 */
 		return -EINVAL;
 
-	/* Differentiate dependong on the chipsets */
+	/* Differentiate depending on the chipsets */
 	switch (devc->model) {
 	case MDL_SMW:
-		return smw_mixer_set(devc, dev, left, right);
+		retval = smw_mixer_set(devc, dev, left, right);
 		break;
 	case MDL_ESS:
 		if (devc->submodel == SUBMDL_ES188X) {
-			return es188x_mixer_set(devc, dev, left, right);
+			retval = es188x_mixer_set(devc, dev, left, right);
+		} else {
+			retval = common_mixer_set(devc, dev, left, right);
 		}
 		break;
+	default:
+		retval = common_mixer_set(devc, dev, left, right);
 	}
+	if (retval >= 0) devc->levels[dev] = retval;
 
-	return common_mixer_set(devc, dev, left, right);
+	return retval;
 }
 
 /*
@@ -496,7 +869,7 @@
 static void sb_mixer_reset(sb_devc * devc)
 {
 	char name[32];
-	int i, regval;
+	int i;
 	extern int sm_games;
 
 	sprintf(name, "SB_%d", devc->sbmixnum);
@@ -516,12 +889,8 @@
 	 * Then call set_recmask twice to do extra ES188x initializations
 	 */
 	if (devc->model == MDL_ESS && devc->submodel == SUBMDL_ES188X) {
-	        regval = sb_getmixer(devc, 0x7a);
-		regval = (regval & 0xe7) | 0x08;
-	        sb_setmixer(devc, 0x7a, regval);
-		regval = sb_getmixer(devc, 0x1c);
-		regval = (regval & 0xf8) | 0x07;
-		sb_setmixer(devc, 0x1c, regval);
+		sb_mixer_bits(devc, 0x7a, 0x18, 0x08);
+		sb_mixer_bits(devc, 0x1c, 0x07, 0x07);
 
 		set_recmask(devc, ES188X_RECORDING_DEVICES);
 		set_recmask(devc, 0);
@@ -568,11 +937,19 @@
 				devc->iomap = &es188x_mix;
 				break;
 			default:
-				devc->supported_devices
-					= ES688_MIXER_DEVICES;
-				devc->supported_rec_devices
-					= ES688_RECORDING_DEVICES;
-				devc->iomap = &es688_mix;
+				if (devc->submodel < 8) {
+					devc->supported_devices
+						= ES688_MIXER_DEVICES;
+					devc->supported_rec_devices
+						= ES688_RECORDING_DEVICES;
+					devc->iomap = &es688_mix;
+				} else {
+					devc->supported_devices
+						= ES1688_MIXER_DEVICES;
+					devc->supported_rec_devices
+						= ES1688_RECORDING_DEVICES;
+					devc->iomap = &es1688_mix;
+				}
 			}
 
 			break;
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/sb_mixer.h linux/drivers/sound/sb_mixer.h
--- v2.2.0-pre4/linux/drivers/sound/sb_mixer.h	Mon Dec 28 15:00:52 1998
+++ linux/drivers/sound/sb_mixer.h	Mon Jan  4 11:37:30 1999
@@ -17,8 +17,10 @@
  *	Added defines for the Sound Galaxy NX Pro mixer.
  *
  *	Rolf Fokkens	Dec 20 1998
- *	Added (BETA?) support for ES188x chips.
- *	Which means: you can adjust the recording levels.
+ *	Added defines for some ES188x chips.
+ *
+ *	Rolf Fokkens	Dec 27 1998
+ *	Moved static stuff to sb_mixer.c
  *
  */
 #include <linux/config.h>
@@ -51,9 +53,12 @@
 #define ES688_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
 #define ES688_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_LINE2|SOUND_MASK_SPEAKER)
 
-#define ES188X_RECORDING_DEVICES	(ES688_RECORDING_DEVICES | SOUND_MASK_LINE2 \
+#define ES1688_RECORDING_DEVICES ES688_RECORDING_DEVICES
+#define ES1688_MIXER_DEVICES (ES688_MIXER_DEVICES|SOUND_MASK_RECLEV)
+
+#define ES188X_RECORDING_DEVICES	(ES1688_RECORDING_DEVICES | SOUND_MASK_LINE2 \
 					 |SOUND_MASK_SYNTH)
-#define ES188X_MIXER_DEVICES (ES688_MIXER_DEVICES)
+#define ES188X_MIXER_DEVICES (ES1688_MIXER_DEVICES)
 
 #define SB16_MIXER_DEVICES		(SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
 					 SOUND_MASK_CD | \
@@ -120,7 +125,7 @@
 /*
  * Mixer registers of ES188x
  *
- * These register specifically take care of recording levels. To make the
+ * These registers specifically take care of recording levels. To make the
  * mapping from playback devices to recording devices every recording
  * devices = playback device + ES188X_MIXER_RECDIFF
  */
@@ -152,245 +157,6 @@
 #define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r)	\
 	{{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}}
 
-#ifdef __SB_MIXER_C__
-static mixer_tab sbpro_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME,	0x22, 7, 4, 0x22, 3, 4),
-MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH,	0x26, 7, 4, 0x26, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM,	0x04, 7, 4, 0x04, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE,	0x2e, 7, 4, 0x2e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC,	0x0a, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_CD,		0x28, 7, 4, 0x28, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0)
-};
-static mixer_tab es688_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME,	0x32, 7, 4, 0x32, 3, 4),
-MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH,	0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM,	0x14, 7, 4, 0x14, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER,	0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE,	0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC,	0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD,		0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_IGAIN,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2,	0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3,	0x00, 0, 0, 0x00, 0, 0)
-};
-
-/*
- * The ES188x specifics.
- * Note that de master volume unlike ES688 is now controlled by two 6 bit
- * registers. These seem to work OK on 1868 too, but I have no idea if it's
- * compatible to 688 or 1688....
- * Also Note that the recording levels (ES188X_MIXER_REC...) have own 
- * entries as if they were playback devices. They are used internally in the
- * driver only!
- */
-static mixer_tab es188x_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME,	0x60, 5, 6, 0x62, 5, 6),
-MIX_ENT(SOUND_MIXER_BASS,       0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE,     0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH,      0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM,        0x14, 7, 4, 0x14, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER,    0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE,       0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC,        0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD,         0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX,       0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM,     0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV,     0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_IGAIN,      0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN,      0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1,      0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2,      0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3,      0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECSYNTH,	0x6b, 7, 4, 0x6b, 3, 4),
-MIX_ENT(ES188X_MIXER_RECPCM,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECSPEAKER,0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECLINE,	0x6e, 7, 4, 0x6e, 3, 4),
-MIX_ENT(ES188X_MIXER_RECMIC,	0x68, 7, 4, 0x68, 3, 4),
-MIX_ENT(ES188X_MIXER_RECCD,	0x6a, 7, 4, 0x6a, 3, 4),
-
-MIX_ENT(ES188X_MIXER_RECIMIX,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECALTPCM,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECRECLEV,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECIGAIN,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECOGAIN,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECLINE1,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES188X_MIXER_RECLINE2,	0x6c, 7, 4, 0x6c, 3, 4),
-MIX_ENT(ES188X_MIXER_RECLINE3,	0x00, 0, 0, 0x00, 0, 0)
-};
-
-#ifdef	__SGNXPRO__
-#if 0
-static mixer_tab sgnxpro_mix = { 	/* not used anywhere */
-MIX_ENT(SOUND_MIXER_VOLUME,	0x22, 7, 4, 0x22, 3, 4),
-MIX_ENT(SOUND_MIXER_BASS,	0x46, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE,	0x44, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH,	0x26, 7, 4, 0x26, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM,	0x04, 7, 4, 0x04, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER,	0x42, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE,	0x2e, 7, 4, 0x2e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC,	0x0a, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_CD,		0x28, 7, 4, 0x28, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_IGAIN,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN,	0x00, 0, 0, 0x00, 0, 0)
-};
-#endif
-#endif
-
-static mixer_tab sb16_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME,	0x30, 7, 5, 0x31, 7, 5),
-MIX_ENT(SOUND_MIXER_BASS,	0x46, 7, 4, 0x47, 7, 4),
-MIX_ENT(SOUND_MIXER_TREBLE,	0x44, 7, 4, 0x45, 7, 4),
-MIX_ENT(SOUND_MIXER_SYNTH,	0x34, 7, 5, 0x35, 7, 5),
-MIX_ENT(SOUND_MIXER_PCM,	0x32, 7, 5, 0x33, 7, 5),
-MIX_ENT(SOUND_MIXER_SPEAKER,	0x3b, 7, 2, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE,	0x38, 7, 5, 0x39, 7, 5),
-MIX_ENT(SOUND_MIXER_MIC,	0x3a, 7, 5, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_CD,		0x36, 7, 5, 0x37, 7, 5),
-MIX_ENT(SOUND_MIXER_IMIX,	0x3c, 0, 1, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV,	0x3f, 7, 2, 0x40, 7, 2), /* Obsolete. Use IGAIN */
-MIX_ENT(SOUND_MIXER_IGAIN,	0x3f, 7, 2, 0x40, 7, 2),
-MIX_ENT(SOUND_MIXER_OGAIN,	0x41, 7, 2, 0x42, 7, 2)
-};
-
-static mixer_tab als007_mix = 
-{
-MIX_ENT(SOUND_MIXER_VOLUME,	0x62, 7, 4, 0x62, 3, 4),
-MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH,	0x66, 7, 4, 0x66, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM,	0x64, 7, 4, 0x64, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE,	0x6e, 7, 4, 0x6e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC,	0x6a, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_CD,		0x68, 7, 4, 0x68, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0), /* Obsolete. Use IGAIN */
-MIX_ENT(SOUND_MIXER_IGAIN,	0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN,	0x00, 0, 0, 0x00, 0, 0)
-};
-
-
-/* SM_GAMES          Master volume is lower and PCM & FM volumes
-			     higher than with SB Pro. This improves the
-			     sound quality */
-
-static int smg_default_levels[32] =
-{
-  0x2020,			/* Master Volume */
-  0x4b4b,			/* Bass */
-  0x4b4b,			/* Treble */
-  0x6464,			/* FM */
-  0x6464,			/* PCM */
-  0x4b4b,			/* PC Speaker */
-  0x4b4b,			/* Ext Line */
-  0x0000,			/* Mic */
-  0x4b4b,			/* CD */
-  0x4b4b,			/* Recording monitor */
-  0x4b4b,			/* SB PCM */
-  0x4b4b,			/* Recording level */
-  0x4b4b,			/* Input gain */
-  0x4b4b,			/* Output gain */
-  0x4040,			/* Line1 */
-  0x4040,			/* Line2 */
-  0x1515			/* Line3 */
-};
-
-static int sb_default_levels[32] =
-{
-  0x5a5a,			/* Master Volume */
-  0x4b4b,			/* Bass */
-  0x4b4b,			/* Treble */
-  0x4b4b,			/* FM */
-  0x4b4b,			/* PCM */
-  0x4b4b,			/* PC Speaker */
-  0x4b4b,			/* Ext Line */
-  0x1010,			/* Mic */
-  0x4b4b,			/* CD */
-  0x0000,			/* Recording monitor */
-  0x4b4b,			/* SB PCM */
-  0x4b4b,			/* Recording level */
-  0x4b4b,			/* Input gain */
-  0x4b4b,			/* Output gain */
-  0x4040,			/* Line1 */
-  0x4040,			/* Line2 */
-  0x1515			/* Line3 */
-};
-
-static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
-{
-	0x00,	/* SOUND_MIXER_VOLUME	*/
-	0x00,	/* SOUND_MIXER_BASS	*/
-	0x00,	/* SOUND_MIXER_TREBLE	*/
-	0x40,	/* SOUND_MIXER_SYNTH	*/
-	0x00,	/* SOUND_MIXER_PCM	*/
-	0x00,	/* SOUND_MIXER_SPEAKER	*/
-	0x10,	/* SOUND_MIXER_LINE	*/
-	0x01,	/* SOUND_MIXER_MIC	*/
-	0x04,	/* SOUND_MIXER_CD	*/
-	0x00,	/* SOUND_MIXER_IMIX	*/
-	0x00,	/* SOUND_MIXER_ALTPCM	*/
-	0x00,	/* SOUND_MIXER_RECLEV	*/
-	0x00,	/* SOUND_MIXER_IGAIN	*/
-	0x00	/* SOUND_MIXER_OGAIN	*/
-};
-
-static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
-{
-	0x00,	/* SOUND_MIXER_VOLUME	*/
-	0x00,	/* SOUND_MIXER_BASS	*/
-	0x00,	/* SOUND_MIXER_TREBLE	*/
-	0x20,	/* SOUND_MIXER_SYNTH	*/
-	0x00,	/* SOUND_MIXER_PCM	*/
-	0x00,	/* SOUND_MIXER_SPEAKER	*/
-	0x08,	/* SOUND_MIXER_LINE	*/
-	0x01,	/* SOUND_MIXER_MIC	*/
-	0x02,	/* SOUND_MIXER_CD	*/
-	0x00,	/* SOUND_MIXER_IMIX	*/
-	0x00,	/* SOUND_MIXER_ALTPCM	*/
-	0x00,	/* SOUND_MIXER_RECLEV	*/
-	0x00,	/* SOUND_MIXER_IGAIN	*/
-	0x00	/* SOUND_MIXER_OGAIN	*/
-};
-
-static char     smw_mix_regs[] =	/* Left mixer registers */
-{
-  0x0b,				/* SOUND_MIXER_VOLUME */
-  0x0d,				/* SOUND_MIXER_BASS */
-  0x0d,				/* SOUND_MIXER_TREBLE */
-  0x05,				/* SOUND_MIXER_SYNTH */
-  0x09,				/* SOUND_MIXER_PCM */
-  0x00,				/* SOUND_MIXER_SPEAKER */
-  0x03,				/* SOUND_MIXER_LINE */
-  0x01,				/* SOUND_MIXER_MIC */
-  0x07,				/* SOUND_MIXER_CD */
-  0x00,				/* SOUND_MIXER_IMIX */
-  0x00,				/* SOUND_MIXER_ALTPCM */
-  0x00,				/* SOUND_MIXER_RECLEV */
-  0x00,				/* SOUND_MIXER_IGAIN */
-  0x00,				/* SOUND_MIXER_OGAIN */
-  0x00,				/* SOUND_MIXER_LINE1 */
-  0x00,				/* SOUND_MIXER_LINE2 */
-  0x00				/* SOUND_MIXER_LINE3 */
-};
-
 /*
  *	Recording sources (SB Pro)
  */
@@ -408,5 +174,4 @@
 #define ALS007_CD	2
 #define ALS007_SYNTH	7
 
-#endif
 #endif
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/sonicvibes.c linux/drivers/sound/sonicvibes.c
--- v2.2.0-pre4/linux/drivers/sound/sonicvibes.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/sound/sonicvibes.c	Mon Jan  4 11:37:30 1999
@@ -2389,11 +2389,11 @@
 		printk(KERN_INFO "sv: found adapter at io %#06x irq %u dmaa %#06x dmac %#06x revision %u\n",
 		       s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION));
 		/* register devices */
-		if ((s->dev_audio = register_sound_dsp(&sv_audio_fops)) < 0)
+		if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1)) < 0)
 			goto err_dev1;
-		if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops)) < 0)
+		if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1)) < 0)
 			goto err_dev2;
-		if ((s->dev_midi = register_sound_midi(&sv_midi_fops)) < 0)
+		if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1)) < 0)
 			goto err_dev3;
 		if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0)
 			goto err_dev4;
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/sound_calls.h linux/drivers/sound/sound_calls.h
--- v2.2.0-pre4/linux/drivers/sound/sound_calls.h	Tue Dec 22 14:16:56 1998
+++ linux/drivers/sound/sound_calls.h	Mon Jan  4 11:37:30 1999
@@ -300,5 +300,5 @@
 void attach_wf_mpu(struct address_info * hw_config);
 int probe_wf_mpu(struct address_info *hw_config);
 void unload_wf_mpu(struct address_info *hw_config);
-int virtual_midi_enable (int mididev, struct address_info *);
-void virtual_midi_disable (int mididev);
+int virtual_midi_enable (void);
+int virtual_midi_disable (void);
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/sound_core.c linux/drivers/sound/sound_core.c
--- v2.2.0-pre4/linux/drivers/sound/sound_core.c	Sat Sep  5 16:46:41 1998
+++ linux/drivers/sound/sound_core.c	Mon Jan  4 11:37:30 1999
@@ -57,24 +57,32 @@
  *	join into it. Called with the lock asserted
  */
 
-static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, struct file_operations *fops, int low, int top)
+static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, struct file_operations *fops, int index, int low, int top)
 {
 	int n=low;
 
-	while(n<top)
-	{
-		/* Found a hole ? */
-		if(*list==NULL || (*list)->unit_minor>n)
-			break;
-		list=&((*list)->next);
-		n+=16;
-	}
-	
-	if(n==top)
-	{
-		return -1;
-	}
-	
+	if (index < 0) {	/* first free */
+		while(n<top)
+		{
+			/* Found a hole ? */
+			if(*list==NULL || (*list)->unit_minor>n)
+				break;
+			list=&((*list)->next);
+			n+=16;
+		}
+
+		if(n>=top)
+			return -ENOMEM;
+	} else {
+		n = low+(index*16);
+		while (*list) {
+			if ((*list)->unit_minor==n)
+				return -EBUSY;
+			if ((*list)->unit_minor>n)
+				break;
+			list=&((*list)->next);
+		}
+	}	
 		
 	/*
 	 *	Fill it in
@@ -127,7 +135,7 @@
  *	list. Acquires locks as needed
  */
  
-static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int low, int top)
+static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top)
 {
 	int r;
 	struct sound_unit *s=(struct sound_unit *)kmalloc(sizeof(struct sound_unit), GFP_KERNEL);
@@ -135,7 +143,7 @@
 		return -1;
 		
 	spin_lock(&sound_loader_lock);
-	r=__sound_insert_unit(s,list,fops,low,top);
+	r=__sound_insert_unit(s,list,fops,index,low,top);
 	spin_unlock(&sound_loader_lock);
 	
 	if(r==-1)
@@ -181,21 +189,21 @@
 
 int register_sound_special(struct file_operations *fops, int unit)
 {
-	return sound_insert_unit(&chains[unit&15], fops, unit, unit+1);
+	return sound_insert_unit(&chains[unit&15], fops, -1, unit, unit+1);
 }
  
 EXPORT_SYMBOL(register_sound_special);
 
-int register_sound_mixer(struct file_operations *fops)
+int register_sound_mixer(struct file_operations *fops, int dev)
 {
-	return sound_insert_unit(&chains[0], fops, 0, 128);
+	return sound_insert_unit(&chains[0], fops, dev, 0, 128);
 }
 
 EXPORT_SYMBOL(register_sound_mixer);
 
-int register_sound_midi(struct file_operations *fops)
+int register_sound_midi(struct file_operations *fops, int dev)
 {
-	return sound_insert_unit(&chains[2], fops, 2, 130);
+	return sound_insert_unit(&chains[2], fops, dev, 2, 130);
 }
 
 EXPORT_SYMBOL(register_sound_midi);
@@ -205,16 +213,16 @@
  *	in open - see below.
  */
  
-int register_sound_dsp(struct file_operations *fops)
+int register_sound_dsp(struct file_operations *fops, int dev)
 {
-	return sound_insert_unit(&chains[3], fops, 3, 131);
+	return sound_insert_unit(&chains[3], fops, dev, 3, 131);
 }
 
 EXPORT_SYMBOL(register_sound_dsp);
 
-int register_sound_synth(struct file_operations *fops)
+int register_sound_synth(struct file_operations *fops, int dev)
 {
-	return sound_insert_unit(&chains[9], fops, 9, 137);
+	return sound_insert_unit(&chains[9], fops, dev, 9, 137);
 }
 
 EXPORT_SYMBOL(register_sound_synth);
@@ -279,6 +287,20 @@
 	NULL
 };
 
+static struct sound_unit *__look_for_unit(int chain, int unit)
+{
+	struct sound_unit *s;
+	
+	s=chains[chain];
+	while(s && s->unit_minor <= unit)
+	{
+		if(s->unit_minor==unit)
+			return s;
+		s=s->next;
+	}
+	return NULL;
+}
+
 int soundcore_open(struct inode *inode, struct file *file)
 {
 	int chain;
@@ -294,28 +316,48 @@
 	}
 	
 	spin_lock(&sound_loader_lock);
+	s = __look_for_unit(chain, unit);
+#ifdef CONFIG_KMOD
+	if (s == NULL) {
+		char mod[32];
 	
-	s=chains[chain];
-	
-	while(s && s->unit_minor <= unit)
-	{
-		if(s->unit_minor==unit)
-		{
-			file->f_op=s->unit_fops;
-			spin_unlock(&sound_loader_lock);
-			if(file->f_op->open)
-				return file->f_op->open(inode,file);
-			else
-				return 0;
-			break;
-		}
-		s=s->next;
+		spin_unlock(&sound_loader_lock);
+		/*
+		 *  Please, don't change this order or code.
+		 *  For ALSA slot means soundcard and OSS emulation code
+		 *  comes as add-on modules which aren't depend on
+		 *  ALSA toplevel modules for soundcards, thus we need
+		 *  load them at first.	  [Jaroslav Kysela <perex@jcu.cz>]
+		 */
+		sprintf(mod, "sound-slot-%i", unit>>4);
+		request_module(mod);
+		sprintf(mod, "sound-service-%i-%i", unit>>4, chain);
+		request_module(mod);
+		spin_lock(&sound_loader_lock);
+		s = __look_for_unit(chain, unit);
+	}
+#endif
+	if (s) {
+		file->f_op=s->unit_fops;
+		spin_unlock(&sound_loader_lock);
+		if(file->f_op->open)
+			return file->f_op->open(inode,file);
+		else
+			return 0;
 	}
 	spin_unlock(&sound_loader_lock);
 	return -ENODEV;
 }
 
 #ifdef MODULE
+
+MODULE_DESCRIPTION("Core sound module");
+MODULE_AUTHOR("Alan Cox");
+
+extern int mod_firmware_load(const char *, char **);
+EXPORT_SYMBOL(mod_firmware_load);
+
+
 void cleanup_module(void)
 {
 	/* We have nothing to really do here - we know the lists must be
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/sound_firmware.c linux/drivers/sound/sound_firmware.c
--- v2.2.0-pre4/linux/drivers/sound/sound_firmware.c	Tue Jun  9 11:57:30 1998
+++ linux/drivers/sound/sound_firmware.c	Mon Jan  4 11:37:30 1999
@@ -58,4 +58,3 @@
 	return r;
 }
 
-EXPORT_SYMBOL(mod_firmware_load);
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/sound_syms.c linux/drivers/sound/sound_syms.c
--- v2.2.0-pre4/linux/drivers/sound/sound_syms.c	Tue Jun  9 11:57:30 1998
+++ linux/drivers/sound/sound_syms.c	Mon Jan  4 11:37:30 1999
@@ -43,6 +43,7 @@
 
 EXPORT_SYMBOL(load_mixer_volumes);
 
+
 EXPORT_SYMBOL(conf_printf);
 EXPORT_SYMBOL(conf_printf2);
 
@@ -54,5 +55,5 @@
 EXPORT_SYMBOL(sound_locker);
 EXPORT_SYMBOL(sound_notifier_chain_register);
 
-MODULE_DESCRIPTION("Sound subsystem");
+MODULE_DESCRIPTION("OSS Sound subsystem");
 MODULE_AUTHOR("Hannu Savolainen, et al.");
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/wavfront.c linux/drivers/sound/wavfront.c
--- v2.2.0-pre4/linux/drivers/sound/wavfront.c	Thu Nov 12 16:21:22 1998
+++ linux/drivers/sound/wavfront.c	Mon Jan  4 11:37:30 1999
@@ -22,10 +22,17 @@
  * now, you just get the YSS225 in the same state as Turtle Beach's
  * "SETUPSND.EXE" utility leaves it.
  *
- * The boards' CODEC (a Crystal CS4232) is supported by cs4232.[co],
+ * The boards' DAC/ADC (a Crystal CS4232) is supported by cs4232.[co],
  * This chip also controls the configuration of the card: the wavefront
  * synth is logical unit 4.
  *
+ *
+ * Supported devices:
+ *
+ *   /dev/dsp                      - using cs4232+ad1848 modules, OSS compatible
+ *   /dev/midiNN and /dev/midiNN+1 - using wf_midi code, OSS compatible
+ *   /dev/synth00                  - raw synth interface
+ * 
  **********************************************************************
  *
  * Copyright (C) by Paul Barton-Davis 1998
@@ -36,9 +43,9 @@
  * Although the relevant code here is all new, the handling of
  * sample/alias/multi- samples is entirely based on a driver by Matt
  * Martin and Rutger Nijlunsing which demonstrated how to get things
- * to most aspects of this to work correctly. The GUS patch loading
- * code has been almost unaltered by me, except to fit formatting and
- * function names in the rest of the file. Many thanks to them.
+ * to work correctly. The GUS patch loading code has been almost
+ * unaltered by me, except to fit formatting and function names in the
+ * rest of the file. Many thanks to them.
  *
  * Appreciation and thanks to Hannu Savolainen for his early work on the Maui
  * driver, and answering a few questions while this one was developed.
@@ -49,16 +56,23 @@
  * aspects of configuring a WaveFront soundcard, particularly the
  * effects processor.
  *
- * $Id: wavfront.c,v 0.5 1998/07/22 16:16:41 pbd Exp $
+ * $Id: wavfront.c,v 0.7 1998/09/09 15:47:36 pbd Exp $
  *
  * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
+ * for more info.  */
 
-#include <linux/config.h>
 #include <linux/module.h>
-#include <asm/init.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>    
+
+#include <linux/interrupt.h>
+#include <linux/config.h>
+#include <linux/init.h>
 
 #include "sound_config.h"
 #include "soundmodule.h"
@@ -69,7 +83,33 @@
 #define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
 #include "midi_synth.h"
 
-/* This thing is meant to work as a module */
+/* Compile-time control of the extent to which OSS is supported.
+
+   I consider /dev/sequencer to be an anachronism, but given its
+   widespread usage by various Linux MIDI software, it seems worth
+   offering support to it if its not too painful. Instead of using
+   /dev/sequencer, I recommend:
+
+     for synth programming and patch loading: /dev/synthNN
+     for kernel-synchronized MIDI sequencing: the ALSA sequencer
+     for direct MIDI control: /dev/midiNN
+
+   I have never tried static compilation into the kernel. The #if's
+   for this are really just notes to myself about what the code is
+   for.
+*/
+
+#define OSS_SUPPORT_SEQ            0x1  /* use of /dev/sequencer */
+#define OSS_SUPPORT_STATIC_INSTALL 0x2  /* static compilation into kernel */
+
+#define OSS_SUPPORT_LEVEL          0x1  /* just /dev/sequencer for now */
+
+#if    OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ
+static int (*midi_load_patch) (int devno, int format, const char *addr,
+			       int offs, int count, int pmgr_flag) = NULL;
+#endif OSS_SUPPORT_SEQ
+
+/* This is meant to work as a module */
 
 #if defined(CONFIG_SOUND_WAVEFRONT_MODULE) && defined(MODULE)
 
@@ -81,14 +121,28 @@
 
 #define WF_DEBUG 1
 
+#ifdef WF_DEBUG
+
+/* Thank goodness for gcc's preprocessor ... */
+
+#define DPRINT(cond, format, args...) \
+       if ((dev.debug & (cond)) == (cond)) { \
+	     printk (KERN_DEBUG LOGNAME format, ## args); \
+       }
+#else
+#define DPRINT(cond, format, args...)
+#endif
+
+#define LOGNAME "WaveFront: "
+
 /* bitmasks for WaveFront status port value */
 
-#define STAT_INTR_WRITE		0x40
-#define STAT_CAN_WRITE		0x20
-#define STAT_WINTR_ENABLED	0x10
-#define STAT_INTR_READ		0x04
-#define STAT_CAN_READ		0x02
 #define STAT_RINTR_ENABLED	0x01
+#define STAT_CAN_READ		0x02
+#define STAT_INTR_READ		0x04
+#define STAT_WINTR_ENABLED	0x10
+#define STAT_CAN_WRITE		0x20
+#define STAT_INTR_WRITE		0x40
 
 /*** Module-accessible parameters ***************************************/
 
@@ -117,9 +171,6 @@
 					     version of the WaveFront OS
 					  */
 
-int sleep_interval = 100;     /* HZ/sleep_interval seconds per sleep */
-int sleep_tries = 50;       /* number of times we'll try to sleep */
-
 int wait_usecs = 150; /* This magic number seems to give pretty optimal
 			 throughput based on my limited experimentation.
 			 If you want to play around with it and find a better
@@ -133,24 +184,40 @@
 			 status waits, only about 250 result in a sleep.
 		      */
 
+int sleep_interval = 100;     /* HZ/sleep_interval seconds per sleep */
+int sleep_tries = 50;       /* number of times we'll try to sleep */
+
+int reset_time = 2;        /* hundreths of a second we wait after a HW reset for
+			      the expected interrupt.
+			   */
+
+int ramcheck_time = 20;    /* time in seconds to wait while ROM code
+			      checks on-board RAM.
+			   */
+
+int osrun_time = 10;       /* time in seconds we wait for the OS to
+			      start running.
+			   */
+
 MODULE_PARM(wf_raw,"i");
 MODULE_PARM(fx_raw,"i");
 MODULE_PARM(debug_default,"i");
+MODULE_PARM(wait_usecs,"i");
 MODULE_PARM(sleep_interval,"i");
 MODULE_PARM(sleep_tries,"i");
-MODULE_PARM(wait_usecs,"i");
 MODULE_PARM(ospath,"s");
+MODULE_PARM(reset_time,"i");
+MODULE_PARM(ramcheck_time,"i");
+MODULE_PARM(osrun_time,"i");
 
 /***************************************************************************/
 
-static struct synth_info wavefront_info =
-{"Turtle Beach WaveFront", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_WAVEFRONT,
- 0, 32, 0, 0, SYNTH_CAP_INPUT};
-
-static int (*midi_load_patch) (int dev, int format, const char *addr,
-			       int offs, int count, int pmgr_flag) = NULL;
+/* Note: because this module doesn't export any symbols, this really isn't
+   a global variable, even if it looks like one. I was quite confused by
+   this when I started writing this as a (newer) module -- pbd.
+*/
 
-typedef struct wf_config {
+struct wf_config {
 	int devno;            /* device number from kernel */
 	int irq;              /* "you were one, one of the few ..." */
 	int base;             /* low i/o port address */
@@ -181,15 +248,23 @@
 #define fx_mod_data     base + 0xf 
 
 	volatile int irq_ok;               /* set by interrupt handler */
-	int opened;                        /* flag, holds open(1) mode */
+        volatile int irq_cnt;              /* ditto */
+	int opened;                        /* flag, holds open(2) mode */
 	char debug;                        /* debugging flags */
 	int freemem;                       /* installed RAM, in bytes */ 
-	int synthdev;                      /* OSS minor devnum for synth */
-	int mididev;                       /* OSS minor devno for internal MIDI */
-	int ext_mididev;                   /* OSS minor devno for external MIDI */ 
+
+	int synth_dev;                     /* devno for "raw" synth */
+	int mididev;                       /* devno for internal MIDI */
+	int ext_mididev;                   /* devno for external MIDI */ 
+        int fx_mididev;                    /* devno for FX MIDI interface */
+#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ
+	int oss_dev;                      /* devno for OSS sequencer synth */
+#endif OSS_SUPPORT_SEQ
+
 	char fw_version[2];                /* major = [0], minor = [1] */
 	char hw_version[2];                /* major = [0], minor = [1] */
 	char israw;                        /* needs Motorola microcode */
+	char has_fx;                       /* has FX processor (Tropez+) */
 	char prog_status[WF_MAX_PROGRAM];  /* WF_SLOT_* */
 	char patch_status[WF_MAX_PATCH];   /* WF_SLOT_* */
 	char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */
@@ -197,27 +272,22 @@
 	char interrupts_on;                /* h/w MPU interrupts enabled ? */
 	char rom_samples_rdonly;           /* can we write on ROM samples */
 	struct wait_queue *interrupt_sleeper; 
-#ifdef  WF_STATS
-	unsigned long status_found_during_loop;
-	unsigned long status_found_during_sleep[4];
-#endif  WF_STATS
+} dev;
 
-} wf_config;
-
-/* Note: because this module doesn't export any symbols, this really isn't
-   a global variable, even if it looks like one. I was quite confused by
-   this when I started writing this as a (newer) module -- pbd.
-*/
-
-static wf_config wavefront_configuration;
-
-#define wavefront_status(hw) (inb (hw->status_port))
-
-/* forward references */
-
-static int wffx_ioctl (struct wf_config *, wavefront_fx_info *);
-static int wffx_init (struct wf_config *hw);
-static int wavefront_delete_sample (struct wf_config *hw, int sampnum);
+static int  detect_wffx(void);
+static int  wffx_ioctl (wavefront_fx_info *);
+static int  wffx_init (void);
+
+static int wavefront_delete_sample (int sampnum);
+static int wavefront_find_free_sample (void);
+
+/* From wf_midi.c */
+
+extern int  virtual_midi_enable  (void);
+extern int  virtual_midi_disable (void);
+extern int  detect_wf_mpu (int, int);
+extern int  install_wf_mpu (void);
+extern int  uninstall_wf_mpu (void);
 
 typedef struct {
 	int cmd;
@@ -339,92 +409,92 @@
 	return (wavefront_command *) 0;
 }
 
+static inline int
+wavefront_status (void) 
+
+{
+	return inb (dev.status_port);
+}
+
 static int
-wavefront_sleep (wf_config *hw, int limit)
+wavefront_sleep (int limit)
 
 {
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout(limit);
+
 	return signal_pending(current);
 }
-    
+
 static int
-wavefront_wait (wf_config *hw, int mask)
+wavefront_wait (int mask)
 
 {
 	int             i;
 	static int      short_loop_cnt = 0;
 
+	/* Compute the loop count that lets us sleep for about the
+	   right amount of time, cache issues, bus speeds and all
+	   other issues being unequal but largely irrelevant.
+	*/
+
 	if (short_loop_cnt == 0) {
-	    short_loop_cnt = (int) (((double) wait_usecs / 1000000.0) *
-		(double) current_cpu_data.loops_per_sec);
+		short_loop_cnt = wait_usecs *
+			(current_cpu_data.loops_per_sec / 1000000);
 	}
 
+	/* Spin for a short period of time, because >99% of all
+	   requests to the WaveFront can be serviced inline like this.
+	*/
+
 	for (i = 0; i < short_loop_cnt; i++) {
-		if (wavefront_status(hw) & mask) {
-#ifdef WF_STATS
-		        hw->status_found_during_loop++;
-#endif WF_STATS
+		if (wavefront_status() & mask) {
 			return 1;
 		}
 	}
 
 	for (i = 0; i < sleep_tries; i++) {
 
-		if (wavefront_status(hw) & mask) {
-#ifdef WF_STATS
-    		        if (i < 4) {
-				hw->status_found_during_sleep[i]++;
-			}
-#endif WF_STATS
+		if (wavefront_status() & mask) {
 			return 1;
 		}
 
-		if (wavefront_sleep (hw, HZ/sleep_interval)) {
+		if (wavefront_sleep (HZ/sleep_interval)) {
 			return (0);
 		}
 	}
 
-	return 0;
+	return (0);
 }
 
 static int
-wavefront_read (wf_config *hw)
+wavefront_read (void)
+
 {
-	if (wavefront_wait (hw, STAT_CAN_READ))
-		return inb (hw->data_port);
+	if (wavefront_wait (STAT_CAN_READ))
+		return inb (dev.data_port);
 
-#ifdef WF_DEBUG
-	if (hw->debug & WF_DEBUG_DATA) {
-		printk (KERN_DEBUG "WaveFront: read timeout.\n");
-	}
-#endif WF_DEBUG
+	DPRINT (WF_DEBUG_DATA, "read timeout.\n");
 
 	return -1;
 }
 
 static int
-wavefront_write (wf_config *hw, unsigned char data)
+wavefront_write (unsigned char data)
 
 {
-	if (wavefront_wait (hw, STAT_CAN_WRITE)) {
-		outb (data, hw->data_port);
-		return 1;
+	if (wavefront_wait (STAT_CAN_WRITE)) {
+		outb (data, dev.data_port);
+		return 0;
 	}
 
-#ifdef WF_DEBUG
-	if (hw->debug & WF_DEBUG_DATA) {
-		printk (KERN_DEBUG "WaveFront: write timeout.\n");
-	}
-#endif WF_DEBUG
+	DPRINT (WF_DEBUG_DATA, "write timeout.\n");
 
-	return 0;
+	return -1;
 }
 
 static int
-wavefront_cmd (wf_config *hw, int cmd,
-	       unsigned char *rbuf,
-	       unsigned char *wbuf)
+wavefront_cmd (int cmd, unsigned char *rbuf, unsigned char *wbuf)
 
 {
 	int ack;
@@ -433,7 +503,7 @@
 	wavefront_command *wfcmd;
 
 	if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) {
-		printk (KERN_WARNING "WaveFront: command 0x%x not supported.\n",
+		printk (KERN_WARNING LOGNAME "command 0x%x not supported.\n",
 			cmd);
 		return 1;
 	}
@@ -448,89 +518,59 @@
 		rbuf = 0;
 	}
 
-#ifdef WF_DEBUG
-	if (hw->debug & WF_DEBUG_CMD) {
-		printk (KERN_DEBUG "Wavefront: 0x%x [%s] (%d,%d,%d)\n",
-			cmd, wfcmd->action, wfcmd->read_cnt, wfcmd->write_cnt,
-			wfcmd->need_ack);
-	}
-#endif WF_DEBUG
-    
-	if (!wavefront_write (hw, cmd)) { 
-#ifdef WF_DEBUG
-		if (hw->debug & (WF_DEBUG_IO|WF_DEBUG_CMD)) {
-			printk (KERN_DEBUG "WaveFront: cannot request "
-				"0x%x [%s].\n",
-				cmd, wfcmd->action);
-		}
-#endif WF_DEBUG
+	DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n",
+			       cmd, wfcmd->action, wfcmd->read_cnt,
+			       wfcmd->write_cnt, wfcmd->need_ack);
+    
+	if (wavefront_write (cmd)) { 
+		DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request "
+						     "0x%x [%s].\n",
+						     cmd, wfcmd->action);
 		return 1;
 	} 
 
 	if (wfcmd->write_cnt > 0) {
-#ifdef WF_DEBUG
-		if (hw->debug & WF_DEBUG_DATA) {
-			printk (KERN_DEBUG "WaveFront: writing %d bytes "
-				"for 0x%x\n",
-				wfcmd->write_cnt, cmd);
-		}
-#endif WF_DEBUG
+		DPRINT (WF_DEBUG_DATA, "writing %d bytes "
+					"for 0x%x\n",
+					wfcmd->write_cnt, cmd);
 
 		for (i = 0; i < wfcmd->write_cnt; i++) {
-			if (!wavefront_write (hw, wbuf[i])) {
-#ifdef WF_DEBUG
-				if (hw->debug & WF_DEBUG_IO) {
-					printk (KERN_DEBUG
-                           "WaveFront: bad write for byte %d of 0x%x [%s].\n",
-						i, cmd, wfcmd->action);
-				}
-#endif WF_DEBUG
+			if (wavefront_write (wbuf[i])) {
+				DPRINT (WF_DEBUG_IO, "bad write for byte "
+						      "%d of 0x%x [%s].\n",
+						      i, cmd, wfcmd->action);
 				return 1;
 			}
-#ifdef WF_DEBUG
-			if (hw->debug & WF_DEBUG_DATA) {
-				printk (KERN_DEBUG
-                                        "WaveFront: write[%d] = 0x%x\n",
-					i, wbuf[i]);
-#endif WF_DEBUG
-			}
+
+			DPRINT (WF_DEBUG_DATA, "write[%d] = 0x%x\n",
+						i, wbuf[i]);
 		}
 	}
 
 	if (wfcmd->read_cnt > 0) {
-#ifdef WF_DEBUG
-		if (hw->debug & WF_DEBUG_DATA) {
-			printk (KERN_DEBUG "WaveFront: reading %d ints "
-				"for 0x%x\n",
-				wfcmd->read_cnt, cmd);
-		}
-#endif WF_DEBUG
+		DPRINT (WF_DEBUG_DATA, "reading %d ints "
+					"for 0x%x\n",
+					wfcmd->read_cnt, cmd);
 
 		for (i = 0; i < wfcmd->read_cnt; i++) {
 
-			if ((c = wavefront_read(hw)) == -1) {
-#ifdef WF_DEBUG
-				if (hw->debug & WF_DEBUG_IO) {
-					printk (KERN_DEBUG
-                               "WaveFront: bad read for byte %d of 0x%x [%s].\n",
-						i, cmd, wfcmd->action);
-				}
-#endif WF_DEBUG
+			if ((c = wavefront_read()) == -1) {
+				DPRINT (WF_DEBUG_IO, "bad read for byte "
+						      "%d of 0x%x [%s].\n",
+						      i, cmd, wfcmd->action);
 				return 1;
 			}
 
 			/* Now handle errors. Lots of special cases here */
 	    
 			if (c == 0xff) { 
-				if ((c = wavefront_read (hw)) == -1) {
-#ifdef WF_DEBUG
-					if (hw->debug & WF_DEBUG_IO) {
-						printk (KERN_DEBUG
-                                 "WaveFront: bad read for error byte at "
-                                  "read byte %d of 0x%x [%s].\n",
-							i, cmd, wfcmd->action);
-					}
-#endif WF_DEBUG
+				if ((c = wavefront_read ()) == -1) {
+					DPRINT (WF_DEBUG_IO, "bad read for "
+							      "error byte at "
+							      "read byte %d "
+							      "of 0x%x [%s].\n",
+							      i, cmd,
+							      wfcmd->action);
 					return 1;
 				}
 
@@ -553,60 +593,44 @@
 
 				} else {
 
-#ifdef WF_DEBUG
-					if (hw->debug & WF_DEBUG_IO) {
-						printk (KERN_DEBUG
-                                            "WaveFront: error %d (%s) during "
-                                                "read for byte "
-					          "%d of 0x%x [%s].\n",
-							c,
-							wavefront_errorstr (c),
-							i, cmd, wfcmd->action);
-					}
-#endif WF_DEBUG
+					DPRINT (WF_DEBUG_IO, "error %d (%s) "
+							      "during "
+							      "read for byte "
+							      "%d of 0x%x "
+							      "[%s].\n",
+							      c,
+							      wavefront_errorstr (c),
+							      i, cmd,
+							      wfcmd->action);
 					return 1;
 
 				}
-			} else {
+		
+		} else {
 				rbuf[i] = c;
 			}
-
-#ifdef WF_DEBUG
-			if (hw->debug & WF_DEBUG_DATA) {
-				printk (KERN_DEBUG
-					"WaveFront: read[%d] = 0x%x\n",
-					i, rbuf[i]);
-			}
-#endif WF_DEBUG
+			
+			DPRINT (WF_DEBUG_DATA, "read[%d] = 0x%x\n",i, rbuf[i]);
 		}
 	}
-
+	
 	if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) {
 
-#ifdef WF_DEBUG
-		if (hw->debug & WF_DEBUG_CMD) {
-			printk (KERN_DEBUG "WaveFront: reading ACK for 0x%x\n",
-				cmd);
-		}
-#endif WF_DEBUG
+		DPRINT (WF_DEBUG_CMD, "reading ACK for 0x%x\n", cmd);
 
 		/* Some commands need an ACK, but return zero instead
 		   of the standard value.
 		*/
 	    
-		if ((ack = wavefront_read(hw)) == 0) {
+		if ((ack = wavefront_read()) == 0) {
 			ack = WF_ACK;
 		}
 	
 		if (ack != WF_ACK) {
 			if (ack == -1) {
-#ifdef WF_DEBUG
-				if (hw->debug & WF_DEBUG_IO) {
-					printk (KERN_DEBUG
-                               "WaveFront: cannot read ack for 0x%x [%s].\n",
-						cmd, wfcmd->action);
-				}
-#endif WF_DEBUG
+				DPRINT (WF_DEBUG_IO, "cannot read ack for "
+						      "0x%x [%s].\n",
+						      cmd, wfcmd->action);
 				return 1;
 		
 			} else {
@@ -614,47 +638,32 @@
 
 				if (ack == 0xff) { /* explicit error */
 		    
-					if ((err = wavefront_read (hw)) == -1) {
-#ifdef WF_DEBUG
-						if (hw->debug & WF_DEBUG_DATA) {
-							printk (KERN_DEBUG
-                               "WaveFront: cannot read err for 0x%x [%s].\n",
-                                                            cmd, wfcmd->action);
-						}
-#endif WF_DEBUG
+					if ((err = wavefront_read ()) == -1) {
+						DPRINT (WF_DEBUG_DATA,
+							"cannot read err "
+							"for 0x%x [%s].\n",
+							cmd, wfcmd->action);
 					}
 				}
-
-#ifdef WF_DEBUG
-				if (hw->debug & WF_DEBUG_IO) {
-					printk (KERN_DEBUG
-						"WaveFront: 0x%x [%s] "
-						"failed (0x%x, 0x%x, %s)\n",
-						cmd, wfcmd->action, ack, err,
-						wavefront_errorstr (err));
-				}
-#endif WF_DEBUG
+				
+				DPRINT (WF_DEBUG_IO, "0x%x [%s] "
+					"failed (0x%x, 0x%x, %s)\n",
+					cmd, wfcmd->action, ack, err,
+					wavefront_errorstr (err));
+				
 				return -err;
-			} 
-		}
-
-#ifdef WF_DEBUG
-		if (hw->debug & WF_DEBUG_DATA) {
-			printk (KERN_DEBUG "WaveFront: ack received "
-				"for 0x%x [%s]\n",
-				cmd, wfcmd->action);
+			}
 		}
-#endif WF_DEBUG
+		
+		DPRINT (WF_DEBUG_DATA, "ack received "
+					"for 0x%x [%s]\n",
+					cmd, wfcmd->action);
 	} else {
-#ifdef WF_DEBUG
-		if (hw->debug & WF_DEBUG_CMD) {
-			printk (KERN_DEBUG 
-				"Wavefront: 0x%x [%s] does not need "
-				"ACK (%d,%d,%d)\n",
-				cmd, wfcmd->action, wfcmd->read_cnt,
-				wfcmd->write_cnt, wfcmd->need_ack);
-#endif WF_DEBUG
-		}
+
+		DPRINT (WF_DEBUG_CMD, "0x%x [%s] does not need "
+				       "ACK (%d,%d,%d)\n",
+				       cmd, wfcmd->action, wfcmd->read_cnt,
+				       wfcmd->write_cnt, wfcmd->need_ack);
 	}
 
 	return 0;
@@ -749,7 +758,7 @@
 ***********************************************************************/
 
 static int
-wavefront_delete_sample (wf_config *hw, int sample_num)
+wavefront_delete_sample (int sample_num)
 
 {
 	unsigned char wbuf[2];
@@ -758,15 +767,15 @@
 	wbuf[0] = sample_num & 0x7f;
 	wbuf[1] = sample_num >> 7;
 
-	if ((x = wavefront_cmd (hw, WFC_DELETE_SAMPLE, 0, wbuf)) == 0) {
-		hw->sample_status[sample_num] = WF_ST_EMPTY;
+	if ((x = wavefront_cmd (WFC_DELETE_SAMPLE, 0, wbuf)) == 0) {
+		dev.sample_status[sample_num] = WF_ST_EMPTY;
 	}
 
 	return x;
 }
 
 static int
-wavefront_get_sample_status (struct wf_config *hw, int assume_rom)
+wavefront_get_sample_status (int assume_rom)
 
 {
 	int i;
@@ -775,29 +784,30 @@
 
 	/* check sample status */
     
-	if (wavefront_cmd (hw, WFC_GET_NSAMPLES, rbuf, wbuf)) {
-		printk ("WaveFront: cannot request sample count.\n");
+	if (wavefront_cmd (WFC_GET_NSAMPLES, rbuf, wbuf)) {
+		printk (KERN_WARNING LOGNAME "cannot request sample count.\n");
+		return -1;
 	} 
     
-	sc_real = sc_alias = sc_multi = hw->samples_used = 0;
+	sc_real = sc_alias = sc_multi = dev.samples_used = 0;
     
 	for (i = 0; i < WF_MAX_SAMPLE; i++) {
 	
 		wbuf[0] = i & 0x7f;
 		wbuf[1] = i >> 7;
 
-		if (wavefront_cmd (hw, WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) {
-			printk (KERN_WARNING
-				"WaveFront: cannot identify sample "
+		if (wavefront_cmd (WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) {
+			printk (KERN_WARNING LOGNAME
+				"cannot identify sample "
 				"type of slot %d\n", i);
-			hw->sample_status[i] = WF_ST_EMPTY;
+			dev.sample_status[i] = WF_ST_EMPTY;
 			continue;
 		}
 
-		hw->sample_status[i] = (WF_SLOT_FILLED|rbuf[0]);
+		dev.sample_status[i] = (WF_SLOT_FILLED|rbuf[0]);
 
 		if (assume_rom) {
-			hw->sample_status[i] |= WF_SLOT_ROM;
+			dev.sample_status[i] |= WF_SLOT_ROM;
 		}
 
 		switch (rbuf[0] & WF_ST_MASK) {
@@ -814,21 +824,20 @@
 			break;
 
 		default:
-			printk (KERN_WARNING
-				"WaveFront: unknown sample type for "
+			printk (KERN_WARNING LOGNAME "unknown sample type for "
 				"slot %d (0x%x)\n", 
 				i, rbuf[0]);
 		}
 
 		if (rbuf[0] != WF_ST_EMPTY) {
-			hw->samples_used++;
+			dev.samples_used++;
 		} 
 	}
 
-	printk (KERN_INFO
-		"WaveFront: %d samples used (%d real, %d aliases, %d multi), "
-		"%d empty\n", hw->samples_used, sc_real, sc_alias, sc_multi,
-		WF_MAX_SAMPLE - hw->samples_used);
+	printk (KERN_INFO LOGNAME
+		"%d samples used (%d real, %d aliases, %d multi), "
+		"%d empty\n", dev.samples_used, sc_real, sc_alias, sc_multi,
+		WF_MAX_SAMPLE - dev.samples_used);
 
 
 	return (0);
@@ -836,7 +845,8 @@
 }
 
 static int
-wavefront_get_patch_status (struct wf_config *hw)
+wavefront_get_patch_status (void)
+
 {
 	unsigned char patchbuf[WF_PATCH_BYTES];
 	unsigned char patchnum[2];
@@ -847,21 +857,21 @@
 		patchnum[0] = i & 0x7f;
 		patchnum[1] = i >> 7;
 
-		if ((x = wavefront_cmd (hw, WFC_UPLOAD_PATCH, patchbuf,
+		if ((x = wavefront_cmd (WFC_UPLOAD_PATCH, patchbuf,
 					patchnum)) == 0) {
 
-			hw->patch_status[i] |= WF_SLOT_FILLED;
+			dev.patch_status[i] |= WF_SLOT_FILLED;
 			p = (wavefront_patch *) patchbuf;
-			hw->sample_status
+			dev.sample_status
 				[p->sample_number|(p->sample_msb<<7)] |=
 				WF_SLOT_USED;
 	    
 		} else if (x == 3) { /* Bad patch number */
-			hw->patch_status[i] = 0;
+			dev.patch_status[i] = 0;
 		} else {
-			printk (KERN_ERR "WaveFront: upload patch "
+			printk (KERN_ERR LOGNAME "upload patch "
 				"error 0x%x\n", x);
-			hw->patch_status[i] = 0;
+			dev.patch_status[i] = 0;
 			return 1;
 		}
 	}
@@ -869,22 +879,23 @@
 	/* program status has already filled in slot_used bits */
 
 	for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) {
-		if (hw->patch_status[i] & WF_SLOT_FILLED) {
+		if (dev.patch_status[i] & WF_SLOT_FILLED) {
 			cnt++;
 		}
-		if (hw->patch_status[i] & WF_SLOT_USED) {
+		if (dev.patch_status[i] & WF_SLOT_USED) {
 			cnt2++;
 		}
 	
 	}
-	printk (KERN_INFO
-		"WaveFront: %d patch slots filled, %d in use\n", cnt, cnt2);
+	printk (KERN_INFO LOGNAME
+		"%d patch slots filled, %d in use\n", cnt, cnt2);
 
 	return (0);
 }
 
 static int
-wavefront_get_program_status (struct wf_config *hw)
+wavefront_get_program_status (void)
+
 {
 	unsigned char progbuf[WF_PROGRAM_BYTES];
 	wavefront_program prog;
@@ -894,64 +905,59 @@
 	for (i = 0; i < WF_MAX_PROGRAM; i++) {
 		prognum = i;
 
-		if ((x = wavefront_cmd (hw, WFC_UPLOAD_PROGRAM, progbuf,
+		if ((x = wavefront_cmd (WFC_UPLOAD_PROGRAM, progbuf,
 					&prognum)) == 0) {
 
-			hw->prog_status[i] |= WF_SLOT_USED;
+			dev.prog_status[i] |= WF_SLOT_USED;
 
 			demunge_buf (progbuf, (unsigned char *) &prog,
 				     WF_PROGRAM_BYTES);
 
 			for (l = 0; l < WF_NUM_LAYERS; l++) {
 				if (prog.layer[l].mute) {
-					hw->patch_status
+					dev.patch_status
 						[prog.layer[l].patch_number] |=
 						WF_SLOT_USED;
 				}
 			}
 		} else if (x == 1) { /* Bad program number */
-			hw->prog_status[i] = 0;
+			dev.prog_status[i] = 0;
 		} else {
-			printk (KERN_ERR "WaveFront: upload program "
+			printk (KERN_ERR LOGNAME "upload program "
 				"error 0x%x\n", x);
-			hw->prog_status[i] = 0;
+			dev.prog_status[i] = 0;
 		}
 	}
 
 	for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) {
-		if (hw->prog_status[i]) {
+		if (dev.prog_status[i]) {
 			cnt++;
 		}
 	}
 
-	printk (KERN_INFO "WaveFront: %d programs slots in use\n", cnt);
+	printk (KERN_INFO LOGNAME "%d programs slots in use\n", cnt);
 
 	return (0);
 }
 
 static int
-wavefront_send_patch (wf_config *hw, 
-		      wavefront_patch_info *header)
+wavefront_send_patch (wavefront_patch_info *header)
 
 {
 	unsigned char buf[WF_PATCH_BYTES+2];
 	unsigned char *bptr;
 
-#ifdef WF_DEBUG
-	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
-		printk (KERN_DEBUG "WaveFront: downloading patch %d\n",
-			header->number);
-	}
-#endif WF_DEBUG
+	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n",
+				      header->number);
 
-	hw->patch_status[header->number] |= WF_SLOT_FILLED;
+	dev.patch_status[header->number] |= WF_SLOT_FILLED;
 
 	bptr = buf;
 	bptr = munge_int32 (header->number, buf, 2);
 	munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES);
     
-	if (wavefront_cmd (hw, WFC_DOWNLOAD_PATCH, 0, buf)) {
-		printk (KERN_ERR "WaveFront: download patch failed\n");
+	if (wavefront_cmd (WFC_DOWNLOAD_PATCH, 0, buf)) {
+		printk (KERN_ERR LOGNAME "download patch failed\n");
 		return -(EIO);
 	}
 
@@ -959,21 +965,16 @@
 }
 
 static int
-wavefront_send_program (wf_config *hw, 
-			wavefront_patch_info *header)
+wavefront_send_program (wavefront_patch_info *header)
 
 {
 	unsigned char buf[WF_PROGRAM_BYTES+1];
 	int i;
 
-#ifdef WF_DEBUG
-	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
-		printk (KERN_DEBUG
-			"WaveFront: downloading program %d\n", header->number);
-	}
-#endif WF_DEBUG
+	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n",
+		header->number);
 
-	hw->prog_status[header->number] = WF_SLOT_USED;
+	dev.prog_status[header->number] = WF_SLOT_USED;
 
 	/* XXX need to zero existing SLOT_USED bit for program_status[i]
 	   where `i' is the program that's being (potentially) overwritten.
@@ -981,7 +982,7 @@
     
 	for (i = 0; i < WF_NUM_LAYERS; i++) {
 		if (header->hdr.pr.layer[i].mute) {
-			hw->patch_status[header->hdr.pr.layer[i].patch_number] |=
+			dev.patch_status[header->hdr.pr.layer[i].patch_number] |=
 				WF_SLOT_USED;
 
 			/* XXX need to mark SLOT_USED for sample used by
@@ -993,8 +994,8 @@
 	buf[0] = header->number;
 	munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES);
     
-	if (wavefront_cmd (hw, WFC_DOWNLOAD_PROGRAM, 0, buf)) {
-		printk (KERN_WARNING "WaveFront: download patch failed\n");	
+	if (wavefront_cmd (WFC_DOWNLOAD_PROGRAM, 0, buf)) {
+		printk (KERN_WARNING LOGNAME "download patch failed\n");	
 		return -(EIO);
 	}
 
@@ -1002,13 +1003,13 @@
 }
 
 static int
-wavefront_freemem (wf_config *hw)
+wavefront_freemem (void)
 
 {
 	char rbuf[8];
 
-	if (wavefront_cmd (hw, WFC_REPORT_FREE_MEMORY, rbuf, 0)) {
-		printk (KERN_WARNING "WaveFront: can't get memory stats.\n");
+	if (wavefront_cmd (WFC_REPORT_FREE_MEMORY, rbuf, 0)) {
+		printk (KERN_WARNING LOGNAME "can't get memory stats.\n");
 		return -1;
 	} else {
 		return demunge_int32 (rbuf, 4);
@@ -1016,8 +1017,7 @@
 }
 
 static int
-wavefront_send_sample (wf_config      *hw,
-		       wavefront_patch_info *header,
+wavefront_send_sample (wavefront_patch_info *header,
 		       UINT16 *dataptr,
 		       int data_is_unsigned)
 
@@ -1045,15 +1045,22 @@
 	int skip = 0;
 	int initial_skip = 0;
 
-#ifdef WF_DEBUG
-	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
-		printk (KERN_DEBUG "WaveFront: sample %sdownload for slot %d, "
-			"type %d, %d bytes from 0x%x\n",
-			header->size ? "" : "header ", 
-			header->number, header->subkey, header->size,
-			(int) header->dataptr);
+	DPRINT (WF_DEBUG_LOAD_PATCH, "sample %sdownload for slot %d, "
+				      "type %d, %d bytes from 0x%x\n",
+				      header->size ? "" : "header ", 
+				      header->number, header->subkey,
+				      header->size,
+				      (int) header->dataptr);
+
+	if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) {
+		int x;
+
+		if ((x = wavefront_find_free_sample ()) < 0) {
+			return -ENOMEM;
+		}
+		printk (KERN_DEBUG LOGNAME "unspecified sample => %d\n", x);
+		header->number = x;
 	}
-#endif WF_DEBUG
 
 	if (header->size) {
 
@@ -1080,24 +1087,24 @@
 		   a copy of the patch/program/sample header data.
 		*/
 
-		if (hw->rom_samples_rdonly) {
-			if (hw->sample_status[header->number] & WF_SLOT_ROM) {
-				printk (KERN_ERR "WaveFront: sample slot %d "
+		if (dev.rom_samples_rdonly) {
+			if (dev.sample_status[header->number] & WF_SLOT_ROM) {
+				printk (KERN_ERR LOGNAME "sample slot %d "
 					"write protected\n",
 					header->number);
 				return -EACCES;
 			}
 		}
 
-		wavefront_delete_sample (hw, header->number);
+		wavefront_delete_sample (header->number);
 	}
 
 	if (header->size) {
-		hw->freemem = wavefront_freemem (hw);
+		dev.freemem = wavefront_freemem ();
 
-		if (hw->freemem < header->size) {
-			printk (KERN_ERR
-				"WaveFront: insufficient memory to "
+		if (dev.freemem < header->size) {
+			printk (KERN_ERR LOGNAME
+				"insufficient memory to "
 				"load %d byte sample.\n",
 				header->size);
 			return -ENOMEM;
@@ -1107,16 +1114,10 @@
 
 	skip = WF_GET_CHANNEL(&header->hdr.s);
 
-	if (skip > 0) {
-		switch (header->hdr.s.SampleResolution) {
-		case LINEAR_16BIT:
-			break;
-		default:
-			printk (KERN_ERR
-				"WaveFront: channel selection only possible "
-				"on 16-bit samples");
-			return -(EINVAL);
-		}
+	if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) {
+		printk (KERN_ERR LOGNAME "channel selection only "
+			"possible on 16-bit samples");
+		return -(EINVAL);
 	}
 
 	switch (skip) {
@@ -1150,13 +1151,10 @@
 		break;
 	}
 
-#ifdef WF_DEBUG
-	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
-		printk (KERN_DEBUG "WaveFront: channel selection: %d => "
-			"initial skip = %d, skip = %d\n",
-			WF_GET_CHANNEL (&header->hdr.s), initial_skip, skip);
-	}
-#endif WF_DEBUG
+	DPRINT (WF_DEBUG_LOAD_PATCH, "channel selection: %d => "
+				      "initial skip = %d, skip = %d\n",
+				      WF_GET_CHANNEL (&header->hdr.s),
+				      initial_skip, skip);
     
 	/* Be safe, and zero the "Unused" bits ... */
 
@@ -1210,10 +1208,10 @@
 	shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1),
 			     shptr, 2);
 
-	if (wavefront_cmd (hw, header->size ?
+	if (wavefront_cmd (header->size ?
 			   WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER,
 			   0, sample_hdr)) {
-		printk (KERN_WARNING "WaveFront: sample %sdownload refused.\n",
+		printk (KERN_WARNING LOGNAME "sample %sdownload refused.\n",
 			header->size ? "" : "header ");
 		return -(EIO);
 	}
@@ -1238,8 +1236,8 @@
 			blocksize = ((length-written+7)&~0x7);
 		}
 
-		if (wavefront_cmd (hw, WFC_DOWNLOAD_BLOCK, 0, 0)) {
-			printk (KERN_WARNING "WaveFront: download block "
+		if (wavefront_cmd (WFC_DOWNLOAD_BLOCK, 0, 0)) {
+			printk (KERN_WARNING LOGNAME "download block "
 				"request refused.\n");
 			return -(EIO);
 		}
@@ -1248,7 +1246,7 @@
 
 			if (dataptr < data_end) {
 		
-				get_user (sample_short, dataptr);
+				__get_user (sample_short, dataptr);
 				dataptr += skip;
 		
 				if (data_is_unsigned) { /* GUS ? */
@@ -1287,21 +1285,23 @@
 			}
 	    
 			if (i < blocksize - 1) {
-				outw (sample_short, hw->block_port);
+				outw (sample_short, dev.block_port);
 			} else {
-				outw (sample_short, hw->last_block_port);
+				outw (sample_short, dev.last_block_port);
 			}
 		}
 
-		/* Get "DMA page acknowledge" */
+		/* Get "DMA page acknowledge", even though its really
+		   nothing to do with DMA at all.
+		*/
 	
-		if ((dma_ack = wavefront_read (hw)) != WF_DMA_ACK) {
+		if ((dma_ack = wavefront_read ()) != WF_DMA_ACK) {
 			if (dma_ack == -1) {
-				printk (KERN_ERR "WaveFront: upload sample "
+				printk (KERN_ERR LOGNAME "upload sample "
 					"DMA ack timeout\n");
 				return -(EIO);
 			} else {
-				printk (KERN_ERR "WaveFront: upload sample "
+				printk (KERN_ERR LOGNAME "upload sample "
 					"DMA ack error 0x%x\n",
 					dma_ack);
 				return -(EIO);
@@ -1309,7 +1309,7 @@
 		}
 	}
 
-	hw->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE);
+	dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE);
 
 	/* Note, label is here because sending the sample header shouldn't
 	   alter the sample_status info at all.
@@ -1320,20 +1320,15 @@
 }
 
 static int
-wavefront_send_alias (struct wf_config *hw, 
-		      wavefront_patch_info *header)
+wavefront_send_alias (wavefront_patch_info *header)
 
 {
 	unsigned char alias_hdr[WF_ALIAS_BYTES];
 
-#ifdef WF_DEBUG
-	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
-		printk (KERN_DEBUG "WaveFront: download alias, %d is "
-			"alias for %d\n",
-			header->number,
-			header->hdr.a.OriginalSample);
-	}
-#endif WF_DEBUG
+	DPRINT (WF_DEBUG_LOAD_PATCH, "download alias, %d is "
+				      "alias for %d\n",
+				      header->number,
+				      header->hdr.a.OriginalSample);
     
 	munge_int32 (header->number, &alias_hdr[0], 2);
 	munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2);
@@ -1348,19 +1343,18 @@
 	munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3);
 	munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2);
 
-	if (wavefront_cmd (hw, WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) {
-		printk (KERN_ERR "WaveFront: download alias failed.\n");
+	if (wavefront_cmd (WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) {
+		printk (KERN_ERR LOGNAME "download alias failed.\n");
 		return -(EIO);
 	}
 
-	hw->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS);
+	dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS);
 
 	return (0);
 }
 
 static int
-wavefront_send_multisample (struct wf_config *hw,
-			    wavefront_patch_info *header)
+wavefront_send_multisample (wavefront_patch_info *header)
 {
 	int i;
 	int num_samples;
@@ -1376,23 +1370,16 @@
 	num_samples = (1<<(header->hdr.ms.NumberOfSamples&7));
 	msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples;
 
-#ifdef WF_DEBUG
-	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
-		printk (KERN_DEBUG "WaveFront: multi %d with %d=%d samples\n",
-			header->number, header->hdr.ms.NumberOfSamples, num_samples);
-	}
-#endif WF_DEBUG
+	DPRINT (WF_DEBUG_LOAD_PATCH, "multi %d with %d=%d samples\n",
+				      header->number,
+				      header->hdr.ms.NumberOfSamples,
+				      num_samples);
 
 	for (i = 0; i < num_samples; i++) {
-#ifdef WF_DEBUG
-		if ((hw->debug & (WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA)) ==
-		    (WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA)) {
-			printk (KERN_DEBUG "WaveFront: sample[%d] = %d\n",
-				i, header->hdr.ms.SampleNumber[i]);
-		}
-#endif WF_DEBUG
+		DPRINT(WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA, "sample[%d] = %d\n",
+		       i, header->hdr.ms.SampleNumber[i]);
 		munge_int32 (header->hdr.ms.SampleNumber[i],
-			     &msample_hdr[3+(i*2)], 2);
+		     &msample_hdr[3+(i*2)], 2);
 	}
     
 	/* Need a hack here to pass in the number of bytes
@@ -1400,21 +1387,20 @@
 	   one day, I'll fix it.
 	*/
 
-	if (wavefront_cmd (hw, WFC_DOWNLOAD_MULTISAMPLE, 
+	if (wavefront_cmd (WFC_DOWNLOAD_MULTISAMPLE, 
 			   (unsigned char *) ((num_samples*2)+3),
 			   msample_hdr)) {
-		printk (KERN_ERR "WaveFront: download of multisample failed.\n");
+		printk (KERN_ERR LOGNAME "download of multisample failed.\n");
 		return -(EIO);
 	}
 
-	hw->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE);
+	dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE);
 
 	return (0);
 }
 
 static int
-wavefront_fetch_multisample (struct wf_config *hw,
-			     wavefront_patch_info *header)
+wavefront_fetch_multisample (wavefront_patch_info *header)
 {
 	int i;
 	unsigned char log_ns[1];
@@ -1423,17 +1409,13 @@
 
 	munge_int32 (header->number, number, 2);
     
-	if (wavefront_cmd (hw, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) {
-		printk (KERN_ERR "WaveFront: upload multisample failed.\n");
+	if (wavefront_cmd (WFC_UPLOAD_MULTISAMPLE, log_ns, number)) {
+		printk (KERN_ERR LOGNAME "upload multisample failed.\n");
 		return -(EIO);
 	}
     
-#ifdef WF_DEBUG
-	if (hw->debug & WF_DEBUG_DATA) {
-		printk (KERN_DEBUG "WaveFront: msample %d has %d samples\n",
-			header->number, log_ns[0]);
-	}
-#endif WF_DEBUG
+	DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n",
+				header->number, log_ns[0]);
 
 	header->hdr.ms.NumberOfSamples = log_ns[0];
 
@@ -1444,14 +1426,14 @@
 	for (i = 0; i < num_samples; i++) {
 		char d[2];
 	
-		if ((d[0] = wavefront_read (hw)) == -1) {
-			printk (KERN_ERR "WaveFront: upload multisample failed "
+		if ((d[0] = wavefront_read ()) == -1) {
+			printk (KERN_ERR LOGNAME "upload multisample failed "
 				"during sample loop.\n");
 			return -(EIO);
 		}
 
-		if ((d[1] = wavefront_read (hw)) == -1) {
-			printk (KERN_ERR "WaveFront: upload multisample failed "
+		if ((d[1] = wavefront_read ()) == -1) {
+			printk (KERN_ERR LOGNAME "upload multisample failed "
 				"during sample loop.\n");
 			return -(EIO);
 		}
@@ -1459,13 +1441,8 @@
 		header->hdr.ms.SampleNumber[i] =
 			demunge_int32 ((unsigned char *) d, 2);
 	
-#ifdef WF_DEBUG
-		if (hw->debug & WF_DEBUG_DATA) {
-			printk (KERN_DEBUG "WaveFront: msample "
-				"sample[%d] = %d\n",
-				i, header->hdr.ms.SampleNumber[i]);
-		}
-#endif WF_DEBUG
+		DPRINT (WF_DEBUG_DATA, "msample sample[%d] = %d\n",
+					i, header->hdr.ms.SampleNumber[i]);
 	}
 
 	return (0);
@@ -1473,21 +1450,16 @@
 
 
 static int
-wavefront_send_drum (struct wf_config *hw, wavefront_patch_info *header)
+wavefront_send_drum (wavefront_patch_info *header)
 
 {
 	unsigned char drumbuf[WF_DRUM_BYTES];
 	wavefront_drum *drum = &header->hdr.d;
 	int i;
 
-#ifdef WF_DEBUG
-	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
-		printk (KERN_DEBUG
-			"WaveFront: downloading edrum for MIDI "
-			"note %d, patch = %d\n", 
-			header->number, drum->PatchNumber);
-	}
-#endif WF_DEBUG
+	DPRINT (WF_DEBUG_LOAD_PATCH, "downloading edrum for MIDI "
+		"note %d, patch = %d\n", 
+		header->number, drum->PatchNumber);
 
 	drumbuf[0] = header->number & 0x7f;
 
@@ -1495,8 +1467,8 @@
 		munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2);
 	}
 
-	if (wavefront_cmd (hw, WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) {
-		printk (KERN_ERR "WaveFront: download drum failed.\n");
+	if (wavefront_cmd (WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) {
+		printk (KERN_ERR LOGNAME "download drum failed.\n");
 		return -(EIO);
 	}
 
@@ -1504,32 +1476,32 @@
 }
 
 static int 
-wavefront_find_free_sample (struct wf_config *hw)
+wavefront_find_free_sample (void)
 
 {
 	int i;
 
 	for (i = 0; i < WF_MAX_SAMPLE; i++) {
-		if (!(hw->sample_status[i] & WF_SLOT_FILLED)) {
+		if (!(dev.sample_status[i] & WF_SLOT_FILLED)) {
 			return i;
 		}
 	}
-	printk (KERN_WARNING "WaveFront: no free sample slots!\n");
+	printk (KERN_WARNING LOGNAME "no free sample slots!\n");
 	return -1;
 }
 
 static int 
-wavefront_find_free_patch (struct wf_config *hw)
+wavefront_find_free_patch (void)
 
 {
 	int i;
 
 	for (i = 0; i < WF_MAX_PATCH; i++) {
-		if (!(hw->patch_status[i] & WF_SLOT_FILLED)) {
+		if (!(dev.patch_status[i] & WF_SLOT_FILLED)) {
 			return i;
 		}
 	}
-	printk (KERN_WARNING "WaveFront: no free patch slots!\n");
+	printk (KERN_WARNING LOGNAME "no free patch slots!\n");
 	return -1;
 }
 
@@ -1562,8 +1534,7 @@
 }
 
 static int
-wavefront_load_gus_patch (struct wf_config *hw,
-			  int dev, int format, const char *addr,
+wavefront_load_gus_patch (int devno, int format, const char *addr,
 			  int offs, int count, int pmgr_flag)
 {
 	struct patch_info guspatch;
@@ -1581,14 +1552,14 @@
 	copy_from_user (&((char *) &guspatch)[offs],
 			&(addr)[offs], sizeof_patch - offs);
 
-	if ((i = wavefront_find_free_patch (hw)) == -1) {
+	if ((i = wavefront_find_free_patch ()) == -1) {
 		return -EBUSY;
 	}
 	pat.number = i;
 	pat.subkey = WF_ST_PATCH;
 	patp = &pat.hdr.p;
 
-	if ((i = wavefront_find_free_sample (hw)) == -1) {
+	if ((i = wavefront_find_free_sample ()) == -1) {
 		return -EBUSY;
 	}
 	samp.number = i;
@@ -1644,7 +1615,7 @@
 	progp->layer[0].mix_level=127  /* guspatch.volume */;
 	progp->layer[0].split_type=0;
 	progp->layer[0].split_point=0;
-	progp->layer[0].updown=0;
+	progp->layer[0].play_below=0;
 
 	for (i = 1; i < 4; i++) {
 		progp->layer[i].mute=0;
@@ -1685,11 +1656,11 @@
 
 	/* Now ship it down */
 
-	wavefront_send_sample (hw, &samp, 
+	wavefront_send_sample (&samp, 
 			       (unsigned short *) &(addr)[sizeof_patch],
 			       (guspatch.mode & WAVE_UNSIGNED) ? 1:0);
-	wavefront_send_patch (hw, &pat);
-	wavefront_send_program (hw, &prog);
+	wavefront_send_patch (&pat);
+	wavefront_send_program (&prog);
 
 	/* Now pan as best we can ... use the slave/internal MIDI device
 	   number if it exists (since it talks to the WaveFront), or the
@@ -1697,8 +1668,8 @@
 	*/
 
 #ifdef CONFIG_MIDI
-	if (hw->mididev > 0) {
-		midi_synth_controller (hw->mididev, guspatch.instr_no, 10,
+	if (dev.mididev > 0) {
+		midi_synth_controller (dev.mididev, guspatch.instr_no, 10,
 				       ((guspatch.panning << 4) > 127) ?
 				       127 : (guspatch.panning << 4));
 	}
@@ -1707,60 +1678,26 @@
 	return(0);
 }
 
-int
-wavefront_load_patch (int dev, int format, const char *addr,
-		      int offs, int count, int pmgr_flag)
-{
-
-	struct wf_config *hw = &wavefront_configuration;
-	wavefront_patch_info header;
-
-	if (format == SYSEX_PATCH) {	/* Handled by midi_synth.c */
-		if (midi_load_patch == NULL) {
-			printk (KERN_ERR
-				"WaveFront: SYSEX not loadable: "
-				"no midi patch loader!\n");
-			return -(EINVAL);
-		}
-		return midi_load_patch (dev, format, addr,
-					offs, count, pmgr_flag);
-
-	} else if (format == GUS_PATCH) {
-		return wavefront_load_gus_patch (hw, dev, format,
-						 addr, offs, count, pmgr_flag);
+static int
+wavefront_load_patch (const char *addr)
 
-	} else if (format != WAVEFRONT_PATCH) {
-		printk (KERN_ERR "WaveFront: unknown patch format %d\n", format);
-		return -(EINVAL);
-	}
 
-	if (count < sizeof (wavefront_patch_info)) {
-		printk (KERN_ERR "WaveFront: sample header too short\n");
+{
+	wavefront_patch_info header;
+	
+	if (copy_from_user (&header, addr, sizeof(wavefront_patch_info) -
+			    sizeof(wavefront_any))) {
+		printk (KERN_WARNING LOGNAME "bad address for load patch.\n");
 		return -(EINVAL);
 	}
 
-	/* copied in so far: `offs' bytes from `addr'. We shouldn't copy
-	   them in again, and they correspond to header->key and header->devno.
-	   So now, copy the rest of the wavefront_patch_info struct, except
-	   for the 'hdr' field, since this is handled via indirection
-	   through the 'hdrptr' field.
-	*/
-
-	copy_from_user (&((char *) &header)[offs], &(addr)[offs],
-			sizeof(wavefront_patch_info) -
-			sizeof(wavefront_any) - offs);
-
-#ifdef WF_DEBUG
-	if (hw->debug & WF_DEBUG_LOAD_PATCH) {
-		printk (KERN_DEBUG "WaveFront: download "
-			"Sample type: %d "
-			"Sample number: %d "
-			"Sample size: %d\n",
-			header.subkey,
-			header.number,
-			header.size);
-	}
-#endif WF_DEBUG
+	DPRINT (WF_DEBUG_LOAD_PATCH, "download "
+				      "Sample type: %d "
+				      "Sample number: %d "
+				      "Sample size: %d\n",
+				      header.subkey,
+				      header.number,
+				      header.size);
 
 	switch (header.subkey) {
 	case WF_ST_SAMPLE:  /* sample or sample_header, based on patch->size */
@@ -1769,7 +1706,7 @@
 				(unsigned char *) header.hdrptr,
 				sizeof (wavefront_sample));
 
-		return wavefront_send_sample (hw, &header, header.dataptr, 0);
+		return wavefront_send_sample (&header, header.dataptr, 0);
 
 	case WF_ST_MULTISAMPLE:
 
@@ -1777,7 +1714,7 @@
 				(unsigned char *) header.hdrptr,
 				sizeof (wavefront_multisample));
 
-		return wavefront_send_multisample (hw, &header);
+		return wavefront_send_multisample (&header);
 
 
 	case WF_ST_ALIAS:
@@ -1786,31 +1723,31 @@
 				(unsigned char *) header.hdrptr,
 				sizeof (wavefront_alias));
 
-		return wavefront_send_alias (hw, &header);
+		return wavefront_send_alias (&header);
 
 	case WF_ST_DRUM:
 		copy_from_user ((unsigned char *) &header.hdr.d, 
 				(unsigned char *) header.hdrptr,
 				sizeof (wavefront_drum));
 
-		return wavefront_send_drum (hw, &header);
+		return wavefront_send_drum (&header);
 
 	case WF_ST_PATCH:
 		copy_from_user ((unsigned char *) &header.hdr.p, 
 				(unsigned char *) header.hdrptr,
 				sizeof (wavefront_patch));
 
-		return wavefront_send_patch (hw, &header);
+		return wavefront_send_patch (&header);
 
 	case WF_ST_PROGRAM:
 		copy_from_user ((unsigned char *) &header.hdr.pr, 
 				(unsigned char *) header.hdrptr,
 				sizeof (wavefront_program));
 
-		return wavefront_send_program (hw, &header);
+		return wavefront_send_program (&header);
 
 	default:
-		printk (KERN_ERR "WaveFront: unknown patch type %d.\n",
+		printk (KERN_ERR LOGNAME "unknown patch type %d.\n",
 			header.subkey);
 		return -(EINVAL);
 	}
@@ -1855,155 +1792,248 @@
 }
 
 static int
-wavefront_synth_control (int dev, int cmd, caddr_t arg)
+wavefront_synth_control (int cmd, wavefront_control *wc)
 
 {
-	struct wf_config *hw = &wavefront_configuration;
-	wavefront_control wc;
 	unsigned char patchnumbuf[2];
 	int i;
 
-	copy_from_user (&wc, arg, sizeof (wc));
+	DPRINT (WF_DEBUG_CMD, "synth control with "
+		"cmd 0x%x\n", wc->cmd);
 
-#ifdef WF_DEBUG
-	if (hw->debug & WF_DEBUG_CMD) {
-		printk (KERN_DEBUG "WaveFront: synth control with "
-			"cmd 0x%x\n", wc.cmd);
-	}
-#endif WF_DEBUG
-
-	/* special case handling of or for various commands */
+	/* Pre-handling of or for various commands */
 
-	switch (wc.cmd) {
+	switch (wc->cmd) {
 	case WFC_DISABLE_INTERRUPTS:
-		printk (KERN_INFO "WaveFront: interrupts disabled.\n");
-		outb (0x80|0x20, hw->control_port);
-		hw->interrupts_on = 0;
+		printk (KERN_INFO LOGNAME "interrupts disabled.\n");
+		outb (0x80|0x20, dev.control_port);
+		dev.interrupts_on = 0;
 		return 0;
 
 	case WFC_ENABLE_INTERRUPTS:
-		printk (KERN_INFO "WaveFront: interrupts enabled.\n");
-		outb (0x80|0x20|0x40, hw->control_port);
-		hw->interrupts_on = 1;
+		printk (KERN_INFO LOGNAME "interrupts enabled.\n");
+		outb (0x80|0x40|0x20, dev.control_port);
+		dev.interrupts_on = 1;
 		return 0;
 
 	case WFC_INTERRUPT_STATUS:
-		wc.rbuf[0] = hw->interrupts_on;
+		wc->rbuf[0] = dev.interrupts_on;
 		return 0;
 
 	case WFC_ROMSAMPLES_RDONLY:
-		hw->rom_samples_rdonly = wc.wbuf[0];
-		wc.status = 0;
+		dev.rom_samples_rdonly = wc->wbuf[0];
+		wc->status = 0;
 		return 0;
 
 	case WFC_IDENTIFY_SLOT_TYPE:
-		i = wc.wbuf[0] | (wc.wbuf[1] << 7);
+		i = wc->wbuf[0] | (wc->wbuf[1] << 7);
 		if (i <0 || i >= WF_MAX_SAMPLE) {
-			printk (KERN_WARNING "WaveFront: invalid slot ID %d\n",
+			printk (KERN_WARNING LOGNAME "invalid slot ID %d\n",
 				i);
-			wc.status = EINVAL;
+			wc->status = EINVAL;
 			return 0;
 		}
-		wc.rbuf[0] = hw->sample_status[i];
-		wc.status = 0;
+		wc->rbuf[0] = dev.sample_status[i];
+		wc->status = 0;
 		return 0;
 
 	case WFC_DEBUG_DRIVER:
-		hw->debug = wc.wbuf[0];
-		printk (KERN_INFO "WaveFront: debug = 0x%x\n", hw->debug);
+		dev.debug = wc->wbuf[0];
+		printk (KERN_INFO LOGNAME "debug = 0x%x\n", dev.debug);
 		return 0;
 
 	case WFC_FX_IOCTL:
-		wffx_ioctl (hw, (wavefront_fx_info *) &wc.wbuf[0]);
+		wffx_ioctl ((wavefront_fx_info *) &wc->wbuf[0]);
 		return 0;
 
 	case WFC_UPLOAD_PATCH:
-		munge_int32 (*((UINT32 *) wc.wbuf), patchnumbuf, 2);
-		memcpy (wc.wbuf, patchnumbuf, 2);
+		munge_int32 (*((UINT32 *) wc->wbuf), patchnumbuf, 2);
+		memcpy (wc->wbuf, patchnumbuf, 2);
 		break;
 
 	case WFC_UPLOAD_MULTISAMPLE:
+		/* multisamples have to be handled differently, and
+		   cannot be dealt with properly by wavefront_cmd() alone.
+		*/
+		wc->status = wavefront_fetch_multisample
+			((wavefront_patch_info *) wc->rbuf);
+		return 0;
+
 	case WFC_UPLOAD_SAMPLE_ALIAS:
-		printk (KERN_INFO "WaveFront: support for various uploads "
+		printk (KERN_INFO LOGNAME "support for sample alias upload "
 			"being considered.\n");
-		wc.status = EINVAL;
+		wc->status = EINVAL;
 		return -EINVAL;
 	}
 
-	wc.status = wavefront_cmd (hw, wc.cmd, wc.rbuf, wc.wbuf);
+	wc->status = wavefront_cmd (wc->cmd, wc->rbuf, wc->wbuf);
 
-	/* Special case handling of certain commands.
+	/* Post-handling of certain commands.
 
 	   In particular, if the command was an upload, demunge the data
 	   so that the user-level doesn't have to think about it.
 	*/
 
-	if (wc.status == 0) {
-		switch (wc.cmd) {
+	if (wc->status == 0) {
+		switch (wc->cmd) {
 			/* intercept any freemem requests so that we know
 			   we are always current with the user-level view
 			   of things.
 			*/
 
 		case WFC_REPORT_FREE_MEMORY:
-			hw->freemem = demunge_int32 (wc.rbuf, 4);
+			dev.freemem = demunge_int32 (wc->rbuf, 4);
 			break;
 
 		case WFC_UPLOAD_PATCH:
-			demunge_buf (wc.rbuf, wc.rbuf, WF_PATCH_BYTES);
+			demunge_buf (wc->rbuf, wc->rbuf, WF_PATCH_BYTES);
 			break;
 
 		case WFC_UPLOAD_PROGRAM:
-			demunge_buf (wc.rbuf, wc.rbuf, WF_PROGRAM_BYTES);
+			demunge_buf (wc->rbuf, wc->rbuf, WF_PROGRAM_BYTES);
 			break;
 
 		case WFC_UPLOAD_EDRUM_PROGRAM:
-			demunge_buf (wc.rbuf, wc.rbuf, WF_DRUM_BYTES - 1);
+			demunge_buf (wc->rbuf, wc->rbuf, WF_DRUM_BYTES - 1);
 			break;
 
 		case WFC_UPLOAD_SAMPLE_HEADER:
-			process_sample_hdr (wc.rbuf);
+			process_sample_hdr (wc->rbuf);
 			break;
 
-		case WFC_UPLOAD_MULTISAMPLE:
 		case WFC_UPLOAD_SAMPLE_ALIAS:
-			printk (KERN_INFO "WaveFront: support for "
-				"various uploads "
+			printk (KERN_INFO LOGNAME "support for "
+				"sample aliases still "
 				"being considered.\n");
 			break;
 
 		case WFC_VMIDI_OFF:
-			virtual_midi_disable (hw->mididev);
+			if (virtual_midi_disable () < 0) {
+				return -(EIO);
+			}
 			break;
 
 		case WFC_VMIDI_ON:
-			virtual_midi_enable (hw->mididev, 0);
-			break;
-
+			if (virtual_midi_enable () < 0) {
+				return -(EIO);
+			}
 			break;
 		}
 	}
 
-	/* XXX It would be nice to avoid a complete copy of the whole
-	   struct sometimes. But I think its fast enough that this
-	   is a low priority fix.
-	*/
+	return 0;
+}
 
-	copy_to_user (arg, &wc, sizeof (wc));
+
+/***********************************************************************/
+/* WaveFront: Linux file system interface (for access via raw synth)    */
+/***********************************************************************/
+
+static loff_t
+wavefront_llseek(struct file *file, loff_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
+static int 
+wavefront_open (struct inode *inode, struct file *file)
+{
+	/* XXX fix me */
+	dev.opened = file->f_flags;
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
+static int
+wavefront_release(struct inode *inode, struct file *file)
+{
+	dev.opened = 0;
+	dev.debug = 0;
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int
+wavefront_ioctl(struct inode *inode, struct file *file,
+		unsigned int cmd, unsigned long arg)
+{
+	wavefront_control wc;
+	int err;
+
+	switch (cmd) {
+
+	case WFCTL_WFCMD:
+		copy_from_user (&wc, (void *) arg, sizeof (wc));
+		
+		if ((err = wavefront_synth_control (cmd, &wc)) == 0) {
+			copy_to_user ((void *) arg, &wc, sizeof (wc));
+		}
+
+		return err;
+		
+	case WFCTL_LOAD_SPP:
+		return wavefront_load_patch ((const char *) arg);
+		
+	default:
+		printk (KERN_WARNING LOGNAME "invalid ioctl %#x\n", cmd);
+		return -(EINVAL);
+
+	}
+	return 0;
+}
+
+static /*const*/ struct file_operations wavefront_fops = {
+	&wavefront_llseek,
+	NULL,  /* read */
+	NULL,  /* write */
+	NULL,  /* readdir */
+	NULL,  /* poll */
+	&wavefront_ioctl,
+	NULL,  /* mmap */
+	&wavefront_open,
+	NULL,  /* flush */
+	&wavefront_release,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
 
-/***********************************************************************
-WaveFront: MIDI synth interface
-***********************************************************************/
+/***********************************************************************/
+/* WaveFront: OSS installation and support interface                   */
+/***********************************************************************/
+
+#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ
+
+static struct synth_info wavefront_info =
+{"Turtle Beach WaveFront", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_WAVEFRONT,
+ 0, 32, 0, 0, SYNTH_CAP_INPUT};
+
+static int
+wavefront_oss_open (int devno, int mode)
+
+{
+	dev.opened = mode;
+	return 0;
+}
 
+static void
+wavefront_oss_close (int devno)
+
+{
+	dev.opened = 0;
+	dev.debug = 0;
+	return;
+}
 
 static int
-wavefront_ioctl (int dev, unsigned int cmd, caddr_t arg)
+wavefront_oss_ioctl (int devno, unsigned int cmd, caddr_t arg)
+
 {
-	wf_config *hw = &wavefront_configuration;
-	unsigned char rbuf[4];
+	wavefront_control wc;
+	int err;
 
 	switch (cmd) {
 	case SNDCTL_SYNTH_INFO:
@@ -2013,183 +2043,156 @@
 		break;
 
 	case SNDCTL_SEQ_RESETSAMPLES:
-		printk (KERN_WARNING
-			"WaveFront: cannot reset sample status in kernel.\n");
+		printk (KERN_WARNING LOGNAME "driver cannot reset samples.\n");
 		return 0; /* don't force an error */
 		break;
 
 	case SNDCTL_SEQ_PERCMODE:
-		/* XXX does this correspond to anything obvious ?*/
 		return 0; /* don't force an error */
 		break;
 
 	case SNDCTL_SYNTH_MEMAVL:
-		if (wavefront_cmd (hw, WFC_REPORT_FREE_MEMORY, rbuf, 0) != 0) {
-			printk (KERN_ERR
-				"WaveFront: cannot get free memory size\n");
-			return 0;
+		if ((dev.freemem = wavefront_freemem ()) < 0) {
+			printk (KERN_ERR LOGNAME "cannot get memory size\n");
+			return -EIO;
 		} else {
-			hw->freemem = demunge_int32 (rbuf, 4);
-			return hw->freemem;
+			return dev.freemem;
 		}
+		break;
 
 	case SNDCTL_SYNTH_CONTROL:
-		return wavefront_synth_control (dev, cmd, arg);
+		copy_from_user (&wc, arg, sizeof (wc));
+
+		if ((err = wavefront_synth_control (cmd, &wc)) == 0) {
+			copy_to_user (arg, &wc, sizeof (wc));
+		}
+
+		return err;
 
 	default:
 		return -(EINVAL);
 	}
 }
 
-static int
-wavefront_open (int dev, int mode)
-
+int
+wavefront_oss_load_patch (int devno, int format, const char *addr,
+			  int offs, int count, int pmgr_flag)
 {
-	struct wf_config *hw = &wavefront_configuration;
 
-	if (hw->opened) {
-		printk (KERN_WARNING "WaveFront: warning: device in use\n");
-	}
+	if (format == SYSEX_PATCH) {	/* Handled by midi_synth.c */
+		if (midi_load_patch == NULL) {
+			printk (KERN_ERR LOGNAME
+				"SYSEX not loadable: "
+				"no midi patch loader!\n");
+			return -(EINVAL);
+		}
 
-	hw->opened = mode;
-	return (0);
-}
+		return midi_load_patch (devno, format, addr,
+					offs, count, pmgr_flag);
 
-static void wavefront_close (int dev)
-{
-	struct wf_config *hw = &wavefront_configuration;
+	} else if (format == GUS_PATCH) {
+		return wavefront_load_gus_patch (devno, format,
+						 addr, offs, count, pmgr_flag);
 
-#ifdef WF_STATS
-	int i;
-	printk ("Status during loop: %ld\n", hw->status_found_during_loop);
-	for (i = 0; i < 4; i++) {
-		printk ("Status during sleep[%d]: %ld\n",
-			i, hw->status_found_during_sleep[i]);
+	} else if (format != WAVEFRONT_PATCH) {
+		printk (KERN_ERR LOGNAME "unknown patch format %d\n", format);
+		return -(EINVAL);
 	}
-#endif WF_STATS
-	hw->opened = 0;
-	hw->debug = 0;
-
-	return;
-}
 
-static void wavefront_aftertouch (int dev, int channel, int pressure)
-{
-	midi_synth_aftertouch (wavefront_configuration.mididev,channel,pressure);
-};
+	if (count < sizeof (wavefront_patch_info)) {
+		printk (KERN_ERR LOGNAME "sample header too short\n");
+		return -(EINVAL);
+	}
 
-static void wavefront_bender (int dev, int chn, int value)
-{
-	midi_synth_bender (wavefront_configuration.mididev, chn, value);
-};
+	/* "addr" points to a user-space wavefront_patch_info */
 
-static void wavefront_controller (int dev, int channel, int ctrl_num, int value)
-{
-	if(ctrl_num==CTRL_PITCH_BENDER) wavefront_bender(0,channel,value);
-	midi_synth_controller (wavefront_configuration.mididev,
-			       channel,ctrl_num,value);
-};
+	return wavefront_load_patch (addr);
+}	
 
-static void wavefront_panning(int dev, int channel, int pressure)
+static struct synth_operations wavefront_operations =
 {
-	midi_synth_controller (wavefront_configuration.mididev,
-			       channel,CTL_PAN,pressure);
+	"WaveFront",
+	&wavefront_info,
+	0,
+	SYNTH_TYPE_SAMPLE,
+	SAMPLE_TYPE_WAVEFRONT,
+	wavefront_oss_open,
+	wavefront_oss_close,
+	wavefront_oss_ioctl,
+
+	midi_synth_kill_note,
+	midi_synth_start_note,
+	midi_synth_set_instr,
+	midi_synth_reset,
+	NULL, /* hw_control */
+	midi_synth_load_patch,
+	midi_synth_aftertouch,
+	midi_synth_controller,
+	midi_synth_panning,
+	NULL, /* volume method */
+	midi_synth_bender,
+	NULL, /* alloc voice */
+	midi_synth_setup_voice
 };
+#endif OSS_SUPPORT_SEQ
 
-static int wavefront_set_instr (int dev, int channel, int instr_no)
-{
-	return(midi_synth_set_instr (wavefront_configuration.mididev,
-				     channel,instr_no));
-};
+#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_STATIC_INSTALL
 
-static int wavefront_kill_note (int dev, int channel, int note, int volume)
-{
-	if (note==255)
-		return (midi_synth_start_note (wavefront_configuration.mididev,
-					       channel, 0, 0));
-	return(midi_synth_kill_note (wavefront_configuration.mididev,
-				     channel, note, volume));
-};
+void attach_wavefront (struct address_info *hw_config)
 
-static int wavefront_start_note (int dev, int channel, int note, int volume)
 {
-	if (note==255) {
-		midi_synth_aftertouch (wavefront_configuration.mididev,
-				       channel,volume); 
-		return(0);
-	};
-
-	if (volume==0) {
-		volume=127;
-		midi_synth_aftertouch
-			(wavefront_configuration.mididev,
-			 channel,0);
-	};
-
-	midi_synth_start_note (wavefront_configuration.mididev,
-			       channel, note, volume);
-	return(0);
-};
+    (void) install_wavefront ();
+}
 
-static void wavefront_setup_voice (int dev, int voice, int chn)
-{
-};
+int probe_wavefront (struct address_info *hw_config)
 
-static void wavefront_reset (int dev)
 {
-	int i;
-
-	for (i = 0; i < 16; i++) {
-		midi_synth_kill_note (dev,i,0,0);
-	};
-};
+    return !detect_wavefront (hw_config->irq, hw_config->io_base);
+}
 
-static struct synth_operations wavefront_operations =
+void unload_wavefront (struct address_info *hw_config) 
 {
-	"WaveFront",
-	&wavefront_info,
-	0,
-	SYNTH_TYPE_SAMPLE,
-	SAMPLE_TYPE_WAVEFRONT,
-	wavefront_open,
-	wavefront_close,
-	wavefront_ioctl,
-	wavefront_kill_note,
-	wavefront_start_note,
-	wavefront_set_instr,
-	wavefront_reset,
-	NULL,
-	wavefront_load_patch,
-	wavefront_aftertouch,
-	wavefront_controller,
-	wavefront_panning,
-	NULL,
-	wavefront_bender,
-	NULL,
-	wavefront_setup_voice
-};
+    (void) uninstall_wavefront ();
+}
 
+#endif OSS_SUPPORT_STATIC_INSTALL
 
-/***********************************************************************
-WaveFront: OSS/Free and/or Linux kernel installation interface
-***********************************************************************/
+/***********************************************************************/
+/* WaveFront: Linux modular sound kernel installation interface        */
+/***********************************************************************/
 
 void
 wavefrontintr (int irq, void *dev_id, struct pt_regs *dummy)
 {
-        /* We don't use this handler except during device
-	   configuration. While the module is installed, the 
-	   interrupt is used to signal MIDI interrupts, and is 
-	   handled by the interrupt routine in wf_midi.c
-	 */
-	   
-	wf_config *hw = (wf_config *) dev_id;
-	hw->irq_ok = 1;
+	struct wf_config *hw = dev_id;
 
-	if ((wavefront_status(hw) & STAT_INTR_WRITE) ||
-	    (wavefront_status(hw) & STAT_INTR_READ)) {
-		wake_up (&hw->interrupt_sleeper);
+	/*
+	   Some comments on interrupts. I attempted a version of this
+	   driver that used interrupts throughout the code instead of
+	   doing busy and/or sleep-waiting. Alas, it appears that once
+	   the Motorola firmware is downloaded, the card *never*
+	   generates an RX interrupt. These are successfully generated
+	   during firmware loading, and after that wavefront_status()
+	   reports that an interrupt is pending on the card from time
+	   to time, but it never seems to be delivered to this
+	   driver. Note also that wavefront_status() continues to
+	   report that RX interrupts are enabled, suggesting that I
+	   didn't goof up and disable them by mistake.
+
+	   Thus, I stepped back to a prior version of
+	   wavefront_wait(), the only place where this really
+	   matters. Its sad, but I've looked through the code to check
+	   on things, and I really feel certain that the Motorola
+	   firmware prevents RX-ready interrupts.
+	*/
+
+	if ((wavefront_status() & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) {
+		return;
 	}
+
+	hw->irq_ok = 1;
+	hw->irq_cnt++;
+	wake_up_interruptible (&hw->interrupt_sleeper);
 }
 
 /* STATUS REGISTER 
@@ -2202,16 +2205,6 @@
 5 Host Tx Register empty (1=Empty)
 6 Host Tx Interrupt Pending (1=Interrupt)
 7 Unused
-
-11111001 
-  Rx Intr enable
-  nothing to read from board
-  no rx interrupt pending
-  unused
-  tx interrupt enabled
-  space to transmit
-  tx interrupt pending
-
 */
 
 int
@@ -2235,7 +2228,7 @@
 		break;
 	
 	default:
-		printk (KERN_WARNING "WaveFront: invalid IRQ %d\n", irq);
+		printk (KERN_WARNING LOGNAME "invalid IRQ %d\n", irq);
 		bits = -1;
 	}
 
@@ -2243,42 +2236,78 @@
 }
 
 void
-wavefront_should_cause_interrupt (wf_config *hw, int val, int port, int timeout)
+wavefront_should_cause_interrupt (int val, int port, int timeout)
 
 {
 	unsigned long flags;
 
 	save_flags (flags);
 	cli();
-	hw->irq_ok = 0;
+	dev.irq_ok = 0;
 	outb (val,port);
-	interruptible_sleep_on_timeout(&hw->interrupt_sleeper, timeout);
+	interruptible_sleep_on_timeout (&dev.interrupt_sleeper, timeout);
 	restore_flags (flags);
 }
 
 static int
-wavefront_hw_reset (wf_config *hw)
+wavefront_hw_reset (void)
 
 {
 	int bits;
 	int hwv[2];
+	unsigned long irq_mask;
+	short reported_irq;
+
+	/* IRQ already checked in init_module() */
+
+	bits = wavefront_interrupt_bits (dev.irq);
+
+	printk (KERN_DEBUG LOGNAME "autodetecting WaveFront IRQ\n");
 
-	/* Check IRQ is legal */
+	sti ();
 
-	if ((bits = wavefront_interrupt_bits (hw->irq)) < 0) {
+	irq_mask = probe_irq_on ();
+
+	outb (0x0, dev.control_port); 
+	outb (0x80 | 0x40 | bits, dev.data_port);	
+	wavefront_should_cause_interrupt(0x80|0x40|0x10|0x1,
+					 dev.control_port,
+					 (reset_time*HZ)/100);
+
+	reported_irq = probe_irq_off (irq_mask);
+
+	if (reported_irq != dev.irq) {
+		if (reported_irq == 0) {
+			printk (KERN_ERR LOGNAME
+				"No unassigned interrupts detected "
+				"after h/w reset\n");
+		} else if (reported_irq < 0) {
+			printk (KERN_ERR LOGNAME
+				"Multiple unassigned interrupts detected "
+				"after h/w reset\n");
+		} else {
+			printk (KERN_ERR LOGNAME "autodetected IRQ %d not the "
+				"value provided (%d)\n", reported_irq,
+				dev.irq);
+		}
+		dev.irq = -1;
 		return 1;
+	} else {
+		printk (KERN_INFO LOGNAME "autodetected IRQ at %d\n",
+			reported_irq);
 	}
 
-	if (request_irq (hw->irq, wavefrontintr,
-			 0, "WaveFront", (void *) hw) < 0) {
-		printk (KERN_WARNING "WaveFront: IRQ %d not available!\n",
-			hw->irq);
+	if (request_irq (dev.irq, wavefrontintr,
+			 SA_INTERRUPT|SA_SHIRQ,
+			 "wavefront synth", &dev) < 0) {
+		printk (KERN_WARNING LOGNAME "IRQ %d not available!\n",
+			dev.irq);
 		return 1;
 	}
 
 	/* try reset of port */
       
-	outb (0x0, hw->control_port); 
+	outb (0x0, dev.control_port); 
   
 	/* At this point, the board is in reset, and the H/W initialization
 	   register is accessed at the same address as the data port.
@@ -2291,8 +2320,8 @@
 
 	   0 - Use the MIDI Input from the 26-pin WaveBlaster
 	   compatible header as the serial MIDI source
-	   1 - Use the MIDI Input from the 9-pin D connector as the serial MIDI 
-	   source.
+	   1 - Use the MIDI Input from the 9-pin D connector as the
+	   serial MIDI source.
      
 	   Bits 5:3 - IRQ Selection
 	   0 0 0 - IRQ 2/9
@@ -2316,7 +2345,7 @@
 	   plus external 9-pin MIDI interface selected
 	*/
 
-	outb (0x80 | 0x40 | bits, hw->data_port);	
+	outb (0x80 | 0x40 | bits, dev.data_port);	
   
 	/* CONTROL REGISTER
 
@@ -2329,85 +2358,28 @@
 	   6 Master Interrupt Enable (1=Enabled)      0x40
 	   7 Master Reset (0=Reset; 1=Run)            0x80
 
-	   Take us out of reset, unmute, master + TX + RX interrupts on.
+	   Take us out of reset, mute output, master + TX + RX interrupts on.
 	   
 	   We'll get an interrupt presumably to tell us that the TX
-	   register is clear. However, this doesn't mean that the
-	   board is ready. We actually have to send it a command, and
-	   wait till it gets back to use. After a cold boot, this can
-	   take some time.
-	   
-	   I think this is because its only after a cold boot that the
-	   onboard ROM does its memory check, which can take "up to 4
-	   seconds" according to the WaveFront SDK. So, since sleeping
-	   doesn't cost us much, we'll give it *plenty* of time. It
-	   turns out that with 12MB of RAM, it can take up to 16
-	   seconds or so!! See the code after "ABOUT INTERRUPTS"
+	   register is clear.
 	*/
 
-	wavefront_should_cause_interrupt(hw,
-					 0x80|0x40|0x10|0x1,
-					 hw->control_port,
-					 (2*HZ)/100);
+	wavefront_should_cause_interrupt(0x80|0x40|0x10|0x1,
+					 dev.control_port,
+					 (reset_time*HZ)/100);
 
 	/* Note: data port is now the data port, not the h/w initialization
 	   port.
 	 */
 
-	if (!hw->irq_ok) {
-		printk (KERN_WARNING
-			"WaveFront: intr not received after h/w un-reset.\n");
+	if (!dev.irq_ok) {
+		printk (KERN_WARNING LOGNAME
+			"intr not received after h/w un-reset.\n");
 		goto gone_bad;
 	} 
 
-	hw->interrupts_on = 1;
+	dev.interrupts_on = 1;
 	
-	/* ABOUT INTERRUPTS:
-	   -----------------
-	   
-	   When we talk about interrupts, there are two kinds
-	   generated by the ICS2115. The first is to signal MPU data
-	   ready to read, and the second is to signal RX or TX status
-	   changes. We *always* want interrupts for MPU stuff but we 
-	   generally avoid using RX/TX interrupts.
-
-	   In theory, we could use the TX and RX interrupts for all
-	   communication with the card. However, there are 2 good
-	   reasons not to do this.
-
-	   First of all, the MIDI interface is going to use the same
-	   interrupt. This presents no practical problem since Linux
-	   allows us to share IRQ's. However, there are times when it
-	   makes sense for a user to ask the driver to disable
-	   interrupts, to avoid bothering Linux with a stream of MIDI
-	   interrupts that aren't going to be used because nothing
-	   cares about them. If we rely on them for communication with
-	   the WaveFront synth as well, this disabling would be
-	   crippling. Since being able to disable them can save quite
-	   a bit of overhead (consider the interrupt frequency of a
-	   physical MIDI controller like a modwheel being shunted back
-	   and forth - its higher than the mouse, and much of
-	   the time is of absolutely no interest to the kernel or any
-	   user space processes whatsoever), we don't want to do this.
-
-	   Secondly, much of the time, there's no reason to go to
-	   sleep on a TX or RX status: the WaveFront gets back to us
-	   quickly enough that its a lot more efficient to just busy
-	   wait on the relevant status. Once we go to sleep, all is
-	   lost anyway, and so interrupts don't really help us much anyway.
-
-	   Therefore, we don't use interrupts for communication with
-	   the WaveFront synth. We just poll the relevant RX/TX status.
-
-	   However, there is one broad exception to this. During module
-	   loading, to deal with several situations where timing would
-	   be an issue, we use TX/RX interrupts to help us avoid busy
-	   waiting for indeterminate and hard to manage periods of
-	   time. So, TX/RX interrupts are enabled until the end of 
-	   wavefront_init(), and not used again after that.
-
-	 */
-
 	/* Note: data port is now the data port, not the h/w initialization
 	   port.
 
@@ -2421,27 +2393,27 @@
 	   subsequent ISC2115 reboots (say, caused by module
 	   reloading) will get through this much faster.
 
-	   Interesting question: why is no RX interrupt received first ?
+	   XXX Interesting question: why is no RX interrupt received first ?
 	*/
-	
-	wavefront_should_cause_interrupt(hw, WFC_HARDWARE_VERSION, 
-					 hw->data_port, 20*HZ);
 
-	if (!hw->irq_ok) {
-		printk (KERN_WARNING
-			"WaveFront: post-RAM-check interrupt not received.\n");
+	wavefront_should_cause_interrupt(WFC_HARDWARE_VERSION, 
+					 dev.data_port, ramcheck_time*HZ);
+
+	if (!dev.irq_ok) {
+		printk (KERN_WARNING LOGNAME
+			"post-RAM-check interrupt not received.\n");
 		goto gone_bad;
 	} 
 
-	if (!(wavefront_status(hw) & STAT_CAN_READ)) {
-		printk (KERN_WARNING
-			"WaveFront: no response to HW version cmd.\n");
+	if (!wavefront_wait (STAT_CAN_READ)) {
+		printk (KERN_WARNING LOGNAME
+			"no response to HW version cmd.\n");
 		goto gone_bad;
 	}
 	
-	if ((hwv[0] = wavefront_read (hw)) == -1) {
-		printk (KERN_WARNING
-			"WaveFront: board not responding correctly.\n");
+	if ((hwv[0] = wavefront_read ()) == -1) {
+		printk (KERN_WARNING LOGNAME
+			"board not responding correctly.\n");
 		goto gone_bad;
 	}
 
@@ -2451,13 +2423,11 @@
 		   and tell us about it either way.
 		*/
 		
-		if ((hwv[0] = wavefront_read (hw)) == -1) {
-			printk (KERN_WARNING 
-				"WaveFront: on-board RAM test failed "
+		if ((hwv[0] = wavefront_read ()) == -1) {
+			printk (KERN_WARNING LOGNAME "on-board RAM test failed "
 				"(bad error code).\n");
 		} else {
-			printk (KERN_WARNING 
-				"WaveFront: on-board RAM test failed "
+			printk (KERN_WARNING LOGNAME "on-board RAM test failed "
 				"(error code: 0x%x).\n",
 				hwv[0]);
 		}
@@ -2466,85 +2436,66 @@
 
 	/* We're OK, just get the next byte of the HW version response */
 
-	if ((hwv[1] = wavefront_read (hw)) == -1) {
-		printk (KERN_WARNING
-			"WaveFront: board not responding correctly(2).\n");
+	if ((hwv[1] = wavefront_read ()) == -1) {
+		printk (KERN_WARNING LOGNAME "incorrect h/w response.\n");
 		goto gone_bad;
 	}
 
-	printk (KERN_INFO "WaveFront: hardware version %d.%d\n",
+	printk (KERN_INFO LOGNAME "hardware version %d.%d\n",
 		hwv[0], hwv[1]);
 
 	return 0;
 
 
      gone_bad:
-	free_irq (hw->irq, hw);
-	return (1);
+	if (dev.irq >= 0) {
+		free_irq (dev.irq, &dev);
+		dev.irq = -1;
 	}
+	return (1);
+}
 
-int
-probe_wavefront (struct address_info *hw_config)
+__initfunc (static int detect_wavefront (int irq, int io_base))
 
 {
 	unsigned char   rbuf[4], wbuf[4];
-	wf_config       *hw;
 
-	if (hw_config->irq < 0 || hw_config->irq > 16) {
-	    printk (KERN_WARNING "WaveFront: impossible IRQ suggested(%d)\n", 
-		    hw_config->irq);
-	    return 0;
-	}
-  
-	/* Yeah yeah, TB docs say 8, but the FX device on the Tropez Plus
-	   takes up another 8 ...
+	/* TB docs say the device takes up 8 ports, but we know that
+	   if there is an FX device present (i.e. a Tropez+) it really
+	   consumes 16.
 	*/
 
-	if (check_region (hw_config->io_base, 16)) {
-		printk (KERN_ERR "WaveFront: IO address range 0x%x - 0x%x "
-			"already in use - ignored\n", hw_config->io_base,
-			hw_config->io_base+15);
-		return 0;
+	if (check_region (io_base, 16)) {
+		printk (KERN_ERR LOGNAME "IO address range 0x%x - 0x%x "
+			"already in use - ignored\n", dev.base,
+			dev.base+15);
+		return -1;
 	}
   
-	hw = &wavefront_configuration;
-
-	hw->irq = hw_config->irq;
-	hw->base = hw_config->io_base;
-
-	hw->israw = 0;
-	hw->debug = debug_default;
-	hw->interrupts_on = 0;
-	hw->rom_samples_rdonly = 1; /* XXX default lock on ROM sample slots */
-
-#ifdef WF_STATS
-	hw->status_found_during_sleep[0] = 0;
-	hw->status_found_during_sleep[1] = 0;
-	hw->status_found_during_sleep[2] = 0;
-	hw->status_found_during_sleep[3] = 0;
-	hw->status_found_during_loop = 0;
-#endif WF_STATS
-
-	hw_config->slots[WF_SYNTH_SLOT] = hw->synthdev = -1;
-	hw_config->slots[WF_INTERNAL_MIDI_SLOT] = hw->mididev = -1;
-	hw_config->slots[WF_EXTERNAL_MIDI_SLOT] = hw->ext_mididev = -1;
-
-	if (wavefront_cmd (hw, WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) {
-
-		hw->fw_version[0] = rbuf[0];
-		hw->fw_version[1] = rbuf[1];
-		printk (KERN_INFO
-			"WaveFront: firmware %d.%d already loaded.\n",
+	dev.irq = irq;
+	dev.base = io_base;
+	dev.israw = 0;
+	dev.debug = debug_default;
+	dev.interrupts_on = 0;
+	dev.irq_cnt = 0;
+	dev.rom_samples_rdonly = 1; /* XXX default lock on ROM sample slots */
+
+	if (wavefront_cmd (WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) {
+
+		dev.fw_version[0] = rbuf[0];
+		dev.fw_version[1] = rbuf[1];
+		printk (KERN_INFO LOGNAME
+			"firmware %d.%d already loaded.\n",
 			rbuf[0], rbuf[1]);
 
 		/* check that a command actually works */
       
-		if (wavefront_cmd (hw, WFC_HARDWARE_VERSION,
+		if (wavefront_cmd (WFC_HARDWARE_VERSION,
 				   rbuf, wbuf) == 0) {
-			hw->hw_version[0] = rbuf[0];
-			hw->hw_version[1] = rbuf[1];
+			dev.hw_version[0] = rbuf[0];
+			dev.hw_version[1] = rbuf[1];
 		} else {
-			printk (KERN_INFO "WaveFront: not raw, but no "
+			printk (KERN_WARNING LOGNAME "not raw, but no "
 				"hardware version!\n");
 			return 0;
 		}
@@ -2552,25 +2503,30 @@
 		if (!wf_raw) {
 			return 1;
 		} else {
-			printk (KERN_INFO
-				"WaveFront: reloading firmware anyway.\n");
+			printk (KERN_INFO LOGNAME
+				"reloading firmware anyway.\n");
+			dev.israw = 1;
 		}
 
 	} else {
 
-		hw->israw = 1;
-		printk (KERN_INFO "WaveFront: no response to firmware probe, "
-			"assume raw.\n");
+		dev.israw = 1;
+		printk (KERN_INFO LOGNAME
+			"no response to firmware probe, assume raw.\n");
 
 	}
 
-	init_waitqueue (&hw->interrupt_sleeper);
+	init_waitqueue (&dev.interrupt_sleeper);
 
-	if (wavefront_hw_reset (hw)) {
-		printk (KERN_WARNING "WaveFront: hardware reset failed\n");
+	if (wavefront_hw_reset ()) {
+		printk (KERN_WARNING LOGNAME "hardware reset failed\n");
 		return 0;
 	}
 
+	/* Check for FX device, present only on Tropez+ */
+
+	dev.has_fx = (detect_wffx () == 0);
+
 	return 1;
 }
 
@@ -2585,7 +2541,7 @@
 static int errno; 
 
 static int
-wavefront_download_firmware (wf_config *hw, char *path)
+wavefront_download_firmware (char *path)
 
 {
 	unsigned char section[WF_SECTION_MAX];
@@ -2610,7 +2566,7 @@
 	set_fs (get_ds());
 
 	if ((fd = open (path, 0, 0)) < 0) {
-		printk (KERN_WARNING "WaveFront: Unable to load \"%s\".\n",
+		printk (KERN_WARNING LOGNAME "Unable to load \"%s\".\n",
 			path);
 		return 1;
 	}
@@ -2620,7 +2576,7 @@
 
 		if ((x = read (fd, &section_length, sizeof (section_length))) !=
 		    sizeof (section_length)) {
-			printk (KERN_ERR "WaveFront: firmware read error.\n");
+			printk (KERN_ERR LOGNAME "firmware read error.\n");
 			goto failure;
 		}
 
@@ -2629,58 +2585,46 @@
 		}
 
 		if (read (fd, section, section_length) != section_length) {
-			printk (KERN_ERR "WaveFront: firmware section "
+			printk (KERN_ERR LOGNAME "firmware section "
 				"read error.\n");
 			goto failure;
 		}
 
 		/* Send command */
 	
-		if (!wavefront_write (hw, WFC_DOWNLOAD_OS)) {
+		if (wavefront_write (WFC_DOWNLOAD_OS)) {
 			goto failure;
 		}
 	
 		for (i = 0; i < section_length; i++) {
-			if (!wavefront_write (hw, section[i])) {
+			if (wavefront_write (section[i])) {
 				goto failure;
 			}
 		}
 	
 		/* get ACK */
 	
-		if (wavefront_wait (hw, STAT_CAN_READ)) {
+		if (wavefront_wait (STAT_CAN_READ)) {
 
-			if ((c = inb (hw->data_port)) != WF_ACK) {
+			if ((c = inb (dev.data_port)) != WF_ACK) {
 
-				printk (KERN_ERR "WaveFront: download "
+				printk (KERN_ERR LOGNAME "download "
 					"of section #%d not "
 					"acknowledged, ack = 0x%x\n",
 					section_cnt_downloaded + 1, c);
 				goto failure;
 		
-			} else {
-#ifdef WF_DEBUG
-			    if ((hw->debug & WF_DEBUG_IO) &&
-				   !(++section_cnt_downloaded % 10)) {
-				printk (KERN_DEBUG ".");
-			    }
-#endif WF_DEBUG
 			}
 
 		} else {
-			printk (KERN_ERR "WaveFront: timed out "
-				"for download ACK.\n");
+			printk (KERN_ERR LOGNAME "time out for firmware ACK.\n");
+			goto failure;
 		}
 
 	}
 
 	close (fd);
 	set_fs (fs);
-#ifdef WF_DEBUG
-	if (hw->debug & WF_DEBUG_IO) {
-		printk (KERN_DEBUG "\n");
-	}
-#endif WF_DEBUG
 	return 0;
 
  failure:
@@ -2690,38 +2634,33 @@
 	return 1;
 }
 
-static int
-wavefront_config_midi (wf_config *hw, struct address_info *hw_config)
+__initfunc (static int wavefront_config_midi (void)) 
 
 {
 	unsigned char rbuf[4], wbuf[4];
     
-	if (!probe_wf_mpu (hw_config)) {
-		printk (KERN_WARNING "WaveFront: could not install "
-			"MPU-401 device.\n");
-		return 1;
+	if (detect_wf_mpu (dev.irq, dev.base) < 0) {
+		printk (KERN_WARNING LOGNAME
+			"could not find working MIDI device\n");
+		return -1;
 	} 
 
-	/* Attach an modified MPU-401 driver to the master MIDI interface */
-
-	hw_config->name = "WaveFront Internal MIDI";
-	attach_wf_mpu (hw_config);
-
-	if (hw_config->slots[WF_INTERNAL_MIDI_SLOT] == -1) {
-		printk (KERN_WARNING "WaveFront: MPU-401 not configured.\n");
-		return 1;
+	if ((dev.mididev = install_wf_mpu ()) < 0) {
+		printk (KERN_WARNING LOGNAME
+			"MIDI interfaces not configured\n");
+		return -1;
 	}
 
-	hw->mididev = hw_config->slots[WF_INTERNAL_MIDI_SLOT];
-
 	/* Route external MIDI to WaveFront synth (by default) */
     
-	if (wavefront_cmd (hw, WFC_MISYNTH_ON, rbuf, wbuf)) {
-		printk (KERN_WARNING
-			"WaveFront: cannot enable MIDI-IN to synth routing.\n");
+	if (wavefront_cmd (WFC_MISYNTH_ON, rbuf, wbuf)) {
+		printk (KERN_WARNING LOGNAME
+			"cannot enable MIDI-IN to synth routing.\n");
 		/* XXX error ? */
 	}
 
+
+#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ
 	/* Get the regular MIDI patch loading function, so we can
 	   use it if we ever get handed a SYSEX patch. This is
 	   unlikely, because its so damn slow, but we may as well
@@ -2730,12 +2669,14 @@
 	   only use MIDI to do patch loading.
 	*/
 
-	if (midi_devs[hw->mididev]->converter != NULL) {
-		midi_load_patch = midi_devs[hw->mididev]->converter->load_patch;
-		midi_devs[hw->mididev]->converter->load_patch =
-		    &wavefront_load_patch;
+	if (midi_devs[dev.mididev]->converter != NULL) {
+		midi_load_patch = midi_devs[dev.mididev]->converter->load_patch;
+		midi_devs[dev.mididev]->converter->load_patch =
+		    &wavefront_oss_load_patch;
 	}
 
+#endif OSS_SUPPORT_SEQ
+	
 	/* Turn on Virtual MIDI, but first *always* turn it off,
 	   since otherwise consectutive reloads of the driver will
 	   never cause the hardware to generate the initial "internal" or 
@@ -2747,22 +2688,19 @@
 	   the internal interface. Duh.
 	*/
 
-	if (wavefront_cmd (hw, WFC_VMIDI_OFF, rbuf, wbuf)) { 
-		printk (KERN_WARNING "WaveFront: cannot disable "
-			"virtual MIDI mode\n");
-		/* XXX go ahead and try anyway ? */
+	if (wavefront_cmd (WFC_VMIDI_OFF, rbuf, wbuf)) { 
+		printk (KERN_WARNING LOGNAME
+			"virtual MIDI mode not disabled\n");
+		return 0; /* We're OK, but missing the external MIDI dev */
 	}
 
-	hw_config->name = "WaveFront External MIDI";
-    
-	if (virtual_midi_enable (hw->mididev, hw_config)) {
-		printk (KERN_WARNING "WaveFront: no virtual MIDI access.\n");
+	if ((dev.ext_mididev = virtual_midi_enable ()) < 0) {
+		printk (KERN_WARNING LOGNAME "no virtual MIDI access.\n");
 	} else {
-		hw->ext_mididev = hw_config->slots[WF_EXTERNAL_MIDI_SLOT];
-		if (wavefront_cmd (hw, WFC_VMIDI_ON, rbuf, wbuf)) {
-			printk (KERN_WARNING
-				"WaveFront: cannot enable virtual MIDI mode.\n");
-			virtual_midi_disable (hw->mididev);
+		if (wavefront_cmd (WFC_VMIDI_ON, rbuf, wbuf)) {
+			printk (KERN_WARNING LOGNAME
+				"cannot enable virtual MIDI mode.\n");
+			virtual_midi_disable ();
 		} 
 	}
     
@@ -2770,62 +2708,66 @@
 }
 
 static int
-wavefront_do_reset (wf_config *hw, int atboot)
+wavefront_do_reset (int atboot)
 
 {
 	char voices[1];
 
-	if (!atboot && wavefront_hw_reset (hw)) {
-		printk (KERN_WARNING "WaveFront: hw reset failed.\n");
+	if (!atboot && wavefront_hw_reset ()) {
+		printk (KERN_WARNING LOGNAME "hw reset failed.\n");
 		goto gone_bad;
 	}
 
-	if (hw->israw || wf_raw) {
-		if (wavefront_download_firmware (hw, ospath)) {
+	if (dev.israw) {
+		if (wavefront_download_firmware (ospath)) {
 			goto gone_bad;
 		}
 
+		dev.israw = 0;
+
 		/* Wait for the OS to get running. The protocol for
 		   this is non-obvious, and was determined by
 		   using port-IO tracing in DOSemu and some
 		   experimentation here.
 		   
-		   Rather than busy-wait, use interrupts creatively.
+		   Rather than using timed waits, use interrupts creatively.
 		*/
 
-		wavefront_should_cause_interrupt (hw, WFC_NOOP,
-					  hw->data_port, (10*HZ));
-		
-		if (!hw->irq_ok) {
-			printk (KERN_WARNING
-				"WaveFront: no post-OS interrupt.\n");
+		wavefront_should_cause_interrupt (WFC_NOOP,
+						  dev.data_port,
+						  (osrun_time*HZ));
+
+		if (!dev.irq_ok) {
+			printk (KERN_WARNING LOGNAME
+				"no post-OS interrupt.\n");
 			goto gone_bad;
 		}
 		
 		/* Now, do it again ! */
 		
-		wavefront_should_cause_interrupt (hw, WFC_NOOP,
-						  hw->data_port, (10*HZ));
+		wavefront_should_cause_interrupt (WFC_NOOP,
+						  dev.data_port, (10*HZ));
 		
-		if (!hw->irq_ok) {
-			printk (KERN_WARNING
-				"WaveFront: no post-OS interrupt(2).\n");
+		if (!dev.irq_ok) {
+			printk (KERN_WARNING LOGNAME
+				"no post-OS interrupt(2).\n");
 			goto gone_bad;
 		}
 
 		/* OK, no (RX/TX) interrupts any more, but leave mute
-		   on. Master interrupts get enabled when we're done here.
+		   in effect. 
 		*/
 		
-		outb (0x80, hw->control_port); 
-		
+		outb (0x80|0x40, dev.control_port); 
+
 		/* No need for the IRQ anymore */
 		
-		free_irq (hw->irq, hw);
+		free_irq (dev.irq, &dev);
+
 	}
 
-	if (/*XXX has_fx_device() && */ fx_raw) {
-		wffx_init (hw);
+	if (dev.has_fx && fx_raw) {
+		wffx_init ();
 	}
 
 	/* SETUPSND.EXE asks for sample memory config here, but since i
@@ -2833,118 +2775,130 @@
 	   about it.
 	*/
 	
-	if ((hw->freemem = wavefront_freemem (hw)) < 0) {
+	if ((dev.freemem = wavefront_freemem ()) < 0) {
 		goto gone_bad;
 	}
 		
-	printk (KERN_INFO "WaveFront: available DRAM %dk\n", hw->freemem / 1024);
+	printk (KERN_INFO LOGNAME "available DRAM %dk\n", dev.freemem / 1024);
 
-	if (!wavefront_write (hw, 0xf0) ||
-	    !wavefront_write (hw, 1) ||
-	    (wavefront_read (hw) < 0)) {
-		hw->debug = 0;
-		printk (KERN_WARNING "WaveFront: MPU emulation mode not set.\n");
+	if (wavefront_write (0xf0) ||
+	    wavefront_write (1) ||
+	    (wavefront_read () < 0)) {
+		dev.debug = 0;
+		printk (KERN_WARNING LOGNAME "MPU emulation mode not set.\n");
 		goto gone_bad;
 	}
 
 	voices[0] = 32;
 
-	if (wavefront_cmd (hw, WFC_SET_NVOICES, 0, voices)) {
-		printk (KERN_WARNING
-			"WaveFront: cannot set number of voices to 32.\n");
+	if (wavefront_cmd (WFC_SET_NVOICES, 0, voices)) {
+		printk (KERN_WARNING LOGNAME
+			"cannot set number of voices to 32.\n");
+		goto gone_bad;
 	}
 
+
 	return 0;
 
  gone_bad:
 	/* reset that sucker so that it doesn't bother us. */
 
-	outb (0x0, hw->control_port);
-	free_irq (hw->irq, hw);
+	outb (0x0, dev.control_port);
+	dev.interrupts_on = 0;
+	if (dev.irq >= 0) {
+		free_irq (dev.irq, &dev);
+	}
 	return 1;
 }
 
 static int
-wavefront_init (wf_config *hw, int atboot)
+wavefront_init (int atboot)
 
 {
 	int samples_are_from_rom;
 
-	if (hw->israw || wf_raw) {
+	if (dev.israw) {
 		samples_are_from_rom = 1;
 	} else {
+		/* XXX is this always true ? */
 		samples_are_from_rom = 0;
 	}
 
-	if (hw->israw || wf_raw || fx_raw) {
-		if (wavefront_do_reset (hw, atboot)) {
-			return 1;
+	if (dev.israw || fx_raw) {
+		if (wavefront_do_reset (atboot)) {
+			return -1;
 		}
 	}
 
-	wavefront_get_sample_status (hw, samples_are_from_rom);
-	wavefront_get_program_status (hw);
-	wavefront_get_patch_status (hw);
+	wavefront_get_sample_status (samples_are_from_rom);
+	wavefront_get_program_status ();
+	wavefront_get_patch_status ();
 
-	/* Start normal operation: unreset, master interrupt enable
-	   (for MPU interrupts) no mute
+	/* Start normal operation: unreset, master interrupt enabled, no mute
 	*/
 
-	outb (0x80|0x40|0x20, hw->control_port); 
+	outb (0x80|0x40|0x20, dev.control_port); 
 
 	return (0);
 }
 
-void
-attach_wavefront (struct address_info *hw_config)
+__initfunc (static int install_wavefront (void))
+
 {
-	int i;
-	struct wf_config *hw = &wavefront_configuration;
+	if ((dev.synth_dev = register_sound_synth (&wavefront_fops, -1)) < 0) {
+		printk (KERN_ERR LOGNAME "cannot register raw synth\n");
+		return -1;
+	}
 
-	if ((i = sound_alloc_synthdev()) == -1) {
-		printk (KERN_ERR "WaveFront: Too many synthesizers\n");
-		return;
+#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ
+	if ((dev.oss_dev = sound_alloc_synthdev()) == -1) {
+		printk (KERN_ERR LOGNAME "Too many sequencers\n");
+		return -1;
 	} else {
-		hw_config->slots[WF_SYNTH_SLOT] = i;
-		hw->synthdev = i;
-		synth_devs[hw->synthdev] = &wavefront_operations;
+		synth_devs[dev.oss_dev] = &wavefront_operations;
 	}
+#endif OSS_SUPPORT_SEQ
 
-	if (wavefront_init (hw, 1)) {
-		printk (KERN_WARNING "WaveFront: board could not "
-			"be initialized.\n");
-		sound_unload_synthdev (i);
-		return;
+	if (wavefront_init (1) < 0) {
+		printk (KERN_WARNING LOGNAME "initialization failed.\n");
+
+#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ
+		sound_unload_synthdev (dev.oss_dev);
+#endif OSS_SUPPORT_SEQ
+
+		return -1;
 	}
     
-	request_region (hw_config->io_base+2, 6, "WaveFront synth");
-	request_region (hw_config->io_base+8, 8, "WaveFront FX");
+	request_region (dev.base+2, 6, "wavefront synth");
 
-	conf_printf2 ("WaveFront Synth", hw_config->io_base, 0, -1, -1);
+	if (dev.has_fx) {
+		request_region (dev.base+8, 8, "wavefront fx");
+	}
 
-#if defined(CONFIG_MIDI)    
-	if (wavefront_config_midi (hw, hw_config)) {
-		printk (KERN_WARNING "WaveFront: could not initialize MIDI.\n");
+	if (wavefront_config_midi ()) {
+		printk (KERN_WARNING LOGNAME "could not initialize MIDI.\n");
 	}
-#else
-	printk (KERN_WARNING
-		"WaveFront: MIDI not configured at kernel-config time.\n");
-#endif CONFIG_MIDI
 
-	return;
+	return dev.oss_dev;
 }
+
 void
-unload_wavefront (struct address_info *hw_config)
+uninstall_wavefront (void)
+
 {
-	struct wf_config *hw = &wavefront_configuration;
+	/* the first two i/o addresses are freed by the wf_mpu code */
+	release_region (dev.base+2, 6);
 
-	/* the first two are freed by the wf_mpu code */
-	release_region (hw->base+2, 6);
-	release_region (hw->base+8, 8);
-	sound_unload_synthdev (hw->synthdev);
-#if defined(CONFIG_MIDI)
-	unload_wf_mpu (hw_config);
-#endif
+	if (dev.has_fx) {
+		release_region (dev.base+8, 8);
+	}
+
+	unregister_sound_synth (dev.synth_dev);
+
+#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ
+	sound_unload_synthdev (dev.oss_dev);
+#endif OSS_SUPPORT_SEQ
+	uninstall_wf_mpu ();
 }
 
 /***********************************************************************/
@@ -2961,83 +2915,111 @@
 #define FX_AUTO_INCR    0x04    /* auto-increment DSP address after transfer */
 
 static int
-wffx_idle (struct wf_config *hw) 
+wffx_idle (void) 
     
 {
 	int i;
 	unsigned int x = 0x80;
     
 	for (i = 0; i < 1000; i++) {
-		x = inb (hw->fx_status);
+		x = inb (dev.fx_status);
 		if ((x & 0x80) == 0) {
 			break;
 		}
 	}
     
 	if (x & 0x80) {
-		printk (KERN_ERR "WaveFront: FX device never idle.\n");
+		printk (KERN_ERR LOGNAME "FX device never idle.\n");
 		return 0;
 	}
     
 	return (1);
 }
 
+__initfunc (static int detect_wffx (void))
+
+{
+	/* This is a crude check, but its the best one I have for now.
+	   Certainly on the Maui and the Tropez, wffx_idle() will
+	   report "never idle", which suggests that this test should
+	   work OK.
+	*/
+
+	if (inb (dev.fx_status) & 0x80) {
+		printk (KERN_INFO LOGNAME "Hmm, probably a Maui or Tropez.\n");
+		return -1;
+	}
+
+	return 0;
+}	
+
+__initfunc (static int attach_wffx (void))
+
+{
+	if ((dev.fx_mididev = sound_alloc_mididev ()) < 0) {
+		printk (KERN_WARNING LOGNAME "cannot install FX Midi driver\n");
+		return -1;
+	}
+
+	return 0;
+}
+
 static void
-wffx_mute (struct wf_config *hw, int onoff)
+wffx_mute (int onoff)
     
 {
-	if (!wffx_idle(hw)) {
+	if (!wffx_idle()) {
 		return;
 	}
     
-	outb (onoff ? 0x02 : 0x00, hw->fx_op);
+	outb (onoff ? 0x02 : 0x00, dev.fx_op);
 }
 
 static int
-wffx_memset (struct wf_config *hw, int page,
+wffx_memset (int page,
 	     int addr, int cnt, unsigned short *data)
 {
 	if (page < 0 || page > 7) {
-		printk (KERN_ERR "WaveFront: FX memset: "
+		printk (KERN_ERR LOGNAME "FX memset: "
 			"page must be >= 0 and <= 7\n");
 		return -(EINVAL);
 	}
 
 	if (addr < 0 || addr > 0x7f) {
-		printk (KERN_ERR "WaveFront: FX memset: "
+		printk (KERN_ERR LOGNAME "FX memset: "
 			"addr must be >= 0 and <= 7f\n");
 		return -(EINVAL);
 	}
 
 	if (cnt == 1) {
 
-		outb (FX_LSB_TRANSFER, hw->fx_lcr);
-		outb (page, hw->fx_dsp_page);
-		outb (addr, hw->fx_dsp_addr);
-		outb ((data[0] >> 8), hw->fx_dsp_msb);
-		outb ((data[0] & 0xff), hw->fx_dsp_lsb);
+		outb (FX_LSB_TRANSFER, dev.fx_lcr);
+		outb (page, dev.fx_dsp_page);
+		outb (addr, dev.fx_dsp_addr);
+		outb ((data[0] >> 8), dev.fx_dsp_msb);
+		outb ((data[0] & 0xff), dev.fx_dsp_lsb);
 
-		printk (KERN_INFO "WaveFront: FX: addr %d:%x set to 0x%x\n",
+		printk (KERN_INFO LOGNAME "FX: addr %d:%x set to 0x%x\n",
 			page, addr, data[0]);
 	
 	} else {
 		int i;
 
-		outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr);
-		outb (page, hw->fx_dsp_page);
-		outb (addr, hw->fx_dsp_addr);
+		outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+		outb (page, dev.fx_dsp_page);
+		outb (addr, dev.fx_dsp_addr);
 
 		for (i = 0; i < cnt; i++) {
-			outb ((data[i] >> 8), hw->fx_dsp_msb);
-			outb ((data[i] & 0xff), hw->fx_dsp_lsb);
-			if (!wffx_idle (hw)) {
+			outb ((data[i] >> 8), dev.fx_dsp_msb);
+			outb ((data[i] & 0xff), dev.fx_dsp_lsb);
+			if (!wffx_idle ()) {
 				break;
 			}
 		}
 
 		if (i != cnt) {
-			printk (KERN_WARNING
-				"WaveFront: FX memset "
+			printk (KERN_WARNING LOGNAME
+				"FX memset "
 				"(0x%x, 0x%x, 0x%x, %d) incomplete\n",
 				page, addr, (int) data, cnt);
 			return -(EIO);
@@ -3048,7 +3030,7 @@
 }
 
 static int
-wffx_ioctl (struct wf_config *hw, wavefront_fx_info *r)
+wffx_ioctl (wavefront_fx_info *r)
 
 {
 	unsigned short page_data[256];
@@ -3056,20 +3038,20 @@
 
 	switch (r->request) {
 	case WFFX_MUTE:
-		wffx_mute (hw, r->data[0]);
+		wffx_mute (r->data[0]);
 		return 0;
 
 	case WFFX_MEMSET:
 
 		if (r->data[2] <= 0) {
-			printk (KERN_ERR "WaveFront: cannot write "
+			printk (KERN_ERR LOGNAME "cannot write "
 				"<= 0 bytes to FX\n");
 			return -(EINVAL);
 		} else if (r->data[2] == 1) {
 			pd = (unsigned short *) &r->data[3];
 		} else {
 			if (r->data[2] > sizeof (page_data)) {
-				printk (KERN_ERR "WaveFront: cannot write "
+				printk (KERN_ERR LOGNAME "cannot write "
 					"> 255 bytes to FX\n");
 				return -(EINVAL);
 			}
@@ -3078,15 +3060,14 @@
 			pd = page_data;
 		}
 
-		return wffx_memset (hw,
-				    r->data[0], /* page */
+		return wffx_memset (r->data[0], /* page */
 				    r->data[1], /* addr */
 				    r->data[2], /* cnt */
 				    pd);
 
 	default:
-		printk (KERN_WARNING
-			"WaveFront: FX: ioctl %d not yet supported\n",
+		printk (KERN_WARNING LOGNAME
+			"FX: ioctl %d not yet supported\n",
 			r->request);
 		return -(EINVAL);
 	}
@@ -3107,7 +3088,7 @@
 */
 
 static int
-wffx_init (struct wf_config *hw)
+wffx_init (void)
 
 {
 	int i;
@@ -3119,147 +3100,147 @@
 	for (j = 0; j < 2; j++) {
 		for (i = 0x10; i <= 0xff; i++) {
 	    
-			if (!wffx_idle (hw)) {
+			if (!wffx_idle ()) {
 				return (-1);
 			}
 	    
-			outb (i, hw->fx_mod_addr);
-			outb (0x0, hw->fx_mod_data);
+			outb (i, dev.fx_mod_addr);
+			outb (0x0, dev.fx_mod_data);
 		}
 	}
 
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x02, hw->fx_op);                        /* mute on */
+	if (!wffx_idle()) return (-1);
+	outb (0x02, dev.fx_op);                        /* mute on */
 
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x44, hw->fx_dsp_addr);
-	outb (0x00, hw->fx_dsp_msb);
-	outb (0x00, hw->fx_dsp_lsb);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x42, hw->fx_dsp_addr);
-	outb (0x00, hw->fx_dsp_msb);
-	outb (0x00, hw->fx_dsp_lsb);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x43, hw->fx_dsp_addr);
-	outb (0x00, hw->fx_dsp_msb);
-	outb (0x00, hw->fx_dsp_lsb);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x7c, hw->fx_dsp_addr);
-	outb (0x00, hw->fx_dsp_msb);
-	outb (0x00, hw->fx_dsp_lsb);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x7e, hw->fx_dsp_addr);
-	outb (0x00, hw->fx_dsp_msb);
-	outb (0x00, hw->fx_dsp_lsb);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x46, hw->fx_dsp_addr);
-	outb (0x00, hw->fx_dsp_msb);
-	outb (0x00, hw->fx_dsp_lsb);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x49, hw->fx_dsp_addr);
-	outb (0x00, hw->fx_dsp_msb);
-	outb (0x00, hw->fx_dsp_lsb);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x47, hw->fx_dsp_addr);
-	outb (0x00, hw->fx_dsp_msb);
-	outb (0x00, hw->fx_dsp_lsb);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x4a, hw->fx_dsp_addr);
-	outb (0x00, hw->fx_dsp_msb);
-	outb (0x00, hw->fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x44, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x42, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x43, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x7c, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x7e, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x46, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x49, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x47, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x4a, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
 
 	/* either because of stupidity by TB's programmers, or because it
 	   actually does something, rezero the MOD page.
 	*/
 	for (i = 0x10; i <= 0xff; i++) {
 	
-		if (!wffx_idle (hw)) {
+		if (!wffx_idle ()) {
 			return (-1);
 		}
 	
-		outb (i, hw->fx_mod_addr);
-		outb (0x0, hw->fx_mod_data);
+		outb (i, dev.fx_mod_addr);
+		outb (0x0, dev.fx_mod_data);
 	}
 	/* load page zero */
 
-	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr);
-	outb (0x00, hw->fx_dsp_page);
-	outb (0x00, hw->fx_dsp_addr);
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x00, dev.fx_dsp_page);
+	outb (0x00, dev.fx_dsp_addr);
 
 	for (i = 0; i < sizeof (page_zero); i += 2) {
-		outb (page_zero[i], hw->fx_dsp_msb);
-		outb (page_zero[i+1], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (page_zero[i], dev.fx_dsp_msb);
+		outb (page_zero[i+1], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
 
 	/* Now load page one */
 
-	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr);
-	outb (0x01, hw->fx_dsp_page);
-	outb (0x00, hw->fx_dsp_addr);
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x01, dev.fx_dsp_page);
+	outb (0x00, dev.fx_dsp_addr);
 
 	for (i = 0; i < sizeof (page_one); i += 2) {
-		outb (page_one[i], hw->fx_dsp_msb);
-		outb (page_one[i+1], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (page_one[i], dev.fx_dsp_msb);
+		outb (page_one[i+1], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
     
-	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr);
-	outb (0x02, hw->fx_dsp_page);
-	outb (0x00, hw->fx_dsp_addr);
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x02, dev.fx_dsp_page);
+	outb (0x00, dev.fx_dsp_addr);
 
 	for (i = 0; i < sizeof (page_two); i++) {
-		outb (page_two[i], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (page_two[i], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
     
-	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr);
-	outb (0x03, hw->fx_dsp_page);
-	outb (0x00, hw->fx_dsp_addr);
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x03, dev.fx_dsp_page);
+	outb (0x00, dev.fx_dsp_addr);
 
 	for (i = 0; i < sizeof (page_three); i++) {
-		outb (page_three[i], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (page_three[i], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
     
-	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr);
-	outb (0x04, hw->fx_dsp_page);
-	outb (0x00, hw->fx_dsp_addr);
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x04, dev.fx_dsp_page);
+	outb (0x00, dev.fx_dsp_addr);
 
 	for (i = 0; i < sizeof (page_four); i++) {
-		outb (page_four[i], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (page_four[i], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
 
 	/* Load memory area (page six) */
     
-	outb (FX_LSB_TRANSFER, hw->fx_lcr); 
-	outb (0x06, hw->fx_dsp_page); 
+	outb (FX_LSB_TRANSFER, dev.fx_lcr); 
+	outb (0x06, dev.fx_dsp_page); 
 
 	for (i = 0; i < sizeof (page_six); i += 3) {
-		outb (page_six[i], hw->fx_dsp_addr);
-		outb (page_six[i+1], hw->fx_dsp_msb);
-		outb (page_six[i+2], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (page_six[i], dev.fx_dsp_addr);
+		outb (page_six[i+1], dev.fx_dsp_msb);
+		outb (page_six[i+2], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
     
-	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x00, hw->fx_dsp_addr);
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x00, dev.fx_dsp_addr);
 
 	for (i = 0; i < sizeof (page_seven); i += 2) {
-		outb (page_seven[i], hw->fx_dsp_msb);
-		outb (page_seven[i+1], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (page_seven[i], dev.fx_dsp_msb);
+		outb (page_seven[i+1], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
 
 	/* Now setup the MOD area. We do this algorithmically in order to
@@ -3268,192 +3249,192 @@
 	*/
 
 	for (i = 0x00; i <= 0x0f; i++) {
-		outb (0x01, hw->fx_mod_addr);
-		outb (i, hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
-		outb (0x02, hw->fx_mod_addr);
-		outb (0x00, hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
+		outb (0x01, dev.fx_mod_addr);
+		outb (i, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+		outb (0x02, dev.fx_mod_addr);
+		outb (0x00, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
 	}
 
 	for (i = 0xb0; i <= 0xbf; i++) {
-		outb (i, hw->fx_mod_addr);
-		outb (0x20, hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
+		outb (i, dev.fx_mod_addr);
+		outb (0x20, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
 	}
 
 	for (i = 0xf0; i <= 0xff; i++) {
-		outb (i, hw->fx_mod_addr);
-		outb (0x20, hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
+		outb (i, dev.fx_mod_addr);
+		outb (0x20, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
 	}
 
 	for (i = 0x10; i <= 0x1d; i++) {
-		outb (i, hw->fx_mod_addr);
-		outb (0xff, hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
+		outb (i, dev.fx_mod_addr);
+		outb (0xff, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
 	}
 
-	outb (0x1e, hw->fx_mod_addr);
-	outb (0x40, hw->fx_mod_data);
-	if (!wffx_idle(hw)) return (-1);
+	outb (0x1e, dev.fx_mod_addr);
+	outb (0x40, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
 
 	for (i = 0x1f; i <= 0x2d; i++) {
-		outb (i, hw->fx_mod_addr);
-		outb (0xff, hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
+		outb (i, dev.fx_mod_addr);
+		outb (0xff, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
 	}
 
-	outb (0x2e, hw->fx_mod_addr);
-	outb (0x00, hw->fx_mod_data);
-	if (!wffx_idle(hw)) return (-1);
+	outb (0x2e, dev.fx_mod_addr);
+	outb (0x00, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
 
 	for (i = 0x2f; i <= 0x3e; i++) {
-		outb (i, hw->fx_mod_addr);
-		outb (0x00, hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
+		outb (i, dev.fx_mod_addr);
+		outb (0x00, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
 	}
 
-	outb (0x3f, hw->fx_mod_addr);
-	outb (0x20, hw->fx_mod_data);
-	if (!wffx_idle(hw)) return (-1);
+	outb (0x3f, dev.fx_mod_addr);
+	outb (0x20, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
 
 	for (i = 0x40; i <= 0x4d; i++) {
-		outb (i, hw->fx_mod_addr);
-		outb (0x00, hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
+		outb (i, dev.fx_mod_addr);
+		outb (0x00, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
 	}
 
-	outb (0x4e, hw->fx_mod_addr);
-	outb (0x0e, hw->fx_mod_data);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x4f, hw->fx_mod_addr);
-	outb (0x0e, hw->fx_mod_data);
-	if (!wffx_idle(hw)) return (-1);
+	outb (0x4e, dev.fx_mod_addr);
+	outb (0x0e, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+	outb (0x4f, dev.fx_mod_addr);
+	outb (0x0e, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
 
 
 	for (i = 0x50; i <= 0x6b; i++) {
-		outb (i, hw->fx_mod_addr);
-		outb (0x00, hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
-	}
-
-	outb (0x6c, hw->fx_mod_addr);
-	outb (0x40, hw->fx_mod_data);
-	if (!wffx_idle(hw)) return (-1);
-
-	outb (0x6d, hw->fx_mod_addr);
-	outb (0x00, hw->fx_mod_data);
-	if (!wffx_idle(hw)) return (-1);
-
-	outb (0x6e, hw->fx_mod_addr);
-	outb (0x40, hw->fx_mod_data);
-	if (!wffx_idle(hw)) return (-1);
-
-	outb (0x6f, hw->fx_mod_addr);
-	outb (0x40, hw->fx_mod_data);
-	if (!wffx_idle(hw)) return (-1);
+		outb (i, dev.fx_mod_addr);
+		outb (0x00, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
+	}
+
+	outb (0x6c, dev.fx_mod_addr);
+	outb (0x40, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+
+	outb (0x6d, dev.fx_mod_addr);
+	outb (0x00, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+
+	outb (0x6e, dev.fx_mod_addr);
+	outb (0x40, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+
+	outb (0x6f, dev.fx_mod_addr);
+	outb (0x40, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
 
 	for (i = 0x70; i <= 0x7f; i++) {
-		outb (i, hw->fx_mod_addr);
-		outb (0xc0, hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
+		outb (i, dev.fx_mod_addr);
+		outb (0xc0, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
 	}
     
 	for (i = 0x80; i <= 0xaf; i++) {
-		outb (i, hw->fx_mod_addr);
-		outb (0x00, hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
+		outb (i, dev.fx_mod_addr);
+		outb (0x00, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
 	}
 
 	for (i = 0xc0; i <= 0xdd; i++) {
-		outb (i, hw->fx_mod_addr);
-		outb (0x00, hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
+		outb (i, dev.fx_mod_addr);
+		outb (0x00, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
 	}
 
-	outb (0xde, hw->fx_mod_addr);
-	outb (0x10, hw->fx_mod_data);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0xdf, hw->fx_mod_addr);
-	outb (0x10, hw->fx_mod_data);
-	if (!wffx_idle(hw)) return (-1);
+	outb (0xde, dev.fx_mod_addr);
+	outb (0x10, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+	outb (0xdf, dev.fx_mod_addr);
+	outb (0x10, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
 
 	for (i = 0xe0; i <= 0xef; i++) {
-		outb (i, hw->fx_mod_addr);
-		outb (0x00, hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
+		outb (i, dev.fx_mod_addr);
+		outb (0x00, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
 	}
 
 	for (i = 0x00; i <= 0x0f; i++) {
-		outb (0x01, hw->fx_mod_addr);
-		outb (i, hw->fx_mod_data);
-		outb (0x02, hw->fx_mod_addr);
-		outb (0x01, hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
+		outb (0x01, dev.fx_mod_addr);
+		outb (i, dev.fx_mod_data);
+		outb (0x02, dev.fx_mod_addr);
+		outb (0x01, dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
 	}
 
-	outb (0x02, hw->fx_op); /* mute on */
+	outb (0x02, dev.fx_op); /* mute on */
 
 	/* Now set the coefficients and so forth for the programs above */
 
 	for (i = 0; i < sizeof (coefficients); i += 4) {
-		outb (coefficients[i], hw->fx_dsp_page);
-		outb (coefficients[i+1], hw->fx_dsp_addr);
-		outb (coefficients[i+2], hw->fx_dsp_msb);
-		outb (coefficients[i+3], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (coefficients[i], dev.fx_dsp_page);
+		outb (coefficients[i+1], dev.fx_dsp_addr);
+		outb (coefficients[i+2], dev.fx_dsp_msb);
+		outb (coefficients[i+3], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
 
 	/* Some settings (?) that are too small to bundle into loops */
 
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x1e, hw->fx_mod_addr);
-	outb (0x14, hw->fx_mod_data);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0xde, hw->fx_mod_addr);
-	outb (0x20, hw->fx_mod_data);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0xdf, hw->fx_mod_addr);
-	outb (0x20, hw->fx_mod_data);
+	if (!wffx_idle()) return (-1);
+	outb (0x1e, dev.fx_mod_addr);
+	outb (0x14, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+	outb (0xde, dev.fx_mod_addr);
+	outb (0x20, dev.fx_mod_data);
+	if (!wffx_idle()) return (-1);
+	outb (0xdf, dev.fx_mod_addr);
+	outb (0x20, dev.fx_mod_data);
     
 	/* some more coefficients */
 
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x06, hw->fx_dsp_page);
-	outb (0x78, hw->fx_dsp_addr);
-	outb (0x00, hw->fx_dsp_msb);
-	outb (0x40, hw->fx_dsp_lsb);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x03, hw->fx_dsp_addr);
-	outb (0x0f, hw->fx_dsp_msb);
-	outb (0xff, hw->fx_dsp_lsb);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x0b, hw->fx_dsp_addr);
-	outb (0x0f, hw->fx_dsp_msb);
-	outb (0xff, hw->fx_dsp_lsb);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x02, hw->fx_dsp_addr);
-	outb (0x00, hw->fx_dsp_msb);
-	outb (0x00, hw->fx_dsp_lsb);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x0a, hw->fx_dsp_addr);
-	outb (0x00, hw->fx_dsp_msb);
-	outb (0x00, hw->fx_dsp_lsb);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x46, hw->fx_dsp_addr);
-	outb (0x00, hw->fx_dsp_msb);
-	outb (0x00, hw->fx_dsp_lsb);
-	if (!wffx_idle(hw)) return (-1);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x49, hw->fx_dsp_addr);
-	outb (0x00, hw->fx_dsp_msb);
-	outb (0x00, hw->fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x06, dev.fx_dsp_page);
+	outb (0x78, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x40, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x03, dev.fx_dsp_addr);
+	outb (0x0f, dev.fx_dsp_msb);
+	outb (0xff, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x0b, dev.fx_dsp_addr);
+	outb (0x0f, dev.fx_dsp_msb);
+	outb (0xff, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x02, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x0a, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x46, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
+	if (!wffx_idle()) return (-1);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x49, dev.fx_dsp_addr);
+	outb (0x00, dev.fx_dsp_msb);
+	outb (0x00, dev.fx_dsp_lsb);
     
 	/* Now, for some strange reason, lets reload every page
 	   and all the coefficients over again. I have *NO* idea
@@ -3461,117 +3442,116 @@
 	   is this phase is omitted.
 	*/
 
-	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr);
-	outb (0x00, hw->fx_dsp_page);  
-	outb (0x10, hw->fx_dsp_addr);
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x00, dev.fx_dsp_page);  
+	outb (0x10, dev.fx_dsp_addr);
 
 	for (i = 0; i < sizeof (page_zero_v2); i += 2) {
-		outb (page_zero_v2[i], hw->fx_dsp_msb);
-		outb (page_zero_v2[i+1], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (page_zero_v2[i], dev.fx_dsp_msb);
+		outb (page_zero_v2[i+1], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
     
-	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr);
-	outb (0x01, hw->fx_dsp_page);
-	outb (0x10, hw->fx_dsp_addr);
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x01, dev.fx_dsp_page);
+	outb (0x10, dev.fx_dsp_addr);
 
 	for (i = 0; i < sizeof (page_one_v2); i += 2) {
-		outb (page_one_v2[i], hw->fx_dsp_msb);
-		outb (page_one_v2[i+1], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (page_one_v2[i], dev.fx_dsp_msb);
+		outb (page_one_v2[i+1], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
     
-	if (!wffx_idle(hw)) return (-1);
-	if (!wffx_idle(hw)) return (-1);
+	if (!wffx_idle()) return (-1);
+	if (!wffx_idle()) return (-1);
     
-	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr);
-	outb (0x02, hw->fx_dsp_page);
-	outb (0x10, hw->fx_dsp_addr);
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x02, dev.fx_dsp_page);
+	outb (0x10, dev.fx_dsp_addr);
 
 	for (i = 0; i < sizeof (page_two_v2); i++) {
-		outb (page_two_v2[i], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (page_two_v2[i], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
-	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr);
-	outb (0x03, hw->fx_dsp_page);
-	outb (0x10, hw->fx_dsp_addr);
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x03, dev.fx_dsp_page);
+	outb (0x10, dev.fx_dsp_addr);
 
 	for (i = 0; i < sizeof (page_three_v2); i++) {
-		outb (page_three_v2[i], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (page_three_v2[i], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
     
-	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr);
-	outb (0x04, hw->fx_dsp_page);
-	outb (0x10, hw->fx_dsp_addr);
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x04, dev.fx_dsp_page);
+	outb (0x10, dev.fx_dsp_addr);
 
 	for (i = 0; i < sizeof (page_four_v2); i++) {
-		outb (page_four_v2[i], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (page_four_v2[i], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
     
-	outb (FX_LSB_TRANSFER, hw->fx_lcr);
-	outb (0x06, hw->fx_dsp_page);
+	outb (FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x06, dev.fx_dsp_page);
 
 	/* Page six v.2 is algorithmic */
     
 	for (i = 0x10; i <= 0x3e; i += 2) {
-		outb (i, hw->fx_dsp_addr);
-		outb (0x00, hw->fx_dsp_msb);
-		outb (0x00, hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (i, dev.fx_dsp_addr);
+		outb (0x00, dev.fx_dsp_msb);
+		outb (0x00, dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
 
-	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr);
-	outb (0x07, hw->fx_dsp_page);
-	outb (0x10, hw->fx_dsp_addr);
+	outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr);
+	outb (0x07, dev.fx_dsp_page);
+	outb (0x10, dev.fx_dsp_addr);
 
 	for (i = 0; i < sizeof (page_seven_v2); i += 2) {
-		outb (page_seven_v2[i], hw->fx_dsp_msb);
-		outb (page_seven_v2[i+1], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (page_seven_v2[i], dev.fx_dsp_msb);
+		outb (page_seven_v2[i+1], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
 
 	for (i = 0x00; i < sizeof(mod_v2); i += 2) {
-		outb (mod_v2[i], hw->fx_mod_addr);
-		outb (mod_v2[i+1], hw->fx_mod_data);
-		if (!wffx_idle(hw)) return (-1);
+		outb (mod_v2[i], dev.fx_mod_addr);
+		outb (mod_v2[i+1], dev.fx_mod_data);
+		if (!wffx_idle()) return (-1);
 	}
 
 	for (i = 0; i < sizeof (coefficients2); i += 4) {
-		outb (coefficients2[i], hw->fx_dsp_page);
-		outb (coefficients2[i+1], hw->fx_dsp_addr);
-		outb (coefficients2[i+2], hw->fx_dsp_msb);
-		outb (coefficients2[i+3], hw->fx_dsp_lsb);
-		if (!wffx_idle(hw)) return (-1);
+		outb (coefficients2[i], dev.fx_dsp_page);
+		outb (coefficients2[i+1], dev.fx_dsp_addr);
+		outb (coefficients2[i+2], dev.fx_dsp_msb);
+		outb (coefficients2[i+3], dev.fx_dsp_lsb);
+		if (!wffx_idle()) return (-1);
 	}
 
-	outb (0x00, hw->fx_op);
-	if (!wffx_idle(hw)) return (-1);
-    
 	for (i = 0; i < sizeof (coefficients3); i += 2) {
 		int x;
 
-		outb (0x07, hw->fx_dsp_page);
+		outb (0x07, dev.fx_dsp_page);
 		x = (i % 4) ? 0x4e : 0x4c;
-		outb (x, hw->fx_dsp_addr);
-		outb (coefficients3[i], hw->fx_dsp_msb);
-		outb (coefficients3[i+1], hw->fx_dsp_lsb);
+		outb (x, dev.fx_dsp_addr);
+		outb (coefficients3[i], dev.fx_dsp_msb);
+		outb (coefficients3[i+1], dev.fx_dsp_lsb);
 	}
 
-	outb (0x00, hw->fx_op); /* mute off */
+	outb (0x00, dev.fx_op); /* mute off */
+	if (!wffx_idle()) return (-1);
 
 	return (0);
 }
 
 EXPORT_NO_SYMBOLS;
-struct address_info cfg;
 
 int io = -1;
 int irq = -1;
 
-MODULE_PARM(io,"i");
-MODULE_PARM(irq,"i");
+MODULE_AUTHOR      ("Paul Barton-Davis <pbd@op.net>");
+MODULE_DESCRIPTION ("Turtle Beach WaveFront Linux Driver");
+MODULE_PARM        (io,"i");
+MODULE_PARM        (irq,"i");
 
 int init_module (void)
 
@@ -3580,19 +3560,27 @@
 		"Copyright (C) by Hannu Solvainen, "
 		"Paul Barton-Davis 1993-1998.\n");
 
+	/* XXX t'would be lovely to ask the CS4232 for these values, eh ? */
+
 	if (io == -1 || irq == -1) {
-		printk (KERN_INFO "WaveFront: irq and io "
-			"options must be set.\n");
+		printk (KERN_INFO LOGNAME "irq and io options must be set.\n");
 		return -EINVAL;
 	}
 
-	cfg.io_base = io;
-	cfg.irq = irq;
+	if (wavefront_interrupt_bits (irq) < 0) {
+		printk (KERN_INFO LOGNAME
+			"IRQ must be 9, 5, 12 or 15 (not %d)\n", irq);
+		return -ENODEV;
+	}
 
-	if (probe_wavefront (&cfg) == 0) {
+	if (detect_wavefront (irq, io) < 0) {
 		return -ENODEV;
 	} 
-	attach_wavefront (&cfg);
+
+	if (install_wavefront () < 0) {
+		return -EIO;
+	}
+
 	SOUND_LOCK;
 	return 0;
 }
@@ -3600,10 +3588,11 @@
 void cleanup_module (void)
 
 {
-	unload_wavefront (&cfg);
+	uninstall_wavefront ();
+
 	SOUND_LOCK_END;
 }
 
-#endif CONFIG_SOUND_WAVEFRONT_MODULE_AND_MODULE
+#endif CONFIG_SOUND_WAVEFRONT_MODULE && MODULE
 
 
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/sound/wf_midi.c linux/drivers/sound/wf_midi.c
--- v2.2.0-pre4/linux/drivers/sound/wf_midi.c	Thu Nov 12 16:21:22 1998
+++ linux/drivers/sound/wf_midi.c	Mon Jan  4 11:37:30 1999
@@ -40,7 +40,7 @@
 
 /*
  * Copyright (C) by Paul Barton-Davis 1998
- * Substantial portions of this file are derived from work that is:
+ * Some portions of this file are derived from work that is:
  *
  *    Copyright (C) by Hannu Savolainen 1993-1996
  *
@@ -50,17 +50,22 @@
  */
 
 #include <linux/config.h>
+#include <linux/init.h>
 #include "sound_config.h"
 #include "soundmodule.h"
 
 #include <linux/wavefront.h>
 
-#if (defined(CONFIG_WAVEFRONT) && defined(CONFIG_MIDI)) || defined(MODULE)
+#if defined(CONFIG_SOUND_WAVEFRONT_MODULE) && defined(MODULE)
 
 struct wf_mpu_config {
-	int             base;	/* I/O base */
+	int             base;
+#define	DATAPORT(d)   (d)->base
+#define	COMDPORT(d)   (d)->base+1
+#define	STATPORT(d)   (d)->base+1
+
 	int             irq;
-	int             opened;	/* Open mode */
+	int             opened;
 	int             devno;
 	int             synthno;
 	int             mode;
@@ -68,69 +73,54 @@
 #define MODE_SYNTH	2
 
 	void            (*inputintr) (int dev, unsigned char data);
-    
-	/* Virtual MIDI support */
-    
-	char configured_for_virtual;   /* setup for virtual completed */
 	char isvirtual;                /* do virtual I/O stuff */
-	char isexternal;               /* i am an external interface */
-	int internal;                  /* external interface midi_devno */
-	int external;                  /* external interface midi_devno */
 };
 
-#define	DATAPORT(base)   (base)
-#define	COMDPORT(base)   (base+1)
-#define	STATPORT(base)   (base+1)
+static struct wf_mpu_config  devs[2];
+static struct wf_mpu_config *phys_dev = &devs[0];
+static struct wf_mpu_config *virt_dev = &devs[1];
 
-static void     start_uart_mode (struct wf_mpu_config *devc);
+static void start_uart_mode (void);
 
-static int 
-wf_mpu_status (struct wf_mpu_config *devc)
-{
-	return inb (STATPORT (devc->base));
-}
+#define	OUTPUT_READY	0x40
+#define	INPUT_AVAIL	0x80
+#define	MPU_ACK		0xFE
+#define	UART_MODE_ON	0x3F
+
+static inline int 
+wf_mpu_status (void)
 
-static void 
-wf_mpu_cmd (struct wf_mpu_config *devc, unsigned char cmd)
 {
-	outb ((cmd), COMDPORT(devc->base));
+	return inb (STATPORT (phys_dev));
 }
 
-#define input_avail(devc)		(!(wf_mpu_status(devc)&INPUT_AVAIL))
-#define output_ready(devc)		(!(wf_mpu_status(devc)&OUTPUT_READY))
+static inline int 
+input_avail (void)
 
-static int 
-read_data (struct wf_mpu_config *devc)
 {
-	return inb (DATAPORT (devc->base));
+	return !(wf_mpu_status() & INPUT_AVAIL);
 }
 
-static void 
-write_data (struct wf_mpu_config *devc, unsigned char byte)
+static inline int
+output_ready (void)
+
 {
-	outb (byte, DATAPORT (devc->base));
+	return !(wf_mpu_status() & OUTPUT_READY);
 }
 
-#define	OUTPUT_READY	0x40
-#define	INPUT_AVAIL	0x80
-#define	MPU_ACK		0xFE
-#define	MPU_RESET	0xFF
-#define	UART_MODE_ON	0x3F
+static inline int 
+read_data (void)
 
-static struct wf_mpu_config dev_conf[MAX_MIDI_DEV] =
 {
-	{0}
-};
-
-static volatile int irq2dev[17] =
-{-1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1};
+	return inb (DATAPORT (phys_dev));
+}
 
-static struct synth_info wf_mpu_synth_info_proto =
-{"WaveFront MPU-401 interface", 0,
- SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT};
+static inline void 
+write_data (unsigned char byte)
 
-static struct synth_info wf_mpu_synth_info[MAX_MIDI_DEV];
+{
+	outb (byte, DATAPORT (phys_dev));
+}
 
 /*
  * States for the input scanner (should be in dev_table.h)
@@ -162,11 +152,10 @@
 };
 
 static int
-wf_mpu_input_scanner (struct wf_mpu_config *devc, unsigned char midic)
-{
-	struct midi_input_info *mi;
+wf_mpu_input_scanner (int devno, int synthdev, unsigned char midic)
 
-	mi = &midi_devs[devc->devno]->in_info;
+{
+	struct midi_input_info *mi = &midi_devs[devno]->in_info;
 
 	switch (mi->m_state) {
 	case MST_INIT:
@@ -236,8 +225,7 @@
 
 			if (mi->m_left <= 0) {
 				mi->m_state = MST_INIT;
-				do_midi_msg (devc->synthno, mi->m_buf,
-					     mi->m_ptr);
+				do_midi_msg (synthdev, mi->m_buf, mi->m_ptr);
 				mi->m_ptr = 0;
 			}
 		} else if (msg == 0xf) {	/* MPU MARK */
@@ -266,8 +254,7 @@
 	      
 			if (mi->m_left <= 0) {
 				mi->m_state = MST_INIT;
-				do_midi_msg (devc->synthno, mi->m_buf,
-					     mi->m_ptr);
+				do_midi_msg (synthdev, mi->m_buf, mi->m_ptr);
 				mi->m_ptr = 0;
 			}
 		}
@@ -362,7 +349,7 @@
 		mi->m_buf[mi->m_ptr++] = midic;
 		if ((--mi->m_left) <= 0) {
 			mi->m_state = MST_INIT;
-			do_midi_msg (devc->synthno, mi->m_buf, mi->m_ptr);
+			do_midi_msg (synthdev, mi->m_buf, mi->m_ptr);
 			mi->m_ptr = 0;
 		}
 		break;
@@ -375,78 +362,65 @@
 	return 1;
 }
 
-void wf_mpuintr (int irq, void *dev_id, struct pt_regs *dummy)
+void
+wf_mpuintr (int irq, void *dev_id, struct pt_regs *dummy)
+
 {
-	struct wf_mpu_config *devc;
-	int dev;
-	static struct wf_mpu_config *isrc = 0;
+	struct wf_mpu_config *physical_dev = dev_id;
+	static struct wf_mpu_config *input_dev = 0;
+	struct midi_input_info *mi = &midi_devs[physical_dev->devno]->in_info;
 	int n;
-	struct midi_input_info *mi;
 
-	if (irq < 0 || irq > 15) 
-	{
-		printk (KERN_ERR "WF-MPU: bogus interrupt #%d", irq);
+	if (!input_avail()) { /* not for us */
 		return;
 	}
-	dev = irq2dev[irq];
-	mi = &midi_devs[dev]->in_info;
-	if (mi->m_busy)
-		return;
+
+	if (mi->m_busy) return;
 	mi->m_busy = 1;
-	
 	sti (); 
 
-	n = 50;
-
-	/* guarantee that we're working with the "real" (internal)
-	   interface before doing anything physical.
-	*/
+	if (!input_dev) {
+		input_dev = physical_dev;
+	}
 
-	devc = &dev_conf[dev];
-	devc = &dev_conf[devc->internal];
+	n = 50; /* XXX why ? */
 
-	if (isrc == 0) {
-      
-		/* This is just an initial setting. If Virtual MIDI mode is
-		   enabled on the ICS2115, we'll get a switch char before
-		   anything else, and if it isn't, then the guess will be
-		   correct for our purposes.
-		*/
-      
-		isrc = &dev_conf[devc->internal];
-	}
-  
-	while (input_avail (devc) && n-- > 0) {
-		unsigned char c = read_data (devc);
+	do {
+		unsigned char c = read_data ();
       
-		if (devc->isvirtual) {
+		if (phys_dev->isvirtual) {
+
 			if (c == WF_EXTERNAL_SWITCH) {
-				isrc = &dev_conf[devc->external];
+				input_dev = virt_dev;
 				continue;
 			} else if (c == WF_INTERNAL_SWITCH) { 
-				isrc = &dev_conf[devc->internal];
+				input_dev = phys_dev;
 				continue;
 			} /* else just leave it as it is */
+
 		} else {
-			isrc = &dev_conf[devc->internal];
+			input_dev = phys_dev;
 		}
 
-		if (isrc->mode == MODE_SYNTH) {
+		if (input_dev->mode == MODE_SYNTH) {
 	  
-			wf_mpu_input_scanner (isrc, c);
+			wf_mpu_input_scanner (input_dev->devno,
+					      input_dev->synthno, c);
 	  
-		} else if (isrc->opened & OPEN_READ) {
+		} else if (input_dev->opened & OPEN_READ) {
 	  
-			if (isrc->inputintr) {
-				isrc->inputintr (isrc->devno, c);
+			if (input_dev->inputintr) {
+				input_dev->inputintr (input_dev->devno, c);
 			} 
 		}
-	}
+
+	} while (input_avail() && n-- > 0);
 
 	mi->m_busy = 0;
 }
 
-static int wf_mpu_open (int dev, int mode,
+static int
+wf_mpu_open (int dev, int mode,
 	     void            (*input) (int dev, unsigned char data),
 	     void            (*output) (int dev)
 	)
@@ -456,7 +430,14 @@
 	if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL)
 		return -(ENXIO);
 
-	devc = &dev_conf[dev];
+	if (phys_dev->devno == dev) {
+		devc = phys_dev;
+	} else if (phys_dev->isvirtual && virt_dev->devno == dev) {
+		devc = virt_dev;
+	} else {
+		printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev);
+		return -(EINVAL);
+	}
 
 	if (devc->opened) {
 		return -(EBUSY);
@@ -475,7 +456,18 @@
 {
 	struct wf_mpu_config *devc;
 
-	devc = &dev_conf[dev];
+	if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL)
+		return;
+
+	if (phys_dev->devno == dev) {
+		devc = phys_dev;
+	} else if (phys_dev->isvirtual && virt_dev->devno == dev) {
+		devc = virt_dev;
+	} else {
+		printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev);
+		return;
+	}
+
 	devc->mode = 0;
 	devc->inputintr = NULL;
 	devc->opened = 0;
@@ -487,40 +479,35 @@
 	int             timeout;
 	unsigned long   flags;
 	static int lastoutdev = -1;
-
-	struct wf_mpu_config *devc;
 	unsigned char switchch;
 
-	/* The actual output has to occur using the "internal" config info
-	 */
-  
-	devc = &dev_conf[dev_conf[dev].internal];
-  
-	if (devc->isvirtual && lastoutdev != dev) {
+	if (phys_dev->isvirtual && lastoutdev != dev) {
       
-		if (dev == devc->internal) { 
+		if (dev == phys_dev->devno) { 
 			switchch = WF_INTERNAL_SWITCH;
-		} else if (dev == devc->external) { 
+		} else if (dev == virt_dev->devno) { 
 			switchch = WF_EXTERNAL_SWITCH;
 		} else {
 			printk (KERN_ERR "WF-MPU: bad device number %d", dev);
 			return (0);
 		}
+
+		/* XXX fix me */
       
-		for (timeout = 30000; timeout > 0 && !output_ready (devc);
+		for (timeout = 30000; timeout > 0 && !output_ready ();
 		     timeout--);
       
 		save_flags (flags);
 		cli ();
       
-		if (!output_ready (devc)) {
+		if (!output_ready ()) {
 			printk (KERN_WARNING "WF-MPU: Send switch "
 				"byte timeout\n");
 			restore_flags (flags);
 			return 0;
 		}
       
-		write_data (devc, switchch);
+		write_data (switchch);
 		restore_flags (flags);
 	} 
 
@@ -531,17 +518,19 @@
 	 * (After reset). Normally it takes just about 10 loops.
 	 */
 
-	for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--);
+	/* XXX fix me */
+
+	for (timeout = 30000; timeout > 0 && !output_ready (); timeout--);
 
 	save_flags (flags);
 	cli ();
-	if (!output_ready (devc)) {
+	if (!output_ready ()) {
 		printk (KERN_WARNING "WF-MPU: Send data timeout\n");
 		restore_flags (flags);
 		return 0;
 	}
 
-	write_data (devc, midi_byte);
+	write_data (midi_byte);
 	restore_flags (flags);
 
 	return 1;
@@ -567,35 +556,63 @@
 	return -(EINVAL);
 }
 
-static void
-wf_mpu_kick (int dev)
-{
-}
-
 static int
 wf_mpu_buffer_status (int dev)
 {
-	return 0;			/*
-					 * No data in buffers
-					 */
+	return 0;
 }
 
+static struct synth_operations wf_mpu_synth_operations[2];
+static struct midi_operations  wf_mpu_midi_operations[2];
+
+static struct midi_operations wf_mpu_midi_proto =
+{
+	{"WF-MPU MIDI", 0, MIDI_CAP_MPU401, SNDCARD_MPU401},
+	NULL,  /*converter*/
+	{0},   /* in_info */
+	wf_mpu_open,
+	wf_mpu_close,
+	wf_mpu_ioctl,
+	wf_mpu_out,
+	wf_mpu_start_read,
+	wf_mpu_end_read,
+	NULL,
+	NULL,
+	wf_mpu_buffer_status,
+	NULL
+};
+
+static struct synth_info wf_mpu_synth_info_proto =
+{"WaveFront MPU-401 interface", 0,
+ SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT};
+
+static struct synth_info wf_mpu_synth_info[2];
+
 static int
 wf_mpu_synth_ioctl (int dev,
 		    unsigned int cmd, caddr_t arg)
 {
 	int             midi_dev;
+	int index;
 
 	midi_dev = synth_devs[dev]->midi_dev;
 
 	if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL)
 		return -(ENXIO);
 
+	if (midi_dev == phys_dev->devno) {
+		index = 0;
+	} else if (phys_dev->isvirtual && midi_dev == virt_dev->devno) {
+		index = 1;
+	} else {
+		return -(EINVAL);
+	}
+
 	switch (cmd) {
 
 	case SNDCTL_SYNTH_INFO:
 		copy_to_user (&((char *) arg)[0],
-			      &wf_mpu_synth_info[midi_dev],
+			      &wf_mpu_synth_info[index],
 			      sizeof (struct synth_info));
 	
 		return 0;
@@ -622,7 +639,15 @@
 		return -(ENXIO);
 	}
   
-	devc = &dev_conf[midi_dev];
+	if (phys_dev->devno == midi_dev) {
+		devc = phys_dev;
+	} else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) {
+		devc = virt_dev;
+	} else {
+		printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev);
+		return -(EINVAL);
+	}
+
 	if (devc->opened) {
 		return -(EBUSY);
 	}
@@ -642,7 +667,15 @@
 
 	midi_dev = synth_devs[dev]->midi_dev;
 
-	devc = &dev_conf[midi_dev];
+	if (phys_dev->devno == midi_dev) {
+		devc = phys_dev;
+	} else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) {
+		devc = virt_dev;
+	} else {
+		printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev);
+		return;
+	}
+
 	devc->inputintr = NULL;
 	devc->opened = 0;
 	devc->mode = 0;
@@ -678,233 +711,145 @@
 	midi_synth_send_sysex
 };
 
-static struct synth_operations wf_mpu_synth_operations[2];
-static struct midi_operations  wf_mpu_midi_operations[2];
-static int wfmpu_cnt = 0;
-
-static struct midi_operations wf_mpu_midi_proto =
-{
-	{"WF-MPU MIDI", 0, MIDI_CAP_MPU401, SNDCARD_MPU401},
-	NULL,  /*converter*/
-	{0},   /* in_info */
-	wf_mpu_open,
-	wf_mpu_close,
-	wf_mpu_ioctl,
-	wf_mpu_out,
-	wf_mpu_start_read,
-	wf_mpu_end_read,
-	wf_mpu_kick,
-	NULL,
-	wf_mpu_buffer_status,
-	NULL
-};
-
-
 static int
-config_wf_mpu (int dev, struct address_info *hw_config)
+config_wf_mpu (struct wf_mpu_config *dev)
 
 {
-	struct wf_mpu_config *devc;
-	int                internal;
-
-	if (wfmpu_cnt >= 2) {
-		printk (KERN_ERR "WF-MPU: more MPU devices than cards ?!!\n");
-		return (-1);
-	}
-  
-	/* There is no synth available on the external interface,
-	   so do the synth stuff to the internal interface only.
-	*/
-
-	internal = dev_conf[dev].internal;
-	devc = &dev_conf[internal];
-
-	if (!dev_conf[dev].isexternal) {
-		memcpy ((char *) &wf_mpu_synth_operations[wfmpu_cnt],
+	int is_external;
+	char *name;
+	int index;
+
+	if (dev == phys_dev) {
+		name = "WaveFront internal MIDI";
+		is_external = 0;
+		index = 0;
+		memcpy ((char *) &wf_mpu_synth_operations[index],
 			(char *) &wf_mpu_synth_proto,
 			sizeof (struct synth_operations));
-	}
-
-	memcpy ((char *) &wf_mpu_midi_operations[wfmpu_cnt],
-		(char *) &wf_mpu_midi_proto,
-		sizeof (struct midi_operations));
-  
-	if (dev_conf[dev].isexternal) {
-		wf_mpu_midi_operations[wfmpu_cnt].converter = NULL;
 	} else {
-		wf_mpu_midi_operations[wfmpu_cnt].converter =
-			&wf_mpu_synth_operations[wfmpu_cnt];
+		name = "WaveFront external MIDI";
+		is_external = 1;
+		index = 1;
+		/* no synth operations for an external MIDI interface */
 	}
 
-	memcpy ((char *) &wf_mpu_synth_info[dev],
+	memcpy ((char *) &wf_mpu_synth_info[dev->devno],
 		(char *) &wf_mpu_synth_info_proto,
 		sizeof (struct synth_info));
 
-	strcpy (wf_mpu_synth_info[dev].name, hw_config->name);
-	strcpy (wf_mpu_midi_operations[wfmpu_cnt].info.name, hw_config->name);
+	strcpy (wf_mpu_synth_info[index].name, name);
 
-	conf_printf (hw_config->name, hw_config);
+	wf_mpu_synth_operations[index].midi_dev = dev->devno;
+	wf_mpu_synth_operations[index].info = &wf_mpu_synth_info[index];
 
-	if (!dev_conf[dev].isexternal) {
-		wf_mpu_synth_operations[wfmpu_cnt].midi_dev = dev;
+	memcpy ((char *) &wf_mpu_midi_operations[index],
+		(char *) &wf_mpu_midi_proto,
+		sizeof (struct midi_operations));
+  
+	if (is_external) {
+		wf_mpu_midi_operations[index].converter = NULL;
+	} else {
+		wf_mpu_midi_operations[index].converter =
+			&wf_mpu_synth_operations[index];
 	}
-	wf_mpu_synth_operations[wfmpu_cnt].info = &wf_mpu_synth_info[dev];
-
-	midi_devs[dev] = &wf_mpu_midi_operations[wfmpu_cnt];
 
-	dev_conf[dev].opened = 0;
-	dev_conf[dev].mode = 0;
-	dev_conf[dev].configured_for_virtual = 0;
-	dev_conf[dev].devno = dev;
+	strcpy (wf_mpu_midi_operations[index].info.name, name);
 
-	midi_devs[dev]->in_info.m_busy = 0;
-	midi_devs[dev]->in_info.m_state = MST_INIT;
-	midi_devs[dev]->in_info.m_ptr = 0;
-	midi_devs[dev]->in_info.m_left = 0;
-	midi_devs[dev]->in_info.m_prev_status = 0;
+	midi_devs[dev->devno] = &wf_mpu_midi_operations[index];
+	midi_devs[dev->devno]->in_info.m_busy = 0;
+	midi_devs[dev->devno]->in_info.m_state = MST_INIT;
+	midi_devs[dev->devno]->in_info.m_ptr = 0;
+	midi_devs[dev->devno]->in_info.m_left = 0;
+	midi_devs[dev->devno]->in_info.m_prev_status = 0;
 
-	wfmpu_cnt++;
+	devs[index].opened = 0;
+	devs[index].mode = 0;
 
 	return (0);
 }
 
-int
-virtual_midi_enable (int dev, struct address_info *hw_config)
+int virtual_midi_enable (void)
 
 {
-	int idev;
-	int edev;
-	struct wf_mpu_config *devc;
-
-	devc = &dev_conf[dev];
-	
-	if (devc->configured_for_virtual) {
-
-		idev = devc->internal;
-		edev = devc->external;
-
-	} else {
-
-		if (hw_config == NULL) {
-			printk (KERN_ERR
-				"WF-MPU: virtual midi first "
-				"enabled without hw_config!\n");
-			return -EINVAL;
-		}
+	if ((virt_dev->devno < 0) &&
+	    (virt_dev->devno = sound_alloc_mididev()) == -1) {
+		printk (KERN_ERR
+			"WF-MPU: too many midi devices detected\n");
+		return -1;
+	}
 
-		idev = devc->internal;
+	config_wf_mpu (virt_dev);
 
-		if ((edev = sound_alloc_mididev()) == -1) {
-			printk (KERN_ERR
-				"WF-MPU: too many midi devices detected\n");
-			return -1;
-		}
+	phys_dev->isvirtual = 1;
+	return virt_dev->devno;
+}
 
-		hw_config->slots[WF_EXTERNAL_MIDI_SLOT] = edev;
-	}
+int
+virtual_midi_disable (void)
 
-	dev_conf[edev].isvirtual = 1;
-	dev_conf[idev].isvirtual = 1;
-    
-	if (dev_conf[idev].configured_for_virtual) {
-		return 0;
-	} 
+{
+	unsigned long flags;
 
-	/* Configure external interface struct */
+	save_flags (flags);
+	cli();
 
-	devc = &dev_conf[edev];
-	devc->internal = idev;
-	devc->external = edev;
-	devc->isexternal = 1;
+	wf_mpu_close (virt_dev->devno);
+	/* no synth on virt_dev, so no need to call wf_mpu_synth_close() */
+	phys_dev->isvirtual = 0;
 
-	/* Configure external interface struct 
-	   (devc->isexternal and devc->internal set in attach_wf_mpu())
-	*/
+	restore_flags (flags);
 
-	devc = &dev_conf[idev];
-	devc->external = edev;
+	return 0;
+}
 
-	/* Configure the tables for the external */
+__initfunc (static int detect_wf_mpu (int irq, int io_base))
 
-	if (config_wf_mpu (edev, hw_config)) {
-		printk (KERN_WARNING "WF-MPU: configuration for MIDI "
-			"device %d failed\n", edev);
-		return (-1);
+{
+	if (check_region (io_base, 2)) {
+		printk (KERN_WARNING "WF-MPU: I/O port %x already in use.\n",
+			io_base);
+		return -1;
 	}
 
-	/* Don't bother to do this again if we are toggled back and
-	   forth between virtual MIDI mode and "normal" operation.
-	*/
-
-	dev_conf[edev].configured_for_virtual = 1;
-	dev_conf[idev].configured_for_virtual = 1;
+	phys_dev->base = io_base;
+	phys_dev->irq = irq;
+	phys_dev->devno = -1;
+	virt_dev->devno = -1;
 
 	return 0;
 }
 
-void
-virtual_midi_disable (int dev)
+__initfunc (int install_wf_mpu (void)) 
 
 {
-	struct wf_mpu_config *devc;
-	unsigned long flags;
+	if ((phys_dev->devno = sound_alloc_mididev()) < 0){
 
-	save_flags (flags);
-	cli();
-
-	/* Assumes for logical purposes that the caller has taken
-	   care of fiddling with WaveFront hardware commands to
-	   turn off Virtual MIDI mode. 
-	*/
-    
-	devc = &dev_conf[dev];
+		printk (KERN_ERR "WF-MPU: Too many MIDI devices detected.\n");
+		return -1;
 
-	devc = &dev_conf[devc->internal];
-	devc->isvirtual = 0;
+	}
 
-	devc = &dev_conf[devc->external];
-	devc->isvirtual = 0;
+	request_region (phys_dev->base, 2, "wavefront midi");
+	phys_dev->isvirtual = 0;
 
-	restore_flags (flags);
-}
+	if (config_wf_mpu (phys_dev)) {
 
-void
-attach_wf_mpu (struct address_info *hw_config)
-{
-	int m;
-	struct wf_mpu_config *devc;
-
-	if (request_irq (hw_config->irq, wf_mpuintr,
-			 0, "WaveFront MIDI", NULL) < 0) {
-		printk (KERN_ERR "WF-MPU: Failed to allocate IRQ%d\n",
-			hw_config->irq);
-		return;
-	}
+		printk (KERN_WARNING
+			"WF-MPU: configuration for MIDI device %d failed\n",
+			phys_dev->devno);
+		sound_unload_mididev (phys_dev->devno);
 
-	if ((m = sound_alloc_mididev()) == -1){
-		printk (KERN_ERR "WF-MPU: Too many MIDI devices detected.\n");
-		free_irq (hw_config->irq, NULL);
-		release_region (hw_config->io_base, 2);
-		return;
 	}
 
-	request_region (hw_config->io_base, 2, "WaveFront MPU");
+	/* OK, now we're configured to handle an interrupt ... */
 
-	hw_config->slots[WF_INTERNAL_MIDI_SLOT] = m;
-	devc = &dev_conf[m];
-	devc->base = hw_config->io_base;
-	devc->irq = hw_config->irq;
-	devc->isexternal = 0;
-	devc->internal = m;
-	devc->external = -1;
-	devc->isvirtual = 0;
+	if (request_irq (phys_dev->irq, wf_mpuintr, SA_INTERRUPT|SA_SHIRQ,
+			 "wavefront midi", phys_dev) < 0) {
 
-	irq2dev[devc->irq] = m;
+		printk (KERN_ERR "WF-MPU: Failed to allocate IRQ%d\n",
+			phys_dev->irq);
+		return -1;
 
-	if (config_wf_mpu (m, hw_config)) {
-		printk (KERN_WARNING
-			"WF-MPU: configuration for MIDI device %d failed\n", m);
-		sound_unload_mididev (m);
 	}
 
 	/* This being a WaveFront (ICS-2115) emulated MPU-401, we have
@@ -912,64 +857,43 @@
 	   won't do anything at all.
 	*/
   
-	start_uart_mode (devc);
+	start_uart_mode ();
 
+	return phys_dev->devno;
 }
-
-int
-probe_wf_mpu (struct address_info *hw_config)
-
-{
-	if (hw_config->irq < 0 || hw_config->irq > 16) {
-		printk (KERN_WARNING "WF-MPU: bogus IRQ value requested (%d)\n",
-			hw_config->irq);
-		return 0;
-	}
-
-	if (check_region (hw_config->io_base, 2)) {
-		printk (KERN_WARNING "WF-MPU: I/O port %x already in use\n\n",
-			hw_config->io_base);
-		return 0;
-	}
-
-	if (inb (hw_config->io_base + 1) == 0xff) { /* Just bus float? */
-		printk ("WF-MPU: Port %x looks dead.\n", hw_config->io_base);
-		return 0;
-	}
-
-	return 1;
-}
-
+ 
 void
-unload_wf_mpu (struct address_info *hw_config)
+uninstall_wf_mpu (void)
+
 {
+	release_region (phys_dev->base, 2); 
+	free_irq (phys_dev->irq, phys_dev);
+	sound_unload_mididev (phys_dev->devno);
 
-	release_region (hw_config->io_base, 2); 
-	sound_unload_mididev (hw_config->slots[WF_INTERNAL_MIDI_SLOT]);
-	if (hw_config->irq > 0) {
-		free_irq (hw_config->irq, NULL);
-	}
-	if (hw_config->slots[WF_EXTERNAL_MIDI_SLOT] > 0) {
-		sound_unload_mididev (hw_config->slots[WF_EXTERNAL_MIDI_SLOT]);
+	if (virt_dev->devno >= 0) {
+		sound_unload_mididev (virt_dev->devno);
 	}
 }
 
 static void
-start_uart_mode (struct wf_mpu_config *devc)
+start_uart_mode (void)
+
 {
-	int             ok, timeout;
+	int             ok, i;
 	unsigned long   flags;
 
 	save_flags (flags);
 	cli ();
 
-	for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--);
+	/* XXX fix me */
 
-	wf_mpu_cmd (devc, UART_MODE_ON);
+	for (i = 0; i < 30000 && !output_ready (); i++);
 
-	for (ok = 0, timeout = 50000; timeout > 0 && !ok; timeout--) {
-		if (input_avail (devc)) {
-			if (read_data (devc) == MPU_ACK) {
+	outb (UART_MODE_ON, COMDPORT(phys_dev));
+
+	for (ok = 0, i = 50000; i > 0 && !ok; i--) {
+		if (input_avail ()) {
+			if (read_data () == MPU_ACK) {
 				ok = 1;
 			}
 		}
@@ -978,6 +902,30 @@
 	restore_flags (flags);
 }
 
-#endif
+#ifdef OSS_SUPPORT
+
+int
+probe_wf_mpu (struct address_info *hw_config)
+
+{
+	return !detect_wf_mpu (hw_config->irq, hw_config->io_base);
+}
+
+void
+attach_wf_mpu (struct address_info *hw_config)
+
+{
+	(void) install_wf_mpu ();
+}
+
+void
+unload_wf_mpu (void)
+{
+	uninstall_wf_mpu ();
+}
+
+#endif OSS_SUPPORT
+
+#endif CONFIG_SOUND_WAVEFRONT_MODULE_AND_MODULE
 
 
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/video/atyfb.c linux/drivers/video/atyfb.c
--- v2.2.0-pre4/linux/drivers/video/atyfb.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/video/atyfb.c	Mon Jan  4 10:09:59 1999
@@ -2752,6 +2752,7 @@
 		addr = pdev->base_address[1];
 	    if (!addr)
 		continue;
+	    addr &= PCI_BASE_ADDRESS_MEM_MASK;
 
 #ifdef __sparc__
 	    /*
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/video/chipsfb.c linux/drivers/video/chipsfb.c
--- v2.2.0-pre4/linux/drivers/video/chipsfb.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/video/chipsfb.c	Mon Jan  4 10:09:59 1999
@@ -53,11 +53,14 @@
 	struct {
 		__u8 red, green, blue;
 	} palette[256];
+	unsigned long frame_buffer_phys;
 	__u8 *frame_buffer;
-	__u8 *blitter_regs;
+	unsigned long blitter_regs_phys;
+	__u32 *blitter_regs;
+	unsigned long blitter_data_phys;
+	__u8 *blitter_data;
+	unsigned long io_base_phys;
 	__u8 *io_base;
-	unsigned long chips_base_phys;
-	unsigned long chips_io_phys;
 	struct fb_info_chips *next;
 #ifdef CONFIG_PMAC_PBOOK
 	unsigned char *save_framebuffer;
@@ -214,27 +217,28 @@
 	if (con == currcon)		/* current console? */
 		return fb_get_cmap(cmap, kspc, chipsfb_getcolreg, info);
 	if (fb_display[con].cmap.len)	/* non default colormap? */
-		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc? 0: 2);
-	else
-		fb_copy_cmap(fb_default_cmap(256), cmap, kspc? 0: 2);
+		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+	else {
+		int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
+		fb_copy_cmap(fb_default_cmap(size), cmap, kspc ? 0 : 2);
+	}
 	return 0;
 }
 
 static int chips_set_cmap(struct fb_cmap *cmap, int kspc, int con,
 			 struct fb_info *info)
 {
-	struct display *disp = &fb_display[con];
 	int err;
 
-	if (disp->cmap.len == 0) {
-		err = fb_alloc_cmap(&disp->cmap, 256, 0);
-		if (err)
+	if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
+		int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
+		if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0)))
 			return err;
 	}
-
-	if (con == currcon)
+	if (con == currcon)			/* current console? */
 		return fb_set_cmap(cmap, kspc, chipsfb_setcolreg, info);
-	fb_copy_cmap(cmap, &disp->cmap, kspc==0);
+	else
+		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
 	return 0;
 }
 
@@ -244,24 +248,21 @@
 	return -EINVAL;
 }
 
-static int chipsfb_switch(int con, struct fb_info *info)
+static int chipsfbcon_switch(int con, struct fb_info *info)
 {
 	struct fb_info_chips *p = (struct fb_info_chips *) info;
-	struct display* old_disp = &fb_display[currcon];
-	struct display* new_disp = &fb_display[con];
-	int	bit_depth;
+	int new_bpp, old_bpp;
 
+	/* Do we have to save the colormap? */
 	if (fb_display[currcon].cmap.len)
-		fb_get_cmap(&old_disp->cmap, 1, chipsfb_getcolreg, info);
+		fb_get_cmap(&fb_display[currcon].cmap, 1, chipsfb_getcolreg, info);
 
-	bit_depth = new_disp->var.bits_per_pixel;
-	if (old_disp->var.bits_per_pixel != bit_depth)
-	{
-	  currcon = con;
-	  chips_set_bitdepth(p, new_disp, con, bit_depth);
-	}
-	else
-	  currcon = con;
+	new_bpp = fb_display[con].var.bits_per_pixel;
+	old_bpp = fb_display[currcon].var.bits_per_pixel;
+	currcon = con;
+
+	if (new_bpp != old_bpp)
+		chips_set_bitdepth(p, &fb_display[con], con, new_bpp);
 	
 	do_install_cmap(con, info);
 	return 0;
@@ -277,9 +278,10 @@
 	struct fb_info_chips *p = (struct fb_info_chips *) info;
 	int i;
 
-	if (blank > 1) {
+	// used to disable backlight only for blank > 1, but it seems
+	// useful at blank = 1 too (saves battery, extends backlight life)
+	if (blank) {
 		pmu_enable_backlight(0);
-	} else if (blank) {
 		for (i = 0; i < 256; ++i) {
 			out_8(p->io_base + 0x3c8, i);
 			udelay(1);
@@ -311,10 +313,8 @@
 			     u_int transp, struct fb_info *info)
 {
 	struct fb_info_chips *p = (struct fb_info_chips *) info;
-	int hr;
 
-	hr = (p->fix.visual != FB_VISUAL_PSEUDOCOLOR)? (regno << 3): regno;
-	if (hr > 255)
+	if (regno > 255)
 		return 1;
 	red >>= 8;
 	green >>= 8;
@@ -322,18 +322,18 @@
 	p->palette[regno].red = red;
 	p->palette[regno].green = green;
 	p->palette[regno].blue = blue;
-	out_8(p->io_base + 0x3c8, hr);
+	out_8(p->io_base + 0x3c8, regno);
 	udelay(1);
 	out_8(p->io_base + 0x3c9, red);
 	out_8(p->io_base + 0x3c9, green);
 	out_8(p->io_base + 0x3c9, blue);
 
 #ifdef FBCON_HAS_CFB16
-	if (regno < 16)
-		p->fbcon_cfb16_cmap[regno] = (red << 10) | (green << 5) | blue;
+	if (regno < 16)	p->fbcon_cfb16_cmap[regno] =
+		((red & 0xf8) << 7) | ((green & 0xf8) << 2) | ((blue & 0xf8) >> 3);
 #endif
 
-    return 0;
+	return 0;
 }
 
 static void do_install_cmap(int con, struct fb_info *info)
@@ -356,14 +356,14 @@
 	
 	if (bpp == 16) {
 		if (con == currcon) {
-			write_cr(0x13, 200);		// 16 bit display width (decimal)
-			write_xr(0x81, 0x14);		// 15 bit (TrueColor) color mode
-			write_xr(0x82, 0x00);		// disable palettes
+			write_cr(0x13, 200);		// Set line length (doublewords)
+			write_xr(0x81, 0x14);		// 15 bit (555) color mode
+			write_xr(0x82, 0x00);		// Disable palettes
 			write_xr(0x20, 0x10);		// 16 bit blitter mode
 		}
 
 		fix->line_length = 800*2;
-		fix->visual = FB_VISUAL_DIRECTCOLOR;
+		fix->visual = FB_VISUAL_TRUECOLOR;
 
 		var->red.offset = 10;
 		var->green.offset = 5;
@@ -378,7 +378,7 @@
 #endif
 	} else if (bpp == 8) {
 		if (con == currcon) {
-			write_cr(0x13, 100);		// 8 bit display width (decimal)
+			write_cr(0x13, 100);		// Set line length (doublewords)
 			write_xr(0x81, 0x12);		// 8 bit color mode
 			write_xr(0x82, 0x08);		// Graphics gamma enable
 			write_xr(0x20, 0x00);		// 8 bit blitter mode
@@ -402,7 +402,7 @@
 	disp->visual = fix->visual;
 	disp->var = *var;
 
-#ifdef CONFIG_PMAC_PBOOK
+#if (defined(CONFIG_PMAC_PBOOK) || defined(CONFIG_FB_COMPAT_XPMAC))
 	display_info.depth = bpp;
 	display_info.pitch = fix->line_length;
 #endif
@@ -549,9 +549,17 @@
 	int i;
 
 	strcpy(p->fix.id, "C&T 65550");
-	p->fix.smem_start = (char *) p->chips_base_phys;
-	p->fix.smem_len = 800 * 600;
-	p->fix.mmio_start = (char *) p->chips_io_phys;
+	p->fix.smem_start = (char *) p->frame_buffer_phys;
+
+// FIXME: Assumes 1MB frame buffer, but 65550 supports 1MB or 2MB.
+// * "3500" PowerBook G3 (the original PB G3) has 2MB.
+// * 2400 has 1MB composed of 2 Mitsubishi M5M4V4265CTP DRAM chips.
+//   Motherboard actually supports 2MB -- there are two blank locations
+//   for a second pair of DRAMs.  (Thanks, Apple!)
+// * 3400 has 1MB (I think).  Don't know if it's expandable.
+// -- Tim Seufert
+	p->fix.smem_len = 0x100000;	// 1MB
+	p->fix.mmio_start = (char *) p->io_base_phys;
 	p->fix.type = FB_TYPE_PACKED_PIXELS;
 	p->fix.visual = FB_VISUAL_PSEUDOCOLOR;
 	p->fix.line_length = 800;
@@ -574,7 +582,7 @@
 	p->disp.cmap.green = NULL;
 	p->disp.cmap.blue = NULL;
 	p->disp.cmap.transp = NULL;
-	p->disp.screen_base = (char *) p->frame_buffer;
+	p->disp.screen_base = p->frame_buffer;
 	p->disp.visual = p->fix.visual;
 	p->disp.type = p->fix.type;
 	p->disp.type_aux = p->fix.type_aux;
@@ -589,7 +597,7 @@
 	p->info.disp = &p->disp;
 	p->info.fontname[0] = 0;
 	p->info.changevar = NULL;
-	p->info.switch_con = &chipsfb_switch;
+	p->info.switch_con = &chipsfbcon_switch;
 	p->info.updatevar = &chipsfb_updatevar;
 	p->info.blank = &chipsfb_blank;
 	p->info.flags = FBINFO_FLAG_DEFAULT;
@@ -606,7 +614,8 @@
 		return;
 	}
 
-	printk("fb%d: Chips 65550 frame buffer\n", GET_FB_IDX(p->info.node));
+	printk("fb%d: Chips 65550 frame buffer (%dK RAM detected)\n",
+		GET_FB_IDX(p->info.node), p->fix.smem_len / 1024);
 
 	chips_hw_init(p);
 
@@ -619,10 +628,10 @@
 		display_info.mode = VMODE_800_600_60;
 		strncpy(display_info.name, "chips65550",
 			sizeof(display_info.name));
-		display_info.fb_address = p->chips_base_phys + 0x800000;
-		display_info.cmap_adr_address = p->chips_io_phys + 0x3c8;
-		display_info.cmap_data_address = p->chips_io_phys + 0x3c9;
-		display_info.disp_reg_address = p->chips_base_phys + 0xc00000;
+		display_info.fb_address = p->frame_buffer_phys;
+		display_info.cmap_adr_address = p->io_base_phys + 0x3c8;
+		display_info.cmap_data_address = p->io_base_phys + 0x3c9;
+		display_info.disp_reg_address = p->blitter_regs_phys;
 		console_fb_info = &p->info;
 	}
 #endif /* CONFIG_FB_COMPAT_XPMAC */
@@ -661,17 +670,23 @@
 		return;
 	memset(p, 0, sizeof(*p));
 	addr = dp->addrs[0].address;
-	p->chips_base_phys = addr;
-	p->frame_buffer = __ioremap(addr+0x800000, 0x100000, _PAGE_NO_CACHE);
-	p->blitter_regs = ioremap(addr + 0xC00000, 0x1000);
+#ifdef __BIG_ENDIAN
+	addr += 0x800000;	// Use big-endian aperture
+#endif
+	p->frame_buffer_phys = addr;
+	p->frame_buffer = __ioremap(addr, 0x200000, _PAGE_NO_CACHE);
+	p->blitter_regs_phys = addr + 0x400000;
+	p->blitter_regs = ioremap(addr + 0x400000, 0x1000);
+	p->blitter_data_phys = addr + 0x410000;
+	p->blitter_data = ioremap(addr + 0x410000, 0x10000);
 
 	if (pci_device_loc(dp, &bus, &devfn) == 0) {
 		pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
 		cmd |= 3;	/* enable memory and IO space */
 		pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
-		p->io_base = (unsigned char *) pci_io_base(bus);
+		p->io_base = (__u8 *) pci_io_base(bus);
 		/* XXX really want the physical address here */
-		p->chips_io_phys = (unsigned long) pci_io_base(bus);
+		p->io_base_phys = (unsigned long) pci_io_base(bus);
 	}
 
 	/* Clear the entire framebuffer */
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/video/matroxfb.c linux/drivers/video/matroxfb.c
--- v2.2.0-pre4/linux/drivers/video/matroxfb.c	Tue Dec 22 14:16:57 1998
+++ linux/drivers/video/matroxfb.c	Mon Jan  4 14:33:44 1999
@@ -2,9 +2,9 @@
  *
  * Hardware accelerated Matrox Millennium I, II, Mystique and G200
  *
- * (c) 1998 Petr Vandrovec <vandrove@vc.cvut.cz>
+ * (c) 1998,1999 Petr Vandrovec <vandrove@vc.cvut.cz>
  *
- * Version: 1.8 1998/12/11
+ * Version: 1.9 1999/01/04
  *
  * MTRR stuff: 1998 Tom Rini <tmrini@ntplx.net>
  *
@@ -4841,7 +4841,7 @@
 static int no_pci_retry = 0;		/* "matrox:nopciretry" */
 static int novga = 0;			/* "matrox:novga" */
 static int nobios = 0;			/* "matrox:nobios" */
-static int noinit = 0;			/* "matrox:noinit" */
+static int noinit = 1;			/* "matrox:noinit" */
 static int inverse = 0;			/* "matrox:inverse" */
 static int hwcursor = 1;		/* "matrox:nohwcursor" */
 static int blink = 1;			/* "matrox:noblink" */
diff -u --recursive --new-file v2.2.0-pre4/linux/drivers/video/offb.c linux/drivers/video/offb.c
--- v2.2.0-pre4/linux/drivers/video/offb.c	Tue Dec 22 14:16:57 1998
+++ linux/drivers/video/offb.c	Mon Jan  4 10:09:59 1999
@@ -68,7 +68,6 @@
 
 static int ofonly = 0;
 
-
     /*
      *  Interface used by the world
      */
@@ -251,8 +250,10 @@
     else if (fb_display[con].cmap.len) /* non default colormap? */
 	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
     else
-	fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
-		     cmap, kspc ? 0 : 2);
+    {
+	int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
+	fb_copy_cmap(fb_default_cmap(size), cmap, kspc ? 0 : 2);
+    }
     return 0;
 }
 
@@ -270,8 +271,8 @@
 	return -ENOSYS;
 
     if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
-	if ((err = fb_alloc_cmap(&fb_display[con].cmap,
-				 1<<fb_display[con].var.bits_per_pixel, 0)))
+	int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
+	if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0)))
 	    return err;
     }
     if (con == currcon)			/* current console? */
@@ -522,18 +523,20 @@
     fix->type = FB_TYPE_PACKED_PIXELS;
     fix->type_aux = 0;
 
-	/* XXX kludge for ati */
-    if (strncmp(name, "ATY,", 4) == 0) {
-	unsigned long base = address & 0xff000000UL;
-	info->cmap_adr = ioremap(base + 0x7ff000, 0x1000) + 0xcc0;
-	info->cmap_data = info->cmap_adr + 1;
-    }
-
     if (depth == 8)
-	fix->visual = info->cmap_adr ? FB_VISUAL_PSEUDOCOLOR
+    {
+    	/* XXX kludge for ati */
+    	if (strncmp(name, "ATY,", 4) == 0) {
+		unsigned long base = address & 0xff000000UL;
+		info->cmap_adr = ioremap(base + 0x7ff000, 0x1000) + 0xcc0;
+		info->cmap_data = info->cmap_adr + 1;
+	}
+        fix->visual = info->cmap_adr ? FB_VISUAL_PSEUDOCOLOR
 				     : FB_VISUAL_STATIC_PSEUDOCOLOR;
+    }
     else
-	fix->visual = FB_VISUAL_TRUECOLOR;
+	fix->visual = /*info->cmap_adr ? FB_VISUAL_DIRECTCOLOR
+				     : */FB_VISUAL_TRUECOLOR;
 
     var->xoffset = var->yoffset = 0;
     var->bits_per_pixel = depth;
@@ -611,10 +614,14 @@
             disp->dispsw = &fbcon_cfb16;
             disp->dispsw_data = info->fbcon_cmap.cfb16;
             for (i = 0; i < 16; i++)
-		info->fbcon_cmap.cfb16[i] =
-		    (((default_blu[i] >> 3) & 0x1f) << 10) |
-		    (((default_grn[i] >> 3) & 0x1f) << 5) |
-		    ((default_red[i] >> 3) & 0x1f);
+            	if (fix->visual == FB_VISUAL_TRUECOLOR)
+		    info->fbcon_cmap.cfb16[i] =
+			    (((default_blu[i] >> 3) & 0x1f) << 10) |
+			    (((default_grn[i] >> 3) & 0x1f) << 5) |
+			    ((default_red[i] >> 3) & 0x1f);
+		else
+		    info->fbcon_cmap.cfb16[i] =
+			    (i << 10) | (i << 5) | i;
             break;
 #endif
 #ifdef FBCON_HAS_CFB32
@@ -622,9 +629,14 @@
             disp->dispsw = &fbcon_cfb32;
             disp->dispsw_data = info->fbcon_cmap.cfb32;
             for (i = 0; i < 16; i++)
-		info->fbcon_cmap.cfb32[i] = (default_blu[i] << 16) |
-					    (default_grn[i] << 8) |
-					    default_red[i];
+            	if (fix->visual == FB_VISUAL_TRUECOLOR)
+		    info->fbcon_cmap.cfb32[i] =
+			(default_blu[i] << 16) |
+			(default_grn[i] << 8) |
+			default_red[i];
+		else
+		    info->fbcon_cmap.cfb32[i] =
+			    (i << 16) | (i << 8) | i;
             break;
 #endif
         default:
@@ -791,7 +803,7 @@
     info2->palette[regno].green = green;
     info2->palette[regno].blue = blue;
 
-    *info2->cmap_adr = regno;
+    *info2->cmap_adr = regno;/* On some chipsets, add << 3 in 15 bits */
     mach_eieio();
     *info2->cmap_data = red;
     mach_eieio();
@@ -804,8 +816,7 @@
 	switch (info2->var.bits_per_pixel) {
 #ifdef FBCON_HAS_CFB16
 	    case 16:
-		info2->fbcon_cmap.cfb16[regno] = (regno << 10) | (regno << 5) |
-						 regno;
+		info2->fbcon_cmap.cfb16[regno] = (regno << 10) | (regno << 5) | regno;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB32
@@ -827,8 +838,10 @@
     if (fb_display[con].cmap.len)
 	fb_set_cmap(&fb_display[con].cmap, 1, offb_setcolreg, info);
     else
-	fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
-				    1, offb_setcolreg, info);
+    {
+	int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
+	fb_set_cmap(fb_default_cmap(size), 1, offb_setcolreg, info);
+    }
 }
 
 
diff -u --recursive --new-file v2.2.0-pre4/linux/fs/Config.in linux/fs/Config.in
--- v2.2.0-pre4/linux/fs/Config.in	Thu Dec 31 10:29:01 1998
+++ linux/fs/Config.in	Mon Jan  4 12:05:11 1999
@@ -22,12 +22,15 @@
 tristate 'ISO 9660 CDROM filesystem support' CONFIG_ISO9660_FS
 if [ "$CONFIG_ISO9660_FS" != "n" ]; then
   bool 'Microsoft Joliet CDROM extensions' CONFIG_JOLIET
+else
+  # needed by nls/Config.in
+  define_bool CONFIG_JOLIET n
 fi
 
 tristate 'Minix fs support' CONFIG_MINIX_FS
 tristate 'NTFS filesystem support (read only)' CONFIG_NTFS_FS
 if [ "$CONFIG_NTFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  bool '   NTFS read-write support (experimental)' CONFIG_NTFS_RW
+  bool '   NTFS read-write support (DANGEROUS)' CONFIG_NTFS_RW
 fi
 tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
 bool '/proc filesystem support' CONFIG_PROC_FS
@@ -44,8 +47,12 @@
 tristate 'Second extended fs support' CONFIG_EXT2_FS
 tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
 tristate 'UFS filesystem support' CONFIG_UFS_FS
+if [ "$CONFIG_UFS_FS" != "n" ]; then
+  bool '   UFS filesystem write support (experimental)' CONFIG_UFS_FS_WRITE
+fi
 
 
+if [ "$CONFIG_NET" = "y" ]; then
 
 mainmenu_option next_comment
 comment 'Network File Systems'
@@ -87,6 +94,7 @@
 fi
 
 endmenu
+fi
 
 if [ "$CONFIG_AFFS_FS" != "n" ]; then
   define_bool CONFIG_AMIGA_PARTITION y
diff -u --recursive --new-file v2.2.0-pre4/linux/fs/buffer.c linux/fs/buffer.c
--- v2.2.0-pre4/linux/fs/buffer.c	Fri Jan  1 12:58:20 1999
+++ linux/fs/buffer.c	Wed Jan  6 00:01:36 1999
@@ -76,8 +76,6 @@
 static int nr_buffers_type[NR_LIST] = {0,};
 static int nr_buffer_heads = 0;
 static int nr_unused_buffer_heads = 0;
-static int refilled = 0;       /* Set NZ when a buffer freelist is refilled 
-				  this is used by the loop device */
 
 /* This is used by some architectures to estimate available memory. */
 int buffermem = 0;
@@ -113,8 +111,8 @@
 } bdf_prm = {{40, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}};
 
 /* These are the min and max parameter values that we will allow to be assigned */
-int bdflush_min[N_PARAM] = {  0,  10,    5,   25,  0,   100,   100, 1, 1};
-int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 60000, 60000, 2047, 5};
+int bdflush_min[N_PARAM] = {  0,  10,    5,   25,  0,   1*HZ,   1*HZ, 1, 1};
+int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 600*HZ, 600*HZ, 2047, 5};
 
 void wakeup_bdflush(int);
 
@@ -1607,7 +1605,7 @@
  * and superblocks so that we could write back only the old ones as well
  */
 
-asmlinkage int sync_old_buffers(void)
+static int sync_old_buffers(void)
 {
 	int i;
 	int ndirty, nwritten;
@@ -1774,7 +1772,6 @@
 #endif
 		 {
 			 ndirty = 0;
-			 refilled = 0;
 		 repeat:
 
 			 bh = lru_list[nlist];
@@ -1801,8 +1798,6 @@
 					  major = MAJOR(bh->b_dev);
 					  /* Should we write back buffers that are shared or not??
 					     currently dirty buffers are not shared, so it does not matter */
-					  if (refilled && major == LOOP_MAJOR)
-						   continue;
 					  next->b_count++;
 					  bh->b_count++;
 					  ndirty++;
diff -u --recursive --new-file v2.2.0-pre4/linux/fs/dcache.c linux/fs/dcache.c
--- v2.2.0-pre4/linux/fs/dcache.c	Thu Dec 31 10:29:01 1998
+++ linux/fs/dcache.c	Tue Jan  5 13:19:12 1999
@@ -190,9 +190,9 @@
 int select_dcache(int inode_count, int page_count)
 {
 	struct list_head *next, *tail = &dentry_unused;
-	int found = 0, forward = 0, young = 8;
+	int found = 0;
 	int depth = dentry_stat.nr_unused >> 1;
-	unsigned long min_value = 0, max_value = 4;
+	unsigned long max_value = 4;
 
 	if (page_count)
 		max_value = -1;
@@ -205,39 +205,12 @@
 		unsigned long value = 0;	
 
 		next = tmp->prev;
-		if (forward)
-			next = tmp->next;
 		if (dentry->d_count) {
 			dentry_stat.nr_unused--;
 			list_del(tmp);
 			INIT_LIST_HEAD(tmp);
 			continue;
 		}
-		/*
-		 * Check the dentry's age to see whether to change direction.
-		 */
-		if (!forward) {
-			int age = (jiffies - dentry->d_reftime) / HZ;
-			if (age < dentry_stat.age_limit) {
-				if (!--young) {
-					forward = 1;
-					next = dentry_unused.next;
-					/*
-		 			 * Update the limits -- we don't want
-					 * files with too few or too many pages.
-					 */
- 					if (page_count) {
-						min_value = 3;
-						max_value = 15;
-					}
-#ifdef DCACHE_DEBUG
-printk("select_dcache: %s/%s age=%d, scanning forward\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, age);
-#endif
-				}
-				continue;
-			}
-		} 
 
 		/*
 		 * Select dentries based on the page cache count ...
@@ -247,7 +220,7 @@
 		 */
 		if (inode) {
 			value = inode->i_nrpages;	
-			if (value >= max_value || value < min_value)
+			if (value >= max_value)
 				continue;
 			if (inode->i_state || inode->i_count > 1)
 				continue;
diff -u --recursive --new-file v2.2.0-pre4/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v2.2.0-pre4/linux/fs/fat/inode.c	Mon Jan  4 15:08:17 1999
+++ linux/fs/fat/inode.c	Mon Jan  4 10:08:28 1999
@@ -243,16 +243,16 @@
 					ret = 0;
 			}
 		}
-                else if (!strcmp(this_char,"cvf_format")) {
-                        if (!value)
-                                return 0;
-                        strncpy(cvf_format,value,20);
-                }
-                else if (!strcmp(this_char,"cvf_options")) {
-                        if (!value)
-                                return 0;
-                        strncpy(cvf_options,value,100);
-                }
+		else if (!strcmp(this_char,"cvf_format")) {
+			if (!value)
+				return 0;
+			strncpy(cvf_format,value,20);
+		}
+		else if (!strcmp(this_char,"cvf_options")) {
+			if (!value)
+				return 0;
+			strncpy(cvf_options,value,100);
+		}
 
 		if (this_char != options) *(this_char-1) = ',';
 		if (value) *savep = save;
@@ -302,7 +302,7 @@
 
 	opts.isvfat = MSDOS_SB(sb)->options.isvfat;
 	if (!parse_options((char *) data, &fat, &blksize, &debug, &opts, 
-	                   cvf_format, cvf_options)
+			   cvf_format, cvf_options)
 	    || (blksize != 512 && blksize != 1024 && blksize != 2048))
 		goto out_fail;
 	/* N.B. we should parse directly into the sb structure */
@@ -364,8 +364,8 @@
 		MSDOS_SB(sb)->root_cluster = CF_LE_L(b->root_cluster);
 		MSDOS_SB(sb)->fsinfo_offset =
 			CF_LE_W(b->info_sector) * logical_sector_size + 0x1e0;
-		if (MSDOS_SB(sb)->fsinfo_offset + sizeof(MSDOS_SB(sb)->fsinfo_offset) > sb->s_blocksize) {
-			printk("fat_read_super: Bad fsinfo_offset 0x%x\n", MSDOS_SB(sb)->fsinfo_offset);
+		if (MSDOS_SB(sb)->fsinfo_offset + sizeof(struct fat_boot_fsinfo) > sb->s_blocksize) {
+			printk("fat_read_super: Bad fsinfo_offset\n");
 			fat_brelse(sb, bh);
 			goto out_invalid;
 		}
@@ -524,10 +524,10 @@
 	int free,nr;
 	struct statfs tmp;
        
-        if (MSDOS_SB(sb)->cvf_format &&
+	if (MSDOS_SB(sb)->cvf_format &&
 	    MSDOS_SB(sb)->cvf_format->cvf_statfs)
 		return MSDOS_SB(sb)->cvf_format->cvf_statfs(sb,buf,bufsiz);
-          
+	  
 	lock_fat(sb);
 	if (MSDOS_SB(sb)->free_clusters != -1)
 		free = MSDOS_SB(sb)->free_clusters;
diff -u --recursive --new-file v2.2.0-pre4/linux/fs/inode.c linux/fs/inode.c
--- v2.2.0-pre4/linux/fs/inode.c	Fri Oct 23 22:01:22 1998
+++ linux/fs/inode.c	Mon Jan  4 17:52:17 1999
@@ -147,12 +147,8 @@
 		__wait_on_inode(inode);
 		spin_lock(&inode_lock);
 	} else {
-		struct list_head *insert = &inode_in_use;
-		if (!inode->i_count)
-			insert = inode_in_use.prev;
 		list_del(&inode->i_list);
-		list_add(&inode->i_list, insert);
-
+		list_add(&inode->i_list, &inode_in_use);
 		/* Set I_LOCK, reset I_DIRTY */
 		inode->i_state ^= I_DIRTY | I_LOCK;
 		spin_unlock(&inode_lock);
@@ -705,7 +701,7 @@
 			}
 			else if (!(inode->i_state & I_DIRTY)) {
 				list_del(&inode->i_list);
-				list_add(&inode->i_list, inode_in_use.prev);
+				list_add(&inode->i_list, &inode_in_use);
 			}
 #ifdef INODE_PARANOIA
 if (inode->i_flock)
diff -u --recursive --new-file v2.2.0-pre4/linux/fs/lockd/clntlock.c linux/fs/lockd/clntlock.c
--- v2.2.0-pre4/linux/fs/lockd/clntlock.c	Sun Nov  8 14:03:06 1998
+++ linux/fs/lockd/clntlock.c	Mon Jan  4 10:23:42 1999
@@ -71,12 +71,7 @@
 	 * a 1 minute timeout would do. See the comment before
 	 * nlmclnt_lock for an explanation.
 	 */
-	/*
-	 * FIXME, can we be not interruptible and so be allowed to use
-	 * a timeout here? -arca
-	 */
-/*  	current->timeout = jiffies + 30 * HZ; */
-	sleep_on(&block.b_wait);
+	sleep_on_timeout(&block.b_wait, 30*HZ);
 
 	for (head = &nlm_blocked; *head; head = &(*head)->b_next) {
 		if (*head == &block) {
diff -u --recursive --new-file v2.2.0-pre4/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v2.2.0-pre4/linux/fs/nfs/inode.c	Mon Dec 28 15:00:53 1998
+++ linux/fs/nfs/inode.c	Tue Jan  5 13:04:08 1999
@@ -474,7 +474,7 @@
 	 * unhashed inode to avoid aliasing problems.
 	 */
 	if ((dentry->d_parent->d_inode->u.nfs_i.flags & NFS_IS_SNAPSHOT) ||
-	    (IS_ROOT(dentry->d_parent) && dentry->d_name.len == 9 &&
+	    (dentry->d_name.len == 9 &&
 	     memcmp(dentry->d_name.name, ".snapshot", 9) == 0)) {
 		struct inode *inode = get_empty_inode();
 		if (!inode)
diff -u --recursive --new-file v2.2.0-pre4/linux/fs/ntfs/ntfsendian.h linux/fs/ntfs/ntfsendian.h
--- v2.2.0-pre4/linux/fs/ntfs/ntfsendian.h	Mon Jan  4 15:08:17 1999
+++ linux/fs/ntfs/ntfsendian.h	Mon Jan  4 10:41:35 1999
@@ -71,7 +71,7 @@
 #define NTFS_PUTU64(p,v)     ((*(ntfs_u64*)(p))=CPU_TO_LE64(v))
  
  /* Macros reading signed integers */
-#define NTFS_GETS8(p)        ((*(ntfs_u8*)(p)))
+#define NTFS_GETS8(p)        ((*(ntfs_s8*)(p)))
 #define NTFS_GETS16(p)       ((ntfs_s16)LE16_TO_CPU(*(short*)(p)))
 #define NTFS_GETS24(p)       (NTFS_GETU24(p) < 0x800000 ? (int)NTFS_GETU24(p) : (int)(NTFS_GETU24(p) - 0x1000000))
 #define NTFS_GETS32(p)       ((ntfs_s32)LE32_TO_CPU(*(int*)(p)))
diff -u --recursive --new-file v2.2.0-pre4/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.2.0-pre4/linux/fs/proc/array.c	Tue Dec 22 14:16:57 1998
+++ linux/fs/proc/array.c	Wed Jan  6 09:26:15 1999
@@ -1397,6 +1397,7 @@
 		case PROC_PID_STAT:
 		case PROC_PID_MAPS:
 		case PROC_PID_CMDLINE:
+		case PROC_PID_CPU:
 			return 0;	
 	}
 	if(capable(CAP_DAC_OVERRIDE) || current->fsuid == euid)
diff -u --recursive --new-file v2.2.0-pre4/linux/fs/qnx4/symlinks.c linux/fs/qnx4/symlinks.c
--- v2.2.0-pre4/linux/fs/qnx4/symlinks.c	Thu Nov 19 09:56:29 1998
+++ linux/fs/qnx4/symlinks.c	Mon Jan  4 11:42:43 1999
@@ -23,7 +23,7 @@
 #include <asm/uaccess.h>
 
 static int qnx4_readlink(struct dentry *, char *, int);
-static struct dentry *qnx4_follow_link(struct dentry *, struct dentry *);
+static struct dentry *qnx4_follow_link(struct dentry *, struct dentry *, unsigned int follow);
 
 /*
  * symlinks can't do much...
@@ -50,7 +50,7 @@
 };
 
 static struct dentry *qnx4_follow_link(struct dentry *dentry,
-				       struct dentry *base)
+				       struct dentry *base, unsigned int follow)
 {
 #if 0
 	struct inode *inode = dentry->d_inode;
diff -u --recursive --new-file v2.2.0-pre4/linux/fs/ufs/acl.c linux/fs/ufs/acl.c
--- v2.2.0-pre4/linux/fs/ufs/acl.c	Sat Sep  5 16:46:41 1998
+++ linux/fs/ufs/acl.c	Mon Jan  4 11:51:29 1999
@@ -58,8 +58,11 @@
 	 * Access is always granted for root. We now check last,
 	 * though, for BSD process accounting correctness
 	 */
-	if (((mode & mask & S_IRWXO) == mask) || fsuser())
+	if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE))
 		return 0;
-	else
-		return -EACCES;
+	if ((mask == S_IROTH) ||
+	    (S_ISDIR(mode)  && !(mask & ~(S_IROTH | S_IXOTH))))
+		if (capable(CAP_DAC_READ_SEARCH))
+			return 0;
+	return -EACCES;
 }
diff -u --recursive --new-file v2.2.0-pre4/linux/fs/ufs/namei.c linux/fs/ufs/namei.c
--- v2.2.0-pre4/linux/fs/ufs/namei.c	Tue Dec 22 14:16:57 1998
+++ linux/fs/ufs/namei.c	Mon Jan  4 12:05:11 1999
@@ -34,6 +34,7 @@
 #include <linux/stat.h>
 #include <linux/string.h>
 #include <linux/locks.h>
+#include <linux/quotaops.h>
 
 #include "swab.h"
 #include "util.h"
@@ -55,21 +56,18 @@
 #define NAMEI_RA_INDEX(c,b)  (((c) * NAMEI_RA_BLOCKS) + (b))
 
 /*
- * NOTE! unlike strncmp, ufs_match returns 1 for success, 0 for failure.
+ * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure.
+ *
+ * Len <= UFS_MAXNAMLEN' is guaranteed by caller.
+ * De != NULL' is guaranteed by caller.
  */
-static int ufs_match (int len, const char * const name,
+static inline int ufs_match (int len, const char * const name,
 	struct ufs_dir_entry * de, unsigned flags, unsigned swab)
 {
-	if (!de || !SWAB32(de->d_ino) || len > UFS_MAXNAMLEN)
-		return 0;
-	/*
-	 * "" means "." ---> so paths like "/usr/lib//libc.a" work
-	 */
-	if (!len && ufs_get_de_namlen(de) == 1 && (de->d_name[0] == '.') &&
-	   (de->d_name[1] == '\0'))
-		return 1;
 	if (len != ufs_get_de_namlen(de))
 		return 0;
+	if (!de->d_ino)
+		return 0;
 	return !memcmp(name, de->d_name, len);
 }
 
@@ -128,8 +126,9 @@
 		}
 		bh = bh_use[block % NAMEI_RA_SIZE];
 		if (!bh) {
-			ufs_error (sb, "ufs_find_entry",
-				"directory #%lu contains a hole at offset %lu", dir->i_ino, offset);
+			ufs_error (sb, "ufs_find_entry", 
+				"directory #%lu contains a hole at offset %lu",
+				dir->i_ino, offset);
 			offset += sb->s_blocksize;
 			continue;
 		}
@@ -144,20 +143,30 @@
 		de = (struct ufs_dir_entry *) bh->b_data;
 		dlimit = bh->b_data + sb->s_blocksize;
 		while ((char *) de < dlimit && offset < dir->i_size) {
-			if (!ufs_check_dir_entry ("ufs_find_entry", dir, de, bh, offset))
-				goto failed;
-			if (SWAB32(de->d_ino) != 0 && ufs_match (namelen, name, de, flags, swab)) {
+			/* this code is executed quadratically often */
+			/* do minimal checking by hand' */
+			int de_len;
+
+			if ((char *) de + namelen <= dlimit &&
+			    ufs_match (namelen, name, de, flags, swab)) {
+				/* found a match -
+				just to be sure, do a full check */
+				if (!ufs_check_dir_entry("ufs_find_entry",
+				    dir, de, bh, offset))
+					goto failed;
 				for (i = 0; i < NAMEI_RA_SIZE; ++i) {
 					if (bh_use[i] != bh)
 						brelse (bh_use[i]);
 				}
 				*res_dir = de;
-				UFSD(("EXIT\n"))
 				return bh;
 			}
-			offset += SWAB16(de->d_reclen);
-			de = (struct ufs_dir_entry *)
-				((char *) de + SWAB16(de->d_reclen));
+                        /* prevent looping on a bad block */
+			de_len = SWAB16(de->d_reclen);
+			if (de_len <= 0)
+				goto failed;
+			offset += de_len;
+			de = (struct ufs_dir_entry *) ((char *) de + de_len);
 		}
 
 		brelse (bh);
@@ -173,7 +182,7 @@
 
 failed:
 	for (i = 0; i < NAMEI_RA_SIZE; ++i) brelse (bh_use[i]);
-	UFSD(("EXIT (FAILED)\n"))
+	UFSD(("EXIT\n"))
 	return NULL;
 }
 
@@ -298,7 +307,7 @@
 			brelse (bh);
 			return NULL;
 		}
-		if (SWAB32(de->d_ino) != 0 && ufs_match (namelen, name, de, flags, swab)) {
+		if (ufs_match (namelen, name, de, flags, swab)) {
 				*err = -EEXIST;
 				brelse (bh);
 				return NULL;
@@ -673,8 +682,7 @@
 		goto end_rmdir;
 
 	inode = dentry->d_inode;
-	if (inode->i_sb->dq_op)
-		inode->i_sb->dq_op->initialize (inode, -1);
+	DQUOT_INIT(inode);
 
 	retval = -EIO;
 	if (SWAB32(de->d_ino) != inode->i_ino)
@@ -741,8 +749,7 @@
 		goto end_unlink;
 
 	inode = dentry->d_inode;
-	if (inode->i_sb->dq_op)
-		inode->i_sb->dq_op->initialize (inode, -1);
+	DQUOT_INIT(inode);
 
 	retval = -EIO;
 	if (SWAB32(de->d_ino) != inode->i_ino)
@@ -778,49 +785,6 @@
 }
 
 
-int ufs_link (struct dentry * old_dentry, struct inode * dir,
-	struct dentry *dentry)
-{
-	struct super_block * sb;
-	struct inode *inode = old_dentry->d_inode;
-	struct ufs_dir_entry * de;
-	struct buffer_head * bh;
-	int err;
-	unsigned swab;
-
-	inode = old_dentry->d_inode;
-	sb = inode->i_sb;
-	swab = sb->u.ufs_sb.s_swab;
-	
-	if (S_ISDIR(inode->i_mode))
-		return -EPERM;
-
-	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
-		return -EPERM;
-
-	if (inode->i_nlink >= UFS_LINK_MAX)
-		return -EMLINK;
-
-	bh = ufs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
-	if (!bh)
-		return err;
-
-	de->d_ino = SWAB32(inode->i_ino);
-	dir->i_version = ++event;
-	mark_buffer_dirty(bh, 1);
-	if (IS_SYNC(dir)) {
-		ll_rw_block (WRITE, 1, &bh);
-		wait_on_buffer (bh);
-	}
-	brelse (bh);
-	inode->i_nlink++;
-	inode->i_ctime = CURRENT_TIME;
-	mark_inode_dirty(inode);
-	inode->i_count++;
-	d_instantiate(dentry, inode);
-	return 0;
-}
-
 /*
  * Create symbolic link. We use only slow symlinks at this time.
  */
@@ -901,6 +865,49 @@
 	goto out;
 }
 
+int ufs_link (struct dentry * old_dentry, struct inode * dir,
+	struct dentry *dentry)
+{
+	struct super_block * sb;
+	struct inode *inode = old_dentry->d_inode;
+	struct ufs_dir_entry * de;
+	struct buffer_head * bh;
+	int err;
+	unsigned swab;
+
+	inode = old_dentry->d_inode;
+	sb = inode->i_sb;
+	swab = sb->u.ufs_sb.s_swab;
+	
+	if (S_ISDIR(inode->i_mode))
+		return -EPERM;
+
+	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+		return -EPERM;
+
+	if (inode->i_nlink >= UFS_LINK_MAX)
+		return -EMLINK;
+
+	bh = ufs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
+	if (!bh)
+		return err;
+
+	de->d_ino = SWAB32(inode->i_ino);
+	dir->i_version = ++event;
+	mark_buffer_dirty(bh, 1);
+	if (IS_SYNC(dir)) {
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
+	}
+	brelse (bh);
+	inode->i_nlink++;
+	inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty(inode);
+	inode->i_count++;
+	d_instantiate(dentry, inode);
+	return 0;
+}
+
 
 #define PARENT_INO(buffer) \
 	((struct ufs_dir_entry *) ((char *) buffer + \
@@ -939,27 +946,26 @@
 	if (old_dentry->d_name.len > UFS_MAXNAMLEN)
 		goto end_rename;
 
-	UFSD(("name %s, len %u\n", old_dentry->d_name.name, old_dentry->d_name.len)) 
 	old_bh = ufs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de);
-	UFSD(("ino %u, reclen %u, namlen %u, name %s\n", SWAB32(old_de->d_ino),
-		SWAB16(old_de->d_reclen), ufs_get_de_namlen(old_de), old_de->d_name))
-
-	/* Arrrgh. See comments in ext2 */
+	/*
+	 *  Check for inode number is _not_ due to possible IO errors.
+	 *  We might rmdir the source, keep it as pwd of some process
+	 *  and merrily kill the link to whatever was created under the
+	 *  same name. Goodbye sticky bit ;-<
+	 */
 	retval = -ENOENT;
+	old_inode = old_dentry->d_inode;
 	if (!old_bh || SWAB32(old_de->d_ino) != old_inode->i_ino)
 		goto end_rename;
-	old_inode = old_dentry->d_inode;
 
 	new_inode = new_dentry->d_inode;
-	UFSD(("name %s, len %u\n", new_dentry->d_name.name, new_dentry->d_name.len)) 
 	new_bh = ufs_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de);
 	if (new_bh) {
 		if (!new_inode) {
 			brelse (new_bh);
 			new_bh = NULL;
 		} else {
-			if (new_inode->i_sb->dq_op)
-				new_inode->i_sb->dq_op->initialize (new_inode, -1);
+			DQUOT_INIT(new_inode);
 		}
 	}
 	retval = 0;
@@ -970,6 +976,7 @@
 		if (is_subdir(new_dentry, old_dentry))
 			goto end_rename;
 		if (new_inode) {
+			/* Prune any children before testing for busy */
 			if (new_dentry->d_count > 1)
 				shrink_dcache_parent(new_dentry);
 			retval = -EBUSY;
diff -u --recursive --new-file v2.2.0-pre4/linux/fs/ufs/super.c linux/fs/ufs/super.c
--- v2.2.0-pre4/linux/fs/ufs/super.c	Fri Oct 23 22:01:22 1998
+++ linux/fs/ufs/super.c	Mon Jan  4 12:05:11 1999
@@ -449,6 +449,9 @@
 	MOD_INC_USE_COUNT;
 	lock_super (sb);
 
+#ifndef CONFIG_UFS_FS_WRITE
+	sb->s_flags |= MS_RDONLY;
+#endif
 	/*
 	 * Set default mount options
 	 * Parse mount options
@@ -527,9 +530,6 @@
 		goto failed;
 	}
 	
-	if (!(sb->s_flags & MS_RDONLY))
-		printk("!!! warning !!! write support of ufs is still in experimental state\n"); 
-
 again:	
 	set_blocksize (sb->s_dev, block_size);
 
diff -u --recursive --new-file v2.2.0-pre4/linux/fs/vfat/namei.c linux/fs/vfat/namei.c
--- v2.2.0-pre4/linux/fs/vfat/namei.c	Tue Dec 22 14:16:57 1998
+++ linux/fs/vfat/namei.c	Wed Jan  6 09:27:14 1999
@@ -1549,8 +1549,10 @@
 	res = -EBUSY;
 	if (list_empty(&dentry->d_hash)) {
 		res = vfat_rmdirx(dir, dentry);
-		if (res >= 0)
+		if (res >= 0) {
 			drop_replace_inodes(dentry, NULL);
+			d_delete(dentry);
+		}
 	}
 	return res;
 }
diff -u --recursive --new-file v2.2.0-pre4/linux/include/asm-m68k/init.h linux/include/asm-m68k/init.h
--- v2.2.0-pre4/linux/include/asm-m68k/init.h	Tue Feb 17 13:12:49 1998
+++ linux/include/asm-m68k/init.h	Tue Jan  5 11:20:43 1999
@@ -15,6 +15,9 @@
 #define __FINIT		.previous
 #define __INITDATA	.section	".data.init",#alloc,#write
 
+#define __cacheline_aligned __attribute__ \
+			 ((__section__ (".data.cacheline_aligned")))
+
 #else
 
 /* gdb doesn't like it all if the code for one source file isn't together in
@@ -27,6 +30,7 @@
 #define __INIT
 #define __FINIT
 #define __INITDATA
+#define __cacheline_aligned
 
 #endif /* CONFIG_KGDB */
 	
diff -u --recursive --new-file v2.2.0-pre4/linux/include/asm-m68k/resource.h linux/include/asm-m68k/resource.h
--- v2.2.0-pre4/linux/include/asm-m68k/resource.h	Wed Sep 25 00:47:42 1996
+++ linux/include/asm-m68k/resource.h	Tue Jan  5 11:20:43 1999
@@ -25,7 +25,7 @@
   {LONG_MAX, LONG_MAX}, \
   {LONG_MAX, LONG_MAX}, \
   {LONG_MAX, LONG_MAX}, \
-  {_STK_LIM, _STK_LIM}, \
+  {_STK_LIM, LONG_MAX}, \
   {       0, LONG_MAX}, \
   {LONG_MAX, LONG_MAX}, \
   {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER}, \
diff -u --recursive --new-file v2.2.0-pre4/linux/include/asm-m68k/softirq.h linux/include/asm-m68k/softirq.h
--- v2.2.0-pre4/linux/include/asm-m68k/softirq.h	Sat Sep  5 16:46:41 1998
+++ linux/include/asm-m68k/softirq.h	Tue Jan  5 11:20:43 1999
@@ -13,7 +13,7 @@
 extern inline void init_bh(int nr, void (*routine)(void))
 {
 	bh_base[nr] = routine;
-	bh_mask_count[nr] = 0;
+	atomic_set(&bh_mask_count[nr], 0);
 	bh_mask |= 1 << nr;
 }
 
@@ -29,12 +29,12 @@
 extern inline void disable_bh(int nr)
 {
 	bh_mask &= ~(1 << nr);
-	bh_mask_count[nr]++;
+	atomic_inc(&bh_mask_count[nr]);
 }
 
 extern inline void enable_bh(int nr)
 {
-	if (!--bh_mask_count[nr])
+	if (atomic_dec_and_test(&bh_mask_count[nr]))
 		bh_mask |= 1 << nr;
 }
 
diff -u --recursive --new-file v2.2.0-pre4/linux/include/asm-m68k/timex.h linux/include/asm-m68k/timex.h
--- v2.2.0-pre4/linux/include/asm-m68k/timex.h	Fri May  8 23:14:55 1998
+++ linux/include/asm-m68k/timex.h	Tue Jan  5 11:20:43 1999
@@ -12,4 +12,11 @@
 	(1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \
 		<< (SHIFT_SCALE-SHIFT_HZ)) / HZ)
 
+typedef unsigned long cycles_t;
+
+static inline cycles_t get_cycles(void)
+{
+	return 0;
+}
+
 #endif
diff -u --recursive --new-file v2.2.0-pre4/linux/include/asm-m68k/uaccess.h linux/include/asm-m68k/uaccess.h
--- v2.2.0-pre4/linux/include/asm-m68k/uaccess.h	Tue Dec 22 14:16:58 1998
+++ linux/include/asm-m68k/uaccess.h	Tue Jan  5 11:20:43 1999
@@ -184,7 +184,7 @@
 	 "   .long 5b,9b\n"
 	 ".previous"
 	 : "=a"(to), "=a"(from), "=d"(n), "=&d"(tmp)
-	 : "r"(n & 3), "0"(to), "1"(from), "2"(n/4)
+	 : "d"(n & 3), "0"(to), "1"(from), "2"(n/4)
 	 : "d0", "memory");
     return n;
 }
diff -u --recursive --new-file v2.2.0-pre4/linux/include/linux/inetdevice.h linux/include/linux/inetdevice.h
--- v2.2.0-pre4/linux/include/linux/inetdevice.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/inetdevice.h	Mon Jan  4 15:31:35 1999
@@ -76,8 +76,6 @@
 extern struct in_device	*inetdev_by_index(int);
 extern u32		inet_select_addr(struct device *dev, u32 dst, int scope);
 extern struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, u32 prefix, u32 mask);
-extern int		inet_add_bootp_addr(struct device *dev);
-extern void		inet_del_bootp_addr(struct device *dev);
 extern void		inet_forward_change(void);
 
 extern __inline__ int inet_ifa_match(u32 addr, struct in_ifaddr *ifa)
diff -u --recursive --new-file v2.2.0-pre4/linux/include/linux/ip_masq.h linux/include/linux/ip_masq.h
--- v2.2.0-pre4/linux/include/linux/ip_masq.h	Mon Oct  5 13:13:44 1998
+++ linux/include/linux/ip_masq.h	Mon Jan  4 15:31:35 1999
@@ -1,6 +1,6 @@
 /*
  *	IP_MASQ user space control interface
- *	$Id: ip_masq.h,v 1.1 1998/08/29 23:50:56 davem Exp $
+ *	$Id: ip_masq.h,v 1.2 1998/12/08 05:41:48 davem Exp $
  */
 
 #ifndef _LINUX_IP_MASQ_H
@@ -90,16 +90,19 @@
 };
 
 /* 
- *	MARKFW stuff 
+ *	MFW stuff 
  */
-struct ip_markfw_user {
+struct ip_mfw_user {
 	u_int32_t           fwmark;	/* Firewalling mark */
 	u_int32_t           raddr;  	/* remote port */
 	u_int16_t           rport;	/* remote port */
 	u_int16_t           dummy;          /* Make up to multiple of 4 */
 	int 		pref;		/* Preference value */
+	unsigned 	flags;		/* misc flags */
 };
 
+#define IP_MASQ_MFW_SCHED	0x01
+
 #define IP_FW_MASQCTL_MAX 256
 #define IP_MASQ_TNAME_MAX  32
 
@@ -110,7 +113,7 @@
 	union {
 		struct ip_portfw_user portfw_user;
 		struct ip_autofw_user autofw_user;
-		struct ip_markfw_user markfw_user;
+		struct ip_mfw_user mfw_user;
 		struct ip_masq_user user;
 		unsigned char m_raw[IP_FW_MASQCTL_MAX];
 	} u;
@@ -123,14 +126,14 @@
 #define IP_MASQ_TARGET_USER	3	
 #define IP_MASQ_TARGET_LAST	4
 
-#define IP_MASQ_CMD_NONE	0
+#define IP_MASQ_CMD_NONE	0	/* just peek */
 #define IP_MASQ_CMD_INSERT	1
 #define IP_MASQ_CMD_ADD		2
 #define IP_MASQ_CMD_SET		3
 #define IP_MASQ_CMD_DEL		4
 #define IP_MASQ_CMD_GET		5
 #define IP_MASQ_CMD_FLUSH	6
-#define IP_MASQ_CMD_LIST	7
+#define IP_MASQ_CMD_LIST	7	/* actually fake: done via /proc */
 #define IP_MASQ_CMD_ENABLE	8
 #define IP_MASQ_CMD_DISABLE	9
 
diff -u --recursive --new-file v2.2.0-pre4/linux/include/linux/mm.h linux/include/linux/mm.h
--- v2.2.0-pre4/linux/include/linux/mm.h	Fri Jan  1 12:58:21 1999
+++ linux/include/linux/mm.h	Wed Jan  6 10:07:46 1999
@@ -130,20 +130,22 @@
 #define PG_locked		 0
 #define PG_error		 1
 #define PG_referenced		 2
-#define PG_uptodate		 3
-#define PG_free_after		 4
-#define PG_decr_after		 5
-#define PG_swap_unlock_after	 6
-#define PG_DMA			 7
-#define PG_Slab			 8
-#define PG_swap_cache		 9
-#define PG_skip			10
+#define PG_dirty		 3
+#define PG_uptodate		 4
+#define PG_free_after		 5
+#define PG_decr_after		 6
+#define PG_swap_unlock_after	 7
+#define PG_DMA			 8
+#define PG_Slab			 9
+#define PG_swap_cache		10
+#define PG_skip			11
 #define PG_reserved		31
 
 /* Make it prettier to test the above... */
 #define PageLocked(page)	(test_bit(PG_locked, &(page)->flags))
 #define PageError(page)		(test_bit(PG_error, &(page)->flags))
 #define PageReferenced(page)	(test_bit(PG_referenced, &(page)->flags))
+#define PageDirty(page)		(test_bit(PG_dirty, &(page)->flags))
 #define PageUptodate(page)	(test_bit(PG_uptodate, &(page)->flags))
 #define PageFreeAfter(page)	(test_bit(PG_free_after, &(page)->flags))
 #define PageDecrAfter(page)	(test_bit(PG_decr_after, &(page)->flags))
@@ -155,12 +157,17 @@
 
 #define PageSetSlab(page)	(set_bit(PG_Slab, &(page)->flags))
 #define PageSetSwapCache(page)	(set_bit(PG_swap_cache, &(page)->flags))
+
+#define PageTestandSetDirty(page)	\
+			(test_and_set_bit(PG_dirty, &(page)->flags))
 #define PageTestandSetSwapCache(page)	\
 			(test_and_set_bit(PG_swap_cache, &(page)->flags))
 
 #define PageClearSlab(page)	(clear_bit(PG_Slab, &(page)->flags))
 #define PageClearSwapCache(page)(clear_bit(PG_swap_cache, &(page)->flags))
 
+#define PageTestandClearDirty(page) \
+			(test_and_clear_bit(PG_dirty, &(page)->flags))
 #define PageTestandClearSwapCache(page)	\
 			(test_and_clear_bit(PG_swap_cache, &(page)->flags))
 
diff -u --recursive --new-file v2.2.0-pre4/linux/include/linux/ntfs_fs_i.h linux/include/linux/ntfs_fs_i.h
--- v2.2.0-pre4/linux/include/linux/ntfs_fs_i.h	Fri Dec 19 15:24:22 1997
+++ linux/include/linux/ntfs_fs_i.h	Mon Jan  4 10:41:35 1999
@@ -5,13 +5,17 @@
 struct ntfs_attribute;
 struct ntfs_sb_info;
 
-/* Duplicate definitions from ntfs/types.h */
+/* Duplicate definitions from ntfs/ntfstypes.h */
 #ifndef NTFS_INTEGRAL_TYPES
 #define NTFS_INTEGRAL_TYPES
-typedef unsigned char      ntfs_u8;
-typedef unsigned short     ntfs_u16;
-typedef unsigned int       ntfs_u32;
-typedef unsigned long long ntfs_u64;
+typedef u8  ntfs_u8;
+typedef u16 ntfs_u16;
+typedef u32 ntfs_u32;
+typedef u64 ntfs_u64;
+typedef s8  ntfs_s8;
+typedef s16 ntfs_s16;
+typedef s32 ntfs_s32;
+typedef s64 ntfs_s64;
 #endif
 
 #ifndef NTMODE_T
diff -u --recursive --new-file v2.2.0-pre4/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.2.0-pre4/linux/include/linux/pci.h	Mon Jan  4 15:08:18 1999
+++ linux/include/linux/pci.h	Wed Jan  6 10:07:41 1999
@@ -1055,6 +1055,7 @@
 
 #define PCI_VENDOR_ID_ADAPTEC2		0x9005
 #define PCI_DEVICE_ID_ADAPTEC2_2940U2	0x0010
+#define PCI_DEVICE_ID_ADAPTEC2_78902	0x0013
 #define PCI_DEVICE_ID_ADAPTEC2_7890	0x001f
 #define PCI_DEVICE_ID_ADAPTEC2_3940U2	0x0050
 #define PCI_DEVICE_ID_ADAPTEC2_3950U2D	0x0051
diff -u --recursive --new-file v2.2.0-pre4/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.2.0-pre4/linux/include/linux/sched.h	Fri Jan  1 12:58:21 1999
+++ linux/include/linux/sched.h	Wed Jan  6 10:07:44 1999
@@ -458,6 +458,8 @@
 
 extern void FASTCALL(__wake_up(struct wait_queue ** p, unsigned int mode));
 extern void FASTCALL(sleep_on(struct wait_queue ** p));
+extern long FASTCALL(sleep_on_timeout(struct wait_queue ** p,
+				      signed long timeout));
 extern void FASTCALL(interruptible_sleep_on(struct wait_queue ** p));
 extern long FASTCALL(interruptible_sleep_on_timeout(struct wait_queue ** p,
 						    signed long timeout));
diff -u --recursive --new-file v2.2.0-pre4/linux/include/linux/sound.h linux/include/linux/sound.h
--- v2.2.0-pre4/linux/include/linux/sound.h	Wed Sep  9 14:51:13 1998
+++ linux/include/linux/sound.h	Mon Jan  4 11:42:43 1999
@@ -2,11 +2,11 @@
  *	Sound core interface functions
  */
  
-extern int register_sound_special(struct file_operations *, int);
-extern int register_sound_mixer(struct file_operations *fops);
-extern int register_sound_midi(struct file_operations *fops);
-extern int register_sound_dsp(struct file_operations *fops);
-extern int register_sound_synth(struct file_operations *fops);
+extern int register_sound_special(struct file_operations *fops, int unit);
+extern int register_sound_mixer(struct file_operations *fops, int dev);
+extern int register_sound_midi(struct file_operations *fops, int dev);
+extern int register_sound_dsp(struct file_operations *fops, int dev);
+extern int register_sound_synth(struct file_operations *fops, int dev);
 
 extern void unregister_sound_special(int unit);
 extern void unregister_sound_mixer(int unit);
diff -u --recursive --new-file v2.2.0-pre4/linux/include/linux/swap.h linux/include/linux/swap.h
--- v2.2.0-pre4/linux/include/linux/swap.h	Mon Dec 28 15:00:53 1998
+++ linux/include/linux/swap.h	Wed Jan  6 10:07:42 1999
@@ -166,12 +166,7 @@
 		return 1;
 	count = atomic_read(&page->count);
 	if (PageSwapCache(page))
-	{
-		/* PARANOID */
-		if (page->inode != &swapper_inode)
-			panic("swap cache page has wrong inode\n");
 		count += swap_count(page->offset) - 2;
-	}
 	if (PageFreeAfter(page))
 		count--;
 	return  count > 1;
diff -u --recursive --new-file v2.2.0-pre4/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.2.0-pre4/linux/include/linux/sysctl.h	Thu Dec 31 10:29:03 1998
+++ linux/include/linux/sysctl.h	Mon Jan  4 15:31:35 1999
@@ -207,7 +207,8 @@
 	NET_IPV4_ICMP_TIMEEXCEED_RATE=61,
 	NET_IPV4_ICMP_PARAMPROB_RATE=62,
 	NET_IPV4_ICMP_ECHOREPLY_RATE=63,
-	NET_IPV4_ICMP_IGNORE_BOGUS_ERROR_RESPONSES=64
+	NET_IPV4_ICMP_IGNORE_BOGUS_ERROR_RESPONSES=64,
+	NET_IPV4_IGMP_MAX_MEMBERSHIPS=65
 };
 
 enum {
diff -u --recursive --new-file v2.2.0-pre4/linux/include/linux/wavefront.h linux/include/linux/wavefront.h
--- v2.2.0-pre4/linux/include/linux/wavefront.h	Thu Sep 17 17:53:38 1998
+++ linux/include/linux/wavefront.h	Mon Jan  4 11:42:43 1999
@@ -307,7 +307,7 @@
     UCHAR8 mute:1;
 
     UCHAR8 split_point:7;
-    UCHAR8 updown:1;
+    UCHAR8 play_below:1;
 
     UCHAR8 pan_mod_src:2;
     UCHAR8 pan_or_mod:1;
@@ -507,9 +507,16 @@
 
 typedef struct wf_patch_info {
     
+    /* the first two fields are used by the OSS "patch loading" interface
+       only, and are unused by the current user-level library.
+    */
+
     INT16   key;               /* Use WAVEFRONT_PATCH here */
     UINT16  devno;             /* fill in when sending */
     UCHAR8  subkey;            /* WF_ST_{SAMPLE,ALIAS,etc.} */
+
+#define WAVEFRONT_FIND_FREE_SAMPLE_SLOT 999
+
     UINT16  number;            /* patch/sample/prog number */
 
     UINT32  size;              /* size of any data included in 
@@ -548,12 +555,14 @@
  */
 
 typedef struct wavefront_control {
-    int devno;                         /* from /dev/sequencer interface */
     int cmd;                           /* WFC_* */
     char status;                       /* return status to user-space */
     unsigned char rbuf[WF_MAX_READ];   /* bytes read from card */
     unsigned char wbuf[WF_MAX_WRITE];  /* bytes written to card */
 } wavefront_control;
+
+#define WFCTL_WFCMD    0x1
+#define WFCTL_LOAD_SPP 0x2
 
 /* Modulator table */
 
diff -u --recursive --new-file v2.2.0-pre4/linux/include/net/ipconfig.h linux/include/net/ipconfig.h
--- v2.2.0-pre4/linux/include/net/ipconfig.h	Sun Nov 30 14:00:39 1997
+++ linux/include/net/ipconfig.h	Mon Jan  4 15:31:35 1999
@@ -1,5 +1,5 @@
 /*
- *  $Id: ipconfig.h,v 1.2 1997/10/17 12:41:16 mj Exp $
+ *  $Id: ipconfig.h,v 1.3 1999/01/04 20:13:29 davem Exp $
  *
  *  Copyright (C) 1997 Martin Mares
  *
@@ -12,8 +12,10 @@
 extern u32 ic_servaddr;
 extern u32 ic_gateway;
 extern u32 ic_netmask;
-extern int ic_bootp_flag;
-extern int ic_rarp_flag;
 extern int ic_enable;
 extern int ic_host_name_set;
 extern int ic_set_manually;
+extern int ic_proto_enabled;
+
+#define IC_BOOTP 1
+#define IC_RARP 2
diff -u --recursive --new-file v2.2.0-pre4/linux/ipc/util.c linux/ipc/util.c
--- v2.2.0-pre4/linux/ipc/util.c	Mon Jan  4 15:08:18 1999
+++ linux/ipc/util.c	Mon Jan  4 14:38:48 1999
@@ -13,8 +13,6 @@
 #include <linux/shm.h>
 #include <linux/init.h>
 #include <linux/msg.h>
-#include <asm/ipc.h>
-#include <asm/uaccess.h>
 
 #if defined(CONFIG_SYSVIPC)
 
diff -u --recursive --new-file v2.2.0-pre4/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.2.0-pre4/linux/kernel/ksyms.c	Thu Dec 31 10:29:03 1998
+++ linux/kernel/ksyms.c	Wed Jan  6 09:41:00 1999
@@ -106,8 +106,10 @@
 EXPORT_SYMBOL(vmtruncate);
 
 /* filesystem internal functions */
+EXPORT_SYMBOL(in_group_p);
 EXPORT_SYMBOL(update_atime);
 EXPORT_SYMBOL(get_super);
+EXPORT_SYMBOL(get_fs_type);
 EXPORT_SYMBOL(getname);
 EXPORT_SYMBOL(__fput);
 EXPORT_SYMBOL(iget);
@@ -128,6 +130,7 @@
 EXPORT_SYMBOL(__mark_inode_dirty);
 EXPORT_SYMBOL(get_empty_filp);
 EXPORT_SYMBOL(init_private_file);
+EXPORT_SYMBOL(filp_open);
 EXPORT_SYMBOL(fput);
 EXPORT_SYMBOL(put_filp);
 EXPORT_SYMBOL(check_disk_change);
@@ -296,6 +299,7 @@
 /* process management */
 EXPORT_SYMBOL(__wake_up);
 EXPORT_SYMBOL(sleep_on);
+EXPORT_SYMBOL(sleep_on_timeout);
 EXPORT_SYMBOL(interruptible_sleep_on);
 EXPORT_SYMBOL(interruptible_sleep_on_timeout);
 EXPORT_SYMBOL(schedule);
diff -u --recursive --new-file v2.2.0-pre4/linux/kernel/sched.c linux/kernel/sched.c
--- v2.2.0-pre4/linux/kernel/sched.c	Thu Dec 31 10:29:03 1998
+++ linux/kernel/sched.c	Mon Jan  4 10:23:42 1999
@@ -161,8 +161,10 @@
  * In particular, if p1 and p2 both want the kernel
  * lock, there is no point in trying to make them
  * extremely parallel..
+ *
+ * (No lock - lock_depth < 0)
  */
-#define related(p1,p2) ((p1)->lock_depth && (p2)->lock_depth)
+#define related(p1,p2) ((p1)->lock_depth >= 0 && (p2)->lock_depth >= 0)
 
 static inline void reschedule_idle(struct task_struct * p)
 {
@@ -854,56 +856,64 @@
  * Either form may be used in conjunction with "up()".
  *
  */
-static inline int __do_down(struct semaphore * sem, int task_state)
-{
-	struct task_struct *tsk = current;
-	struct wait_queue wait = { tsk, NULL };
-	int		  ret = 0;
 
-	tsk->state = task_state;
-	add_wait_queue(&sem->wait, &wait);
+#define DOWN_VAR				\
+	struct task_struct *tsk = current;	\
+	struct wait_queue wait = { tsk, NULL };
 
-	/*
-	 * Ok, we're set up.  sem->count is known to be less than zero
-	 * so we must wait.
-	 *
-	 * We can let go the lock for purposes of waiting.
-	 * We re-acquire it after awaking so as to protect
-	 * all semaphore operations.
-	 *
-	 * If "up()" is called before we call waking_non_zero() then
-	 * we will catch it right away.  If it is called later then
-	 * we will have to go through a wakeup cycle to catch it.
-	 *
-	 * Multiple waiters contend for the semaphore lock to see
-	 * who gets to gate through and who has to wait some more.
-	 */
-	for (;;) {
-		if (waking_non_zero(sem))	/* are we waking up?  */
+#define DOWN_HEAD(task_state)						 \
+									 \
+									 \
+	tsk->state = (task_state);					 \
+	add_wait_queue(&sem->wait, &wait);				 \
+									 \
+	/*								 \
+	 * Ok, we're set up.  sem->count is known to be less than zero	 \
+	 * so we must wait.						 \
+	 *								 \
+	 * We can let go the lock for purposes of waiting.		 \
+	 * We re-acquire it after awaking so as to protect		 \
+	 * all semaphore operations.					 \
+	 *								 \
+	 * If "up()" is called before we call waking_non_zero() then	 \
+	 * we will catch it right away.  If it is called later then	 \
+	 * we will have to go through a wakeup cycle to catch it.	 \
+	 *								 \
+	 * Multiple waiters contend for the semaphore lock to see	 \
+	 * who gets to gate through and who has to wait some more.	 \
+	 */								 \
+	for (;;) {							 \
+		if (waking_non_zero(sem))	/* are we waking up?  */ \
 			break;			/* yes, exit loop */
 
-		if (task_state == TASK_INTERRUPTIBLE && signal_pending(tsk)) {
-			ret = -EINTR;			/* interrupted */
-			atomic_inc(&sem->count);	/* give up on down operation */
-			break;
-		}
-
-		schedule();
-		tsk->state = task_state;
-	}
-	tsk->state = TASK_RUNNING;
+#define DOWN_TAIL(task_state)			\
+		tsk->state = (task_state);	\
+	}					\
+	tsk->state = TASK_RUNNING;		\
 	remove_wait_queue(&sem->wait, &wait);
-	return ret;
-}
 
 void __down(struct semaphore * sem)
 {
-	__do_down(sem,TASK_UNINTERRUPTIBLE);
+	DOWN_VAR
+	DOWN_HEAD(TASK_UNINTERRUPTIBLE)
+	schedule();
+	DOWN_TAIL(TASK_UNINTERRUPTIBLE)
 }
 
 int __down_interruptible(struct semaphore * sem)
 {
-	return __do_down(sem,TASK_INTERRUPTIBLE);
+	DOWN_VAR
+	int ret = 0;
+	DOWN_HEAD(TASK_INTERRUPTIBLE)
+	if (signal_pending(tsk))
+	{
+		ret = -EINTR;			/* interrupted */
+		atomic_inc(&sem->count);	/* give up on down operation */
+		break;
+	}
+	schedule();
+	DOWN_TAIL(TASK_INTERRUPTIBLE)
+	return ret;
 }
 
 #define	SLEEP_ON_VAR				\
@@ -954,6 +964,19 @@
 	SLEEP_ON_HEAD
 	schedule();
 	SLEEP_ON_TAIL
+}
+
+long sleep_on_timeout(struct wait_queue **p, long timeout)
+{
+	SLEEP_ON_VAR
+	
+	current->state = TASK_UNINTERRUPTIBLE;
+
+	SLEEP_ON_HEAD
+	timeout = schedule_timeout(timeout);
+	SLEEP_ON_TAIL
+
+	return timeout;
 }
 
 void scheduling_functions_end_here(void) { }
diff -u --recursive --new-file v2.2.0-pre4/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.2.0-pre4/linux/kernel/sysctl.c	Thu Dec 31 10:29:03 1998
+++ linux/kernel/sysctl.c	Mon Jan  4 11:42:43 1999
@@ -44,6 +44,9 @@
 #ifdef CONFIG_CHR_DEV_SG
 extern int sg_big_buff;
 #endif
+#ifdef CONFIG_SYSVIPC
+extern int shmmax;
+#endif
 
 #ifdef __sparc__
 extern char reboot_command [];
@@ -205,6 +208,10 @@
 	 0444, NULL, &proc_dointvec},
 	{KERN_RTSIGMAX, "rtsig-max", &max_queued_signals, sizeof(int),
 	 0644, NULL, &proc_dointvec},
+#ifdef CONFIG_SYSVIPC
+	{KERN_SHMMAX, "shmmax", &shmmax, sizeof (int),
+	 0644, NULL, &proc_dointvec},
+#endif
 	{0}
 };
 
diff -u --recursive --new-file v2.2.0-pre4/linux/mm/filemap.c linux/mm/filemap.c
--- v2.2.0-pre4/linux/mm/filemap.c	Fri Jan  1 12:58:21 1999
+++ linux/mm/filemap.c	Wed Jan  6 09:51:49 1999
@@ -125,10 +125,16 @@
 	struct page * page;
 	int count;
 
-	count = limit >> priority;
+	count = (limit << 1) >> priority;
 
 	page = mem_map + clock;
 	do {
+		int referenced;
+
+		/* This works even in the presence of PageSkip because
+		 * the first two entries at the beginning of a hole will
+		 * be marked, not just the first.
+		 */
 		page++;
 		clock++;
 		if (clock >= max_mapnr) {
@@ -141,11 +147,9 @@
 			clock = page->map_nr;
 		}
 		
-		if (test_and_clear_bit(PG_referenced, &page->flags))
-			continue;
-
-		/* Decrement count only for non-referenced pages */
 		count--;
+		referenced = test_and_clear_bit(PG_referenced, &page->flags);
+
 		if (PageLocked(page))
 			continue;
 
@@ -156,6 +160,21 @@
 		if (atomic_read(&page->count) != 1)
 			continue;
 
+		/*
+		 * Is it a page swap page? If so, we want to
+		 * drop it if it is no longer used, even if it
+		 * were to be marked referenced..
+		 */
+		if (PageSwapCache(page)) {
+			if (referenced && swap_count(page->offset) != 1)
+				continue;
+			delete_from_swap_cache(page);
+			return 1;
+		}	
+
+		if (referenced)
+			continue;
+
 		/* Is it a buffer page? */
 		if (page->buffers) {
 			if (buffer_under_min())
@@ -165,14 +184,10 @@
 			return 1;
 		}
 
-		/* is it a swap-cache or page-cache page? */
+		/* is it a page-cache page? */
 		if (page->inode) {
 			if (pgcache_under_min())
 				continue;
-			if (PageSwapCache(page)) {
-				delete_from_swap_cache(page);
-				return 1;
-			}
 			remove_inode_page(page);
 			return 1;
 		}
diff -u --recursive --new-file v2.2.0-pre4/linux/mm/memory.c linux/mm/memory.c
--- v2.2.0-pre4/linux/mm/memory.c	Fri Nov 27 13:09:30 1998
+++ linux/mm/memory.c	Wed Jan  6 02:18:29 1999
@@ -642,37 +642,47 @@
 	page_map = mem_map + MAP_NR(old_page);
 	
 	/*
-	 * Do we need to copy?
+	 * We can avoid the copy if:
+	 * - we're the only user (count == 1)
+	 * - the only other user is the swap cache,
+	 *   and the only swap cache user is itself,
+	 *   in which case we can remove the page
+	 *   from the swap cache.
 	 */
-	if (is_page_shared(page_map)) {
+	switch (atomic_read(&page_map->count)) {
+	case 2:
+		if (!PageSwapCache(page_map))
+			break;
+		if (swap_count(page_map->offset) != 1)
+			break;
+		delete_from_swap_cache(page_map);
+		/* FallThrough */
+	case 1:
+		/* We can release the kernel lock now.. */
 		unlock_kernel();
-		if (!new_page)
-			return 0;
 
-		if (PageReserved(mem_map + MAP_NR(old_page)))
-			++vma->vm_mm->rss;
-		copy_cow_page(old_page,new_page);
-		flush_page_to_ram(old_page);
-		flush_page_to_ram(new_page);
 		flush_cache_page(vma, address);
-		set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
-		free_page(old_page);
+		set_pte(page_table, pte_mkdirty(pte_mkwrite(pte)));
 		flush_tlb_page(vma, address);
+end_wp_page:
+		if (new_page)
+			free_page(new_page);
 		return 1;
 	}
-
-	if (PageSwapCache(page_map))
-		delete_from_swap_cache(page_map);
-
-	/* We can release the kernel lock now.. */
+		
 	unlock_kernel();
+	if (!new_page)
+		return 0;
 
+	if (PageReserved(mem_map + MAP_NR(old_page)))
+		++vma->vm_mm->rss;
+	copy_cow_page(old_page,new_page);
+	flush_page_to_ram(old_page);
+	flush_page_to_ram(new_page);
 	flush_cache_page(vma, address);
-	set_pte(page_table, pte_mkdirty(pte_mkwrite(pte)));
+	set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
+	free_page(old_page);
 	flush_tlb_page(vma, address);
-end_wp_page:
-	if (new_page)
-		free_page(new_page);
 	return 1;
 
 bad_wp_page:
diff -u --recursive --new-file v2.2.0-pre4/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.2.0-pre4/linux/mm/page_alloc.c	Mon Jan  4 15:08:18 1999
+++ linux/mm/page_alloc.c	Tue Jan  5 17:55:58 1999
@@ -245,6 +245,14 @@
 		}
 
 		/*
+		 * If this is a recursive call, we'd better
+		 * do our best to just allocate things without
+		 * further thought.
+		 */
+		if (current->flags & PF_MEMALLOC)
+			goto ok_to_allocate;
+
+		/*
 		 * Avoid going back-and-forth between allocating
 		 * memory and trying to free it. If we get into
 		 * a bad memory situation, we're better off trying
@@ -271,8 +279,14 @@
 		 * memory.
 		 */
 		current->trashing_memory = 1;
-		if (!try_to_free_pages(gfp_mask, SWAP_CLUSTER_MAX) && !(gfp_mask & (__GFP_MED | __GFP_HIGH)))
-			goto nopage;
+		{
+			int freed;
+			current->flags |= PF_MEMALLOC;
+			freed = try_to_free_pages(gfp_mask, SWAP_CLUSTER_MAX);
+			current->flags &= ~PF_MEMALLOC;
+			if (!freed && !(gfp_mask & (__GFP_MED | __GFP_HIGH)))
+				goto nopage;
+		}
 	}
 ok_to_allocate:
 	spin_lock_irqsave(&page_alloc_lock, flags);
diff -u --recursive --new-file v2.2.0-pre4/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.2.0-pre4/linux/mm/vmscan.c	Mon Jan  4 15:08:18 1999
+++ linux/mm/vmscan.c	Wed Jan  6 02:03:12 1999
@@ -20,13 +20,6 @@
 
 #include <asm/pgtable.h>
 
-/* 
- * The wait queue for waking up the pageout daemon:
- */
-static struct task_struct * kswapd_task = NULL;
-
-static void init_swap_timer(void);
-
 /*
  * The swap-out functions return 1 if they successfully
  * threw something out, and we got a free page. It returns
@@ -38,7 +31,7 @@
  * using a process that no longer actually exists (it might
  * have died while we slept).
  */
-static inline int try_to_swap_out(struct task_struct * tsk, struct vm_area_struct* vma,
+static int try_to_swap_out(struct task_struct * tsk, struct vm_area_struct* vma,
 	unsigned long address, pte_t * page_table, int gfp_mask)
 {
 	pte_t pte;
@@ -59,50 +52,6 @@
 	    || ((gfp_mask & __GFP_DMA) && !PageDMA(page_map)))
 		return 0;
 
-	/* 
-	 * Deal with page aging.  There are several special cases to
-	 * consider:
-	 * 
-	 * Page has been accessed, but is swap cached.  If the page is
-	 * getting sufficiently "interesting" --- its age is getting
-	 * high --- then if we are sufficiently short of free swap
-	 * pages, then delete the swap cache.  We can only do this if
-	 * the swap page's reference count is one: ie. there are no
-	 * other references to it beyond the swap cache (as there must
-	 * still be PTEs pointing to it if count > 1).
-	 * 
-	 * If the page has NOT been touched, and its age reaches zero,
-	 * then we are swapping it out:
-	 *
-	 *   If there is already a swap cache page for this page, then
-	 *   another process has already allocated swap space, so just
-	 *   dereference the physical page and copy in the swap entry
-	 *   from the swap cache.  
-	 * 
-	 * Note, we rely on all pages read in from swap either having
-	 * the swap cache flag set, OR being marked writable in the pte,
-	 * but NEVER BOTH.  (It IS legal to be neither cached nor dirty,
-	 * however.)
-	 *
-	 * -- Stephen Tweedie 1998 */
-
-	if (PageSwapCache(page_map)) {
-		if (pte_write(pte)) {
-			struct page *found;
-			printk ("VM: Found a writable swap-cached page!\n");
-			/* Try to diagnose the problem ... */
-			found = find_page(&swapper_inode, page_map->offset);
-			if (found) {
-				printk("page=%p@%08lx, found=%p, count=%d\n",
-					page_map, page_map->offset,
-					found, atomic_read(&found->count));
-				__free_page(found);
-			} else 
-				printk ("Spurious, page not in cache\n");
-			return 0;
-		}
-	}
-	
 	if (pte_young(pte)) {
 		/*
 		 * Transfer the "accessed" bit from the page
@@ -110,109 +59,101 @@
 		 */
 		set_pte(page_table, pte_mkold(pte));
 		set_bit(PG_referenced, &page_map->flags);
-
-		/* 
-		 * We should test here to see if we want to recover any
-		 * swap cache page here.  We do this if the page seeing
-		 * enough activity, AND we are sufficiently low on swap
-		 *
-		 * We need to track both the number of available swap
-		 * pages and the total number present before we can do
-		 * this...  
-		 */
 		return 0;
 	}
 
-	if (pte_dirty(pte)) {
-		if (vma->vm_ops && vma->vm_ops->swapout) {
-			pid_t pid = tsk->pid;
-			vma->vm_mm->rss--;
-			if (vma->vm_ops->swapout(vma, address - vma->vm_start + vma->vm_offset, page_table))
-				kill_proc(pid, SIGBUS, 1);
-		} else {
-			/*
-			 * This is a dirty, swappable page.  First of all,
-			 * get a suitable swap entry for it, and make sure
-			 * we have the swap cache set up to associate the
-			 * page with that swap entry.
-			 */
-        		entry = in_swap_cache(page_map);
-			if (!entry) {
-				entry = get_swap_page();
-				if (!entry)
-					return 0; /* No swap space left */
-			}
-			
-			vma->vm_mm->rss--;
-			tsk->nswap++;
-			flush_cache_page(vma, address);
-			set_pte(page_table, __pte(entry));
-			flush_tlb_page(vma, address);
-			swap_duplicate(entry);
-
-			/* Now to write back the page.  We have two
-			 * cases: if the page is already part of the
-			 * swap cache, then it is already on disk.  Just
-			 * free the page and return (we release the swap
-			 * cache on the last accessor too).
-			 *
-			 * If we have made a new swap entry, then we
-			 * start the write out to disk.  If the page is
-			 * shared, however, we still need to keep the
-			 * copy in memory, so we add it to the swap
-			 * cache. */
-			if (PageSwapCache(page_map)) {
-				__free_page(page_map);
-				return (atomic_read(&page_map->count) == 0);
-			}
-			add_to_swap_cache(page_map, entry);
-			/* We checked we were unlocked way up above, and we
-			   have been careful not to stall until here */
-			set_bit(PG_locked, &page_map->flags);
-			/* OK, do a physical write to swap.  */
-			rw_swap_page(WRITE, entry, (char *) page, (gfp_mask & __GFP_WAIT));
-		}
-		/* Now we can free the current physical page.  We also
-		 * free up the swap cache if this is the last use of the
-		 * page.  Note that there is a race here: the page may
-		 * still be shared COW by another process, but that
-		 * process may exit while we are writing out the page
-		 * asynchronously.  That's no problem, shrink_mmap() can
-		 * correctly clean up the occassional unshared page
-		 * which gets left behind in the swap cache. */
+	/*
+	 * Is the page already in the swap cache? If so, then
+	 * we can just drop our reference to it without doing
+	 * any IO - it's already up-to-date on disk.
+	 *
+	 * Return 0, as we didn't actually free any real
+	 * memory, and we should just continue our scan.
+	 */
+	if (PageSwapCache(page_map)) {
+		entry = page_map->offset;
+		swap_duplicate(entry);
+		set_pte(page_table, __pte(entry));
+drop_pte:
+		vma->vm_mm->rss--;
+		tsk->nswap++;
+		flush_tlb_page(vma, address);
 		__free_page(page_map);
-		return 1;	/* we slept: the process may not exist any more */
+		return 0;
 	}
 
-	/* The page was _not_ dirty, but still has a zero age.  It must
-	 * already be uptodate on disk.  If it is in the swap cache,
-	 * then we can just unlink the page now.  Remove the swap cache
-	 * too if this is the last user.  */
-        if ((entry = in_swap_cache(page_map)))  {
+	/*
+	 * Is it a clean page? Then it must be recoverable
+	 * by just paging it in again, and we can just drop
+	 * it..
+	 *
+	 * However, this won't actually free any real
+	 * memory, as the page will just be in the page cache
+	 * somewhere, and as such we should just continue
+	 * our scan.
+	 *
+	 * Basically, this just makes it possible for us to do
+	 * some real work in the future in "shrink_mmap()".
+	 */
+	if (!pte_dirty(pte)) {
+		pte_clear(page_table);
+		goto drop_pte;
+	}
+
+	/*
+	 * Ok, it's really dirty. That means that
+	 * we should either create a new swap cache
+	 * entry for it, or we should write it back
+	 * to its own backing store.
+	 *
+	 * Note that in neither case do we actually
+	 * know that we make a page available, but
+	 * as we potentially sleep we can no longer
+	 * continue scanning, so we migth as well
+	 * assume we free'd something.
+	 *
+	 * NOTE NOTE NOTE! This should just set a
+	 * dirty bit in page_map, and just drop the
+	 * pte. All the hard work would be done by
+	 * shrink_mmap().
+	 *
+	 * That would get rid of a lot of problems.
+	 */
+	if (vma->vm_ops && vma->vm_ops->swapout) {
+		pid_t pid = tsk->pid;
 		vma->vm_mm->rss--;
-		flush_cache_page(vma, address);
-		set_pte(page_table, __pte(entry));
-		flush_tlb_page(vma, address);
-		swap_duplicate(entry);
+		if (vma->vm_ops->swapout(vma, address - vma->vm_start + vma->vm_offset, page_table))
+			kill_proc(pid, SIGBUS, 1);
 		__free_page(page_map);
-		return (atomic_read(&page_map->count) == 0);
-	} 
-	/* 
-	 * A clean page to be discarded?  Must be mmap()ed from
-	 * somewhere.  Unlink the pte, and tell the filemap code to
-	 * discard any cached backing page if this is the last user.
-	 */
-	if (PageSwapCache(page_map)) {
-		printk ("VM: How can this page _still_ be cached?");
-		return 0;
+		return 1;
 	}
+
+	/*
+	 * This is a dirty, swappable page.  First of all,
+	 * get a suitable swap entry for it, and make sure
+	 * we have the swap cache set up to associate the
+	 * page with that swap entry.
+	 */
+	entry = get_swap_page();
+	if (!entry)
+		return 0; /* No swap space left */
+		
 	vma->vm_mm->rss--;
+	tsk->nswap++;
 	flush_cache_page(vma, address);
-	pte_clear(page_table);
+	set_pte(page_table, __pte(entry));
 	flush_tlb_page(vma, address);
-	entry = (atomic_read(&page_map->count) == 1);
+	swap_duplicate(entry);	/* One for the process, one for the swap cache */
+	add_to_swap_cache(page_map, entry);
+	/* We checked we were unlocked way up above, and we
+	   have been careful not to stall until here */
+	set_bit(PG_locked, &page_map->flags);
+
+	/* OK, do a physical asynchronous write to swap.  */
+	rw_swap_page(WRITE, entry, (char *) page, 0);
+
 	__free_page(page_map);
-	return entry;
+	return 1;
 }
 
 /*
@@ -409,11 +350,7 @@
 			goto out;
 		}
 
-		/*
-		 * Nonzero means we cleared out something, but only "1" means
-		 * that we actually free'd up a page as a result.
-		 */
-		if (swap_out_process(pbest, gfp_mask) == 1)
+		if (swap_out_process(pbest, gfp_mask))
 			return 1;
 	}
 out:
@@ -441,71 +378,36 @@
        printk ("Starting kswapd v%.*s\n", i, s);
 }
 
-#define free_memory(fn) \
-	count++; do { if (!--count) goto done; } while (fn)
-
-static int kswapd_free_pages(int kswapd_state)
-{
-	unsigned long end_time;
-
-	/* Always trim SLAB caches when memory gets low. */
-	kmem_cache_reap(0);
-
-	/* max one hundreth of a second */
-	end_time = jiffies + (HZ-1)/100;
-	do {
-		int priority = 8;
-		int count = pager_daemon.swap_cluster;
-
-		switch (kswapd_state) {
-			do {
-			default:
-				free_memory(shrink_mmap(priority, 0));
-				free_memory(swap_out(priority, 0));
-				kswapd_state++;
-			case 1:
-				free_memory(shm_swap(priority, 0));
-				shrink_dcache_memory(priority, 0);
-				kswapd_state = 0;
-			} while (--priority >= 0);
-			return kswapd_state;
-		}
-done:
-		if (nr_free_pages > freepages.high + pager_daemon.swap_cluster)
-			break;
-	} while (time_before_eq(jiffies,end_time));
-	return kswapd_state;
-}
-
 /*
- * The background pageout daemon.
- * Started as a kernel thread from the init process.
+ * The background pageout daemon, started as a kernel thread
+ * from the init process. 
+ *
+ * This basically executes once a second, trickling out pages
+ * so that we have _some_ free memory available even if there
+ * is no other activity that frees anything up. This is needed
+ * for things like routing etc, where we otherwise might have
+ * all activity going on in asynchronous contexts that cannot
+ * page things out.
+ *
+ * If there are applications that are active memory-allocators
+ * (most normal use), this basically shouldn't matter.
  */
 int kswapd(void *unused)
 {
 	current->session = 1;
 	current->pgrp = 1;
 	strcpy(current->comm, "kswapd");
-	sigfillset(&current->blocked);
-	
-	/*
-	 *	As a kernel thread we want to tamper with system buffers
-	 *	and other internals and thus be subject to the SMP locking
-	 *	rules. (On a uniprocessor box this does nothing).
-	 */
-	lock_kernel();
 
 	/*
-	 * Set the base priority to something smaller than a
-	 * regular process. We will scale up the priority
-	 * dynamically depending on how much memory we need.
+	 * Hey, if somebody wants to kill us, be our guest. 
+	 * Don't come running to mama if things don't work.
 	 */
-	current->priority = (DEF_PRIORITY * 2) / 3;
-
+	siginitsetinv(&current->blocked, sigmask(SIGKILL));
+	
 	/*
 	 * Tell the memory management that we're a "memory allocator",
 	 * and that if we need more memory we should get access to it
-	 * regardless (see "try_to_free_pages()"). "kswapd" should
+	 * regardless (see "__get_free_pages()"). "kswapd" should
 	 * never get caught in the normal page freeing logic.
 	 *
 	 * (Kswapd normally doesn't need memory anyway, but sometimes
@@ -516,21 +418,23 @@
 	 */
 	current->flags |= PF_MEMALLOC;
 
-	init_swap_timer();
-	kswapd_task = current;
 	while (1) {
-		int state = 0;
-
+		if (signal_pending(current))
+			break;
 		current->state = TASK_INTERRUPTIBLE;
-		flush_signals(current);
 		run_task_queue(&tq_disk);
-		schedule();
-		swapstats.wakeups++;
-		state = kswapd_free_pages(state);
+		schedule_timeout(HZ);
+
+		/*
+		 * kswapd isn't even meant to keep up with anything,
+		 * so just a few pages per second is plenty: the only
+		 * point is to make sure that the system doesn't stay
+		 * forever in a really bad memory squeeze.
+		 */
+		if (nr_free_pages < freepages.high)
+			try_to_free_pages(0, 16);
 	}
-	/* As if we could ever get here - maybe we want to make this killable */
-	kswapd_task = NULL;
-	unlock_kernel();
+
 	return 0;
 }
 
@@ -539,111 +443,42 @@
  * now we need this so that we can do page allocations
  * without holding the kernel lock etc.
  *
- * The "PF_MEMALLOC" flag protects us against recursion:
- * if we need more memory as part of a swap-out effort we
- * will just silently return "success" to tell the page
- * allocator to accept the allocation.
- *
  * We want to try to free "count" pages, and we need to 
  * cluster them so that we get good swap-out behaviour. See
  * the "free_memory()" macro for details.
  */
 int try_to_free_pages(unsigned int gfp_mask, int count)
 {
-	int retval;
+	int priority;
 
 	lock_kernel();
 
 	/* Always trim SLAB caches when memory gets low. */
 	kmem_cache_reap(gfp_mask);
 
-	retval = 1;
-	if (!(current->flags & PF_MEMALLOC)) {
-		int priority;
+	priority = 6;
+	do {
+		while (shrink_mmap(priority, gfp_mask)) {
+			if (!--count)
+				goto done;
+		}
 
-		current->flags |= PF_MEMALLOC;
+		/* Try to get rid of some shared memory pages.. */
+		while (shm_swap(priority, gfp_mask)) {
+			if (!--count)
+				goto done;
+		}
 	
-		priority = 8;
-		do {
-			free_memory(shrink_mmap(priority, gfp_mask));
-			free_memory(shm_swap(priority, gfp_mask));
-			free_memory(swap_out(priority, gfp_mask));
-			shrink_dcache_memory(priority, gfp_mask);
-		} while (--priority >= 0);
-		retval = 0;
+		/* Then, try to page stuff out.. */
+		while (swap_out(priority, gfp_mask)) {
+			if (!--count)
+				goto done;
+		}
+
+		shrink_dcache_memory(priority, gfp_mask);
+	} while (--priority >= 0);
 done:
-		current->flags &= ~PF_MEMALLOC;
-	}
 	unlock_kernel();
 
-	return retval;
-}
-
-/*
- * Wake up kswapd according to the priority
- *	0 - no wakeup
- *	1 - wake up as a low-priority process
- *	2 - wake up as a normal process
- *	3 - wake up as an almost real-time process
- *
- * This plays mind-games with the "goodness()"
- * function in kernel/sched.c.
- */
-static inline void kswapd_wakeup(struct task_struct *p, int priority)
-{
-	if (priority) {
-		p->counter = p->priority << priority;
-		wake_up_process(p);
-	}
-}
-
-/* 
- * The swap_tick function gets called on every clock tick.
- */
-void swap_tick(void)
-{
-	struct task_struct *p = kswapd_task;
-
-	/*
-	 * Only bother to try to wake kswapd up
-	 * if the task exists and can be woken.
-	 */
-	if (p && (p->state & TASK_INTERRUPTIBLE)) {
-		unsigned int pages;
-		int want_wakeup;
-
-		/*
-		 * Schedule for wakeup if there isn't lots
-		 * of free memory or if there is too much
-		 * of it used for buffers or pgcache.
-		 *
-		 * "want_wakeup" is our priority: 0 means
-		 * not to wake anything up, while 3 means
-		 * that we'd better give kswapd a realtime
-		 * priority.
-		 */
-		want_wakeup = 0;
-		pages = nr_free_pages;
-		if (pages < freepages.high)
-			want_wakeup = 1;
-		if (pages < freepages.low)
-			want_wakeup = 2;
-		if (pages < freepages.min)
-			want_wakeup = 3;
-	
-		kswapd_wakeup(p,want_wakeup);
-	}
-
-	timer_active |= (1<<SWAP_TIMER);
-}
-
-/* 
- * Initialise the swap timer
- */
-
-void init_swap_timer(void)
-{
-	timer_table[SWAP_TIMER].expires = jiffies;
-	timer_table[SWAP_TIMER].fn = swap_tick;
-	timer_active |= (1<<SWAP_TIMER);
+	return priority >= 0;
 }
diff -u --recursive --new-file v2.2.0-pre4/linux/net/TUNABLE linux/net/TUNABLE
--- v2.2.0-pre4/linux/net/TUNABLE	Sun Jun  7 11:16:40 1998
+++ linux/net/TUNABLE	Mon Jan  4 15:31:35 1999
@@ -41,7 +41,7 @@
 ARP_CHECK_INTERVAL	Check interval to refresh an arp (tunable)
 ARP_CONFIRM_INTERVAL	Confirm poll time (tunable)
 ARP_TABLE_SIZE		Hash table size for ARP (tunable)
-IP_MAX_MEMBERSHIPS	Largest number of groups per socket (BSD style)
+IP_MAX_MEMBERSHIPS	Largest number of groups per socket (BSD style) (tunable)
 16			Hard coded constant for amount of room allowed for
 			cache align and faster forwarding (tunable)
 IPFRAG_HIGH_THRESH	Limit on fragments, we free fragments until we reach
diff -u --recursive --new-file v2.2.0-pre4/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v2.2.0-pre4/linux/net/appletalk/ddp.c	Mon Dec 28 15:00:53 1998
+++ linux/net/appletalk/ddp.c	Mon Jan  4 15:31:35 1999
@@ -2154,7 +2154,7 @@
 	atalk_register_sysctl();
 #endif /* CONFIG_SYSCTL */
 
-	printk(KERN_INFO "AppleTalk 0.18 for Linux NET3.037\n");
+	printk(KERN_INFO "NET4: AppleTalk 0.18 for Linux NET4.0\n");
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.2.0-pre4/linux/net/ax25/af_ax25.c	Tue Dec 22 14:16:59 1998
+++ linux/net/ax25/af_ax25.c	Mon Jan  4 15:31:35 1999
@@ -1806,7 +1806,7 @@
 	proc_net_register(&proc_ax25_calls);
 #endif
 
-	printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.37 for Linux NET3.038 (Linux 2.1)\n");
+	printk(KERN_INFO "NET4: G4KLX/GW4PTS AX.25 for Linux. Version 0.37 for Linux NET4.0\n");
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.2.0-pre4/linux/net/bridge/br.c linux/net/bridge/br.c
--- v2.2.0-pre4/linux/net/bridge/br.c	Mon Dec 28 15:00:53 1998
+++ linux/net/bridge/br.c	Mon Jan  4 15:31:35 1999
@@ -842,7 +842,7 @@
 {						  /* (4.8.1)	 */
 	int port_no;
 
-	printk(KERN_INFO "Ethernet Bridge 005 for NET3.037 (Linux 2.1)\n");
+	printk(KERN_INFO "NET4: Ethernet Bridge 005 for NET4.0\n");
 
 	/*
 	 * Form initial topology change time.
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/Config.in linux/net/ipv4/Config.in
--- v2.2.0-pre4/linux/net/ipv4/Config.in	Wed Dec 16 10:32:56 1998
+++ linux/net/ipv4/Config.in	Mon Jan  4 15:31:35 1999
@@ -47,6 +47,7 @@
         if [ "$CONFIG_IP_MASQUERADE_MOD" = "y" ]; then
           tristate 'IP: ipautofw masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPAUTOFW
           tristate 'IP: ipportfw masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPPORTFW
+          tristate 'IP: ip fwmark masq-forwarding support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_MFW
 	fi
       fi
     fi
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/Makefile linux/net/ipv4/Makefile
--- v2.2.0-pre4/linux/net/ipv4/Makefile	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/Makefile	Mon Jan  4 15:31:35 1999
@@ -81,6 +81,14 @@
     endif
   endif
   
+  ifeq ($(CONFIG_IP_MASQUERADE_MFW),y)
+  IPV4_OBJS += ip_masq_mfw.o
+  else
+    ifeq ($(CONFIG_IP_MASQUERADE_MFW),m)
+    M_OBJS += ip_masq_mfw.o
+    endif
+  endif
+
 endif
 
 M_OBJS += ip_masq_user.o
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.2.0-pre4/linux/net/ipv4/af_inet.c	Thu Nov 19 09:56:29 1998
+++ linux/net/ipv4/af_inet.c	Mon Jan  4 15:31:35 1999
@@ -5,7 +5,7 @@
  *
  *		PF_INET protocol family socket handler.
  *
- * Version:	$Id: af_inet.c,v 1.80 1998/11/08 11:17:03 davem Exp $
+ * Version:	$Id: af_inet.c,v 1.82 1999/01/04 20:36:44 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -190,8 +190,9 @@
 	 * [PR]
 	 */
 		  
-	printk(KERN_DEBUG "Socket destroy delayed (r=%d w=%d)\n",
-	       atomic_read(&sk->rmem_alloc), atomic_read(&sk->wmem_alloc));
+	NETDEBUG(printk(KERN_DEBUG "Socket destroy delayed (r=%d w=%d)\n",
+			atomic_read(&sk->rmem_alloc),
+			atomic_read(&sk->wmem_alloc)));
 
 	sk->destroy = 1;
 	sk->ack_backlog = 0;
@@ -1059,7 +1060,7 @@
 	struct sk_buff *dummy_skb;
 	struct inet_protocol *p;
 
-	printk(KERN_INFO "Swansea University Computer Society TCP/IP for NET3.037\n");
+	printk(KERN_INFO "NET4: Linux TCP/IP 1.0 for NET4.0\n");
 
 	if (sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb))
 	{
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.2.0-pre4/linux/net/ipv4/devinet.c	Thu Dec 31 10:29:03 1998
+++ linux/net/ipv4/devinet.c	Mon Jan  4 15:31:35 1999
@@ -1,7 +1,7 @@
 /*
  *	NET3	IP device support routines.
  *
- *	Version: $Id: devinet.c,v 1.23 1998/08/26 12:03:21 davem Exp $
+ *	Version: $Id: devinet.c,v 1.25 1999/01/04 20:14:33 davem Exp $
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -208,16 +208,9 @@
 {
 	struct in_ifaddr *ifa1, **ifap, **last_primary;
 
-	/* Allow 0.0.0.0, but it must be the only address to avoid
-           multiple matches. */
-	if (in_dev->ifa_list) {
-		if (ifa->ifa_local == 0) {
-			inet_free_ifa(ifa);
-			return 0;
-		}
-
-		if (in_dev->ifa_list->ifa_local == 0)
-			inet_del_ifa(in_dev, &in_dev->ifa_list, 1);
+	if (ifa->ifa_local == 0) {
+		inet_free_ifa(ifa);
+		return 0;
 	}
 
 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
@@ -995,33 +988,6 @@
 		kfree(t);
 	}
 }
-#endif
-
-#ifdef CONFIG_IP_PNP_BOOTP
-
-/*
- *	Addition and deletion of fake interface addresses
- *	for sending of BOOTP packets. In this case, we must
- *	set the local address to zero which is not permitted
- *	otherwise.
- */
-
-__initfunc(int inet_add_bootp_addr(struct device *dev))
-{
-	struct in_ifaddr *ifa;
-
-	if (!(ifa = inet_alloc_ifa()))
-		return -ENOBUFS;
-
-	return inet_set_ifa(dev, ifa);
-}
-
-__initfunc(void inet_del_bootp_addr(struct device *dev))
-{
-	if (dev->ip_ptr)
-		inetdev_destroy(dev->ip_ptr);
-}
-
 #endif
 
 __initfunc(void devinet_init(void))
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/fib_frontend.c linux/net/ipv4/fib_frontend.c
--- v2.2.0-pre4/linux/net/ipv4/fib_frontend.c	Thu Dec 31 10:29:03 1998
+++ linux/net/ipv4/fib_frontend.c	Mon Jan  4 15:31:35 1999
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: FIB frontend.
  *
- * Version:	$Id: fib_frontend.c,v 1.12 1998/08/26 12:03:24 davem Exp $
+ * Version:	$Id: fib_frontend.c,v 1.14 1999/01/04 20:13:55 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -443,13 +443,13 @@
 	if (ifa->ifa_broadcast && ifa->ifa_broadcast != 0xFFFFFFFF)
 		fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
 
-	if (!(ifa->ifa_flags&IFA_F_SECONDARY) &&
+	if (!ZERONET(prefix) && !(ifa->ifa_flags&IFA_F_SECONDARY) &&
 	    (prefix != addr || ifa->ifa_prefixlen < 32)) {
 		fib_magic(RTM_NEWROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
 			  RTN_UNICAST, prefix, ifa->ifa_prefixlen, prim);
 
 		/* Add network specific broadcasts, when it takes a sense */
-		if (!ZERONET(prefix) && ifa->ifa_prefixlen < 31) {
+		if (ifa->ifa_prefixlen < 31) {
 			fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32, prim);
 			fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix|~mask, 32, prim);
 		}
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v2.2.0-pre4/linux/net/ipv4/icmp.c	Tue Dec 22 14:16:59 1998
+++ linux/net/ipv4/icmp.c	Mon Jan  4 15:31:35 1999
@@ -3,7 +3,7 @@
  *	
  *		Alan Cox, <alan@cymru.net>
  *
- *	Version: $Id: icmp.c,v 1.47 1998/10/21 05:32:24 davem Exp $
+ *	Version: $Id: icmp.c,v 1.48 1999/01/02 16:51:41 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v2.2.0-pre4/linux/net/ipv4/igmp.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv4/igmp.c	Mon Jan  4 15:31:35 1999
@@ -8,7 +8,7 @@
  *	the older version didn't come out right using gcc 2.5.8, the newer one
  *	seems to fall out with gcc 2.6.2.
  *
- *	Version: $Id: igmp.c,v 1.27 1998/08/26 12:03:39 davem Exp $
+ *	Version: $Id: igmp.c,v 1.28 1998/11/30 15:53:13 davem Exp $
  *
  *	Authors:
  *		Alan Cox <Alan.Cox@linux.org>
@@ -538,6 +538,7 @@
 /*
  *	Join a socket to a group
  */
+int sysctl_igmp_max_memberships = IP_MAX_MEMBERSHIPS;
 
 int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
 {
@@ -578,7 +579,7 @@
 		count++;
 	}
 	err = -ENOBUFS;
-	if (iml == NULL || count >= IP_MAX_MEMBERSHIPS)
+	if (iml == NULL || count >= sysctl_igmp_max_memberships)
 		goto done;
 	memcpy(&iml->multi, imr, sizeof(*imr));
 	iml->next = sk->ip_mc_list;
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v2.2.0-pre4/linux/net/ipv4/ip_fw.c	Fri Oct  9 13:27:17 1998
+++ linux/net/ipv4/ip_fw.c	Mon Jan  4 15:31:35 1999
@@ -115,8 +115,8 @@
  * UP.
  *
  * For backchains and counters, we use an array, indexed by
- * [smp_processor_id()*2 + !in_interrupt()]; the array is of size
- * [smp_num_cpus*2].  For v2.0, smp_num_cpus is effectively 1.  So,
+ * [cpu_number_map[smp_processor_id()]*2 + !in_interrupt()]; the array is of 
+ * size [smp_num_cpus*2].  For v2.0, smp_num_cpus is effectively 1.  So,
  * confident of uniqueness, we modify counters even though we only
  * have a read lock (to read the counters, you need a write lock,
  * though).  */
@@ -140,7 +140,11 @@
 static struct sock *ipfwsk;
 #endif
 
-#define SLOT_NUMBER() (smp_processor_id()*2 + !in_interrupt())
+#ifdef __SMP__
+#define SLOT_NUMBER() (cpu_number_map[smp_processor_id()]*2 + !in_interrupt())
+#else
+#define SLOT_NUMBER() (!in_interrupt())
+#endif
 #define NUM_SLOTS (smp_num_cpus*2)
 
 #define SIZEOF_STRUCT_IP_CHAIN (sizeof(struct ip_chain) \
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
--- v2.2.0-pre4/linux/net/ipv4/ip_masq.c	Fri Jan  1 12:58:21 1999
+++ linux/net/ipv4/ip_masq.c	Mon Jan  4 15:31:35 1999
@@ -4,7 +4,7 @@
  *
  * 	Copyright (c) 1994 Pauline Middelink
  *
- *	$Id: ip_masq.c,v 1.28 1998/11/21 00:33:30 davem Exp $
+ *	$Id: ip_masq.c,v 1.32 1999/01/04 20:37:05 davem Exp $
  *
  *
  *	See ip_fw.c for original log
@@ -44,6 +44,8 @@
  *	Juan Jose Ciarlante	: 	fixed stupid SMP locking bug
  *	Juan Jose Ciarlante	: 	fixed "tap"ing in demasq path by copy-on-w
  *	Juan Jose Ciarlante	: 	make masq_proto_doff() robust against fake sized/corrupted packets
+ *	Kai Bankett		:	do not toss other IP protos in proto_doff()
+ *	Dan Kegel		:	pointed correct NAT behavior for UDP streams
  *	
  */
 
@@ -391,6 +393,20 @@
 struct ip_fw_masq *ip_masq_expire = &ip_masq_dummy;
 #endif
 
+/*
+ *	These flags enable non-strict d{addr,port} checks
+ *	Given that both (in/out) lookup tables are hashed
+ *	by m{addr,port} and s{addr,port} this is quite easy 
+ */
+
+#define MASQ_DADDR_PASS	(IP_MASQ_F_NO_DADDR|IP_MASQ_F_DLOOSE)
+#define MASQ_DPORT_PASS	(IP_MASQ_F_NO_DPORT|IP_MASQ_F_DLOOSE)
+
+/*
+ *	By default enable dest loose semantics
+ */
+#define CONFIG_IP_MASQ_LOOSE_DEFAULT 1
+
 
 /*
  * 	Set masq expiration (deletion) and adds timer,
@@ -522,12 +538,12 @@
 
         hash = ip_masq_hash_key(protocol, d_addr, d_port);
 
-
         for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) {
- 		if (protocol==ms->protocol &&
-		    ((s_addr==ms->daddr || ms->flags & IP_MASQ_F_NO_DADDR)) &&
-		    (s_port==ms->dport || ms->flags & IP_MASQ_F_NO_DPORT) &&
-		    (d_addr==ms->maddr && d_port==ms->mport)) {
+		if (protocol==ms->protocol && 
+		    (d_addr==ms->maddr && d_port==ms->mport) &&
+		    (s_addr==ms->daddr || ms->flags & MASQ_DADDR_PASS) &&
+		    (s_port==ms->dport || ms->flags & MASQ_DPORT_PASS)
+		    ) {
 			IP_MASQ_DEBUG(2, "look/in %d %08X:%04hX->%08X:%04hX OK\n",
 			       protocol,
 			       s_addr,
@@ -578,7 +594,9 @@
         for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) {
 		if (protocol == ms->protocol &&
 		    s_addr == ms->saddr && s_port == ms->sport &&
-                    d_addr == ms->daddr && d_port == ms->dport ) {
+		    (d_addr==ms->daddr || ms->flags & MASQ_DADDR_PASS) &&
+		    (d_port==ms->dport || ms->flags & MASQ_DPORT_PASS)
+                   ) {
 			IP_MASQ_DEBUG(2, "lk/out1 %d %08X:%04hX->%08X:%04hX OK\n",
 			       protocol,
 			       s_addr,
@@ -600,7 +618,9 @@
 		if (ms->flags & IP_MASQ_F_NO_SPORT &&
 		    protocol == ms->protocol &&
 		    s_addr == ms->saddr && 
-                    d_addr == ms->daddr && d_port == ms->dport ) {
+		    (d_addr==ms->daddr || ms->flags & MASQ_DADDR_PASS) &&
+		    (d_port==ms->dport || ms->flags & MASQ_DPORT_PASS)
+                    ) {
 			IP_MASQ_DEBUG(2, "lk/out2 %d %08X:%04hX->%08X:%04hX OK\n",
 			       protocol,
 			       s_addr,
@@ -623,7 +643,7 @@
         return ms;
 }
 
-#ifdef CONFIG_IP_MASQUERADE_NREUSE
+#ifdef CONFIG_IP_MASQ_NREUSE
 /*
  *	Returns ip_masq for given proto,m_addr,m_port.
  *      called by allocation routine to find an unused m_port.
@@ -841,7 +861,15 @@
 	atomic_set(&ms->refcnt,0);
 
         if (proto == IPPROTO_UDP && !mport)
+#ifdef CONFIG_IP_MASQ_LOOSE_DEFAULT
+		/*
+		 *	Flag this tunnel as "dest loose"
+		 *	
+		 */
+		ms->flags |= IP_MASQ_F_DLOOSE;
+#else
                 ms->flags |= IP_MASQ_F_NO_DADDR;
+#endif
 
         
         /* get masq address from rif */
@@ -916,7 +944,7 @@
 		else
 			write_lock(&__ip_masq_lock);
 
-#ifdef CONFIG_IP_MASQUERADE_NREUSE
+#ifdef CONFIG_IP_MASQ_NREUSE
 		mst = __ip_masq_getbym(proto, maddr, mport);
 #else
 		mst = __ip_masq_in_get(proto, daddr, dport, maddr, mport);
@@ -966,6 +994,9 @@
 
 /*
  *	Get transport protocol data offset, check against size
+ *	return:
+ *		0  if other IP proto
+ *		-1 if error
  */
 static __inline__ int proto_doff(unsigned proto, char *th, unsigned size)
 {
@@ -993,6 +1024,9 @@
 			}
 
 			break;
+		default:
+			/* 	Other proto: nothing to say, by now :) */
+			ret = 0;
 	}
 	if (ret < 0)
 		IP_MASQ_DEBUG(0, "mess proto_doff for proto=%d, size =%d\n",
@@ -1024,11 +1058,16 @@
 	h.raw = (char*) iph + iph->ihl * 4;
 	size = ntohs(iph->tot_len) - (iph->ihl * 4);
 
+
 	doff = proto_doff(iph->protocol, h.raw, size);
-	if (doff < 0) {
-		IP_MASQ_DEBUG(0, "O-pkt invalid packet data size\n");
+	if (doff <= 0) {
+		/*	
+		 *	Output path: do not pass other IP protos nor
+		 *	invalid packets.
+		 */
 		return -1;
 	}
+
 	switch (iph->protocol) {
 	case IPPROTO_ICMP:
 		return(ip_fw_masq_icmp(skb_p, maddr));
@@ -1131,6 +1170,13 @@
 			IP_MASQ_DEBUG(1, "ip_fw_masquerade(): filled sport=%d\n",
 			       ntohs(ms->sport));
 		}
+		if (ms->flags & IP_MASQ_F_DLOOSE) {
+			/*
+			 *	update dest loose values
+			 */
+			ms->dport = h.portp[1];
+			ms->daddr = iph->daddr;
+		}
         } else {
 		/*
 		 *	Nope, not found, create a new entry for it
@@ -1431,8 +1477,8 @@
 	if (ip_compute_csum((unsigned char *) icmph, len))
 	{
 		/* Failed checksum! */
-		IP_MASQ_WARNING( "forward ICMP: failed checksum from %d.%d.%d.%d!\n",
-		       NIPQUAD(iph->saddr));
+		IP_MASQ_DEBUG(0, "forward ICMP: failed checksum from %d.%d.%d.%d!\n",
+			      NIPQUAD(iph->saddr));
 		return(-1);
 	}
 
@@ -1776,9 +1822,17 @@
 	size = ntohs(iph->tot_len) - (iph->ihl * 4);
 
 	doff = proto_doff(iph->protocol, h.raw, size);
-	if (doff < 0) {
-		IP_MASQ_DEBUG(0, "I-pkt invalid packet data size\n");
-		return -1;
+
+	switch (doff) {
+		case 0:
+			/*
+			 *	Input path: other IP protos Ok, will
+			 *	reach local sockets path.
+			 */
+			return 0;
+		case -1:
+			IP_MASQ_DEBUG(0, "I-pkt invalid packet data size\n");
+			return -1;
 	}
 
 	maddr = iph->daddr;
@@ -1870,10 +1924,18 @@
                  */
                 ms->flags &= ~IP_MASQ_F_NO_REPLY;
                 
-                /*
-                 *	Set dport if not defined yet.
+		/*
+		 *	Set daddr,dport if not defined yet
+		 *	and tunnel is not setup as "dest loose"
                  */
 
+		if (ms->flags & IP_MASQ_F_DLOOSE) {
+			/*
+			 *	update dest loose values
+			 */
+			ms->dport = h.portp[0];
+			ms->daddr = iph->saddr;
+		} else {
                 if ( ms->flags & IP_MASQ_F_NO_DPORT ) { /*  && ms->protocol == IPPROTO_TCP ) { */
                         ms->flags &= ~IP_MASQ_F_NO_DPORT;
                         ms->dport = h.portp[0];
@@ -1890,6 +1952,7 @@
                                ntohl(ms->daddr));
 
                 }
+		}
 		if ((skb=masq_skb_cow(skb_p, &iph, &h.raw)) == NULL) {
 			ip_masq_put(ms);
 			return -1;
@@ -2309,8 +2372,8 @@
 #ifdef CONFIG_IP_MASQUERADE_IPPORTFW
 	ip_portfw_init();
 #endif
-#ifdef CONFIG_IP_MASQUERADE_IPMARKFW
-	ip_markfw_init();
+#ifdef CONFIG_IP_MASQUERADE_MFW
+	ip_mfw_init();
 #endif
         ip_masq_app_init();
 
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/ip_masq_mfw.c linux/net/ipv4/ip_masq_mfw.c
--- v2.2.0-pre4/linux/net/ipv4/ip_masq_mfw.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ipv4/ip_masq_mfw.c	Mon Jan  4 15:31:35 1999
@@ -0,0 +1,776 @@
+/*
+ *		IP_MASQ_MARKFW masquerading module
+ *
+ *	Does (reverse-masq) forwarding based on skb->fwmark value
+ *
+ *	$Id: ip_masq_mfw.c,v 1.2 1998/12/12 02:40:42 davem Exp $
+ *
+ * Author:	Juan Jose Ciarlante   <jjciarla@raiz.uncu.edu.ar>
+ *		  based on Steven Clarke's portfw
+ *
+ * Fixes:	
+ *	JuanJo Ciarlante:	added u-space sched support
+ *	JuanJo Ciarlante:	if rport==0, use packet dest port *grin*
+ *	JuanJo Ciarlante:	fixed tcp syn&&!ack creation
+ *
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <net/ip.h>
+#include <linux/ip_fw.h>
+#include <linux/ip_masq.h>
+#include <net/ip_masq.h>
+#include <net/ip_masq_mod.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <asm/softirq.h>
+#include <asm/spinlock.h>
+#include <asm/atomic.h>
+
+static struct ip_masq_mod *mmod_self = NULL;
+#ifdef CONFIG_IP_MASQ_DEBUG
+static int debug=0;
+MODULE_PARM(debug, "i");
+#endif
+
+/*
+ *  Lists structure:
+ *	There is a "main" linked list with entries hashed
+ *	by fwmark value (struct ip_masq_mfw, the "m-entries").
+ *
+ *	Each of this m-entry holds a double linked list
+ *	of "forward-to" hosts (struct ip_masq_mfw_host, the "m.host"),
+ *	the round-robin scheduling takes place by rotating m.host entries
+ *	"inside" its m-entry.
+ */
+
+/*
+ *	Each forwarded host (addr:port) is stored here
+ */
+struct ip_masq_mfw_host {
+	struct 	list_head list;
+	__u32 	addr;
+	__u16	port;
+	__u16	pad0;
+	__u32 	fwmark;
+	int 	pref;
+	atomic_t	pref_cnt;
+};
+
+#define IP_MASQ_MFW_HSIZE	16
+/*
+ *	This entries are indexed by fwmark, 
+ *	they hold a list of forwarded addr:port
+ */	
+
+struct ip_masq_mfw {
+	struct ip_masq_mfw *next;	/* linked list */
+	__u32 fwmark;			/* key: firewall mark */
+	struct list_head hosts;		/* list of forward-to hosts */
+	atomic_t nhosts;		/* number of "" */
+#ifdef __SMP__
+	rwlock_t lock;
+#endif
+};
+
+
+static struct semaphore mfw_sema;
+#ifdef __SMP__
+static rwlock_t mfw_lock = RW_LOCK_UNLOCKED;
+#endif
+
+static struct ip_masq_mfw *ip_masq_mfw_table[IP_MASQ_MFW_HSIZE];
+
+static __inline__ int mfw_hash_val(int fwmark)
+{
+	return fwmark & 0x0f;
+}
+
+/*
+ *	Get m-entry by "fwmark"
+ *	Caller must lock tables.
+ */
+
+static struct ip_masq_mfw *__mfw_get(int fwmark)
+{
+	struct ip_masq_mfw* mfw;
+	int hash = mfw_hash_val(fwmark);
+
+	for (mfw=ip_masq_mfw_table[hash];mfw;mfw=mfw->next) {
+		if (mfw->fwmark==fwmark) {
+			goto out;
+		}
+	}
+out:
+	return mfw;
+}
+
+/*
+ *	Links m-entry.
+ *	Caller should have checked if already present for same fwmark
+ *
+ *	Caller must lock tables.
+ */
+static int __mfw_add(struct ip_masq_mfw *mfw)
+{
+	int fwmark = mfw->fwmark;
+	int hash = mfw_hash_val(fwmark);
+
+	mfw->next = ip_masq_mfw_table[hash];
+	ip_masq_mfw_table[hash] = mfw;
+	ip_masq_mod_inc_nent(mmod_self);
+
+	return 0;
+}
+
+/*
+ *	Creates a m-entry (doesn't link it)
+ */
+
+static struct ip_masq_mfw * mfw_new(int fwmark)
+{
+	struct ip_masq_mfw *mfw;
+
+	mfw = kmalloc(sizeof(*mfw), GFP_KERNEL);
+	if (mfw == NULL) 
+		goto out;
+
+	MOD_INC_USE_COUNT;
+	memset(mfw, 0, sizeof(*mfw));
+	mfw->fwmark = fwmark;
+#ifdef __SMP__
+	mfw->lock = (rwlock_t) RW_LOCK_UNLOCKED;
+#endif
+
+	INIT_LIST_HEAD(&mfw->hosts);
+out:
+	return mfw;
+}
+
+static void mfw_host_to_user(struct ip_masq_mfw_host *h, struct ip_mfw_user *mu)
+{
+	mu->raddr = h->addr;
+	mu->rport = h->port;
+	mu->fwmark = h->fwmark;
+	mu->pref = h->pref;
+}
+
+/*
+ *	Creates a m.host (doesn't link it in a m-entry)
+ */
+static struct ip_masq_mfw_host * mfw_host_new(struct ip_mfw_user *mu)
+{
+	struct ip_masq_mfw_host * mfw_host;
+	mfw_host = kmalloc(sizeof (*mfw_host), GFP_KERNEL);
+	if (!mfw_host)
+		return NULL;
+
+	MOD_INC_USE_COUNT;
+	memset(mfw_host, 0, sizeof(*mfw_host));
+	mfw_host->addr = mu->raddr;
+	mfw_host->port = mu->rport;
+	mfw_host->fwmark = mu->fwmark;
+	mfw_host->pref = mu->pref;
+	atomic_set(&mfw_host->pref_cnt, mu->pref);
+
+	return mfw_host;
+}
+
+/*
+ *	Create AND link m.host to m-entry.
+ *	It locks m.lock.
+ */
+static int mfw_addhost(struct ip_masq_mfw *mfw, struct ip_mfw_user *mu, int attail)
+{
+	struct ip_masq_mfw_host *mfw_host;
+
+	mfw_host = mfw_host_new(mu);
+	if (!mfw_host) 
+		return -ENOMEM;
+
+	write_lock_bh(&mfw->lock);
+	list_add(&mfw_host->list, attail? mfw->hosts.prev : &mfw->hosts);
+	atomic_inc(&mfw->nhosts);
+	write_unlock_bh(&mfw->lock);
+
+	return 0;
+}
+
+/*
+ *	Unlink AND destroy m.host(s) from m-entry.
+ *	Wildcard (nul host or addr) ok.
+ *	It uses m.lock.
+ */
+static int mfw_delhost(struct ip_masq_mfw *mfw, struct ip_mfw_user *mu)
+{
+
+	struct list_head *l,*e;
+	struct ip_masq_mfw_host *h;
+	int n_del = 0;
+	l = &mfw->hosts;
+
+	write_lock_bh(&mfw->lock);
+	for (e=l->next; e!=l; e=e->next)
+	{
+		h = list_entry(e, struct ip_masq_mfw_host, list);
+		if ((!mu->raddr || h->addr == mu->raddr) && 
+			(!mu->rport || h->port == mu->rport)) {
+			/* HIT */
+			atomic_dec(&mfw->nhosts);
+			list_del(&h->list);
+			kfree_s(h, sizeof(*h));
+			MOD_DEC_USE_COUNT;
+			n_del++;
+		}
+				
+	}
+	write_unlock_bh(&mfw->lock);
+	return n_del? 0 : -ESRCH;
+}
+
+/*
+ *	Changes m.host parameters
+ *	Wildcards ok
+ *
+ *	Caller must lock tables.
+ */
+static int __mfw_edithost(struct ip_masq_mfw *mfw, struct ip_mfw_user *mu)
+{
+
+	struct list_head *l,*e;
+	struct ip_masq_mfw_host *h;
+	int n_edit = 0;
+	l = &mfw->hosts;
+
+	for (e=l->next; e!=l; e=e->next)
+	{
+		h = list_entry(e, struct ip_masq_mfw_host, list);
+		if ((!mu->raddr || h->addr == mu->raddr) && 
+			(!mu->rport || h->port == mu->rport)) {
+			/* HIT */
+			h->pref = mu->pref;
+			atomic_set(&h->pref_cnt, mu->pref);
+			n_edit++;
+		}
+				
+	}
+	return n_edit? 0 : -ESRCH;
+}
+
+/*
+ *	Destroys m-entry.
+ *	Caller must have checked that it doesn't hold any m.host(s)
+ */
+static void mfw_destroy(struct ip_masq_mfw *mfw)
+{
+	kfree_s(mfw, sizeof(*mfw));
+	MOD_DEC_USE_COUNT;
+}
+
+/* 
+ *	Unlink m-entry.
+ *
+ *	Caller must lock tables.
+ */
+static int __mfw_del(struct ip_masq_mfw *mfw)
+{
+	struct ip_masq_mfw **mfw_p;
+	int ret = -EINVAL;
+
+
+	for(mfw_p=&ip_masq_mfw_table[mfw_hash_val(mfw->fwmark)]; 
+			*mfw_p; 
+			mfw_p = &((*mfw_p)->next)) 
+	{
+		if (mfw==(*mfw_p)) {
+			*mfw_p = mfw->next;
+			ip_masq_mod_dec_nent(mmod_self);
+			ret = 0;
+			goto out;
+		}
+	}
+out:
+	return ret;
+}
+
+/*
+ *	Crude m.host scheduler
+ *	This interface could be exported to allow playing with 
+ *	other sched policies.
+ *
+ *	Caller must lock m-entry.
+ */
+static struct ip_masq_mfw_host * __mfw_sched(struct ip_masq_mfw *mfw, int force)
+{
+	struct ip_masq_mfw_host *h = NULL;
+
+	if (atomic_read(&mfw->nhosts) == 0)
+		goto out;
+
+	/*
+	 *	Here resides actual sched policy: 
+	 *	When pref_cnt touches 0, entry gets shifted to tail and
+	 *	its pref_cnt reloaded from h->pref (actual value
+	 *	passed from u-space).
+	 *
+	 *	Exception is pref==0: avoid scheduling.
+	 */
+
+	h = list_entry(mfw->hosts.next, struct ip_masq_mfw_host, list);
+
+	if (atomic_read(&mfw->nhosts) <= 1)
+		goto out;
+
+	if ((h->pref && atomic_dec_and_test(&h->pref_cnt)) || force) {
+		atomic_set(&h->pref_cnt, h->pref);
+		list_del(&h->list);
+		list_add(&h->list, mfw->hosts.prev);
+	}
+out:
+	return h;
+}
+
+/*
+ *	Main lookup routine.
+ *	HITs fwmark and schedules m.host entries if required
+ */
+static struct ip_masq_mfw_host * mfw_lookup(int fwmark)
+{
+	struct ip_masq_mfw *mfw;
+	struct ip_masq_mfw_host *h = NULL;
+
+	read_lock(&mfw_lock);
+	mfw = __mfw_get(fwmark);
+
+	if (mfw) {
+		write_lock(&mfw->lock);
+		h = __mfw_sched(mfw, 0);
+		write_unlock(&mfw->lock);
+	}
+
+	read_unlock(&mfw_lock);
+	return h;
+}
+
+#ifdef CONFIG_PROC_FS
+static int mfw_procinfo(char *buffer, char **start, off_t offset,
+			      int length, int dummy)
+{
+	struct ip_masq_mfw *mfw;
+	struct ip_masq_mfw_host *h;
+	struct list_head *l,*e;
+	off_t pos=0, begin;
+	char temp[129];
+        int idx = 0;
+	int len=0;
+
+	MOD_INC_USE_COUNT;
+
+	IP_MASQ_DEBUG(1-debug, "Entered mfw_info\n");
+
+	if (offset < 64)
+	{
+                sprintf(temp, "FwMark > RAddr    RPort PrCnt  Pref");
+		len = sprintf(buffer, "%-63s\n", temp);
+	}
+	pos = 64;
+
+        for(idx = 0; idx < IP_MASQ_MFW_HSIZE; idx++)
+	{
+		read_lock(&mfw_lock);
+		for(mfw = ip_masq_mfw_table[idx]; mfw ; mfw = mfw->next)
+		{
+			read_lock_bh(&mfw->lock);
+			l=&mfw->hosts;
+
+			for(e=l->next;l!=e;e=e->next) {
+				h = list_entry(e, struct ip_masq_mfw_host, list);
+				pos += 64;
+				if (pos <= offset) {
+					len = 0;
+					continue;
+				}
+
+				sprintf(temp,"0x%x > %08lX %5u %5d %5d",
+						h->fwmark,
+						ntohl(h->addr), ntohs(h->port),
+						atomic_read(&h->pref_cnt), h->pref);
+				len += sprintf(buffer+len, "%-63s\n", temp);
+
+				if(len >= length) {
+					read_unlock_bh(&mfw->lock);
+					read_unlock(&mfw_lock);
+					goto done;
+				}
+			}
+			read_unlock_bh(&mfw->lock);
+		}
+		read_unlock(&mfw_lock);
+	}
+
+done:
+
+	if (len) {
+		begin = len - (pos - offset);
+		*start = buffer + begin;
+		len -= begin;
+	}
+	if(len>length)
+		len = length;
+	MOD_DEC_USE_COUNT;
+	return len;
+}
+static struct proc_dir_entry mfw_proc_entry = {
+/* 		0, 0, NULL", */
+		0, 3, "mfw",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations,
+		mfw_procinfo
+};
+
+#define proc_ent &mfw_proc_entry
+#else /* !CONFIG_PROC_FS */
+
+#define proc_ent NULL
+#endif
+
+
+static void mfw_flush(void)
+{
+	struct ip_masq_mfw *mfw, *local_table[IP_MASQ_MFW_HSIZE];
+	struct ip_masq_mfw_host *h;
+	struct ip_masq_mfw *mfw_next;
+	int idx;
+	struct list_head *l,*e;
+
+	write_lock_bh(&mfw_lock);
+	memcpy(local_table, ip_masq_mfw_table, sizeof ip_masq_mfw_table);
+	memset(ip_masq_mfw_table, 0, sizeof ip_masq_mfw_table);
+	write_unlock_bh(&mfw_lock);
+
+	/*
+	 *	For every hash table row ...
+	 */
+	for(idx=0;idx<IP_MASQ_MFW_HSIZE;idx++) {
+
+		/*
+		 *	For every m-entry in row ...
+		 */
+		for(mfw=local_table[idx];mfw;mfw=mfw_next) {
+			/*
+			 *	For every m.host in m-entry ...
+			 */
+			l=&mfw->hosts;
+			while((e=l->next) != l) {
+				h = list_entry(e, struct ip_masq_mfw_host, list);
+				atomic_dec(&mfw->nhosts);
+				list_del(&h->list);
+				kfree_s(h, sizeof(*h));
+				MOD_DEC_USE_COUNT;
+			}
+
+			if (atomic_read(&mfw->nhosts)) {
+				IP_MASQ_ERR("mfw_flush(): after flushing row nhosts=%d\n",
+						atomic_read(&mfw->nhosts));
+			}
+			mfw_next = mfw->next;
+			kfree_s(mfw, sizeof(*mfw));	
+			MOD_DEC_USE_COUNT;
+			ip_masq_mod_dec_nent(mmod_self);
+		}
+	}
+}
+
+/*
+ *	User space control entry point
+ */
+static int mfw_ctl(int optname, struct ip_masq_ctl *mctl, int optlen)
+{
+        struct ip_mfw_user *mu =  &mctl->u.mfw_user;
+	struct ip_masq_mfw *mfw;
+	int ret = EINVAL;
+	int arglen = optlen - IP_MASQ_CTL_BSIZE;
+	int cmd;
+
+
+	IP_MASQ_DEBUG(1-debug, "ip_masq_user_ctl(len=%d/%d|%d/%d)\n",
+		arglen,
+		sizeof (*mu),
+		optlen,
+		sizeof (*mctl));
+
+	/*
+	 *	checks ...
+	 */
+	if (arglen != sizeof(*mu) && optlen != sizeof(*mctl)) 
+		return -EINVAL;
+ 
+	/* 
+	 *	Don't trust the lusers - plenty of error checking! 
+	 */
+	cmd = mctl->m_cmd;
+	IP_MASQ_DEBUG(1-debug, "ip_masq_mfw_ctl(cmd=%d, fwmark=%d)\n",
+			cmd, mu->fwmark);
+
+
+	switch(cmd) {
+		case IP_MASQ_CMD_NONE:
+			return 0;
+		case IP_MASQ_CMD_FLUSH:
+			break;
+		case IP_MASQ_CMD_ADD:
+		case IP_MASQ_CMD_INSERT:
+		case IP_MASQ_CMD_SET:
+			if (mu->fwmark == 0) {
+				IP_MASQ_DEBUG(1-debug, "invalid fwmark==0\n");
+				return -EINVAL;
+			}
+			if (mu->pref < 0) {
+				IP_MASQ_DEBUG(1-debug, "invalid pref==%d\n",
+					mu->pref);
+				return -EINVAL;
+			}
+			break;
+	}
+
+
+	ret = -EINVAL;
+
+	switch(cmd) {
+	case IP_MASQ_CMD_ADD:
+	case IP_MASQ_CMD_INSERT:
+		if (!mu->raddr) {
+			IP_MASQ_DEBUG(0-debug, "ip_masq_mfw_ctl(ADD): invalid redirect 0x%x:%d\n",
+					mu->raddr, mu->rport);
+			goto out;
+		}
+
+		/*
+		 *	Cannot just use mfw_lock because below
+		 *	are allocations that can sleep; so
+		 *	to assure "new entry" atomic creation
+		 *	I use a semaphore.
+		 *
+		 */
+		down(&mfw_sema);
+
+		read_lock(&mfw_lock);
+		mfw = __mfw_get(mu->fwmark);
+		read_unlock(&mfw_lock);
+		
+		/*
+		 *	If first host, create m-entry
+		 */
+		if (mfw == NULL) {
+			mfw = mfw_new(mu->fwmark);
+			if (mfw == NULL) 
+				ret = -ENOMEM;
+		} 
+
+		if (mfw) {
+			/*
+			 *	Put m.host in m-entry.
+			 */
+			ret = mfw_addhost(mfw, mu, cmd == IP_MASQ_CMD_ADD);
+
+			/*
+			 *	If first host, link m-entry to hash table.
+			 *	Already protected by global lock.
+			 */
+			if (ret == 0 && atomic_read(&mfw->nhosts) == 1)  {
+				write_lock_bh(&mfw_lock);
+				__mfw_add(mfw);
+				write_unlock_bh(&mfw_lock);
+			} 
+			if (atomic_read(&mfw->nhosts) == 0) {
+				mfw_destroy(mfw);
+			}
+		}
+
+		up(&mfw_sema);
+
+		break;
+
+	case IP_MASQ_CMD_DEL:
+		down(&mfw_sema);
+
+		read_lock(&mfw_lock);
+		mfw = __mfw_get(mu->fwmark);
+		read_unlock(&mfw_lock);
+
+		if (mfw) {
+			ret = mfw_delhost(mfw, mu);
+
+			/*
+			 *	Last lease will free
+			 *	XXX check logic XXX
+			 */
+			if (atomic_read(&mfw->nhosts) == 0) {
+				write_lock_bh(&mfw_lock);
+				__mfw_del(mfw);
+				write_unlock_bh(&mfw_lock);
+				mfw_destroy(mfw);
+			}
+		} else 
+			ret = -ESRCH;
+
+		up(&mfw_sema);
+		break;
+	case IP_MASQ_CMD_FLUSH:
+
+		down(&mfw_sema);
+		mfw_flush();
+		up(&mfw_sema);
+		ret = 0;
+		break;
+	case IP_MASQ_CMD_SET:
+		/*
+		 *	No need to semaphorize here, main list is not 
+		 *	modified.
+		 */
+		read_lock(&mfw_lock);
+		
+		mfw = __mfw_get(mu->fwmark);
+		if (mfw) {
+			write_lock_bh(&mfw->lock);
+			
+			if (mu->flags & IP_MASQ_MFW_SCHED) {
+				struct ip_masq_mfw_host *h;
+				if ((h=__mfw_sched(mfw, 1))) {
+					mfw_host_to_user(h, mu);
+					ret = 0;
+				} 
+			} else {
+				ret = __mfw_edithost(mfw, mu);
+			}
+				
+			write_unlock_bh(&mfw->lock);
+		}
+
+		read_unlock(&mfw_lock);
+		break;
+	}
+out:
+	
+	return ret;
+}
+
+/*
+ *	Module stubs called from ip_masq core module
+ */
+ 
+/*
+ *	Input rule stub, called very early for each incoming packet, 
+ *	to see if this module has "interest" in packet.
+ */
+static int mfw_in_rule(const struct sk_buff *skb, const struct iphdr *iph)
+{
+	int val;
+	read_lock(&mfw_lock);
+	val = ( __mfw_get(skb->fwmark) != 0);
+	read_unlock(&mfw_lock);
+	return val;
+}
+
+/*
+ *	Input-create stub, called to allow "custom" masq creation
+ */
+static struct ip_masq * mfw_in_create(const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr)
+{
+	union ip_masq_tphdr tph;
+	struct ip_masq *ms = NULL;
+	struct ip_masq_mfw_host *h = NULL;
+
+	tph.raw = (char*) iph + iph->ihl * 4;
+
+	switch (iph->protocol) {
+		case IPPROTO_TCP:
+			/* 	
+			 *	Only open TCP tunnel if SYN+!ACK packet
+			 */
+			if (!tph.th->syn && tph.th->ack)
+				return NULL;
+		case IPPROTO_UDP:
+			break;
+		default:
+			return NULL;
+	}
+
+	/* 
+	 *	If no entry exists in the masquerading table
+ 	 * 	and the port is involved
+	 *  	in port forwarding, create a new masq entry 
+	 */
+
+	if ((h=mfw_lookup(skb->fwmark))) {
+		ms = ip_masq_new(iph->protocol,
+				iph->daddr, tph.portp[1],	
+				/* if no redir-port, use packet dest port */
+				h->addr, h->port? h->port : tph.portp[1],
+				iph->saddr, tph.portp[0],
+				0);
+
+		if (ms != NULL)
+			ip_masq_listen(ms);
+	}
+	return ms;
+}
+
+
+#define mfw_in_update	NULL
+#define mfw_out_rule	NULL
+#define mfw_out_create	NULL
+#define mfw_out_update	NULL
+
+static struct ip_masq_mod mfw_mod = {
+	NULL,			/* next */
+	NULL,			/* next_reg */
+	"mfw",		/* name */
+	ATOMIC_INIT(0),		/* nent */
+	ATOMIC_INIT(0),		/* refcnt */
+	proc_ent,
+	mfw_ctl,
+	NULL,			/* masq_mod_init */
+	NULL,			/* masq_mod_done */
+	mfw_in_rule,
+	mfw_in_update,
+	mfw_in_create,
+	mfw_out_rule,
+	mfw_out_update,
+	mfw_out_create,
+};
+
+
+__initfunc(int ip_mfw_init(void))
+{
+	sema_init(&mfw_sema, 1);
+	return register_ip_masq_mod ((mmod_self=&mfw_mod));
+}
+
+int ip_mfw_done(void)
+{
+	return unregister_ip_masq_mod(&mfw_mod);
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+	if (ip_mfw_init() != 0)
+		return -EIO;
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	if (ip_mfw_done() != 0)
+		printk(KERN_INFO "can't remove module");
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/ip_masq_portfw.c linux/net/ipv4/ip_masq_portfw.c
--- v2.2.0-pre4/linux/net/ipv4/ip_masq_portfw.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ip_masq_portfw.c	Mon Jan  4 15:31:35 1999
@@ -2,7 +2,7 @@
  *		IP_MASQ_PORTFW masquerading module
  *
  *
- *	$Id: ip_masq_portfw.c,v 1.2 1998/08/29 23:51:11 davem Exp $
+ *	$Id: ip_masq_portfw.c,v 1.3 1998/12/08 05:42:12 davem Exp $
  *
  * Author:	Steven Clarke <steven.clarke@monmouth.demon.co.uk>
  *
@@ -269,15 +269,18 @@
 	IP_MASQ_DEBUG(1-debug, "ip_masq_portfw_ctl(cmd=%d)\n", cmd);
 
 
-        if (cmd != IP_MASQ_CMD_FLUSH) {
-		if (htons(mm->lport) < IP_PORTFW_PORT_MIN 
-				|| htons(mm->lport) > IP_PORTFW_PORT_MAX)
-			return EINVAL;
-
-                if (mm->protocol!=IPPROTO_TCP && mm->protocol!=IPPROTO_UDP)
-                        return EINVAL;
-        }
+	switch (cmd) {
+		case IP_MASQ_CMD_NONE:
+			return 0;
+		case IP_MASQ_CMD_FLUSH:
+			break;
+		default:
+			if (htons(mm->lport) < IP_PORTFW_PORT_MIN || htons(mm->lport) > IP_PORTFW_PORT_MAX)
+				return EINVAL;
 
+			if (mm->protocol!=IPPROTO_TCP && mm->protocol!=IPPROTO_UDP)
+				return EINVAL;
+	}
 
 	switch(cmd) {
 	case IP_MASQ_CMD_ADD:
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.2.0-pre4/linux/net/ipv4/ip_output.c	Mon Oct  5 13:13:48 1998
+++ linux/net/ipv4/ip_output.c	Tue Jan  5 11:33:43 1999
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) output module.
  *
- * Version:	$Id: ip_output.c,v 1.63 1998/10/03 09:37:30 davem Exp $
+ * Version:	$Id: ip_output.c,v 1.64 1999/01/04 20:05:33 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -461,7 +461,7 @@
 	id = htons(ip_id_count++);
 
 	/*
-	 *	Being outputting the bytes.
+	 *	Begin outputting the bytes.
 	 */
 	 
 	do {
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/ipconfig.c linux/net/ipv4/ipconfig.c
--- v2.2.0-pre4/linux/net/ipv4/ipconfig.c	Thu Dec 31 10:29:03 1998
+++ linux/net/ipv4/ipconfig.c	Mon Jan  4 15:31:35 1999
@@ -1,13 +1,17 @@
 /*
- *  $Id: ipconfig.c,v 1.16 1998/10/21 22:27:26 davem Exp $
+ *  $Id: ipconfig.c,v 1.18 1999/01/04 20:14:10 davem Exp $
  *
  *  Automatic Configuration of IP -- use BOOTP or RARP or user-supplied
  *  information to configure own IP address and routes.
  *
- *  Copyright (C) 1996, 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *  Copyright (C) 1996--1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
  *
  *  Derived from network configuration code in fs/nfs/nfsroot.c,
  *  originally Copyright (C) 1995, 1996 Gero Kuhlmann and me.
+ *
+ *  BOOTP rewritten to construct and analyse packets itself instead
+ *  of misusing the IP layer. num_bugs_causing_wrong_arp_replies--;
+ *					     -- MJ, December 1998
  */
 
 #include <linux/config.h>
@@ -21,22 +25,20 @@
 #include <linux/in.h>
 #include <linux/if.h>
 #include <linux/inet.h>
-#include <linux/net.h>
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
 #include <linux/skbuff.h>
 #include <linux/ip.h>
 #include <linux/socket.h>
-#include <linux/inetdevice.h>
 #include <linux/route.h>
-#include <net/route.h>
-#include <net/sock.h>
+#include <linux/udp.h>
 #include <net/arp.h>
-#include <net/ip_fib.h>
+#include <net/ip.h>
 #include <net/ipconfig.h>
 
 #include <asm/segment.h>
 #include <asm/uaccess.h>
+#include <asm/checksum.h>
 
 /* Define this to allow debugging output */
 #undef IPCONFIG_DEBUG
@@ -60,8 +62,6 @@
 u32 ic_servaddr __initdata = INADDR_NONE;	/* Server IP address */
 u32 ic_gateway __initdata = INADDR_NONE;	/* Gateway IP address */
 u32 ic_netmask __initdata = INADDR_NONE;	/* Netmask for local subnet */
-int ic_bootp_flag __initdata = 1;		/* Use BOOTP */
-int ic_rarp_flag __initdata = 1;		/* Use RARP */
 int ic_enable __initdata = 1;			/* Automatic IP configuration enabled */
 int ic_host_name_set __initdata = 0;		/* Host name configured manually */
 int ic_set_manually __initdata = 0;		/* IPconfig parameters set manually */
@@ -73,13 +73,17 @@
 
 #define CONFIG_IP_PNP_DYNAMIC
 
-static int ic_got_reply __initdata = 0;
+static int ic_proto_enabled __initdata = IC_BOOTP | IC_RARP;	/* Protocols enabled */
+static int ic_got_reply __initdata = 0;				/* Protocol(s) we got reply from */
+
+#else
 
-#define IC_GOT_BOOTP 1
-#define IC_GOT_RARP 2
+static int ic_proto_enabled __initdata = 0;
 
 #endif
 
+static int ic_proto_have_if __initdata = 0;
+
 /*
  *	Network devices
  */
@@ -88,14 +92,13 @@
 	struct ic_device *next;
 	struct device *dev;
 	unsigned short flags;
+	int able;
 };
 
 static struct ic_device *ic_first_dev __initdata = NULL;/* List of open device */
 static struct device *ic_dev __initdata = NULL;		/* Selected device */
-static int bootp_dev_count __initdata = 0;		/* BOOTP capable devices */
-static int rarp_dev_count __initdata = 0;		/* RARP capable devices */
 
-__initfunc(int ic_open_devs(void))
+static int __init ic_open_devs(void)
 {
 	struct ic_device *d, **last;
 	struct device *dev;
@@ -103,10 +106,20 @@
 
 	last = &ic_first_dev;
 	for (dev = dev_base; dev; dev = dev->next)
-		if (dev->type < ARPHRD_SLIP &&
-		    !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) &&
-		    strncmp(dev->name, "dummy", 5) &&
-		    (!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) {
+		if (user_dev_name[0] ? !strcmp(dev->name, user_dev_name) :
+		    (!(dev->flags & IFF_LOOPBACK) &&
+		     (dev->flags & (IFF_POINTOPOINT|IFF_BROADCAST)) &&
+		     strncmp(dev->name, "dummy", 5))) {
+			int able = 0;
+			if (dev->mtu >= 364)
+				able |= IC_BOOTP;
+			else
+				printk(KERN_WARNING "BOOTP: Ignoring device %s, MTU %d too small", dev->name, dev->mtu);
+			if (!(dev->flags & IFF_NOARP))
+				able |= IC_RARP;
+			able &= ic_proto_enabled;
+			if (ic_proto_enabled && !able)
+				continue;
 			oflags = dev->flags;
 			if (dev_change_flags(dev, oflags | IFF_UP) < 0) {
 				printk(KERN_ERR "IP-Config: Failed to open %s\n", dev->name);
@@ -118,14 +131,13 @@
 			*last = d;
 			last = &d->next;
 			d->flags = oflags;
-			bootp_dev_count++;
-			if (!(dev->flags & IFF_NOARP))
-				rarp_dev_count++;
-			DBG(("IP-Config: Opened %s\n", dev->name));
+			d->able = able;
+			ic_proto_have_if |= able;
+			DBG(("IP-Config: Opened %s (able=%d)\n", dev->name, able));
 		}
 	*last = NULL;
 
-	if (!bootp_dev_count) {
+	if (!ic_first_dev) {
 		if (user_dev_name[0])
 			printk(KERN_ERR "IP-Config: Device `%s' not found.\n", user_dev_name);
 		else
@@ -135,7 +147,7 @@
 	return 0;
 }
 
-__initfunc(void ic_close_devs(void))
+static void __init ic_close_devs(void)
 {
 	struct ic_device *d, *next;
 	struct device *dev;
@@ -164,7 +176,7 @@
 	sin->sin_port = port;
 }
 
-__initfunc(static int ic_dev_ioctl(unsigned int cmd, struct ifreq *arg))
+static int __init ic_dev_ioctl(unsigned int cmd, struct ifreq *arg)
 {
 	int res;
 
@@ -175,7 +187,7 @@
 	return res;
 }
 
-__initfunc(static int ic_route_ioctl(unsigned int cmd, struct rtentry *arg))
+static int __init ic_route_ioctl(unsigned int cmd, struct rtentry *arg)
 {
 	int res;
 
@@ -190,7 +202,7 @@
  *	Set up interface addresses and routes.
  */
 
-__initfunc(static int ic_setup_if(void))
+static int __init ic_setup_if(void)
 {
 	struct ifreq ir;
 	struct sockaddr_in *sin = (void *) &ir.ifr_ifru.ifru_addr;
@@ -216,7 +228,7 @@
 	return 0;
 }
 
-__initfunc(int ic_setup_routes(void))
+static int __init ic_setup_routes(void)
 {
 	/* No need to setup device routes, only the default route... */
 
@@ -246,7 +258,7 @@
  *	Fill in default values for all missing parameters.
  */
 
-__initfunc(int ic_defaults(void))
+static int __init ic_defaults(void)
 {
 	/*
 	 *	At this point we have no userspace running so need not
@@ -270,6 +282,7 @@
 			printk(KERN_ERR "IP-Config: Unable to guess netmask for address %08x\n", ic_myaddr);
 			return -1;
 		}
+		printk("IP-Config: Guessing netmask %s\n", in_ntoa(ic_netmask));
 	}
 
 	return 0;
@@ -281,25 +294,22 @@
 
 #ifdef CONFIG_IP_PNP_RARP
 
-static int ic_rarp_recv(struct sk_buff *skb, struct device *dev,
-			struct packet_type *pt);
+static int ic_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt);
 
 static struct packet_type rarp_packet_type __initdata = {
-	0,			/* Should be: __constant_htons(ETH_P_RARP)
-				 * - but this _doesn't_ come out constant! */
+	__constant_htons(ETH_P_RARP),
 	NULL,			/* Listen to all devices */
 	ic_rarp_recv,
 	NULL,
 	NULL
 };
 
-__initfunc(static void ic_rarp_init(void))
+static inline void ic_rarp_init(void)
 {
-	rarp_packet_type.type = htons(ETH_P_RARP);
 	dev_add_pack(&rarp_packet_type);
 }
 
-__initfunc(static void ic_rarp_cleanup(void))
+static inline void ic_rarp_cleanup(void)
 {
 	dev_remove_pack(&rarp_packet_type);
 }
@@ -307,14 +317,18 @@
 /*
  *  Process received RARP packet.
  */
-__initfunc(static int
-ic_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt))
+static int __init
+ic_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
 	struct arphdr *rarp = (struct arphdr *)skb->h.raw;
 	unsigned char *rarp_ptr = (unsigned char *) (rarp + 1);
 	unsigned long sip, tip;
 	unsigned char *sha, *tha;		/* s for "source", t for "target" */
 
+	/* If we already have a reply, just drop the packet */
+	if (ic_got_reply)
+		goto drop;
+
 	/* If this test doesn't pass, it's not IP, or we should ignore it anyway */
 	if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd))
 		goto drop;
@@ -346,7 +360,7 @@
 
 	/* Victory! The packet is what we were looking for! */
 	if (!ic_got_reply) {
-		ic_got_reply = IC_GOT_RARP;
+		ic_got_reply = IC_RARP;
 		ic_dev = dev;
 		if (ic_myaddr == INADDR_NONE)
 			ic_myaddr = tip;
@@ -363,16 +377,16 @@
 /*
  *  Send RARP request packet over all devices which allow RARP.
  */
-__initfunc(static void ic_rarp_send(void))
+static void __init ic_rarp_send(void)
 {
 	struct ic_device *d;
 
-	for (d=ic_first_dev; d; d=d->next) {
-		struct device *dev = d->dev;
-		if (!(dev->flags & IFF_NOARP))
+	for (d=ic_first_dev; d; d=d->next)
+		if (d->able & IC_RARP) {
+			struct device *dev = d->dev;
 			arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL,
 				 dev->dev_addr, dev->dev_addr);
-	}
+		}
 }
 
 #endif
@@ -383,10 +397,9 @@
 
 #ifdef CONFIG_IP_PNP_BOOTP
 
-static struct socket *ic_bootp_xmit_sock __initdata = NULL; /* BOOTP send socket */
-static struct socket *ic_bootp_recv_sock __initdata = NULL; /* BOOTP receive socket */
-
 struct bootp_pkt {		/* BOOTP packet format */
+	struct iphdr iph;	/* IP header */
+	struct udphdr udph;	/* UDP header */
 	u8 op;			/* 1=request, 2=reply */
 	u8 htype;		/* HW address type */
 	u8 hlen;		/* HW address length */
@@ -407,150 +420,23 @@
 #define BOOTP_REQUEST 1
 #define BOOTP_REPLY 2
 
-static struct bootp_pkt *ic_xmit_bootp __initdata = NULL; /* Packet being transmitted */
-static struct bootp_pkt *ic_recv_bootp __initdata = NULL; /* Packet being received */
-
-/*
- *  Allocation and freeing of BOOTP packet buffers.
- */
-__initfunc(static int ic_bootp_alloc(void))
-{
-	if (!(ic_xmit_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL)) ||
-	    !(ic_recv_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL))) {
-		printk(KERN_ERR "BOOTP: Out of memory!\n");
-		return -1;
-	}
-	return 0;
-}
-
-__initfunc(static void ic_bootp_free(void))
-{
-	if (ic_xmit_bootp) {
-		kfree_s(ic_xmit_bootp, sizeof(struct bootp_pkt));
-		ic_xmit_bootp = NULL;
-	}
-	if (ic_recv_bootp) {
-		kfree_s(ic_recv_bootp, sizeof(struct bootp_pkt));
-		ic_recv_bootp = NULL;
-	}
-}
-
-
-/*
- *  Add / Remove fake interface addresses for BOOTP packet sending.
- */
-__initfunc(static int ic_bootp_addrs_add(void))
-{
-	struct ic_device *d;
-	int err;
-
-	for(d=ic_first_dev; d; d=d->next)
-		if ((err = inet_add_bootp_addr(d->dev)) < 0) {
-			printk(KERN_ERR "BOOTP: Unable to set interface address\n");
-			return -1;
-		}
-	return 0;
-}
-
-__initfunc(static void ic_bootp_addrs_del(void))
-{
-	struct ic_device *d;
-
-	for(d=ic_first_dev; d; d=d->next)
-		inet_del_bootp_addr(d->dev);
-}
-
-/*
- *  UDP socket operations.
- */
-__initfunc(static int ic_udp_open(struct socket **sock))
-{
-	int err;
-
-	if ((err = sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, sock)) < 0)
-		printk(KERN_ERR "BOOTP: Cannot open UDP socket!\n");
-	return err;
-}
-
-static inline void ic_udp_close(struct socket *sock)
-{
-	if (sock)
-		sock_release(sock);
-}
-
-__initfunc(static int ic_udp_connect(struct socket *sock, u32 addr, u16 port))
-{
-	struct sockaddr_in sa;
-	int err;
-
-	set_sockaddr(&sa, htonl(addr), htons(port));
-	err = sock->ops->connect(sock, (struct sockaddr *) &sa, sizeof(sa), 0);
-	if (err < 0) {
-		printk(KERN_ERR "BOOTP: connect() failed (%d)\n", err);
-		return -1;
-	}
-	return 0;
-}
-
-__initfunc(static int ic_udp_bind(struct socket *sock, u32 addr, u16 port))
-{
-	struct sockaddr_in sa;
-	int err;
-
-	set_sockaddr(&sa, htonl(addr), htons(port));
-	err = sock->ops->bind(sock, (struct sockaddr *) &sa, sizeof(sa));
-	if (err < 0) {
-		printk(KERN_ERR "BOOTP: bind() failed (%d)\n", err);
-		return -1;
-	}
-	return 0;
-}
-
-__initfunc(static int ic_udp_send(struct socket *sock, void *buf, int size))
-{
-	mm_segment_t oldfs;
-	int result;
-	struct msghdr msg;
-	struct iovec iov;
-
-	oldfs = get_fs();
-	set_fs(get_ds());
-	iov.iov_base = buf;
-	iov.iov_len = size;
-	memset(&msg, 0, sizeof(msg));
-	msg.msg_iov = &iov;
-	msg.msg_iovlen = 1;
-	result = sock_sendmsg(sock, &msg, size);
-	set_fs(oldfs);
-
-	return (result != size);
-}
+static u32 ic_bootp_xid;
 
-__initfunc(static int ic_udp_recv(struct socket *sock, void *buf, int size))
-{
-	mm_segment_t oldfs;
-	int result;
-	struct msghdr msg;
-	struct iovec iov;
+static int ic_bootp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt);
 
-	oldfs = get_fs();
-	set_fs(get_ds());
-	iov.iov_base = buf;
-	iov.iov_len = size;
-	memset(&msg, 0, sizeof(msg));
-	msg.msg_flags = MSG_DONTWAIT;
-	msg.msg_iov = &iov;
-	msg.msg_iovlen = 1;
-	result = sock_recvmsg(sock, &msg, size, MSG_DONTWAIT);
-	set_fs(oldfs);
-	return result;
-}
+static struct packet_type bootp_packet_type __initdata = {
+	__constant_htons(ETH_P_IP),
+	NULL,			/* Listen to all devices */
+	ic_bootp_recv,
+	NULL,
+	NULL
+};
 
 
 /*
  *  Initialize BOOTP extension fields in the request.
  */
-__initfunc(static void ic_bootp_init_ext(u8 *e))
+static void __init ic_bootp_init_ext(u8 *e)
 {
 	*e++ = 99;		/* RFC1048 Magic Cookie */
 	*e++ = 130;
@@ -578,92 +464,96 @@
 /*
  *  Initialize the BOOTP mechanism.
  */
-__initfunc(static int ic_bootp_init(void))
+static inline void ic_bootp_init(void)
 {
-	/* Allocate memory for BOOTP packets */
-	if (ic_bootp_alloc() < 0)
-		return -1;
-
-	/* Add fake zero addresses to all interfaces */
-	if (ic_bootp_addrs_add() < 0)
-		return -1;
-	
-	/* Setting the addresses automatically creates appropriate
-	   routes. */	
-
-	/* Initialize common portion of BOOTP request */
-	memset(ic_xmit_bootp, 0, sizeof(struct bootp_pkt));
-	ic_xmit_bootp->op = BOOTP_REQUEST;
-	get_random_bytes(&ic_xmit_bootp->xid, sizeof(ic_xmit_bootp->xid));
-	ic_bootp_init_ext(ic_xmit_bootp->vendor_area);
-
-	DBG(("BOOTP: XID=%08x\n", ic_xmit_bootp->xid));
-
-	/* Open the sockets */
-	if (ic_udp_open(&ic_bootp_xmit_sock) ||
-	    ic_udp_open(&ic_bootp_recv_sock))
-		return -1;
-
-	/* Bind/connect the sockets */
-	ic_bootp_xmit_sock->sk->broadcast = 1;
-	ic_bootp_xmit_sock->sk->reuse = 1;
-	ic_bootp_recv_sock->sk->reuse = 1;
-	if (ic_udp_bind(ic_bootp_recv_sock, INADDR_ANY, 68) ||
-	    ic_udp_bind(ic_bootp_xmit_sock, INADDR_ANY, 68) ||
-	    ic_udp_connect(ic_bootp_xmit_sock, INADDR_BROADCAST, 67))
-		return -1;
-
-	return 0;
+	get_random_bytes(&ic_bootp_xid, sizeof(u32));
+	DBG(("BOOTP: XID=%08x\n", ic_bootp_xid));
+	dev_add_pack(&bootp_packet_type);
 }
 
 
 /*
  *  BOOTP cleanup.
  */
-__initfunc(static void ic_bootp_cleanup(void))
+static inline void ic_bootp_cleanup(void)
 {
-	ic_udp_close(ic_bootp_xmit_sock);
-	ic_udp_close(ic_bootp_recv_sock);
-	ic_bootp_addrs_del();
-	ic_bootp_free();
+	dev_remove_pack(&bootp_packet_type);
 }
 
 
 /*
  *  Send BOOTP request to single interface.
  */
-__initfunc(static int ic_bootp_send_if(struct ic_device *d, u32 jiffies))
+static void __init ic_bootp_send_if(struct ic_device *d, u32 jiffies)
 {
 	struct device *dev = d->dev;
-	struct bootp_pkt *b = ic_xmit_bootp;
+	struct sk_buff *skb;
+	struct bootp_pkt *b;
+	int hh_len = (dev->hard_header_len + 15) & ~15;
+	struct iphdr *h;
+
+	/* Allocate packet */
+	skb = alloc_skb(sizeof(struct bootp_pkt) + hh_len + 15, GFP_KERNEL);
+	if (!skb)
+		return;
+	skb_reserve(skb, hh_len);
+	b = (struct bootp_pkt *) skb_put(skb, sizeof(struct bootp_pkt));
+	memset(b, 0, sizeof(struct bootp_pkt));
+
+	/* Construct IP header */
+	skb->nh.iph = h = &b->iph;
+	h->version = 4;
+	h->ihl = 5;
+	h->tot_len = htons(sizeof(struct bootp_pkt));
+	h->frag_off = htons(IP_DF);
+	h->ttl = 1;
+	h->protocol = IPPROTO_UDP;
+	h->daddr = INADDR_BROADCAST;
+	h->check = 0;
+	h->check = ip_fast_csum((unsigned char *) h, h->ihl);
+
+	/* Construct UDP header */
+	b->udph.source = htons(68);
+	b->udph.dest = htons(67);
+	b->udph.len = htons(sizeof(struct bootp_pkt) - sizeof(struct iphdr));
+	/* UDP checksum not calculated -- explicitly allowed in BOOTP RFC */
 
+	/* Construct BOOTP header */
+	b->op = BOOTP_REQUEST;
 	b->htype = dev->type;
 	b->hlen = dev->addr_len;
-	memset(b->hw_addr, 0, sizeof(b->hw_addr));
 	memcpy(b->hw_addr, dev->dev_addr, dev->addr_len);
 	b->secs = htons(jiffies / HZ);
-	return ic_udp_send(ic_bootp_xmit_sock, b, sizeof(struct bootp_pkt));
+	b->xid = ic_bootp_xid;
+	ic_bootp_init_ext(b->vendor_area);
+
+	/* Chain packet down the line... */
+	skb->dev = dev;
+	skb->protocol = __constant_htons(ETH_P_IP);
+	if ((dev->hard_header &&
+	     dev->hard_header(skb, dev, ntohs(skb->protocol), dev->broadcast, dev->dev_addr, skb->len) < 0) ||
+	    dev_queue_xmit(skb) < 0)
+		printk("E");
 }
 
 
 /*
  *  Send BOOTP requests to all interfaces.
  */
-__initfunc(static int ic_bootp_send(u32 jiffies))
+static void __init ic_bootp_send(u32 jiffies)
 {
 	struct ic_device *d;
 
 	for(d=ic_first_dev; d; d=d->next)
-		if (ic_bootp_send_if(d, jiffies) < 0)
-			return -1;
-	return 0;
+		if (d->able & IC_BOOTP)
+			ic_bootp_send_if(d, jiffies);
 }
 
 
 /*
  *  Copy BOOTP-supplied string if not already set.
  */
-__initfunc(static int ic_bootp_string(char *dest, char *src, int len, int max))
+static int __init ic_bootp_string(char *dest, char *src, int len, int max)
 {
 	if (!len)
 		return 0;
@@ -678,7 +568,7 @@
 /*
  *  Process BOOTP extension.
  */
-__initfunc(static void ic_do_bootp_ext(u8 *ext))
+static void __init ic_do_bootp_ext(u8 *ext)
 {
 #ifdef IPCONFIG_DEBUG
 	u8 *c;
@@ -714,65 +604,64 @@
 
 
 /*
- *  Receive BOOTP request.
+ *  Receive BOOTP reply.
  */
-__initfunc(static void ic_bootp_recv(void))
+static int __init ic_bootp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
+	struct bootp_pkt *b = (struct bootp_pkt *) skb->nh.iph;
+	struct iphdr *h = &b->iph;
 	int len;
-	u8 *ext, *end, *opt;
-	struct ic_device *d;
-	struct bootp_pkt *b = ic_recv_bootp;
 
-	if ((len = ic_udp_recv(ic_bootp_recv_sock, b, sizeof(struct bootp_pkt))) < 0)
-		return;
+	/* If we already have a reply, just drop the packet */
+	if (ic_got_reply)
+		goto drop;
 
-	/* Check consistency of incoming packet */
-	if (len < 300 ||			/* See RFC 1542:2.1 */
-	    b->op != BOOTP_REPLY ||
-	    b->xid != ic_xmit_bootp->xid) {
-		printk("?");
-		return;
-		}
+	/* Check whether it's a BOOTP packet */
+	if (skb->pkt_type == PACKET_OTHERHOST ||
+	    skb->len < sizeof(struct udphdr) + sizeof(struct iphdr) ||
+	    h->ihl != 5 ||
+	    h->version != 4 ||
+	    ip_fast_csum((char *) h, h->ihl) != 0 ||
+	    skb->len < ntohs(h->tot_len) ||
+	    h->protocol != IPPROTO_UDP ||
+	    b->udph.source != htons(67) ||
+	    b->udph.dest != htons(68) ||
+	    ntohs(h->tot_len) < ntohs(b->udph.len) + sizeof(struct iphdr))
+		goto drop;
 
-	/* Find interface this arrived from */
-	for(d=ic_first_dev; d; d=d->next) {
-		struct device *dev = d->dev;
-		if (b->htype == dev->type ||
-		    b->hlen == dev->addr_len ||
-		    !memcmp(b->hw_addr, dev->dev_addr, dev->addr_len))
-			break;
-	}
-	if (!d) {	/* Unknown device */
-		printk("!");
-		return;
+	/* Fragments are not supported */
+	if (h->frag_off & htons(IP_OFFSET|IP_MF)) {
+		printk(KERN_ERR "BOOTP: Ignoring fragmented reply.\n");
+		goto drop;
 	}
 
-	/* Record BOOTP packet arrival */
-	cli();
-	if (ic_got_reply) {
-		sti();
-		return;
+	/* Is it a reply to our BOOTP request? */
+	len = ntohs(b->udph.len) - sizeof(struct udphdr);
+	if (len < 300 ||				    /* See RFC 951:2.1 */
+	    b->op != BOOTP_REPLY ||
+	    b->xid != ic_bootp_xid) {
+		printk("?");
+		goto drop;
 	}
-	ic_got_reply = IC_GOT_BOOTP;
-	sti();
-	ic_dev = d->dev;
 
 	/* Extract basic fields */
 	ic_myaddr = b->your_ip;
 	ic_servaddr = b->server_ip;
+	ic_got_reply = IC_BOOTP;
+	ic_dev = dev;
 
 	/* Parse extensions */
 	if (b->vendor_area[0] == 99 &&	/* Check magic cookie */
 	    b->vendor_area[1] == 130 &&
 	    b->vendor_area[2] == 83 &&
 	    b->vendor_area[3] == 99) {
-		ext = &b->vendor_area[4];
-		end = (u8 *) b + len;
+		u8 *ext = &b->vendor_area[4];
+		u8 *end = (u8 *) b + len;
 		while (ext < end && *ext != 0xff) {
 			if (*ext == 0)		/* Padding */
 				ext++;
 			else {
-				opt = ext;
+				u8 *opt = ext;
 				ext += ext[1] + 2;
 				if (ext <= end)
 					ic_do_bootp_ext(opt);
@@ -782,7 +671,12 @@
 
 	if (ic_gateway == INADDR_NONE && b->relay_ip)
 		ic_gateway = b->relay_ip;
-}
+
+drop:
+	kfree_skb(skb);
+	return 0;
+}	
+
 
 #endif
 
@@ -793,11 +687,13 @@
 
 #ifdef CONFIG_IP_PNP_DYNAMIC
 
-__initfunc(int ic_dynamic(void))
+static int __init ic_dynamic(void)
 {
 	int retries;
 	unsigned long timeout, jiff;
 	unsigned long start_jiffies;
+	int do_rarp = ic_proto_have_if & IC_RARP;
+	int do_bootp = ic_proto_have_if & IC_BOOTP;
 
 	/*
 	 * If neither BOOTP nor RARP was selected, return with an error. This
@@ -805,30 +701,22 @@
 	 * sing, and without BOOTP and RARP we are not able to get that in-
 	 * formation.
 	 */
-	if (!ic_bootp_flag && !ic_rarp_flag) {
+	if (!ic_proto_enabled) {
 		printk(KERN_ERR "IP-Config: Incomplete network configuration information.\n");
 		return -1;
 	}
 
 #ifdef CONFIG_IP_PNP_BOOTP
-	if (ic_bootp_flag && !bootp_dev_count) {
+	if ((ic_proto_enabled ^ ic_proto_have_if) & IC_BOOTP)
 		printk(KERN_ERR "BOOTP: No suitable device found.\n");
-		ic_bootp_flag = 0;
-	}
-#else
-	ic_bootp_flag = 0;
 #endif
 
 #ifdef CONFIG_IP_PNP_RARP
-	if (ic_rarp_flag && !rarp_dev_count) {
+	if ((ic_proto_enabled ^ ic_proto_have_if) & IC_RARP)
 		printk(KERN_ERR "RARP: No suitable device found.\n");
-		ic_rarp_flag = 0;
-	}
-#else
-	ic_rarp_flag = 0;
 #endif
 
-	if (!ic_bootp_flag && !ic_rarp_flag)
+	if (!ic_proto_have_if)
 		/* Error message already printed */
 		return -1;
 
@@ -836,14 +724,12 @@
 	 * Setup RARP and BOOTP protocols
 	 */
 #ifdef CONFIG_IP_PNP_RARP
-	if (ic_rarp_flag)
+	if (do_rarp)
 		ic_rarp_init();
 #endif
 #ifdef CONFIG_IP_PNP_BOOTP
-	if (ic_bootp_flag && ic_bootp_init() < 0) {
-		ic_bootp_cleanup();
-		return -1;
-	}
+	if (do_bootp)
+		ic_bootp_init();
 #endif
 
 	/*
@@ -855,36 +741,26 @@
 	 *  applies.. - AC]
 	 */
 	printk(KERN_NOTICE "Sending %s%s%s requests...",
-		ic_bootp_flag ? "BOOTP" : "",
-		ic_bootp_flag && ic_rarp_flag ? " and " : "",
-		ic_rarp_flag ? "RARP" : "");
+	        do_bootp ? "BOOTP" : "",
+		do_bootp && do_rarp ? " and " : "",
+		do_rarp ? "RARP" : "");
 	start_jiffies = jiffies;
 	retries = CONF_RETRIES;
 	get_random_bytes(&timeout, sizeof(timeout));
 	timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM);
 	for(;;) {
 #ifdef CONFIG_IP_PNP_BOOTP
-		if (ic_bootp_flag && ic_bootp_send(jiffies - start_jiffies) < 0) {
-			printk(" BOOTP failed!\n");
-			ic_bootp_cleanup();
-			ic_bootp_flag = 0;
-			if (!ic_rarp_flag)
-				break;
-		}
+		if (do_bootp)
+			ic_bootp_send(jiffies - start_jiffies);
 #endif
 #ifdef CONFIG_IP_PNP_RARP
-		if (ic_rarp_flag)
+		if (do_rarp)
 			ic_rarp_send();
 #endif
 		printk(".");
 		jiff = jiffies + timeout;
 		while (jiffies < jiff && !ic_got_reply)
-#ifdef CONFIG_IP_PNP_BOOTP
-			if (ic_bootp_flag)
-				ic_bootp_recv();
-#else
 			;
-#endif
 		if (ic_got_reply) {
 			printk(" OK\n");
 			break;
@@ -899,11 +775,11 @@
 	}
 
 #ifdef CONFIG_IP_PNP_RARP
-	if (ic_rarp_flag)
+	if (do_rarp)
 		ic_rarp_cleanup();
 #endif
 #ifdef CONFIG_IP_PNP_BOOTP
-	if (ic_bootp_flag)
+	if (do_bootp)
 		ic_bootp_cleanup();
 #endif
 
@@ -911,7 +787,7 @@
 		return -1;
 
 	printk("IP-Config: Got %s answer from %s, ",
-		(ic_got_reply == IC_GOT_BOOTP) ? "BOOTP" : "RARP",
+		(ic_got_reply & IC_BOOTP) ? "BOOTP" : "RARP",
 		in_ntoa(ic_servaddr));
 	printk("my address is %s\n", in_ntoa(ic_myaddr));
 
@@ -924,7 +800,7 @@
  *	IP Autoconfig dispatcher.
  */
 
-__initfunc(int ip_auto_config(void))
+int __init ip_auto_config(void)
 {
 	if (!ic_enable)
 		return 0;
@@ -1000,25 +876,44 @@
  *	<device>	- use all available devices
  *	<bootp|rarp|both|off> - use both protocols to determine my own address
  */
-__initfunc(void ip_auto_config_setup(char *addrs, int *ints))
+static int __init ic_proto_name(char *name)
+{
+	if (!strcmp(name, "off")) {
+		ic_proto_enabled = 0;
+		return 1;
+	}
+#ifdef CONFIG_IP_PNP_BOOTP
+	else if (!strcmp(name, "bootp")) {
+		ic_proto_enabled &= ~IC_RARP;
+		return 1;
+	}
+#endif
+#ifdef CONFIG_IP_PNP_RARP
+	else if (!strcmp(name, "rarp")) {
+		ic_proto_enabled &= ~IC_BOOTP;
+		return 1;
+	}
+#endif
+#ifdef CONFIG_IP_PNP_DYNAMIC
+	else if (!strcmp(name, "both")) {
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+void __init ip_auto_config_setup(char *addrs, int *ints)
 {
 	char *cp, *ip, *dp;
 	int num = 0;
 
 	ic_set_manually = 1;
-
-	if (!strcmp(addrs, "bootp")) {
-		ic_rarp_flag = 0;
-		return;
-	} else if (!strcmp(addrs, "rarp")) {
-		ic_bootp_flag = 0;
-		return;
-	} else if (!strcmp(addrs, "both")) {
-		return;
-	} else if (!strcmp(addrs, "off")) {
+	if (!strcmp(addrs, "off")) {
 		ic_enable = 0;
 		return;
 	}
+	if (ic_proto_name(addrs))
+		return;
 
 	/* Parse the whole string */
 	ip = addrs;
@@ -1059,12 +954,7 @@
 				user_dev_name[IFNAMSIZ-1] = '\0';
 				break;
 			case 6:
-				if (!strcmp(ip, "rarp"))
-					ic_bootp_flag = 0;
-				else if (!strcmp(ip, "bootp"))
-					ic_rarp_flag = 0;
-				else if (strcmp(ip, "both"))
-					ic_bootp_flag = ic_rarp_flag = 0;
+				ic_proto_name(ip);
 				break;
 			}
 		}
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.2.0-pre4/linux/net/ipv4/route.c	Thu Dec 31 10:29:03 1998
+++ linux/net/ipv4/route.c	Mon Jan  4 15:31:35 1999
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.58 1998/10/03 09:37:50 davem Exp $
+ * Version:	$Id: route.c,v 1.60 1999/01/04 20:14:52 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -949,10 +949,6 @@
 
 	if (BADCLASS(daddr) || ZERONET(daddr) || LOOPBACK(daddr))
 		goto martian_destination;
-
-	/* Accept anything arriving at 0.0.0.0 */
-	if (in_dev->ifa_list && in_dev->ifa_list->ifa_local == 0)
-		goto local_input;
 
 	/*
 	 *	Now we are ready to route packet.
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c
--- v2.2.0-pre4/linux/net/ipv4/sysctl_net_ipv4.c	Tue Dec 22 14:16:59 1998
+++ linux/net/ipv4/sysctl_net_ipv4.c	Mon Jan  4 15:31:35 1999
@@ -1,7 +1,7 @@
 /*
  * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem.
  *
- * $Id: sysctl_net_ipv4.c,v 1.36 1998/10/21 05:26:59 davem Exp $
+ * $Id: sysctl_net_ipv4.c,v 1.38 1999/01/02 16:51:48 davem Exp $
  *
  * Begun April 1, 1996, Mike Shaver.
  * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS]
@@ -67,6 +67,9 @@
 extern int sysctl_icmp_paramprob_time;
 extern int sysctl_icmp_echoreply_time;
 
+/* From igmp.c */
+extern int sysctl_igmp_max_memberships;
+
 int tcp_retr1_max = 255; 
 
 struct ipv4_config ipv4_config;
@@ -177,6 +180,10 @@
 	{NET_IPV4_ICMP_ECHOREPLY_RATE, "icmp_echoreply_rate",
 	 &sysctl_icmp_echoreply_time, sizeof(int), 0644, NULL, &proc_dointvec},
 	{NET_IPV4_ROUTE, "route", NULL, 0, 0555, ipv4_route_table},
+#ifdef CONFIG_IP_MULTICAST
+	{NET_IPV4_IGMP_MAX_MEMBERSHIPS, "igmp_max_memberships",
+	 &sysctl_igmp_max_memberships, sizeof(int), 0644, NULL, &proc_dointvec},
+#endif
 	{0}
 };
 
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.2.0-pre4/linux/net/ipv4/tcp_input.c	Fri Jan  1 12:58:21 1999
+++ linux/net/ipv4/tcp_input.c	Mon Jan  4 15:31:35 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.143 1998/12/20 20:20:20 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.145 1999/01/04 20:49:11 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -301,7 +301,7 @@
 			/* The retransmission queue is always in order, so
 			 * we can short-circuit the walk early.
 			 */
-			if(after(TCP_SKB_CB(skb)->seq, start_seq))
+			if(after(TCP_SKB_CB(skb)->seq, end_seq))
 				break;
 
 			/* We play conservative, we don't allow SACKS to partially
@@ -1170,7 +1170,7 @@
 	/* Zap SWALK, by moving every further SACK up by one slot.
 	 * Decrease num_sacks.
 	 */
-	for(this_sack += 1; this_sack < num_sacks-1; this_sack++, swalk++) {
+	for(; this_sack < num_sacks-1; this_sack++, swalk++) {
 		struct tcp_sack_block *next = (swalk + 1);
 		swalk->start_seq = next->start_seq;
 		swalk->end_seq = next->end_seq;
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.2.0-pre4/linux/net/ipv4/tcp_ipv4.c	Thu Dec 31 10:29:03 1998
+++ linux/net/ipv4/tcp_ipv4.c	Mon Jan  4 15:31:35 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.163 1998/11/30 15:24:22 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.164 1999/01/04 20:36:55 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -1642,14 +1642,15 @@
 		skb->csum = csum_partial((char *)th, len, 0);
 	case CHECKSUM_HW:
 		if (tcp_v4_check(th,len,skb->nh.iph->saddr,skb->nh.iph->daddr,skb->csum)) {
-			printk(KERN_DEBUG "TCPv4 bad checksum from %d.%d.%d.%d:%04x to %d.%d.%d.%d:%04x, "
-			       "len=%d/%d/%d\n",
- 			       NIPQUAD(skb->nh.iph->saddr),
-			       ntohs(th->source), 
-			       NIPQUAD(skb->nh.iph->daddr),
-			       ntohs(th->dest),
-			       len, skb->len,
-			       ntohs(skb->nh.iph->tot_len));
+			NETDEBUG(printk(KERN_DEBUG "TCPv4 bad checksum "
+					"from %d.%d.%d.%d:%04x to %d.%d.%d.%d:%04x, "
+					"len=%d/%d/%d\n",
+					NIPQUAD(skb->nh.iph->saddr),
+					ntohs(th->source), 
+					NIPQUAD(skb->nh.iph->daddr),
+					ntohs(th->dest),
+					len, skb->len,
+					ntohs(skb->nh.iph->tot_len)));
 	bad_packet:		
 			tcp_statistics.TcpInErrs++;
 			goto discard_it;
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.2.0-pre4/linux/net/ipv6/af_inet6.c	Mon Dec 28 15:00:53 1998
+++ linux/net/ipv6/af_inet6.c	Mon Jan  4 15:31:35 1999
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/af_inet.c
  *
- *	$Id: af_inet6.c,v 1.39 1998/10/03 09:38:23 davem Exp $
+ *	$Id: af_inet6.c,v 1.41 1999/01/02 16:51:50 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -502,7 +502,7 @@
 	__this_module.can_unload = &ipv6_unload;
 #endif
 
-	printk(KERN_INFO "IPv6 v0.2 for NET3.037\n");
+	printk(KERN_INFO "IPv6 v0.8 for NET4.0\n");
 
 	if (sizeof(struct inet6_skb_parm) > sizeof(dummy_skb->cb))
 	{
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.2.0-pre4/linux/net/ipx/af_ipx.c	Mon Dec 28 15:00:53 1998
+++ linux/net/ipx/af_ipx.c	Mon Jan  4 15:31:35 1999
@@ -2456,7 +2456,7 @@
 	proc_net_register(&ipx_rt_procinfo);
 #endif
 
-	printk(KERN_INFO "Swansea University Computer Society IPX 0.38 for NET3.037\n");
+	printk(KERN_INFO "NET4: Linux IPX 0.38 for NET4.0\n");
 	printk(KERN_INFO "IPX Portions Copyright (c) 1995 Caldera, Inc.\n");
 }
 
diff -u --recursive --new-file v2.2.0-pre4/linux/net/ipx/af_spx.c linux/net/ipx/af_spx.c
--- v2.2.0-pre4/linux/net/ipx/af_spx.c	Fri Jan  1 12:58:21 1999
+++ linux/net/ipx/af_spx.c	Mon Jan  4 15:31:35 1999
@@ -886,7 +886,7 @@
 
 	/* route socket(PF_IPX, SOCK_SEQPACKET) calls through spx_create() */
 
-	printk(KERN_INFO "Sequenced Packet eXchange (SPX) 0.02 for Linux NET3.037\n");
+	printk(KERN_INFO "NET4: Sequenced Packet eXchange (SPX) 0.02 for Linux NET4.0\n");
 	return;
 }
 
diff -u --recursive --new-file v2.2.0-pre4/linux/net/lapb/lapb_iface.c linux/net/lapb/lapb_iface.c
--- v2.2.0-pre4/linux/net/lapb/lapb_iface.c	Mon Jul  7 08:19:59 1997
+++ linux/net/lapb/lapb_iface.c	Mon Jan  4 15:31:35 1999
@@ -403,7 +403,7 @@
 
 __initfunc(void lapb_proto_init(struct net_proto *pro))
 {
-	printk(KERN_INFO "LAPB for Linux. Version 0.01 for Linux NET3.038 (Linux 2.1)\n");
+	printk(KERN_INFO "NET4: LAPB for Linux. Version 0.01 for NET4.0\n");
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.2.0-pre4/linux/net/socket.c linux/net/socket.c
--- v2.2.0-pre4/linux/net/socket.c	Fri Nov 27 13:09:31 1998
+++ linux/net/socket.c	Mon Jan  4 15:31:35 1999
@@ -1466,7 +1466,8 @@
 {
 	int i;
 
-	printk(KERN_INFO "Swansea University Computer Society NET3.039 for Linux 2.1\n");
+	printk(KERN_INFO "Linux NET4.0 for Linux 2.2\n");
+	printk(KERN_INFO "Based upon Swansea University Computer Society NET3.039\n");
 
 	/*
 	 *	Initialize all address (protocol) families. 
diff -u --recursive --new-file v2.2.0-pre4/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.2.0-pre4/linux/net/unix/af_unix.c	Mon Oct  5 13:13:49 1998
+++ linux/net/unix/af_unix.c	Mon Jan  4 15:31:35 1999
@@ -8,7 +8,7 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  *
- * Version:	$Id: af_unix.c,v 1.71 1998/10/03 09:39:05 davem Exp $
+ * Version:	$Id: af_unix.c,v 1.72 1998/11/21 06:50:00 davem Exp $
  *
  * Fixes:
  *		Linus Torvalds	:	Assorted bug cures.
@@ -1536,7 +1536,7 @@
 	struct sk_buff *dummy_skb;
 	struct proc_dir_entry *ent;
 	
-	printk(KERN_INFO "NET3: Unix domain sockets 0.16 for Linux NET3.038.\n");
+	printk(KERN_INFO "NET4: Unix domain sockets 1.0 for Linux NET4.0.\n");
 	if (sizeof(struct unix_skb_parms) > sizeof(dummy_skb->cb))
 	{
 		printk(KERN_CRIT "unix_proto_init: panic\n");
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops/Makefile linux/scripts/ksymoops/Makefile
--- v2.2.0-pre4/linux/scripts/ksymoops/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops/Makefile	Tue Jan  5 11:13:56 1999
@@ -0,0 +1,79 @@
+# Description file for ksymoops
+
+#	Thu Nov 26 16:37:46 EST 1998
+#	Version 0.6c
+#	Add -c option.
+
+#	Tue Nov  3 02:31:01 EST 1998
+#	Version 0.6
+#	Read lsmod (/proc/modules).
+#	Add Makefile defaults for vmlinux, ksyms, objects, System.map, lsmod.
+#	Upper case variables.
+#	Convert from a.out to bfd, using same format as ksymoops.
+
+DEFS = Makefile ksymoops.h
+
+# Defaults for vmlinux, ksyms, objects, lsmod, System.map.  Externalised so
+# distributions can tweak to suit their own file system layout.
+
+# To default to not reading a source, set to any empty string.
+# To default to reading a source, supply a quoted and escaped string.
+
+# If the string contains *r (*m, *n, *s) then it is replaced at run time by
+# the current value of `uname -r` (-m, -n, -s).  '*' was chosen as something
+# that rarely appears in filenames and does not cause problems like '%' or '$'.
+
+DEF_VMLINUX =				# default no vmlinux
+DEF_OBJECTS = \"/lib/modules/*r/\"	# default current modules
+DEF_KSYMS = \"/proc/ksyms\"		# default current ksyms
+DEF_LSMOD = \"/proc/modules\"		# default current lsmod
+DEF_MAP = \"/usr/src/linux/System.map\"	# default current map
+DEF_CODE_BYTES = 1			# default bytes per code unit
+
+# RedHat users might want defaults like these
+# DEF_MAP = \"/boot/System.map-*r\"
+# DEF_OBJECTS = \"/boot/module-info-*r\"
+
+PROGS = ksymoops
+
+CC=gcc
+CFLAGS = -Dlinux \
+	 -Wall \
+	 -Wno-conversion \
+	 -Waggregate-return \
+	 -Wstrict-prototypes \
+	 -Wmissing-prototypes \
+	 $(DEBUG)
+
+ifneq ($(strip $(DEF_VMLINUX)),)
+	CFLAGS += -DDEF_VMLINUX=$(strip $(DEF_VMLINUX))
+endif
+ifneq ($(strip $(DEF_OBJECTS)),)
+	CFLAGS += -DDEF_OBJECTS=$(strip $(DEF_OBJECTS))
+endif
+ifneq ($(strip $(DEF_KSYMS)),)
+	CFLAGS += -DDEF_KSYMS=$(strip $(DEF_KSYMS))
+endif
+ifneq ($(strip $(DEF_LSMOD)),)
+	CFLAGS += -DDEF_LSMOD=$(strip $(DEF_LSMOD))
+endif
+ifneq ($(strip $(DEF_MAP)),)
+	CFLAGS += -DDEF_MAP=$(strip $(DEF_MAP))
+endif
+
+CFLAGS += -DDEF_CODE_BYTES=$(strip $(DEF_CODE_BYTES))
+
+OBJECTS = io.o ksyms.o ksymoops.o map.o misc.o object.o oops.o re.o symbol.o
+
+all: 	$(PROGS)
+
+:	$(OBJECTS)
+
+$(OBJECTS): $(DEFS)
+
+$(PROGS): %: %.o $(DEFS) $(OBJECTS)
+	$(CC) $(OBJECTS) $(CFLAGS) -lbfd -liberty -o $@
+	-@size $@
+
+clean:
+	rm -f core *.o $(PROGS)
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops/README linux/scripts/ksymoops/README
--- v2.2.0-pre4/linux/scripts/ksymoops/README	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops/README	Tue Jan  5 11:13:56 1999
@@ -0,0 +1,395 @@
+  ksymoops.
+
+  Read a kernel Oops file and make the best stab at converting the code to
+  instructions and mapping stack values to kernel symbols.
+
+  Copyright Keith Owens <kaos@ocs.com.au>.
+  Released under the GNU Public Licence, Version 2.
+
+  To compile, simply type "make" in the ksymoops directory.
+
+  TESTERS WANTED.
+
+  ksymoops handles ix86.  It appears to handle Alpha, Sparc, M68K, PPC,
+  MIPS but I have no machine to test on.  I would appreciate feedback
+  from users of non ix86 machines.  In particular, it would be nice if
+  you could run
+
+   ksymoops -VMO -k /proc/ksyms -dd <oops.file >/tmp/ksymoops.log 2>&1
+
+  and mail /tmp/ksymoops.log to kaos@ocs.com.au
+
+  TODO:
+  Clean up these docs.
+  Tweak System.map to include arch information.
+  Tweak modutils to log at least one symbol for each module loaded,
+  otherwise they are invisible to ksymoops.  Also arch and version data.
+  Include sparc/sparc64 patches from Jakub Jelinek <jj@sunsite.mff.cuni.cz>.
+  Add object format override for sparc/soparc64 or any cross platform
+  oops debugging.
+
+  Mon Jan  4 09:48:13 EST 1999
+  Version 0.6e
+  Added to kernel.
+  Add ARM support.
+  Typo in oops_code.
+  Add -c option.
+  Add -1 option.
+  Report if options were specified or defaulted.
+  Remove false warnings when comparing ksyms and lsmod.
+  Performance inprovements.
+
+  Wed Oct 28 23:14:55 EST 1998
+  Version 0.5
+  No longer read vmlinux by default, it only duplicates System.map.
+
+  Wed Oct 28 13:46:39 EST 1998
+  Version 0.4
+  Split into separate sources.
+
+  Mon Oct 26 00:01:47 EST 1998
+  Version 0.3c
+  Add alpha (arm) processing.
+
+  Mon Oct 26 00:01:47 EST 1998
+  Version 0.3b
+  Add sparc processing.
+  Handle kernel symbol versions.
+
+  Fri Oct 23 13:11:20 EST 1998
+  Version 0.3
+  Add -follow to find command for people who use symlinks to modules.
+  Add Version_ checking.
+
+  Thu Oct 22 22:28:30 EST 1998
+  Version 0.2.
+  Generalise text prefix handling.
+  Handle messages on Code: line.
+  Format addresses with leading zeroes.
+  Minor bug fixes.
+
+  Wed Oct 21 23:28:48 EST 1998
+  Version 0.1.  Rewrite from scratch in C.
+
+  CREDITS.
+  Oops disassembly based on ksymoops.cc,
+    Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
+  m68k code based on ksymoops.cc changes by
+    Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
+
+  This code subsumes the Perl script make_System.map.pl which is no longer
+  supported.
+
+  Why another ksymoops I hear you ask?  Various complaints about
+  ksymoops.cc -
+
+  * It requires C++.
+  * It has hard wired limitations on the number of symbols.
+  * It does not handle modules at all.
+  * Very rigid requirements on the format of input, especially the Oops
+    log.
+  * No cross checking between ksyms, modules, System.map etc.
+  * Very little error checking, diagnostics are not suitable for
+    beginners.
+  * It only prints the trace and decoded code, users have to manually
+    extract the other lines from the Oops.
+  * Gives up on the slightest problem.
+  * Only handles i386 and possibly m68k.  The code is difficult to extend
+    to other architectures.
+  * Stops after the first Oops, you have to manually extract each one and
+    run through ksymoops one at a time.
+
+  This version is -
+  * C.
+  * No hard wired limitations (malloc as far as the eye can see).
+  * Handles modules by default.
+  * Uses regular pattern matching so it is a lot more forgiving about
+    input formats.
+  * By default, cross checks ksyms, modules, System.map and vmlinux.
+  * Lots of diagnostics and error checking.
+  * Prints all relevant lines for a complete Oops report.
+  * Tries to provide output no matter how bad the input is.  The level of
+     progress and error reporting is aimed at beginners.
+  * Handles i386, alpha, sparc, m68k.  It is a lot easier to extend to
+    other architectures (patches and/or sample data gratefully accepted).
+  * Handles all Oops in the input file(s).
+
+
+  Usage:	ksymoops
+		  [-v vmlinux]	Where to read vmlinux
+		  [-V]		No vmlinux is available
+		  [-o object_dir]	Directory containing modules
+		  [-O]		No modules is available
+		  [-k ksyms]	Where to read ksyms
+		  [-K]		No ksyms is available
+		  [-l lsmod]	Where to read lsmod
+		  [-L]		No lsmod is available
+		  [-m system.map]	Where to read System.map
+		  [-M]		No System.map is available
+		  [-s save.map]	Save consolidated map
+		  [-c code_bytes]	How many bytes in each unit of code
+		  [-1]		One shot toggle (exit after first Oops)
+		  [-d]		Increase debug level by 1
+		  [-h]		Print help text
+		  Oops.file	Oops to decode
+
+	  All flags can occur more than once.  With the exception of -o
+	  and -d which are cumulative, the last occurrence of each flag is
+	  used.  Note that "-v my.vmlinux -V" will be taken as "No vmlinux
+	  available" but "-V -v my.vmlinux" will read my.vmlinux.  You
+	  will be warned about such combinations.
+
+	  Each occurrence of -d increases the debug level.
+
+	  Each -o flag can refer to a directory or to a single object
+	  file.  If a directory is specified then all *.o files in that
+	  directory and its subdirectories are assumed to be modules.
+
+	  If any of the vmlinux, object_dir, ksyms or system.map options
+	  contain the string *r (*m, *n, *s) then it is replaced at run time
+	  by the current value of `uname -r` (-m, -n, -s).
+
+	  The defaults can be changed in the Makefile, typical options are
+
+	  Defaults:	  -V
+			  -o /lib/modules/%r
+			  -k /proc/ksyms
+			  -l /proc/modules
+			  -m /usr/src/linux/System.map
+			  -c 1
+			  Oops report is read from stdin
+
+  Note:	  Unless you tell ksymoops *NOT* to read a particular file, it
+	  will try to read and reconcile almost all possible sources of kernel
+	  symbol information.  This is intended for beginners, they just
+	  type
+
+	    ksymoops < /var/log/syslog
+
+	  no thinking required.  Experts can point at different files or
+	  suppress the input from selected files.  For example, if you
+	  save /proc/ksyms before doing a test that creates an Oops, you
+	  can point ksymoops at the saved ksyms instead of using
+	  /proc/ksyms.
+
+	  vmlinux is not read by default, it only duplicates the
+	  information in System.map.  If you want to read vmlinux as well
+	  as or instead of System.map, use -v.
+
+	  To get the equivalent of the old ksymoops.cc (no vmlinux, no
+	  modules objects, no ksyms, no System.map) just do ksymoops
+	  -VOKLM.  Or to just read System.map, ksymoops -VOKL -m mapfile.
+
+
+  Return codes:	  0 - normal.
+		  1 - error(s) or warning(s) issued, results may not be
+		      reliable.
+		  2 - fatal error, no useful results.
+		  3 - One shot mode, end of input reached.
+
+  Supported architectures
+
+	  i386 tested.
+          m68k code derived from ksymoops.cc and reading traps.c, untested.
+	  MIPS tested.
+	  Sparc tested.
+	  Alpha tested.
+	  ARM tested.
+
+	  The term "eip" is generic, for example it includes the i386 EIP
+	  and the m68k PC.  Remember that objdump output always says EIP,
+	  no matter what the architecture, see objfile_head.
+
+	  To support another arch, check the Oops_ procedures between
+	  'Start architecture sensitive code' and 'End architecture
+	  sensitive code'.
+
+	  The pattern matching should take care of different lengths for
+	  the address, i.e. addresses should not be arch sensitive.  I
+	  assume that all addresses are at least 4 characters.
+
+	  If nm output has a different format on your arch, check for uses
+	  of re_nm.
+
+
+
+  Because ksymoops reads kernel information from multiple sources, there
+  could be mismatches.  ksymoops does the following cross checks, but only
+  if the specified files exist -
+
+  * Compare Version_nnn numbers from all sources against each other.  Pity
+    that only vmlinux and System.map have these symbols (as at 2.1.125),
+    however I check ksyms, modules and Oops as well.  If somebody adds
+    symbol Version_nnn to ksyms or modules or adds a Version_nnn line to
+    the Oops log, this code is ready.
+
+  * Compare kernel ksyms against vmlinux.  vmlinux takes precedence.
+
+  * Compare System.map against vmlinux.   vmlinux takes precedence.
+
+  * Compare vmlinux against System.map.   vmlinux takes precedence.
+
+  * Compare kernel ksyms against System.map.  System.map takes precedence.
+
+  * Compare modules against module ksyms.  modules take precedence.  Only
+    if at least one module appears in ksyms.
+
+  * Compare module names in ksyms against lsmod.  Warn if a module
+    appears in lsmod but not in ksyms.  Error if a modules appears in
+    ksyms but is not in lsmod.  Only if both ksyms and lsmod have being
+    read.
+
+  The precedence order is somewhat arbitrary, however it only applies if
+  there is any difference between the various sources.
+
+  Handling modules is awkward.  They can be loaded under different names
+  (insmod -o dummy1 dummy.o) and the text, data and read only data are
+  loaded at different offsets.  Although you can give the -m option to
+  insmod which will output the module map when it is loaded, this has a
+  few problems -
+
+  * No equivalent for removing a module.  If you load and remove a lot of
+    modules, you end up with multiple sets of symbols around the same
+    offsets, which set is correct?
+
+  * "insmod -o dummy1 dummy.o" still reports as dummy.  That is, there is
+     no way of telling which particular version of a multiply loaded
+     module the insmod output refers to.  Therefore there is no way of
+     telling which instantiation failed.
+
+  * Even if the above problems are fixed, how do you tell what the module
+    environment looked like when the Oops occurred?  What if a module is
+    loaded or removed just after Oops, how is the user expected to edit
+    the insmod log?  Rule 1 - make ksymoops easy for beginners.
+
+  Although those problems could be fixed, they require changes to
+  modutils.  Working from ksyms and the module objects can be done without
+  changing modutils and without confusing beginners.
+  
+  Alas the ksyms plus object approach has another problem - matching ksyms
+  to module objects.  Nowhere does the kernel say that module dummy1 came
+  from module /lib/modules/2.1.215/net/dummy.o, ksyms just says dummy1.  I
+  have to match ksyms to the relevant object by finding a globally unique
+  external symbol in each module that can be used to map to the external
+  symbols in ksyms.  This assumes that each module exports at least one
+  text symbol that is unique amongst all modules.
+
+  It may not be possible to correctly map other sections such as data and
+  readonly data for modules because they may not have exported symbols.
+  Since the main aim of ksymoops is to map a code Oops, this should not be
+  a problem.
+
+  Unfortunately some modules export no symbols.  They are marked as
+  EXPORT_NO_SYMBOLS are simply do not export anything.  It is
+  impossible to detect these in ksyms because, by definition, ksyms
+  only contains exported symbols for modules.  Since all modules appear
+  in lsmod (/proc/modules), a cross check of lsmod against the module
+  names will find loaded modules with no symbols, at least I can warn
+  about these.
+
+  After merging the various sources, ksymoops has a (hopefully) accurate
+  map including modules.  The -s option lets you save the merged
+  System.map, but remember that module data and readonly data sections may
+  not be correctly relocated, see above.
+
+  Environment Variables.
+  KSYMOOPS_NM		path for nm, defaults to /usr/bin/nm.
+  KSYMOOPS_FIND		path for find, defaults to /usr/bin/find.
+  KSYMOOPS_OBJDUMP	path for objdump, defaults to /usr/bin/objdump.
+
+
+  Input Oops data.
+
+  The ideal input is to feed the syslog straight into this program.  If
+  you cannot do that, you need to know what the program looks for.
+  Especially if you are typing in the Oops by hand :(.  All input is case
+  insensitive.
+
+  * White space in this context means space or tab.  It does not include
+    newline.
+
+  * Oops in syslog has a syslog prefix.  Leading text up to and including
+    ' kernel: ' is always ignored, there is no need to edit syslog first.
+    This leading text need not exist but if it does, it must end in
+    ' kernel: '.
+
+  * An alternative prefix is <n> where n is the kernel print level.  Also
+    ignored if present.
+
+  * Leading white space is treated as a prefix and ignored, the input is
+    not indentation sensitive.
+
+  * In the following paragraphs, assume that any prefixes have been
+    skipped.  If there is more than one prefix, all are skipped, no matter
+    which order they appear in.
+
+  * A bracketed address is optional '[', required '<', at least 4 hex
+    digits, required '>', optional ']'.  For example [<01234567>] or
+    <1234>.
+
+  * The ix86 EIP line is identified by optional white space followed by
+    'EIP:', followed by a least one white space, followed by a bracketed
+    address.
+
+  * The m68k PC line is identified by optional white space followed by
+    'PC', optionally followed by white space, followed by '=', optionally
+    followed by white space, followed by a bracketed address.
+
+  * The sparc PC line starts with PSR and PC is the second hex value, not
+    bracketed.
+
+  * A call trace line is identified by 'Call Trace:' followed by at least
+    one white space.  Or it is a line starting with a bracketed address,
+    but only if the previous line was a call trace line (I hate multi line
+    output that relies on identation for recognition, especially when
+    lines can have a variable prefix).
+
+  * The Code line is identified by 'Code:' followed by a least one white
+    space character followed by at least one hex value.  The line can
+    contain multiple hex values, each separated by at least one white
+    space.  Each hex value must be 2 to 8 digits and must be a multiple of
+    2 digits.
+
+    On some architectures the Code: data is a stream of single bytes,
+    in machine order.  On other architectures, it is a stream of shorts
+    or ints in human readable order which does not always match the
+    machine order, endianess raises its ugly head.  We are consistently
+    inconsistent.
+
+    To cater for these architecture inconsistencies, use the -c option.
+    If the Code: line is already in machine order, use -c 1.  If the
+    Code: data is a stream of shorts or ints which do not match the
+    machine order, use -c 2 or -c 4.  Each set of 'c' bytes are swapped
+    to (hopefully) reflect the machine order.
+
+    Special cases where Code: can be followed by text.
+      'Code: general protection'
+      'Code: <n>'
+    Dump the data anyway, the code was unavailable.
+
+  * Formatted data is only output when the Code: line is seen.  If any
+    data has been stored and more than 5 lines other than Oops text (see
+    Oops_print) or end of file are encountered then ksymoops assumes that
+    the Code: line is missing or garbled and dumps the formatted data
+    anyway.  Fail safe, I hope.
+
+  * By default, ksymoops reads its entire input file.  If the -1 toggle
+    is set, it will run in one shot mode and exit after the first Oops.
+    This is useful for automatically mailing reports as they happen,
+    like this :-
+
+    #!/bin/sh
+    # ksymoops1
+    while (true)
+    do
+    	ksymoops -1 > $HOME/oops1
+	if [ $? -eq 3 ]
+	then
+	   exit 0
+	fi
+	mail -s Oops admin < $HOME/oops1
+    done
+
+    tail -f /var/log/messages | ksymoops1
+
+    Restarting after log rotation is left as an exercise for the reader.
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops/io.c linux/scripts/ksymoops/io.c
--- v2.2.0-pre4/linux/scripts/ksymoops/io.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops/io.c	Tue Jan  5 11:13:56 1999
@@ -0,0 +1,139 @@
+/*
+	io.c.
+
+	Local I/O routines for ksymoops.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	fwrite_local is redundant, replaced by bfd.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+
+ */
+
+#include "ksymoops.h"
+#include <errno.h>
+#include <malloc.h>
+#include <string.h>
+#include <sys/stat.h>
+
+int regular_file(const char *file, const char *msg)
+{
+	struct stat statbuf;
+	if (stat(file, &statbuf)) {
+		fprintf(stderr, "%s: %s stat %s failed",
+			prefix, msg, file);
+		perror(" ");
+		++errors;
+		return 0;
+	}
+
+	if (!S_ISREG(statbuf.st_mode)) {
+		fprintf(stderr,
+			"%s: %s %s is not a regular file, ignored\n",
+			prefix, msg, file);
+		++errors;
+		return 0;
+	}
+	return 1;
+}
+
+FILE *fopen_local(const char *file, const char *mode, const char *msg)
+{
+	FILE *f;
+	if (!(f = fopen(file, mode))) {
+		fprintf(stderr, "%s: %s fopen '%s' failed",
+			prefix, msg, file);
+		perror(" ");
+		++errors;
+	}
+	return f;
+}
+
+void fclose_local(FILE *f, const char *msg)
+{
+	int i;
+	if ((i = fclose(f))) {
+		fprintf(stderr, "%s: %s fclose failed %d", prefix, msg, i);
+		perror(" ");
+		++errors;
+	}
+}
+
+/* Read a line, increasing the size of the line as necessary until \n is read */
+#define INCREMENT 10	/* arbitrary */
+char *fgets_local(char **line, int *size, FILE *f, const char *msg)
+{
+	char *l, *p, *r;
+	int longline = 1;
+
+	if (!*line) {
+		*size = INCREMENT;
+		*line = malloc(*size);
+		if (!*line)
+			malloc_error("fgets_local alloc line");
+	}
+
+	l = *line;
+	while (longline) {
+		r = fgets(l, *size-(l-*line), f);
+		if (!r) {
+			if (ferror(f)) {
+				fprintf(stderr,
+					"%s: %s fgets failed", prefix, msg);
+				perror(" ");
+				++errors;
+			}
+			if (l != *line)
+				return(*line);
+			else
+				return(r);
+		}
+		if (!(p = strchr(*line, '\n'))) {
+			*size += INCREMENT;
+			*line = realloc(*line, *size);
+			if (!*line)
+				malloc_error("fgets_local realloc line");
+			l = *line+*size-INCREMENT-1;
+		}
+		else {
+			*p = '\0';
+			longline = 0;
+		}
+	}
+
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s line '%s'\n", msg, *line);
+	return(*line);
+}
+
+FILE *popen_local(const char *cmd, const char *msg)
+{
+	FILE *f;
+	if (!(f = popen(cmd, "r"))) {
+		fprintf(stderr, "%s: %s popen '%s' failed",
+			prefix, msg, cmd);
+		perror(" ");
+		++errors;
+	}
+	return f;
+}
+
+void pclose_local(FILE *f, const char *msg)
+{
+	int i;
+	errno = 0;
+	if ((i = pclose(f))) {
+		fprintf(stderr, "%s: %s pclose failed 0x%x", prefix, msg, i);
+		if (errno)
+			perror(" ");
+		else
+			fprintf(stderr, "\n");
+		++errors;
+	}
+}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops/ksymoops.c linux/scripts/ksymoops/ksymoops.c
--- v2.2.0-pre4/linux/scripts/ksymoops/ksymoops.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops/ksymoops.c	Tue Jan  5 11:13:56 1999
@@ -0,0 +1,678 @@
+/*
+	ksymoops.c.
+
+	Read a kernel Oops file and make the best stab at converting the code to
+	instructions and mapping stack values to kernel symbols.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+*/
+
+#define VERSION "0.6e"
+
+/*
+
+	Tue Jan  5 19:26:02 EST 1999
+	Version 0.6e
+	Added to kernel.
+
+	Mon Jan  4 09:48:13 EST 1999
+	Version 0.6d
+	Add ARM support.
+
+	Thu Nov 26 16:37:46 EST 1998
+	Version 0.6c
+	Typo in oops_code.
+	Add -c option.
+	Add -1 option.
+	Report if options were specified or defaulted.
+
+	Fri Nov  6 10:38:42 EST 1998
+	Version 0.6b
+	Remove false warnings when comparing ksyms and lsmod.
+
+	Tue Nov  3 23:33:04 EST 1998
+	Version 0.6a
+	Performance inprovements.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Read lsmod (/proc/modules).
+	Ignore addresses 0-4095 when mapping address to symbol.
+	Discard default objects if -o specified.
+	Oops file must be regular.
+	Add "invalid operand" to Oops_print.
+	Move "Using_Version" copy to map.c.
+	Add Makefile defaults for vmlinux, ksyms, objects, System.map, lsmod.
+	Minor adjustment to re for ppc.
+	Minor adjustment to re for objdump lines with <_EIP+xxx>.
+	Convert from a.out to bfd, using same format as ksymoops.
+	Added MIPS.
+	PPC handling based on patches by "Ryan Nielsen" <ran@krazynet.com>
+
+	Wed Oct 28 23:14:55 EST 1998
+	Version 0.5
+	No longer read vmlinux by default, it only duplicates System.map.
+
+	Wed Oct 28 13:47:38 EST 1998
+	Version 0.4
+	Split into separate sources.
+
+	Mon Oct 26 00:01:47 EST 1998
+	Version 0.3c
+	Add alpha (arm) processing.
+
+	Mon Oct 26 00:01:47 EST 1998
+	Version 0.3b
+	Add sparc processing.
+	Handle kernel symbol versions.
+
+	Fri Oct 23 13:11:20 EST 1998
+	Version 0.3
+	Add -follow to find command for people who use symlinks to modules.
+	Add Version_ checking.
+
+	Thu Oct 22 22:28:30 EST 1998
+	Version 0.2.
+	Generalise text prefix handling.
+	Handle messages on Code: line.
+	Format addresses with leading zeroes.
+	Minor bug fixes.
+
+	Wed Oct 21 23:28:48 EST 1998
+	Version 0.1.  Rewrite from scratch in C.
+
+	CREDITS.
+	Oops disassembly based on ksymoops.cc,
+	  Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
+	m68k code based on ksymoops.cc changes by
+	  Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
+ */
+
+#include "ksymoops.h"
+#include <ctype.h>
+#include <errno.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+char *prefix;
+char *path_nm = "/usr/bin/nm";			/* env KSYMOOPS_NM */
+char *path_find = "/usr/bin/find";		/* env KSYMOOPS_FIND */
+char *path_objdump = "/usr/bin/objdump";	/* env KSYMOOPS_OBJDUMP */
+int debug = 0;
+int errors = 0;
+int warnings = 0;
+
+SYMBOL_SET  ss_vmlinux;
+SYMBOL_SET  ss_ksyms_base;
+SYMBOL_SET *ss_ksyms_module;
+int         ss_ksyms_modules;
+SYMBOL_SET  ss_lsmod;
+SYMBOL_SET *ss_object;
+int         ss_objects;
+SYMBOL_SET  ss_system_map;
+
+SYMBOL_SET  ss_merged;   /* merged map with info from all sources */
+SYMBOL_SET  ss_Version;  /* Version_ numbers where available */
+
+/* Regular expression stuff */
+
+regex_t     re_nm;
+regmatch_t *re_nm_pmatch;
+regex_t     re_bracketed_address;
+regmatch_t *re_bracketed_address_pmatch;
+regex_t     re_unbracketed_address;
+regmatch_t *re_unbracketed_address_pmatch;
+
+static void usage(void)
+{
+	fprintf(stderr, "Version " VERSION "\n");
+	fprintf(stderr, "usage: %s\n", prefix);
+	fprintf(stderr,
+		"\t\t[-v vmlinux]\tWhere to read vmlinux\n"
+		"\t\t[-V]\t\tNo vmlinux is available\n"
+		"\t\t[-o object_dir]\tDirectory containing modules\n"
+		"\t\t[-O]\t\tNo modules is available\n"
+		"\t\t[-k ksyms]\tWhere to read ksyms\n"
+		"\t\t[-K]\t\tNo ksyms is available\n"
+		"\t\t[-l lsmod]\tWhere to read lsmod\n"
+		"\t\t[-L]\t\tNo lsmod is available\n"
+		"\t\t[-m system.map]\tWhere to read System.map\n"
+		"\t\t[-M]\t\tNo System.map is available\n"
+		"\t\t[-s save.map]\tSave consolidated map\n"
+		"\t\t[-d]\t\tIncrease debug level by 1\n"
+		"\t\t[-h]\t\tPrint help text\n"
+		"\t\t[-c code_bytes]\tHow many bytes in each unit of code\n"
+		"\t\t[-1]\t\tOne shot toggle (exit after first Oops)\n"
+		"\t\t<Oops.file\tOops report to decode\n"
+		"\n"
+		"\t\tAll flags can occur more than once.  With the exception "
+			"of -o\n"
+		"\t\tand -d which are cumulative, the last occurrence of each "
+			"flag is\n"
+		"\t\tused.  Note that \"-v my.vmlinux -V\" will be taken as "
+			"\"No vmlinux\n"
+		"\t\tavailable\" but \"-V -v my.vmlinux\" will read "
+			"my.vmlinux.  You\n"
+		"\t\twill be warned about such combinations.\n"
+		"\n"
+		"\t\tEach occurrence of -d increases the debug level.\n"
+		"\n"
+		"\t\tEach -o flag can refer to a directory or to a single "
+			"object\n"
+		"\t\tfile.  If a directory is specified then all *.o files in "
+			"that\n"
+		"\t\tdirectory and its subdirectories are assumed to be "
+			"modules.\n"
+		"\n"
+		"\t\tIf any of the vmlinux, object_dir, ksyms or system.map "
+		"options\n"
+		"\t\tcontain the string *r (*m, *n, *s) then it is replaced "
+		"at run\n"
+		"\t\ttime by the current value of `uname -r` (-m, -n, -s).\n"
+		"\n"
+		"\t\tThe defaults can be changed in the Makefile, current "
+		"defaults\n"
+		"\t\tare\n\n"
+		"\t\t\t"
+#ifdef DEF_VMLINUX
+		"-v " DEF_LINUX
+#else
+		"-V"
+#endif
+		"\n"
+		"\t\t\t"
+#ifdef DEF_OBJECTS
+		"-o " DEF_OBJECTS
+#else
+		"-O"
+#endif
+		"\n"
+		"\t\t\t"
+#ifdef DEF_KSYMS
+		"-k " DEF_KSYMS
+#else
+		"-K"
+#endif
+		"\n"
+		"\t\t\t"
+#ifdef DEF_LSMOD
+		"-l " DEF_LSMOD
+#else
+		"-L"
+#endif
+		"\n"
+		"\t\t\t"
+#ifdef DEF_MAP
+		"-m " DEF_MAP
+#else
+		"-M"
+#endif
+		"\n"
+		"\t\t\t-c %d\n"	/* DEF_CODE_BYTES */
+		"\t\t\tOops report is read from stdin\n"
+		"\n",
+	DEF_CODE_BYTES
+	       );
+}
+
+/* Check if possibly conflicting options were specified */
+static void multi_opt(int specl, int specu, char type, const char *using)
+{
+	if (specl && specu) {
+		fprintf(stderr,
+			"Warning - you specified both -%c and -%c.  Using '",
+			type, toupper(type));
+		++warnings;
+		if (using) {
+			fprintf(stderr, "-%c %s", type, using);
+			if (type == 'o')
+				fprintf(stderr, " ...");
+			fprintf(stderr, "'\n");
+		}
+		else
+			fprintf(stderr, "-%c'\n", toupper(type));
+	}
+	else if (specl > 1 && type != 'o') {
+		fprintf(stderr,
+			"Warning - you specified -%c more than once.  "
+			"Using '-%c %s'\n",
+			type, type, using);
+		++warnings;
+	}
+	else if (specu > 1) {
+		fprintf(stderr,
+			"Warning - you specified -%c more than once.  "
+			"Second and subsequent '-%c' ignored\n",
+			toupper(type), toupper(type));
+		++warnings;
+	}
+}
+
+/* If a name contains *r (*m, *n, *s), replace with the current value of
+ * `uname -r` (-m, -n, -s).  Actually uses uname system call rather than the
+ * uname command but the result is the same.
+ */
+static void convert_uname(char **name)
+{
+	char *p, *newname, *oldname, *replacement;
+	unsigned len;
+	int free_oldname = 0;
+	static char procname[] = "convert_uname";
+
+	if (!*name)
+		return;
+
+	while ((p = strchr(*name, '*'))) {
+		struct utsname buf;
+		int i = uname(&buf);
+		if (debug)
+			fprintf(stderr, "DEBUG: %s %s in\n", procname, *name);
+		if (i) {
+			fprintf(stderr,
+				"%s: uname failed, %s will not be processed\n",
+				prefix, *name);
+			perror(prefix);
+			++errors;
+			return;
+		}
+		switch (*(p+1)) {
+		case 'r':
+			replacement = buf.release;
+			break;
+		case 'm':
+			replacement = buf.machine;
+			break;
+		case 'n':
+			replacement = buf.nodename;
+			break;
+		case 's':
+			replacement = buf.sysname;
+			break;
+		default:
+			fprintf(stderr,
+				"%s: invalid replacement character '*%c' "
+				"in %s\n",
+				prefix, *(p+1), *name);
+			++errors;
+			return;
+		}
+		len = strlen(*name)-2+strlen(replacement)+1;
+		if (!(newname = malloc(len)))
+			malloc_error(procname);
+		strncpy(newname, *name, (p-*name));
+		strcpy(newname+(p-*name), replacement);
+		strcpy(newname+(p-*name)+strlen(replacement), p+2);
+		p = newname+(p-*name)+strlen(replacement);	/* no rescan */
+		oldname = *name;
+		*name = newname;
+		if (free_oldname)
+			free(oldname);
+		free_oldname = 1;
+		if (debug)
+			fprintf(stderr, "DEBUG: %s %s out\n", procname, *name);
+	}
+	return;
+}
+
+/* Report if the option was specified or defaulted */
+static void spec_or_default(int spec, int *some_spec) {
+	if (spec) {
+		printf(" (specified)\n");
+		if (some_spec)
+			*some_spec = 1;
+	}
+	else
+		printf(" (default)\n");
+}
+
+/* Parse the options.  Verbose but what's new with getopt? */
+static void parse(int argc,
+		  char **argv,
+		  char **vmlinux,
+		  char ***object,
+		  int *objects,
+		  char **ksyms,
+		  char **lsmod,
+		  char **system_map,
+		  char **save_system_map,
+		  char ***filename,
+		  int *filecount,
+		  int *spec_h,
+		  int *code_bytes,
+		  int *one_shot
+		 )
+{
+	int spec_v = 0, spec_V = 0;
+	int spec_o = 0, spec_O = 0;
+	int spec_k = 0, spec_K = 0;
+	int spec_l = 0, spec_L = 0;
+	int spec_m = 0, spec_M = 0;
+	int spec_s = 0;
+	int spec_c = 0;
+
+	int c, i, some_spec = 0;
+	char *p;
+
+	while ((c = getopt(argc, argv, "v:Vo:Ok:Kl:Lm:Ms:dhc:1")) != EOF) {
+		if (debug && c != 'd')
+			fprintf(stderr, "DEBUG: getopt '%c' '%s'\n", c, optarg);
+		switch(c) {
+		case 'v':
+			*vmlinux = optarg;
+			++spec_v;
+			break;
+		case 'V':
+			*vmlinux = NULL;
+			++spec_V;
+			break;
+		case 'o':
+			if (!spec_o) {
+				/* First -o, discard default value(s) */
+				for (i = 0; i < *objects; ++i)
+					free((*object)[i]);
+				free(*object);
+				*object = NULL;
+				*objects = 0;
+			}
+			*object = realloc(*object,
+				((*objects)+1)*sizeof(**object));
+			if (!*object)
+				malloc_error("object");
+			if (!(p = strdup(optarg)))
+				malloc_error("strdup -o");
+			else {
+				(*object)[(*objects)++] = p;
+				++spec_o;
+			}
+			break;
+		case 'O':
+			++spec_O;
+			for (i = 0; i < *objects; ++i)
+				free((*object)[i]);
+			free(*object);
+			*object = NULL;
+			*objects = 0;
+			break;
+		case 'k':
+			*ksyms = optarg;
+			++spec_k;
+			break;
+		case 'K':
+			*ksyms = NULL;
+			++spec_K;
+			break;
+		case 'l':
+			*lsmod = optarg;
+			++spec_l;
+			break;
+		case 'L':
+			*lsmod = NULL;
+			++spec_L;
+			break;
+		case 'm':
+			*system_map = optarg;
+			++spec_m;
+			break;
+		case 'M':
+			*system_map = NULL;
+			++spec_M;
+			break;
+		case 's':
+			*save_system_map = optarg;
+			++spec_s;
+			break;
+		case 'd':
+			++debug;
+			break;
+		case 'h':
+			usage();
+			++*spec_h;
+			break;
+		case 'c':
+			++spec_c;
+			errno = 0;
+			*code_bytes = strtoul(optarg, &p, 10);
+			/* Oops_code_values assumes that code_bytes is a
+			 * multiple of 2.
+			 */
+			if (!*optarg || *p || errno ||
+				(*code_bytes != 1 &&
+				 *code_bytes != 2 &&
+				 *code_bytes != 4 &&
+				 *code_bytes != 8)) {
+				fprintf(stderr,
+					"%s Invalid value for -c '%s'\n",
+					prefix, optarg);
+				++errors;
+				if (errno)
+					perror(" ");
+				*code_bytes = DEF_CODE_BYTES;
+			}
+			break;
+		case '1':
+			*one_shot = !*one_shot;
+			break;
+		case '?':
+			usage();
+			exit(2);
+		}
+	}
+
+	*filecount = argc - optind;
+	*filename = argv + optind;
+
+	/* Expand any requests for the current uname values */
+	convert_uname(vmlinux);
+	if (*objects) {
+		for (i = 0; i < *objects; ++i)
+			convert_uname(*object+i);
+	}
+	convert_uname(ksyms);
+	convert_uname(lsmod);
+	convert_uname(system_map);
+
+	/* Check for multiple options specified */
+	multi_opt(spec_v, spec_V, 'v', *vmlinux);
+	multi_opt(spec_o, spec_O, 'o', *object ? **object : NULL);
+	multi_opt(spec_k, spec_K, 'k', *ksyms);
+	multi_opt(spec_l, spec_L, 'l', *lsmod);
+	multi_opt(spec_m, spec_M, 'm', *system_map);
+
+	printf("Options used:");
+	if (*vmlinux)
+		printf(" -v %s", *vmlinux);
+	else
+		printf(" -V");
+	spec_or_default(spec_v || spec_V, &some_spec);
+	
+	printf("             ");
+	if (*objects) {
+		for (i = 0; i < *objects; ++i)
+			printf(" -o %s", (*object)[i]);
+	}
+	else
+		printf(" -O");
+	spec_or_default(spec_o || spec_O, &some_spec);
+
+	printf("             ");
+	if (*ksyms)
+		printf(" -k %s", *ksyms);
+	else
+		printf(" -K");
+	spec_or_default(spec_k || spec_K, &some_spec);
+
+	printf("             ");
+	if (*lsmod)
+		printf(" -l %s", *lsmod);
+	else
+		printf(" -L");
+	spec_or_default(spec_l || spec_L, &some_spec);
+
+	printf("             ");
+	if (*system_map)
+		printf(" -m %s", *system_map);
+	else
+		printf(" -M");
+	spec_or_default(spec_m || spec_M, &some_spec);
+
+	printf("             ");
+	printf(" -c %d", *code_bytes);
+	spec_or_default(spec_c, NULL);
+
+	if (*one_shot) {
+		printf("             ");
+		printf(" -1");
+	}
+
+	printf("\n");
+
+	if (!some_spec) {
+		printf(
+"You did not tell me where to find symbol information.  I will assume\n"
+"that the log matches the kernel and modules that are running right now\n"
+"and I'll use the default options above for symbol resolution.\n"
+"If the current kernel and/or modules do not match the log, you can get\n"
+"more accurate output by telling me the kernel version and where to find\n"
+"map, modules, ksyms etc.  ksymoops -h explains the options.\n"
+			"\n");
+		++warnings;
+	}
+}
+
+/* Read environment variables */
+static void read_env(const char *external, char **internal)
+{
+	char *p;
+	if ((p = getenv(external))) {
+		*internal = p;
+		if (debug)
+			fprintf(stderr,
+				"DEBUG: env override %s=%s\n",
+				external, *internal);
+	}
+	else {
+		if (debug)
+			fprintf(stderr,
+				"DEBUG: env default %s=%s\n",
+				external, *internal);
+	}
+}
+
+
+int main(int argc, char **argv)
+{
+	char *vmlinux = NULL;
+	char **object = NULL;
+	int objects = 0;
+	char *ksyms = NULL;
+	char *lsmod = NULL;
+	char *system_map = NULL;
+	char *save_system_map = NULL;
+	char **filename;
+	int filecount = 0;
+	int spec_h = 0;		/* -h was specified */
+	int code_bytes = DEF_CODE_BYTES;
+	int one_shot = 0;
+	int i, ret;
+
+	prefix = *argv;
+	setvbuf(stdout, NULL, _IONBF, 0);
+
+#ifdef DEF_VMLINUX
+	vmlinux = DEF_LINUX;
+#endif
+#ifdef DEF_OBJECTS
+	{
+		char *p;
+		object = realloc(object, (objects+1)*sizeof(*object));
+		if (!object)
+			malloc_error("DEF_OBJECTS");
+		if (!(p = strdup(DEF_OBJECTS)))
+			malloc_error("DEF_OBJECTS");
+		else
+			object[objects++] = p;
+	}
+#endif
+#ifdef DEF_KSYMS
+	ksyms = DEF_KSYMS;
+#endif
+#ifdef DEF_LSMOD
+	lsmod = DEF_LSMOD;
+#endif
+#ifdef DEF_MAP
+	system_map = DEF_MAP;
+#endif
+
+	parse(argc,
+	      argv,
+	      &vmlinux,
+	      &object,
+	      &objects,
+	      &ksyms,
+	      &lsmod,
+	      &system_map,
+	      &save_system_map,
+	      &filename,
+	      &filecount,
+	      &spec_h,
+	      &code_bytes,
+	      &one_shot
+	     );
+
+	if (spec_h && filecount == 0)
+		return(0);	/* just the help text */
+
+	if (errors)
+		return(1);
+
+	if (debug)
+		fprintf(stderr, "DEBUG: level %d\n", debug);
+
+	read_env("KSYMOOPS_NM", &path_nm);
+	read_env("KSYMOOPS_FIND", &path_find);
+	read_env("KSYMOOPS_OBJDUMP", &path_objdump);
+
+	re_compile_common();
+	ss_init_common();
+
+	read_vmlinux(vmlinux);
+	read_ksyms(ksyms);
+	/* No point in reading modules unless ksyms shows modules loaded */
+	if (ss_ksyms_modules) {
+		expand_objects(object, objects);
+		for (i = 0; i < ss_objects; ++i)
+			read_object(ss_object[i].source, i);
+	}
+	else if (objects)
+		printf("No modules in ksyms, skipping objects\n");
+	/* No point in reading lsmod without ksyms */
+	if (ss_ksyms_modules || ss_ksyms_base.used)
+		read_lsmod(lsmod);
+	else if (lsmod)
+		printf("No ksyms, skipping lsmod\n");
+	read_system_map(system_map);
+	merge_maps(save_system_map);
+
+	/* After all that work, it is finally time to read the Oops report */
+	ret = Oops_read(filecount, filename, code_bytes, one_shot);
+
+	if (warnings || errors) {
+		printf("\n");
+		if (warnings)
+			printf("%d warning%s ",
+			       warnings, warnings == 1 ? "" : "s");
+		if (warnings && errors)
+			printf("and ");
+		if (errors)
+			printf("%d error%s ", errors, errors == 1 ? "" : "s");
+		printf("issued.  Results may not be reliable.\n");
+		if (!ret)
+			return(1);
+	}
+
+	return(ret);
+}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops/ksymoops.h linux/scripts/ksymoops/ksymoops.h
--- v2.2.0-pre4/linux/scripts/ksymoops/ksymoops.h	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops/ksymoops.h	Tue Jan  5 11:13:56 1999
@@ -0,0 +1,146 @@
+/*
+	ksymoops.h.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Read lsmod (/proc/modules).
+	Convert from a.out to bfd, using same format as ksymoops.
+	PPC trace addresses are not bracketed, add new re.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+*/
+
+#include <sys/types.h>
+#include <regex.h>
+#include <stdio.h>
+
+
+/* Pity this is not externalised, see binfmt_elf.c */
+#define elf_addr_t unsigned long
+
+extern char *prefix;
+extern char *path_nm;		/* env KSYMOOPS_NM */
+extern char *path_find;		/* env KSYMOOPS_FIND */
+extern char *path_objdump;	/* env KSYMOOPS_OBJDUMP */
+extern int debug;
+extern int errors;
+extern int warnings;
+
+typedef struct symbol SYMBOL;
+
+struct symbol {
+	char *name;		/* name of symbol */
+	char type;		/* type of symbol from nm/System.map */
+	char keep;		/* keep this symbol in merged map? */
+	elf_addr_t address;	/* address in kernel */
+};
+
+/* Header for symbols from one particular source */
+
+typedef struct symbol_set SYMBOL_SET;
+
+struct symbol_set {
+	char *source;			/* where the symbols came from */
+	int used;			/* number of symbols used */
+	int alloc;			/* number of symbols allocated */
+	SYMBOL *symbol;			/* dynamic array of symbols */
+	SYMBOL_SET *related;		/* any related symbol set */
+};
+
+extern SYMBOL_SET  ss_vmlinux;
+extern SYMBOL_SET  ss_ksyms_base;
+extern SYMBOL_SET *ss_ksyms_module;
+extern int         ss_ksyms_modules;
+extern SYMBOL_SET  ss_lsmod;
+extern SYMBOL_SET *ss_object;
+extern int         ss_objects;
+extern SYMBOL_SET  ss_system_map;
+
+extern SYMBOL_SET  ss_merged;	/* merged map with info from all sources */
+extern SYMBOL_SET  ss_Version;	/* Version_ numbers where available */
+
+/* Regular expression stuff */
+
+extern regex_t     re_nm;
+extern regmatch_t *re_nm_pmatch;
+extern regex_t     re_bracketed_address;
+extern regmatch_t *re_bracketed_address_pmatch;
+extern regex_t     re_unbracketed_address;
+extern regmatch_t *re_unbracketed_address_pmatch;
+
+/* Bracketed address: optional '[', required '<', at least 4 hex characters,
+ * required '>', optional ']', optional white space.
+ */
+#define BRACKETED_ADDRESS	"\\[*<([0-9a-fA-F]{4,})>\\]*[ \t]*"
+
+#define UNBRACKETED_ADDRESS	"([0-9a-fA-F]{4,})[ \t]*"
+
+/* io.c */
+extern int regular_file(const char *file, const char *msg);
+extern FILE *fopen_local(const char *file, const char *mode, const char *msg);
+extern void fclose_local(FILE *f, const char *msg);
+extern char *fgets_local(char **line, int *size, FILE *f, const char *msg);
+extern int fwrite_local(void const *ptr, size_t size, size_t nmemb,
+			FILE *stream, const char *msg);
+extern FILE *popen_local(const char *cmd, const char *msg);
+extern void pclose_local(FILE *f, const char *msg);
+
+/* ksyms.c */
+extern void read_ksyms(const char *ksyms);
+extern void map_ksyms_to_modules(void);
+extern void read_lsmod(const char *lsmod);
+extern void compare_ksyms_lsmod(void);
+
+/* misc.c */
+extern void malloc_error(const char *msg);
+extern const char *format_address(elf_addr_t address);
+extern char *find_fullpath(const char *program);
+
+/* map.c */
+extern void read_system_map(const char *system_map);
+extern void merge_maps(const char *save_system_map);
+extern void compare_maps(const SYMBOL_SET *ss1, const SYMBOL_SET *ss2,
+			 int precedence);
+
+
+/* object.c */
+extern SYMBOL_SET *adjust_object_offsets(SYMBOL_SET *ss);
+extern void read_vmlinux(const char *vmlinux);
+extern void expand_objects(char * const *object, int objects);
+extern void read_object(const char *object, int i);
+
+/* oops.c */
+extern int Oops_read(int filecount, char * const *filename, int code_bytes,
+		     int one_shot);
+
+/* re.c */
+extern void re_compile(regex_t *preg, const char *regex, int cflags,
+		       regmatch_t **pmatch);
+extern void re_compile_common(void);
+extern void re_strings(regex_t *preg, const char *text, regmatch_t *pmatch,
+		       char ***string);
+extern void re_strings_free(const regex_t *preg, char ***string);
+extern void re_string_check(int need, int available, const char *msg);
+
+/* symbol.c */
+extern void ss_init(SYMBOL_SET *ss, const char *msg);
+extern void ss_free(SYMBOL_SET *ss);
+extern void ss_init_common(void);
+extern SYMBOL *find_symbol_name(const SYMBOL_SET *ss, const char *symbol,
+				int *start);
+extern void add_symbol_n(SYMBOL_SET *ss, const elf_addr_t address,
+			 const char type, const char keep, const char *symbol);
+extern void add_symbol(SYMBOL_SET *ss, const char *address, const char type,
+		       const char keep, const char *symbol);
+extern char *map_address(const SYMBOL_SET *ss, const elf_addr_t address);
+extern void ss_sort_atn(SYMBOL_SET *ss);
+extern void ss_sort_na(SYMBOL_SET *ss);
+extern SYMBOL_SET *ss_copy(const SYMBOL_SET *ss);
+extern void add_Version(const char *version, const char *source);
+extern void extract_Version(SYMBOL_SET *ss);
+extern void compare_Version(void);
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops/ksyms.c linux/scripts/ksymoops/ksyms.c
--- v2.2.0-pre4/linux/scripts/ksymoops/ksyms.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops/ksyms.c	Tue Jan  5 11:13:56 1999
@@ -0,0 +1,294 @@
+/*
+	ksyms.c.
+
+	Process ksyms for ksymoops.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Fri Nov  6 10:38:42 EST 1998
+	Version 0.6b
+	Remove false warnings when comparing ksyms and lsmod.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Read lsmod (/proc/modules).
+	Move "Using_Version" copy to map.c.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <malloc.h>
+#include <string.h>
+
+/* Scan one line from ksyms.  Split lines into the base symbols and the module
+ * symbols.  Separate ss for base and each module.
+ */
+static void scan_ksyms_line(const char *line)
+{
+	int i;
+	char **string = NULL;
+	SYMBOL_SET *ssp;
+	static char *prev_module = NULL;
+	static regex_t     re_ksyms;
+	static regmatch_t *re_ksyms_pmatch;
+	static char const procname[] = "scan_ksyms_line";
+
+	/* ksyms: address, symbol, optional module */
+	re_compile(&re_ksyms,
+		"^([0-9a-fA-F]{4,}) +([^ \t]+)([ \t]+\\[([^ ]+)\\])?$",
+		REG_NEWLINE|REG_EXTENDED,
+		&re_ksyms_pmatch);
+
+	i = regexec(&re_ksyms, line,
+		    re_ksyms.re_nsub+1, re_ksyms_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i)
+		return;
+
+	/* string [1] - address, [2] - symbol, [3] - white space+module,
+	 * [4] - module.
+	 */
+	re_strings(&re_ksyms, line, re_ksyms_pmatch, &string);
+	if (string[4]) {
+		if (!prev_module || strcmp(prev_module, string[4])) {
+			/* start of a new module in ksyms */
+			++ss_ksyms_modules;
+			ss_ksyms_module = realloc(ss_ksyms_module,
+				ss_ksyms_modules*sizeof(*ss_ksyms_module));
+			if (!ss_ksyms_module)
+				malloc_error("realloc ss_ksyms_module");
+			ssp = ss_ksyms_module+ss_ksyms_modules-1;
+			ss_init(ssp, string[4]);
+			prev_module = strdup(string[4]);
+			if (!prev_module)
+				malloc_error("strdup prev_module");
+		}
+		ssp = ss_ksyms_module+ss_ksyms_modules-1;
+	}
+	else
+		ssp = &ss_ksyms_base;
+	add_symbol(ssp, string[1], ' ', 1, string[2]);
+	re_strings_free(&re_ksyms, &string);
+}
+
+/* Read the symbols from ksyms.  */
+void read_ksyms(const char *ksyms)
+{
+	FILE *f;
+	char *line = NULL;
+	int i, size;
+	static char const procname[] = "read_ksyms";
+
+	if (!ksyms)
+		return;
+	ss_init(&ss_ksyms_base, "ksyms_base");
+	if (debug)
+		fprintf(stderr, "DEBUG: %s %s\n", procname, ksyms);
+
+	if (!regular_file(ksyms, procname))
+		return;
+
+	if (!(f = fopen_local(ksyms, "r", procname)))
+		return;
+
+	while (fgets_local(&line, &size, f, procname))
+		scan_ksyms_line(line);
+
+	fclose_local(f, procname);
+	free(line);
+
+	for (i = 0; i < ss_ksyms_modules; ++i) {
+		ss_sort_na(ss_ksyms_module+i);
+		extract_Version(ss_ksyms_module+i);
+	}
+	if (ss_ksyms_base.used) {
+		ss_sort_na(&ss_ksyms_base);
+		extract_Version(&ss_ksyms_base);
+	}
+	else {
+		fprintf(stderr,
+			"Warning, no kernel symbols in ksyms, is %s a valid "
+			"ksyms file?\n",
+			ksyms);
+		++warnings;
+	}
+
+	if (debug > 1) {
+		for (i = 0; i < ss_ksyms_modules; ++i) {
+			fprintf(stderr,
+				"DEBUG: %s %s used %d out of %d entries\n",
+				procname,
+				ss_ksyms_module[i].source,
+				ss_ksyms_module[i].used,
+				ss_ksyms_module[i].alloc);
+		}
+		fprintf(stderr,
+			"DEBUG: %s %s used %d out of %d entries\n",
+			procname, ss_ksyms_base.source, ss_ksyms_base.used,
+			ss_ksyms_base.alloc);
+	}
+}
+
+/* Map each ksyms module entry to the corresponding object entry.  Tricky,
+ * see the comments in the docs about needing a unique symbol in each
+ * module.
+ */
+static void map_ksym_to_module(SYMBOL_SET *ss)
+{
+	int i, j, matches;
+	char *name = NULL;
+
+	for (i = 0; i < ss->used; ++i) {
+		matches = 0;
+		for (j = 0; j < ss_objects; ++j) {
+			name = (ss->symbol)[i].name;
+			if (find_symbol_name(ss_object+j, name, NULL)) {
+				++matches;
+				ss->related = ss_object+j;
+			}
+		}
+		if (matches == 1)
+			break;		/* unique symbol over all objects */
+		ss->related = NULL;	/* keep looking */
+	}
+	if (!(ss->related)) {
+		fprintf(stderr,
+			"Warning: cannot match loaded module %s to any "
+			"module object.  Trace may not be reliable.\n",
+			ss->source);
+		++warnings;
+	}
+	else if (debug)
+		fprintf(stderr,
+			"DEBUG: ksyms %s matches to %s based on unique "
+			"symbol %s\n",
+			ss->source, ss->related->source, name);
+}
+
+/* Map all ksyms module entries to their corresponding objects */
+void map_ksyms_to_modules(void)
+{
+	int i;
+	SYMBOL_SET *ss, *ssc;
+
+	for (i = 0; i < ss_ksyms_modules; ++i) {
+		ss = ss_ksyms_module+i;
+		map_ksym_to_module(ss);
+		if (ss->related) {
+			ssc = adjust_object_offsets(ss);
+			compare_maps(ss, ssc, 1);
+		}
+	}
+}
+
+/* Read the modules from lsmod.  */
+void read_lsmod(const char *lsmod)
+{
+	FILE *f;
+	char *line = NULL;
+	int i, size;
+	char **string = NULL;
+	static regex_t     re_lsmod;
+	static regmatch_t *re_lsmod_pmatch;
+	static char const procname[] = "read_lsmod";
+
+	if (!lsmod)
+		return;
+	ss_init(&ss_lsmod, "lsmod");
+	if (debug)
+		fprintf(stderr, "DEBUG: %s %s\n", procname, lsmod);
+
+	if (!regular_file(lsmod, procname))
+		return;
+
+	if (!(f = fopen_local(lsmod, "r", procname)))
+		return;
+
+	/* lsmod: module, size, use count, optional used by */
+	re_compile(&re_lsmod,
+		"^"
+		"[ \t]*([^ \t]+)"				/* 1 module */
+		"[ \t]*([^ \t]+)"				/* 2 size */
+		"[ \t]*([^ \t]+)"				/* 3 count */
+		"[ \t]*(.*)"					/* 4 used by */
+		"$",
+		REG_NEWLINE|REG_EXTENDED,
+		&re_lsmod_pmatch);
+
+	while (fgets_local(&line, &size, f, procname)) {
+		i = regexec(&re_lsmod, line,
+			    re_lsmod.re_nsub+1, re_lsmod_pmatch, 0);
+		if (debug > 3)
+			fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+		if (i)
+			continue;
+		re_strings(&re_lsmod, line, re_lsmod_pmatch, &string);
+		add_symbol(&ss_lsmod, string[2], ' ', 1, string[1]);
+	}
+
+	fclose_local(f, procname);
+	free(line);
+	re_strings_free(&re_lsmod, &string);
+	if (ss_lsmod.used)
+		ss_sort_na(&ss_lsmod);
+	else {
+		fprintf(stderr,
+			"Warning, no symbols in lsmod, is %s a valid "
+			"lsmod file?\n",
+			lsmod);
+		++warnings;
+	}
+
+	if (debug > 1)
+		fprintf(stderr,
+			"DEBUG: %s %s used %d out of %d entries\n",
+			procname, ss_lsmod.source, ss_lsmod.used,
+			ss_lsmod.alloc);
+}
+
+/* Compare modules from ksyms against module list in lsmod and vice versa.
+ * There is one ss_ for each ksyms module and a single ss_lsmod to cross
+ * check.
+ */
+void compare_ksyms_lsmod(void)
+{
+	int i, j;
+	SYMBOL_SET *ss;
+	SYMBOL *s;
+	static char const procname[] = "compare_ksyms_lsmod";
+
+	if (!(ss_lsmod.used && ss_ksyms_modules))
+		return;
+
+	s = ss_lsmod.symbol;
+	for (i = 0; i < ss_lsmod.used; ++i, ++s) {
+		for (j = 0; j < ss_ksyms_modules; ++j) {
+			ss = ss_ksyms_module+j;
+			if (strcmp(s->name, ss->source) == 0)
+				break;
+		}
+		if (j >= ss_ksyms_modules) {
+			fprintf(stderr,
+				"Warning in %s, module %s is in lsmod but not "
+				"in ksyms, probably no symbols exported\n",
+				procname, s->name);
+			++warnings;
+		}
+	}
+
+	for (i = 0; i < ss_ksyms_modules; ++i) {
+		ss = ss_ksyms_module+i;
+		if (!find_symbol_name(&ss_lsmod, ss->source, NULL)) {
+			fprintf(stderr,
+				"Error in %s, module %s is in ksyms but not "
+				"in lsmod\n",
+				procname, ss->source);
+			++errors;
+		}
+	}
+}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops/map.c linux/scripts/ksymoops/map.c
--- v2.2.0-pre4/linux/scripts/ksymoops/map.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops/map.c	Tue Jan  5 11:13:56 1999
@@ -0,0 +1,251 @@
+/*
+	map.c.
+
+	Read System.map for ksymoops, create merged System.map.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Remove addresses 0-4095 from merged map after writing new map.
+	Move "Using_Version" copy to map.c.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <malloc.h>
+
+/* Read the symbols from System.map */
+void read_system_map(const char *system_map)
+{
+	FILE *f;
+	char *line = NULL, **string = NULL;
+	int i, size = 0;
+	static char const procname[] = "read_system_map";
+
+	if (!system_map)
+		return;
+	ss_init(&ss_system_map, "System.map");
+	if (debug)
+		fprintf(stderr, "DEBUG: %s %s\n", procname, system_map);
+
+	if (!regular_file(system_map, procname))
+		return;
+
+	if (!(f = fopen_local(system_map, "r", procname)))
+		return;
+
+	while (fgets_local(&line, &size, f, procname)) {
+		i = regexec(&re_nm, line, re_nm.re_nsub+1, re_nm_pmatch, 0);
+		if (debug > 3)
+			fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+		if (i == 0) {
+			re_strings(&re_nm, line, re_nm_pmatch, &string);
+			add_symbol(&ss_system_map, string[1], *string[2],
+				   1, string[3]);
+		}
+	}
+
+	fclose_local(f, procname);
+	re_strings_free(&re_nm, &string);
+	free(line);
+	if (ss_system_map.used) {
+		ss_sort_na(&ss_system_map);
+		extract_Version(&ss_system_map);
+	}
+	else {
+		fprintf(stderr,
+			"Warning, no kernel symbols in System.map, is %s a "
+			"valid System.map file?\n",
+			system_map);
+		++warnings;
+	}
+
+	if (debug > 1)
+		fprintf(stderr,
+			"DEBUG: %s %s used %d out of %d entries\n",
+			procname,
+			ss_system_map.source,
+			ss_system_map.used,
+			ss_system_map.alloc);
+}
+
+/* Compare two maps, all symbols in the first should appear in the second. */
+void compare_maps(const SYMBOL_SET *ss1, const SYMBOL_SET *ss2,
+			 int precedence)
+{
+	int i, start = 0;
+	SYMBOL *s1, *s2, **sdrop = precedence == 1 ? &s2 : &s1;
+	const SYMBOL_SET **ssdrop = precedence == 1 ? &ss2 : &ss1;
+
+	if (!(ss1->used && ss2->used))
+		return;
+
+	if (debug > 1)
+		fprintf(stderr,
+			"DEBUG: compare_maps %s vs %s, %s takes precedence\n",
+			ss1->source, ss2->source,
+			precedence == 1 ? ss1->source : ss2->source);
+
+	for (i = 0; i < ss1->used; ++i) {
+		s1 = ss1->symbol+i;
+		if (!(s1->keep))
+			continue;
+		s2 = find_symbol_name(ss2, s1->name, &start);
+		if (!s2) {
+			/* Some types only appear in nm output, not in things
+			 * like System.map.  Silently ignore them.
+			 */
+			if (s1->type == 'a' || s1->type == 't')
+				continue;
+			fprintf(stderr,
+				"Warning: %s symbol %s not found in %s.  "
+				"Ignoring %s entry\n",
+				ss1->source, s1->name,
+				ss2->source, (*ssdrop)->source);
+			++warnings;
+			if (*sdrop)
+				(*sdrop)->keep = 0;
+		}
+		else if (s1->address != s2->address) {
+			/* Type C symbols cannot be resolved from nm to ksyms,
+			 * silently ignore them.
+			 */
+			if (s1->type == 'C' || s2->type == 'C')
+				continue;
+			fprintf(stderr,
+				"Warning: mismatch on symbol %s %c, "
+				"%s says %lx, %s says %lx.  "
+				"Ignoring %s entry\n",
+				s1->name, s1->type, ss1->source, s1->address,
+				ss2->source, s2->address, (*ssdrop)->source);
+			++warnings;
+			if (*sdrop)
+				(*sdrop)->keep = 0;
+		}
+		else
+			++start;	/* step to next entry in ss2 */
+	}
+}
+
+/* Append the second symbol set onto the first */
+static void append_map(SYMBOL_SET *ss1, const SYMBOL_SET *ss2)
+{
+	int i;
+	SYMBOL *s;
+
+	if (!ss2 || !ss2->used)
+		return;
+	if (debug > 1)
+		fprintf(stderr, "DEBUG: append_map %s to %s\n",
+			ss2->source, ss1->source);
+
+	for (i = 0; i < ss2->used; ++i) {
+		s = ss2->symbol+i;
+		if (s->keep)
+			add_symbol_n(ss1, s->address, s->type, 1,
+				s->name);
+	}
+}
+
+/* Compare the various sources and build a merged system map */
+void merge_maps(const char *save_system_map)
+{
+	int i;
+	SYMBOL *s;
+	FILE *f;
+	static char const procname[] = "merge_maps";
+
+	if (debug)
+		fprintf(stderr, "DEBUG: %s\n", procname);
+
+	/* Using_Versions only appears in ksyms, copy to other tables */
+	if ((s = find_symbol_name(&ss_ksyms_base,
+			"Using_Versions", 0))) {
+		if (ss_system_map.used) {
+			add_symbol_n(&ss_system_map, s->address,
+				s->type, s->keep, s->name);
+			ss_sort_na(&ss_system_map);
+		}
+		if (ss_vmlinux.used) {
+			add_symbol_n(&ss_vmlinux, s->address, s->type,
+				s->keep, s->name);
+			ss_sort_na(&ss_vmlinux);
+		}
+	}
+
+	compare_Version();	/* highlight any version problems first */
+	compare_ksyms_lsmod();	/* highlight any missing modules next */
+	compare_maps(&ss_ksyms_base, &ss_vmlinux, 2);
+	compare_maps(&ss_system_map, &ss_vmlinux, 2);
+	compare_maps(&ss_vmlinux, &ss_system_map, 1);
+	compare_maps(&ss_ksyms_base, &ss_system_map, 2);
+
+	if (ss_objects) {
+		map_ksyms_to_modules();
+	}
+
+	ss_init(&ss_merged, "merged");
+	append_map(&ss_merged, &ss_vmlinux);
+	append_map(&ss_merged, &ss_ksyms_base);
+	append_map(&ss_merged, &ss_system_map);
+	for (i = 0; i < ss_ksyms_modules; ++i)
+		append_map(&ss_merged, (ss_ksyms_module+i)->related);
+	if (!ss_merged.used) {
+		fprintf(stderr, "Warning, no symbols in merged map\n");
+		++warnings;
+	}
+
+	/* drop duplicates, type a (registers) and gcc2_compiled. */
+	ss_sort_atn(&ss_merged);
+	s = ss_merged.symbol;
+	for (i = 0; i < ss_merged.used-1; ++i) {
+		if (s->type == 'a' ||
+		    (s->type == 't' && !strcmp(s->name, "gcc2_compiled.")))
+			s->keep = 0;
+		else if (strcmp(s->name, (s+1)->name) == 0 &&
+		    s->address == (s+1)->address) {
+			if (s->type != ' ')
+				(s+1)->keep = 0;
+			else
+				s->keep = 0;
+		}
+		++s;
+	}
+	ss_sort_atn(&ss_merged);	/* will remove dropped variables */
+
+	if (save_system_map) {
+		if (debug)
+			fprintf(stderr, "DEBUG: writing merged map to %s\n",
+				save_system_map);
+		if (!(f = fopen_local(save_system_map, "w", procname)))
+			return;
+		s = ss_merged.symbol;
+		for (i = 0; i < ss_merged.used; ++i) {
+			if (s->keep)
+				fprintf(f, "%s %c %s\n",
+					format_address(s->address),
+					s->type, s->name);
+			++s;
+		}
+	}
+
+	/* The merged map may contain symbols with an address of 0, e.g.
+	 * Using_Versions.  These give incorrect results for low addresses in
+	 * map_address, such addresses map to "Using_Versions+xxx".  Remove
+	 * any addresses below (arbitrary) 4096 from the merged map.  AFAIK,
+	 * Linux does not use the first page on any arch.
+	 */
+	for (i = 0; i < ss_merged.used; ++i) {
+		if ((ss_merged.symbol+i)->address < 4096)
+			(ss_merged.symbol+i)->keep = 0;
+		else
+			break;
+	}
+	if (i)
+		ss_sort_atn(&ss_merged);	/* remove dropped variables */
+}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops/misc.c linux/scripts/ksymoops/misc.c
--- v2.2.0-pre4/linux/scripts/ksymoops/misc.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops/misc.c	Tue Jan  5 11:13:56 1999
@@ -0,0 +1,108 @@
+/*
+	misc.c.
+
+	Miscellaneous routines for ksymoops.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Convert from a.out to bfd, using same format as ksymoops.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void malloc_error(const char *msg)
+{
+	fprintf(stderr, "%s: fatal malloc error for %s\n", prefix, msg);
+	exit(2);
+}
+
+/* Format an address with the correct number of leading zeroes */
+const char *format_address(elf_addr_t address)
+{
+	/* Well oversized */
+	static char format[10], text[200];
+	if (!*format)
+		snprintf(format, sizeof(format), "%%0%dlx",
+			2*sizeof(address));
+	snprintf(text, sizeof(text), format, address);
+	return(text);
+}
+
+/* Find the full pathname of a program.  Code heavily based on
+ * glibc-2.0.5/posix/execvp.c.
+ */
+char *find_fullpath(const char *program)
+{
+	char *fullpath = NULL;
+	char *path, *p;
+	size_t len;
+	static const char procname[] = "find_fullpath";
+
+	/* Don't search when it contains a slash.  */
+	if (strchr(program, '/')) {
+		if (!(fullpath = strdup(program)))
+			malloc_error(procname);
+		if (debug > 1)
+			fprintf(stderr, "DEBUG: %s %s\n", procname, fullpath);
+		return(fullpath);
+	}
+
+	path = getenv ("PATH");
+	if (!path) {
+		/* There is no `PATH' in the environment.  The default search
+		   path is the current directory followed by the path `confstr'
+		   returns for `_CS_PATH'.
+		 */
+		len = confstr(_CS_PATH, (char *) NULL, 0);
+		if (!(path = malloc(1 + len)))
+			malloc_error(procname);
+		path[0] = ':';
+		confstr(_CS_PATH, path+1, len);
+	}
+
+	len = strlen(program) + 1;
+	if (!(fullpath = malloc(strlen(path) + len)))
+		malloc_error(procname);
+	p = path;
+	do {
+		path = p;
+		p = strchr(path, ':');
+		if (p == NULL)
+			p = strchr(path, '\0');
+
+		/* Two adjacent colons, or a colon at the beginning or the end
+		 * of `PATH' means to search the current directory.
+		 */
+		if (p == path)
+			memcpy(fullpath, program, len);
+		else {
+			/* Construct the pathname to try.  */
+			memcpy(fullpath, path, p - path);
+			fullpath[p - path] = '/';
+			memcpy(&fullpath[(p - path) + 1], program, len);
+		}
+
+		/* If we have execute access, assume this is the program. */
+		if (access(fullpath, X_OK) == 0) {
+			if (debug > 1)
+				fprintf(stderr, "DEBUG: %s %s\n",
+					procname, fullpath);
+			return(fullpath);
+		}
+	} while (*p++ != '\0');
+
+	fprintf(stderr, "Error: %s %s could not find executable %s\n",
+		prefix, procname, program);
+	++errors;
+	return(NULL);
+}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops/object.c linux/scripts/ksymoops/object.c
--- v2.2.0-pre4/linux/scripts/ksymoops/object.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops/object.c	Tue Jan  5 11:13:56 1999
@@ -0,0 +1,230 @@
+/*
+	object.c.
+
+	object handling routines for ksymoops.  Read modules, vmlinux, etc. 
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <malloc.h>
+#include <string.h>
+#include <sys/stat.h>
+
+/* Extract all symbols definitions from an object using nm */
+static void read_nm_symbols(SYMBOL_SET *ss, const char *file)
+{
+	FILE *f;
+	char *cmd, *line = NULL, **string = NULL;
+	int i, size = 0;
+	static char const procname[] = "read_nm_symbols";
+
+	if (!regular_file(file, procname))
+		return;
+
+	cmd = malloc(strlen(path_nm)+strlen(file)+2);
+	if (!cmd)
+		malloc_error("nm command");
+	strcpy(cmd, path_nm);
+	strcat(cmd, " ");
+	strcat(cmd, file);
+	if (debug > 1)
+		fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
+	if (!(f = popen_local(cmd, procname)))
+		return;
+	free(cmd);
+
+	while (fgets_local(&line, &size, f, procname)) {
+		i = regexec(&re_nm, line, re_nm.re_nsub+1, re_nm_pmatch, 0);
+		if (debug > 3)
+			fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+		if (i == 0) {
+			re_strings(&re_nm, line, re_nm_pmatch, &string);
+			add_symbol(ss, string[1], *string[2], 1, string[3]);
+		}
+	}
+
+	pclose_local(f, procname);
+	re_strings_free(&re_nm, &string);
+	free(line);
+	if (debug > 1)
+		fprintf(stderr,
+			"DEBUG: %s %s used %d out of %d entries\n",
+			procname, ss->source, ss->used, ss->alloc);
+}
+
+/* Read the symbols from vmlinux */
+void read_vmlinux(const char *vmlinux)
+{
+	if (!vmlinux)
+		return;
+	ss_init(&ss_vmlinux, "vmlinux");
+	read_nm_symbols(&ss_vmlinux, vmlinux);
+	if (ss_vmlinux.used) {
+		ss_sort_na(&ss_vmlinux);
+		extract_Version(&ss_vmlinux);
+	}
+	else {
+		fprintf(stderr,
+			"Warning, no kernel symbols in vmlinux, is %s a valid "
+			"vmlinux file?\n",
+			vmlinux);
+		++warnings;
+	}
+}
+
+
+/* Read the symbols from one object (module) */
+void read_object(const char *object, int i)
+{
+	ss_init(ss_object+i, object);
+	read_nm_symbols(ss_object+i, object);
+	if ((ss_object+i)->used) {
+		ss_sort_na(ss_object+i);
+		extract_Version(ss_object+i);
+	}
+	else {
+		fprintf(stderr, "Warning, no symbols in %s\n", object);
+		++warnings;
+	}
+}
+
+/* Add a new entry to the list of objects */
+static void add_ss_object(const char *file)
+{
+	++ss_objects;
+	ss_object = realloc(ss_object, ss_objects*sizeof(*ss_object));
+	if (!ss_object)
+		malloc_error("realloc ss_object");
+	ss_init(ss_object+ss_objects-1, file);
+}
+
+/* Run a directory and its subdirectories, looking for *.o files */
+static void find_objects(const char *dir)
+{
+	FILE *f;
+	char *cmd, *line = NULL;
+	int size = 0, files = 0;
+	static char const procname[] = "find_objects";
+	static char const options[] = " -follow -name '*.o' -print";
+
+	cmd = malloc(strlen(path_find)+1+strlen(dir)+strlen(options)+1);
+	if (!cmd)
+		malloc_error("find command");
+	strcpy(cmd, path_find);
+	strcat(cmd, " ");
+	strcat(cmd, dir);
+	strcat(cmd, options);
+	if (debug > 1)
+		fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
+	if (!(f = popen_local(cmd, procname)))
+		return;
+	free(cmd);
+
+	while (fgets_local(&line, &size, f, procname)) {
+		if (debug > 1)
+			fprintf(stderr, "DEBUG: %s - %s\n", procname, line);
+		add_ss_object(line);
+		++files;
+	}
+
+	pclose_local(f, procname);
+	if (!files) {
+		fprintf(stderr,
+			"Warning: no *.o files in %s.  "
+			"Is %s a valid module directory?\n",
+			dir, dir);
+		++warnings;
+	}
+}
+
+/* Take the user supplied list of objects which can include directories.
+ * Expand directories into any *.o files.  The results are stored in
+ * ss_object, leaving the user supplied options untouched.
+ */
+void expand_objects(char * const *object, int objects)
+{
+	struct stat statbuf;
+	int i;
+	const char *file;
+	static char const procname[] = "expand_objects";
+
+	for (i = 0; i < objects; ++i) {
+		file = object[i];
+		if (debug > 1)
+			fprintf(stderr, "DEBUG: %s checking '%s' - ",
+				procname, file);
+		if (!stat(file, &statbuf) && S_ISDIR(statbuf.st_mode)) {
+			if (debug > 1)
+				fprintf(stderr, "directory, expanding\n");
+			find_objects(file);
+		}
+		else {
+			if (debug > 1)
+				fprintf(stderr, "not directory\n");
+			add_ss_object(file);
+		}
+	}
+}
+
+/* Map a symbol type to a section code. 0 - text, 1 - data, 2 - read only data,
+ * 3 - C (cannot relocate), 4 - the rest.
+ */
+static int section(char type)
+{
+	switch (type) {
+	case 'T':
+	case 't':
+		return 0;
+	case 'D':
+	case 'd':
+		return 1;
+	case 'R':
+	case 'r':
+		return 2;
+	case 'C':
+		return 3;
+	default:
+		return 4;
+	}
+}
+
+/* Given ksyms module data which has a related object, create a copy of the
+ * object data, adjusting the offsets to match where the module was loaded.
+ */
+SYMBOL_SET *adjust_object_offsets(SYMBOL_SET *ss)
+{
+	int i;
+	elf_addr_t adjust[] = {0, 0, 0, 0, 0};
+	SYMBOL *sk, *so;
+	SYMBOL_SET *ssc;
+
+	if (debug > 1)
+		fprintf(stderr,
+			"DEBUG: adjust_object_offsets %s\n", ss->source);
+
+	ssc = ss_copy(ss->related);
+
+	/* For common symbols, calculate the adjustment */
+	for (i = 0; i < ss->used; ++i) {
+		sk = ss->symbol+i;
+		if ((so = find_symbol_name(ssc, sk->name, NULL)))
+			adjust[section(so->type)] = sk->address - so->address;
+	}
+	for (i = 0; i < ssc->used; ++i) {
+		so = ssc->symbol+i;
+		/* Type C does not relocate well, silently ignore */
+		if (so->type != 'C' && adjust[section(so->type)])
+			so->address += adjust[section(so->type)];
+		else
+			so->keep = 0;  /* do not merge into final map */
+	}
+
+	ss->related = ssc;	/* map using adjusted copy */
+	return(ssc);
+}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops/oops.c linux/scripts/ksymoops/oops.c
--- v2.2.0-pre4/linux/scripts/ksymoops/oops.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops/oops.c	Tue Jan  5 11:13:56 1999
@@ -0,0 +1,1200 @@
+/*
+	oops.c.
+
+	Oops processing for ksymoop.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Mon Jan  4 08:47:55 EST 1999
+	Version 0.6d
+	Add ARM support.
+
+	Thu Nov 26 16:37:46 EST 1998
+	Version 0.6c
+	Typo in oops_code.
+	Add -c option.
+
+	Tue Nov  3 23:33:04 EST 1998
+	Version 0.6a
+	Performance inprovements.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Oops file must be regular.
+	Add "invalid operand" to Oops_print.
+	Minor adjustment to re for ppc.
+	Minor adjustment to re for objdump lines with <_EIP+xxx>.
+	Convert from a.out to bfd, using same format as ksymoops.
+	Added MIPS.
+	PPC handling based on patches by "Ryan Nielsen" <ran@krazynet.com>
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into seperate sources.
+ */
+
+#include "ksymoops.h"
+#include <bfd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <malloc.h>
+#include <memory.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Error detected by bfd */
+static void Oops_bfd_perror(const char *msg)
+{
+	fprintf(stderr, "Error ");
+	bfd_perror(msg);
+	++errors;
+}
+
+/* Safest way to get correct output bfd format is to copy ksymoops' format. */
+static int Oops_copy_bfd_format(bfd **ibfd, bfd **obfd, asection **isec,
+				const char *file)
+{
+	char *me, **matches, **match;
+
+	if (!(*obfd = bfd_openw(file, NULL))) {
+		Oops_bfd_perror(file);
+		return(0);
+	}
+
+	me = find_fullpath(prefix);
+	if (!me)
+		return(0);
+
+	if (!(*ibfd = bfd_openr(me, NULL))) {
+		Oops_bfd_perror(me);
+		return(0);
+	}
+	free(me);	/* Who is Tommy? */
+
+	if (!bfd_check_format_matches(*ibfd, bfd_object, &matches)) {
+		Oops_bfd_perror(me);
+		if (bfd_get_error() == bfd_error_file_ambiguously_recognized) {
+			fprintf(stderr, "Matching formats:");
+			match = matches;
+			while (*match)
+				fprintf(stderr, " %s", *match++);
+			fprintf(stderr, "\n");
+			free(matches);
+		}
+		return(0);
+	}
+
+	if (!(*isec = bfd_get_section_by_name(*ibfd, ".text"))) {
+		Oops_bfd_perror("get_section");
+		return(0);
+	}
+
+	bfd_set_format(*obfd, bfd_object);
+	bfd_set_arch_mach(*obfd, bfd_get_arch(*ibfd), bfd_get_mach(*ibfd));
+
+	if (!bfd_set_file_flags(*obfd, bfd_get_file_flags(*ibfd))) {
+		Oops_bfd_perror("set_file_flags");
+		return(0);
+	}
+
+	return(1);
+}
+
+/* Write the code values to a file using bfd. */
+static int Oops_write_bfd_data(bfd *ibfd, bfd *obfd, asection *isec,
+			       const char *code, int size)
+{
+	asection *osec;
+	asymbol *osym;
+
+	if (!bfd_set_start_address(obfd, 0)) {
+		Oops_bfd_perror("set_start_address");
+		return(0);
+	}
+	if (!(osec = bfd_make_section(obfd, ".text"))) {
+		Oops_bfd_perror("make_section");
+		return(0);
+	}
+	if (!bfd_set_section_flags(obfd, osec, 
+		bfd_get_section_flags(ibfd, isec))) {
+		Oops_bfd_perror("set_section_flags");
+		return(0);
+	}
+	if (!bfd_set_section_alignment(obfd, osec, 
+		bfd_get_section_alignment(ibfd, isec))) {
+		Oops_bfd_perror("set_section_alignment");
+		return(0);
+	}
+	osec->output_section = osec;
+	if (!(osym = bfd_make_empty_symbol(obfd))) {
+		Oops_bfd_perror("make_empty_symbol");
+		return(0);
+	}
+	osym->name = "_EIP";
+	osym->section = osec;
+	osym->flags = BSF_GLOBAL;
+	osym->value = 0;
+	if (!bfd_set_symtab(obfd, &osym, 1)) {
+		Oops_bfd_perror("set_symtab");
+		return(0);
+	}
+	if (!bfd_set_section_size(obfd, osec, size)) {
+		Oops_bfd_perror("set_section_size");
+		return(0);
+	}
+	if (!bfd_set_section_vma(obfd, osec, 0)) {
+		Oops_bfd_perror("set_section_vma");
+		return(0);
+	}
+	if (!bfd_set_section_contents(obfd, osec, (PTR) code, 0, size)) {
+		Oops_bfd_perror("set_section_contents");
+		return(0);
+	}
+	if (!bfd_close(obfd)) {
+		Oops_bfd_perror("close(obfd)");
+		return(0);
+	}
+	if (!bfd_close(ibfd)) {
+		Oops_bfd_perror("close(ibfd)");
+		return(0);
+	}
+	return 1;
+}
+
+/* Write the Oops code to a temporary file with suitable header and trailer. */
+static char *Oops_code_to_file(const char *code, int size)
+{
+	char *file;
+	bfd *ibfd, *obfd;
+	asection *isec;
+
+	bfd_init();
+	file = tmpnam(NULL);
+	if (!Oops_copy_bfd_format(&ibfd, &obfd, &isec, file))
+		return(NULL);
+	if (!Oops_write_bfd_data(ibfd, obfd, isec, code, size))
+		return(NULL);
+	return(file);
+}
+
+/* Run objdump against the binary Oops code */
+static FILE *Oops_objdump(const char *file)
+{
+	char *cmd;
+	FILE *f;
+	static char const options[] = "-dhf ";
+	static char const procname[] = "Oops_objdump";
+
+	cmd = malloc(strlen(path_objdump)+1+strlen(options)+strlen(file)+1);
+	if (!cmd)
+		malloc_error(procname);
+	strcpy(cmd, path_objdump);
+	strcat(cmd, " ");
+	strcat(cmd, options);
+	strcat(cmd, file);
+	if (debug > 1)
+		fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
+	f = popen_local(cmd, procname);
+	free(cmd);
+	return(f);
+}
+
+/* Process one code line from objdump, ignore everything else */
+static void Oops_decode_one(SYMBOL_SET *ss, const char *line, elf_addr_t eip,
+			    int adjust)
+{
+	int i;
+	elf_addr_t address, eip_relative;
+	char *line2, *map, **string = NULL;
+	static regex_t     re_Oops_objdump;
+	static regmatch_t *re_Oops_objdump_pmatch;
+	static char const procname[] = "Oops_decode_one";
+
+	/* objdump output.  Optional whitespace, hex digits, optional
+	 * ' <_EIP+offset>', ':'.  The '+offset' after _EIP is also optional.
+	 * Older binutils output 'xxxxxxxx <_EIP+offset>:', newer versions do
+	 * '00000000 <_EIP>:' first followed by '      xx:' lines.
+	 *
+	 * Just to complicate things even more, objdump recognises jmp, call,
+	 * etc., converts the code to something like this :-
+	 * "   f: e8 32 34 00 00  call   3446 <_EIP+0x3446>"
+	 * Recognise this and append the eip adjusted address, followed by the
+	 * map_address text for that address.
+	 *
+	 * With any luck, objdump will take care of all such references which
+	 * makes this routine architecture insensitive.  No need to test for
+	 * i386 jmp, call or m68k swl etc.
+	 */
+	re_compile(&re_Oops_objdump,
+			"^[ \t]*"
+			"([0-9a-fA-F]+)"				/* 1 */
+			"( <_EIP[^>]*>)?"				/* 2 */
+			":"
+			"("						/* 3 */
+			".* +<_EIP\\+0?x?([0-9a-fA-F]+)>[ \t]*$"	/* 4 */
+			")?"
+			".*"
+			,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_objdump_pmatch);
+
+	i = regexec(&re_Oops_objdump, line, re_Oops_objdump.re_nsub+1,
+		re_Oops_objdump_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i != 0)
+		return;
+
+	re_strings(&re_Oops_objdump, line, re_Oops_objdump_pmatch, &string);
+	errno = 0;
+	address = strtoul(string[1], NULL, 16);
+	if (errno) {
+		fprintf(stderr,
+			"%s Invalid hex value in objdump line, "
+			"treated as zero - '%s'\n"
+			"  objdump line '%s'\n",
+			procname, string[1], line);
+		perror(" ");
+		++errors;
+		address = 0;
+	}
+	address += eip + adjust;
+	if (string[4]) {
+		/* EIP relative data to be adjusted */
+		errno = 0;
+		eip_relative = strtoul(string[4], NULL, 16);
+		if (errno) {
+			fprintf(stderr,
+				"%s Invalid hex value in objdump line, "
+				"treated as zero - '%s'\n"
+				"  objdump line '%s'\n",
+				procname, string[4], line);
+			perror(" ");
+			++errors;
+			eip_relative = 0;
+		}
+		eip_relative += eip + adjust;
+		map = map_address(&ss_merged, eip_relative);
+		/* new text is original line, eip_relative in hex, map text */
+		i = strlen(line)+1+2*sizeof(eip_relative)+1+strlen(map)+1;
+		line2 = malloc(i);
+		if (!line2)
+			malloc_error(procname);
+		snprintf(line2, i, "%s %s %s",
+			line, format_address(eip_relative), map);
+		add_symbol_n(ss, address, 'C', 1, line2);
+		free(line2);
+	}
+	else
+		add_symbol_n(ss, address, 'C', 1, line);	/* as is */
+	re_strings_free(&re_Oops_objdump, &string);
+}
+
+/* Maximum number of code bytes to process.  It needs to be a multiple of 2 for
+ * code_byte (-c) swapping.  Sparc and alpha dump 36 bytes so use 64.
+ */
+#define CODE_SIZE 64
+
+/******************************************************************************/
+/*                     Start architecture sensitive code                      */
+/******************************************************************************/
+
+/* Extract the hex values from the Code: line and convert to binary */
+static int Oops_code_values(const unsigned char* code_text, char *code,
+			    int *adjust, char ***string, int string_max,
+			    int code_bytes)
+{
+	int byte = 0, i, l;
+	unsigned long c;
+	char *value;
+	const char *p;
+	static regex_t     re_Oops_code_value;
+	static regmatch_t *re_Oops_code_value_pmatch;
+	static const char procname[] = "Oops_code_values";
+
+	/* Given by re_Oops_code: code_text is a message (e.g. "general
+	 * protection") or one or more hex fields separated by space or tab.
+	 * Some architectures bracket the current instruction with '<'
+	 * and '>', others use '(' and ')'.  The first character is
+	 * nonblank.
+	 */
+	if (!isxdigit(*code_text)) {
+		fprintf(stderr,
+			"Warning, Code looks like message, not hex digits.  "
+			"No disassembly attempted.\n");
+		++warnings;
+		return(0);
+	}
+	memset(code, '\0', CODE_SIZE);
+	p = code_text;
+	*adjust = 0;	/* EIP points to code byte 0 */
+
+	/* Code values.  Hex values separated by white space.  On sparc, the
+	 * current instruction is bracketed in '<' and '>'.
+	 */
+	re_compile(&re_Oops_code_value,
+			"^"
+			"([<(]?)"					/* 1 */
+			"([0-9a-fA-F]+)"				/* 2 */
+			"[>)]?"
+			"[ \t]*"
+			,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_code_value_pmatch);
+
+	re_string_check(re_Oops_code_value.re_nsub+1, string_max, procname);
+	while (regexec(&re_Oops_code_value, p, re_Oops_code_value.re_nsub+1,
+			re_Oops_code_value_pmatch, 0) == 0) {
+		re_strings(&re_Oops_code_value, p,
+			re_Oops_code_value_pmatch, string);
+		if (byte >= CODE_SIZE)
+			break;
+		errno = 0;
+		value = (*string)[2];
+		c = strtoul(value, NULL, 16);
+		if (errno) {
+			fprintf(stderr,
+				"%s Invalid hex value in code_value line, "
+				"treated as zero - '%s'\n"
+				"  code_value line '%s'\n",
+				procname, value, code_text);
+			perror(" ");
+			++errors;
+			c = 0;
+		}
+		if ((*string)[1] && *((*string)[1]))
+			*adjust = -byte;	/* this byte is EIP */
+		/* i386 - 2 byte code, m68k - 4 byte, sparc - 8 byte.
+		 * On some architectures Code: is a stream of bytes, on some it
+		 * is a stream of shorts, on some it is a stream of ints.
+		 * Consistent we're not!
+		 */
+		l = strlen(value);
+		if (l%2) {
+			fprintf(stderr,
+				"%s invalid value 0x%s in Code line, not a "
+				"multiple of 2 digits, value ignored\n",
+				procname, value);
+			++errors;
+		}
+		else while (l) {
+			if (byte >= CODE_SIZE) {
+				fprintf(stderr,
+					"%s Warning: extra values in Code "
+					"line, ignored - '%s'\n",
+					procname, value);
+				++warnings;
+				break;
+			}
+			l -= 2;
+			code[byte++] = (c >> l*4) & 0xff;
+			value += 2;
+		}
+		p += re_Oops_code_value_pmatch[0].rm_eo;
+	}
+
+	if (*p) {
+		fprintf(stderr,
+			"Warning garbage '%s' at end of code line ignored "
+			"by %s\n",
+			p, procname);
+		++warnings;
+	}
+
+	/* The code_bytes parameter says how many readable bytes form a single
+	 * code unit in machine terms.  -c 1 says that the text is already in
+	 * machine order, -c 2 (4, 8) says each chunk of 2 (4, 8) bytes must be
+	 * swapped to get back to machine order.  Which end is up?
+	 */
+	if (code_bytes != 1) {
+		if (byte % code_bytes) {
+			fprintf(stderr,
+				"Warning: the number of code bytes (%d) is not "
+				"a multiple of -c (%d)\n"
+				"Byte swapping may not give sensible results\n",
+				byte, code_bytes);
+			++warnings;
+		}
+		for (l = 0; l < byte; l+= code_bytes) {
+			for (i = 0; i < code_bytes/2; ++i) {
+				c = code[l+i];
+				code[l+i] = code[l+code_bytes-i-1];
+				code[l+code_bytes-i-1] = c;
+			}
+		}
+	}
+
+	return(1);
+}
+
+/* Look for the EIP: line, returns start of the relevant hex value */
+static char *Oops_eip(const char *line, char ***string, int string_max)
+{
+	int i;
+	static regex_t     re_Oops_eip_sparc;
+	static regmatch_t *re_Oops_eip_sparc_pmatch;
+	static regex_t     re_Oops_eip_ppc;
+	static regmatch_t *re_Oops_eip_ppc_pmatch;
+	static regex_t     re_Oops_eip_mips;
+	static regmatch_t *re_Oops_eip_mips_pmatch;
+	static regex_t     re_Oops_eip_other;
+	static regmatch_t *re_Oops_eip_other_pmatch;
+	static const char procname[] = "Oops_eip";
+
+	/* Oops 'EIP:' line for sparc, actually PSR followed by PC */
+	re_compile(&re_Oops_eip_sparc,
+			"^PSR: [0-9a-fA-F]+ PC: " UNBRACKETED_ADDRESS,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_eip_sparc_pmatch);
+
+	i = regexec(&re_Oops_eip_sparc, line, re_Oops_eip_sparc.re_nsub+1,
+		re_Oops_eip_sparc_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec sparc %d\n", procname, i);
+	if (i == 0) {
+		re_string_check(re_Oops_eip_sparc.re_nsub+1, string_max,
+			procname);
+		re_strings(&re_Oops_eip_sparc, line, re_Oops_eip_sparc_pmatch,
+			string);
+		return((*string)[re_Oops_eip_sparc.re_nsub]);
+	}
+
+	/* Oops 'EIP:' line for PPC, all over the place */
+	re_compile(&re_Oops_eip_ppc,
+			"("
+			  "(kernel pc )"
+			  "|(trap at PC: )"
+			  "|(bad area pc )"
+			  "|(NIP: )"
+			")"
+			UNBRACKETED_ADDRESS,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_eip_ppc_pmatch);
+
+	i = regexec(&re_Oops_eip_ppc, line, re_Oops_eip_ppc.re_nsub+1,
+		re_Oops_eip_ppc_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec ppc %d\n", procname, i);
+	if (i == 0) {
+		re_string_check(re_Oops_eip_ppc.re_nsub+1, string_max,
+			procname);
+		re_strings(&re_Oops_eip_ppc, line, re_Oops_eip_ppc_pmatch,
+			string);
+		return((*string)[re_Oops_eip_ppc.re_nsub]);
+	}
+
+	/* Oops 'EIP:' line for MIPS, epc, optional white space, ':',
+	 * optional white space, unbracketed address.
+	 */
+	re_compile(&re_Oops_eip_mips,
+			"^(epc[ \t]*:+[ \t]*)"
+			UNBRACKETED_ADDRESS,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_eip_mips_pmatch);
+
+	i = regexec(&re_Oops_eip_mips, line, re_Oops_eip_mips.re_nsub+1,
+		re_Oops_eip_mips_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec mips %d\n", procname, i);
+	if (i == 0) {
+		re_string_check(re_Oops_eip_mips.re_nsub+1, string_max,
+			procname);
+		re_strings(&re_Oops_eip_mips, line, re_Oops_eip_mips_pmatch,
+			string);
+		return((*string)[re_Oops_eip_mips.re_nsub]);
+	}
+
+	/* Oops 'EIP:' line for other architectures */
+	re_compile(&re_Oops_eip_other,
+			"^("
+	/* i386 */	"(EIP:[ \t]+.*)"
+	/* m68k */	"|(PC[ \t]*=[ \t]*)"
+	/* ARM */	"|(pc *: *)"
+			")"
+			BRACKETED_ADDRESS
+			,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_eip_other_pmatch);
+
+	i = regexec(&re_Oops_eip_other, line, re_Oops_eip_other.re_nsub+1,
+		re_Oops_eip_other_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec other %d\n", procname, i);
+	if (i == 0) {
+		re_string_check(re_Oops_eip_other.re_nsub+1, string_max,
+			procname);
+		re_strings(&re_Oops_eip_other, line, re_Oops_eip_other_pmatch,
+			string);
+		return((*string)[re_Oops_eip_other.re_nsub]);
+	}
+	return(NULL);
+}
+
+/* Set the eip from the EIP line */
+static void Oops_set_eip(const char *value, elf_addr_t *eip, SYMBOL_SET *ss)
+{
+	static const char procname[] = "Oops_set_eip";
+	errno = 0;
+	*eip = strtoul(value, NULL, 16);
+	if (errno) {
+		fprintf(stderr,
+			"%s Invalid hex value in EIP line, ignored - '%s'\n",
+			procname, value);
+		perror(" ");
+		++errors;
+		*eip = 0;
+	}
+	add_symbol_n(ss, *eip, 'E', 1, ">>EIP:");
+}
+
+/* Look for the MIPS ra line, returns start of the relevant hex value */
+static char *Oops_ra(const char *line, char ***string, int string_max)
+{
+	int i;
+	static regex_t     re_Oops_ra;
+	static regmatch_t *re_Oops_ra_pmatch;
+	static const char procname[] = "Oops_ra";
+
+	/* Oops 'ra:' line for MIPS, ra, optional white space, one or
+	 * more '=', optional white space, unbracketed address.
+	 */
+	re_compile(&re_Oops_ra,
+			"(ra[ \t]*=+[ \t]*)"
+			UNBRACKETED_ADDRESS,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_ra_pmatch);
+
+	i = regexec(&re_Oops_ra, line, re_Oops_ra.re_nsub+1,
+		re_Oops_ra_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i == 0) {
+		re_string_check(re_Oops_ra.re_nsub+1, string_max, procname);
+		re_strings(&re_Oops_ra, line, re_Oops_ra_pmatch,
+			string);
+		return((*string)[re_Oops_ra.re_nsub]);
+	}
+	return(NULL);
+}
+
+/* Set the MIPS ra from the ra line */
+static void Oops_set_ra(const char *value, SYMBOL_SET *ss)
+{
+	static const char procname[] = "Oops_set_ra";
+	elf_addr_t ra;
+	errno = 0;
+	ra = strtoul(value, NULL, 16);
+	if (errno) {
+		fprintf(stderr,
+			"%s Invalid hex value in ra line, ignored - '%s'\n",
+			procname, value);
+		perror(" ");
+		++errors;
+		ra = 0;
+	}
+	add_symbol_n(ss, ra, 'R', 1, ">>RA :");
+}
+
+/* Look for the Trace multilines :(.  Returns start of addresses. */
+static const char *Oops_trace(const char *line, char ***string, int string_max)
+{
+	int i;
+	const char *start = NULL;
+	static int trace_line = 0;
+	static regex_t     re_Oops_trace;
+	static regmatch_t *re_Oops_trace_pmatch;
+	static const char procname[] = "Oops_trace";
+
+	/* ppc is different, not a bracketed address, just an address */
+	/* ARM is different, two bracketed addresses on each line */
+
+	/* Oops 'Trace' lines */
+	re_compile(&re_Oops_trace,
+			"^("					/*  1 */
+			"(Call Trace: )"			/*  2 */
+	/* alpha */	"|(Trace: )"				/*  3 */
+	/* various */	"|(" BRACKETED_ADDRESS ")"	   	/* 4,5*/
+	/* ppc */	"|(Call backtrace:)"			/*  6 */
+	/* ppc */	"|(" UNBRACKETED_ADDRESS ")"		/* 7,8*/
+	/* ARM */	"|(Function entered at (" BRACKETED_ADDRESS "))"	/* 9,10,11 */
+			")",
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_trace_pmatch);
+
+	i = regexec(&re_Oops_trace, line, re_Oops_trace.re_nsub+1,
+		re_Oops_trace_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i == 0) {
+#undef MATCHED
+#define MATCHED(n) (re_Oops_trace_pmatch[n].rm_so != -1)
+		if (MATCHED(2) || MATCHED(3)) {
+			trace_line = 1;
+			start = line + re_Oops_trace_pmatch[0].rm_eo;
+		}
+		else if (MATCHED(6)) {
+			trace_line = 2;		/* ppc */
+			start = line + re_Oops_trace_pmatch[0].rm_eo;
+		}
+		else if (trace_line == 1 && MATCHED(5))
+			start = line + re_Oops_trace_pmatch[5].rm_so;
+		else if (trace_line == 2 && MATCHED(8))	/* ppc */
+			start = line + re_Oops_trace_pmatch[8].rm_so;
+		else if (MATCHED(10)){
+			trace_line = 1;		/* ARM */
+			start = line + re_Oops_trace_pmatch[10].rm_so;
+		}
+		else
+			trace_line = 0;
+	}
+	else
+		trace_line = 0;
+	if (trace_line)
+		return(start);
+	return(NULL);
+}
+
+/* Process a trace call line, extract addresses */
+static void Oops_trace_line(const char *line, const char *p, SYMBOL_SET *ss)
+{
+	char **string = NULL;
+	regex_t *pregex;
+	regmatch_t *pregmatch;
+	static const char procname[] = "Oops_trace_line";
+
+	/* ppc does not bracket its addresses */
+	if (isxdigit(*p)) {
+		pregex = &re_unbracketed_address;
+		pregmatch = re_unbracketed_address_pmatch;
+	}
+	else {
+		pregex = &re_bracketed_address;
+		pregmatch = re_bracketed_address_pmatch;
+	}
+
+	/* Loop over [un]?bracketed addresses */
+	while (1) {
+		if (regexec(pregex, p, pregex->re_nsub+1, pregmatch, 0) == 0) {
+			re_strings(pregex, p, pregmatch, &string);
+			add_symbol(ss, string[1], 'T', 1, "Trace:");
+			p += pregmatch[0].rm_eo;
+		}
+		else if (strncmp(p, "from ", 5) == 0)
+			p += 5;		/* ARM does "address from address" */
+		else
+			break;
+	}
+
+	if (*p && !strcmp(p, "...")) {
+		fprintf(stderr,
+			"Warning garbage '%s' at end of trace line ignored "
+			"by %s\n",
+			p, procname);
+		++warnings;
+	}
+	re_strings_free(pregex, &string);
+}
+
+/* Do pattern matching to decide if the line should be printed.  When reading a
+ * syslog containing multiple Oops, you need the intermediate data (registers,
+ * tss etc.) to go with the decoded text.  Sets text to the start of the useful
+ * text, after any prefix.  Note that any leading white space is treated as part
+ * of the prefix, later routines do not see any indentation.
+ *
+ * Note: If a line is not printed, it will not be scanned for any other text.
+ */
+static int Oops_print(const char *line, const char **text, char ***string,
+		      int string_max)
+{
+	int i, print = 0;
+	static int stack_line = 0, trace_line = 0;
+	static regex_t     re_Oops_prefix;
+	static regmatch_t *re_Oops_prefix_pmatch;
+	static regex_t     re_Oops_print_s;
+	static regmatch_t *re_Oops_print_s_pmatch;
+	static regex_t     re_Oops_print_a;
+	static regmatch_t *re_Oops_print_a_pmatch;
+	static const char procname[] = "Oops_print";
+
+	*text = line;
+
+	/* Lines to be ignored.  For some reason the "amuse the user" print in
+	 * some die_if_kernel routines causes regexec to run very slowly.
+	 */
+
+	if (strstr(*text, "\\|/ ____ \\|/")  ||
+	    strstr(*text, "\"@'/ ,. \\`@\"") ||
+	    strstr(*text, "/_| \\__/ |_\\")  ||
+	    strstr(*text, "   \\__U_/"))
+		return(1);	/* print but avoid regexec */
+
+	/* Prefixes to be ignored */
+	re_compile(&re_Oops_prefix,
+			"^("			/* start of line */
+			"([^ ]{3} [ 0-9][0-9] [0-9]{2}:[0-9]{2}:[0-9]{2} "
+			  "[^ ]+ kernel: +)"	/* syslogd */
+			"|(<[0-9]+>)"		/* kmsg */
+			"|([ \t]+)"		/* leading white space */
+			")+"			/* any prefixes, in any order */
+			,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_prefix_pmatch);
+
+	i = regexec(&re_Oops_prefix, *text, re_Oops_prefix.re_nsub+1,
+		re_Oops_prefix_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec prefix %d\n", procname, i);
+	if (i == 0)
+		*text += re_Oops_prefix_pmatch[0].rm_eo;  /* step over prefix */
+
+
+	/* Lots of possibilities.  Expand as required for all architectures.
+	 *
+	 * Trial and error shows that regex does not like a lot of sub patterns
+	 * that start with "^".  So split the patterns into two groups, one set
+	 * must appear at the start of the line, the other set can appear
+	 * anywhere.
+	 */
+
+	/* These patterns must appear at the start of the line, after stripping
+	 * the prefix above.
+	 *
+	 * The order below is required to handle multiline outupt.
+	 * string 2 is defined if the text is 'Stack from '.
+	 * string 3 is defined if the text is 'Stack: '.
+	 * string 4 is defined if the text might be a stack continuation.
+	 * string 5 is defined if the text is 'Call Trace: '.
+	 * string 6 is defined if the text might be a trace continuation.
+	 * string 7 is the address part of the BRACKETED_ADDRESS.
+	 *
+	 * string 8 is defined if the text contains a version number.  No Oops
+	 * report contains this as of 2.1.125 but IMHO it should be added.  If
+	 * anybody wants to print a VERSION_nnnn line in their Oops, this code
+	 * is ready.
+	 *
+	 * string 9 is defined if the text is 'Trace: ' (alpha).
+	 * string 10 is defined if the text is 'Call backtrace:' (ppc).
+	 */
+	re_compile(&re_Oops_print_s,
+       /* arch type */					    /* Required order */
+			"^("						/*  1 */
+	/* i386 */	"(Stack: )"					/*  2 */
+	/* m68k */	"|(Stack from )"				/*  3 */
+	/* various */	"|([0-9a-fA-F]{4,})"				/*  4 */
+	/* various */	"|(Call Trace: )"				/*  5 */
+	/* various */	"|(" BRACKETED_ADDRESS ")"			/* 6,7*/
+	/* various */	"|(Version_[0-9]+)"				/*  8 */
+	/* alpha */	"|(Trace: )"					/*  9 */
+	/* ppc */	"|(Call backtrace:)"				/* 10 */
+
+			/* order does not matter from here on */
+	
+	/* various */	"|(Process .*stackpage=)"
+	/* various */	"|(Call Trace:[ \t])"
+	/* various */	"|(Code *:[ \t])"
+	/* various */	"|(Kernel panic)"
+	/* various */	"|(In swapper task)"
+
+	/* i386 2.0 */	"|(Corrupted stack page)"
+	/* i386 */	"|(invalid operand: )"
+	/* i386 */	"|(Oops: )"
+	/* i386 */	"|(Cpu: +[0-9])"
+	/* i386 */	"|(current->tss)"
+	/* i386 */	"|(\\*pde +=)"
+	/* i386 */	"|(EIP: )"
+	/* i386 */	"|(EFLAGS: )"
+	/* i386 */	"|(eax: )"
+	/* i386 */	"|(esi: )"
+	/* i386 */	"|(ds: )"
+
+	/* m68k */	"|(pc[:=])"
+	/* m68k */	"|(68060 access)"
+	/* m68k */	"|(Exception at )"
+	/* m68k */	"|(d[04]: )"
+	/* m68k */	"|(Frame format=)"
+	/* m68k */	"|(wb [0-9] stat)"
+	/* m68k */	"|(push data: )"
+	/* m68k */	"|(baddr=)"
+	/* any other m68K lines to print? */
+
+	/* sparc */	"|(Bad unaligned kernel)"
+	/* sparc */	"|(Forwarding unaligned exception)"
+	/* sparc */	"|(: unhandled unaligned exception)"
+	/* sparc */	"|(<sc)"
+	/* sparc */	"|(pc *=)"
+	/* sparc */	"|(r[0-9]+ *=)"
+	/* sparc */	"|(gp *=)"
+	/* any other sparc lines to print? */
+
+	/* alpha */	"|(tsk->)"
+	/* alpha */	"|(PSR: )"
+	/* alpha */	"|([goli]0: )"
+	/* alpha */	"|(Instruction DUMP: )"
+	/* any other alpha lines to print? */
+
+	/* ppc */	"|(MSR: )"
+	/* ppc */	"|(TASK = )"
+	/* ppc */	"|(last math )"
+	/* ppc */	"|(GPR[0-9]+: )"
+	/* any other ppc lines to print? */
+
+	/* MIPS */	"|(\\$[0-9 ]+:)"
+	/* MIPS */	"|(epc )"
+	/* MIPS */	"|(Status:)"
+	/* MIPS */	"|(Cause :)"
+	/* any other MIPS lines to print? */
+
+	/* ARM */	"|(Backtrace:)"
+	/* ARM */	"|(Function entered at)"
+	/* ARM */	"|(\\*pgd =)"
+	/* ARM */	"|(Internal error)"
+	/* ARM */	"|(pc :)"
+	/* ARM */	"|(sp :)"
+	/* ARM */	"|(r[0-9][0-9 ]:)"
+	/* ARM */	"|(Flags:)"
+	/* ARM */	"|(Control:)"
+	/* any other ARM lines to print? */
+
+			")",
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_print_s_pmatch);
+
+	i = regexec(&re_Oops_print_s, *text, re_Oops_print_s.re_nsub+1,
+		re_Oops_print_s_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec start %d\n", procname, i);
+	print = 0;
+	if (i == 0) {
+#undef MATCHED
+#define MATCHED(n) (re_Oops_print_s_pmatch[n].rm_so != -1)
+		print = 1;
+		/* Handle multiline messages, messy */
+		if (!MATCHED(2) && !MATCHED(3) && !MATCHED(4))
+			stack_line = 0;
+		else if (MATCHED(2) || MATCHED(3))
+			stack_line = 1;
+		else if (stack_line && !MATCHED(4)) {
+			print = 0;
+			stack_line = 0;
+		}
+		if (!MATCHED(5) && !MATCHED(6) && !MATCHED(9) && !MATCHED(10))
+			trace_line = 0;
+		else if (MATCHED(5) || MATCHED(9) || MATCHED(10))
+			trace_line = 1;
+		else if (stack_line && !MATCHED(6)) {
+			print = 0;
+			trace_line = 0;
+		}
+		/* delay splitting into strings until we really them */
+		if (MATCHED(8)) {
+			re_string_check(re_Oops_print_s.re_nsub+1, string_max,
+				procname);
+			re_strings(&re_Oops_print_s, *text,
+				re_Oops_print_s_pmatch,
+				string);
+			add_Version((*string)[8]+8, "Oops");
+		}
+	}
+
+	/* These patterns can appear anywhere in the line, after stripping
+	 * the prefix above.
+	 */
+	re_compile(&re_Oops_print_a,
+       /* arch type */
+
+	/* various */	"(Unable to handle kernel)"
+	/* various */	"|(Aiee)"      /* anywhere in text is a bad sign (TM) */
+	/* various */	"|(die_if_kernel)"	/* ditto */
+
+	/* sparc */	"|(\\([0-9]\\): Oops )"
+	/* sparc */	"|(: memory violation)"
+	/* sparc */	"|(: Exception at)"
+	/* sparc */	"|(: Arithmetic fault)"
+	/* sparc */	"|(: Instruction fault)"
+	/* sparc */	"|(: arithmetic trap)"
+	/* sparc */	"|(: unaligned trap)"
+
+	/* sparc      die_if_kernel has no fixed text, identify by (pid): text.
+	 *            Somebody has been playful with the texts.
+	 *
+	 *            Alas adding this next pattern increases run time by 15% on
+	 *            its own!  It would be considerably faster if sparc had
+	 *            consistent error texts.
+	 */
+	/* sparc */	"|("
+			   "\\([0-9]+\\): "
+			   "("
+			     "(Whee)"
+			     "|(Oops)"
+			     "|(Kernel)"
+			     "|(Penguin)"
+			     "|(Too many Penguin)"
+			     "|(BOGUS)"
+			   ")"
+			 ")"
+
+	/* ppc */	"|(kernel pc )"
+	/* ppc */	"|(trap at PC: )"
+	/* ppc */	"|(bad area pc )"
+	/* ppc */	"|(NIP: )"
+
+	/* MIPS */	"|( ra *=)"
+
+			")",
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_print_a_pmatch);
+
+	i = regexec(&re_Oops_print_a, *text, re_Oops_print_a.re_nsub+1,
+		re_Oops_print_a_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec anywhere %d\n", procname, i);
+	if (i == 0)
+		print = 1;
+
+	return(print);
+}
+
+/* Look for the Code: line.  Returns start of the code bytes. */
+static const char *Oops_code(const char *line, char ***string, int string_max)
+{
+	int i;
+	static regex_t     re_Oops_code;
+	static regmatch_t *re_Oops_code_pmatch;
+	static const char procname[] = "Oops_code";
+
+	/* Oops 'Code: ' hopefully followed by at least one hex code.  sparc
+	 * brackets the PC in '<' and '>'.  ARM brackets the PC in '(' and ')'.
+	 */
+	re_compile(&re_Oops_code,
+			"^("						/*  1 */
+	/* sparc */	  "(Instruction DUMP)"				/*  2 */
+	/* various */	  "|(Code *)"					/*  3 */
+			")"
+			":[ \t]+"
+			"("						/*  4 */
+			  "(general protection.*)"
+			  "|(<[0-9]+>)"
+			  "|(([<(]?[0-9a-fA-F]+[>)]?[ \t]*)+)"
+			")"
+			"(.*)$"				/* trailing garbage */
+			,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_code_pmatch);
+
+	i = regexec(&re_Oops_code, line, re_Oops_code.re_nsub+1,
+		re_Oops_code_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i == 0) {
+		re_string_check(re_Oops_code.re_nsub+1, string_max, procname);
+		re_strings(&re_Oops_code, line, re_Oops_code_pmatch,
+			string);
+		if ((*string)[re_Oops_code.re_nsub] &&
+		    *((*string)[re_Oops_code.re_nsub])) {
+			fprintf(stderr,
+				"Warning: trailing garbage ignored on Code: "
+				"line\n"
+				"  Text: '%s'\n"
+				"  Garbage: '%s'\n",
+				line, (*string)[re_Oops_code.re_nsub]);
+			++warnings;
+		}
+		return((*string)[4]);
+	}
+	return(NULL);
+}
+
+/******************************************************************************/
+/*                      End architecture sensitive code                       */
+/******************************************************************************/
+
+/* Decode the Oops Code: via objdump*/
+static void Oops_decode(const unsigned char* code_text, elf_addr_t eip,
+			SYMBOL_SET *ss, char ***string, int string_max,
+			int code_bytes)
+{
+	FILE *f;
+	char *file, *line = NULL, code[CODE_SIZE];
+	int size = 0, adjust;
+	static char const procname[] = "Oops_decode";
+
+	if (debug)
+		fprintf(stderr, "DEBUG: %s\n", procname);
+	/* text to binary */
+	if (!Oops_code_values(code_text, code, &adjust, string, string_max,
+		code_bytes))
+		return;
+	/* binary to same format as ksymoops */
+	if (!(file = Oops_code_to_file(code, CODE_SIZE)))
+		return;
+	/* objdump the pseudo object */
+	if (!(f = Oops_objdump(file)))	
+		return;
+	while (fgets_local(&line, &size, f, procname)) {
+		if (debug > 1)
+			fprintf(stderr, "DEBUG: %s - %s\n", procname, line);
+		Oops_decode_one(ss, line, eip, adjust);
+	}
+	pclose_local(f, procname);	/* opened in Oops_objdump */
+	free(line);
+	if (unlink(file)) {
+		fprintf(stderr, "%s could not unlink %s", prefix, file);
+		perror(" ");
+	}
+}
+
+/* Reached the end of an Oops report, format the extracted data. */
+static void Oops_format(const SYMBOL_SET *ss_format)
+{
+	int i;
+	SYMBOL *s;
+	static const char procname[] = "Oops_format";
+
+	if (debug)
+		fprintf(stderr, "DEBUG: %s\n", procname);
+
+	compare_Version();	/* Oops might have a version one day */
+	printf("\n");
+	for (s = ss_format->symbol, i = 0; i < ss_format->used; ++i, ++s) {
+		/* For type C data, print Code:, address, map, "name" (actually
+		 * the text of an objdump line).  For other types print name,
+		 * address, map.
+		 */
+		if (s->type == 'C')
+			printf("Code:  %s %-30s %s\n",
+				format_address(s->address),
+				map_address(&ss_merged, s->address),
+				s->name);
+		else
+			printf("%s %s %s\n",
+				s->name,
+				format_address(s->address),
+				map_address(&ss_merged, s->address));
+	}
+	printf("\n");
+}
+
+/* Select next Oops input file */
+static FILE *Oops_next_file(int *filecount, char * const **filename)
+{
+	static FILE *f = NULL;
+	static const char procname[] = "Oops_next_file";
+	static int first_file = 1;
+
+	if (first_file) {
+		f = stdin;
+		first_file = 0;
+	}
+	while (*filecount) {
+		if (f)
+			fclose_local(f, procname);
+		f = NULL;
+		if (regular_file(**filename, procname))
+			f = fopen_local(**filename, "r", procname);
+		if (f) {
+			if (debug)
+				fprintf(stderr,
+					"DEBUG: reading Oops report "
+					"from %s\n", **filename);
+		}
+		++*filename;
+		--*filecount;
+		if (f)
+			return(f);
+	}
+	return(f);
+}
+
+/* Read the Oops report */
+#define MAX_STRINGS 300	/* Maximum strings in any Oops re */
+int Oops_read(int filecount, char * const *filename, int code_bytes,
+	      int one_shot)
+{
+	char *line = NULL, **string = NULL;
+	const char *start, *text;
+	int i, size = 0, lineno = 0, lastprint = 0;
+	elf_addr_t eip = 0;
+	FILE *f;
+	SYMBOL_SET ss_format;
+	static const char procname[] = "Oops_read";
+
+	ss_init(&ss_format, "Oops log data");
+
+	if (!filecount && isatty(0))
+		printf("Reading Oops report from the terminal\n");
+
+	string = malloc(MAX_STRINGS*sizeof(*string));
+	if (!string)
+		malloc_error(procname);
+	memset(string, '\0', MAX_STRINGS*sizeof(*string));
+
+	do {
+		if (!(f = Oops_next_file(&filecount, &filename)))
+			continue;
+		while (fgets_local(&line, &size, f, procname)) {
+			if (debug > 2)
+				fprintf(stderr,
+					"DEBUG: %s - %s\n", procname, line);
+			++lineno;
+			if (Oops_print(line, &text, &string, MAX_STRINGS)) {
+				puts(line);
+				lastprint = lineno;
+				if ((start = Oops_eip(text,
+					&string, MAX_STRINGS)))
+					Oops_set_eip(start, &eip, &ss_format);
+				if ((start = Oops_ra(text,
+					&string, MAX_STRINGS)))
+					Oops_set_ra(start, &ss_format);
+				if ((start = Oops_trace(text,
+					&string, MAX_STRINGS)))
+					Oops_trace_line(text, start,
+						&ss_format);
+				if ((start = Oops_code(text,
+					&string, MAX_STRINGS))) {
+					Oops_decode(start, eip, &ss_format,
+						&string, MAX_STRINGS,
+						code_bytes);
+					Oops_format(&ss_format);
+					ss_free(&ss_format);
+					if (one_shot)
+						return(0);
+				}
+			}
+			/* More than 5 (arbitrary) lines which were not printed
+			 * and there is some saved data, assume we missed the
+			 * Code: line.
+			 */
+			if (ss_format.used && lineno > lastprint+5) {
+				fprintf(stderr,
+					"Warning, Code line not seen, dumping "
+					"what data is available\n");
+				++warnings;
+				Oops_format(&ss_format);
+				ss_free(&ss_format);
+				if (one_shot)
+					return(0);
+			}
+		}
+		if (ss_format.used) {
+			fprintf(stderr,
+				"Warning, Code line not seen, dumping "
+				"what data is available\n");
+			++warnings;
+			Oops_format(&ss_format);
+			ss_free(&ss_format);
+			if (one_shot)
+				return(0);
+		}
+	} while (filecount != 0);
+
+	for (i = 0; i < sizeof(string); ++i) {
+		free(string[i]);
+		string[i] = NULL;
+	}
+	free(line);
+	if (one_shot)
+		return(3);	/* one shot mode, end of input, no data */
+	return(0);
+}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops/re.c linux/scripts/ksymoops/re.c
--- v2.2.0-pre4/linux/scripts/ksymoops/re.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops/re.c	Tue Jan  5 11:13:56 1999
@@ -0,0 +1,145 @@
+/*
+	re.c.
+
+	Regular expression processing for ksymoops.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	PPC trace addresses are not bracketed, add new re.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <malloc.h>
+#include <string.h>
+
+/* Compile a regular expression */
+void re_compile(regex_t *preg, const char *regex, int cflags,
+		regmatch_t **pmatch)
+{
+	int i, l;
+	char *p;
+	static char const procname[] = "re_compile";
+
+	if (preg->re_nsub)
+		return;		/* already compiled */
+
+	if (debug)
+		fprintf(stderr, "DEBUG: %s '%s'", procname, regex);
+	if ((i = regcomp(preg, regex, cflags))) {
+		l = regerror(i, preg, NULL, 0);
+		++l;	/* doc is ambiguous, be safe */
+		p = malloc(l);
+		if (!p)
+			malloc_error("regerror text");
+		regerror(i, preg, p, l);
+		fprintf(stderr,
+			"%s: fatal %s error on '%s' - %s\n",
+			prefix, procname, regex, p);
+		exit(2);
+	}
+	if (debug)
+		fprintf(stderr, " %d sub expression(s)\n", preg->re_nsub);
+	/* [0] is entire match, [1] is first substring */
+	*pmatch = malloc((preg->re_nsub+1)*sizeof(**pmatch));
+	if (!*pmatch)
+		malloc_error("pmatch");
+
+}
+
+/* Compile common regular expressions */
+void re_compile_common(void)
+{
+
+	/* nm: address, type, symbol */
+	re_compile(&re_nm,
+		"^([0-9a-fA-F]{4,}) +([^ ]) +([^ ]+)$",
+		REG_NEWLINE|REG_EXTENDED,
+		&re_nm_pmatch);
+
+	/* bracketed address preceded by optional white space */
+	re_compile(&re_bracketed_address,
+		"^[ \t]*" BRACKETED_ADDRESS,
+		REG_NEWLINE|REG_EXTENDED,
+		&re_bracketed_address_pmatch);
+
+	/* unbracketed address preceded by optional white space */
+	re_compile(&re_unbracketed_address,
+		"^[ \t*]*" UNBRACKETED_ADDRESS,
+		REG_NEWLINE|REG_EXTENDED,
+		&re_unbracketed_address_pmatch);
+
+}
+
+/* Split text into the matching re substrings - Perl is so much easier :).
+ * Each element of *string is set to a malloced copy of the substring or
+ * NULL if the substring did not match (undef).  A zero length substring match
+ * is represented by a zero length **string.
+ */
+void re_strings(regex_t *preg, const char *text, regmatch_t *pmatch,
+		char ***string)
+{
+	int i;
+	if (!*string) {
+		*string = malloc((preg->re_nsub+1)*sizeof(**string));
+		if (!*string)
+			malloc_error("re_strings base");
+		for (i = 0; i < preg->re_nsub+1; ++i)
+			(*string)[i] = NULL;
+	}
+	for (i = 0; i < preg->re_nsub+1; ++i) {
+		if (debug > 4)
+			fprintf(stderr,
+				"DEBUG: re_string %d offsets %d %d",
+				i, pmatch[i].rm_so, pmatch[i].rm_eo);
+		if (pmatch[i].rm_so == -1) {
+			/* no match for this sub expression */
+			free((*string)[i]);
+			(*string)[i] = NULL;
+			if (debug > 4)
+				fprintf(stderr, " (undef)\n");
+		}
+		else {
+			int l = pmatch[i].rm_eo - pmatch[i].rm_so + 1;
+			char *p;
+			p = malloc(l);
+			if (!p)
+				malloc_error("re_strings");
+			strncpy(p, text+pmatch[i].rm_so, l-1);
+			*(p+l-1) = '\0';
+			(*string)[i] = p;
+			if (debug > 4)
+				fprintf(stderr, " '%s'\n", p);
+		}
+	}
+}
+
+/* Free the matching re substrings */
+void re_strings_free(const regex_t *preg, char ***string)
+{
+	if (*string) {
+		int i;
+		for (i = 0; i < preg->re_nsub+1; ++i)
+			free((*string)[i]);
+		free(*string);
+		*string = NULL;
+	}
+}
+
+/* Check that there are enough strings for an re */
+void re_string_check(int need, int available, const char *msg)
+{
+	if (need > available) {
+		fprintf(stderr,
+			"%s: fatal not enough re_strings in %s.  "
+			"Need %d, available %d\n",
+			prefix, msg, need, available);
+		exit(2);
+	}
+}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops/symbol.c linux/scripts/ksymoops/symbol.c
--- v2.2.0-pre4/linux/scripts/ksymoops/symbol.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops/symbol.c	Tue Jan  5 11:13:56 1999
@@ -0,0 +1,444 @@
+/*
+	symbol.c.
+
+	Symbol handling routines for ksymoops.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Mon Jan  4 09:08:19 EST 1999
+	Version 0.6d
+	Cast Version to int, glibc 2.1 made elf_addr_t a long.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Fix end of code calculation.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <errno.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Initialise a symbol source */
+void ss_init(SYMBOL_SET *ss, const char *msg)
+{
+	memset(ss, '\0', sizeof(*ss));
+	ss->source = strdup(msg);
+	if (!ss->source)
+		malloc_error(msg);
+}
+
+/* Free dynamic data from a symbol source */
+void ss_free(SYMBOL_SET *ss)
+{
+	int i;
+	SYMBOL *s;
+	for (s = ss->symbol, i = 0; i < ss->used; ++i, ++s)
+		free(s->name);
+	free(ss->symbol);
+	free(ss->source);
+	memset(ss, '\0', sizeof(*ss));
+}
+
+/* Initialise common symbol sets */
+void ss_init_common(void)
+{
+	ss_init(&ss_Version, "Version_");
+}
+
+/* Find a symbol name in a symbol source.  Brute force ascending order search,
+ * no hashing.  If start is not NULL, it contains the starting point for the
+ * scan and is updated to point to the found entry.  If the entry is not found,
+ * return NULL with start pointing to the next highest entry.
+ * NOTE: Assumes that ss is sorted by name.
+ */
+SYMBOL *find_symbol_name(const SYMBOL_SET *ss, const char *symbol, int *start)
+{
+	int i, l;
+	SYMBOL *s;
+	for (i = start ? *start : 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
+		if ((l = strcmp(symbol, s->name)) == 0) {
+			if (start)
+				*start = i;
+			return(s);
+		}
+		if (l < 0)
+			break;
+	}
+	if (start)
+		*start = i;
+	return NULL;
+}
+
+/* Find an address in a symbol source.  Brute force ascending order search, no
+ * hashing.  If start is not NULL, it contains the starting point for the scan
+ * and is updated to point to the found entry.  If the entry is not found,
+ * return NULL with start pointing to the next highest entry.
+ * NOTE: Assumes that ss is sorted by address.
+ */
+static SYMBOL *find_symbol_address(const SYMBOL_SET *ss,
+			    const elf_addr_t address, int *start)
+{
+	int i;
+	SYMBOL *s;
+	for (i = start ? *start : 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
+		if (address > s->address)
+			continue;
+		else if (address == s->address) {
+			if (start)
+				*start = i;
+			return(s);
+		}
+		else
+			break;
+	}
+	if (start)
+		*start = i;
+	return NULL;
+}
+
+/* Add a symbol to a symbol set, address in binary */
+void add_symbol_n(SYMBOL_SET *ss, const elf_addr_t address,
+		  const char type, const char keep, const char *symbol)
+{
+	int i;
+	char **string = NULL;
+	SYMBOL *s;
+	static regex_t     re_symbol_ver;
+	static regmatch_t *re_symbol_ver_pmatch;
+	static const char procname[] = "add_symbol_n";
+
+	/* Strip out any trailing symbol version _Rxxxxxxxx. */
+	re_compile(&re_symbol_ver,
+		"^(.*)_R[0-9a-fA-F]{8,}$",
+		REG_NEWLINE|REG_EXTENDED,
+		&re_symbol_ver_pmatch);
+
+	i = regexec(&re_symbol_ver, symbol,
+		    re_symbol_ver.re_nsub+1, re_symbol_ver_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i == 0)
+		re_strings(&re_symbol_ver, symbol, re_symbol_ver_pmatch,
+			&string);
+
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s %s %s '%c' %d '%s'\n",
+			procname, ss->source, format_address(address),
+			type, keep, i == 0 ? string[1] : symbol);
+	if (ss->used > ss->alloc) {
+		fprintf(stderr,
+			"%s: fatal %s ss %s used (%d) > alloc (%d)\n",
+			procname, prefix, ss->source, ss->used, ss->alloc);
+		exit(2);
+	}
+	if (ss->used == ss->alloc) {
+		/* increase by 20% or 10, whichever is larger, arbitrary */
+		int newsize = ss->alloc*120/100;
+		if (newsize < ss->alloc+10)
+			newsize = ss->alloc+10;
+		if (debug > 3)
+			fprintf(stderr,
+				"DEBUG: %s increasing %s from %d to %d "
+				"entries\n",
+				procname, ss->source, ss->alloc, newsize);
+		ss->symbol = realloc(ss->symbol, newsize*sizeof(*(ss->symbol)));
+		if (!ss->symbol)
+			malloc_error("realloc ss");
+		ss->alloc = newsize;
+	}
+	s = ss->symbol+ss->used;
+	if (i == 0) {
+		s->name = string[1];
+		string[1] = NULL;	/* don't free this one */
+	}
+	else {
+		s->name = strdup(symbol);
+		if (!s->name)
+			malloc_error("strdup symbol");
+	}
+	s->type = type;
+	s->keep = keep;
+	s->address = address;
+	++ss->used;
+	re_strings_free(&re_symbol_ver, &string);
+}
+
+/* Add a symbol to a symbol set, address in character */
+void add_symbol(SYMBOL_SET *ss, const char *address, const char type,
+		const char keep, const char *symbol)
+{
+	elf_addr_t a;
+	static char const procname[] = "add_symbol";
+	errno = 0;
+	a = strtoul(address, NULL, 16);
+	if (errno) {
+		fprintf(stderr,
+			"%s: %s address '%s' is in error",
+			prefix, procname, address);
+		perror(" ");
+		++errors;
+	}
+	add_symbol_n(ss, a, type, 1, symbol);
+}
+
+/* Map an address to symbol, offset and length, address in binary */
+char *map_address(const SYMBOL_SET *ss, const elf_addr_t address)
+{
+	int i = 0, l;
+	SYMBOL *s;
+	static char *map = NULL;
+	static int size = 0;
+	static const char procname[] = "map_address_n";
+
+	if (debug > 2)
+		fprintf(stderr, "DEBUG: %s %s %s\n",
+			procname, ss->source, format_address(address));
+	s = find_symbol_address(ss, address, &i);
+	if (!s && --i >= 0)
+		s = ss->symbol+i;	/* address is between s and s+1 */
+			
+	/* Extra map text is always < 100 bytes */
+	if (s)
+		l = strlen(s->name) + 100;
+	else
+		l = 100;
+	if (l > size) {
+		map = realloc(map, l);
+		if (!map)
+			malloc_error(procname);
+		size = l;
+	}
+	if (!s) {
+		if (ss->used == 0)
+			snprintf(map, size, "No symbols available");
+		else
+			snprintf(map, size, "Before first symbol");
+	}
+	else if ((i+1) >= ss->used) {
+		/* Somewhere past last symbol.  Length of last section of code
+		 * is unknown, arbitrary cutoff at 32K.
+		 */
+		elf_addr_t offset = address - s->address;
+		if (offset > 32768)
+			snprintf(map, size, "<END_OF_CODE+%lx/????>", offset);
+		else
+			snprintf(map, size, "<%s+%lx/????>", s->name, offset);
+	}
+	else
+		snprintf(map, size,
+			"<%s+%lx/%lx>",
+			s->name, address - s->address,
+			(s+1)->address - s->address);
+	return(map);
+}
+
+/* After sorting, obsolete symbols are at the top.  Delete them. */
+static void ss_compress(SYMBOL_SET *ss)
+{
+	int i, j;
+	SYMBOL *s;
+	static const char procname[] = "ss_compress";
+
+	if (debug > 1)
+		fprintf(stderr, "DEBUG: %s on table %s, before %d ",
+			procname, ss->source, ss->used);
+	for (i = 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
+		if (!s->keep) {
+			for (j = i; j < ss->used; ++j, ++s) {
+				if (s->keep) {
+					fprintf(stderr,
+						"%s: fatal %s table %s is not "
+						"sorted\n",
+						prefix, procname, ss->source);
+					exit(2);
+				}
+			}
+			break;
+		}
+	}
+	for (j = i, s = ss->symbol+j; j < ss->used; ++j, ++s)
+		free(s->name);
+	ss->used = i;
+	if (debug > 1)
+		fprintf(stderr, "after %d\n", ss->used);
+}
+
+static int ss_compare_atn(const void *a, const void *b)
+{
+	SYMBOL *c = (SYMBOL *) a;
+	SYMBOL *d = (SYMBOL *) b;
+	int i;
+
+	/* obsolete symbols to the top */
+	if (c->keep != d->keep)
+		return(d->keep - c->keep);
+	if (c->address > d->address)
+		return(1);
+	if (c->address < d->address)
+		return(-1);
+	if (c->type > d->type)
+		return(1);
+	if (c->type < d->type)
+		return(-1);
+	if ((i = strcmp(c->name, d->name)))
+		return(i);
+	return(0);
+}
+
+/* Sort a symbol set by address, type and name */
+void ss_sort_atn(SYMBOL_SET *ss)
+{
+	if (debug)
+		fprintf(stderr, "DEBUG: sorting symbols for %s (atn)\n",
+			ss->source);
+	qsort((char *) ss->symbol, (unsigned) ss->used,
+		sizeof(*(ss->symbol)), ss_compare_atn);
+	ss_compress(ss);
+}
+
+static int ss_compare_na(const void *a, const void *b)
+{
+	SYMBOL *c = (SYMBOL *) a;
+	SYMBOL *d = (SYMBOL *) b;
+	int i;
+
+	/* obsolete symbols to the top */
+	if (c->keep != d->keep)
+		return(d->keep - c->keep);
+	if ((i = strcmp(c->name, d->name)))
+		return(i);
+	if (c->address > d->address)
+		return(1);
+	if (c->address < d->address)
+		return(-1);
+	return(0);
+}
+
+/* Sort a symbol set by name and address, drop duplicates.  There should be
+ * no duplicates but I have seen duplicates in ksyms on 2.0.35.
+ */
+void ss_sort_na(SYMBOL_SET *ss)
+{
+	int i;
+	SYMBOL *s;
+	if (debug)
+		fprintf(stderr, "DEBUG: sorting symbols for %s (na)\n",
+			ss->source);
+	qsort((char *) ss->symbol, (unsigned) ss->used,
+		sizeof(*(ss->symbol)), ss_compare_na);
+	ss_compress(ss);
+	s = ss->symbol;
+	for (i = 0; i < ss->used-1; ++i) {
+		if (strcmp(s->name, (s+1)->name) == 0 &&
+		    s->address == (s+1)->address) {
+			if (s->type != ' ')
+				(s+1)->keep = 0;
+			else
+				s->keep = 0;
+		}
+		++s;
+	}
+	qsort((char *) ss->symbol, (unsigned) ss->used,
+		sizeof(*(ss->symbol)), ss_compare_na);
+	ss_compress(ss);
+}
+
+/* Copy a symbol set, including all its strings */
+SYMBOL_SET *ss_copy(const SYMBOL_SET *ss)
+{
+	SYMBOL_SET *ssc;
+	if (debug > 3)
+		fprintf(stderr,
+			"DEBUG: ss_copy %s\n", ss->source);
+	ssc = malloc(sizeof(*ssc));
+	if (!ssc)
+		malloc_error("copy ssc");
+	ss_init(ssc, ss->source);
+	ssc->used = ss->used;
+	ssc->alloc = ss->used;	/* shrink the copy */
+	ssc->symbol = malloc(ssc->used*sizeof(*(ssc->symbol)));
+	if (!(ssc->symbol))
+		malloc_error("copy ssc symbols");
+	memcpy(ssc->symbol, ss->symbol, ssc->used*sizeof(*(ssc->symbol)));
+	return(ssc);
+}
+
+/* Convert version number to major, minor string.  */
+static const char *format_Version(elf_addr_t Version)
+{
+	static char string[12];	/* 255.255.255\0 worst case */
+	snprintf(string, sizeof(string), "%d.%d.%d",
+		(int) ((Version >> 16) & 0xff),
+		(int) ((Version >> 8) & 0xff),
+		(int) ((Version) & 0xff));
+	return(string);
+}
+
+/* Save version number.  The "address" is the version number, the "symbol" is
+ * the source of the version.
+ */
+void add_Version(const char *version, const char *source)
+{
+	static char const procname[] = "add_Version";
+	int i = atoi(version);
+	if (debug > 1)
+		fprintf(stderr, "DEBUG: %s %s %s %s\n",
+			procname, source, version, format_Version(i));
+	add_symbol_n(&ss_Version, i, 'V', 1, source);
+}
+
+/* Extract Version_ number from a symbol set and save it.  */
+void extract_Version(SYMBOL_SET *ss)
+{
+	int i = 0;
+	SYMBOL *s;
+
+	s = find_symbol_name(ss, "Version_", &i);
+	if (!s && i < ss->used)
+		s = ss->symbol+i;	/* first symbol after "Version_" */
+	if (!s || strncmp(s->name, "Version_", 8))
+		return;
+	add_Version(s->name+8, ss->source);
+}
+
+/* Compare all extracted Version numbers.  Silent unless there is a problem. */
+void compare_Version(void)
+{
+	int i = 0;
+	SYMBOL *s, *s0;
+	static int prev_used = 0;
+
+	if (!ss_Version.used)
+		return;
+	/* Only check if the Version table has changed in size */
+	if (prev_used == ss_Version.used)
+		return;
+
+	ss_sort_na(&ss_Version);
+	s0 = s = ss_Version.symbol;
+	if (debug)
+		fprintf(stderr, "DEBUG: Version %s\n",
+			format_Version(s0->address));
+	for (i = 0; i < ss_Version.used; ++i, ++s) {
+		if (s->address != s0->address) {
+			fprintf(stderr,
+				"Version mismatch error.  %s says %s, ",
+				s0->name,
+				format_Version(s0->address));
+			fprintf(stderr,
+				"%s says %s.  Expect lots of address "
+				"mismatches.\n",
+				s->name,
+				format_Version(s->address));
+			++errors;
+		}
+	}
+	prev_used = ss_Version.used;
+}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/Makefile linux/scripts/ksymoops-0.6/Makefile
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/Makefile	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/Makefile	Wed Dec 31 16:00:00 1969
@@ -1,72 +0,0 @@
-# Description file for ksymoops
-
-#	Tue Nov  3 02:31:01 EST 1998
-#	Version 0.6
-#	Read lsmod (/proc/modules).
-#	Add Makefile defaults for vmlinux, ksyms, objects, System.map, lsmod.
-#	Upper case variables.
-#	Convert from a.out to bfd, using same format as ksymoops.
-
-DEFS = Makefile ksymoops.h
-
-# Defaults for vmlinux, ksyms, objects, lsmod, System.map.  Externalised so
-# distributions can tweak to suit their own file system layout.
-
-# To default to not reading a source, set to any empty string.
-# To default to reading a source, supply a quoted and escaped string.
-
-# If the string contains *r (*m, *n, *s) then it is replaced at run time by
-# the current value of `uname -r` (-m, -n, -s).  '*' was chosen as something
-# that rarely appears in filenames and does not cause problems like '%' or '$'.
-
-DEF_VMLINUX =				# default no vmlinux
-DEF_OBJECTS = \"/lib/modules/*r/\"	# default current modules
-DEF_KSYMS = \"/proc/ksyms\"		# default current ksyms
-DEF_LSMOD = \"/proc/modules\"		# default current lsmod
-DEF_MAP = \"/usr/src/linux/System.map\"	# default current map
-
-# RedHat users might want defaults like these
-# DEF_MAP = \"/boot/System.map-*r\"
-# DEF_OBJECTS = \"/boot/module-info-*r\"
-
-PROGS = ksymoops
-
-CC=gcc
-CFLAGS = -Dlinux \
-	 -Wall \
-	 -Wno-conversion \
-	 -Waggregate-return \
-	 -Wstrict-prototypes \
-	 -Wmissing-prototypes \
-	 $(DEBUG)
-
-ifneq ($(strip $(DEF_VMLINUX)),)
-	CFLAGS += -DDEF_VMLINUX=$(strip $(DEF_VMLINUX))
-endif
-ifneq ($(strip $(DEF_OBJECTS)),)
-	CFLAGS += -DDEF_OBJECTS=$(strip $(DEF_OBJECTS))
-endif
-ifneq ($(strip $(DEF_KSYMS)),)
-	CFLAGS += -DDEF_KSYMS=$(strip $(DEF_KSYMS))
-endif
-ifneq ($(strip $(DEF_LSMOD)),)
-	CFLAGS += -DDEF_LSMOD=$(strip $(DEF_LSMOD))
-endif
-ifneq ($(strip $(DEF_MAP)),)
-	CFLAGS += -DDEF_MAP=$(strip $(DEF_MAP))
-endif
-
-OBJECTS = io.o ksyms.o ksymoops.o map.o misc.o object.o oops.o re.o symbol.o
-
-all: 	$(PROGS)
-
-:	$(OBJECTS)
-
-$(OBJECTS): $(DEFS)
-
-$(PROGS): %: %.o $(DEFS) $(OBJECTS)
-	$(CC) $(OBJECTS) $(CFLAGS) -lbfd -liberty -o $@
-	-@size $@
-
-clean:
-	rm -f core *.o $(PROGS)
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/README linux/scripts/ksymoops-0.6/README
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/README	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/README	Wed Dec 31 16:00:00 1969
@@ -1,358 +0,0 @@
-  ksymoops.
-
-  Read a kernel Oops file and make the best stab at converting the code to
-  instructions and mapping stack values to kernel symbols.
-
-  Copyright Keith Owens <kaos@ocs.com.au>.
-  Released under the GNU Public Licence, Version 2.
-
-  To compile, simply type "make" in the ksymoops directory.
-
-  TESTERS WANTED.
-
-  ksymoops handles ix86.  It appears to handle Alpha, Sparc, M68K, PPC,
-  MIPS but I have no machine to test on.  I would appreciate feedback
-  from users of non ix86 machines.  In particular, it would be nice if
-  you could run
-
-   ksymoops -VMO -k /proc/ksyms -dd <oops.file >/tmp/ksymoops.log 2>&1
-
-  and mail /tmp/ksymoops.log to kaos@ocs.com.au
-
-  The patches subdirectory contains some arch specific patches to
-  provide more info on their Oops reports.  At the moment (2.1.126),
-  some archs do not print traces or do not print code lines, makes it
-  impossible to report properly.
-
-  TODO:
-  Performance improvements.  Reading a large log is quite slow, probably
-	  one of the Oops regular expressions is pathological.
-  Clean up these docs.
-  Add "guess", "same" options, distinguish between default and supplied
-  values on report (Andries, I get the message :).
-
-  Tue Nov  3 02:31:01 EST 1998
-  Version 0.6
-  Read lsmod (/proc/modules).
-
-  Wed Oct 28 23:14:55 EST 1998
-  Version 0.5
-  No longer read vmlinux by default, it only duplicates System.map.
-
-  Wed Oct 28 13:46:39 EST 1998
-  Version 0.4
-  Split into separate sources.
-
-  Mon Oct 26 00:01:47 EST 1998
-  Version 0.3c
-  Add alpha (arm) processing.
-
-  Mon Oct 26 00:01:47 EST 1998
-  Version 0.3b
-  Add sparc processing.
-  Handle kernel symbol versions.
-
-  Fri Oct 23 13:11:20 EST 1998
-  Version 0.3
-  Add -follow to find command for people who use symlinks to modules.
-  Add Version_ checking.
-
-  Thu Oct 22 22:28:30 EST 1998
-  Version 0.2.
-  Generalise text prefix handling.
-  Handle messages on Code: line.
-  Format addresses with leading zeroes.
-  Minor bug fixes.
-
-  Wed Oct 21 23:28:48 EST 1998
-  Version 0.1.  Rewrite from scratch in C.
-
-  CREDITS.
-  Oops disassembly based on ksymoops.cc,
-    Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
-  m68k code based on ksymoops.cc changes by
-    Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
-
-  This code subsumes the Perl script make_System.map.pl which is no longer
-  supported.
-
-  Why another ksymoops I hear you ask?  Various complaints about
-  ksymoops.cc -
-
-  * It requires C++.
-  * It has hard wired limitations on the number of symbols.
-  * It does not handle modules at all.
-  * Very rigid requirements on the format of input, especially the Oops
-    log.
-  * No cross checking between ksyms, modules, System.map etc.
-  * Very little error checking, diagnostics are not suitable for
-    beginners.
-  * It only prints the trace and decoded code, users have to manually
-    extract the other lines from the Oops.
-  * Gives up on the slightest problem.
-  * Only handles i386 and possibly m68k.  The code is difficult to extend
-    to other architectures.
-  * Stops after the first Oops, you have to manually extract each one and
-    run through ksymoops one at a time.
-
-  This version is -
-  * C.
-  * No hard wired limitations (malloc as far as the eye can see).
-  * Handles modules by default.
-  * Uses regular pattern matching so it is a lot more forgiving about
-    input formats.
-  * By default, cross checks ksyms, modules, System.map and vmlinux.
-  * Lots of diagnostics and error checking.
-  * Prints all relevant lines for a complete Oops report.
-  * Tries to provide output no matter how bad the input is.  The level of
-     progress and error reporting is aimed at beginners.
-  * Handles i386, alpha, sparc, m68k.  It is a lot easier to extend to
-    other architectures (patches and/or sample data gratefully accepted).
-  * Handles all Oops in the input file(s).
-
-
-  Usage:	ksymoops
-		  [-v vmlinux]	Where to read vmlinux
-		  [-V]		No vmlinux is available
-		  [-o object_dir]	Directory containing modules
-		  [-O]		No modules is available
-		  [-k ksyms]	Where to read ksyms
-		  [-K]		No ksyms is available
-		  [-l lsmod]	Where to read lsmod
-		  [-L]		No lsmod is available
-		  [-m system.map]	Where to read System.map
-		  [-M]		No System.map is available
-		  [-s save.map]	Save consolidated map
-		  [-d]		Increase debug level by 1
-		  [-h]		Print help text
-		  Oops.file	Oops to decode
-
-	  All flags can occur more than once.  With the exception of -o
-	  and -d which are cumulative, the last occurrence of each flag is
-	  used.  Note that "-v my.vmlinux -V" will be taken as "No vmlinux
-	  available" but "-V -v my.vmlinux" will read my.vmlinux.  You
-	  will be warned about such combinations.
-
-	  Each occurrence of -d increases the debug level.
-
-	  Each -o flag can refer to a directory or to a single object
-	  file.  If a directory is specified then all *.o files in that
-	  directory and its subdirectories are assumed to be modules.
-
-	  If any of the vmlinux, object_dir, ksyms or system.map options
-	  contain the string *r (*m, *n, *s) then it is replaced at run time
-	  by the current value of `uname -r` (-m, -n, -s).
-
-	  The defaults can be changed in the Makefile, typical options are
-
-	  Defaults:	  -V
-			  -o /lib/modules/%r
-			  -k /proc/ksyms
-			  -l /proc/modules
-			  -m /usr/src/linux/System.map
-			  Oops report is read from stdin
-
-  Note:	  Unless you tell ksymoops *NOT* to read a particular file, it
-	  will try to read and reconcile almost all possible sources of kernel
-	  symbol information.  This is intended for beginners, they just
-	  type
-
-	    ksymoops < /var/log/syslog
-
-	  no thinking required.  Experts can point at different files or
-	  suppress the input from selected files.  For example, if you
-	  save /proc/ksyms before doing a test that creates an Oops, you
-	  can point ksymoops at the saved ksyms instead of using
-	  /proc/ksyms.
-
-	  vmlinux is not read by default, it only duplicates the
-	  information in System.map.  If you want to read vmlinux as well
-	  as or instead of System.map, use -v.
-
-	  To get the equivalent of the old ksymoops.cc (no vmlinux, no
-	  modules objects, no ksyms, no System.map) just do ksymoops
-	  -VOKLM.  Or to just read System.map, ksymoops -VOKL -m mapfile.
-
-
-  WARNING:  The user interface will change slightly in 0.7, users will
-            have to give some indication of the state of their
-            environment.  Otherwise it may be too easy to pick the
-            wrong input files.
-
-  Return codes:	0 - normal.
-		  1 - error(s) or warning(s) issued, results may not be
-		      reliable.
-		  2 - fatal error, no useful results.
-
-  Supported architectures
-
-	  i386 tested.
-          m68k code derived from ksymoops.cc and reading traps.c, untested.
-	  MIPS tested.
-	  Sparc tested.
-	  Alpha tested.
-
-	  The term "eip" is generic, for example it includes the i386 EIP
-	  and the m68k PC.  Remember that objdump output always says EIP,
-	  no matter what the architecture, see objfile_head.
-
-	  To support another arch, check the Oops_ procedures between
-	  'Start architecture sensitive code' and 'End architecture
-	  sensitive code'.
-
-	  The pattern matching should take care of different lengths for
-	  the address, i.e. addresses should not be arch sensitive.  I
-	  assume that all addresses are at least 4 characters.
-
-	  If nm output has a different format on your arch, check for uses
-	  of re_nm.
-
-
-
-  Because ksymoops reads kernel information from multiple sources, there
-  could be mismatches.  ksymoops does the following cross checks, but only
-  if the specified files exist -
-
-  * Compare Version_nnn numbers from all sources against each other.  Pity
-    that only vmlinux and System.map have these symbols (as at 2.1.125),
-    however I check ksyms, modules and Oops as well.  If somebody adds
-    symbol Version_nnn to ksyms or modules or adds a Version_nnn line to
-    the Oops log, this code is ready.
-
-  * Compare kernel ksyms against vmlinux.  vmlinux takes precedence.
-
-  * Compare System.map against vmlinux.   vmlinux takes precedence.
-
-  * Compare vmlinux against System.map.   vmlinux takes precedence.
-
-  * Compare kernel ksyms against System.map.  System.map takes precedence.
-
-  * Compare modules against module ksyms.  modules take precedence.  Only
-    if at least one module appears in ksyms.
-
-  * Compare module names in ksyms against lsmod.  Warn if a module
-    appears in lsmod but not in ksyms.  Error if a modules appears in
-    ksyms but is not in lsmod.  Only if both ksyms and lsmod have being
-    read.
-
-  The precedence order is somewhat arbitrary, however it only applies if
-  there is any difference between the various sources.
-
-  Handling modules is awkward.  They can be loaded under different names
-  (insmod -o dummy1 dummy.o) and the text, data and read only data are
-  loaded at different offsets.  Although you can give the -m option to
-  insmod which will output the module map when it is loaded, this has a
-  few problems -
-
-  * No equivalent for removing a module.  If you load and remove a lot of
-    modules, you end up with multiple sets of symbols around the same
-    offsets, which set is correct?
-
-  * "insmod -o dummy1 dummy.o" still reports as dummy.  That is, there is
-     no way of telling which particular version of a multiply loaded
-     module the insmod output refers to.  Therefore there is no way of
-     telling which instantiation failed.
-
-  * Even if the above problems are fixed, how do you tell what the module
-    environment looked like when the Oops occurred?  What if a module is
-    loaded or removed just after Oops, how is the user expected to edit
-    the insmod log?  Rule 1 - make ksymoops easy for beginners.
-
-  Although those problems could be fixed, they require changes to
-  modutils.  Working from ksyms and the module objects can be done without
-  changing modutils and without confusing beginners.
-  
-  Alas the ksyms plus object approach has another problem - matching ksyms
-  to module objects.  Nowhere does the kernel say that module dummy1 came
-  from module /lib/modules/2.1.215/net/dummy.o, ksyms just says dummy1.  I
-  have to match ksyms to the relevant object by finding a globally unique
-  external symbol in each module that can be used to map to the external
-  symbols in ksyms.  This assumes that each module exports at least one
-  text symbol that is unique amongst all modules.
-
-  It may not be possible to correctly map other sections such as data and
-  readonly data for modules because they may not have exported symbols.
-  Since the main aim of ksymoops is to map a code Oops, this should not be
-  a problem.
-
-  Unfortunately some modules export no symbols.  They are marked as
-  EXPORT_NO_SYMBOLS are simply do not export anything.  It is
-  impossible to detect these in ksyms because, by definition, ksyms
-  only contains exported symbols for modules.  Since all modules appear
-  in lsmod (/proc/modules), a cross check of lsmod against the module
-  names will find loaded modules with no symbols, at least I can warn
-  about these.
-
-  After merging the various sources, ksymoops has a (hopefully) accurate
-  map including modules.  The -s option lets you save the merged
-  System.map, but remember that module data and readonly data sections may
-  not be correctly relocated, see above.
-
-  Environment Variables.
-  KSYMOOPS_NM		path for nm, defaults to /usr/bin/nm.
-  KSYMOOPS_FIND		path for find, defaults to /usr/bin/find.
-  KSYMOOPS_OBJDUMP	path for objdump, defaults to /usr/bin/objdump.
-
-
-  Input Oops data.
-
-  The ideal input is to feed the syslog straight into this program.  If
-  you cannot do that, you need to know what the program looks for.
-  Especially if you are typing in the Oops by hand :(.  All input is case
-  insensitive.
-
-  * White space in this context means space or tab.  It does not include
-    newline.
-
-  * Oops in syslog has a syslog prefix.  Leading text up to and including
-    ' kernel: ' is always ignored, there is no need to edit syslog first.
-    This leading text need not exist but if it does, it must end in
-    ' kernel: '.
-
-  * An alternative prefix is <n> where n is the kernel print level.  Also
-    ignored if present.
-
-  * Leading white space is treated as a prefix and ignored, the input is
-    not indentation sensitive.
-
-  * In the following paragraphs, assume that any prefixes have been
-    skipped.  If there is more than one prefix, all are skipped, no matter
-    which order they appear in.
-
-  * A bracketed address is optional '[', required '<', at least 4 hex
-    digits, required '>', optional ']'.  For example [<01234567>] or
-    <1234>.
-
-  * The ix86 EIP line is identified by optional white space followed by
-    'EIP:', followed by a least one white space, followed by a bracketed
-    address.
-
-  * The m68k PC line is identified by optional white space followed by
-    'PC', optionally followed by white space, followed by '=', optionally
-    followed by white space, followed by a bracketed address.
-
-  * The sparc PC line starts with PSR and PC is the second hex value, not
-    bracketed.
-
-  * A call trace line is identified by 'Call Trace:' followed by at least
-    one white space.  Or it is a line starting with a bracketed address,
-    but only if the previous line was a call trace line (I hate multi line
-    output that relies on identation for recognition, especially when
-    lines can have a variable prefix).
-
-  * The Code line is identified by 'Code:' followed by a least one white
-    space character followed by at least one hex value.  The line can
-    contain multiple hex values, each separated by at least one white
-    space.  Each hex value must be 2 to 8 digits and must be a multiple of
-    2 digits.
-
-    Special cases where Code: can be followed by text.
-      'Code: general protection'
-      'Code: <n>'
-    Dump the data anyway, the code was unavailable.
-
-  * Formatted data is only output when the Code: line is seen.  If any
-    data has been stored and more than 5 lines other than Oops text (see
-    Oops_print) or end of file are encountered then ksymoops assumes that
-    the Code: line is missing or garbled and dumps the formatted data
-    anyway.  Fail safe, I hope.
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/io.c linux/scripts/ksymoops-0.6/io.c
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/io.c	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/io.c	Wed Dec 31 16:00:00 1969
@@ -1,139 +0,0 @@
-/*
-	io.c.
-
-	Local I/O routines for ksymoops.
-
-	Copyright Keith Owens <kaos@ocs.com.au>.
-	Released under the GNU Public Licence, Version 2.
-
-	Tue Nov  3 02:31:01 EST 1998
-	Version 0.6
-	fwrite_local is redundant, replaced by bfd.
-
-	Wed Oct 28 13:47:23 EST 1998
-	Version 0.4
-	Split into separate sources.
-
- */
-
-#include "ksymoops.h"
-#include <errno.h>
-#include <malloc.h>
-#include <string.h>
-#include <sys/stat.h>
-
-int regular_file(const char *file, const char *msg)
-{
-	struct stat statbuf;
-	if (stat(file, &statbuf)) {
-		fprintf(stderr, "%s: %s stat %s failed",
-			prefix, msg, file);
-		perror(" ");
-		++errors;
-		return 0;
-	}
-
-	if (!S_ISREG(statbuf.st_mode)) {
-		fprintf(stderr,
-			"%s: %s %s is not a regular file, ignored\n",
-			prefix, msg, file);
-		++errors;
-		return 0;
-	}
-	return 1;
-}
-
-FILE *fopen_local(const char *file, const char *mode, const char *msg)
-{
-	FILE *f;
-	if (!(f = fopen(file, mode))) {
-		fprintf(stderr, "%s: %s fopen '%s' failed",
-			prefix, msg, file);
-		perror(" ");
-		++errors;
-	}
-	return f;
-}
-
-void fclose_local(FILE *f, const char *msg)
-{
-	int i;
-	if ((i = fclose(f))) {
-		fprintf(stderr, "%s: %s fclose failed %d", prefix, msg, i);
-		perror(" ");
-		++errors;
-	}
-}
-
-/* Read a line, increasing the size of the line as necessary until \n is read */
-#define INCREMENT 10	/* arbitrary */
-char *fgets_local(char **line, int *size, FILE *f, const char *msg)
-{
-	char *l, *p, *r;
-	int longline = 1;
-
-	if (!*line) {
-		*size = INCREMENT;
-		*line = malloc(*size);
-		if (!*line)
-			malloc_error("fgets_local alloc line");
-	}
-
-	l = *line;
-	while (longline) {
-		r = fgets(l, *size-(l-*line), f);
-		if (!r) {
-			if (ferror(f)) {
-				fprintf(stderr,
-					"%s: %s fgets failed", prefix, msg);
-				perror(" ");
-				++errors;
-			}
-			if (l != *line)
-				return(*line);
-			else
-				return(r);
-		}
-		if (!(p = strchr(*line, '\n'))) {
-			*size += INCREMENT;
-			*line = realloc(*line, *size);
-			if (!*line)
-				malloc_error("fgets_local realloc line");
-			l = *line+*size-INCREMENT-1;
-		}
-		else {
-			*p = '\0';
-			longline = 0;
-		}
-	}
-
-	if (debug > 3)
-		fprintf(stderr, "DEBUG: %s line '%s'\n", msg, *line);
-	return(*line);
-}
-
-FILE *popen_local(const char *cmd, const char *msg)
-{
-	FILE *f;
-	if (!(f = popen(cmd, "r"))) {
-		fprintf(stderr, "%s: %s popen '%s' failed",
-			prefix, msg, cmd);
-		perror(" ");
-		++errors;
-	}
-	return f;
-}
-
-void pclose_local(FILE *f, const char *msg)
-{
-	int i;
-	errno = 0;
-	if ((i = pclose(f))) {
-		fprintf(stderr, "%s: %s pclose failed 0x%x", prefix, msg, i);
-		if (errno)
-			perror(" ");
-		else
-			fprintf(stderr, "\n");
-		++errors;
-	}
-}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/ksymoops.c linux/scripts/ksymoops-0.6/ksymoops.c
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/ksymoops.c	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/ksymoops.c	Wed Dec 31 16:00:00 1969
@@ -1,569 +0,0 @@
-/*
-	ksymoops.c.
-
-	Read a kernel Oops file and make the best stab at converting the code to
-	instructions and mapping stack values to kernel symbols.
-
-	Copyright Keith Owens <kaos@ocs.com.au>.
-	Released under the GNU Public Licence, Version 2.
-*/
-
-#define VERSION "0.6"
-
-/*
-
-	Tue Nov  3 02:31:01 EST 1998
-	Version 0.6
-	Read lsmod (/proc/modules).
-	Ignore addresses 0-4095 when mapping address to symbol.
-	Discard default objects if -o specified.
-	Oops file must be regular.
-	Add "invalid operand" to Oops_print.
-	Move "Using_Version" copy to map.c.
-	Add Makefile defaults for vmlinux, ksyms, objects, System.map, lsmod.
-	Minor adjustment to re for ppc.
-	Minor adjustment to re for objdump lines with <_EIP+xxx>.
-	Convert from a.out to bfd, using same format as ksymoops.
-	Added MIPS.
-	PPC handling based on patches by "Ryan Nielsen" <ran@krazynet.com>
-
-	Wed Oct 28 23:14:55 EST 1998
-	Version 0.5
-	No longer read vmlinux by default, it only duplicates System.map.
-
-	Wed Oct 28 13:47:38 EST 1998
-	Version 0.4
-	Split into separate sources.
-
-	Mon Oct 26 00:01:47 EST 1998
-	Version 0.3c
-	Add alpha (arm) processing.
-
-	Mon Oct 26 00:01:47 EST 1998
-	Version 0.3b
-	Add sparc processing.
-	Handle kernel symbol versions.
-
-	Fri Oct 23 13:11:20 EST 1998
-	Version 0.3
-	Add -follow to find command for people who use symlinks to modules.
-	Add Version_ checking.
-
-	Thu Oct 22 22:28:30 EST 1998
-	Version 0.2.
-	Generalise text prefix handling.
-	Handle messages on Code: line.
-	Format addresses with leading zeroes.
-	Minor bug fixes.
-
-	Wed Oct 21 23:28:48 EST 1998
-	Version 0.1.  Rewrite from scratch in C.
-
-	CREDITS.
-	Oops disassembly based on ksymoops.cc,
-	  Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
-	m68k code based on ksymoops.cc changes by
-	  Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
- */
-
-#include "ksymoops.h"
-#include <ctype.h>
-#include <malloc.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/utsname.h>
-
-char *prefix;
-char *path_nm = "/usr/bin/nm";			/* env KSYMOOPS_NM */
-char *path_find = "/usr/bin/find";		/* env KSYMOOPS_FIND */
-char *path_objdump = "/usr/bin/objdump";	/* env KSYMOOPS_OBJDUMP */
-int debug = 0;
-int errors = 0;
-int warnings = 0;
-
-SYMBOL_SET  ss_vmlinux;
-SYMBOL_SET  ss_ksyms_base;
-SYMBOL_SET *ss_ksyms_module;
-int         ss_ksyms_modules;
-SYMBOL_SET  ss_lsmod;
-SYMBOL_SET *ss_object;
-int         ss_objects;
-SYMBOL_SET  ss_system_map;
-
-SYMBOL_SET  ss_merged;   /* merged map with info from all sources */
-SYMBOL_SET  ss_Version;  /* Version_ numbers where available */
-
-/* Regular expression stuff */
-
-regex_t     re_nm;
-regmatch_t *re_nm_pmatch;
-regex_t     re_bracketed_address;
-regmatch_t *re_bracketed_address_pmatch;
-regex_t     re_unbracketed_address;
-regmatch_t *re_unbracketed_address_pmatch;
-
-static void usage(void)
-{
-	fprintf(stderr, "Version " VERSION "\n");
-	fprintf(stderr, "usage: %s\n", prefix);
-	fprintf(stderr,
-		"\t\t[-v vmlinux]\tWhere to read vmlinux\n"
-		"\t\t[-V]\t\tNo vmlinux is available\n"
-		"\t\t[-o object_dir]\tDirectory containing modules\n"
-		"\t\t[-O]\t\tNo modules is available\n"
-		"\t\t[-k ksyms]\tWhere to read ksyms\n"
-		"\t\t[-K]\t\tNo ksyms is available\n"
-		"\t\t[-l lsmod]\tWhere to read lsmod\n"
-		"\t\t[-L]\t\tNo lsmod is available\n"
-		"\t\t[-m system.map]\tWhere to read System.map\n"
-		"\t\t[-M]\t\tNo System.map is available\n"
-		"\t\t[-s save.map]\tSave consolidated map\n"
-		"\t\t[-d]\t\tIncrease debug level by 1\n"
-		"\t\t[-h]\t\tPrint help text\n"
-		"\t\t<Oops.file\tOops report to decode\n"
-		"\n"
-		"\t\tAll flags can occur more than once.  With the exception "
-			"of -o\n"
-		"\t\tand -d which are cumulative, the last occurrence of each "
-			"flag is\n"
-		"\t\tused.  Note that \"-v my.vmlinux -V\" will be taken as "
-			"\"No vmlinux\n"
-		"\t\tavailable\" but \"-V -v my.vmlinux\" will read "
-			"my.vmlinux.  You\n"
-		"\t\twill be warned about such combinations.\n"
-		"\n"
-		"\t\tEach occurrence of -d increases the debug level.\n"
-		"\n"
-		"\t\tEach -o flag can refer to a directory or to a single "
-			"object\n"
-		"\t\tfile.  If a directory is specified then all *.o files in "
-			"that\n"
-		"\t\tdirectory and its subdirectories are assumed to be "
-			"modules.\n"
-		"\n"
-		"\t\tIf any of the vmlinux, object_dir, ksyms or system.map "
-		"options\n"
-		"\t\tcontain the string *r (*m, *n, *s) then it is replaced "
-		"at run\n"
-		"\t\ttime by the current value of `uname -r` (-m, -n, -s).\n"
-		"\n"
-		"\t\tThe defaults can be changed in the Makefile, current "
-		"defaults\n"
-		"\t\tare\n\n"
-		"\t\t\t"
-#ifdef DEF_VMLINUX
-		"-v " DEF_LINUX
-#else
-		"-V"
-#endif
-		"\n"
-		"\t\t\t"
-#ifdef DEF_OBJECTS
-		"-o " DEF_OBJECTS
-#else
-		"-O"
-#endif
-		"\n"
-		"\t\t\t"
-#ifdef DEF_KSYMS
-		"-k " DEF_KSYMS
-#else
-		"-K"
-#endif
-		"\n"
-		"\t\t\t"
-#ifdef DEF_LSMOD
-		"-l " DEF_LSMOD
-#else
-		"-L"
-#endif
-		"\n"
-		"\t\t\t"
-#ifdef DEF_MAP
-		"-m " DEF_MAP
-#else
-		"-M"
-#endif
-		"\n"
-		"\t\t\tOops report is read from stdin\n"
-		"\n"
-	       );
-}
-
-/* Check if possibly conflicting options were specified */
-static void multi_opt(int specl, int specu, char type, const char *using)
-{
-	if (specl && specu) {
-		fprintf(stderr,
-			"Warning - you specified both -%c and -%c.  Using '",
-			type, toupper(type));
-		++warnings;
-		if (using) {
-			fprintf(stderr, "-%c %s", type, using);
-			if (type == 'o')
-				fprintf(stderr, " ...");
-			fprintf(stderr, "'\n");
-		}
-		else
-			fprintf(stderr, "-%c'\n", toupper(type));
-	}
-	else if (specl > 1 && type != 'o') {
-		fprintf(stderr,
-			"Warning - you specified -%c more than once.  "
-			"Using '-%c %s'\n",
-			type, type, using);
-		++warnings;
-	}
-	else if (specu > 1) {
-		fprintf(stderr,
-			"Warning - you specified -%c more than once.  "
-			"Second and subsequent '-%c' ignored\n",
-			toupper(type), toupper(type));
-		++warnings;
-	}
-}
-
-/* If a name contains *r (*m, *n, *s), replace with the current value of
- * `uname -r` (-m, -n, -s).  Actually uses uname system call rather than the
- * uname command but the result is the same.
- */
-static void convert_uname(char **name)
-{
-	char *p, *newname, *oldname, *replacement;
-	unsigned len;
-	int free_oldname = 0;
-	static char procname[] = "convert_uname";
-
-	if (!*name)
-		return;
-
-	while ((p = strchr(*name, '*'))) {
-		struct utsname buf;
-		int i = uname(&buf);
-		if (debug)
-			fprintf(stderr, "DEBUG: %s %s in\n", procname, *name);
-		if (i) {
-			fprintf(stderr,
-				"%s: uname failed, %s will not be processed\n",
-				prefix, *name);
-			perror(prefix);
-			++errors;
-			return;
-		}
-		switch (*(p+1)) {
-		case 'r':
-			replacement = buf.release;
-			break;
-		case 'm':
-			replacement = buf.machine;
-			break;
-		case 'n':
-			replacement = buf.nodename;
-			break;
-		case 's':
-			replacement = buf.sysname;
-			break;
-		default:
-			fprintf(stderr,
-				"%s: invalid replacement character '*%c' "
-				"in %s\n",
-				prefix, *(p+1), *name);
-			++errors;
-			return;
-		}
-		len = strlen(*name)-2+strlen(replacement)+1;
-		if (!(newname = malloc(len)))
-			malloc_error(procname);
-		strncpy(newname, *name, (p-*name));
-		strcpy(newname+(p-*name), replacement);
-		strcpy(newname+(p-*name)+strlen(replacement), p+2);
-		p = newname+(p-*name)+strlen(replacement);	/* no rescan */
-		oldname = *name;
-		*name = newname;
-		if (free_oldname)
-			free(oldname);
-		free_oldname = 1;
-		if (debug)
-			fprintf(stderr, "DEBUG: %s %s out\n", procname, *name);
-	}
-	return;
-}
-
-/* Parse the options.  Verbose but what's new with getopt? */
-static void parse(int argc,
-		  char **argv,
-		  char **vmlinux,
-		  char ***object,
-		  int *objects,
-		  char **ksyms,
-		  char **lsmod,
-		  char **system_map,
-		  char **save_system_map,
-		  char ***filename,
-		  int *filecount,
-		  int *spec_h
-		 )
-{
-	int spec_v = 0, spec_V = 0;
-	int spec_o = 0, spec_O = 0;
-	int spec_k = 0, spec_K = 0;
-	int spec_l = 0, spec_L = 0;
-	int spec_m = 0, spec_M = 0;
-	int spec_s = 0;
-
-	int c, i;
-	char *p;
-
-	while ((c = getopt(argc, argv, "v:Vo:Ok:Kl:Lm:Ms:dh")) != EOF) {
-		if (debug && c != 'd')
-			fprintf(stderr, "DEBUG: getopt '%c' '%s'\n", c, optarg);
-		switch(c) {
-		case 'v':
-			*vmlinux = optarg;
-			++spec_v;
-			break;
-		case 'V':
-			*vmlinux = NULL;
-			++spec_V;
-			break;
-		case 'o':
-			if (!spec_o) {
-				/* First -o, discard default value(s) */
-				for (i = 0; i < *objects; ++i)
-					free((*object)[i]);
-				free(*object);
-				*object = NULL;
-				*objects = 0;
-			}
-			*object = realloc(*object,
-				((*objects)+1)*sizeof(**object));
-			if (!*object)
-				malloc_error("object");
-			if (!(p = strdup(optarg)))
-				malloc_error("strdup -o");
-			else {
-				(*object)[(*objects)++] = p;
-				++spec_o;
-			}
-			break;
-		case 'O':
-			++spec_O;
-			for (i = 0; i < *objects; ++i)
-				free((*object)[i]);
-			free(*object);
-			*object = NULL;
-			*objects = 0;
-			break;
-		case 'k':
-			*ksyms = optarg;
-			++spec_k;
-			break;
-		case 'K':
-			*ksyms = NULL;
-			++spec_K;
-			break;
-		case 'l':
-			*lsmod = optarg;
-			++spec_l;
-			break;
-		case 'L':
-			*lsmod = NULL;
-			++spec_L;
-			break;
-		case 'm':
-			*system_map = optarg;
-			++spec_m;
-			break;
-		case 'M':
-			*system_map = NULL;
-			++spec_M;
-			break;
-		case 's':
-			*save_system_map = optarg;
-			++spec_s;
-			break;
-		case 'd':
-			++debug;
-			break;
-		case 'h':
-			usage();
-			++*spec_h;
-			break;
-		case '?':
-			usage();
-			exit(2);
-		}
-	}
-
-	*filecount = argc - optind;
-	*filename = argv + optind;
-
-	/* Expand any requests for the current uname values */
-	convert_uname(vmlinux);
-	if (*objects) {
-		for (i = 0; i < *objects; ++i)
-			convert_uname(*object+i);
-	}
-	convert_uname(ksyms);
-	convert_uname(lsmod);
-	convert_uname(system_map);
-
-	/* Check for multiple options specified */
-	multi_opt(spec_v, spec_V, 'v', *vmlinux);
-	multi_opt(spec_o, spec_O, 'o', *object ? **object : NULL);
-	multi_opt(spec_k, spec_K, 'k', *ksyms);
-	multi_opt(spec_l, spec_L, 'l', *lsmod);
-	multi_opt(spec_m, spec_M, 'm', *system_map);
-
-	printf("Options used:");
-	if (*vmlinux)
-		printf(" -v %s", *vmlinux);
-	else
-		printf(" -V");
-	if (*objects) {
-		for (i = 0; i < *objects; ++i)
-			printf(" -o %s", (*object)[i]);
-	}
-	else
-		printf(" -O");
-	if (*ksyms)
-		printf(" -k %s", *ksyms);
-	else
-		printf(" -K");
-	if (*lsmod)
-		printf(" -l %s", *lsmod);
-	else
-		printf(" -L");
-	if (*system_map)
-		printf(" -m %s", *system_map);
-	else
-		printf(" -M");
-	printf("\n\n");
-}
-
-/* Read environment variables */
-static void read_env(const char *external, char **internal)
-{
-	char *p;
-	if ((p = getenv(external))) {
-		*internal = p;
-		if (debug)
-			fprintf(stderr,
-				"DEBUG: env override %s=%s\n",
-				external, *internal);
-	}
-	else {
-		if (debug)
-			fprintf(stderr,
-				"DEBUG: env default %s=%s\n",
-				external, *internal);
-	}
-}
-
-
-int main(int argc, char **argv)
-{
-	char *vmlinux = NULL;
-	char **object = NULL;
-	int objects = 0;
-	char *ksyms = NULL;
-	char *lsmod = NULL;
-	char *system_map = NULL;
-	char *save_system_map = NULL;
-	char **filename;
-	int filecount = 0;
-	int spec_h = 0;		/* -h was specified */
-	int i;
-
-	prefix = *argv;
-	setvbuf(stdout, NULL, _IONBF, 0);
-
-#ifdef DEF_VMLINUX
-	vmlinux = DEF_LINUX;
-#endif
-#ifdef DEF_OBJECTS
-	{
-		char *p;
-		object = realloc(object, (objects+1)*sizeof(*object));
-		if (!object)
-			malloc_error("DEF_OBJECTS");
-		if (!(p = strdup(DEF_OBJECTS)))
-			malloc_error("DEF_OBJECTS");
-		else
-			object[objects++] = p;
-	}
-#endif
-#ifdef DEF_KSYMS
-	ksyms = DEF_KSYMS;
-#endif
-#ifdef DEF_LSMOD
-	lsmod = DEF_LSMOD;
-#endif
-#ifdef DEF_MAP
-	system_map = DEF_MAP;
-#endif
-
-	parse(argc,
-	      argv,
-	      &vmlinux,
-	      &object,
-	      &objects,
-	      &ksyms,
-	      &lsmod,
-	      &system_map,
-	      &save_system_map,
-	      &filename,
-	      &filecount,
-	      &spec_h
-	     );
-
-	if (spec_h && filecount == 0)
-		return(0);	/* just the help text */
-
-	if (debug)
-		fprintf(stderr, "DEBUG: level %d\n", debug);
-
-	read_env("KSYMOOPS_NM", &path_nm);
-	read_env("KSYMOOPS_FIND", &path_find);
-	read_env("KSYMOOPS_OBJDUMP", &path_objdump);
-
-	re_compile_common();
-	ss_init_common();
-
-	read_vmlinux(vmlinux);
-	read_ksyms(ksyms);
-	/* No point in reading modules unless ksyms shows modules loaded */
-	if (ss_ksyms_modules) {
-		expand_objects(object, objects);
-		for (i = 0; i < ss_objects; ++i)
-			read_object(ss_object[i].source, i);
-	}
-	else if (objects)
-		printf("No modules in ksyms, skipping objects\n");
-	/* No point in reading lsmod without ksyms */
-	if (ss_ksyms_modules || ss_ksyms_base.used)
-		read_lsmod(lsmod);
-	else if (lsmod)
-		printf("No ksyms, skipping lsmod\n");
-	read_system_map(system_map);
-	merge_maps(save_system_map);
-
-	/* After all that work, it is finally time to read the Oops report */
-	Oops_read(filecount, filename);
-
-	if (warnings || errors) {
-		printf("\n");
-		if (warnings)
-			printf("%d warning%s ",
-			       warnings, warnings == 1 ? "" : "s");
-		if (warnings && errors)
-			printf("and ");
-		if (errors)
-			printf("%d error%s ", errors, errors == 1 ? "" : "s");
-		printf("issued.  Results may not be reliable.\n");
-		return(1);
-	}
-
-	return(0);
-}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/ksymoops.h linux/scripts/ksymoops-0.6/ksymoops.h
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/ksymoops.h	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/ksymoops.h	Wed Dec 31 16:00:00 1969
@@ -1,145 +0,0 @@
-/*
-	ksymoops.h.
-
-	Copyright Keith Owens <kaos@ocs.com.au>.
-	Released under the GNU Public Licence, Version 2.
-
-	Tue Nov  3 02:31:01 EST 1998
-	Version 0.6
-	Read lsmod (/proc/modules).
-	Convert from a.out to bfd, using same format as ksymoops.
-	PPC trace addresses are not bracketed, add new re.
-
-	Wed Oct 28 13:47:23 EST 1998
-	Version 0.4
-	Split into separate sources.
-*/
-
-#include <sys/types.h>
-#include <regex.h>
-#include <stdio.h>
-
-
-/* Pity this is not externalised, see binfmt_elf.c */
-#define elf_addr_t unsigned long
-
-extern char *prefix;
-extern char *path_nm;		/* env KSYMOOPS_NM */
-extern char *path_find;		/* env KSYMOOPS_FIND */
-extern char *path_objdump;	/* env KSYMOOPS_OBJDUMP */
-extern int debug;
-extern int errors;
-extern int warnings;
-
-typedef struct symbol SYMBOL;
-
-struct symbol {
-	char *name;		/* name of symbol */
-	char type;		/* type of symbol from nm/System.map */
-	char keep;		/* keep this symbol in merged map? */
-	elf_addr_t address;	/* address in kernel */
-};
-
-/* Header for symbols from one particular source */
-
-typedef struct symbol_set SYMBOL_SET;
-
-struct symbol_set {
-	char *source;			/* where the symbols came from */
-	int used;			/* number of symbols used */
-	int alloc;			/* number of symbols allocated */
-	SYMBOL *symbol;			/* dynamic array of symbols */
-	SYMBOL_SET *related;		/* any related symbol set */
-};
-
-extern SYMBOL_SET  ss_vmlinux;
-extern SYMBOL_SET  ss_ksyms_base;
-extern SYMBOL_SET *ss_ksyms_module;
-extern int         ss_ksyms_modules;
-extern SYMBOL_SET  ss_lsmod;
-extern SYMBOL_SET *ss_object;
-extern int         ss_objects;
-extern SYMBOL_SET  ss_system_map;
-
-extern SYMBOL_SET  ss_merged;	/* merged map with info from all sources */
-extern SYMBOL_SET  ss_Version;	/* Version_ numbers where available */
-
-/* Regular expression stuff */
-
-extern regex_t     re_nm;
-extern regmatch_t *re_nm_pmatch;
-extern regex_t     re_bracketed_address;
-extern regmatch_t *re_bracketed_address_pmatch;
-extern regex_t     re_unbracketed_address;
-extern regmatch_t *re_unbracketed_address_pmatch;
-
-/* Bracketed address: optional '[', required '<', at least 4 hex characters,
- * required '>', optional ']', optional white space.
- */
-#define BRACKETED_ADDRESS	"\\[*<([0-9a-fA-F]{4,})>\\]*[ \t]*"
-
-#define UNBRACKETED_ADDRESS	"([0-9a-fA-F]{4,})[ \t]*"
-
-/* io.c */
-extern int regular_file(const char *file, const char *msg);
-extern FILE *fopen_local(const char *file, const char *mode, const char *msg);
-extern void fclose_local(FILE *f, const char *msg);
-extern char *fgets_local(char **line, int *size, FILE *f, const char *msg);
-extern int fwrite_local(void const *ptr, size_t size, size_t nmemb,
-			FILE *stream, const char *msg);
-extern FILE *popen_local(const char *cmd, const char *msg);
-extern void pclose_local(FILE *f, const char *msg);
-
-/* ksyms.c */
-extern void read_ksyms(const char *ksyms);
-extern void map_ksyms_to_modules(void);
-extern void read_lsmod(const char *lsmod);
-extern void compare_ksyms_lsmod(void);
-
-/* misc.c */
-extern void malloc_error(const char *msg);
-extern const char *format_address(elf_addr_t address);
-extern char *find_fullpath(const char *program);
-
-/* map.c */
-extern void read_system_map(const char *system_map);
-extern void merge_maps(const char *save_system_map);
-extern void compare_maps(const SYMBOL_SET *ss1, const SYMBOL_SET *ss2,
-			 int precedence);
-
-
-/* object.c */
-extern SYMBOL_SET *adjust_object_offsets(SYMBOL_SET *ss);
-extern void read_vmlinux(const char *vmlinux);
-extern void expand_objects(char * const *object, int objects);
-extern void read_object(const char *object, int i);
-
-/* oops.c */
-extern void Oops_read(int filecount, char * const *filename);
-
-/* re.c */
-extern void re_compile(regex_t *preg, const char *regex, int cflags,
-		       regmatch_t **pmatch);
-extern void re_compile_common(void);
-extern void re_strings(regex_t *preg, const char *text, regmatch_t *pmatch,
-		       char ***string);
-extern void re_strings_free(const regex_t *preg, char ***string);
-extern void re_string_check(int need, int available, const char *msg);
-
-/* symbol.c */
-extern void ss_init(SYMBOL_SET *ss, const char *msg);
-extern void ss_free(SYMBOL_SET *ss);
-extern void ss_init_common(void);
-extern SYMBOL *find_symbol_name(const SYMBOL_SET *ss, const char *symbol,
-				int *start);
-extern void add_symbol_n(SYMBOL_SET *ss, const elf_addr_t address,
-			 const char type, const char keep, const char *symbol);
-extern void add_symbol(SYMBOL_SET *ss, const char *address, const char type,
-		       const char keep, const char *symbol);
-extern char *map_address(const SYMBOL_SET *ss, const elf_addr_t address);
-extern void ss_sort_atn(SYMBOL_SET *ss);
-extern void ss_sort_na(SYMBOL_SET *ss);
-extern SYMBOL_SET *ss_copy(const SYMBOL_SET *ss);
-extern void add_Version(const char *version, const char *source);
-extern void extract_Version(SYMBOL_SET *ss);
-extern void compare_Version(void);
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/ksyms.c linux/scripts/ksymoops-0.6/ksyms.c
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/ksyms.c	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/ksyms.c	Wed Dec 31 16:00:00 1969
@@ -1,287 +0,0 @@
-/*
-	ksyms.c.
-
-	Process ksyms for ksymoops.
-
-	Copyright Keith Owens <kaos@ocs.com.au>.
-	Released under the GNU Public Licence, Version 2.
-
-	Tue Nov  3 02:31:01 EST 1998
-	Version 0.6
-	Read lsmod (/proc/modules).
-	Move "Using_Version" copy to map.c.
-
-	Wed Oct 28 13:47:23 EST 1998
-	Version 0.4
-	Split into separate sources.
- */
-
-#include "ksymoops.h"
-#include <malloc.h>
-#include <string.h>
-
-/* Scan one line from ksyms.  Split lines into the base symbols and the module
- * symbols.  Separate ss for base and each module.
- */
-static void scan_ksyms_line(const char *line)
-{
-	int i;
-	char **string = NULL;
-	SYMBOL_SET *ssp;
-	static char *prev_module = NULL;
-	static regex_t     re_ksyms;
-	static regmatch_t *re_ksyms_pmatch;
-	static char const procname[] = "scan_ksyms_line";
-
-	/* ksyms: address, symbol, optional module */
-	re_compile(&re_ksyms,
-		"^([0-9a-fA-F]{4,}) +([^ \t]+)([ \t]+\\[([^ ]+)\\])?$",
-		REG_NEWLINE|REG_EXTENDED,
-		&re_ksyms_pmatch);
-
-	i = regexec(&re_ksyms, line,
-		    re_ksyms.re_nsub+1, re_ksyms_pmatch, 0);
-	if (debug > 3)
-		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
-	if (i)
-		return;
-
-	/* string [1] - address, [2] - symbol, [3] - white space+module,
-	 * [4] - module.
-	 */
-	re_strings(&re_ksyms, line, re_ksyms_pmatch, &string);
-	if (string[4]) {
-		if (!prev_module || strcmp(prev_module, string[4])) {
-			/* start of a new module in ksyms */
-			++ss_ksyms_modules;
-			ss_ksyms_module = realloc(ss_ksyms_module,
-				ss_ksyms_modules*sizeof(*ss_ksyms_module));
-			if (!ss_ksyms_module)
-				malloc_error("realloc ss_ksyms_module");
-			ssp = ss_ksyms_module+ss_ksyms_modules-1;
-			ss_init(ssp, string[4]);
-			prev_module = strdup(string[4]);
-			if (!prev_module)
-				malloc_error("strdup prev_module");
-		}
-		ssp = ss_ksyms_module+ss_ksyms_modules-1;
-	}
-	else
-		ssp = &ss_ksyms_base;
-	add_symbol(ssp, string[1], ' ', 1, string[2]);
-	re_strings_free(&re_ksyms, &string);
-}
-
-/* Read the symbols from ksyms.  */
-void read_ksyms(const char *ksyms)
-{
-	FILE *f;
-	char *line = NULL;
-	int i, size;
-	static char const procname[] = "read_ksyms";
-
-	if (!ksyms)
-		return;
-	ss_init(&ss_ksyms_base, "ksyms_base");
-	if (debug)
-		fprintf(stderr, "DEBUG: %s %s\n", procname, ksyms);
-
-	if (!regular_file(ksyms, procname))
-		return;
-
-	if (!(f = fopen_local(ksyms, "r", procname)))
-		return;
-
-	while (fgets_local(&line, &size, f, procname))
-		scan_ksyms_line(line);
-
-	fclose_local(f, procname);
-	free(line);
-
-	for (i = 0; i < ss_ksyms_modules; ++i) {
-		ss_sort_na(ss_ksyms_module+i);
-		extract_Version(ss_ksyms_module+i);
-	}
-	if (ss_ksyms_base.used) {
-		ss_sort_na(&ss_ksyms_base);
-		extract_Version(&ss_ksyms_base);
-	}
-	else {
-		fprintf(stderr,
-			"Warning, no kernel symbols in ksyms, is %s a valid "
-			"ksyms file?\n",
-			ksyms);
-		++warnings;
-	}
-
-	if (debug > 1) {
-		for (i = 0; i < ss_ksyms_modules; ++i) {
-			fprintf(stderr,
-				"DEBUG: %s %s used %d out of %d entries\n",
-				procname,
-				ss_ksyms_module[i].source,
-				ss_ksyms_module[i].used,
-				ss_ksyms_module[i].alloc);
-		}
-		fprintf(stderr,
-			"DEBUG: %s %s used %d out of %d entries\n",
-			procname, ss_ksyms_base.source, ss_ksyms_base.used,
-			ss_ksyms_base.alloc);
-	}
-}
-
-/* Map each ksyms module entry to the corresponding object entry.  Tricky,
- * see the comments in the docs about needing a unique symbol in each
- * module.
- */
-static void map_ksym_to_module(SYMBOL_SET *ss)
-{
-	int i, j, matches;
-	char *name = NULL;
-
-	for (i = 0; i < ss->used; ++i) {
-		matches = 0;
-		for (j = 0; j < ss_objects; ++j) {
-			name = (ss->symbol)[i].name;
-			if (find_symbol_name(ss_object+j, name, NULL)) {
-				++matches;
-				ss->related = ss_object+j;
-			}
-		}
-		if (matches == 1)
-			break;		/* unique symbol over all objects */
-		ss->related = NULL;	/* keep looking */
-	}
-	if (!(ss->related)) {
-		fprintf(stderr,
-			"Warning: cannot match loaded module %s to any "
-			"module object.  Trace may not be reliable.\n",
-			ss->source);
-		++warnings;
-	}
-	else if (debug)
-		fprintf(stderr,
-			"DEBUG: ksyms %s matches to %s based on unique "
-			"symbol %s\n",
-			ss->source, ss->related->source, name);
-}
-
-/* Map all ksyms module entries to their corresponding objects */
-void map_ksyms_to_modules(void)
-{
-	int i;
-	SYMBOL_SET *ss, *ssc;
-
-	for (i = 0; i < ss_ksyms_modules; ++i) {
-		ss = ss_ksyms_module+i;
-		map_ksym_to_module(ss);
-		if (ss->related) {
-			ssc = adjust_object_offsets(ss);
-			compare_maps(ss, ssc, 1);
-		}
-	}
-}
-
-/* Read the modules from lsmod.  */
-void read_lsmod(const char *lsmod)
-{
-	FILE *f;
-	char *line = NULL;
-	int i, size;
-	char **string = NULL;
-	static regex_t     re_lsmod;
-	static regmatch_t *re_lsmod_pmatch;
-	static char const procname[] = "read_lsmod";
-
-	if (!lsmod)
-		return;
-	ss_init(&ss_lsmod, "lsmod");
-	if (debug)
-		fprintf(stderr, "DEBUG: %s %s\n", procname, lsmod);
-
-	if (!regular_file(lsmod, procname))
-		return;
-
-	if (!(f = fopen_local(lsmod, "r", procname)))
-		return;
-
-	/* lsmod: module, size, use count, optional used by */
-	re_compile(&re_lsmod,
-		"^"
-		"[ \t]*([^ \t]+)"				/* 1 module */
-		"[ \t]*([^ \t]+)"				/* 2 size */
-		"[ \t]*([^ \t]+)"				/* 3 count */
-		"[ \t]*(.*)"					/* 4 used by */
-		"$",
-		REG_NEWLINE|REG_EXTENDED,
-		&re_lsmod_pmatch);
-
-	while (fgets_local(&line, &size, f, procname)) {
-		i = regexec(&re_lsmod, line,
-			    re_lsmod.re_nsub+1, re_lsmod_pmatch, 0);
-		if (debug > 3)
-			fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
-		if (i)
-			continue;
-		re_strings(&re_lsmod, line, re_lsmod_pmatch, &string);
-		add_symbol(&ss_lsmod, string[2], ' ', 1, string[1]);
-	}
-
-	fclose_local(f, procname);
-	free(line);
-	re_strings_free(&re_lsmod, &string);
-	if (ss_lsmod.used)
-		ss_sort_na(&ss_lsmod);
-	else {
-		fprintf(stderr,
-			"Warning, no symbols in lsmod, is %s a valid "
-			"lsmod file?\n",
-			lsmod);
-		++warnings;
-	}
-
-	if (debug > 1)
-		fprintf(stderr,
-			"DEBUG: %s %s used %d out of %d entries\n",
-			procname, ss_lsmod.source, ss_lsmod.used,
-			ss_lsmod.alloc);
-}
-
-/* Compare modules from ksyms against module list in lsmod and vice versa.
- * There is one ss_ for each ksyms module and a single ss_lsmod to cross
- * check.
- */
-void compare_ksyms_lsmod(void)
-{
-	int i, j;
-	SYMBOL_SET *ss;
-	SYMBOL *s;
-	static char const procname[] = "compare_ksyms_lsmod";
-
-	s = ss_lsmod.symbol;
-	for (i = 0; i < ss_lsmod.used; ++i, ++s) {
-		for (j = 0; j < ss_ksyms_modules; ++j) {
-			ss = ss_ksyms_module+j;
-			if (strcmp(s->name, ss->source) == 0)
-				break;
-		}
-		if (j >= ss_ksyms_modules) {
-			fprintf(stderr,
-				"Warning in %s, module %s is in lsmod but not "
-				"in ksyms, probably no symbols exported\n",
-				procname, s->name);
-			++warnings;
-		}
-	}
-
-	for (i = 0; i < ss_ksyms_modules; ++i) {
-		ss = ss_ksyms_module+i;
-		if (!find_symbol_name(&ss_lsmod, ss->source, NULL)) {
-			fprintf(stderr,
-				"Error in %s, module %s is in ksyms but not "
-				"in lsmod\n",
-				procname, ss->source);
-			++errors;
-		}
-	}
-}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/map.c linux/scripts/ksymoops-0.6/map.c
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/map.c	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/map.c	Wed Dec 31 16:00:00 1969
@@ -1,251 +0,0 @@
-/*
-	map.c.
-
-	Read System.map for ksymoops, create merged System.map.
-
-	Copyright Keith Owens <kaos@ocs.com.au>.
-	Released under the GNU Public Licence, Version 2.
-
-	Tue Nov  3 02:31:01 EST 1998
-	Version 0.6
-	Remove addresses 0-4095 from merged map after writing new map.
-	Move "Using_Version" copy to map.c.
-
-	Wed Oct 28 13:47:23 EST 1998
-	Version 0.4
-	Split into separate sources.
- */
-
-#include "ksymoops.h"
-#include <malloc.h>
-
-/* Read the symbols from System.map */
-void read_system_map(const char *system_map)
-{
-	FILE *f;
-	char *line = NULL, **string = NULL;
-	int i, size = 0;
-	static char const procname[] = "read_system_map";
-
-	if (!system_map)
-		return;
-	ss_init(&ss_system_map, "System.map");
-	if (debug)
-		fprintf(stderr, "DEBUG: %s %s\n", procname, system_map);
-
-	if (!regular_file(system_map, procname))
-		return;
-
-	if (!(f = fopen_local(system_map, "r", procname)))
-		return;
-
-	while (fgets_local(&line, &size, f, procname)) {
-		i = regexec(&re_nm, line, re_nm.re_nsub+1, re_nm_pmatch, 0);
-		if (debug > 3)
-			fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
-		if (i == 0) {
-			re_strings(&re_nm, line, re_nm_pmatch, &string);
-			add_symbol(&ss_system_map, string[1], *string[2],
-				   1, string[3]);
-		}
-	}
-
-	fclose_local(f, procname);
-	re_strings_free(&re_nm, &string);
-	free(line);
-	if (ss_system_map.used) {
-		ss_sort_na(&ss_system_map);
-		extract_Version(&ss_system_map);
-	}
-	else {
-		fprintf(stderr,
-			"Warning, no kernel symbols in System.map, is %s a "
-			"valid System.map file?\n",
-			system_map);
-		++warnings;
-	}
-
-	if (debug > 1)
-		fprintf(stderr,
-			"DEBUG: %s %s used %d out of %d entries\n",
-			procname,
-			ss_system_map.source,
-			ss_system_map.used,
-			ss_system_map.alloc);
-}
-
-/* Compare two maps, all symbols in the first should appear in the second. */
-void compare_maps(const SYMBOL_SET *ss1, const SYMBOL_SET *ss2,
-			 int precedence)
-{
-	int i, start = 0;
-	SYMBOL *s1, *s2, **sdrop = precedence == 1 ? &s2 : &s1;
-	const SYMBOL_SET **ssdrop = precedence == 1 ? &ss2 : &ss1;
-
-	if (!(ss1->used && ss2->used))
-		return;
-
-	if (debug > 1)
-		fprintf(stderr,
-			"DEBUG: compare_maps %s vs %s, %s takes precedence\n",
-			ss1->source, ss2->source,
-			precedence == 1 ? ss1->source : ss2->source);
-
-	for (i = 0; i < ss1->used; ++i) {
-		s1 = ss1->symbol+i;
-		if (!(s1->keep))
-			continue;
-		s2 = find_symbol_name(ss2, s1->name, &start);
-		if (!s2) {
-			/* Some types only appear in nm output, not in things
-			 * like System.map.  Silently ignore them.
-			 */
-			if (s1->type == 'a' || s1->type == 't')
-				continue;
-			fprintf(stderr,
-				"Warning: %s symbol %s not found in %s.  "
-				"Ignoring %s entry\n",
-				ss1->source, s1->name,
-				ss2->source, (*ssdrop)->source);
-			++warnings;
-			if (*sdrop)
-				(*sdrop)->keep = 0;
-		}
-		else if (s1->address != s2->address) {
-			/* Type C symbols cannot be resolved from nm to ksyms,
-			 * silently ignore them.
-			 */
-			if (s1->type == 'C' || s2->type == 'C')
-				continue;
-			fprintf(stderr,
-				"Warning: mismatch on symbol %s %c, "
-				"%s says %lx, %s says %lx.  "
-				"Ignoring %s entry\n",
-				s1->name, s1->type, ss1->source, s1->address,
-				ss2->source, s2->address, (*ssdrop)->source);
-			++warnings;
-			if (*sdrop)
-				(*sdrop)->keep = 0;
-		}
-		else
-			++start;	/* step to next entry in ss2 */
-	}
-}
-
-/* Append the second symbol set onto the first */
-static void append_map(SYMBOL_SET *ss1, const SYMBOL_SET *ss2)
-{
-	int i;
-	SYMBOL *s;
-
-	if (!ss2 || !ss2->used)
-		return;
-	if (debug > 1)
-		fprintf(stderr, "DEBUG: append_map %s to %s\n",
-			ss2->source, ss1->source);
-
-	for (i = 0; i < ss2->used; ++i) {
-		s = ss2->symbol+i;
-		if (s->keep)
-			add_symbol_n(ss1, s->address, s->type, 1,
-				s->name);
-	}
-}
-
-/* Compare the various sources and build a merged system map */
-void merge_maps(const char *save_system_map)
-{
-	int i;
-	SYMBOL *s;
-	FILE *f;
-	static char const procname[] = "merge_maps";
-
-	if (debug)
-		fprintf(stderr, "DEBUG: %s\n", procname);
-
-	/* Using_Versions only appears in ksyms, copy to other tables */
-	if ((s = find_symbol_name(&ss_ksyms_base,
-			"Using_Versions", 0))) {
-		if (ss_system_map.used) {
-			add_symbol_n(&ss_system_map, s->address,
-				s->type, s->keep, s->name);
-			ss_sort_na(&ss_system_map);
-		}
-		if (ss_vmlinux.used) {
-			add_symbol_n(&ss_vmlinux, s->address, s->type,
-				s->keep, s->name);
-			ss_sort_na(&ss_vmlinux);
-		}
-	}
-
-	compare_Version();	/* highlight any version problems first */
-	compare_ksyms_lsmod();	/* highlight any missing modules next */
-	compare_maps(&ss_ksyms_base, &ss_vmlinux, 2);
-	compare_maps(&ss_system_map, &ss_vmlinux, 2);
-	compare_maps(&ss_vmlinux, &ss_system_map, 1);
-	compare_maps(&ss_ksyms_base, &ss_system_map, 2);
-
-	if (ss_objects) {
-		map_ksyms_to_modules();
-	}
-
-	ss_init(&ss_merged, "merged");
-	append_map(&ss_merged, &ss_vmlinux);
-	append_map(&ss_merged, &ss_ksyms_base);
-	append_map(&ss_merged, &ss_system_map);
-	for (i = 0; i < ss_ksyms_modules; ++i)
-		append_map(&ss_merged, (ss_ksyms_module+i)->related);
-	if (!ss_merged.used) {
-		fprintf(stderr, "Warning, no symbols in merged map\n");
-		++warnings;
-	}
-
-	/* drop duplicates, type a (registers) and gcc2_compiled. */
-	ss_sort_atn(&ss_merged);
-	s = ss_merged.symbol;
-	for (i = 0; i < ss_merged.used-1; ++i) {
-		if (s->type == 'a' ||
-		    (s->type == 't' && !strcmp(s->name, "gcc2_compiled.")))
-			s->keep = 0;
-		else if (strcmp(s->name, (s+1)->name) == 0 &&
-		    s->address == (s+1)->address) {
-			if (s->type != ' ')
-				(s+1)->keep = 0;
-			else
-				s->keep = 0;
-		}
-		++s;
-	}
-	ss_sort_atn(&ss_merged);	/* will remove dropped variables */
-
-	if (save_system_map) {
-		if (debug)
-			fprintf(stderr, "DEBUG: writing merged map to %s\n",
-				save_system_map);
-		if (!(f = fopen_local(save_system_map, "w", procname)))
-			return;
-		s = ss_merged.symbol;
-		for (i = 0; i < ss_merged.used; ++i) {
-			if (s->keep)
-				fprintf(f, "%s %c %s\n",
-					format_address(s->address),
-					s->type, s->name);
-			++s;
-		}
-	}
-
-	/* The merged map may contain symbols with an address of 0, e.g.
-	 * Using_Versions.  These give incorrect results for low addresses in
-	 * map_address, such addresses map to "Using_Versions+xxx".  Remove
-	 * any addresses below (arbitrary) 4096 from the merged map.  AFAIK,
-	 * Linux does not use the first page on any arch.
-	 */
-	for (i = 0; i < ss_merged.used; ++i) {
-		if ((ss_merged.symbol+i)->address < 4096)
-			(ss_merged.symbol+i)->keep = 0;
-		else
-			break;
-	}
-	if (i)
-		ss_sort_atn(&ss_merged);	/* remove dropped variables */
-}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/misc.c linux/scripts/ksymoops-0.6/misc.c
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/misc.c	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/misc.c	Wed Dec 31 16:00:00 1969
@@ -1,108 +0,0 @@
-/*
-	misc.c.
-
-	Miscellaneous routines for ksymoops.
-
-	Copyright Keith Owens <kaos@ocs.com.au>.
-	Released under the GNU Public Licence, Version 2.
-
-	Tue Nov  3 02:31:01 EST 1998
-	Version 0.6
-	Convert from a.out to bfd, using same format as ksymoops.
-
-	Wed Oct 28 13:47:23 EST 1998
-	Version 0.4
-	Split into separate sources.
- */
-
-#include "ksymoops.h"
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-void malloc_error(const char *msg)
-{
-	fprintf(stderr, "%s: fatal malloc error for %s\n", prefix, msg);
-	exit(2);
-}
-
-/* Format an address with the correct number of leading zeroes */
-const char *format_address(elf_addr_t address)
-{
-	/* Well oversized */
-	static char format[10], text[200];
-	if (!*format)
-		snprintf(format, sizeof(format), "%%0%dlx",
-			2*sizeof(address));
-	snprintf(text, sizeof(text), format, address);
-	return(text);
-}
-
-/* Find the full pathname of a program.  Code heavily based on
- * glibc-2.0.5/posix/execvp.c.
- */
-char *find_fullpath(const char *program)
-{
-	char *fullpath = NULL;
-	char *path, *p;
-	size_t len;
-	static const char procname[] = "find_fullpath";
-
-	/* Don't search when it contains a slash.  */
-	if (strchr(program, '/')) {
-		if (!(fullpath = strdup(program)))
-			malloc_error(procname);
-		if (debug > 1)
-			fprintf(stderr, "DEBUG: %s %s\n", procname, fullpath);
-		return(fullpath);
-	}
-
-	path = getenv ("PATH");
-	if (!path) {
-		/* There is no `PATH' in the environment.  The default search
-		   path is the current directory followed by the path `confstr'
-		   returns for `_CS_PATH'.
-		 */
-		len = confstr(_CS_PATH, (char *) NULL, 0);
-		if (!(path = malloc(1 + len)))
-			malloc_error(procname);
-		path[0] = ':';
-		confstr(_CS_PATH, path+1, len);
-	}
-
-	len = strlen(program) + 1;
-	if (!(fullpath = malloc(strlen(path) + len)))
-		malloc_error(procname);
-	p = path;
-	do {
-		path = p;
-		p = strchr(path, ':');
-		if (p == NULL)
-			p = strchr(path, '\0');
-
-		/* Two adjacent colons, or a colon at the beginning or the end
-		 * of `PATH' means to search the current directory.
-		 */
-		if (p == path)
-			memcpy(fullpath, program, len);
-		else {
-			/* Construct the pathname to try.  */
-			memcpy(fullpath, path, p - path);
-			fullpath[p - path] = '/';
-			memcpy(&fullpath[(p - path) + 1], program, len);
-		}
-
-		/* If we have execute access, assume this is the program. */
-		if (access(fullpath, X_OK) == 0) {
-			if (debug > 1)
-				fprintf(stderr, "DEBUG: %s %s\n",
-					procname, fullpath);
-			return(fullpath);
-		}
-	} while (*p++ != '\0');
-
-	fprintf(stderr, "Error: %s %s could not find executable %s\n",
-		prefix, procname, program);
-	++errors;
-	return(NULL);
-}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/object.c linux/scripts/ksymoops-0.6/object.c
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/object.c	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/object.c	Wed Dec 31 16:00:00 1969
@@ -1,230 +0,0 @@
-/*
-	object.c.
-
-	object handling routines for ksymoops.  Read modules, vmlinux, etc. 
-
-	Copyright Keith Owens <kaos@ocs.com.au>.
-	Released under the GNU Public Licence, Version 2.
-
-	Wed Oct 28 13:47:23 EST 1998
-	Version 0.4
-	Split into separate sources.
- */
-
-#include "ksymoops.h"
-#include <malloc.h>
-#include <string.h>
-#include <sys/stat.h>
-
-/* Extract all symbols definitions from an object using nm */
-static void read_nm_symbols(SYMBOL_SET *ss, const char *file)
-{
-	FILE *f;
-	char *cmd, *line = NULL, **string = NULL;
-	int i, size = 0;
-	static char const procname[] = "read_nm_symbols";
-
-	if (!regular_file(file, procname))
-		return;
-
-	cmd = malloc(strlen(path_nm)+strlen(file)+2);
-	if (!cmd)
-		malloc_error("nm command");
-	strcpy(cmd, path_nm);
-	strcat(cmd, " ");
-	strcat(cmd, file);
-	if (debug > 1)
-		fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
-	if (!(f = popen_local(cmd, procname)))
-		return;
-	free(cmd);
-
-	while (fgets_local(&line, &size, f, procname)) {
-		i = regexec(&re_nm, line, re_nm.re_nsub+1, re_nm_pmatch, 0);
-		if (debug > 3)
-			fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
-		if (i == 0) {
-			re_strings(&re_nm, line, re_nm_pmatch, &string);
-			add_symbol(ss, string[1], *string[2], 1, string[3]);
-		}
-	}
-
-	pclose_local(f, procname);
-	re_strings_free(&re_nm, &string);
-	free(line);
-	if (debug > 1)
-		fprintf(stderr,
-			"DEBUG: %s %s used %d out of %d entries\n",
-			procname, ss->source, ss->used, ss->alloc);
-}
-
-/* Read the symbols from vmlinux */
-void read_vmlinux(const char *vmlinux)
-{
-	if (!vmlinux)
-		return;
-	ss_init(&ss_vmlinux, "vmlinux");
-	read_nm_symbols(&ss_vmlinux, vmlinux);
-	if (ss_vmlinux.used) {
-		ss_sort_na(&ss_vmlinux);
-		extract_Version(&ss_vmlinux);
-	}
-	else {
-		fprintf(stderr,
-			"Warning, no kernel symbols in vmlinux, is %s a valid "
-			"vmlinux file?\n",
-			vmlinux);
-		++warnings;
-	}
-}
-
-
-/* Read the symbols from one object (module) */
-void read_object(const char *object, int i)
-{
-	ss_init(ss_object+i, object);
-	read_nm_symbols(ss_object+i, object);
-	if ((ss_object+i)->used) {
-		ss_sort_na(ss_object+i);
-		extract_Version(ss_object+i);
-	}
-	else {
-		fprintf(stderr, "Warning, no symbols in %s\n", object);
-		++warnings;
-	}
-}
-
-/* Add a new entry to the list of objects */
-static void add_ss_object(const char *file)
-{
-	++ss_objects;
-	ss_object = realloc(ss_object, ss_objects*sizeof(*ss_object));
-	if (!ss_object)
-		malloc_error("realloc ss_object");
-	ss_init(ss_object+ss_objects-1, file);
-}
-
-/* Run a directory and its subdirectories, looking for *.o files */
-static void find_objects(const char *dir)
-{
-	FILE *f;
-	char *cmd, *line = NULL;
-	int size = 0, files = 0;
-	static char const procname[] = "find_objects";
-	static char const options[] = " -follow -name '*.o' -print";
-
-	cmd = malloc(strlen(path_find)+1+strlen(dir)+strlen(options)+1);
-	if (!cmd)
-		malloc_error("find command");
-	strcpy(cmd, path_find);
-	strcat(cmd, " ");
-	strcat(cmd, dir);
-	strcat(cmd, options);
-	if (debug > 1)
-		fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
-	if (!(f = popen_local(cmd, procname)))
-		return;
-	free(cmd);
-
-	while (fgets_local(&line, &size, f, procname)) {
-		if (debug > 1)
-			fprintf(stderr, "DEBUG: %s - %s\n", procname, line);
-		add_ss_object(line);
-		++files;
-	}
-
-	pclose_local(f, procname);
-	if (!files) {
-		fprintf(stderr,
-			"Warning: no *.o files in %s.  "
-			"Is %s a valid module directory?\n",
-			dir, dir);
-		++warnings;
-	}
-}
-
-/* Take the user supplied list of objects which can include directories.
- * Expand directories into any *.o files.  The results are stored in
- * ss_object, leaving the user supplied options untouched.
- */
-void expand_objects(char * const *object, int objects)
-{
-	struct stat statbuf;
-	int i;
-	const char *file;
-	static char const procname[] = "expand_objects";
-
-	for (i = 0; i < objects; ++i) {
-		file = object[i];
-		if (debug > 1)
-			fprintf(stderr, "DEBUG: %s checking '%s' - ",
-				procname, file);
-		if (!stat(file, &statbuf) && S_ISDIR(statbuf.st_mode)) {
-			if (debug > 1)
-				fprintf(stderr, "directory, expanding\n");
-			find_objects(file);
-		}
-		else {
-			if (debug > 1)
-				fprintf(stderr, "not directory\n");
-			add_ss_object(file);
-		}
-	}
-}
-
-/* Map a symbol type to a section code. 0 - text, 1 - data, 2 - read only data,
- * 3 - C (cannot relocate), 4 - the rest.
- */
-static int section(char type)
-{
-	switch (type) {
-	case 'T':
-	case 't':
-		return 0;
-	case 'D':
-	case 'd':
-		return 1;
-	case 'R':
-	case 'r':
-		return 2;
-	case 'C':
-		return 3;
-	default:
-		return 4;
-	}
-}
-
-/* Given ksyms module data which has a related object, create a copy of the
- * object data, adjusting the offsets to match where the module was loaded.
- */
-SYMBOL_SET *adjust_object_offsets(SYMBOL_SET *ss)
-{
-	int i;
-	elf_addr_t adjust[] = {0, 0, 0, 0, 0};
-	SYMBOL *sk, *so;
-	SYMBOL_SET *ssc;
-
-	if (debug > 1)
-		fprintf(stderr,
-			"DEBUG: adjust_object_offsets %s\n", ss->source);
-
-	ssc = ss_copy(ss->related);
-
-	/* For common symbols, calculate the adjustment */
-	for (i = 0; i < ss->used; ++i) {
-		sk = ss->symbol+i;
-		if ((so = find_symbol_name(ssc, sk->name, NULL)))
-			adjust[section(so->type)] = sk->address - so->address;
-	}
-	for (i = 0; i < ssc->used; ++i) {
-		so = ssc->symbol+i;
-		/* Type C does not relocate well, silently ignore */
-		if (so->type != 'C' && adjust[section(so->type)])
-			so->address += adjust[section(so->type)];
-		else
-			so->keep = 0;  /* do not merge into final map */
-	}
-
-	ss->related = ssc;	/* map using adjusted copy */
-	return(ssc);
-}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/oops.c linux/scripts/ksymoops-0.6/oops.c
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/oops.c	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/oops.c	Wed Dec 31 16:00:00 1969
@@ -1,1061 +0,0 @@
-/*
-	oops.c.
-
-	Oops processing for ksymoop.
-
-	Copyright Keith Owens <kaos@ocs.com.au>.
-	Released under the GNU Public Licence, Version 2.
-
-	Tue Nov  3 02:31:01 EST 1998
-	Version 0.6
-	Oops file must be regular.
-	Add "invalid operand" to Oops_print.
-	Minor adjustment to re for ppc.
-	Minor adjustment to re for objdump lines with <_EIP+xxx>.
-	Convert from a.out to bfd, using same format as ksymoops.
-	Added MIPS.
-	PPC handling based on patches by "Ryan Nielsen" <ran@krazynet.com>
-
-	Wed Oct 28 13:47:23 EST 1998
-	Version 0.4
-	Split into seperate sources.
- */
-
-#include "ksymoops.h"
-#include <bfd.h>
-#include <ctype.h>
-#include <errno.h>
-#include <malloc.h>
-#include <memory.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-/* Error detected by bfd */
-static void Oops_bfd_perror(const char *msg)
-{
-	fprintf(stderr, "Error ");
-	bfd_perror(msg);
-	++errors;
-}
-
-/* Safest way to get correct output bfd format is to copy ksymoops' format. */
-static int Oops_copy_bfd_format(bfd **ibfd, bfd **obfd, asection **isec,
-				const char *file)
-{
-	char *me, **matches, **match;
-
-	if (!(*obfd = bfd_openw(file, NULL))) {
-		Oops_bfd_perror(file);
-		return(0);
-	}
-
-	me = find_fullpath(prefix);
-	if (!me)
-		return(0);
-
-	if (!(*ibfd = bfd_openr(me, NULL))) {
-		Oops_bfd_perror(me);
-		return(0);
-	}
-	free(me);	/* Who is Tommy? */
-
-	if (!bfd_check_format_matches(*ibfd, bfd_object, &matches)) {
-		Oops_bfd_perror(me);
-		if (bfd_get_error() == bfd_error_file_ambiguously_recognized) {
-			fprintf(stderr, "Matching formats:");
-			match = matches;
-			while (*match)
-				fprintf(stderr, " %s", *match++);
-			fprintf(stderr, "\n");
-			free(matches);
-		}
-		return(0);
-	}
-
-	if (!(*isec = bfd_get_section_by_name(*ibfd, ".text"))) {
-		Oops_bfd_perror("get_section");
-		return(0);
-	}
-
-	bfd_set_format(*obfd, bfd_object);
-	bfd_set_arch_mach(*obfd, bfd_get_arch(*ibfd), bfd_get_mach(*ibfd));
-
-	if (!bfd_set_file_flags(*obfd, bfd_get_file_flags(*ibfd))) {
-		Oops_bfd_perror("set_file_flags");
-		return(0);
-	}
-
-	return(1);
-}
-
-/* Write the code values to a file using bfd. */
-static int Oops_write_bfd_data(bfd *ibfd, bfd *obfd, asection *isec,
-			       const char *code, int size)
-{
-	asection *osec;
-	asymbol *osym;
-
-	if (!bfd_set_start_address(obfd, 0)) {
-		Oops_bfd_perror("set_start_address");
-		return(0);
-	}
-	if (!(osec = bfd_make_section(obfd, ".text"))) {
-		Oops_bfd_perror("make_section");
-		return(0);
-	}
-	if (!bfd_set_section_flags(obfd, osec, 
-		bfd_get_section_flags(ibfd, isec))) {
-		Oops_bfd_perror("set_section_flags");
-		return(0);
-	}
-	if (!bfd_set_section_alignment(obfd, osec, 
-		bfd_get_section_alignment(ibfd, isec))) {
-		Oops_bfd_perror("set_section_alignment");
-		return(0);
-	}
-	osec->output_section = osec;
-	if (!(osym = bfd_make_empty_symbol(obfd))) {
-		Oops_bfd_perror("make_empty_symbol");
-		return(0);
-	}
-	osym->name = "_EIP";
-	osym->section = osec;
-	osym->flags = BSF_GLOBAL;
-	osym->value = 0;
-	if (!bfd_set_symtab(obfd, &osym, 1)) {
-		Oops_bfd_perror("set_symtab");
-		return(0);
-	}
-	if (!bfd_set_section_size(obfd, osec, size)) {
-		Oops_bfd_perror("set_section_size");
-		return(0);
-	}
-	if (!bfd_set_section_vma(obfd, osec, 0)) {
-		Oops_bfd_perror("set_section_vma");
-		return(0);
-	}
-	if (!bfd_set_section_contents(obfd, osec, (PTR) code, 0, size)) {
-		Oops_bfd_perror("set_section_contents");
-		return(0);
-	}
-	if (!bfd_close(obfd)) {
-		Oops_bfd_perror("close(obfd)");
-		return(0);
-	}
-	if (!bfd_close(ibfd)) {
-		Oops_bfd_perror("close(ibfd)");
-		return(0);
-	}
-	return 1;
-}
-
-/* Write the Oops code to a temporary file with suitable header and trailer. */
-static char *Oops_code_to_file(const char *code, int size)
-{
-	char *file;
-	bfd *ibfd, *obfd;
-	asection *isec;
-
-	bfd_init();
-	file = tmpnam(NULL);
-	if (!Oops_copy_bfd_format(&ibfd, &obfd, &isec, file))
-		return(NULL);
-	if (!Oops_write_bfd_data(ibfd, obfd, isec, code, size))
-		return(NULL);
-	return(file);
-}
-
-/* Run objdump against the binary Oops code */
-static FILE *Oops_objdump(const char *file)
-{
-	char *cmd;
-	FILE *f;
-	static char const options[] = "-dhf ";
-	static char const procname[] = "Oops_objdump";
-
-	cmd = malloc(strlen(path_objdump)+1+strlen(options)+strlen(file)+1);
-	if (!cmd)
-		malloc_error(procname);
-	strcpy(cmd, path_objdump);
-	strcat(cmd, " ");
-	strcat(cmd, options);
-	strcat(cmd, file);
-	if (debug > 1)
-		fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
-	f = popen_local(cmd, procname);
-	free(cmd);
-	return(f);
-}
-
-/* Process one code line from objdump, ignore everything else */
-static void Oops_decode_one(SYMBOL_SET *ss, const char *line, elf_addr_t eip,
-			    int adjust)
-{
-	int i;
-	elf_addr_t address, eip_relative;
-	char *line2, *map, **string = NULL;
-	static regex_t     re_Oops_objdump;
-	static regmatch_t *re_Oops_objdump_pmatch;
-	static char const procname[] = "Oops_decode_one";
-
-	/* objdump output.  Optional whitespace, hex digits, optional
-	 * ' <_EIP+offset>', ':'.  The '+offset' after _EIP is also optional.
-	 * Older binutils output 'xxxxxxxx <_EIP+offset>:', newer versions do
-	 * '00000000 <_EIP>:' first followed by '      xx:' lines.
-	 *
-	 * Just to complicate things even more, objdump recognises jmp, call,
-	 * etc., converts the code to something like this :-
-	 * "   f: e8 32 34 00 00  call   3446 <_EIP+0x3446>"
-	 * Recognise this and append the eip adjusted address, followed by the
-	 * map_address text for that address.
-	 *
-	 * With any luck, objdump will take care of all such references which
-	 * makes this routine architecture insensitive.  No need to test for
-	 * i386 jmp, call or m68k swl etc.
-	 */
-	re_compile(&re_Oops_objdump,
-			"^[ \t]*"
-			"([0-9a-fA-F]+)"				/* 1 */
-			"( <_EIP[^>]*>)?"				/* 2 */
-			":"
-			"("						/* 3 */
-			".* +<_EIP\\+0?x?([0-9a-fA-F]+)>[ \t]*$"	/* 4 */
-			")?"
-			".*"
-			,
-		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
-		&re_Oops_objdump_pmatch);
-
-	i = regexec(&re_Oops_objdump, line, re_Oops_objdump.re_nsub+1,
-		re_Oops_objdump_pmatch, 0);
-	if (debug > 3)
-		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
-	if (i != 0)
-		return;
-
-	re_strings(&re_Oops_objdump, line, re_Oops_objdump_pmatch, &string);
-	errno = 0;
-	address = strtoul(string[1], NULL, 16);
-	if (errno) {
-		fprintf(stderr,
-			"%s Invalid hex value in objdump line, "
-			"treated as zero - '%s'\n"
-			"  objdump line '%s'\n",
-			procname, string[1], line);
-		perror(" ");
-		++errors;
-		address = 0;
-	}
-	address += eip + adjust;
-	if (string[4]) {
-		/* EIP relative data to be adjusted */
-		errno = 0;
-		eip_relative = strtoul(string[4], NULL, 16);
-		if (errno) {
-			fprintf(stderr,
-				"%s Invalid hex value in objdump line, "
-				"treated as zero - '%s'\n"
-				"  objdump line '%s'\n",
-				procname, string[4], line);
-			perror(" ");
-			++errors;
-			eip_relative = 0;
-		}
-		eip_relative += eip + adjust;
-		map = map_address(&ss_merged, eip_relative);
-		/* new text is original line, eip_relative in hex, map text */
-		i = strlen(line)+1+2*sizeof(eip_relative)+1+strlen(map)+1;
-		line2 = malloc(i);
-		if (!line2)
-			malloc_error(procname);
-		snprintf(line2, i, "%s %s %s",
-			line, format_address(eip_relative), map);
-		add_symbol_n(ss, address, 'C', 1, line2);
-		free(line2);
-	}
-	else
-		add_symbol_n(ss, address, 'C', 1, line);	/* as is */
-	re_strings_free(&re_Oops_objdump, &string);
-}
-
-/* Maximum number of code bytes to process */
-#define CODE_SIZE 36	/* sparc and alpha dump 36 bytes */
-
-/******************************************************************************/
-/*                     Start architecture sensitive code                      */
-/******************************************************************************/
-
-/* Extract the hex values from the Code: line and convert to binary */
-static int Oops_code_values(const unsigned char* code_text, char *code,
-			    int *adjust, char ***string, int string_max)
-{
-	int byte = 0, l;
-	unsigned long c;
-	char *value;
-	const char *p;
-	static regex_t     re_Oops_code_value;
-	static regmatch_t *re_Oops_code_value_pmatch;
-	static const char procname[] = "Oops_code_values";
-
-	/* Given by re_Oops_code: code_text is a message (e.g. "general
-	 * protection") or one or more hex fields separated by space or tab.
-	 * Some architectures bracket the current instruction with '<' and '>'.
-	 * The first character is nonblank.
-	 */
-	if (!isxdigit(*code_text)) {
-		fprintf(stderr,
-			"Warning, Code looks like message, not hex digits.  "
-			"No disassembly attempted.\n");
-		++warnings;
-		return(0);
-	}
-	memset(code, '\0', CODE_SIZE);
-	p = code_text;
-	*adjust = 0;	/* EIP points to code byte 0 */
-
-	/* Code values.  Hex values separated by white space.  On sparc, the
-	 * current instruction is bracketed in '<' and '>'.
-	 */
-	re_compile(&re_Oops_code_value,
-			"^"
-			"(<?)"						/* 1 */
-			"([0-9a-fA-F]+)"				/* 2 */
-			">?"
-			"[ \t]*"
-			,
-		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
-		&re_Oops_code_value_pmatch);
-
-	re_string_check(re_Oops_code_value.re_nsub+1, string_max, procname);
-	while (regexec(&re_Oops_code_value, p, re_Oops_code_value.re_nsub+1,
-			re_Oops_code_value_pmatch, 0) == 0) {
-		re_strings(&re_Oops_code_value, p,
-			re_Oops_code_value_pmatch, string);
-		if (byte >= CODE_SIZE)
-			break;
-		errno = 0;
-		value = (*string)[2];
-		c = strtoul(value, NULL, 16);
-		if (errno) {
-			fprintf(stderr,
-				"%s Invalid hex value in code_value line, "
-				"treated as zero - '%s'\n"
-				"  code_value line '%s'\n",
-				procname, value, code_text);
-			perror(" ");
-			++errors;
-			c = 0;
-		}
-		if ((*string[1]) && *((*string)[1]))
-			*adjust = -byte;	/* this byte is EIP */
-		/* i386 - 2 byte code, m68k - 4 byte, sparc - 8 byte.
-		 * Consistent we're not!
-		 */
-		l = strlen(value);
-		if (l%2) {
-			fprintf(stderr,
-				"%s invalid value 0x%s in Code line, not a "
-				"multiple of 2 digits, value ignored\n",
-				procname, value);
-			++errors;
-		}
-		else while (l) {
-			if (byte >= CODE_SIZE) {
-				fprintf(stderr,
-					"%s Warning: extra values in Code "
-					"line, ignored - '%s'\n",
-					procname, value);
-				++warnings;
-				break;
-			}
-			l -= 2;
-			code[byte++] = (c >> l*4) & 0xff;
-			value += 2;
-		}
-		p += re_Oops_code_value_pmatch[0].rm_eo;
-	}
-
-	if (*p) {
-		fprintf(stderr,
-			"Warning garbage '%s' at end of code line ignored "
-			"by %s\n",
-			p, procname);
-		++warnings;
-	}
-	return(1);
-}
-
-/* Look for the EIP: line, returns start of the relevant hex value */
-static char *Oops_eip(const char *line, char ***string, int string_max)
-{
-	int i;
-	static regex_t     re_Oops_eip_sparc;
-	static regmatch_t *re_Oops_eip_sparc_pmatch;
-	static regex_t     re_Oops_eip_ppc;
-	static regmatch_t *re_Oops_eip_ppc_pmatch;
-	static regex_t     re_Oops_eip_mips;
-	static regmatch_t *re_Oops_eip_mips_pmatch;
-	static regex_t     re_Oops_eip_other;
-	static regmatch_t *re_Oops_eip_other_pmatch;
-	static const char procname[] = "Oops_eip";
-
-	/* Oops 'EIP:' line for sparc, actually PSR followed by PC */
-	re_compile(&re_Oops_eip_sparc,
-			"^PSR: [0-9a-fA-F]+ PC: " UNBRACKETED_ADDRESS,
-		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
-		&re_Oops_eip_sparc_pmatch);
-
-	re_string_check(re_Oops_eip_sparc.re_nsub+1, string_max, procname);
-	i = regexec(&re_Oops_eip_sparc, line, re_Oops_eip_sparc.re_nsub+1,
-		re_Oops_eip_sparc_pmatch, 0);
-	if (debug > 3)
-		fprintf(stderr, "DEBUG: %s regexec sparc %d\n", procname, i);
-	if (i == 0) {
-		re_strings(&re_Oops_eip_sparc, line, re_Oops_eip_sparc_pmatch,
-			string);
-		return((*string)[re_Oops_eip_sparc.re_nsub]);
-	}
-
-	/* Oops 'EIP:' line for PPC, all over the place */
-	re_compile(&re_Oops_eip_ppc,
-			"("
-			  "(kernel pc )"
-			  "|(trap at PC: )"
-			  "|(bad area pc )"
-			  "|(NIP: )"
-			")"
-			UNBRACKETED_ADDRESS,
-		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
-		&re_Oops_eip_ppc_pmatch);
-
-	re_string_check(re_Oops_eip_ppc.re_nsub+1, string_max, procname);
-	i = regexec(&re_Oops_eip_ppc, line, re_Oops_eip_ppc.re_nsub+1,
-		re_Oops_eip_ppc_pmatch, 0);
-	if (debug > 3)
-		fprintf(stderr, "DEBUG: %s regexec ppc %d\n", procname, i);
-	if (i == 0) {
-		re_strings(&re_Oops_eip_ppc, line, re_Oops_eip_ppc_pmatch,
-			string);
-		return((*string)[re_Oops_eip_ppc.re_nsub]);
-	}
-
-	/* Oops 'EIP:' line for MIPS, epc, optional white space, ':',
-	 * optional white space, unbracketed address.
-	 */
-	re_compile(&re_Oops_eip_mips,
-			"^(epc[ \t]*:+[ \t]*)"
-			UNBRACKETED_ADDRESS,
-		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
-		&re_Oops_eip_mips_pmatch);
-
-	re_string_check(re_Oops_eip_mips.re_nsub+1, string_max, procname);
-	i = regexec(&re_Oops_eip_mips, line, re_Oops_eip_mips.re_nsub+1,
-		re_Oops_eip_mips_pmatch, 0);
-	if (debug > 3)
-		fprintf(stderr, "DEBUG: %s regexec mips %d\n", procname, i);
-	if (i == 0) {
-		re_strings(&re_Oops_eip_mips, line, re_Oops_eip_mips_pmatch,
-			string);
-		return((*string)[re_Oops_eip_mips.re_nsub]);
-	}
-
-	/* Oops 'EIP:' line for other architectures */
-	re_compile(&re_Oops_eip_other,
-			"^("
-	/* i386 */	"(EIP:[ \t]+.*)"
-	/* m68k */	"|(PC[ \t]*=[ \t]*)"
-			")"
-			BRACKETED_ADDRESS
-			,
-		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
-		&re_Oops_eip_other_pmatch);
-
-	re_string_check(re_Oops_eip_other.re_nsub+1, string_max, procname);
-	i = regexec(&re_Oops_eip_other, line, re_Oops_eip_other.re_nsub+1,
-		re_Oops_eip_other_pmatch, 0);
-	if (debug > 3)
-		fprintf(stderr, "DEBUG: %s regexec other %d\n", procname, i);
-	if (i == 0) {
-		re_strings(&re_Oops_eip_other, line, re_Oops_eip_other_pmatch,
-			string);
-		return((*string)[re_Oops_eip_other.re_nsub]);
-	}
-	return(NULL);
-}
-
-/* Set the eip from the EIP line */
-static void Oops_set_eip(const char *value, elf_addr_t *eip, SYMBOL_SET *ss)
-{
-	static const char procname[] = "Oops_set_eip";
-	errno = 0;
-	*eip = strtoul(value, NULL, 16);
-	if (errno) {
-		fprintf(stderr,
-			"%s Invalid hex value in EIP line, ignored - '%s'\n",
-			procname, value);
-		perror(" ");
-		++errors;
-		*eip = 0;
-	}
-	add_symbol_n(ss, *eip, 'E', 1, ">>EIP:");
-}
-
-/* Look for the MIPS ra line, returns start of the relevant hex value */
-static char *Oops_ra(const char *line, char ***string, int string_max)
-{
-	int i;
-	static regex_t     re_Oops_ra;
-	static regmatch_t *re_Oops_ra_pmatch;
-	static const char procname[] = "Oops_ra";
-
-	/* Oops 'ra:' line for MIPS, ra, optional white space, one or
-	 * more '=', optional white space, unbracketed address.
-	 */
-	re_compile(&re_Oops_ra,
-			"(ra[ \t]*=+[ \t]*)"
-			UNBRACKETED_ADDRESS,
-		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
-		&re_Oops_ra_pmatch);
-
-	re_string_check(re_Oops_ra.re_nsub+1, string_max, procname);
-	i = regexec(&re_Oops_ra, line, re_Oops_ra.re_nsub+1,
-		re_Oops_ra_pmatch, 0);
-	if (debug > 3)
-		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
-	if (i == 0) {
-		re_strings(&re_Oops_ra, line, re_Oops_ra_pmatch,
-			string);
-		return((*string)[re_Oops_ra.re_nsub]);
-	}
-	return(NULL);
-}
-
-/* Set the MIPS ra from the ra line */
-static void Oops_set_ra(const char *value, SYMBOL_SET *ss)
-{
-	static const char procname[] = "Oops_set_ra";
-	elf_addr_t ra;
-	errno = 0;
-	ra = strtoul(value, NULL, 16);
-	if (errno) {
-		fprintf(stderr,
-			"%s Invalid hex value in ra line, ignored - '%s'\n",
-			procname, value);
-		perror(" ");
-		++errors;
-		ra = 0;
-	}
-	add_symbol_n(ss, ra, 'R', 1, ">>RA :");
-}
-
-/* Look for the Trace multilines :(.  Returns start of addresses. */
-static const char *Oops_trace(const char *line, char ***string, int string_max)
-{
-	int i;
-	const char *start = NULL;
-	static int trace_line = 0;
-	static regex_t     re_Oops_trace;
-	static regmatch_t *re_Oops_trace_pmatch;
-	static const char procname[] = "Oops_trace";
-
-	/* ppc is different, not a bracketed address, just an address */
-
-	/* Oops 'Trace' lines */
-	re_compile(&re_Oops_trace,
-			"^(Call Trace: )"			/*  1 */
-	/* alpha */	"|^(Trace: )"				/*  2 */
-	/* various */	"|(" BRACKETED_ADDRESS ")"	   	/* 3,4*/
-	/* ppc */	"|^(Call backtrace:)"			/*  5 */
-	/* ppc */	"|^(" UNBRACKETED_ADDRESS ")"		/* 6,7*/
-			,
-		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
-		&re_Oops_trace_pmatch);
-
-	re_string_check(re_Oops_trace.re_nsub+1, string_max, procname);
-	i = regexec(&re_Oops_trace, line, re_Oops_trace.re_nsub+1,
-		re_Oops_trace_pmatch, 0);
-	if (debug > 3)
-		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
-	if (i == 0) {
-		re_strings(&re_Oops_trace, line, re_Oops_trace_pmatch,
-			string);
-		if ((*string)[1] || (*string)[2]) {
-			trace_line = 1;
-			start = line + re_Oops_trace_pmatch[0].rm_eo;
-		}
-		else if ((*string)[5]) {
-			trace_line = 2;		/* ppc */
-			start = line + re_Oops_trace_pmatch[0].rm_eo;
-		}
-		else if (trace_line == 1 && (*string)[3])
-			start = line + re_Oops_trace_pmatch[3].rm_so;
-		else if (trace_line == 2 && (*string)[6])	/* ppc */
-			start = line + re_Oops_trace_pmatch[6].rm_so;
-		else
-			trace_line = 0;
-	}
-	else
-		trace_line = 0;
-	if (trace_line)
-		return(start);
-	return(NULL);
-}
-
-/* Process a trace call line, extract addresses */
-static void Oops_trace_line(const char *line, const char *p, SYMBOL_SET *ss)
-{
-	char **string = NULL;
-	regex_t *pregex;
-	regmatch_t *pregmatch;
-	static const char procname[] = "Oops_trace_line";
-
-	/* ppc does not bracket its addresses */
-	if (isxdigit(*p)) {
-		pregex = &re_unbracketed_address;
-		pregmatch = re_unbracketed_address_pmatch;
-	}
-	else {
-		pregex = &re_bracketed_address;
-		pregmatch = re_bracketed_address_pmatch;
-	}
-
-	/* Loop over [un]?bracketed addresses */
-	while (regexec(pregex, p, pregex->re_nsub+1, pregmatch, 0) == 0) {
-		re_strings(pregex, p, pregmatch, &string);
-		add_symbol(ss, string[1], 'T', 1, "Trace:");
-		p += pregmatch[0].rm_eo;
-	}
-
-	if (*p && !strcmp(p, "...")) {
-		fprintf(stderr,
-			"Warning garbage '%s' at end of trace line ignored "
-			"by %s\n",
-			p, procname);
-		++warnings;
-	}
-	re_strings_free(pregex, &string);
-}
-
-/* Do pattern matching to decide if the line should be printed.  When reading a
- * syslog containing multiple Oops, you need the intermediate data (registers,
- * tss etc.) to go with the decoded text.  Sets text to the start of the useful
- * text, after any prefix.  Note that any leading white space is treated as part
- * of the prefix, later routines do not see any indentation.
- */
-static int Oops_print(const char *line, const char **text, char ***string,
-		      int string_max)
-{
-	int i, print;
-	static int stack_line = 0, trace_line = 0;
-	static regex_t     re_Oops_prefix;
-	static regmatch_t *re_Oops_prefix_pmatch;
-	static regex_t     re_Oops_print;
-	static regmatch_t *re_Oops_print_pmatch;
-	static const char procname[] = "Oops_print";
-
-	*text = line;
-
-	/* Lines to be ignored.  For some reason the "amuse the user" print in
-	 * some die_if_kernel routines causes regexec to run very slowly.
-	 */
-
-	if (strstr(*text, "\\|/ ____ \\|/")  ||
-	    strstr(*text, "\"@'/ ,. \\`@\"") ||
-	    strstr(*text, "/_| \\__/ |_\\")  ||
-	    strstr(*text, "   \\__U_/"))
-		return(1);	/* print but avoid regexec */
-
-	/* Prefixes to be ignored */
-	re_compile(&re_Oops_prefix,
-			"^("			/* start of line */
-			"([^ ]{3} [ 0-9][0-9] [0-9]{2}:[0-9]{2}:[0-9]{2} "
-			  "[^ ]+ kernel: +)"	/* syslogd */
-			"|(<[0-9]+>)"		/* kmsg */
-			"|([ \t]+)"		/* leading white space */
-			")+"			/* any prefixes, in any order */
-			,
-		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
-		&re_Oops_prefix_pmatch);
-
-	re_string_check(re_Oops_prefix.re_nsub+1, string_max, procname);
-	i = regexec(&re_Oops_prefix, *text, re_Oops_prefix.re_nsub+1,
-		re_Oops_prefix_pmatch, 0);
-	if (debug > 3)
-		fprintf(stderr, "DEBUG: %s regexec prefix %d\n", procname, i);
-	if (i == 0)
-		*text += re_Oops_prefix_pmatch[0].rm_eo;  /* step over prefix */
-
-
-	/* Lots of possibilities.  Expand as required for all architectures.
-	 *
-	 * The order below is required to handle multiline outupt.
-	 * string[2] is defined if the text is 'Stack from '.
-	 * string[3] is defined if the text is 'Stack: '.
-	 * string[4] is defined if the text might be a stack continuation.
-	 * string[5] is defined if the text is 'Call Trace: '.
-	 * string[6] is defined if the text might be a trace continuation.
-	 * string[7] is the address part of the BRACKETED_ADDRESS.
-	 *
-	 * string[8] is defined if the text contains a version number.  No Oops
-	 * report contains this as of 2.1.125 but IMHO it should be added.  If
-	 * anybody wants to print a VERSION_nnnn line in their Oops, this code
-	 * is ready.
-	 *
-	 * string[9] is defined if the text is 'Trace: ' (alpha).
-	 * string[10] is defined if the text is 'Call backtrace:' (ppc).
-	 */
-	re_compile(&re_Oops_print,
-       /* arch type */					    /* Required order */
-			"("						/*  1 */
-	/* i386 */	"^(Stack: )"					/*  2 */
-	/* m68k */	"|^(Stack from )"				/*  3 */
-	/* various */	"|^([0-9a-fA-F]{4,})"				/*  4 */
-	/* various */	"|^(Call Trace: )"				/*  5 */
-	/* various */	"|^(" BRACKETED_ADDRESS ")"			/* 6,7*/
-	/* various */	"|^(Version_[0-9]+)"				/*  8 */
-	/* alpha */	"|^(Trace: )"					/*  9 */
-	/* ppc */	"|^(Call backtrace:)"				/* 10 */
-
-			/* order does not matter from here on */
-	
-	/* various */	"|(Unable to handle kernel)"
-	/* various */	"|^(Process .*stackpage=)"
-	/* various */	"|^(Call Trace:[ \t])"
-	/* various */	"|^(Code *:[ \t])"
-	/* various */	"|^(Kernel panic)"
-	/* various */	"|^(In swapper task)"
-	/* various */	"|(Aiee)"      /* anywhere in text is a bad sign (TM) */
-	/* various */	"|(die_if_kernel)"	/* ditto */
-
-	/* i386 2.0 */	"|^(Corrupted stack page)"
-	/* i386 */	"|^(invalid operand: )"
-	/* i386 */	"|^(Oops: )"
-	/* i386 */	"|^(Cpu: +[0-9])"
-	/* i386 */	"|^(current->tss)"
-	/* i386 */	"|^(\\*pde +=)"
-	/* i386 */	"|^(EIP: )"
-	/* i386 */	"|^(EFLAGS: )"
-	/* i386 */	"|^(eax: )"
-	/* i386 */	"|^(esi: )"
-	/* i386 */	"|^(ds: )"
-
-	/* m68k */	"|^(pc=)"
-	/* m68k */	"|^(68060 access)"
-	/* m68k */	"|^(Exception at )"
-	/* m68k */	"|^(PC: )"
-	/* m68k */	"|^(d[04]: )"
-	/* m68k */	"|^(Frame format=)"
-	/* m68k */	"|^(wb [0-9] stat)"
-	/* m68k */	"|^(push data: )"
-	/* m68k */	"|^(baddr=)"
-	/* any other m68K lines to print? */
-
-	/* sparc */	"|(\\([0-9]\\): Oops )"
-	/* sparc */	"|(: memory violation)"
-	/* sparc */	"|(: Exception at)"
-	/* sparc */	"|(: Arithmetic fault)"
-	/* sparc */	"|(: Instruction fault)"
-	/* sparc */	"|(: arithmetic trap)"
-	/* sparc */	"|^(Bad unaligned kernel)"
-	/* sparc */	"|^(Forwarding unaligned exception)"
-	/* sparc */	"|^(: unhandled unaligned exception)"
-	/* sparc */	"|(: unaligned trap)"
-	/* sparc */	"|^(<sc)"
-	/* sparc */	"|^(pc *=)"
-	/* sparc */	"|^(r[0-9]+ *=)"
-	/* sparc */	"|^(gp *=)"
-	/* any other sparc lines to print? */
-
-	/* alpha */	"|^(tsk->)"
-	/* alpha   die_if_kernel has no fixed text, identify by (pid): text. */
-	/* alpha */	"|^(.*\\([0-9]+\\): "
-			   /* Somebody has been playful with the texts.  */
-			   "((Whee)|(Oops)|(Kernel)|(.*Penguin)|(BOGUS))"
-			  ")"
-	/* alpha */	"|^(PSR: )"
-	/* alpha */	"|^(g0: )"
-	/* alpha */	"|^(o0: )"
-	/* alpha */	"|^(l0: )"
-	/* alpha */	"|^(i0: )"
-	/* alpha */	"|^(Instruction DUMP: )"
-	/* any other alpha lines to print? */
-
-	/* ppc */	"|^(MSR: )"
-	/* ppc */	"|^(TASK = )"
-	/* ppc */	"|^(last math )"
-	/* ppc */	"|^(GPR[0-9]+: )"
-	/* ppc */	"|(kernel pc )"
-	/* ppc */	"|(trap at PC: )"
-	/* ppc */	"|(bad area pc )"
-	/* ppc */	"|(NIP: )"
-	/* any other ppc lines to print? */
-
-	/* MIPS */	"|^(\\$[0-9 ]+:)"
-	/* MIPS */	"|^(epc )"
-	/* MIPS */	"|^(Status:)"
-	/* MIPS */	"|^(Cause :)"
-	/* MIPS */	"|( ra *=)"
-	/* any other MIPS lines to print? */
-
-			")",
-		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
-		&re_Oops_print_pmatch);
-
-	re_string_check(re_Oops_print.re_nsub+1, string_max, procname);
-	i = regexec(&re_Oops_print, *text, re_Oops_print.re_nsub+1,
-		re_Oops_print_pmatch, 0);
-	if (debug > 3)
-		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
-	print = 0;
-	if (i == 0) {
-		re_strings(&re_Oops_print, *text, re_Oops_print_pmatch,
-			string);
-		print = 1;
-		/* Handle multiline messages, messy */
-		if (!(*string)[2] && !(*string)[3] && !(*string)[4])
-			stack_line = 0;
-		else if ((*string)[2] || (*string)[3])
-			stack_line = 1;
-		else if (stack_line && !(*string)[4]) {
-			print = 0;
-			stack_line = 0;
-		}
-		if (!(*string)[5] && !(*string)[6] && !(*string)[9])
-			trace_line = 0;
-		else if ((*string)[5] || (*string)[9] || (*string)[10])
-			trace_line = 1;
-		else if (stack_line && !(*string)[6]) {
-			print = 0;
-			trace_line = 0;
-		}
-		if ((*string)[8])
-			add_Version((*string)[8]+8, "Oops");
-	}
-	return(print);
-}
-
-/* Look for the Code: line.  Returns start of the code bytes. */
-static const char *Oops_code(const char *line, char ***string, int string_max)
-{
-	int i;
-	static regex_t     re_Oops_code;
-	static regmatch_t *re_Oops_code_pmatch;
-	static const char procname[] = "Oops_code";
-
-	/* Oops 'Code: ' hopefully followed by at least one hex code.  sparc
-	 * brackets the PC in '<' and '>'.
-	 */
-	re_compile(&re_Oops_code,
-			"^("						/*  1 */
-	/* sparc */	  "(Instruction DUMP)"				/*  2 */
-	/* various */	  "|(Code *)"					/*  3 */
-			")"
-			":[ \t]+"
-			"("						/*  4 */
-			  "(general protection.*)"
-			  "|(<[0-9]+>)"
-			  "|((<?[0-9a-fA-F]+>?[ \t]*)+)"
-			")"
-			"(.*)$"				/* trailing garbage */
-			,
-		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
-		&re_Oops_code_pmatch);
-
-	re_string_check(re_Oops_code.re_nsub+1, string_max, procname);
-	i = regexec(&re_Oops_code, line, re_Oops_code.re_nsub+1,
-		re_Oops_code_pmatch, 0);
-	if (debug > 3)
-		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
-	if (i == 0) {
-		re_strings(&re_Oops_code, line, re_Oops_code_pmatch,
-			string);
-		if ((*string)[re_Oops_code.re_nsub] &&
-		    *((*string)[re_Oops_code.re_nsub])) {
-			fprintf(stderr,
-				"Warning: trailing garbage ignored on Code: "
-				"line\n"
-				"  Text: '%s'\n"
-				"  Garbage: '%s'\n",
-				line, (*string)[re_Oops_code.re_nsub]);
-			++warnings;
-		}
-		return((*string)[4]);
-	}
-	return(NULL);
-}
-
-/******************************************************************************/
-/*                      End architecture sensitive code                       */
-/******************************************************************************/
-
-/* Decode the Oops Code: via objdump*/
-static void Oops_decode(const unsigned char* code_text, elf_addr_t eip,
-			SYMBOL_SET *ss, char ***string, int string_max)
-{
-	FILE *f;
-	char *file, *line = NULL, code[CODE_SIZE];
-	int size = 0, adjust;
-	static char const procname[] = "Oops_decode";
-
-	if (debug)
-		fprintf(stderr, "DEBUG: %s\n", procname);
-	/* text to binary */
-	if (!Oops_code_values(code_text, code, &adjust, string, string_max))
-		return;
-	/* binary to same format as ksymoops */
-	if (!(file = Oops_code_to_file(code, CODE_SIZE)))
-		return;
-	/* objdump the pseudo object */
-	if (!(f = Oops_objdump(file)))	
-		return;
-	while (fgets_local(&line, &size, f, procname)) {
-		if (debug > 1)
-			fprintf(stderr, "DEBUG: %s - %s\n", procname, line);
-		Oops_decode_one(ss, line, eip, adjust);
-	}
-	pclose_local(f, procname);	/* opened in Oops_objdump */
-	free(line);
-	if (unlink(file)) {
-		fprintf(stderr, "%s could not unlink %s", prefix, file);
-		perror(" ");
-	}
-}
-
-/* Reached the end of an Oops report, format the extracted data. */
-static void Oops_format(const SYMBOL_SET *ss_format)
-{
-	int i;
-	SYMBOL *s;
-	static const char procname[] = "Oops_format";
-
-	if (debug)
-		fprintf(stderr, "DEBUG: %s\n", procname);
-
-	compare_Version();	/* Oops might have a version one day */
-	printf("\n");
-	for (s = ss_format->symbol, i = 0; i < ss_format->used; ++i, ++s) {
-		/* For type C data, print Code:, address, map, "name" (actually
-		 * the text of an objdump line).  For other types print name,
-		 * address, map.
-		 */
-		if (s->type == 'C')
-			printf("Code:  %s %-30s %s\n",
-				format_address(s->address),
-				map_address(&ss_merged, s->address),
-				s->name);
-		else
-			printf("%s %s %s\n",
-				s->name,
-				format_address(s->address),
-				map_address(&ss_merged, s->address));
-	}
-	printf("\n");
-}
-
-/* Select next Oops input file */
-static FILE *Oops_next_file(int *filecount, char * const **filename)
-{
-	static FILE *f = NULL;
-	static const char procname[] = "Oops_next_file";
-	static int first_file = 1;
-
-	if (first_file) {
-		f = stdin;
-		first_file = 0;
-	}
-	while (*filecount) {
-		if (f)
-			fclose_local(f, procname);
-		f = NULL;
-		if (regular_file(**filename, procname))
-			f = fopen_local(**filename, "r", procname);
-		if (f) {
-			if (debug)
-				fprintf(stderr,
-					"DEBUG: reading Oops report "
-					"from %s\n", **filename);
-		}
-		++*filename;
-		--*filecount;
-		if (f)
-			return(f);
-	}
-	return(f);
-}
-
-/* Read the Oops report */
-#define MAX_STRINGS 300	/* Maximum strings in any Oops re */
-void Oops_read(int filecount, char * const *filename)
-{
-	char *line = NULL, **string = NULL;
-	const char *start, *text;
-	int i, size = 0, lineno = 0, lastprint = 0;
-	elf_addr_t eip = 0;
-	FILE *f;
-	SYMBOL_SET ss_format;
-	static const char procname[] = "Oops_read";
-
-	ss_init(&ss_format, "Oops log data");
-
-	if (!filecount && isatty(0))
-		printf("Reading Oops report from the terminal\n");
-
-	string = malloc(MAX_STRINGS*sizeof(*string));
-	if (!string)
-		malloc_error(procname);
-	memset(string, '\0', MAX_STRINGS*sizeof(*string));
-
-	do {
-		if (!(f = Oops_next_file(&filecount, &filename)))
-			continue;
-		while (fgets_local(&line, &size, f, procname)) {
-			if (debug > 2)
-				fprintf(stderr,
-					"DEBUG: %s - %s\n", procname, line);
-			++lineno;
-			if (Oops_print(line, &text, &string, MAX_STRINGS)) {
-				puts(line);
-				lastprint = lineno;
-			}
-			if ((start = Oops_eip(text, &string, MAX_STRINGS)))
-				Oops_set_eip(start, &eip, &ss_format);
-			if ((start = Oops_ra(text, &string, MAX_STRINGS)))
-				Oops_set_ra(start, &ss_format);
-			if ((start = Oops_trace(text, &string, MAX_STRINGS)))
-				Oops_trace_line(text, start, &ss_format);
-			if ((start = Oops_code(text, &string, MAX_STRINGS))) {
-				Oops_decode(start, eip, &ss_format,
-					&string, MAX_STRINGS);
-				Oops_format(&ss_format);
-				ss_free(&ss_format);
-			}
-			/* More than 5 (arbitrary) lines which were not printed
-			 * and there is some saved data, assume we missed the
-			 * Code: line.
-			 */
-			if (ss_format.used && lineno > lastprint+5) {
-				fprintf(stderr,
-					"Warning, Code line not seen, dumping "
-					"what data is available\n");
-				++warnings;
-				Oops_format(&ss_format);
-				ss_free(&ss_format);
-			}
-		}
-		if (ss_format.used) {
-			fprintf(stderr,
-				"Warning, Code line not seen, dumping "
-				"what data is available\n");
-			++warnings;
-			Oops_format(&ss_format);
-			ss_free(&ss_format);
-		}
-	} while (filecount != 0);
-
-	for (i = 0; i < sizeof(string); ++i) {
-		free(string[i]);
-		string[i] = NULL;
-	}
-	free(line);
-}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/patches/README linux/scripts/ksymoops-0.6/patches/README
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/patches/README	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/patches/README	Wed Dec 31 16:00:00 1969
@@ -1,15 +0,0 @@
-An ad-hoc collection of patches to the Linux kernel and related
-utilities, to make the best use of ksymoops.
-
-Most of the kernel patches are to extend the information logged on an
-Oops, some architectures currently (2.1.126) do not provide full
-diagnostics.  Hopefully these patches will be integrated into the
-kernel tree in future.  ksymoops will still run without these patches
-but will provide degraded output.
-
-mips    - Kernel patch by Ulf Carlsson <ulfc@bun.falkenberg.se> to
-          provide full diagnostics on a mips Oops.
-
-ppc     - Kernel patch by "Ryan Nielsen" <ran@krazynet.com> to provide
-          full diagnostics on a ppc Oops.
-
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/patches/mips linux/scripts/ksymoops-0.6/patches/mips
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/patches/mips	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/patches/mips	Wed Dec 31 16:00:00 1969
@@ -1,167 +0,0 @@
---- linux/arch/mips/kernel/traps.c-orig	Thu Oct 29 17:23:19 1998
-+++ linux/arch/mips/kernel/traps.c	Fri Oct 30 13:25:21 1998
-@@ -6,6 +6,7 @@
-  *
-  * Copyright 1994, 1995, 1996, 1997, 1998 by Ralf Baechle
-  * Modified for R3000 by Paul M. Antoine, 1995, 1996
-+ * Complete output from die() by Ulf Carlsson, 1998
-  */
- #include <linux/config.h>
- #include <linux/init.h>
-@@ -80,50 +81,61 @@
-  * This routine abuses get_user()/put_user() to reference pointers
-  * with at least a bit of error checking ...
-  */
--void show_registers(char * str, struct pt_regs * regs, long err)
-+void show_stack(unsigned int *sp)
- {
--	int	i;
--	int	*stack;
--	u32	*sp, *pc, addr, module_start, module_end;
--	extern	char start_kernel, _etext;
-+	int i;
-+	unsigned int *stack;
- 
--	sp = (u32 *)regs->regs[29];
--	pc = (u32 *)regs->cp0_epc;
-+	stack = sp;
-+	i = 0;
- 
--	show_regs(regs);
-+	printk("Stack:");
-+	while ((unsigned long) stack & (PAGE_SIZE - 1)) {
-+		unsigned long stackdata;
- 
--	/*
--	 * Dump the stack
--	 */
--	printk("Process %s (pid: %ld, stackpage=%08lx)\nStack: ",
--		current->comm, current->pid, (unsigned long)current);
--	for(i=0;i<5;i++)
--		printk("%08x ", *sp++);
--	stack = (int *) sp;
-+		if (__get_user(stackdata, stack++)) {
-+			printk(" (Bad stack address)");
-+			break;
-+		}
- 
--	for(i=0; i < kstack_depth_to_print; i++) {
--		unsigned int stackdata;
-+		printk(" %08lx", stackdata);
- 
--		if (((u32) stack & (PAGE_SIZE -1)) == 0)
--			break;
--		if (i && ((i % 8) == 0))
--			printk("\n       ");
--		if (get_user(stackdata, stack++) < 0) {
--			printk("(Bad stack address)");
-+		if (++i > 40) {
-+			printk(" ...");
- 			break;
- 		}
--		printk("%08x ", stackdata);
-+
-+		if (i % 8 == 0)
-+			printk("\n      ");
- 	}
--	printk("\nCall Trace: ");
--	stack = (int *)sp;
--	i = 1;
-+}
-+
-+void show_trace(unsigned int *sp)
-+{
-+	int i;
-+	unsigned int *stack;
-+	unsigned long kernel_start, kernel_end;
-+	unsigned long module_start, module_end;
-+	extern char _stext, _etext;
-+
-+	stack = sp;
-+	i = 0;
-+
-+	kernel_start = (unsigned long) &_stext;
-+	kernel_end = (unsigned long) &_etext;
- 	module_start = VMALLOC_START;
- 	module_end = module_start + MODULE_RANGE;
--	while (((unsigned long)stack & (PAGE_SIZE -1)) != 0) {
--		if (get_user(addr, stack++) < 0) {
--			printk("(Bad address)\n");
-+
-+	printk("\nCall Trace:");
-+
-+	while ((unsigned long) stack & (PAGE_SIZE -1)) {
-+		unsigned long addr;
-+
-+		if (__get_user(addr, stack++)) {
-+			printk(" (Bad stack address)\n");
- 			break;
- 		}
-+
- 		/*
- 		 * If the address is either in the text segment of the
- 		 * kernel, or in the region which contains vmalloc'ed
-@@ -132,26 +144,33 @@
- 		 * down the cause of the crash will be able to figure
- 		 * out the call path that was taken.
- 		 */
--		if (((addr >= (u32) &start_kernel) &&
--		     (addr <= (u32) &_etext)) ||
--		    ((addr >= module_start) && (addr <= module_end))) {
--			if (i && ((i % 8) == 0))
--				printk("\n       ");
--			printk("%08x ", addr);
--			i++;
-+
-+		if ((addr >= kernel_start && addr < kernel_end) ||
-+		    (addr >= module_start && addr < module_end)) { 
-+
-+			printk(" [<%08lx>]", addr);
-+			if (++i > 40) {
-+				printk(" ...");
-+				break;
-+			}
- 		}
- 	}
-+}
- 
--	printk("\nCode : ");
--	if ((KSEGX(pc) == KSEG0 || KSEGX(pc) == KSEG1) &&
--	    (((unsigned long) pc & 3) == 0))
--	{
--		for(i=0;i<5;i++)
--			printk("%08x ", *pc++);
--		printk("\n");
-+void show_code(unsigned int *pc)
-+{
-+	long i;
-+
-+	printk("\nCode:");
-+
-+	for(i = -3 ; i < 6 ; i++) {
-+		unsigned long insn;
-+		if (__get_user(insn, pc + i)) {
-+			printk(" (Bad address in epc)\n");
-+			break;
-+		}
-+		printk("%c%08lx%c",(i?' ':'<'),insn,(i?' ':'>'));
- 	}
--	else
--		printk("(Bad address in epc)\n");
- }
- 
- void die(const char * str, struct pt_regs * regs, unsigned long err)
-@@ -162,6 +181,12 @@
- 	console_verbose();
- 	printk("%s: %04lx\n", str, err & 0xffff);
- 	show_regs(regs);
-+	printk("Process %s (pid: %ld, stackpage=%08lx)\n",
-+		current->comm, current->pid, (unsigned long) current);
-+	show_stack((unsigned int *) regs->regs[29]);
-+	show_trace((unsigned int *) regs->regs[29]);
-+	show_code((unsigned int *) regs->cp0_epc);
-+	printk("\n");
- 	do_exit(SIGSEGV);
- }
- 
-
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/patches/ppc linux/scripts/ksymoops-0.6/patches/ppc
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/patches/ppc	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/patches/ppc	Wed Dec 31 16:00:00 1969
@@ -1,67 +0,0 @@
---- linux/arch/ppc/kernel/process.c	1998/10/11 17:47:23	1.67
-+++ linux/arch/ppc/kernel/process.c	1998/11/02 03:11:28
-@@ -196,6 +198,19 @@
- 	_enable_interrupts(s);
- }
- 
-+void instruction_dump (unsigned long *pc)
-+{
-+	int i;
-+
-+	if((((unsigned long) pc) & 3))
-+                return;
-+
-+	printk("Instruction DUMP:");
-+	for(i = -3; i < 6; i++)
-+		printk("%c%08lx%c",i?' ':'<',pc[i],i?' ':'>');
-+	printk("\n");
-+}
-+
- void show_regs(struct pt_regs * regs)
- {
- 	int i;
---- linux/arch/ppc/kernel/traps.c	1998/05/05 19:18:53	1.21
-+++ linux/arch/ppc/kernel/traps.c	1998/11/02 03:11:36
-@@ -79,6 +79,7 @@
- 		debugger(regs);
- #endif
- 		print_backtrace((unsigned long *)regs->gpr[1]);
-+		instruction_dump((unsigned long *)regs->nip);
- 		panic("Exception in kernel pc %lx signal %d",regs->nip,signr);
- 	}
- 	force_sig(signr, current);
-@@ -126,6 +127,7 @@
- 		debugger(regs);
- #endif
- 		print_backtrace((unsigned long *)regs->gpr[1]);
-+		instruction_dump((unsigned long *)regs->nip);
- 		panic("machine check");
- 	}
- 	_exception(SIGSEGV, regs);	
-@@ -219,6 +221,7 @@
- #endif
- 	show_regs(regs);
- 	print_backtrace((unsigned long *)regs->gpr[1]);
-+	instruction_dump((unsigned long *)regs->nip);
- 	panic("kernel stack overflow");
- }
- 
---- linux/arch/ppc/mm/fault.c	1998/10/06 03:13:19	1.28
-+++ linux/arch/ppc/mm/fault.c	1998/11/02 03:11:36
-@@ -89,6 +89,7 @@
- 			printk("page fault in interrupt handler, addr=%lx\n",
- 			       address);
- 			show_regs(regs);
-+			instruction_dump((unsigned long *)regs->nip);
- #if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
- 			if (debugger_kernel_faults)
- 				debugger(regs);
-@@ -174,6 +175,7 @@
- 	/* kernel has accessed a bad area */
- 	show_regs(regs);
- 	print_backtrace( (unsigned long *)regs->gpr[1] );
-+	instruction_dump((unsigned long *)regs->nip);
- #if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
- 	if (debugger_kernel_faults)
- 		debugger(regs);
-
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/re.c linux/scripts/ksymoops-0.6/re.c
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/re.c	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/re.c	Wed Dec 31 16:00:00 1969
@@ -1,145 +0,0 @@
-/*
-	re.c.
-
-	Regular expression processing for ksymoops.
-
-	Copyright Keith Owens <kaos@ocs.com.au>.
-	Released under the GNU Public Licence, Version 2.
-
-	Tue Nov  3 02:31:01 EST 1998
-	Version 0.6
-	PPC trace addresses are not bracketed, add new re.
-
-	Wed Oct 28 13:47:23 EST 1998
-	Version 0.4
-	Split into separate sources.
- */
-
-#include "ksymoops.h"
-#include <malloc.h>
-#include <string.h>
-
-/* Compile a regular expression */
-void re_compile(regex_t *preg, const char *regex, int cflags,
-		regmatch_t **pmatch)
-{
-	int i, l;
-	char *p;
-	static char const procname[] = "re_compile";
-
-	if (preg->re_nsub)
-		return;		/* already compiled */
-
-	if (debug)
-		fprintf(stderr, "DEBUG: %s '%s'", procname, regex);
-	if ((i = regcomp(preg, regex, cflags))) {
-		l = regerror(i, preg, NULL, 0);
-		++l;	/* doc is ambiguous, be safe */
-		p = malloc(l);
-		if (!p)
-			malloc_error("regerror text");
-		regerror(i, preg, p, l);
-		fprintf(stderr,
-			"%s: fatal %s error on '%s' - %s\n",
-			prefix, procname, regex, p);
-		exit(2);
-	}
-	if (debug)
-		fprintf(stderr, " %d sub expression(s)\n", preg->re_nsub);
-	/* [0] is entire match, [1] is first substring */
-	*pmatch = malloc((preg->re_nsub+1)*sizeof(**pmatch));
-	if (!*pmatch)
-		malloc_error("pmatch");
-
-}
-
-/* Compile common regular expressions */
-void re_compile_common(void)
-{
-
-	/* nm: address, type, symbol */
-	re_compile(&re_nm,
-		"^([0-9a-fA-F]{4,}) +([^ ]) +([^ ]+)$",
-		REG_NEWLINE|REG_EXTENDED,
-		&re_nm_pmatch);
-
-	/* bracketed address preceded by optional white space */
-	re_compile(&re_bracketed_address,
-		"^[ \t]*" BRACKETED_ADDRESS,
-		REG_NEWLINE|REG_EXTENDED,
-		&re_bracketed_address_pmatch);
-
-	/* unbracketed address preceded by optional white space */
-	re_compile(&re_unbracketed_address,
-		"^[ \t*]*" UNBRACKETED_ADDRESS,
-		REG_NEWLINE|REG_EXTENDED,
-		&re_unbracketed_address_pmatch);
-
-}
-
-/* Split text into the matching re substrings - Perl is so much easier :).
- * Each element of *string is set to a malloced copy of the substring or
- * NULL if the substring did not match (undef).  A zero length substring match
- * is represented by a zero length **string.
- */
-void re_strings(regex_t *preg, const char *text, regmatch_t *pmatch,
-		char ***string)
-{
-	int i;
-	if (!*string) {
-		*string = malloc((preg->re_nsub+1)*sizeof(**string));
-		if (!*string)
-			malloc_error("re_strings base");
-		for (i = 0; i < preg->re_nsub+1; ++i)
-			(*string)[i] = NULL;
-	}
-	for (i = 0; i < preg->re_nsub+1; ++i) {
-		if (debug > 4)
-			fprintf(stderr,
-				"DEBUG: re_string %d offsets %d %d",
-				i, pmatch[i].rm_so, pmatch[i].rm_eo);
-		if (pmatch[i].rm_so == -1) {
-			/* no match for this sub expression */
-			free((*string)[i]);
-			(*string)[i] = NULL;
-			if (debug > 4)
-				fprintf(stderr, " (undef)\n");
-		}
-		else {
-			int l = pmatch[i].rm_eo - pmatch[i].rm_so + 1;
-			char *p;
-			p = malloc(l);
-			if (!p)
-				malloc_error("re_strings");
-			strncpy(p, text+pmatch[i].rm_so, l-1);
-			*(p+l-1) = '\0';
-			(*string)[i] = p;
-			if (debug > 4)
-				fprintf(stderr, " '%s'\n", p);
-		}
-	}
-}
-
-/* Free the matching re substrings */
-void re_strings_free(const regex_t *preg, char ***string)
-{
-	if (*string) {
-		int i;
-		for (i = 0; i < preg->re_nsub+1; ++i)
-			free((*string)[i]);
-		free(*string);
-		*string = NULL;
-	}
-}
-
-/* Check that there are enough strings for an re */
-void re_string_check(int need, int available, const char *msg)
-{
-	if (need > available) {
-		fprintf(stderr,
-			"%s: fatal not enough re_strings in %s.  "
-			"Need %d, available %d\n",
-			prefix, msg, need, available);
-		exit(2);
-	}
-}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops-0.6/symbol.c linux/scripts/ksymoops-0.6/symbol.c
--- v2.2.0-pre4/linux/scripts/ksymoops-0.6/symbol.c	Mon Jan  4 15:08:18 1999
+++ linux/scripts/ksymoops-0.6/symbol.c	Wed Dec 31 16:00:00 1969
@@ -1,440 +0,0 @@
-/*
-	symbol.c.
-
-	Symbol handling routines for ksymoops.
-
-	Copyright Keith Owens <kaos@ocs.com.au>.
-	Released under the GNU Public Licence, Version 2.
-
-	Tue Nov  3 02:31:01 EST 1998
-	Version 0.6
-	Fix end of code calculation.
-
-	Wed Oct 28 13:47:23 EST 1998
-	Version 0.4
-	Split into separate sources.
- */
-
-#include "ksymoops.h"
-#include <errno.h>
-#include <malloc.h>
-#include <stdlib.h>
-#include <string.h>
-
-/* Initialise a symbol source */
-void ss_init(SYMBOL_SET *ss, const char *msg)
-{
-	memset(ss, '\0', sizeof(*ss));
-	ss->source = strdup(msg);
-	if (!ss->source)
-		malloc_error(msg);
-}
-
-/* Free dynamic data from a symbol source */
-void ss_free(SYMBOL_SET *ss)
-{
-	int i;
-	SYMBOL *s;
-	for (s = ss->symbol, i = 0; i < ss->used; ++i, ++s)
-		free(s->name);
-	free(ss->symbol);
-	free(ss->source);
-	memset(ss, '\0', sizeof(*ss));
-}
-
-/* Initialise common symbol sets */
-void ss_init_common(void)
-{
-	ss_init(&ss_Version, "Version_");
-}
-
-/* Find a symbol name in a symbol source.  Brute force ascending order search,
- * no hashing.  If start is not NULL, it contains the starting point for the
- * scan and is updated to point to the found entry.  If the entry is not found,
- * return NULL with start pointing to the next highest entry.
- * NOTE: Assumes that ss is sorted by name.
- */
-SYMBOL *find_symbol_name(const SYMBOL_SET *ss, const char *symbol, int *start)
-{
-	int i, l;
-	SYMBOL *s;
-	for (i = start ? *start : 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
-		if ((l = strcmp(symbol, s->name)) == 0) {
-			if (start)
-				*start = i;
-			return(s);
-		}
-		if (l < 0)
-			break;
-	}
-	if (start)
-		*start = i;
-	return NULL;
-}
-
-/* Find an address in a symbol source.  Brute force ascending order search, no
- * hashing.  If start is not NULL, it contains the starting point for the scan
- * and is updated to point to the found entry.  If the entry is not found,
- * return NULL with start pointing to the next highest entry.
- * NOTE: Assumes that ss is sorted by address.
- */
-static SYMBOL *find_symbol_address(const SYMBOL_SET *ss,
-			    const elf_addr_t address, int *start)
-{
-	int i;
-	SYMBOL *s;
-	for (i = start ? *start : 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
-		if (address > s->address)
-			continue;
-		else if (address == s->address) {
-			if (start)
-				*start = i;
-			return(s);
-		}
-		else
-			break;
-	}
-	if (start)
-		*start = i;
-	return NULL;
-}
-
-/* Add a symbol to a symbol set, address in binary */
-void add_symbol_n(SYMBOL_SET *ss, const elf_addr_t address,
-		  const char type, const char keep, const char *symbol)
-{
-	int i;
-	char **string = NULL;
-	SYMBOL *s;
-	static regex_t     re_symbol_ver;
-	static regmatch_t *re_symbol_ver_pmatch;
-	static const char procname[] = "add_symbol_n";
-
-	/* Strip out any trailing symbol version _Rxxxxxxxx. */
-	re_compile(&re_symbol_ver,
-		"^(.*)_R[0-9a-fA-F]{8,}$",
-		REG_NEWLINE|REG_EXTENDED,
-		&re_symbol_ver_pmatch);
-
-	i = regexec(&re_symbol_ver, symbol,
-		    re_symbol_ver.re_nsub+1, re_symbol_ver_pmatch, 0);
-	if (debug > 3)
-		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
-	if (i == 0)
-		re_strings(&re_symbol_ver, symbol, re_symbol_ver_pmatch,
-			&string);
-
-	if (debug > 3)
-		fprintf(stderr, "DEBUG: %s %s %s '%c' %d '%s'\n",
-			procname, ss->source, format_address(address),
-			type, keep, i == 0 ? string[1] : symbol);
-	if (ss->used > ss->alloc) {
-		fprintf(stderr,
-			"%s: fatal %s ss %s used (%d) > alloc (%d)\n",
-			procname, prefix, ss->source, ss->used, ss->alloc);
-		exit(2);
-	}
-	if (ss->used == ss->alloc) {
-		/* increase by 20% or 10, whichever is larger, arbitrary */
-		int newsize = ss->alloc*120/100;
-		if (newsize < ss->alloc+10)
-			newsize = ss->alloc+10;
-		if (debug > 3)
-			fprintf(stderr,
-				"DEBUG: %s increasing %s from %d to %d "
-				"entries\n",
-				procname, ss->source, ss->alloc, newsize);
-		ss->symbol = realloc(ss->symbol, newsize*sizeof(*(ss->symbol)));
-		if (!ss->symbol)
-			malloc_error("realloc ss");
-		ss->alloc = newsize;
-	}
-	s = ss->symbol+ss->used;
-	if (i == 0) {
-		s->name = string[1];
-		string[1] = NULL;	/* don't free this one */
-	}
-	else {
-		s->name = strdup(symbol);
-		if (!s->name)
-			malloc_error("strdup symbol");
-	}
-	s->type = type;
-	s->keep = keep;
-	s->address = address;
-	++ss->used;
-	re_strings_free(&re_symbol_ver, &string);
-}
-
-/* Add a symbol to a symbol set, address in character */
-void add_symbol(SYMBOL_SET *ss, const char *address, const char type,
-		const char keep, const char *symbol)
-{
-	elf_addr_t a;
-	static char const procname[] = "add_symbol";
-	errno = 0;
-	a = strtoul(address, NULL, 16);
-	if (errno) {
-		fprintf(stderr,
-			"%s: %s address '%s' is in error",
-			prefix, procname, address);
-		perror(" ");
-		++errors;
-	}
-	add_symbol_n(ss, a, type, 1, symbol);
-}
-
-/* Map an address to symbol, offset and length, address in binary */
-char *map_address(const SYMBOL_SET *ss, const elf_addr_t address)
-{
-	int i = 0, l;
-	SYMBOL *s;
-	static char *map = NULL;
-	static int size = 0;
-	static const char procname[] = "map_address_n";
-
-	if (debug > 2)
-		fprintf(stderr, "DEBUG: %s %s %s\n",
-			procname, ss->source, format_address(address));
-	s = find_symbol_address(ss, address, &i);
-	if (!s && --i >= 0)
-		s = ss->symbol+i;	/* address is between s and s+1 */
-			
-	/* Extra map text is always < 100 bytes */
-	if (s)
-		l = strlen(s->name) + 100;
-	else
-		l = 100;
-	if (l > size) {
-		map = realloc(map, l);
-		if (!map)
-			malloc_error(procname);
-		size = l;
-	}
-	if (!s) {
-		if (ss->used == 0)
-			snprintf(map, size, "No symbols available");
-		else
-			snprintf(map, size, "Before first symbol");
-	}
-	else if ((i+1) >= ss->used) {
-		/* Somewhere past last symbol.  Length of last section of code
-		 * is unknown, arbitrary cutoff at 32K.
-		 */
-		elf_addr_t offset = address - s->address;
-		if (offset > 32768)
-			snprintf(map, size, "<END_OF_CODE+%lx/????>", offset);
-		else
-			snprintf(map, size, "<%s+%lx/????>", s->name, offset);
-	}
-	else
-		snprintf(map, size,
-			"<%s+%lx/%lx>",
-			s->name, address - s->address,
-			(s+1)->address - s->address);
-	return(map);
-}
-
-/* After sorting, obsolete symbols are at the top.  Delete them. */
-static void ss_compress(SYMBOL_SET *ss)
-{
-	int i, j;
-	SYMBOL *s;
-	static const char procname[] = "ss_compress";
-
-	if (debug > 1)
-		fprintf(stderr, "DEBUG: %s on table %s, before %d ",
-			procname, ss->source, ss->used);
-	for (i = 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
-		if (!s->keep) {
-			for (j = i; j < ss->used; ++j, ++s) {
-				if (s->keep) {
-					fprintf(stderr,
-						"%s: fatal %s table %s is not "
-						"sorted\n",
-						prefix, procname, ss->source);
-					exit(2);
-				}
-			}
-			break;
-		}
-	}
-	for (j = i, s = ss->symbol+j; j < ss->used; ++j, ++s)
-		free(s->name);
-	ss->used = i;
-	if (debug > 1)
-		fprintf(stderr, "after %d\n", ss->used);
-}
-
-static int ss_compare_atn(const void *a, const void *b)
-{
-	SYMBOL *c = (SYMBOL *) a;
-	SYMBOL *d = (SYMBOL *) b;
-	int i;
-
-	/* obsolete symbols to the top */
-	if (c->keep != d->keep)
-		return(d->keep - c->keep);
-	if (c->address > d->address)
-		return(1);
-	if (c->address < d->address)
-		return(-1);
-	if (c->type > d->type)
-		return(1);
-	if (c->type < d->type)
-		return(-1);
-	if ((i = strcmp(c->name, d->name)))
-		return(i);
-	return(0);
-}
-
-/* Sort a symbol set by address, type and name */
-void ss_sort_atn(SYMBOL_SET *ss)
-{
-	if (debug)
-		fprintf(stderr, "DEBUG: sorting symbols for %s (atn)\n",
-			ss->source);
-	qsort((char *) ss->symbol, (unsigned) ss->used,
-		sizeof(*(ss->symbol)), ss_compare_atn);
-	ss_compress(ss);
-}
-
-static int ss_compare_na(const void *a, const void *b)
-{
-	SYMBOL *c = (SYMBOL *) a;
-	SYMBOL *d = (SYMBOL *) b;
-	int i;
-
-	/* obsolete symbols to the top */
-	if (c->keep != d->keep)
-		return(d->keep - c->keep);
-	if ((i = strcmp(c->name, d->name)))
-		return(i);
-	if (c->address > d->address)
-		return(1);
-	if (c->address < d->address)
-		return(-1);
-	return(0);
-}
-
-/* Sort a symbol set by name and address, drop duplicates.  There should be
- * no duplicates but I have seen duplicates in ksyms on 2.0.35.
- */
-void ss_sort_na(SYMBOL_SET *ss)
-{
-	int i;
-	SYMBOL *s;
-	if (debug)
-		fprintf(stderr, "DEBUG: sorting symbols for %s (na)\n",
-			ss->source);
-	qsort((char *) ss->symbol, (unsigned) ss->used,
-		sizeof(*(ss->symbol)), ss_compare_na);
-	ss_compress(ss);
-	s = ss->symbol;
-	for (i = 0; i < ss->used-1; ++i) {
-		if (strcmp(s->name, (s+1)->name) == 0 &&
-		    s->address == (s+1)->address) {
-			if (s->type != ' ')
-				(s+1)->keep = 0;
-			else
-				s->keep = 0;
-		}
-		++s;
-	}
-	qsort((char *) ss->symbol, (unsigned) ss->used,
-		sizeof(*(ss->symbol)), ss_compare_na);
-	ss_compress(ss);
-}
-
-/* Copy a symbol set, including all its strings */
-SYMBOL_SET *ss_copy(const SYMBOL_SET *ss)
-{
-	SYMBOL_SET *ssc;
-	if (debug > 3)
-		fprintf(stderr,
-			"DEBUG: ss_copy %s\n", ss->source);
-	ssc = malloc(sizeof(*ssc));
-	if (!ssc)
-		malloc_error("copy ssc");
-	ss_init(ssc, ss->source);
-	ssc->used = ss->used;
-	ssc->alloc = ss->used;	/* shrink the copy */
-	ssc->symbol = malloc(ssc->used*sizeof(*(ssc->symbol)));
-	if (!(ssc->symbol))
-		malloc_error("copy ssc symbols");
-	memcpy(ssc->symbol, ss->symbol, ssc->used*sizeof(*(ssc->symbol)));
-	return(ssc);
-}
-
-/* Convert version number to major, minor string.  */
-static const char *format_Version(elf_addr_t Version)
-{
-	static char string[12];	/* 255.255.255\0 worst case */
-	snprintf(string, sizeof(string), "%d.%d.%d",
-		(Version >> 16) & 0xff,
-		(Version >> 8) & 0xff,
-		(Version) & 0xff);
-	return(string);
-}
-
-/* Save version number.  The "address" is the version number, the "symbol" is
- * the source of the version.
- */
-void add_Version(const char *version, const char *source)
-{
-	static char const procname[] = "add_Version";
-	int i = atoi(version);
-	if (debug > 1)
-		fprintf(stderr, "DEBUG: %s %s %s %s\n",
-			procname, source, version, format_Version(i));
-	add_symbol_n(&ss_Version, i, 'V', 1, source);
-}
-
-/* Extract Version_ number from a symbol set and save it.  */
-void extract_Version(SYMBOL_SET *ss)
-{
-	int i = 0;
-	SYMBOL *s;
-
-	s = find_symbol_name(ss, "Version_", &i);
-	if (!s && i < ss->used)
-		s = ss->symbol+i;	/* first symbol after "Version_" */
-	if (!s || strncmp(s->name, "Version_", 8))
-		return;
-	add_Version(s->name+8, ss->source);
-}
-
-/* Compare all extracted Version numbers.  Silent unless there is a problem. */
-void compare_Version(void)
-{
-	int i = 0;
-	SYMBOL *s, *s0;
-	static int prev_used = 0;
-
-	if (!ss_Version.used)
-		return;
-	/* Only check if the Version table has changed in size */
-	if (prev_used == ss_Version.used)
-		return;
-
-	ss_sort_na(&ss_Version);
-	s0 = s = ss_Version.symbol;
-	if (debug)
-		fprintf(stderr, "DEBUG: Version %s\n",
-			format_Version(s0->address));
-	for (i = 0; i < ss_Version.used; ++i, ++s) {
-		if (s->address != s0->address) {
-			fprintf(stderr,
-				"Version mismatch error.  %s says %s, ",
-				s0->name,
-				format_Version(s0->address));
-			fprintf(stderr,
-				"%s says %s.  Expect lots of address "
-				"mismatches.\n",
-				s->name,
-				format_Version(s->address));
-			++errors;
-		}
-	}
-	prev_used = ss_Version.used;
-}
diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/tkgen.c linux/scripts/tkgen.c
--- v2.2.0-pre4/linux/scripts/tkgen.c	Wed Apr  1 20:11:55 1998
+++ linux/scripts/tkgen.c	Mon Jan  4 21:48:18 1999
@@ -307,10 +307,10 @@
     case tok_hex:
     case tok_string:
       printf("} then { ");
-      printf(".menu%d.config.f.x%d.x configure -state normal -fore [ cget .ref -foreground ]; ", menu_num, line_num);
+      printf(".menu%d.config.f.x%d.x configure -state normal -foreground [ cget .ref -foreground ]; ", menu_num, line_num);
       printf(".menu%d.config.f.x%d.l configure -state normal; ", menu_num, line_num);
       printf("} else { ");
-      printf(".menu%d.config.f.x%d.x configure -state disabled -fore [ cget .ref -disabledforeground ];", menu_num, line_num );
+      printf(".menu%d.config.f.x%d.x configure -state disabled -foreground [ cget .ref -disabledforeground ];", menu_num, line_num );
       printf(".menu%d.config.f.x%d.l configure -state disabled;", menu_num, line_num );
       printf("}\n");
       break;
