diff -u --recursive --new-file v2.1.121/linux/CREDITS linux/CREDITS
--- v2.1.121/linux/CREDITS	Wed Sep  9 14:51:03 1998
+++ linux/CREDITS	Mon Sep 14 12:50:54 1998
@@ -10,11 +10,11 @@
 ----------
 
 N: Matti Aarnio
-E: mea@utu.fi
-D: LILO for AHA1542, modularized several of drivers/net/,
-D: dynamic SLIP devices, dynamic /proc/net/, true size /proc/ksyms,
-D: and other hacks..
-D: Documenting various parts of network subsystem (kernel side)
+E: mea@nic.funet.fi
+D: Alpha systems hacking, IPv6 and other network related stuff
+D: One of assisting postmasters for vger.rutgers.edu's lists
+S: (ask for current address)
+S: Finland
 
 N: Werner Almesberger
 E: werner.almesberger@lrc.di.epfl.ch
@@ -1403,7 +1403,8 @@
 S: USA
 
 N: Avery Pennarun
-E: apenwarr@bond.net
+E: apenwarr@worldvisions.ca
+W: http://www.worldvisions.ca/~apenwarr/
 D: ARCnet driver
 D: "make xconfig" improvements
 D: Various minor hacking
@@ -1615,14 +1616,12 @@
 S: USA
 
 N: Mike Shaver
-E: shaver@ingenia.com
-W: http://neon.ingenia.com/~shaver/
-D: Network hacking, /proc/sys/net
-S: c/o Ingenia Communications Corporation
-S: Suite 4200, CTTC Building
-S: 1125 Colonel By Drive
+E: shaver@netscape.com
+W: http://people.netscape.com/shaver/
+D: MIPS work, /proc/sys/net, misc net hacking
+S: 149 Union St.
 S: Ottawa, Ontario
-S: Canada K1S 5R1
+S: Canada K7L 2P4
 
 N: John Shifflett
 E: john@geolog.com
@@ -1698,6 +1697,14 @@
 S: Willowdale, Ontario
 S: Canada M2N 2Z1
 
+N: Adrian Sun
+E: asun@u.washington.edu
+D: hfs support
+D: alpha rtc port, random appletalk fixes
+S: Department of Zoology, University of Washington
+S: Seattle, WA  98195-1800
+S: USA
+
 N: Tommy Thorn
 E: Tommy.Thorn@irisa.fr
 W: http://www.irisa.fr/prive/thorn/index.html
@@ -1885,9 +1892,9 @@
 N: Tim Waugh
 E: tim@cyberelk.demon.co.uk
 D: Co-architect of the parallel-port sharing system
-S: 110 Twyford Road
-S: EASTLEIGH
-S: SO50 4HN
+S: 4 Fox Close
+S: Bishopstoke
+S: SO50 8NB
 S: United Kingdom
 
 N: Juergen Weigert
diff -u --recursive --new-file v2.1.121/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.121/linux/Documentation/Configure.help	Sat Sep  5 16:46:40 1998
+++ linux/Documentation/Configure.help	Wed Sep 16 13:25:56 1998
@@ -113,14 +113,14 @@
   If you are not sure, say Y; apart from resulting in a 45kB bigger
   kernel, it won't hurt.
 
-Normal floppy disk support
+Normal PC floppy disk support
 CONFIG_BLK_DEV_FD
-  If you want to use your floppy disk drive(s) under Linux, say
-  Y. Information about this driver, especially important for IBM
+  If you want to use the floppy disk drive(s) of your PC under Linux,
+  say Y. Information about this driver, especially important for IBM
   Thinkpad users, is contained in drivers/block/README.fd. This file
   also contains the location of the Floppy driver FAQ as well as
   location of the fdutils package used to configure additional
-  parameters of the driver at run time. 
+  parameters of the driver at run time.
 
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
@@ -174,12 +174,13 @@
   ftp://ftp.replay.com/pub/crypto/linux/all, and then you need to say
   Y to this option.
 
-  Note that alternative ways use encrypted filesystems are provided by
-  the cfs package, which can be gotten via FTP (user: anonymous) from
-  ftp://ftp.replay.com/pub/crypto/disk/, and the newer tcfs package,
-  available at http://tcfs.dia.unisa.it/. These do not require any
-  kernel support and you can say N here if you want to use one of
-  them.
+  Note that alternative ways to use encrypted filesystems are provided
+  by the cfs package, which can be gotten via FTP (user: anonymous)
+  from ftp://ftp.replay.com/pub/crypto/disk/, and the newer tcfs
+  package, available at http://tcfs.dia.unisa.it/. You do not need to
+  say Y here if you want to use one of these. However, using cfs
+  requires saying Y to "NFS filesystem support" below while using tcfs
+  requires applying a kernel patch.
 
   To use the loop device, you need the losetup utility and a recent
   version of the mount program, both contained in the util-linux
@@ -199,7 +200,7 @@
 Network Block Device support
 CONFIG_BLK_DEV_NBD
   Saying Y here will allow your computer to be a client for network
-  block devices - it will be able to use block devices exported by
+  block devices -- it will be able to use block devices exported by
   servers (mount filesystems on them etc.). Communication between
   client and server works over TCP/IP networking, but to the client
   program this is hidden: it looks like a regular local file access to
@@ -285,7 +286,7 @@
 Include IDE/ATAPI CDROM support
 CONFIG_BLK_DEV_IDECD
   If you have a CDROM drive using the ATAPI protocol, say Y. ATAPI is
-  a new protocol used by IDE CDROM and TAPE drives, similar to the
+  a newer protocol used by IDE CDROM and TAPE drives, similar to the
   SCSI protocol. Most new CDROM drives use ATAPI, including the
   NEC-260, Mitsumi FX400, Sony 55E, and just about all non-SCSI
   double(2X), quad(4X), and six(6X) speed drives.
@@ -311,7 +312,7 @@
 Include IDE/ATAPI TAPE support
 CONFIG_BLK_DEV_IDETAPE
   If you have an IDE tape drive using the ATAPI protocol, say Y.
-  ATAPI is a new protocol used by IDE tape and CDROM drives, similar
+  ATAPI is a newer protocol used by IDE tape and CDROM drives, similar
   to the SCSI protocol.  
 
   If you say Y here, the tape drive will be identified at boot time
@@ -329,7 +330,7 @@
 Include IDE/ATAPI FLOPPY support
 CONFIG_BLK_DEV_IDEFLOPPY
   If you have an IDE floppy drive which uses the ATAPI protocol, say
-  Y.  ATAPI is a new protocol used by IDE CDROM/tape/floppy drives,
+  Y.  ATAPI is a newer protocol used by IDE CDROM/tape/floppy drives,
   similar to the SCSI protocol.  IDE floppy drives include the LS-120
   and the ATAPI ZIP (ATAPI PD-CD/CDR drives are not supported by this
   driver; support for PD-CD/CDR drives is available if you say Y to
@@ -417,11 +418,13 @@
 CONFIG_BLK_DEV_IDEDMA
   If your PCI system uses IDE drive(s) (as opposed to SCSI, say) and
   is capable of bus-master DMA operation (most Pentium PCI systems),
-  you will want to say Y here to reduce CPU overhead.
-  You can use the "hdparm" utility to enable DMA for drives which
-  were not enabled automatically.  You can get the latest version of
-  the hdparm utility via anonymous FTP from
-  ftp://sunsite.unc.edu/pub/Linux/system/hardware/. 
+  you will want to say Y here to reduce CPU overhead. You can then use
+  the "hdparm" utility to enable DMA for drives for which it was not
+  enabled automatically. By default, DMA is not enabled automatically
+  for these drives, but you can change that by saying Y to the
+  following question "Use DMA by default when available". You can get
+  the latest version of the hdparm utility via anonymous FTP from
+  ftp://sunsite.unc.edu/pub/Linux/system/hardware/.
 
   Read the comments at the beginning of drivers/block/idedma.c and the
   file Documentation/ide.txt for more information. 
@@ -435,6 +438,7 @@
   about a couple of cases where buggy hardware may have caused damage,
   the default is now to NOT use DMA automatically.  To revert to the
   previous behaviour, say Y to this question.
+
   If you suspect your hardware is at all flakey, say N here.
   Do NOT email the IDE kernel people regarding this issue!
 
@@ -507,6 +511,27 @@
 
   Please read the comments at the top of drivers/block/ns87415.c.
 
+VIA82C586 chipset support (EXPERIMENTAL)
+CONFIG_BLK_DEV_VIA82C586
+  This adds initial timing settings for VIA (U)DMA onboard ide
+  controllers that are ATA3 compliant.  May work with ATA4 systems,
+  but not tested to date.
+
+  This requires CONFIG_IDEDMA_AUTO to be enabled.
+  Please read the comments at the top of drivers/block/via82C586.c
+
+  If unsure, say N.
+
+HPT343 chipset support (EXPERIMENTAL)
+CONFIG_BLK_DEV_HPT343
+  This driver adds up to 4 more eide devices sharing a single interrupt.
+  The HPT343 chipset in it's current form is a non-bootable PCI
+  UDMA controller.  This driver requires dynamic tuning of the
+  chipset during the ide-probe at boot.  It is reported to support
+  DVD II drives, by the manufacturer.
+
+  Please read the comments at the top of drivers/block/hpt343.c
+
 QDI QD6580 support
 CONFIG_BLK_DEV_QD6580
   This driver is enabled at runtime using the "ide0=qd6580" kernel
@@ -528,16 +553,6 @@
   I/O speeds to be set as well. See the files Documentation/ide.txt
   and ali14xx.c for more info.
 
-Apple Macintosh builtin IDE interface support (EXPERIMENTAL)
-CONFIG_BLK_DEV_MAC_IDE
-  This is the IDE driver for the builtin IDE interface on some Apple
-  Macintosh models.  It supports both the Quadra/Performa/LC 630 and
-  the PowerBook 190 IDE interface.
-
-  Say Y if you have such a Macintosh model and want to use IDE devices
-  (hard disks, CD-ROM drives, etc.) that are connected to the builtin
-  IDE interface.
-
 XT hard disk support
 CONFIG_BLK_DEV_XD
   Very old 8 bit hard disk controllers used in the IBM XT computer
@@ -576,7 +591,7 @@
   etc.).
 
 Parallel port IDE disks
-CONFIG_PARIDE_PD 
+CONFIG_PARIDE_PD
   This option enables the high-level driver for IDE-type disk devices 
   connected through a parallel port.  If you chose to build PARIDE 
   support into your kernel, you may answer Y here to build in the 
@@ -588,7 +603,7 @@
   hard drives from MicroSolutions.
 
 Parallel port ATAPI CD-ROMs
-CONFIG_PARIDE_PCD 
+CONFIG_PARIDE_PCD
   This option enables the high-level driver for ATAPI CD-ROM devices
   connected through a parallel port. If you chose to build PARIDE
   support into your kernel, you may answer Y here to build in the
@@ -602,7 +617,7 @@
   on CDROMs.
 
 Parallel port ATAPI disks
-CONFIG_PARIDE_PF 
+CONFIG_PARIDE_PF
   This option enables the high-level driver for ATAPI disk devices
   connected through a parallel port.  If you chose to build PARIDE
   support into your kernel, you may answer Y here to build in the
@@ -640,7 +655,7 @@
   the required patches to cdrecord.
 
 ATEN EH-100 protocol
-CONFIG_PARIDE_ATEN 
+CONFIG_PARIDE_ATEN
   This option enables support for the ATEN EH-100 parallel port IDE
   protocol.  This protocol is used in some inexpensive low performance 
   parallel port kits made in Hong Kong. If you chose to build PARIDE 
@@ -651,7 +666,7 @@
   support.
 
 MicroSolutions backpack protocol
-CONFIG_PARIDE_BPCK 
+CONFIG_PARIDE_BPCK
   This option enables support for the MicroSolutions backpack 
   parallel port IDE protocol.  If you chose to build PARIDE support
   into your kernel, you may answer Y here to build in the protocol
@@ -660,7 +675,7 @@
   a high-level driver for the type of device that you want to support.
 
 DataStor Commuter protocol
-CONFIG_PARIDE_COMM 
+CONFIG_PARIDE_COMM
   This option enables support for the Commuter parallel port IDE 
   protocol from DataStor.  If you chose to build PARIDE support
   into your kernel, you may answer Y here to build in the protocol
@@ -669,7 +684,7 @@
   a high-level driver for the type of device that you want to support.
 
 DataStor EP-2000 protocol
-CONFIG_PARIDE_DSTR 
+CONFIG_PARIDE_DSTR
   This option enables support for the EP-2000 parallel port IDE 
   protocol from DataStor.  If you chose to build PARIDE support
   into your kernel, you may answer Y here to build in the protocol
@@ -678,37 +693,37 @@
   a high-level driver for the type of device that you want to support.
 
 Shuttle EPAT/EPEZ protocol
-CONFIG_PARIDE_EPAT 
-  This option enables support for the EPAT parallel port IDE 
-  protocol.  EPAT is a parallel port IDE adapter manufactured by
-  Shuttle Technology and widely used in devices from major vendors
-  such as Hewlett-Packard, SyQuest, Imation and Avatar. If you 
-  chose to build PARIDE support into your kernel, you may answer Y 
-  here to build in the protocol driver, otherwise you should answer M 
-  to build it as a loadable module.  The module will be called epat.o.  
-  You must also have a high-level driver for the type of device that 
-  you want to support.
+CONFIG_PARIDE_EPAT
+  This option enables support for the EPAT parallel port IDE protocol.
+  EPAT is a parallel port IDE adapter manufactured by Shuttle
+  Technology and widely used in devices from major vendors such as
+  Hewlett-Packard, SyQuest, Imation and Avatar. If you chose to build
+  PARIDE support into your kernel, you may answer Y here to build in
+  the protocol driver, otherwise you should answer M to build it as a
+  loadable module. The module will be called epat.o. You must also
+  have a high-level driver for the type of device that you want to
+  support.
 
 Shuttle EPIA protocol
-CONFIG_PARIDE_EPIA 
-  This option enables support for the (obsolete) EPIA parallel port 
-  IDE protocol from Shuttle Technology.  This adapter can still be found
-  in some no-name kits. If you chose to build PARIDE support into your 
-  kernel, you may answer Y here to build in the protocol driver,
-  otherwise you should answer M to build it as a loadable module.  
-  The module will be called epia.o.  You must also have a high-level 
-  driver for the type of device that you want to support.
+CONFIG_PARIDE_EPIA
+  This option enables support for the (obsolete) EPIA parallel port
+  IDE protocol from Shuttle Technology. This adapter can still be
+  found in some no-name kits. If you chose to build PARIDE support
+  into your kernel, you may answer Y here to build in the protocol
+  driver, otherwise you should answer M to build it as a loadable
+  module. The module will be called epia.o. You must also have a
+  high-level driver for the type of device that you want to support.
 
 FIT TD-2000 protocol
 CONFIG_PARIDE_FIT2
-  This option enables support for the TD-2000 parallel port IDE protocol
-  from Fidelity International Technology.  This is a simple (low speed) 
-  adapter that is used in some portable hard drives.  If you chose to 
-  build PARIDE support into your kernel, you may answer Y here to 
-  build in the protocol driver, otherwise you should answer M to 
-  build it as a loadable module.  The module will be called ktti.o.  
-  You must also have a high-level driver for the type of device 
-  that you want to support.
+  This option enables support for the TD-2000 parallel port IDE
+  protocol from Fidelity International Technology. This is a simple
+  (low speed) adapter that is used in some portable hard drives. If
+  you chose to build PARIDE support into your kernel, you may answer Y
+  here to build in the protocol driver, otherwise you should answer M
+  to build it as a loadable module. The module will be called ktti.o.
+  You must also have a high-level driver for the type of device that
+  you want to support.
 
 FIT TD-3000 protocol
 CONFIG_PARIDE_FIT3
@@ -722,7 +737,7 @@
   of device that you want to support.
 
 FreeCom power protocol
-CONFIG_PARIDE_FRPW 
+CONFIG_PARIDE_FRPW
   This option enables support for the Freecom power parallel port IDE 
   protocol.  If you chose to build PARIDE support into your kernel, you 
   may answer Y here to build in the protocol driver, otherwise you 
@@ -731,15 +746,15 @@
   of device that you want to support.
 
 KingByte KBIC-951A/971A protocols
-CONFIG_PARIDE_KBIC 
-  This option enables support for the KBIC-951A and KBIC-971A parallel 
-  port IDE protocols from KingByte Information Corp.  KingByte's adapters
-  appear in many no-name portable disk and CD-ROM products, especially 
-  in Europe. If you chose to build PARIDE support into your kernel, you 
-  may answer Y here to build in the protocol driver, otherwise you should 
-  answer M to build it as a loadable module.  The module will be called 
-  kbic.o.  You must also have a high-level driver for the type of device 
-  that you want to support.
+CONFIG_PARIDE_KBIC
+  This option enables support for the KBIC-951A and KBIC-971A parallel
+  port IDE protocols from KingByte Information Corp. KingByte's
+  adapters appear in many no-name portable disk and CD-ROM products,
+  especially in Europe. If you chose to build PARIDE support into your
+  kernel, you may answer Y here to build in the protocol driver,
+  otherwise you should answer M to build it as a loadable module. The
+  module will be called kbic.o. You must also have a high-level driver
+  for the type of device that you want to support.
 
 KT PHd protocol
 CONFIG_PARIDE_KTTI
@@ -753,7 +768,7 @@
   support.
 
 OnSpec 90c20 protocol
-CONFIG_PARIDE_ON20 
+CONFIG_PARIDE_ON20
   This option enables support for the (obsolete) 90c20 parallel port 
   IDE protocol from OnSpec (often marketed under the ValuStore brand
   name).  If you chose to build PARIDE support into your kernel, you 
@@ -763,7 +778,7 @@
   type of device that you want to support.
 
 OnSpec 90c26 protocol
-CONFIG_PARIDE_ON26 
+CONFIG_PARIDE_ON26
   This option enables support for the 90c26 parallel port IDE protocol 
   from OnSpec Electronics (often marketed under the ValuStore brand
   name).  If you chose to build PARIDE support into your kernel, you 
@@ -848,24 +863,6 @@
   want to compile it as a module, say M here and read
   Documentation/modules.txt. If unsure, say Y.
 
-IDE card support
-CONFIG_BLK_DEV_IDE_CARDS
-  On Acorn systems, say Y here if you wish to use an IDE interface
-  expansion card. If you do not or are unsure, say N.
-
-ICS IDE interface
-CONFIG_BLK_DEV_IDE_ICS
-  On Acorn systems, say Y here if you wish to use the ICS IDE
-  interface card. This is not required for ICS partition support. If
-  you are unsure, say.
-
-ADFS partition support
-CONFIG_BLK_DEV_PART
-  This allows Linux on Acorn systems to determine its partitions in
-  the 'non-ADFS' partition area of the hard disk - usually located
-  after the ADFS partition.  You are probably using this system, so
-  you should say Y here.
-
 Boot support (linear, striped)
 CONFIG_MD_BOOT
   To boot with an initial linear or striped md device you have to say
@@ -1008,7 +1005,7 @@
   network think they're talking to a remote computer, while in reality
   the traffic is redirected by your Linux firewall to a local proxy
   server).
-  
+
   Make sure to say N to "Fast switching" below if you intend to say Y
   here.
 
@@ -1110,7 +1107,7 @@
 
 Non-standard serial port support
 CONFIG_SERIAL_NONSTANDARD
-  Say Y here if you have any non-standard serial boards --- boards
+  Say Y here if you have any non-standard serial boards -- boards
   which aren't supported using the standard "dumb" serial driver.
   This includes intelligent serial boards such as Cyclades,
   Digiboards, etc. These are usually used for systems that need many
@@ -1170,26 +1167,6 @@
   Say Y here to enable support in the dumb serial driver to support
   the HUB6 card.
 
-Unix98 PTY support
-CONFIG_UNIX98_PTYS
-  Linux traditionally uses BSD-like /dev/ptyxx and /dev/ttyxx names
-  for pseudo-ttys (PTYs).  This scheme has a number or problems.  The
-  GNU C library 2.1 and later, however, supports the Unix98 naming
-  standard, using a cloning device /dev/ptmx and numbered devices in a
-  subdirectory /dev/pts/xxx.  The device nodes in /dev/pts can be
-  automatically generated by the devpts virtual filesystem.
-
-  Say Y here if you are uncertain, unless you are very short on memory.
-
-Maximum number of Unix98 PTYs in use (0-2048)
-CONFIG_UNIX98_PTY_COUNT
-  The maximum number of Unix98 PTYs that can be used at any one time.
-  The default is 256, and should be enough for desktop systems,
-  however, server machines which support incoming telnet/rlogin/ssh
-  connections may want to increase this.  When not in use, each
-  additional set of 256 PTYs occupy approximately 8K of kernel memory
-  on 32-bit architectures.
-
 TGA Console Support
 CONFIG_TGA_CONSOLE
   Many Alpha systems (e.g the Multia) are shipped with a graphics card
@@ -1328,7 +1305,7 @@
   want to say Y here. 
 
   Information about ELF is on the WWW at
-  http://www.sjc.ox.ac.uk/users/barlow/elf-howto.html (To browse the
+  http://www.sjc.ox.ac.uk/users/barlow/elf-howto.html (to browse the
   WWW, you need to have access to a machine on the Internet that has a
   program like lynx or netscape). If you find that after upgrading
   from Linux kernel 1.2 and saying Y here, you still can't run any ELF
@@ -1382,8 +1359,8 @@
   any other Linux program: by simply typing in its name. (You also
   need to have the JDK installed for this to work).  As more and more
   Java programs become available, the use for this will gradually
-  increase. You can even execute HTML files containing JAVA applets (=
-  JAVA binaries) if those files start with the string
+  increase. You can even execute HTML files containing JAVA applets 
+  (= JAVA binaries) if those files start with the string
   "<!--applet-->". If you want to use this, say Y here and read
   Documentation/java.txt. 
 
@@ -1412,12 +1389,15 @@
 
 Kernel support for MISC binaries
 CONFIG_BINFMT_MISC
-  This enables the possibility to plug wrapper-driven binary formats
-  into the kernel. You will like this especially when you use programs
-  that need an interpreter to run like Java, Python or Emacs-Lisp.
-  Once you have registered such a binary class with the kernel, you
-  can start such a program simply by typing in its name; Linux will
-  feed it to the correct interpreter.
+  If you say Y here, it will be possible to plug wrapper-driven binary
+  formats into the kernel. You will like this especially when you use
+  programs that need an interpreter to run like Java, Python or
+  Emacs-Lisp. It's also useful if you often run DOS executables under
+  the Linux DOS emulator DOSEMU (read the DOSEMU-HOWTO, available in
+  ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO). Once you have
+  registered such a binary class with the kernel, you can start one of
+  those programs simply by typing in its name at a shell prompt; Linux
+  will automatically feed it to the correct interpreter.
 
   If you say Y here, you won't need "Kernel support for JAVA binaries"
   (CONFIG_BINFMT_JAVA) or "Kernel support for Linux/Intel ELF
@@ -1428,10 +1408,10 @@
   Documentation/java.txt for information about how to include Java
   support.
 
-  You must enable the "proc filesystem support" (CONFIG_PROC_FS) to
+  You must say Y to "proc filesystem support" (CONFIG_PROC_FS) to
   use this part of the kernel.
 
-  You may answer M for module support and later load the module when
+  You may say M here for module support and later load the module when
   you have use for it; the module is called binfmt_misc.o. If you
   don't know what to answer at this point, say Y.
 
@@ -1503,20 +1483,26 @@
   hardware. It represents the frame buffer of some video hardware and
   allows application software to access the graphics hardware through
   a well-defined interface, so the software doesn't need to know
-  anything about the low-level (hardware register) stuff. This works
-  across the different architectures supported by Linux and makes the
-  implementation of application programs easier and more portable; at
-  this point, an X server exists which uses the frame buffer device
-  exclusively.
+  anything about the low-level (hardware register) stuff. 
 
+  Frame buffer devices work identically across the different
+  architectures supported by Linux and make the implementation of
+  application programs easier and more portable; at this point, an X
+  server exists which uses the frame buffer device exclusively.
+  On several non-X86 architectures, the frame buffer device is the
+  only way to use the graphics hardware.
+ 
   The device is accessed through special device nodes, usually located
   in the /dev directory, i.e. /dev/fb*.
 
-  Please read the file Documentation/fb/framebuffer.txt for more
-  information.
-
-  If you want to play with it, say Y here and to the driver for your
-  graphics board, below. If unsure, say N.
+  You need an utility program called fbset to make full use of frame
+  buffer devices. Please read the file
+  Documentation/fb/framebuffer.txt for more information.
+
+  If you want to play with it, say Y here and also to the driver for
+  your graphics board, below. If unsure, say N, unless you are
+  compiling a kernel for a non-X86 architecture, in which case you
+  should say Y.
 
 Acorn VIDC support
 CONFIG_FB_ACORN
@@ -1555,33 +1541,34 @@
 
 Amiga CyberVision support
 CONFIG_FB_CYBER
-  This enables support for the Cybervision 64 graphics card from Phase5.
-  Please note that its use is not all that intuitive (i.e. if you have
-  any questions, be sure to ask!).  Say N unless you have a Cybervision
-  64 or plan to get one before you next recompile the kernel.         
-  Please note that this driver DOES NOT support the Cybervision 64 3D
-  card, as they use incompatible video chips.           
+  This enables support for the Cybervision 64 graphics card from
+  Phase5. Please note that its use is not all that intuitive (i.e. if
+  you have any questions, be sure to ask!). Say N unless you have a
+  Cybervision 64 or plan to get one before you next recompile the
+  kernel. Please note that this driver DOES NOT support the
+  Cybervision 64 3D card, as they use incompatible video chips.
 
 Amiga CyberVision3D support (EXPERIMENTAL)
 CONFIG_FB_VIRGE
-  This enables support for the Cybervision 64/3D graphics card from Phase5.
-  Please note that its use is not all that intuitive (i.e. if you have
-  any questions, be sure to ask!).  Say N unless you have a Cybervision
-  64/3D or plan to get one before you next recompile the kernel.         
-  Please note that this driver DOES NOT support the older Cybervision 64
-  card, as they use incompatible video chips.           
+  This enables support for the Cybervision 64/3D graphics card from
+  Phase5. Please note that its use is not all that intuitive (i.e. if
+  you have any questions, be sure to ask!). Say N unless you have a
+  Cybervision 64/3D or plan to get one before you next recompile the
+  kernel. Please note that this driver DOES NOT support the older
+  Cybervision 64 card, as they use incompatible video chips.
 
 Amiga RetinaZ3 support (EXPERIMENTAL)
 CONFIG_FB_RETINAZ3
-  This enables support for the Retina Z3 graphics card. Say N unless you
-  have a Retina Z3 or plan to get one before you next recompile the kernel.
+  This enables support for the Retina Z3 graphics card. Say N unless
+  you have a Retina Z3 or plan to get one before you next recompile
+  the kernel.
 
 Amiga CLgen driver (EXPERIMENTAL)
 CONFIG_FB_CLGEN
-  This enables support for Cirrus Logic GD542x/543x based boards on Amiga:
-  SD64, Piccolo, Picasso II/II+, Picasso IV, or EGS Spectrum.  Say N
-  unless you have such a graphics board or plan to get one before you next
-  recompile the kernel.
+  This enables support for Cirrus Logic GD542x/543x based boards on
+  Amiga: SD64, Piccolo, Picasso II/II+, Picasso IV, or EGS Spectrum.
+  Say N unless you have such a graphics board or plan to get one
+  before you next recompile the kernel.
 
 Atari native chipset support
 CONFIG_FB_ATARI
@@ -1628,8 +1615,10 @@
 VGA chipset support (text only)
 CONFIG_FB_VGA
   This is the frame buffer device driver for generic VGA chips. This
-  driver works only in text mode; if you want graphics mode, say Y to
-  "VESA VGA graphics console" as well.
+  driver works only in text mode and is deprecated; it is preferable
+  to say Y to "VGA text console" instead. For a graphical frame buffer
+  device driver that works for VGA cards, say Y to "VESA VGA graphics
+  console" below.
 
 TGA frame buffer support' 
 CONFIG_FB_TGA
@@ -1638,15 +1627,17 @@
 
 VESA VGA graphics console
 CONFIG_FB_VESA
-  This is the frame buffer device driver for generic VESA graphic cards.
-  Please read Documentation/fb/vesafb.txt.
+  This is the frame buffer device driver for generic VESA graphic
+  cards. You will get a boot time penguin logo at no additional cost.
+  Please read Documentation/fb/vesafb.txt. If unsure, say Y.
 
 MDA dual-headed support
 CONFIG_FB_MDA
   Say Y here if you have an old MDA or monochrome Hercules graphics
-  adapter in your system acting as a second head ( = video card).  Do 
-  not enable this driver if your MDA card is the primary card in your
-  system; the normal VGA driver will handle it.
+  adapter in your system acting as a second head ( = video card). You
+  will then be able to use two monitors with your Linux system. Do not
+  say Y here if your MDA card is the primary card in your system; the
+  normal VGA driver will handle it.
   
   This driver is also available as a module ( = code which can be
   inserted and removed from the running kernel whenever you want).
@@ -1683,13 +1674,14 @@
 
 Virtual Frame Buffer support (ONLY FOR TESTING!)
 CONFIG_FB_VIRTUAL
-  This is a `virtual' frame buffer device.  It operates on a chunk of
-  unswapable kernel memory instead of on the memory of a graphics board.
-  This means you cannot see any output sent to this frame buffer device,
-  while it does consume precious memory.  The main use of this frame
-  buffer device is testing and debugging the frame buffer subsystem. Do
-  NOT enable it for normal systems! To protect the innocent, it has to
-  be enabled explicitly on boot time using the kernel option `video=vfb:'.
+  This is a `virtual' frame buffer device. It operates on a chunk of
+  unswapable kernel memory instead of on the memory of a graphics
+  board. This means you cannot see any output sent to this frame
+  buffer device, while it does consume precious memory. The main use
+  of this frame buffer device is testing and debugging the frame
+  buffer subsystem. Do NOT enable it for normal systems! To protect
+  the innocent, it has to be enabled explicitly at boot time using the
+  kernel option `video=vfb:'.
 
   This driver is also available as a module ( = code which can be
   inserted and removed from the running kernel whenever you want).
@@ -1706,12 +1698,12 @@
   drivers. Note that they are used for text console output only; they are
   NOT needed for graphical applications.
 
-  If you do not enable this option, the needed low level drivers are
-  automatically enabled, depending on what frame buffer devices you
-  selected. This is recommended for most users.
+  If you say N here, the needed low level drivers are automatically
+  enabled, depending on what frame buffer devices you selected above.
+  This is recommended for most users.
 
-  If you enable this option, you have more fine-grained control over which
-  low level drivers are enabled. You can e.g. leave out low level drivers
+  If you say Y here, you have more fine-grained control over which low
+  level drivers are enabled. You can e.g. leave out low level drivers
   for color depths you do not intend to use for text consoles.
 
   Low level frame buffer console drivers can be modules ( = code which
@@ -1785,13 +1777,13 @@
 Mac variable bpp packed pixels support
 CONFIG_FBCON_MAC
   This is the low level frame buffer console driver for 1/2/4/8/16/32
-  bits per pixel packed pixels on Mac. It supports variable fontwidths
+  bits per pixel packed pixels on Mac. It supports variable font widths
   for low resolution screens.
   
 VGA characters/attributes support
 CONFIG_FBCON_VGA
-  This is the low level frame buffer console driver for VGA text mode, as
-  used by vgafb.
+  This is the low level frame buffer console driver for VGA text mode;
+  it is used if you said Y to "VGA chipset support (text only)" above.
 
 Parallel-port support
 CONFIG_PARPORT
@@ -1809,8 +1801,8 @@
 
   It is possible to share a single parallel port among several devices
   and it is safe to compile all the corresponding drivers into the
-  kernel. If you want to compile parallel port support as a module ( =
-  code which can be inserted in and removed from the running kernel
+  kernel. If you want to compile parallel port support as a module 
+  ( = code which can be inserted in and removed from the running kernel
   whenever you want), say M here and read Documentation/modules.txt.
   The module will be called parport.o. If you have more than one
   parallel port and want to specify which port and IRQ to be used by
@@ -1851,8 +1843,8 @@
 
 Plug and Play support
 CONFIG_PNP
-  Plug and Play support allows the kernel to automatically configure some
-  peripheral devices.  Say Y to enable PnP.
+  Plug and Play support allows the kernel to automatically configure
+  some peripheral devices. Say Y to enable PnP.
 
 Auto-probe for parallel devices
 CONFIG_PNP_PARPORT
@@ -2088,11 +2080,10 @@
 
 IP: firewall packet netlink device
 CONFIG_IP_FIREWALL_NETLINK
-  If you say Y here and then packets hit your Linux firewall and are
-  blocked, the first 128 bytes of each such packet are passed on to
-  optional user space monitoring software that can then look for
-  attacks and take actions such as paging the administrator of the
-  site. 
+  If you say Y here, then the first 128 bytes of each packet that hit
+  your Linux firewall and was blocked are passed on to optional user
+  space monitoring software that can then look for attacks and take
+  actions such as paging the administrator of the site.
 
   To use this, you need to create a character special file under /dev
   with major number 36 and minor number 3 using mknod ("man mknod"),
@@ -2218,13 +2209,14 @@
   http://www.tor.shaw.wave.ca/~ambrose/kernel21.html. 
 
   If you say Y here, you should also say Y to "IP: always defragment",
-  below. If you say Y here, then the modules ip_masq_ftp.o (for ftp
-  transfers through the firewall), ip_masq_irc.o (for irc chats
-  through the firewall), and ip_masq_raudio.o (for RealAudio downloads
-  through the firewall) will automatically be compiled. Modules are
-  pieces of code which can be inserted in and removed from the running
-  kernel whenever you want; read Documentation/modules.txt for
-  details.
+  below. 
+
+  If you say Y here, then the modules ip_masq_ftp.o (for ftp transfers
+  through the firewall), ip_masq_irc.o (for irc chats through the
+  firewall), and ip_masq_raudio.o (for RealAudio downloads through the
+  firewall) will automatically be compiled. Modules are pieces of code
+  which can be inserted in and removed from the running kernel
+  whenever you want; read Documentation/modules.txt for details.
 
 IP: ICMP masquerading
 CONFIG_IP_MASQUERADE_ICMP
@@ -2441,16 +2433,16 @@
 
 IP: Allow large windows (not recommended if <16Mb of memory)
 CONFIG_SKB_LARGE
-  On high speed, long distance networks the performance limit on 
+  On high speed, long distance networks the performance limit on
   networking becomes the amount of data a machine can buffer until the
   other end confirms its reception. (At 45Mbit/second there are a lot
-  of bits between New York and London ..). This option allows larger
-  amounts of data to be "in flight" at a given time. It also means a user
-  process can require a lot more memory for network buffers and thus this
-  option is best only used on machines with 16Mb of memory or higher. 
-  Unless you are using long links with end to end speeds of over 2Mbit
-  a second or satellite links this option will make no difference to
-  performance.
+  of bits between New York and London ...). This option allows larger
+  amounts of data to be "in flight" at a given time. It also means a
+  user process can require a lot more memory for network buffers and
+  thus this option is best used only on machines with 16Mb of memory
+  or higher. Unless you are using long links with end to end speeds of
+  over 2Mbit a second or satellite links this option will make no
+  difference to performance.
 
 Unix domain sockets
 CONFIG_UNIX
@@ -2484,13 +2476,10 @@
   and the file net/ipv6/README in the kernel source. 
 
   If you want to use IPv6, please upgrade to the newest net-tools as
-  given in Documentation/Changes.
+  given in Documentation/Changes. You will still be able to do regular
+  IPv4 networking as well.
 
-  The IPv6 support is also available as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want).
-  The module will be called ipv6.o. If you want to compile it as a
-  module, say M here and read Documentation/modules.txt. It's safe to
-  say N for now.
+  It is safe to say N here for now.
 
 IPv6: enable EUI-64 token format
 CONFIG_IPV6_EUI64
@@ -2522,7 +2511,7 @@
   to access Novell NetWare file or print servers using the Linux
   Novell client ncpfs (available via FTP (user: anonymous) from
   ftp://sunsite.unc.edu/pub/Linux/system/filesystems/) or from within
-  the Linux DOS emulator dosemu (read the DOSEMU-HOWTO, available in
+  the Linux DOS emulator DOSEMU (read the DOSEMU-HOWTO, available in
   ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO). In order to do the
   former, you'll also have to say Y to "NCP filesystem support",
   below.
@@ -2533,9 +2522,9 @@
 
   To turn your Linux box into a fully featured NetWare file server and
   IPX router, say Y here and fetch either lwared from
-  ftp://sunsite.unc.edu/pub/Linux/system/network/daemons/ or mars_nwe from
-  ftp://ftp.gwdg.de/pub/linux/misc/ncpfs. For more information, read the
-  IPX-HOWTO in ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO.
+  ftp://sunsite.unc.edu/pub/Linux/system/network/daemons/ or mars_nwe
+  from ftp://ftp.gwdg.de/pub/linux/misc/ncpfs. For more information,
+  read the IPX-HOWTO in ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO.
 
   General information about how to connect Linux, Windows machines and
   Macs is on the WWW at http://www.eats.com/linux_mac_win.html (to
@@ -2774,9 +2763,9 @@
   A comprehensive listing of all the software for Linux amateur radio
   users as well as information about how to configure an AX.25 port is
   contained in the AX25-HOWTO, available via FTP (user: anonymous) in
-  ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO. You also might want to check
-  out the file Documentation/networking/ax25.txt. More information
-  about digital amateur radio in general is on the WWW at
+  ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO. You also might want to
+  check out the file Documentation/networking/ax25.txt. More
+  information about digital amateur radio in general is on the WWW at
   http://www.tapr.org/tapr/html/pkthome.html (To browse the WWW, you
   need to have access to a machine on the Internet that has a program
   like lynx or netscape).
@@ -2836,9 +2825,9 @@
 
   Currently, this driver supports Ottawa PI/PI2
   (http://hydra.carleton.ca/info/pi2.html) and Gracilis PackeTwin
-  (http://www.paccomm.com/) boards. They are detected automatically.
-  If you have one of these cards, say Y here and read the HAM-HOWTO,
-  available via FTP (user: anonymous) in
+  (http://www.paccomm.com/gracilis.html) boards. They are detected
+  automatically. If you have one of these cards, say Y here and read
+  the AX25-HOWTO, available via FTP (user: anonymous) in
   ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO/.
 
   This driver can operate multiple boards simultaneously. If you compile
@@ -2862,11 +2851,11 @@
 Z8530 SCC driver for AX.25
 CONFIG_SCC
   These cards are used to connect your Linux box to an amateur radio
-  in order to communicate with other computers.  If you want to use
+  in order to communicate with other computers. If you want to use
   this, read Documentation/networking/z8530drv.txt and the
-  AX.25-HOWTO, available via FTP (user: anonymous) at
-  ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO.  Also make sure to say Y to
-  "Amateur Radio AX.25 Level 2" support. 
+  AX25-HOWTO, available via FTP (user: anonymous) at
+  ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO. Also make sure to say Y
+  to "Amateur Radio AX.25 Level 2" support.
 
   If you want to compile this as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
@@ -3383,7 +3372,7 @@
 Maximum number of commands per LUN
 CONFIG_AIC7XXX_CMDS_PER_LUN
   Specify the maximum number of commands you would like to allocate
-  per LUN (a LUN is a Logical Unit Number - some physical SCSI devices,
+  per LUN (a LUN is a Logical Unit Number -- some physical SCSI devices,
   e.g. CD jukeboxes, act logically as several separate units, each of
   which gets its own number).  
 
@@ -3618,7 +3607,7 @@
   rates: FAST-5, FAST-10, FAST-20 and FAST-40. The numbers are
   respectively the maximum data transfer rates in mega-transfers per
   second for each class. For example, a FAST-20 Wide 16 device is able
-  to transfer data at 40 million 16 bit packets per second for a total
+  to transfer data at 20 million 16 bit packets per second for a total
   rate of 40 MB/s.
 
   You may specify 0 if you want to only use asynchronous data
@@ -3753,26 +3742,27 @@
 CONFIG_IBMMCA_SCSI_ORDER_STANDARD
   In the PC-world and in most modern SCSI-BIOS-setups, SCSI-hard disks
   are assigned to the drive letters, starting with the lowest SCSI-id
-  (physical number - pun) to be drive C:, as seen from DOS and similar
-  operating systems. When looking into papers describing the
+  (physical number -- pun) to be drive C:, as seen from DOS and
+  similar operating systems. When looking into papers describing the
   ANSI-SCSI-standard, this assignment of drives appears to be wrong.
-  The SCSI-standard follows a hardware-hierarchy which says that
-  id 7 has the highest priority and id 0 the lowest. Therefore, the
-  host adapters are still today everywhere placed as SCSI-id 7 by
-  default. In the SCSI-standard, the drive letters express the priority
-  of the disk. C: should be the hard disk, or a partition on it, with the
+  The SCSI-standard follows a hardware-hierarchy which says that id 7
+  has the highest priority and id 0 the lowest. Therefore, the host
+  adapters are still today everywhere placed as SCSI-id 7 by default.
+  In the SCSI-standard, the drive letters express the priority of the
+  disk. C: should be the hard disk, or a partition on it, with the
   highest priority. This must therefore be the disk with the highest
   SCSI-id (e.g. 6) and not the one with the lowest! IBM-BIOS kept the
   original definition of the SCSI-standard as also industrial- and
   process-control-machines, like VME-CPUs running under realtime-OSs
   (e.g. LynxOS, OS9) do.
 
-  If you like to run Linux on your MCA-machine with the same assignment
-  of hard disks as seen from e.g. DOS or OS/2 on your machine, which
-  is in addition conformant to the SCSI-standard, you must say Y here.
-  This is also necessary for MCA-Linux-users who want to keep downward-
-  compatibility to older releases of the IBM-MCA-SCSI-driver (older than
-  driver-release 2.00 and older than June 1997).
+  If you like to run Linux on your MCA-machine with the same
+  assignment of hard disks as seen from e.g. DOS or OS/2 on your
+  machine, which is in addition conformant to the SCSI-standard, you
+  must say Y here. This is also necessary for MCA-Linux users who want
+  to keep downward- compatibility to older releases of the
+  IBM-MCA-SCSI-driver (older than driver-release 2.00 and older than
+  June 1997).
 
   If you like to have the lowest SCSI-id assigned as drive C:, as
   modern SCSI-BIOSes do, which does not conform to the standard, but
@@ -4073,6 +4063,7 @@
   support, as well as the drivers for the storage array itself and
   for the interface adapter such as SOC. This subsystem could even
   serve for IP networking, with some code extensions.
+
   If unsure, say N.
 
 Sun SOC
@@ -4124,7 +4115,7 @@
 
 EcoSCSI support
 CONFIG_SCSI_ECOSCSI
-  This enables support for the EcoSCSI card - a small card that sits in
+  This enables support for the EcoSCSI card -- a small card that sits in
   the Econet socket.  If you have an Acorn system with one of these,
   say Y.  If unsure, say N.
 
@@ -4354,58 +4345,6 @@
   module, say M here and read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt.
 
-AIMSlab RadioTrack (aka RadioReveal) support
-CONFIG_RADIO_RTRACK
-  Choose Y here if you have one of these FM radio cards, and then fill
-  in the port address below.
-
-  In order to control your radio card, you will need to use programs
-  that are compatible with the Video for Linux API.  Information on 
-  this API and pointers to "v4l" programs may be found on the WWW at
-  http://roadrunner.swansea.uk.linux.org/v4l.shtml; to browse the WWW,
-  you need to have access to a machine on the Internet that has a 
-  program like lynx or netscape.
-
-  If you want to compile this driver as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want),
-  say M here and read Documentation/modules.txt. The module will be
-  called radio-aimslab.o.
-
-RadioTrack i/o port
-CONFIG_RADIO_RTRACK_PORT
-  Enter either 0x30f or 0x20f here.  The card default is 0x30f, if you
-  haven't changed the jumper setting on the card.
-
-Aztech/Packard Bell Radio
-CONFIG_RADIO_AZTECH
-  Choose Y here if you have one of these FM radio cards, and then fill
-  in the port address below.
-  
-  If you want to compile this driver as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want),
-  say M here and read Documentation/modules.txt. The module will be
-  called radio-aztech.o.
-
-Aztech/Packard Bell radio card i/o port
-CONFIG_RADIO_AZTECH_PORT
-  Enter either 0x350 or 0x358 here.  The card default is 0x350, if you
-  haven't changed the setting of jumper JP3 on the card.  Removing the
-  jumper sets the card to 0x358.
-
-SF16FMI Radio
-CONFIG_RADIO_SF16FMI
-  Choose Y here if you have one of these FM radio cards, and then fill
-  in the port address below.
-
-  If you want to compile this driver as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want),
-  say M here and read Documentation/modules.txt. The module will be
-  called radio-sf16fmi.o
-
-SF16FMI I/O port (0x284 or 0x384)
-CONFIG_RADIO_SF16FMI_PORT
-  Enter the I/O port of your SF16FMI radio card.
-
 LAPB over Ethernet driver
 CONFIG_LAPBETHER
   This is a driver for a pseudo device (typically called /dev/lapb0)
@@ -4645,7 +4584,7 @@
 CONFIG_NET_HW_FLOWCONTROL
   This option enables NIC (Network Interface Card) hardware throttling
   during periods of extremal congestion. At the moment only a couple
-  of device drivers support it (really only one ---tulip, modified
+  of device drivers support it (really only one -- tulip, modified
   8390 can be found at
   ftp://ftp.inr.ac.ru/ip-routing/fastroute-8390.tar.gz). Really, this
   option is applicable to any machine attached to a fast enough
@@ -4827,11 +4766,17 @@
   performance will be written to /proc/net/profile. If you don't know
   what it is about, you don't need it: say N.
 
+#Comtrol Hostess SV-11 support
+#CONFIG_HOSTESS_SV11
+###
+### I don't know what this is.
+###  
+
 WAN Drivers
 CONFIG_WAN_DRIVERS
   Say Y to this option if your Linux box contains a WAN card and you
-  are planning to use the box as a WAN ( = Wide Area Network) router (
-  = device used to interconnect local area networks over wide area
+  are planning to use the box as a WAN ( = Wide Area Network) router
+  ( = device used to interconnect local area networks over wide area
   communication links, such as leased lines and public data networks,
   e.g. X.25 and frame relay) and you will be offered a list of drivers
   for WAN cards currently available. For more information, read
@@ -5650,7 +5595,7 @@
 
   Devices currently supported by this driver are Compaq Netelligent,
   Compaq NetFlex and Olicom cards. Please read the file
-  Documentation/tlan.txt for details.
+  Documentation/networking/tlan.txt for more details.
 
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
@@ -5974,8 +5919,8 @@
 CONFIG_AZTCD
   This is your driver if you have an Aztech CDA268-01A, Orchid
   CD-3110, Okano or Wearnes CDD110, Conrad TXC, or CyCDROM CR520 or
-  CR540 CDROM drive.  This driver - just like all these CDROM drivers
-  - is NOT for CDROM drives with IDE/ATAPI interfaces, such as Aztech
+  CR540 CDROM drive.  This driver -- just like all these CDROM drivers
+  -- is NOT for CDROM drives with IDE/ATAPI interfaces, such as Aztech
   CDA269-031SE. Please read the file Documentation/cdrom/aztcd. 
 
   If you say Y here, you should also say Y or M to "ISO 9660 CDROM
@@ -6143,8 +6088,10 @@
   directories on ext2 filesystems, use chattr ("man chattr").
   
   Ext2fs partitions can be read from within DOS using the ext2tool
-  package available via FTP (user: anonymous) from
-  ftp://sunsite.unc.edu/pub/Linux/system/filesystems/ext2.
+  package (available via FTP (user: anonymous) from
+  ftp://sunsite.unc.edu/pub/Linux/system/filesystems/ext2) and from
+  within Windows 95 and Windows NT using the ex2fs explorer available
+  from http://jnewbigin-pc.it.swin.edu.au/Linux/Explore2fs.htm.
 
   If you want to compile this filesystem as a module ( = code which
   can be inserted in and removed from the running kernel whenever you
@@ -6198,7 +6145,7 @@
   want), say M here and read Documentation/modules.txt. The module
   will be called fat.o. Note that if you compile the FAT support as a
   module, you cannot compile any of the FAT-based filesystems into the
-  kernel - they will have to be modules as well. The filesystem of
+  kernel -- they will have to be modules as well. The filesystem of
   your root partition cannot be a module, so don't say M here if you
   intend to use UMSDOS as your root filesystem.
 
@@ -6288,7 +6235,7 @@
   It's totally cool; for example, "cat /proc/interrupts" gives
   information about what the different IRQs are used for at the moment
   (there is a small number of Interrupt ReQuest lines in your computer
-  that are used by the attached devices to gain the CPU's attention -
+  that are used by the attached devices to gain the CPU's attention --
   often a source of trouble if two devices are mistakenly configured
   to use the same IRQ). This option will enlarge your kernel by about
   18 kB. Several programs depend on this, so everyone should say Y
@@ -6308,8 +6255,8 @@
   ftp://sunsite.unc.edu/pub/Linux/docs/LDP, on its man page: "man
   nfs", and in the NFS-HOWTO.
   
-  An alternative to NFS is provided by the Coda filesystem; see "Coda
-  filesystem support" below.
+  An superior but less widely used alternative to NFS is provided by
+  the Coda filesystem; see "Coda filesystem support" below.
 
   If you say Y here, you should have said Y to TCP/IP networking also.
   This option would enlarge your kernel by about 27 kB. 
@@ -6480,8 +6427,8 @@
 
 QNX filesystem support (EXPERIMENTAL)
 CONFIG_QNX4FS_FS  
-  This is the filesystem used by QNX 4. Say Y if you intend to mount
-  QNX hard disks and floppies.
+  This is the filesystem used by the operating system QNX 4. Say Y if
+  you intend to mount QNX hard disks or floppies.
 
   This filesystem support is also available as a module ( = code which
   can be inserted in and removed from the running kernel whenever you
@@ -6498,8 +6445,8 @@
   automounter (amd), which is a pure user space daemon.
 
   To use the automounter you need the user-space tools from
-  ftp://ftp.kernel.org/pub/linux/daemons/autofs; you also want to say Y to
-  "NFS filesystem support", above. 
+  ftp://ftp.kernel.org/pub/linux/daemons/autofs; you also want to say
+  Y to "NFS filesystem support", above.
 
   If you want to compile this as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
@@ -6509,31 +6456,33 @@
   If you are not a part of a fairly large, distributed network, you
   probably do not need an automounter, and can say N here.
 
-UFS filesystem support (read only)
+UFS filesystem support
 CONFIG_UFS_FS
   BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD,
   OpenBSD and NeXTstep) use a filesystem called UFS. Some System V
   Unixes can create and mount hard disk partitions and diskettes using
   this filesystem as well. Saying Y here allows you to mount these
-  partitions and diskettes read-only.
+  partitions and diskettes.
 
   If you only intend to mount files from some other Unix over the
   network using NFS, you don't need the UFS filesystem support (but
-  you need NFS filesystem support obviously). Note that this option is
-  generally not needed for floppies, since a good portable way to
-  transport files and directories between unixes (and even other
-  operating systems) is given by the tar program ("man tar" or
-  preferably "info tar"). 
+  you need NFS filesystem support obviously). 
+
+  Note that this option is generally not needed for floppies, since a
+  good portable way to transport files and directories between unixes
+  (and even other operating systems) is given by the tar program ("man
+  tar" or preferably "info tar").
 
   When accessing NeXTstep files, you may need to convert them from the
   NeXT character set to the Latin1 character set; use the program
   recode ("info recode") for this purpose. 
 
-  If you want to compile this as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want),
-  say M here and read Documentation/modules.txt. The module will be
-  called ufs.o. If you haven't heard about all of this before, it's
-  safe to say N.
+  If you want to compile the UFS filesystem support as a module ( =
+  code which can be inserted in and removed from the running kernel
+  whenever you want), say M here and read Documentation/modules.txt.
+  The module will be called ufs.o. 
+
+  If you haven't heard about all of this before, it's safe to say N.
 
 BSD disklabel (FreeBSD partition tables) support
 CONFIG_BSD_DISKLABEL
@@ -6588,18 +6537,22 @@
 
 /dev/pts filesystem (experimental)
 CONFIG_DEVPTS_FS
-  If you say Y here, you'll get a virtual filesystem which can be
-  mounted on /dev/pts with "mount -t devpts". This, together with the
-  pseudo terminal master multiplexer /dev/ptmx, is used for pseudo
-  terminal support as described in the Open Group's Unix98 standard:
-  in order to acquire a pseudo terminal, a process opens /dev/ptmx;
-  the number of the pseudo terminal is then made available to the
-  process and the pseudo terminal slave can be accessed as
-  /dev/pts/<number>. What was traditionally /dev/ttyp2 will then be
-  /dev/pts/2, for example. The GNU C library glibc 2.1 contains the
-  requisite support for this mode of operation.
+  You should say Y here if you said Y to "Unix98 PTY support" above.
+  You'll then get a virtual filesystem which can be mounted on
+  /dev/pts with "mount -t devpts". This, together with the pseudo
+  terminal master multiplexer /dev/ptmx, is used for pseudo terminal
+  support as described in the Open Group's Unix98 standard: in order
+  to acquire a pseudo terminal, a process opens /dev/ptmx; the number
+  of the pseudo terminal is then made available to the process and the
+  pseudo terminal slave can be accessed as /dev/pts/<number>. What was
+  traditionally /dev/ttyp2 will then be /dev/pts/2, for example. The
+  GNU C library glibc 2.1 contains the requisite support for this mode
+  of operation.
 
-  Say Y here if you have enabled support for Unix98 PTYs.
+  This code is also available as a module called devpts.o ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want). If you want to compile it as a module, say M
+  here and read Documentation/modules.txt.
 
 Macintosh partition map support
 CONFIG_MAC_PARTITION
@@ -6804,7 +6757,7 @@
   DOS/Windows partitions correctly. This does apply to the filenames
   only, not to the file contents. You can include several codepages;
   say Y here if you want to include the DOS codepage that is used for
-  much of Europe--United Kingdom, Germany, Spain, Italy, and [add more
+  much of Europe -- United Kingdom, Germany, Spain, Italy, and [add more
   countries here]. It has some characters useful to many European
   languages that are not part of the US codepage 437. If unsure, say
   Y.
@@ -7235,6 +7188,42 @@
   and read Documentation/modules.txt. The module will be called esp.o.
   If unsure, say N.
 
+Unix98 PTY support
+CONFIG_UNIX98_PTYS
+  A pseudo terminal (PTY) is a software device consisting of two
+  halves: a master and a slave. The slave device behaves identical to
+  a physical terminal; the master device is used by a process to
+  read data from and write data to the slave, thereby emulating a
+  terminal. Typical programs for the master side are xterm and telnet
+  servers. 
+  
+  Linux has traditionally used the BSD-like names /dev/ptyxx for
+  masters and /dev/ttyxx for slaves of pseudo terminals. This scheme
+  has a number of problems. The GNU C library glibc 2.1 and later,
+  however, supports the Unix98 naming standard: in order to acquire a
+  pseudo terminal, a process opens /dev/ptmx; the number of the pseudo
+  terminal is then made available to the process and the pseudo
+  terminal slave can be accessed as /dev/pts/<number>. What was
+  traditionally /dev/ttyp2 will then be /dev/pts/2, for example.
+
+  The entries in /dev/pts/ are created on the fly by a virtual
+  filesystem; therefore, if you say Y here you should say Y to
+  "/dev/pts filesystem for Unix98 PTYs" as well.
+
+  Say Y here if you are uncertain, unless you are very short on
+  memory.
+
+Maximum number of Unix98 PTYs in use (0-2048)
+CONFIG_UNIX98_PTY_COUNT
+  The maximum number of Unix98 PTYs that can be used at any one time.
+  The default is 256, and should be enough for desktop systems. Server
+  machines which support incoming telnet/rlogin/ssh connections and/or
+  serve several X terminals may want to increase this: every incoming
+  connection and every xterm uses up one PTY.
+
+  When not in use, each additional set of 256 PTYs occupy
+  approximately 8K of kernel memory on 32-bit architectures.
+
 Parallel printer support
 CONFIG_PRINTER
   If you intend to attach a printer to the parallel port of your Linux
@@ -7355,7 +7344,7 @@
 
   If you are unsure, say N and read the HOWTO nevertheless: it will
   tell you what you have. Also be aware that several vendors talk
-  about 'Microsoft busmouse' and actually mean PS/2 busmouse - so
+  about 'Microsoft busmouse' and actually mean PS/2 busmouse -- so
   count the pins on the connector.
 
   If you want to compile this as a module ( = code which can be
@@ -7807,12 +7796,12 @@
 Do CPU IDLE calls
 CONFIG_APM_CPU_IDLE
   Enable calls to APM CPU Idle/CPU Busy inside the kernel's idle loop.
-  On some machines, this can activate improved power savings, such as a
-  slowed CPU clock rate, when the machine is idle.  These idle calls are
-  made after the idle loop has run for some length of time (e.g., 333
-  mS).  On some machines, this will cause a hang at boot time or whenever
-  the CPU becomes idle.  (On machines with more than one CPU, this option
-  does nothing.)
+  On some machines, this can activate improved power savings, such as
+  a slowed CPU clock rate, when the machine is idle. These idle calls
+  are made after the idle loop has run for some length of time (e.g.,
+  333 mS). On some machines, this will cause a hang at boot time or
+  whenever the CPU becomes idle. (On machines with more than one CPU,
+  this option does nothing.)
 
 Enable console blanking using APM
 CONFIG_APM_DISPLAY_BLANK
@@ -7899,8 +7888,8 @@
 
 Fan Tachometer
 CONFIG_WDT_501_FAN
-  Enable the Fan Tachometer on the WDT501. Only do this if you have a fan
-  tachometer actually set up.
+  Enable the Fan Tachometer on the WDT501. Only do this if you have a
+  fan tachometer actually set up.
 
 Software Watchdog
 CONFIG_SOFT_WATCHDOG
@@ -8027,7 +8016,7 @@
   from ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO. There is also some
   information in various README files in drivers/sound, esp. in
   Readme.cards which you should read first to find out whether your
-  card is supported by Linux.
+  card is supported by Linux, and, if yes, which driver to use.
 
   If you have a PnP sound card and you want to configure it at boot
   time using the ISA PnP tools (read
@@ -8046,11 +8035,25 @@
   ftp://sunsite.unc.edu/pub/Linux/kernel/patches/console/ and in the
   pcsp patch at ftp://dwmw2.robinson.cam.ac.uk/pub/kernel/ .
 
+OSS sound modules
+CONFIG_SOUND_OSS
+  OSS is the Open Sound System suite of sound card drivers. They make
+  sound programming easier since they provide a common API. Say Y or M
+  here (the module will be called sound.o) if you haven't found a
+  driver for your sound card above, then pick your driver from the
+  list below.
+ 
 Support for Aztech Sound Galaxy (non-PnP) cards
 CONFIG_SOUND_SGALAXY
-  This module initializes the older non Plug and Play sound galaxy cards
-  from Aztech. It supports the Waverider Pro 32 - 3D and the Galaxy
-  Washington 16.
+  This module initializes the older non Plug and Play sound galaxy
+  cards from Aztech. It supports the Waverider Pro 32 - 3D and the
+  Galaxy Washington 16.
+
+Yamaha OPL3-SA1 audio controller
+CONFIG_SOUND_OPL3SA1
+  Say Y or M if you have a Yamaha OPL3-SA1 sound chip, which is
+  usually built into motherboards. Read Documentation/sound/OPL3-SA
+  for details.
 
 ProAudioSpectrum 16 support
 CONFIG_SOUND_PAS
@@ -8065,15 +8068,21 @@
   Creative Labs or a 100% hardware compatible clone (like the
   Thunderboard or SM Games). If your card was in the list of supported
   cards look at the card specific instructions in the
-  drivers/sound/Readme.cards file before answering this question. For
+  drivers/sound/Readme.cards file before answering this question.  For
   an unknown card you may answer Y if the card claims to be
   Sound Blaster-compatible. 
 
-  Please read Documentation/sound/Soundblaster.
+  You can say M here to compile this driver as a module; the module is
+  called sb.o.
 
-  If you have an SB AWE 32 or SB AWE 64, say
-  Y here and to "Additional lowlevel drivers" and to "SB32/AWE
-  support" below.
+  You should also say Y here for cards based on the Avance Logic
+  ALS-007 chip (read Documentation/sound/ALS007) and for ESS1688 and
+  ESS1868 cards (read Documentation/sound/ESS1868). If you have an SB
+  AWE 32 or SB AWE 64, say Y here and also to "Additional lowlevel
+  drivers" and to "SB32/AWE support" below. If you have an IBM Mwave
+  card, say Y here and read Documentation/sound/mwave.
+  
+  Please read Documentation/sound/Soundblaster.
 
 Generic OPL2/OPL3 FM synthesizer support
 CONFIG_SOUND_ADLIB
@@ -8081,7 +8090,13 @@
   Answering Y is usually a safe and recommended choice, however some
   cards may have software (TSR) FM emulation. Enabling FM support with
   these cards may cause trouble (I don't currently know of any such
-  cards, however). If unsure, say Y.
+  cards, however). 
+
+  Please read the file Documentation/sound/OPL3 if your card has an
+  OPL3 chip.
+
+  If unsure, say Y.
+  
 
 #Loopback MIDI device support
 #CONFIG_SOUND_VMIDI
@@ -8114,24 +8129,33 @@
 
 PSS (AD1848, ADSP-2115, ESC614) support
 CONFIG_SOUND_PSS
-  Answer Y only if you have Orchid SW32, Cardinal DSP16 or some other
-  card based on the PSS chipset (AD1848 codec + ADSP-2115 DSP chip +
-  Echo ESC614 ASIC CHIP).
+  Answer Y or M if you have an Orchid SW32, Cardinal DSP16, Beethoven
+  ADSP-16 or some other card based on the PSS chipset (AD1848 codec +
+  ADSP-2115 DSP chip + Echo ESC614 ASIC CHIP). For more information on
+  how to compile it into the kernel or as a module see the file
+  Documentation/sound/PSS.
+
+Enable PSS mixer (Beethoven ADSP-16 and other compatible)
+CONFIG_PSS_MIXER
+  Answer Y for Beethoven ADSP-16. You may try to say Y also for other
+  cards if they have master volume, bass, treble, and you can't
+  control it under Linux. If you answer N for Beethoven ADSP-16, you
+  can't control master volume, bass, treble and synth volume.
+
+  If you said M to "PSS support" above, you may enable or disable this
+  PSS mixer with the module parameter pss_mixer. For more information
+  see the file Documentation/sound/PSS.
 
-#Enable PSS mixer (Beethoven ADSP-16 and other compatible)
-#CONFIG_PSS_MIXER
-###
-### Don't know what this is
-###
-#
 Have DSPxxx.LD firmware file
 CONFIG_PSS_HAVE_BOOT
-  If you want to emulate the Sound Blaster card and you have a DSPxxx.LD
-  file, then answer Y here to include this file.
+  If you have the DSPxxx.LD file or SYNTH.LD file for you card, answer
+  Y to include this file. Without this file the synth device (OPL) may
+  not work.
 
 Full pathname of DSPxxx.LD firmware file
 CONFIG_PSS_BOOT_FILE
-  Enter the full pathname of your DSPxxx.LD file, starting from /.
+  Enter the full pathname of your DSPxxx.LD file or SYNTH.LD file,
+  starting from /.
 
 16 bit sampling option of GUS (_NOT_ GUS MAX)
 CONFIG_SOUND_GUS16
@@ -8200,9 +8224,19 @@
   quite common so it's possible that many no-name cards have one of
   them. In addition the MAD16 chip is used in some cards made by known
   manufacturers such as Turtle Beach (Tropez), Reveal (some models)
-  and Diamond (latest ones). See also Documentation/sound/Opti for
+  and Diamond (latest ones). Note however that the Tropez sound cards
+  have their own driver; if you have one of those, say N here and Y or
+  M to "Full support for Turtle Beach WaveFront", below. 
+
+  See also Documentation/sound/Opti and Documentation/sound/MAD16 for
   more information on setting these cards up as modules.
 
+Full support for Turtle Beach WaveFront synth/sound cards
+CONFIG_SOUND_WAVEFRONT
+  Answer Y or M if you have a Tropez Plus, Tropez or Maui sound card
+  and read the files Documentation/sound/Wavefront and
+  Documentation/sound/Tropez+.
+  
 Support MIDI in older MAD16 based cards (requires SB)
 CONFIG_MAD16_OLDCARD
   Answer Y (or M) if you have an older card based on the C928
@@ -8272,7 +8306,15 @@
   obtained from Turtle Beach.  See Documentation/sound/MultiSound for
   information on how to obtain this.
 
-MSND Pinnacle Non-PnP Mode
+MSND Pinnacle have S/PDIF I/O
+CONFIG_MSNDPIN_DIGITAL
+  If you have the S/PDIF daughterboard for the Pinnacle or Fiji, say Y
+  here; otherwise, say N.  If you have this, you will be able to play
+  and record from the S/PDIF port (digital signal). See
+  Documentation/sound/MultiSound for information on how to make use of
+  this capability.
+
+MSND Pinnacle non-PnP Mode
 CONFIG_MSNDPIN_NONPNP
   The Pinnacle and Fiji card resources can be configured either with
   PnP, or through a configuration port.  For the Pinnacle,
@@ -8283,13 +8325,20 @@
   you must say N here and use isapnptools to configure the card's
   resources.
 
-MSND Pinnacle Config Port
+MSND Pinnacle config port
 CONFIG_MSNDPIN_CFG
   This is the port which the Pinnacle and Fiji uses to configure
   the card's resources when not in PnP mode.  If your card is in
   PnP mode, then be sure to say N to the previous option,
   CONFIG_MSNDPIN_NONPNP.
 
+MSND buffer size (kB)
+CONFIG_MSND_FIFOSIZE
+  Configures the size of each audio buffer, in kilobytes, for
+  recording and playing in the MultiSound drivers (both the Classic
+  and Pinnacle).  Larger values reduce the chance of data overruns at
+  the expense of overall latency. If unsure, use the default.
+
 /dev/dsp and /dev/audio support
 CONFIG_SOUND_AUDIO
   Answering N disables /dev/dsp and /dev/audio, the A/D and D/A
@@ -8339,18 +8388,22 @@
 
 Gallant's Audio Excel DSP 16 support (SC-6000 and SC-6600)
 CONFIG_AEDSP16
-  Answer Y if you have a Gallant's Audio Excel DSP 16 card. This card
-  can emulate either an SBPro or a Microsoft Sound System card, so you
-  should have said Y to either "Sound Blaster (SB, SBPro, SB16, clones)
-  support" or "Microsoft Sound System support", above, and you need to
-  answer the "MSS emulation" and "SBPro emulation" questions below
+  Answer Y if you have a Gallant's Audio Excel DSP 16 card. This
+  driver supports Audio Excel DSP 16 but not the III nor PnP versions
+  of this card.
+
+  The Gallant's Audio Excel DSP 16 card can emulate either an SBPro or
+  a Microsoft Sound System card, so you should have said Y to either
+  "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support"
+  or "Microsoft Sound System support", above, and you need to answer
+  the "MSS emulation" and "SBPro emulation" questions below
   accordingly. You should say Y to one and only one of these two
-  questions.  Read the drivers/sound/lowlevel/README.aedsp16 file and
-  the head of drivers/sound/lowlevel/aedsp16.c to get more information
-  about this driver and its configuration.  This driver supports Audio
-  Excel DSP 16 but not the III nor PnP versions of this card. Read
-  drivers/sound/lowlevel/README.aedsp16 if you want to know something
-  more on how to use the III version with this sound driver.
+  questions.
+
+  Read the drivers/sound/lowlevel/README.aedsp16 file and the head of
+  drivers/sound/lowlevel/aedsp16.c as well as
+  Documentation/sound/AudioExcelDSP16 to get more information about
+  this driver and its configuration.
 
 SC-6600 based audio cards (new Audio Excel DSP 16)
 CONFIG_SC6600
@@ -8367,8 +8420,9 @@
 Audio Excel DSP 16 (SBPro emulation)
 CONFIG_AEDSP16_SBPRO
   Answer Y if you want your audio card to emulate Sound Blaster Pro.
-  You should then say Y to "Sound Blaster (SB, SBPro, SB16, clones)
-  support" and N to "Audio Excel DSP 16 (MSS emulation)".
+  You should then say Y to "100% Sound Blaster compatibles
+  (SB16/32/64, ESS, Jazz16) support" and N to "Audio Excel DSP 16 (MSS
+  emulation)".
 
 Ensoniq ES1370 based PCI sound cards
 CONFIG_SOUND_ES1370
@@ -8404,11 +8458,11 @@
 CONFIG_MAGIC_SYSRQ
   If you say Y here, you will have some control over the system even
   if the system crashes for example during kernel debugging (e.g., you
-  will be able to flush the disks, reboot the system immediately or
-  dump some status information). This is accomplished by pressing
-  various keys while holding SysRq (Alt+PrintScreen). The keys are
-  documented in Documentation/sysrq.txt. Don't say Y unless you really
-  know what this hack does.
+  will be able to flush the buffer cache to disk, reboot the system
+  immediately or dump some status information). This is accomplished
+  by pressing various keys while holding SysRq (Alt+PrintScreen). The
+  keys are documented in Documentation/sysrq.txt. Don't say Y unless
+  you really know what this hack does.
 
 ISDN subsystem
 CONFIG_ISDN
@@ -8640,8 +8694,8 @@
 CONFIG_DE_AOC
   If you want that the HiSax hardware driver sends messages to the
   upper level of the isdn code on each AOCD (Advice Of Charge, During
-  the call - transmission of the fee information during a call) and on
-  each AOCE (Advice Of Charge, at the End of the call - transmission
+  the call -- transmission of the fee information during a call) and on
+  each AOCE (Advice Of Charge, at the End of the call -- transmission
   of fee information at the end of the call), say Y here. This works
   only in Germany.
 
@@ -8719,9 +8773,9 @@
 CONFIG_ISDN_DRV_ACT2000
   Say Y here if you have an IBM Active 2000 ISDN card. In order to use
   this card, additional firmware is necessary, which has to be loaded
-  into the card using a utility which is part of the latest isdn4k-utils
-  package. Please read the file Documentation/isdn/README.act2000 for
-  more information.
+  into the card using a utility which is part of the latest
+  isdn4k-utils package. Please read the file
+  Documentation/isdn/README.act2000 for more information.
 
 Support for AP1000 multicomputer
 CONFIG_AP1000
@@ -8733,9 +8787,9 @@
 
 Support for Sun4 architecture
 CONFIG_SUN4
-  Use this option if, and only if, your machine is sun4. Note that
-  kernel compiled with this option will run on sun4 only.
-  (And in current version, it will probably work on sun4/330, only.)
+  Say Y here if, and only if, your machine is a Sun4. Note that
+  a kernel compiled with this option will run only on Sun4.
+  (And the current version will probably work only on sun4/330.)
 
 SPARC ESP SCSI support
 CONFIG_SCSI_SUNESP
@@ -8760,6 +8814,7 @@
 ###
 ### Please someone fill these in.
 ###
+
 #
 # m68k-specific kernel options
 # Documented by Chris Lawrence <quango@themall.net> et al.
@@ -8844,14 +8899,14 @@
 CONFIG_RMW_INSNS
   This allows to use certain instructions that work with indivisible
   read-modify-write bus cycles. While this is faster than the
-  workaround of disabling interrupts, it can conflict with DMA (=
-  direct memory access) on many Amiga systems, and it is also said to
-  destabilize other machines. It is very likely that this will cause
-  serious problems on any Amiga or Atari Medusa if set. The only
+  workaround of disabling interrupts, it can conflict with DMA 
+  ( = direct memory access) on many Amiga systems, and it is also said
+  to destabilize other machines. It is very likely that this will
+  cause serious problems on any Amiga or Atari Medusa if set. The only
   configuration where it should work are 68030-based Ataris, where it
   apparently improves performance. But you've been warned! Unless you
-  really know what you are doing, say N. Try Y only if you're
-  quite adventurous.
+  really know what you are doing, say N. Try Y only if you're quite
+  adventurous.
 
 Amiga AutoConfig Identification
 CONFIG_ZORRO
@@ -8885,15 +8940,6 @@
   and CD32.  If you intend to run Linux on any of these systems, say Y;
   otherwise say N.
 
-Amiga Cybervision support
-CONFIG_FB_CYBER
-  This enables support for the Cybervision 64 graphics card from Phase5.
-  Please note that its use is not all that intuitive (i.e. if you have
-  any questions, be sure to ask!).  Say N unless you have a Cybervision
-  64 or plan to get one before you next recompile the kernel.
-  Please note that this driver DOES NOT support the Cybervision 64 3D
-  card at present, as they use incompatible video chips.
-
 Amiga GSP (TMS340x0) support
 CONFIG_AMIGA_GSP
   Include support for Amiga graphics cards that use the Texas
@@ -8915,17 +8961,17 @@
 CONFIG_AMIGA_Z2RAM
   This enables support for using Chip RAM and Zorro II RAM as a
   ramdisk or as a swap partition.  Say Y if you want to include this
-  driver in the kernel.  This driver is also available as a module ( =
-  code which can be inserted in and removed from the running kernel
-  whenever you want). The module is called z2ram.o. If you want to
-  compile it as a module, say M here and read
+  driver in the kernel.  This driver is also available as a module 
+  ( = code which can be inserted in and removed from the running
+  kernel whenever you want). The module is called z2ram.o. If you want
+  to compile it as a module, say M here and read
   Documentation/modules.txt.
 
 Atari ST-RAM swap support
 CONFIG_STRAM_SWAP
   This enables support for using (parts of) ST-RAM as swap space,
   instead of as normal system memory. This can first enhance system
-  performace if you have lots of alternate RAM (compared to the size
+  performance if you have lots of alternate RAM (compared to the size
   of ST-RAM), because executable code always will reside in faster
   memory. ST-RAM will remain as ultra-fast swap space. On the other
   hand, it allows much improved dynamic allocations of ST-RAM buffers
@@ -9006,12 +9052,13 @@
 Cyberstorm II SCSI support
 CONFIG_CYBERSTORMII_SCSI
   If you have an Amiga with a Phase5 Cyberstorm MkII accelerator board
-  and the optional Cyberstorm SCSI controller, say Y.  Otherwise, say N.
+  and the optional Cyberstorm SCSI controller, say Y. Otherwise, say
+  N.
 
 Blizzard 2060 SCSI support
 CONFIG_BLZ2060_SCSI
   If you have an Amiga with a Phase5 Blizzard 2060 accelerator board
-  and want to use the onboard SCSI controller, say Y.  Otherwise, say
+  and want to use the onboard SCSI controller, say Y. Otherwise, say
   N.
 
 Blizzard 1230IV/1260 SCSI support
@@ -9235,17 +9282,6 @@
   Say Y if you need this feature; users who are only using their
   system-native partitioning scheme can say N here.
 
-Board Type
-CONFIG_PMAC
-  There are currently several different kinds of PowerPC-based machines
-  available: Apple Power Macintoshes and clones (such as the Motorola
-  Starmax series, PReP (PowerPC Reference Platform) machines such
-  as the Motorola PowerStack, Amiga Power-Up systems (APUS), CHRP and the
-  embedded MBX boards from Motorola.  Currently, a single kernel binary
-  only supports one type or the other.  However, there is very early work
-  on support CHRP, PReP and PowerMac's from a single binary.
-
-
 Processor Type
 CONFIG_6xx
   There are two types of PowerPC chips supported.  The more common
@@ -9253,6 +9289,17 @@
   Unless you are building a kernel for one of the embedded boards using
   the 821 or 860 choose 6xx.
 
+Machine Type
+CONFIG_PMAC
+  Linux currently supports several different kinds of PowerPC-based
+  machines: Apple Power Macintoshes and clones (such as the
+  Motorola Starmax series), PReP (PowerPC Reference Platform) machines
+  such as the Motorola PowerStack, Amiga Power-Up systems (APUS), CHRP
+  and the embedded MBX boards from Motorola. Currently, a single
+  kernel binary only supports one type or the other. However, there is
+  very early work on support for CHRP, PReP and PowerMac's from a
+  single binary.
+
 Support for Open Firmware device tree in /proc
 CONFIG_PROC_DEVICETREE
   This option adds a device-tree directory under /proc which contains
@@ -9275,10 +9322,10 @@
   Many Power Macintoshes and clones have a MESH (Macintosh Enhanced
   SCSI Hardware) SCSI bus adaptor (the 7200 doesn't, but all of the
   other Power Macintoshes do).  Say Y to include support for this SCSI
-  adaptor.  This driver is also available as a module called mesh.o (
-  = code which can be inserted in and removed from the running kernel
-  whenever you want). If you want to compile it as a module, say M
-  here and read Documentation/modules.txt.
+  adaptor.  This driver is also available as a module called mesh.o
+  ( = code which can be inserted in and removed from the running
+  kernel whenever you want). If you want to compile it as a module,
+  say M here and read Documentation/modules.txt.
 
 Maximum synchronous transfer rate
 CONFIG_SCSI_MESH_SYNC_RATE
@@ -9309,6 +9356,11 @@
   motherboard will usually use a MACE (Medium Access Control for
   Ethernet) interface.  Say Y to include support for the MACE chip.
 
+BMAC (G3 ethernet) support
+CONFIG_BMAC
+  Say Y for support of BMAC Ethernet interfaces. These are used on G3
+  computers. 
+
 Video For Linux
 CONFIG_VIDEO_DEV
   Support for audio/video capture and overlay devices and FM radio
@@ -9316,6 +9368,10 @@
   this are available from
   ftp://ftp.uk.linux.org/pub/linux/video4linux.
 
+  If you are interested in writing a driver for such an audio/video
+  device or user software interacting with such a driver, please read
+  the file Documentation/video4linux/API.html.
+
   This driver is also available as a module called videodev.o ( = code
   which can be inserted in and removed from the running kernel
   whenever you want). If you want to compile it as a module, say M
@@ -9326,16 +9382,17 @@
   Choose Y here if you have one of these FM radio cards, and then fill
   in the port address below.
 
-  Note that newer AIMSlab RadioTrack cards have a different chipset,
-  not supported by this driver.  For these cards, use the RadioTrack II
-  driver below.  
+  Note that newer AIMSlab RadioTrack cards have a different chipset
+  and are not supported by this driver. For these cards, use the
+  RadioTrack II driver below.
 
   In order to control your radio card, you will need to use programs
   that are compatible with the Video for Linux API.  Information on 
   this API and pointers to "v4l" programs may be found on the WWW at
   http://roadrunner.swansea.uk.linux.org/v4l.shtml; to browse the WWW,
   you need to have access to a machine on the Internet that has a 
-  program like lynx or netscape.
+  program like lynx or netscape. More information is contained in the
+  file Documentation/video4linux/radiotrack.txt.
 
   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),
@@ -9434,10 +9491,28 @@
 CONFIG_RADIO_ZOLTRIX_PORT
   Enter the I/O port of your Zoltrix radio card.
 
+Miro PCM20 Radio
+CONFIG_RADIO_MIROPCM20
+  Choose Y here if you have one of these FM radio cards, and then fill
+  in the port address below.
+
+  In order to control your radio card, you will need to use programs
+  that are compatible with the Video for Linux API.  Information on 
+  this API and pointers to "v4l" programs may be found on the WWW at
+  http://roadrunner.swansea.uk.linux.org/v4l.shtml; to browse the WWW,
+  you need to have access to a machine on the Internet that has a 
+  program like lynx or netscape.
+
+  If you want to compile this driver as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt. The module will be
+  called radio-miropcm20.o
+
 BT848 Video For Linux
 CONFIG_VIDEO_BT848
   Support for BT848 based frame grabber/overlay boards. This includes
-  the Miro, Hauppauge and STB boards. 
+  the Miro, Hauppauge and STB boards. Please read the material in
+  Documentation/video4linux/bttv for more information.
 
   This driver is also available as a module called bttv.o ( = code
   which can be inserted in and removed from the running kernel
@@ -9497,13 +9572,15 @@
 
 Build Tools Selection
 CONFIG_BINUTILS_NEW
-  Say Y here if you're using GCC 2.8.1/EGCS with a binutils 
-  version >= 2.8.1 to compile the kernel. Otherwise, say N.
+  Say Y here if and only if you're using GCC 2.8.1/EGCS with a
+  binutils version >= 2.8.1 to compile the kernel (check with "gcc
+  --version" and "ld -v").
 
 Compile kernel with frame pointer
 CONFIG_FRAME_POINTER
-  In order to give useful debugging/error results, say Y here, otherwise
-  say N.
+  If you say Y here, the resulting kernel will be slightly larger, but
+  it will give useful debugging/error results. If you don't debug the
+  kernel, you can say N.
 
 VIDC Sound
 CONFIG_VIDC_SOUND
@@ -9515,7 +9592,7 @@
 #   capitalize: AppleTalk, Ethernet, DMA, FTP, Internet, Intel, IRQ, 
 #               Linux, NetWare, NFS, PCI, SCSI, SPARC
 #   two words:  hard drive, hard disk, sound card, home page
-#   other:      it's safe to save; daemon
+#   other:      it's safe to save; daemon; use --, not - or ---
 #
 # This is used by Emacs' spell checker ispell.el:
 #
@@ -9605,7 +9682,7 @@
 # LocalWords:  YAMADA tetsu cauchy nslab ntt nevod perm su doc kaf kheops wsc
 # LocalWords:  traduc Bourgin dbourgin menuconfig kfill READMEs HOWTOs Virge WA
 # LocalWords:  IDEDISK IDEFLOPPY EIDE firewalls QMAGIC ZMAGIC LocalWords opti
-# LocalWords:  SVGATextMode vga svga Xterminal Xkernel syr jmwobus comfaqs dhcp
+# LocalWords:  SVGATextMode vga svga Xkernel syr jmwobus comfaqs dhcp flakey GD
 # LocalWords:  IPv IPng interoperability ipng ipv radio's tapr pkthome PLP nano
 # LocalWords:  Ses Mhz sethdlc SOUNDMODEM WindowsSoundSystem smdiag pcf inka ES
 # LocalWords:  smmixer ptt circ soundmodem MKISS FDDI DEFEA DEFPA DEFXX redhat
@@ -9646,7 +9723,7 @@
 # LocalWords:  hardlinked NAMETRANS env mtab fstab umount nologin runlevel gid
 # LocalWords:  transname filespace adm Nodename hostname uname Kernelname bootp
 # LocalWords:  KERNNAME kname ktype kernelname Kerneltype KERNTYPE Alt RX mdafb
-# LocalWords:  dataless kerneltype SYSNAME Comtrol Rocketport palmtop
+# LocalWords:  dataless kerneltype SYSNAME Comtrol Rocketport palmtop fbset EGS
 # LocalWords:  nvram SYSRQ SysRq PrintScreen sysrq NVRAMs NvRAM Shortwave RTTY
 # LocalWords:  HFMODEM shortwave Sitor Amtor Pactor GTOR hfmodem hayes TX TMOUT
 # LocalWords:  IDEPCI IDEDMA idedma PDC pdc TRM trm raidtools luthien nuclecu
@@ -9693,10 +9770,16 @@
 # LocalWords:  AcornSCSI EcoSCSI EESOX EESOXSCSI Powertec POWERTECSCSI dec SF
 # LocalWords:  RadioReveal gatekeeper aimslab aztech FMI sf fmi RTL rtl cesdis
 # LocalWords:  Yellowfin gsfc nasa gov yellowfin pcnet Mylex LNE lne EtherH hs
-# LocalWords:  EBSA chattr RiscOS Winmodem AGP Atomwide DUALSP pcsp robinson
+# LocalWords:  EBSA chattr RiscOS Winmodem AGP Atomwide DUALSP pcsp robinson CT
 # LocalWords:  SGALAXY Waverider DSPxxx TRXPRO AudioTrix OSWF MOT CFB DSY kbps
 # LocalWords:  tuwien kkudielk LVD mega lun MAXTAGS Gbps arcnet Olicom SKTR SNA
-# LocalWords:  SysKonnect sktr sna etherboot ufs NetBEUI MultiSound MSNDCLAS
-# LocalWords:  MSNDINIT MSNDPERM MSNDPIN PNDSPINI PNDSPERM Ensoniq's
-# LocalWords:  AudioPCI lspci SonicVibes sonicvibes SPARCs roadrunner
+# LocalWords:  SysKonnect sktr sna etherboot ufs NetBEUI MultiSound MSNDCLAS GX
+# LocalWords:  MSNDINIT MSNDPERM MSNDPIN PNDSPINI PNDSPERM Ensoniq's RetinaZ SS
+# LocalWords:  AudioPCI lspci SonicVibes sonicvibes SPARCs roadrunner CLgen UPA
 # LocalWords:  swansea shtml Zoltrix zoltrix BINUTILS EGCS binutils VIDC DACs
+# LocalWords:  CyberVision Cirrus PowerBooks Topcat SBUS CGsix TurboGX BWtwo SS
+# LocalWords:  CGthree TCX unswapable vfb fbcon hicolor truecolor AFB ILBM SOC
+# LocalWords:  IPLAN gracilis Fibre SBus SparcSTORAGE SV jnewbigin swin QNX qnx
+# LocalWords:  PTY PTYS ptyxx ttyxx PTYs ssh sb Avance ALS pss
+# LocalWords:  synth WaveFront MSND NONPNP AudioExcelDSP STRAM APUS CHRP MBX
+# LocalWords:  PowerMac's BMAC radiotrack rtrack miropcm
diff -u --recursive --new-file v2.1.121/linux/Documentation/networking/arcnet-hardware.txt linux/Documentation/networking/arcnet-hardware.txt
--- v2.1.121/linux/Documentation/networking/arcnet-hardware.txt	Sun Jun  7 11:16:26 1998
+++ linux/Documentation/networking/arcnet-hardware.txt	Mon Sep 14 11:32:22 1998
@@ -10,8 +10,8 @@
 Because so many people (myself included) seem to have obtained ARCnet cards
 without manuals, this file contains a quick introduction to ARCnet hardware,
 some cabling tips, and a listing of all jumper settings I can find. Please
-e-mail apenwarr@bond.net with any settings for your particular card, or any
-other information you have!
+e-mail apenwarr@worldvisions.ca with any settings for your particular card,
+or any other information you have!
 
 
 INTRODUCTION TO ARCNET
@@ -80,7 +80,7 @@
 This section was rewritten by 
         Vojtech Pavlik     <Vojtech.Pavlik@st.mff.cuni.cz>
 using information from several people, including:
-        Avery Pennraun     <apenwarr@bond.net>
+        Avery Pennraun     <apenwarr@worldvisions.ca>
  	Stephen A. Wood    <saw@hallc1.cebaf.gov>
  	John Paul Morrison <jmorriso@bogomips.ee.ubc.ca>
  	Joachim Koenig     <jojo@repas.de>
@@ -438,8 +438,8 @@
 PC100, PC110, PC120, PC130 (8-bit cards)
 PC500, PC600 (16-bit cards)
 ---------------------------------
-  - mainly from Avery Pennarun <apenwarr@foxnet.net>.  Values depicted are
-    from Avery's setup.
+  - mainly from Avery Pennarun <apenwarr@worldvisions.ca>.  Values depicted
+    are from Avery's setup.
   - special thanks to Timo Hilbrink <timoh@xs4all.nl> for noting that PC120,
     130, 500, and 600 all have the same switches as Avery's PC100. 
     PC500/600 have several extra, undocumented pins though. (?)
@@ -3129,6 +3129,6 @@
 
 I have no information on other models of ARCnet cards at the moment.  Please
 send any and all info to:
-	apenwarr@bond.net
+	apenwarr@worldvisions.ca
 
 Thanks.
diff -u --recursive --new-file v2.1.121/linux/Documentation/networking/arcnet.txt linux/Documentation/networking/arcnet.txt
--- v2.1.121/linux/Documentation/networking/arcnet.txt	Sun Jun  7 11:16:26 1998
+++ linux/Documentation/networking/arcnet.txt	Mon Sep 14 11:32:22 1998
@@ -37,7 +37,7 @@
 include the type of card(s) you're using, software, size of network, and
 whether it's working or not.)
 
-My e-mail address is: apenwarr@bond.net
+My e-mail address is: apenwarr@worldvisions.ca
 
 
 ---------------------------------------------------------------------------
@@ -79,9 +79,8 @@
 Other Drivers and Info
 ----------------------
 
-You can (could - foxnet.net is no more - DW.) try my ARCNET page on the 
-World Wide Web at:
-	http://www.foxnet.net/~apenwarr/arcnet/
+You can try my ARCNET page on the World Wide Web at:
+	http://www.worldvisions.ca/~apenwarr/arcnet/
 
 Also, SMC (one of the companies that makes ARCnet cards) has a WWW site you
 might be interested in, which includes several drivers for various cards
@@ -494,7 +493,7 @@
 Send mail describing your setup, preferably including driver version, kernel
 version, ARCnet card model, CPU type, number of systems on your network, and
 list of software in use to me at the following address:
-	apenwarr@bond.net
+	apenwarr@worldvisions.ca
 
 I do send (sometimes automated) replies to all messages I receive.  My email
 can be weird (and also usually gets forwarded all over the place along the
diff -u --recursive --new-file v2.1.121/linux/Documentation/networking/cs89x0.txt linux/Documentation/networking/cs89x0.txt
--- v2.1.121/linux/Documentation/networking/cs89x0.txt	Sun Jun  7 11:16:26 1998
+++ linux/Documentation/networking/cs89x0.txt	Fri Sep 11 11:21:57 1998
@@ -612,7 +612,7 @@
 6.3.1 CRYSTAL'S WEB SITE
 
 Crystal Semiconductor maintains a web page at http://www.crystal.com with the
-the latest drivers and technical publications.
+latest drivers and technical publications.
 
 
 6.3.2 CRYSTAL'S BULLETIN BOARD SERVICE
diff -u --recursive --new-file v2.1.121/linux/Documentation/sound/ChangeLog.multisound linux/Documentation/sound/ChangeLog.multisound
--- v2.1.121/linux/Documentation/sound/ChangeLog.multisound	Tue Aug 18 22:02:01 1998
+++ linux/Documentation/sound/ChangeLog.multisound	Thu Sep 10 16:37:25 1998
@@ -1,3 +1,66 @@
+1998-09-10  Andrew Veliath  <andrewtv@usa.net>
+
+	* Update version to 0.8.2
+
+	* Add SNDCTL_DSP_GETOSPACE and SNDCTL_DSP_GETISPACE ioctls.
+
+1998-09-09  Andrew Veliath  <andrewtv@usa.net>
+
+	* Update version to 0.8.1
+	
+	* msnd_pinnacle.c: Fix resetting of default audio parameters. Turn
+	flush code from dsp_halt into dsp_write_flush, and use that for
+	SNDCTL_DSP_SYNC.
+
+1998-09-07  Andrew Veliath  <andrewtv@usa.net>
+
+	* Update version to 0.8.0
+
+	* Provide separate signal parameters for play and record.
+	
+	* Cleanups to locking and interrupt handling, change default
+	fifosize to 128kB.
+
+	* Update version to 0.7.15
+
+	* Interprocess full-duplex support (ie `cat /dev/dsp > /dev/dsp').
+
+	* More mutex sections for read and write fifos (read + write locks
+	added).
+
+1998-09-05  Andrew Veliath  <andrewtv@usa.net>
+
+	* msnd_pinnacle.c: (chk_send_dsp_cmd) Do full DSP reset upon DSP
+	timeout (when not in interrupt; maintains mixer settings).  Fixes
+	to flushing and IRQ ref counting. Rewrote queuing for smoother
+	playback and fixed initial playback cutoff problem.
+
+1998-09-03  Andrew Veliath  <andrewtv@usa.net>
+
+	* Replaced packed structure accesses with standard C equivalents.
+
+1998-09-01  Andrew Veliath  <andrewtv@usa.net>
+
+	* msnd_pinnacle.c: Add non-PnP configuration to driver code, which
+	  will facilitate compiled-in operation.
+
+1998-08-29  Andrew Veliath  <andrewtv@usa.net>
+
+	* Update version to 0.7.6
+	
+	* msnd_pinnacle.c (dsp_ioctl): Add DSP_GETFMTS, change SAMPLESIZE
+	  to DSP_SETFMT.
+
+	* Update version to 0.7.5
+	
+	* Create pinnaclecfg.c and turn MultiSound doc into a shell
+	  archive with pinnaclecfg.c included.  pinnaclecfg.c can
+	  now fully configure the card in non-PnP mode, including the
+	  joystick and IDE controller.  Also add an isapnp conf
+	  example.
+
+	* Reduce DSP reset timeout from 20000 to 100
+
 1998-08-06  Andrew Veliath  <andrewtv@usa.net>
 
 	* Update version to 0.7.2
diff -u --recursive --new-file v2.1.121/linux/Documentation/sound/Wavefront linux/Documentation/sound/Wavefront
--- v2.1.121/linux/Documentation/sound/Wavefront	Tue Aug 18 22:02:01 1998
+++ linux/Documentation/sound/Wavefront	Thu Sep 10 16:37:25 1998
@@ -3,15 +3,15 @@
 
 		     Paul Barton-Davis, July 1998
 
-			  VERSION 0.2.4
+			  VERSION 0.2.5
 
 Driver Status
 -------------
 
-Requires: Kernel 2.1.106 or later (a version of the driver is included
-with kernels 2.1.109 and above)
+Requires: Kernel 2.1.106 or later (the driver is included with kernels
+2.1.109 and above)
 	  
-As of 7/20/1998, this driver is currently in *BETA* state. This means
+As of 7/22/1998, this driver is currently in *BETA* state. This means
 that it compiles and runs, and that I use it on my system (Linux
 2.1.106) with some reasonably demanding applications and uses.  I
 believe the code is approaching an initial "finished" state that
diff -u --recursive --new-file v2.1.121/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.121/linux/MAINTAINERS	Wed Sep  9 14:51:03 1998
+++ linux/MAINTAINERS	Sat Sep 12 10:34:39 1998
@@ -292,6 +292,12 @@
 W:	http://www.nyx.net/~arobinso
 S:	Maintainted
 
+HFS FILESYSTEM
+P:      Adrian Sun
+M:      asun@u.washington.edu
+L:      linux-kernel@vger.rutgers.edu
+S:      Maintained
+
 HIGH-SPEED SCC DRIVER FOR AX.25
 P:	Klaus Kudielka
 M:	oe1kib@oe1kib.ampr.org
@@ -585,12 +591,6 @@
 M:	R.E.Wolff@BitWizard.nl
 M:	io8-linux@specialix.co.uk
 L:	linux-kernel@vger.rutgers.edu ?
-S:	Supported
-
-SPX NETWORK LAYER
-P:	Jay Schulist
-M:	Jay.Schulist@spacs.k12.wi.us
-L:	linux-net@vger.rutgers.edu
 S:	Supported
 
 SPX NETWORK LAYER
diff -u --recursive --new-file v2.1.121/linux/Makefile linux/Makefile
--- v2.1.121/linux/Makefile	Wed Sep  9 14:51:03 1998
+++ linux/Makefile	Wed Sep  9 16:25:25 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 121
+SUBLEVEL = 122
 
 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.1.121/linux/arch/alpha/kernel/osf_sys.c linux/arch/alpha/kernel/osf_sys.c
--- v2.1.121/linux/arch/alpha/kernel/osf_sys.c	Wed Sep  9 14:51:04 1998
+++ linux/arch/alpha/kernel/osf_sys.c	Sun Sep 13 10:22:17 1998
@@ -253,9 +253,11 @@
 	unsigned long ret = -EBADF;
 
 	lock_kernel();
+#if 0
 	if (flags & (_MAP_HASSEMAPHORE | _MAP_INHERIT | _MAP_UNALIGNED))
 		printk("%s: unimplemented OSF mmap flags %04lx\n", 
 			current->comm, flags);
+#endif
 	if (!(flags & MAP_ANONYMOUS)) {
 		file = fget(fd);
 		if (!file)
diff -u --recursive --new-file v2.1.121/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.1.121/linux/arch/i386/config.in	Thu Aug  6 14:06:28 1998
+++ linux/arch/i386/config.in	Wed Sep  9 22:29:39 1998
@@ -64,6 +64,16 @@
   fi
 fi
 
+bool 'Advanced Power Management BIOS support' CONFIG_APM
+if [ "$CONFIG_APM" = "y" ]; then
+  bool '   Ignore USER SUSPEND' CONFIG_APM_IGNORE_USER_SUSPEND
+  bool '   Enable PM at boot time' CONFIG_APM_DO_ENABLE
+  bool '   Make CPU Idle calls when idle' CONFIG_APM_CPU_IDLE
+  bool '   Enable console blanking using APM' CONFIG_APM_DISPLAY_BLANK
+  bool '   Power off on shutdown' CONFIG_APM_POWER_OFF
+  bool '   Ignore multiple suspend' CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
+fi
+
 endmenu
 
 source drivers/pnp/Config.in
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile
--- v2.1.121/linux/arch/i386/kernel/Makefile	Tue Jul 21 00:15:30 1998
+++ linux/arch/i386/kernel/Makefile	Wed Sep  9 22:40:36 1998
@@ -34,6 +34,10 @@
   endif
 endif
 
+ifdef CONFIG_APM
+OX_OBJS += apm.o
+endif
+
 ifdef SMP
 O_OBJS += io_apic.o smp.o trampoline.o
 endif
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c
--- v2.1.121/linux/arch/i386/kernel/apm.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/i386/kernel/apm.c	Sun Sep 13 12:16:22 1998
@@ -0,0 +1,1345 @@
+/* -*- linux-c -*-
+ * APM BIOS driver for Linux
+ * Copyright 1994-1998 Stephen Rothwell
+ *                     (Stephen.Rothwell@canb.auug.org.au)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * October 1995, Rik Faith (faith@cs.unc.edu):
+ *    Minor enhancements and updates (to the patch set) for 1.3.x
+ *    Documentation
+ * January 1996, Rik Faith (faith@cs.unc.edu):
+ *    Make /proc/apm easy to format (bump driver version)
+ * March 1996, Rik Faith (faith@cs.unc.edu):
+ *    Prohibit APM BIOS calls unless apm_enabled.
+ *    (Thanks to Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>)
+ * April 1996, Stephen Rothwell (Stephen.Rothwell@canb.auug.org.au)
+ *    Version 1.0 and 1.1
+ * May 1996, Version 1.2
+ * Feb 1998, Version 1.3
+ * Feb 1998, Version 1.4
+ * Aug 1998, Version 1.5
+ *
+ * History:
+ *    0.6b: first version in official kernel, Linux 1.3.46
+ *    0.7: changed /proc/apm format, Linux 1.3.58
+ *    0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
+ *    0.9: only call bios if bios is present, Linux 1.3.72
+ *    1.0: use fixed device number, consolidate /proc/apm into this file,
+ *         Linux 1.3.85
+ *    1.1: support user-space standby and suspend, power off after system
+ *         halted, Linux 1.3.98
+ *    1.2: When resetting RTC after resume, take care so that the time
+ *         is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
+ *         <jtoth@princeton.edu>); improve interaction between
+ *         screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
+ *    1.2a:Simple change to stop mysterious bug reports with SMP also added
+ *	   levels to the printk calls. APM is not defined for SMP machines.
+ *         The new replacment for it is, but Linux doesn't yet support this.
+ *         Alan Cox Linux 2.1.55
+ *    1.3: Set up a valid data descriptor 0x40 for buggy BIOS's
+ *    1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by 
+ *         Dean Gaudet <dgaudet@arctic.org>.
+ *         C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87
+ *    1.5: Fix segment register reloading (in case of bad segments saved
+ *         across BIOS call).
+ *         Stephen ROthwell
+ *
+ * APM 1.1 Reference:
+ *
+ *   Intel Corporation, Microsoft Corporation. Advanced Power Management
+ *   (APM) BIOS Interface Specification, Revision 1.1, September 1993.
+ *   Intel Order Number 241704-001.  Microsoft Part Number 781-110-X01.
+ *
+ * [This document is available free from Intel by calling 800.628.8686 (fax
+ * 916.356.6100) or 800.548.4725; or via anonymous ftp from
+ * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc.  It is also
+ * available from Microsoft by calling 206.882.8080.]
+ *
+ * APM 1.2 Reference:
+ *   Intel Corporation, Microsoft Corporation. Advanced Power Management
+ *   (APM) BIOS Interface Specification, Revision 1.2, February 1996.
+ *
+ * [This document is available from Intel at:
+ *    http://www.intel.com/IAL/powermgm
+ *  or Microsoft at
+ *    http://www.microsoft.com/windows/thirdparty/hardware/pcfuture.htm
+ * ]
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/poll.h>
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/fcntl.h>
+#include <linux/malloc.h>
+#include <linux/linkage.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#endif
+#include <linux/miscdevice.h>
+#include <linux/apm_bios.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/desc.h>
+
+EXPORT_SYMBOL(apm_register_callback);
+EXPORT_SYMBOL(apm_unregister_callback);
+
+extern unsigned long get_cmos_time(void);
+
+/*
+ * The apm_bios device is one of the misc char devices.
+ * This is its minor number.
+ */
+#define	APM_MINOR_DEV	134
+
+/* Configurable options:
+ *
+ * CONFIG_APM_IGNORE_USER_SUSPEND: define to ignore USER SUSPEND requests.
+ * This is necessary on the NEC Versa M series, which generates these when
+ * resuming from SYSTEM SUSPEND.  However, enabling this on other laptops
+ * will cause the laptop to generate a CRITICAL SUSPEND when an appropriate
+ * USER SUSPEND is ignored -- this may prevent the APM driver from updating
+ * the system time on a RESUME.
+ *
+ * CONFIG_APM_DO_ENABLE: enable APM features at boot time.  From page 36 of
+ * the specification: "When disabled, the APM BIOS does not automatically
+ * power manage devices, enter the Standby State, enter the Suspend State,
+ * or take power saving steps in response to CPU Idle calls."  This driver
+ * will make CPU Idle calls when Linux is idle (unless this feature is
+ * turned off -- see below).  This should always save battery power, but
+ * more complicated APM features will be dependent on your BIOS
+ * implementation.  You may need to turn this option off if your computer
+ * hangs at boot time when using APM support, or if it beeps continuously
+ * instead of suspending.  Turn this off if you have a NEC UltraLite Versa
+ * 33/C or a Toshiba T400CDT.  This is off by default since most machines
+ * do fine without this feature.
+ *
+ * CONFIG_APM_CPU_IDLE: enable calls to APM CPU Idle/CPU Busy inside the
+ * idle loop.  On some machines, this can activate improved power savings,
+ * such as a slowed CPU clock rate, when the machine is idle.  These idle
+ * call is made after the idle loop has run for some length of time (e.g.,
+ * 333 mS).  On some machines, this will cause a hang at boot time or
+ * whenever the CPU becomes idle.
+ *
+ * CONFIG_APM_DISPLAY_BLANK: enable console blanking using the APM.  Some
+ * laptops can use this to turn of the LCD backlight when the VC screen
+ * blanker blanks the screen.  Note that this is only used by the VC screen
+ * blanker, and probably won't turn off the backlight when using X11.  Some
+ * problems have been reported when using this option with gpm (if you'd
+ * like to debug this, please do so).
+ *
+ * CONFIG_APM_IGNORE_MULTIPLE_SUSPEND: The IBM TP560 bios seems to insist
+ * on returning multiple suspend/standby events whenever one occurs.  We
+ * really only need one at a time, so just ignore any beyond the first.
+ * This is probably safe on most laptops.
+ *
+ * If you are debugging the APM support for your laptop, note that code for
+ * all of these options is contained in this file, so you can #define or
+ * #undef these on the next line to avoid recompiling the whole kernel.
+ *
+ */
+
+/* KNOWN PROBLEM MACHINES:
+ *
+ * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
+ *                         [Confirmed by TI representative]
+ * U: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
+ *                    [Confirmed by BIOS disassembly]
+ *                    [This may work now ...]
+ * P: Toshiba 1950S: battery life information only gets updated after resume
+ * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking
+ * 	broken in BIOS [Reported by Garst R. Reese <reese@isn.net>]
+ *
+ * Legend: U = unusable with APM patches
+ *         P = partially usable with APM patches
+ */
+
+/*
+ * Define to have debug messages.
+ */
+#undef APM_DEBUG
+
+/*
+ * Define to always call the APM BIOS busy routine even if the clock was
+ * not slowed by the idle routine.
+ */
+#define ALWAYS_CALL_BUSY
+
+/*
+ * Define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call
+ * should turn interrupts on before it does a 'hlt').
+ * This reportedly needs undefining for the ThinkPad 600.
+ */
+#define APM_NOINTS
+
+/*
+ * Define to make the APM BIOS calls zero all data segment registers (so
+ * that an incorrect BIOS implementation will cause a kernel panic if it
+ * tries to write to arbitrary memory).
+ */
+#define APM_ZERO_SEGS
+
+/*
+ * Define to make all set_limit calls use 64k limits.  The APM 1.1 BIOS is
+ * supposed to provide limit information that it recognizes.  Many machines
+ * do this correctly, but many others do not restrict themselves to their
+ * claimed limit.  When this happens, they will cause a segmentation
+ * violation in the kernel at boot time.  Most BIOS's, however, will
+ * respect a 64k limit, so we use that.  If you want to be pedantic and
+ * hold your BIOS to its claims, then undefine this.
+ */
+#define APM_RELAX_SEGMENTS
+
+/*
+ * Need to poll the APM BIOS every second
+ */
+#define APM_CHECK_TIMEOUT	(HZ)
+
+/*
+ * Save a segment register away
+ */
+#define savesegment(seg, where)	__asm__ __volatile__("movl %%" #seg ",%0" : "=m" (where))
+
+/*
+ * Forward declarations
+ */
+static void	suspend(void);
+static void	standby(void);
+static void	set_time(void);
+
+static void	check_events(void);
+static void	do_apm_timer(unsigned long);
+
+static int	do_open(struct inode *, struct file *);
+static int	do_release(struct inode *, struct file *);
+static ssize_t	do_read(struct file *, char *, size_t , loff_t *);
+static unsigned int do_poll(struct file *, poll_table *);
+static int	do_ioctl(struct inode *, struct file *, u_int, u_long);
+
+#ifdef CONFIG_PROC_FS
+static int	apm_get_info(char *, char **, off_t, int, int);
+#endif
+
+extern int	apm_register_callback(int (*)(apm_event_t));
+extern void	apm_unregister_callback(int (*)(apm_event_t));
+
+/*
+ * Local variables
+ */
+static asmlinkage struct {
+	unsigned long	offset;
+	unsigned short	segment;
+}				apm_bios_entry;
+static int			apm_enabled = 0;
+#ifdef CONFIG_APM_CPU_IDLE
+static int			clock_slowed = 0;
+#endif
+static int			suspends_pending = 0;
+static int			standbys_pending = 0;
+#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
+static int			waiting_for_resume = 0;
+#endif
+
+static long			clock_cmos_diff;
+static int			got_clock_diff = 0;
+
+static struct wait_queue *	process_list = NULL;
+static struct apm_bios_struct *	user_list = NULL;
+
+static struct timer_list	apm_timer;
+
+static char			driver_version[] = "1.5";	/* no spaces */
+
+#ifdef APM_DEBUG
+static char *	apm_event_name[] = {
+	"system standby",
+	"system suspend",
+	"normal resume",
+	"critical resume",
+	"low battery",
+	"power status change",
+	"update time",
+	"critical suspend",
+	"user standby",
+	"user suspend",
+	"system standby resume",
+	"capabilities change"
+};
+#define NR_APM_EVENT_NAME	\
+		(sizeof(apm_event_name) / sizeof(apm_event_name[0]))
+#endif
+
+static struct file_operations apm_bios_fops = {
+	NULL,		/* lseek */
+	do_read,
+	NULL,		/* write */
+	NULL,		/* readdir */
+	do_poll,
+	do_ioctl,
+	NULL,		/* mmap */
+	do_open,
+	NULL,		/* flush */
+	do_release,
+	NULL,		/* fsync */
+	NULL		/* fasync */
+};
+
+static struct miscdevice apm_device = {
+	APM_MINOR_DEV,
+	"apm",
+	&apm_bios_fops
+};
+
+typedef struct callback_list_t {
+	int (*				callback)(apm_event_t);
+	struct callback_list_t *	next;
+} callback_list_t;
+
+static callback_list_t *	callback_list = NULL;
+
+typedef struct lookup_t {
+	int	key;
+	char *	msg;
+} lookup_t;
+
+static const lookup_t error_table[] = {
+/* N/A	{ APM_SUCCESS,		"Operation succeeded" }, */
+	{ APM_DISABLED,		"Power management disabled" },
+	{ APM_CONNECTED,	"Real mode interface already connected" },
+	{ APM_NOT_CONNECTED,	"Interface not connected" },
+	{ APM_16_CONNECTED,	"16 bit interface already connected" },
+/* N/A	{ APM_16_UNSUPPORTED,	"16 bit interface not supported" }, */
+	{ APM_32_CONNECTED,	"32 bit interface already connected" },
+	{ APM_32_UNSUPPORTED,	"32 bit interface not supported" },
+	{ APM_BAD_DEVICE,	"Unrecognized device ID" },
+	{ APM_BAD_PARAM,	"Parameter out of range" },
+	{ APM_NOT_ENGAGED,	"Interface not engaged" },
+	{ APM_BAD_FUNCTION,     "Function not supported" },
+	{ APM_RESUME_DISABLED,	"Resume timer disabled" },
+	{ APM_BAD_STATE,	"Unable to enter requested state" },
+/* N/A	{ APM_NO_EVENTS,	"No events pending" }, */
+	{ APM_NOT_PRESENT,	"No APM present" }
+};
+#define ERROR_COUNT	(sizeof(error_table)/sizeof(lookup_t))
+
+/*
+ * These are the actual BIOS calls.  Depending on APM_ZERO_SEGS
+ * and APM_NOINTS, we are being really paranoid here!  Not only are
+ * interrupts disabled, but all the segment registers (except SS) are
+ * saved and zeroed this means that if the BIOS tries to reference any
+ * data without explicitly loading the segment registers, the kernel will
+ * fault immediately rather than have some unforeseen circumstances for
+ * the rest of the kernel.  And it will be very obvious!  :-) Doing this
+ * depends on CS referring to the same physical memory as DS so that DS
+ * can be zeroed before the call. Unfortunately, we can't do anything
+ * about the stack segment/pointer.  Also, we tell the compiler that
+ * everything could change.
+ */
+
+static inline int apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in,
+	u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi)
+{
+	unsigned int old_fs, old_gs;
+	int	error;
+
+#ifdef APM_ZERO_SEGS
+	savesegment(fs, old_fs);
+	savesegment(gs, old_gs);
+#endif
+	__asm__ __volatile__(
+		"pushfl\n\t"
+#ifdef APM_NOINTS
+		"cli\n\t"
+#endif
+#ifdef APM_ZERO_SEGS
+		"pushl %%ds\n\t"
+		"pushl %%es\n\t"
+		"movl %w9,%%ds\n\t"
+		"movl %w9,%%es\n\t"
+		"movl %w9,%%fs\n\t"
+		"movl %w9,%%gs\n\t"
+#endif
+		"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
+		"movl $0, %%edi\n\t"
+		"jnc 1f\n\t"
+		"movl $1, %%edi\n"
+		"1:\tpopl %%es\n\t"
+		"popl %%ds\n\t"
+		"popfl\n\t"
+		:  "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx),
+		   "=S" (*esi), "=D" (error)
+		: "a" (eax_in), "b" (ebx_in), "c" (ecx_in)
+#ifdef APM_ZERO_SEGS
+		  , "r" (0)
+#endif
+		: "ax", "bx", "cx", "dx", "si", "di", "bp", "memory");
+#ifdef APM_ZERO_SEGS
+	loadsegment(fs, old_fs);
+	loadsegment(gs, old_gs);
+#endif
+	return error;
+}
+
+/*
+ * This version only returns one value (usually an error code)
+ */
+
+static inline int apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in, u32 *eax)
+{
+	unsigned int old_fs, old_gs;
+	int	error;
+
+#ifdef APM_ZERO_SEGS
+	savesegment(fs, old_fs);
+	savesegment(gs, old_gs);
+#endif
+	__asm__ __volatile__(
+		"pushfl\n\t"
+#ifdef APM_NOINTS
+		"cli\n\t"
+#endif
+#ifdef APM_ZERO_SEGS
+		"pushl %%ds\n\t"
+		"pushl %%es\n\t"
+		"movl %w5,%%ds\n\t"
+		"movl %w5,%%es\n\t"
+		"movl %w5,%%fs\n\t"
+		"movl %w5,%%gs\n\t"
+#endif
+		"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
+		"movl $0, %%edi\n\t"
+		"jnc 1f\n\t"
+		"movl $1, %%edi\n"
+		"1:\tpopl %%es\n\t"
+		"popl %%ds\n\t"
+		"popfl\n\t"
+		: "=a" (*eax), "=D" (error)
+		: "a" (eax_in), "b" (ebx_in), "c" (ecx_in)
+#ifdef APM_ZERO_SEGS
+		  , "r" (0)
+#endif
+		: "ax", "bx", "cx", "dx", "si", "di", "bp", "memory");
+#ifdef APM_ZERO_SEGS
+	loadsegment(fs, old_fs);
+	loadsegment(gs, old_gs);
+#endif
+	return error;
+}
+
+static int apm_driver_version(u_short *val)
+{
+	int	error;
+	u32	eax;
+
+	error = apm_bios_call_simple(0x530e, 0, *val, &eax);
+	if (error)
+		return (eax >> 8) & 0xff;
+	*val = eax;
+	return APM_SUCCESS;
+}
+
+static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info)
+{
+	int	error;
+	u32	eax;
+	u32	ebx;
+	u32	ecx;
+	u32	dummy;
+
+	error = apm_bios_call(0x530b, 0, 0, &eax, &ebx, &ecx, &dummy, &dummy);
+	if (error)
+		return (eax >> 8) & 0xff;
+	*event = ebx;
+	if (apm_bios_info.version < 0x0102)
+		*info = ~0; /* indicate info not valid */
+	else
+		*info = ecx;
+	return APM_SUCCESS;
+}
+
+static int set_power_state(u_short what, u_short state)
+{
+	int	error;
+	u32	eax;
+
+	error = apm_bios_call_simple(0x5307, what, state, &eax);
+	if (error)
+		return (eax >> 8) & 0xff;
+	return APM_SUCCESS;
+}
+
+int apm_set_power_state(u_short state)
+{
+	return set_power_state(0x0001, state);
+}
+
+#ifdef CONFIG_APM_DISPLAY_BLANK
+/* Called by apm_display_blank and apm_display_unblank when apm_enabled. */
+static int apm_set_display_power_state(u_short state)
+{
+	return set_power_state(0x01ff, state);
+}
+#endif
+
+#ifdef CONFIG_APM_DO_ENABLE
+/* Called by apm_setup if apm_enabled will be true. */
+static int apm_enable_power_management(void)
+{
+	int	error;
+	u32	eax;
+
+	error = apm_bios_call_simple(0x5308,
+			(apm_bios_info.version > 0x100) ? 0x0001 : 0xffff, 1, &eax);
+	if (error)
+		return (eax >> 8) & 0xff;
+	return APM_SUCCESS;
+}
+#endif
+
+static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
+{
+	int	error;
+	u32	eax;
+	u32	ebx;
+	u32	ecx;
+	u32	edx;
+	u32	dummy;
+
+	error = apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy);
+	if (error)
+		return (eax >> 8) & 0xff;
+	*status = ebx;
+	*bat = ecx;
+	*life = edx;
+	return APM_SUCCESS;
+}
+
+#if 0
+/* not used anywhere */
+static int apm_get_battery_status(u_short which, 
+				  u_short *bat, u_short *life, u_short *nbat)
+{
+	u_short status;
+	int	error;
+	u32	eax;
+	u32	ebx;
+	u32	ecx;
+	u32	edx;
+	u32	esi;
+
+	if (apm_bios_info.version < 0x0102) {
+		/* pretend we only have one battery. */
+		if (which != 1)
+			return APM_BAD_DEVICE;
+		*nbat = 1;
+		return apm_get_power_status(&status, bat, life);
+	}
+
+	error = apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax, &ebx, &ecx, &edx, &esi);
+	if (error)
+		return (eax >> 8) & 0xff;
+	*bat = ecx;
+	*life = edx;
+	*nbat = esi;
+	return APM_SUCCESS;
+}
+#endif
+
+static int apm_engage_power_management(u_short device)
+{
+	int	error;
+	u32	eax;
+
+	error = apm_bios_call_simple(0x530f, device, 1, &eax);
+	if (error)
+		return (eax >> 8) & 0xff;
+	return APM_SUCCESS;
+}
+
+static void apm_error(char *str, int err)
+{
+	int	i;
+
+	for (i = 0; i < ERROR_COUNT; i++)
+		if (error_table[i].key == err) break;
+	if (i < ERROR_COUNT)
+		printk(KERN_NOTICE "apm_bios: %s: %s\n", str, error_table[i].msg);
+	else
+		printk(KERN_NOTICE "apm_bios: %s: unknown error code %#2.2x\n", str, err);
+}
+
+/* Called from console driver -- must make sure apm_enabled. */
+int apm_display_blank(void)
+{
+#ifdef CONFIG_APM_DISPLAY_BLANK
+	int	error;
+
+	if (!apm_enabled)
+		return 0;
+	error = apm_set_display_power_state(APM_STATE_STANDBY);
+	if (error == APM_SUCCESS)
+		return 1;
+	apm_error("set display standby", error);
+#endif
+	return 0;
+}
+
+/* Called from console driver -- must make sure apm_enabled. */
+int apm_display_unblank(void)
+{
+#ifdef CONFIG_APM_DISPLAY_BLANK
+	int error;
+
+	if (!apm_enabled)
+		return 0;
+	error = apm_set_display_power_state(APM_STATE_READY);
+	if (error == APM_SUCCESS)
+		return 1;
+	apm_error("set display ready", error);
+#endif
+	return 0;
+}
+
+int apm_register_callback(int (*callback)(apm_event_t))
+{
+	callback_list_t *	new;
+
+	new = kmalloc(sizeof(callback_list_t), GFP_KERNEL);
+	if (new == NULL)
+		return -ENOMEM;
+	new->callback = callback;
+	new->next = callback_list;
+	callback_list = new;
+	return 0;
+}
+
+void apm_unregister_callback(int (*callback)(apm_event_t))
+{
+	callback_list_t **	ptr;
+	callback_list_t *	old;
+
+	ptr = &callback_list;
+	for (ptr = &callback_list; *ptr != NULL; ptr = &(*ptr)->next)
+		if ((*ptr)->callback == callback)
+			break;
+	old = *ptr;
+	*ptr = old->next;
+	kfree_s(old, sizeof(callback_list_t));
+}
+
+static int queue_empty(struct apm_bios_struct * as)
+{
+	return as->event_head == as->event_tail;
+}
+
+static apm_event_t get_queued_event(struct apm_bios_struct * as)
+{
+	as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+	return as->events[as->event_tail];
+}
+
+static int queue_event(apm_event_t event, struct apm_bios_struct *sender)
+{
+	struct apm_bios_struct *	as;
+
+	if (user_list == NULL)
+		return 0;
+	for (as = user_list; as != NULL; as = as->next) {
+		if (as == sender)
+			continue;
+		as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
+		if (as->event_head == as->event_tail) {
+			static int notified;
+
+			if (notified == 0) {
+			    printk( "apm_bios: an event queue overflowed\n" );
+			    notified = 1;
+			}
+			as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+		}
+		as->events[as->event_head] = event;
+		if (!as->suser)
+			continue;
+		switch (event) {
+		case APM_SYS_SUSPEND:
+		case APM_USER_SUSPEND:
+			as->suspends_pending++;
+			suspends_pending++;
+			break;
+
+		case APM_SYS_STANDBY:
+		case APM_USER_STANDBY:
+			as->standbys_pending++;
+			standbys_pending++;
+			break;
+		}
+	}
+	wake_up_interruptible(&process_list);
+	return 1;
+}
+
+static void set_time(void)
+{
+	unsigned long	flags;
+
+	if (!got_clock_diff)	/* Don't know time zone, can't set clock */
+		return;
+
+	save_flags(flags);
+	cli();
+	CURRENT_TIME = get_cmos_time() + clock_cmos_diff;
+	restore_flags(flags);
+}
+
+static void suspend(void)
+{
+	unsigned long	flags;
+	int		err;
+
+				/* Estimate time zone so that set_time can
+                                   update the clock */
+	save_flags(flags);
+	clock_cmos_diff = -get_cmos_time();
+	cli();
+	clock_cmos_diff += CURRENT_TIME;
+	got_clock_diff = 1;
+	restore_flags(flags);
+
+	err = apm_set_power_state(APM_STATE_SUSPEND);
+	if (err)
+		apm_error("suspend", err);
+	set_time();
+}
+
+static void standby(void)
+{
+	int	err;
+
+	err = apm_set_power_state(APM_STATE_STANDBY);
+	if (err)
+		apm_error("standby", err);
+}
+
+static apm_event_t get_event(void)
+{
+	int		error;
+	apm_event_t	event;
+	apm_eventinfo_t	info;
+
+	static int notified = 0;
+
+	/* we don't use the eventinfo */
+	error = apm_get_event(&event, &info);
+	if (error == APM_SUCCESS)
+		return event;
+
+	if ((error != APM_NO_EVENTS) && (notified++ == 0))
+		apm_error("get_event", error);
+
+	return 0;
+}
+
+static void send_event(apm_event_t event, apm_event_t undo,
+		       struct apm_bios_struct *sender)
+{
+	callback_list_t *	call;
+	callback_list_t *	fix;
+
+	for (call = callback_list; call != NULL; call = call->next) {
+		if (call->callback(event) && undo) {
+			for (fix = callback_list; fix != call; fix = fix->next)
+				fix->callback(undo);
+			if (apm_bios_info.version > 0x100)
+				apm_set_power_state(APM_STATE_REJECT);
+			return;
+		}
+	}
+
+	queue_event(event, sender);
+}
+
+static void check_events(void)
+{
+	apm_event_t	event;
+
+	while ((event = get_event()) != 0) {
+#ifdef APM_DEBUG
+		if (event <= NR_APM_EVENT_NAME)
+			printk(KERN_DEBUG "APM BIOS received %s notify\n",
+			       apm_event_name[event - 1]);
+		else
+			printk(KERN_DEBUG "APM BIOS received unknown "
+			       "event 0x%02x\n", event);
+#endif
+		switch (event) {
+		case APM_SYS_STANDBY:
+		case APM_USER_STANDBY:
+#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
+			if (waiting_for_resume) {
+			    return;
+			}
+			waiting_for_resume = 1;
+#endif
+			send_event(event, APM_STANDBY_RESUME, NULL);
+			if (standbys_pending <= 0)
+				standby();
+			break;
+
+		case APM_USER_SUSPEND:
+#ifdef CONFIG_APM_IGNORE_USER_SUSPEND
+			if (apm_bios_info.version > 0x100)
+				apm_set_power_state(APM_STATE_REJECT);
+			break;
+#endif
+		case APM_SYS_SUSPEND:
+#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
+			if (waiting_for_resume) {
+			    return;
+			}
+			waiting_for_resume = 1;
+#endif
+			send_event(event, APM_NORMAL_RESUME, NULL);
+			if (suspends_pending <= 0)
+				suspend();
+			break;
+
+		case APM_NORMAL_RESUME:
+		case APM_CRITICAL_RESUME:
+		case APM_STANDBY_RESUME:
+#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
+			waiting_for_resume = 0;
+#endif
+			set_time();
+			send_event(event, 0, NULL);
+			break;
+
+		case APM_LOW_BATTERY:
+		case APM_POWER_STATUS_CHANGE:
+		case APM_CAPABILITY_CHANGE:
+			send_event(event, 0, NULL);
+			break;
+
+		case APM_UPDATE_TIME:
+			set_time();
+			break;
+
+		case APM_CRITICAL_SUSPEND:
+			suspend();
+			break;
+		}
+	}
+}
+
+static void do_apm_timer(unsigned long unused)
+{
+	int	err;
+
+	static int	pending_count = 0;
+
+	if (((standbys_pending > 0) || (suspends_pending > 0))
+	    && (apm_bios_info.version > 0x100)
+	    && (pending_count-- <= 0)) {
+		pending_count = 4;
+
+		err = apm_set_power_state(APM_STATE_BUSY);
+		if (err)
+			apm_error("busy", err);
+	}
+
+	if (!(((standbys_pending > 0) || (suspends_pending > 0))
+	      && (apm_bios_info.version == 0x100)))
+		check_events();
+
+	init_timer(&apm_timer);
+	apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
+	add_timer(&apm_timer);
+}
+
+/* Called from sys_idle, must make sure apm_enabled. */
+int apm_do_idle(void)
+{
+#ifdef CONFIG_APM_CPU_IDLE
+	int	error;
+	u32	dummy;
+
+	if (!apm_enabled)
+		return 0;
+
+	error = apm_bios_call_simple(0x5305, 0, 0, &dummy);
+	if (error)
+		return 0;
+
+	clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
+	return 1;
+#else
+	return 0;
+#endif
+}
+
+/* Called from sys_idle, must make sure apm_enabled. */
+void apm_do_busy(void)
+{
+#ifdef CONFIG_APM_CPU_IDLE
+	u32	dummy;
+
+	if (apm_enabled
+#ifndef ALWAYS_CALL_BUSY
+		&& clock_slowed
+#endif
+	) {
+		(void) apm_bios_call_simple(0x5306, 0, 0, &dummy);
+		clock_slowed = 0;
+	}
+#endif
+}
+
+static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
+{
+	if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
+		printk(KERN_ERR "apm_bios: %s passed bad filp", func);
+		return 1;
+	}
+	return 0;
+}
+
+static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
+{
+	struct apm_bios_struct *	as;
+	int			i;
+	apm_event_t		event;
+	struct wait_queue	wait = { current,	NULL };
+
+	as = fp->private_data;
+	if (check_apm_bios_struct(as, "read"))
+		return -EIO;
+	if (count < sizeof(apm_event_t))
+		return -EINVAL;
+	if (queue_empty(as)) {
+		if (fp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		add_wait_queue(&process_list, &wait);
+repeat:
+		current->state = TASK_INTERRUPTIBLE;
+		if (queue_empty(as) && !signal_pending(current)) {
+			schedule();
+			goto repeat;
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&process_list, &wait);
+	}
+	i = count;
+	while ((i >= sizeof(event)) && !queue_empty(as)) {
+		event = get_queued_event(as);
+		copy_to_user(buf, &event, sizeof(event));
+		switch (event) {
+		case APM_SYS_SUSPEND:
+		case APM_USER_SUSPEND:
+			as->suspends_read++;
+			break;
+
+		case APM_SYS_STANDBY:
+		case APM_USER_STANDBY:
+			as->standbys_read++;
+			break;
+		}
+		buf += sizeof(event);
+		i -= sizeof(event);
+	}
+	if (i < count)
+		return count - i;
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+	return 0;
+}
+
+static unsigned int do_poll(struct file *fp, poll_table * wait)
+{
+	struct apm_bios_struct * as;
+
+	as = fp->private_data;
+	if (check_apm_bios_struct(as, "select"))
+		return 0;
+	poll_wait(fp, &process_list, wait);
+	if (!queue_empty(as))
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int do_ioctl(struct inode * inode, struct file *filp,
+		    u_int cmd, u_long arg)
+{
+	struct apm_bios_struct *	as;
+
+	as = filp->private_data;
+	if (check_apm_bios_struct(as, "ioctl"))
+		return -EIO;
+	if (!as->suser)
+		return -EPERM;
+	switch (cmd) {
+	case APM_IOC_STANDBY:
+		if (as->standbys_read > 0) {
+			as->standbys_read--;
+			as->standbys_pending--;
+			standbys_pending--;
+		}
+		else
+			send_event(APM_USER_STANDBY, APM_STANDBY_RESUME, as);
+		if (standbys_pending <= 0)
+			standby();
+		break;
+	case APM_IOC_SUSPEND:
+		if (as->suspends_read > 0) {
+			as->suspends_read--;
+			as->suspends_pending--;
+			suspends_pending--;
+		}
+		else
+			send_event(APM_USER_SUSPEND, APM_NORMAL_RESUME, as);
+		if (suspends_pending <= 0)
+			suspend();
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int do_release(struct inode * inode, struct file * filp)
+{
+	struct apm_bios_struct *	as;
+
+	as = filp->private_data;
+	filp->private_data = NULL;
+	if (check_apm_bios_struct(as, "release"))
+		return 0;
+	if (as->standbys_pending > 0) {
+		standbys_pending -= as->standbys_pending;
+		if (standbys_pending <= 0)
+			standby();
+	}
+	if (as->suspends_pending > 0) {
+		suspends_pending -= as->suspends_pending;
+		if (suspends_pending <= 0)
+			suspend();
+	}
+	if (user_list == as)
+		user_list = as->next;
+	else {
+		struct apm_bios_struct *	as1;
+
+		for (as1 = user_list;
+		     (as1 != NULL) && (as1->next != as);
+		     as1 = as1->next)
+			;
+		if (as1 == NULL)
+			printk(KERN_ERR "apm_bios: filp not in user list");
+		else
+			as1->next = as->next;
+	}
+	kfree_s(as, sizeof(*as));
+	return 0;
+}
+
+static int do_open(struct inode * inode, struct file * filp)
+{
+	struct apm_bios_struct *	as;
+
+	as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
+	if (as == NULL) {
+		printk(KERN_ERR "apm_bios: cannot allocate struct of size %d bytes",
+		       sizeof(*as));
+		return -ENOMEM;
+	}
+	as->magic = APM_BIOS_MAGIC;
+	as->event_tail = as->event_head = 0;
+	as->suspends_pending = as->standbys_pending = 0;
+	as->suspends_read = as->standbys_read = 0;
+	/*
+	 * XXX - this is a tiny bit broken, when we consider BSD
+         * process accounting. If the device is opened by root, we
+	 * instantly flag that we used superuser privs. Who knows,
+	 * we might close the device immediately without doing a
+	 * privileged operation -- cevans
+	 */
+	as->suser = capable(CAP_SYS_ADMIN);
+	as->next = user_list;
+	user_list = as;
+	filp->private_data = as;
+	return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
+{
+	char *		p;
+	unsigned short	bx;
+	unsigned short	cx;
+	unsigned short	dx;
+	unsigned short	error;
+	unsigned short  ac_line_status = 0xff;
+	unsigned short  battery_status = 0xff;
+	unsigned short  battery_flag   = 0xff;
+	int		percentage     = -1;
+	int             time_units     = -1;
+	char            *units         = "?";
+
+	if (!apm_enabled)
+		return 0;
+	p = buf;
+
+	if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
+		ac_line_status = (bx >> 8) & 0xff;
+		battery_status = bx & 0xff;
+		if ((cx & 0xff) != 0xff)
+			percentage = cx & 0xff;
+
+		if (apm_bios_info.version > 0x100) {
+			battery_flag = (cx >> 8) & 0xff;
+			if (dx != 0xffff) {
+				if ((dx & 0x8000) == 0x8000) {
+					units = "min";
+					time_units = dx & 0x7ffe;
+				} else {
+					units = "sec";
+					time_units = dx & 0x7fff;
+				}
+			}
+		}
+	}
+	/* Arguments, with symbols from linux/apm_bios.h.  Information is
+	   from the Get Power Status (0x0a) call unless otherwise noted.
+
+	   0) Linux driver version (this will change if format changes)
+	   1) APM BIOS Version.  Usually 1.0 or 1.1.
+	   2) APM flags from APM Installation Check (0x00):
+	      bit 0: APM_16_BIT_SUPPORT
+	      bit 1: APM_32_BIT_SUPPORT
+	      bit 2: APM_IDLE_SLOWS_CLOCK
+	      bit 3: APM_BIOS_DISABLED
+	      bit 4: APM_BIOS_DISENGAGED
+	   3) AC line status
+	      0x00: Off-line
+	      0x01: On-line
+	      0x02: On backup power (APM BIOS 1.1 only)
+	      0xff: Unknown
+	   4) Battery status
+	      0x00: High
+	      0x01: Low
+	      0x02: Critical
+	      0x03: Charging
+	      0xff: Unknown
+	   5) Battery flag
+	      bit 0: High
+	      bit 1: Low
+	      bit 2: Critical
+	      bit 3: Charging
+	      bit 7: No system battery
+	      0xff: Unknown
+	   6) Remaining battery life (percentage of charge):
+	      0-100: valid
+	      -1: Unknown
+	   7) Remaining battery life (time units):
+	      Number of remaining minutes or seconds
+	      -1: Unknown
+	   8) min = minutes; sec = seconds */
+
+	p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
+		     driver_version,
+		     (apm_bios_info.version >> 8) & 0xff,
+		     apm_bios_info.version & 0xff,
+		     apm_bios_info.flags,
+		     ac_line_status,
+		     battery_status,
+		     battery_flag,
+		     percentage,
+		     time_units,
+		     units);
+
+	return p - buf;
+}
+#endif
+
+void __init apm_bios_init(void)
+{
+	unsigned short	bx;
+	unsigned short	cx;
+	unsigned short	dx;
+	unsigned short	error;
+	char *		power_stat;
+	char *		bat_stat;
+	static struct proc_dir_entry *ent;
+
+#ifdef __SMP__
+	if (smp_num_cpus > 1) {
+		printk(KERN_NOTICE "APM disabled: APM is not SMP safe.\n");
+		return;
+	}
+#endif
+	if (apm_bios_info.version == 0) {
+		printk(KERN_INFO "APM BIOS not found.\n");
+		return;
+	}
+	printk(KERN_INFO "APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n",
+	       ((apm_bios_info.version >> 8) & 0xff) + '0',
+	       (apm_bios_info.version & 0xff) + '0',
+	       apm_bios_info.flags,
+	       driver_version);
+	if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
+		printk(KERN_INFO "    No 32 bit BIOS support\n");
+		return;
+	}
+
+	/*
+	 * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
+	 * but is reportedly a 1.0 BIOS.
+	 */
+	if (apm_bios_info.version == 0x001)
+		apm_bios_info.version = 0x100;
+
+	/* BIOS < 1.2 doesn't set cseg_16_len */
+	if (apm_bios_info.version < 0x102)
+		apm_bios_info.cseg_16_len = 0xFFFF; /* 64k */
+
+	printk(KERN_INFO "    Entry %x:%lx cseg16 %x dseg %x",
+	       apm_bios_info.cseg, apm_bios_info.offset,
+	       apm_bios_info.cseg_16, apm_bios_info.dseg);
+	if (apm_bios_info.version > 0x100)
+		printk(" cseg len %x, cseg16 len %x, dseg len %x",
+		       apm_bios_info.cseg_len, apm_bios_info.cseg_16_len,
+		       apm_bios_info.dseg_len);
+	printk("\n");
+
+	/*
+	 * Set up a segment that references the real mode segment 0x40
+	 * that extends up to the end of page zero (that we have reserved).
+	 * This is for buggy BIOS's that refer to (real mode) segment 0x40
+	 * even though they are called in protected mode.
+	 */
+	set_base(gdt[APM_40 >> 3],
+		 __va((unsigned long)0x40 << 4));
+	set_limit(gdt[APM_40 >> 3], 4096 - (0x40 << 4));
+
+	apm_bios_entry.offset = apm_bios_info.offset;
+	apm_bios_entry.segment = APM_CS;
+	set_base(gdt[APM_CS >> 3],
+		 __va((unsigned long)apm_bios_info.cseg << 4));
+	set_base(gdt[APM_CS_16 >> 3],
+		 __va((unsigned long)apm_bios_info.cseg_16 << 4));
+	set_base(gdt[APM_DS >> 3],
+		 __va((unsigned long)apm_bios_info.dseg << 4));
+	if (apm_bios_info.version == 0x100) {
+		set_limit(gdt[APM_CS >> 3], 64 * 1024);
+		set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
+		set_limit(gdt[APM_DS >> 3], 64 * 1024);
+	} else {
+#ifdef APM_RELAX_SEGMENTS
+		/* For ASUS motherboard, Award BIOS rev 110 (and others?) */
+		set_limit(gdt[APM_CS >> 3], 64 * 1024);
+		/* For some unknown machine. */
+		set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
+		/* For the DEC Hinote Ultra CT475 (and others?) */
+		set_limit(gdt[APM_DS >> 3], 64 * 1024);
+#else
+		set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
+		set_limit(gdt[APM_CS_16 >> 3], apm_bios_info.cseg_16_len);
+		set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
+#endif
+		/* The APM 1.2 docs state that the apm_driver_version
+		 * call can fail if we try to connect as 1.2 to a 1.1 bios.
+		 */
+		apm_bios_info.version = 0x0102;
+		error = apm_driver_version(&apm_bios_info.version);
+		if (error != APM_SUCCESS) { /* Fall back to an APM 1.1 connection. */
+			apm_bios_info.version = 0x0101;
+			error = apm_driver_version(&apm_bios_info.version);
+		}
+		if (error != APM_SUCCESS) /* Fall back to an APM 1.0 connection. */
+			apm_bios_info.version = 0x100;
+		else {
+			apm_engage_power_management(0x0001);
+			printk( "    Connection version %d.%d\n",
+				(apm_bios_info.version >> 8) & 0xff,
+				apm_bios_info.version & 0xff );
+		}
+	}
+
+	error = apm_get_power_status(&bx, &cx, &dx);
+	if (error)
+		printk(KERN_INFO "    Power status not available\n");
+	else {
+		switch ((bx >> 8) & 0xff) {
+		case 0: power_stat = "off line"; break;
+		case 1: power_stat = "on line"; break;
+		case 2: power_stat = "on backup power"; break;
+		default: power_stat = "unknown"; break;
+		}
+		switch (bx & 0xff) {
+		case 0: bat_stat = "high"; break;
+		case 1: bat_stat = "low"; break;
+		case 2: bat_stat = "critical"; break;
+		case 3: bat_stat = "charging"; break;
+		default: bat_stat = "unknown"; break;
+		}
+		printk(KERN_INFO "    AC %s, battery status %s, battery life ",
+		       power_stat, bat_stat);
+		if ((cx & 0xff) == 0xff)
+			printk("unknown\n");
+		else
+			printk("%d%%\n", cx & 0xff);
+		if (apm_bios_info.version > 0x100) {
+			printk("    battery flag 0x%02x, battery life ",
+			       (cx >> 8) & 0xff);
+			if (dx == 0xffff)
+				printk("unknown\n");
+			else {
+				if ((dx & 0x8000))
+					printk("%d minutes\n", dx & 0x7ffe );
+				else
+					printk("%d seconds\n", dx & 0x7fff );
+			}
+		}
+	}
+
+#ifdef CONFIG_APM_DO_ENABLE
+	/*
+	 * This call causes my NEC UltraLite Versa 33/C to hang if it is
+	 * booted with PM disabled but not in the docking station.
+	 * Unfortunate ...
+	 */
+	error = apm_enable_power_management();
+	if (error)
+		apm_error("enable power management", error);
+	if (error == APM_DISABLED)
+		return;
+#endif
+
+	init_timer(&apm_timer);
+	apm_timer.function = do_apm_timer;
+	apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
+	add_timer(&apm_timer);
+
+#ifdef CONFIG_PROC_FS
+	ent = create_proc_entry("apm", 0, 0);
+	ent->get_info = apm_get_info;
+#endif
+
+	misc_register(&apm_device);
+
+	apm_enabled = 1;
+}
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/desc.h linux/arch/i386/kernel/desc.h
--- v2.1.121/linux/arch/i386/kernel/desc.h	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/kernel/desc.h	Wed Dec 31 16:00:00 1969
@@ -1,61 +0,0 @@
-#ifndef __ARCH_DESC_H
-#define __ARCH_DESC_H
-
-struct desc_struct {
-	unsigned long a,b;
-};
-
-extern struct desc_struct gdt_table[];
-extern struct desc_struct *idt, *gdt;
-
-struct Xgt_desc_struct {
-	unsigned short size;
-	unsigned long address __attribute__((packed));
-};
-
-#define idt_descr (*(struct Xgt_desc_struct *)((char *)&idt - 2))
-#define gdt_descr (*(struct Xgt_desc_struct *)((char *)&gdt - 2))
-
-/*
- * Entry into gdt where to find first TSS. GDT layout:
- *   0 - null
- *   1 - not used
- *   2 - kernel code segment
- *   3 - kernel data segment
- *   4 - user code segment
- *   5 - user data segment
- *   6 - not used
- *   7 - not used
- *   8 - APM BIOS support
- *   9 - APM BIOS support
- *  10 - APM BIOS support
- *  11 - APM BIOS support
- *  12 - TSS #0
- *  13 - LDT #0
- *  14 - TSS #1
- *  15 - LDT #1
- */
-#define FIRST_TSS_ENTRY 12
-#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
-#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
-#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
-#define load_TR(n) __asm__ __volatile__("ltr %%ax": /* no output */ :"a" (_TSS(n)))
-#define load_ldt(n) __asm__ __volatile__("lldt %%ax": /* no output */ :"a" (_LDT(n)))
-#define store_TR(n) \
-__asm__("str %%ax\n\t" \
-	"subl %2,%%eax\n\t" \
-	"shrl $4,%%eax" \
-	:"=a" (n) \
-	:"0" (0),"i" (FIRST_TSS_ENTRY<<3))
-
-extern void set_intr_gate(unsigned int irq, void * addr);
-extern void set_ldt_desc(unsigned int n, void *addr, unsigned int size);
-extern void set_tss_desc(unsigned int n, void *addr);
-
-/*
- * This is the ldt that every process will get unless we need
- * something other than this.
- */
-extern struct desc_struct default_ldt;
-
-#endif
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.1.121/linux/arch/i386/kernel/entry.S	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/kernel/entry.S	Tue Sep 15 09:30:06 1998
@@ -112,7 +112,11 @@
 	jmp 1b;		\
 5:	movl $0,(%esp);	\
 	jmp 2b;		\
-6:	pushl $11;	\
+6:	pushl %ss;	\
+	popl %ds;	\
+	pushl %ss;	\
+	popl %es;	\
+	pushl $11;	\
 	call do_exit;	\
 .previous;		\
 .section __ex_table,"a";\
@@ -184,25 +188,26 @@
 	jne reschedule
 	cmpl $0,sigpending(%ebx)
 	jne signal_return
+restore_all:
 	RESTORE_ALL
+
 	ALIGN
 signal_return:
 	testl $(VM_MASK),EFLAGS(%esp)
-	pushl %esp
+	movl %esp,%eax
 	jne v86_signal_return
-	pushl $0
+	xorl %edx,%edx
 	call SYMBOL_NAME(do_signal)
-	addl $8,%esp
-	RESTORE_ALL
+	jmp restore_all
+
 	ALIGN
 v86_signal_return:
 	call SYMBOL_NAME(save_v86_state)
 	movl %eax,%esp
-	pushl %eax
-	pushl $0
+	xorl %edx,%edx
 	call SYMBOL_NAME(do_signal)
-	addl $8,%esp
-	RESTORE_ALL
+	jmp restore_all
+
 	ALIGN
 tracesys:
 	movl $-ENOSYS,EAX(%esp)
@@ -228,7 +233,7 @@
 	movb CS(%esp),%al
 	testl $(VM_MASK | 3),%eax	# return to VM86 mode or non-supervisor?
 	jne ret_with_reschedule
-	RESTORE_ALL
+	jmp restore_all
 
 	ALIGN
 handle_bottom_half:
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/init_task.c linux/arch/i386/kernel/init_task.c
--- v2.1.121/linux/arch/i386/kernel/init_task.c	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/kernel/init_task.c	Sun Sep 13 12:16:22 1998
@@ -3,8 +3,7 @@
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
-
-#include "desc.h"
+#include <asm/desc.h>
 
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/io_apic.c linux/arch/i386/kernel/io_apic.c
--- v2.1.121/linux/arch/i386/kernel/io_apic.c	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/kernel/io_apic.c	Wed Sep 16 09:05:22 1998
@@ -905,7 +905,7 @@
 	unsigned int t1 = jiffies;
 
 	sti();
-	udelay(10*10000);
+	mdelay(100);
 
 	if (jiffies-t1>1)
 		return 1;
@@ -947,18 +947,37 @@
 }
 
 /*
- * Level triggered interrupts can just be masked.
+ * Starting up a edge-triggered IO-APIC interrupt is
+ * nasty - we need to make sure that we get the edge.
+ * If it is already asserted for some reason, we need
+ * to fake an edge by marking it IRQ_PENDING..
+ *
+ * This is not complete - we should be able to fake
+ * an edge even if it isn't on the 8259A...
  */
-static void enable_level_ioapic_irq(unsigned int irq)
-{
-	unmask_IO_APIC_irq(irq);
-}
 
-static void disable_level_ioapic_irq(unsigned int irq)
+static void startup_edge_ioapic_irq(unsigned int irq)
 {
-	mask_IO_APIC_irq(irq);
+	if (irq < 16) {
+		disable_8259A_irq(irq);
+		if (i8259A_irq_pending(irq))
+			irq_desc[irq].status |= IRQ_PENDING;
+	}
+	enable_edge_ioapic_irq(irq);
 }
 
+#define shutdown_edge_ioapic_irq	disable_edge_ioapic_irq
+
+/*
+ * Level triggered interrupts can just be masked,
+ * and shutting down and starting up the interrupt
+ * is the same as enabling and disabling them.
+ */
+#define startup_level_ioapic_irq	unmask_IO_APIC_irq
+#define shutdown_level_ioapic_irq	mask_IO_APIC_irq
+#define enable_level_ioapic_irq		unmask_IO_APIC_irq
+#define disable_level_ioapic_irq	mask_IO_APIC_irq
+
 static void do_edge_ioapic_IRQ(unsigned int irq, struct pt_regs * regs)
 {
 	irq_desc_t *desc = irq_desc + irq;
@@ -1065,6 +1084,8 @@
 
 static struct hw_interrupt_type ioapic_edge_irq_type = {
 	"IO-APIC-edge",
+	startup_edge_ioapic_irq,
+	shutdown_edge_ioapic_irq,
 	do_edge_ioapic_IRQ,
 	enable_edge_ioapic_irq,
 	disable_edge_ioapic_irq
@@ -1072,6 +1093,8 @@
 
 static struct hw_interrupt_type ioapic_level_irq_type = {
 	"IO-APIC-level",
+	startup_level_ioapic_irq,
+	shutdown_level_ioapic_irq,
 	do_level_ioapic_IRQ,
 	enable_level_ioapic_irq,
 	disable_level_ioapic_irq
@@ -1162,20 +1185,11 @@
  *   directly from the FPU to the old PIC. Linux doesn't
  *   really care, because Linux doesn't want to use IRQ13
  *   anyway (exception 16 is the proper FPU error signal)
- * - IRQ9 is broken on PIIX4 motherboards:
  *
- *		"IRQ9 cannot be re-assigned"
- *
- *		IRQ9 is not available to assign to
- *		ISA add-in cards because it is
- *		dedicated to the power
- *		management function of the PIIX4
- *		controller on the motherboard.
- *		This is true for other motherboards
- *		which use the 82371AB PIIX4
- *		component.
+ * Additionally, something is definitely wrong with irq9
+ * on PIIX4 boards.
  */
-#define PIC_IRQS	((1<<2)|(1<<9)|(1<<13))
+#define PIC_IRQS	((1<<2)|(1<<13))
 
 void __init setup_IO_APIC(void)
 {
@@ -1224,7 +1238,6 @@
 	setup_IO_APIC_irqs();
 	init_IRQ_SMP();
 	check_timer();
- 
+
 	print_IO_APIC();
 }
-
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v2.1.121/linux/arch/i386/kernel/irq.c	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/kernel/irq.c	Tue Sep 15 14:48:26 1998
@@ -37,9 +37,9 @@
 #include <asm/smp.h>
 #include <asm/pgtable.h>
 #include <asm/delay.h>
+#include <asm/desc.h>
 
 #include "irq.h"
-#include "desc.h"
 
 unsigned int local_bh_count[NR_CPUS];
 unsigned int local_irq_count[NR_CPUS];
@@ -84,6 +84,10 @@
 static void enable_8259A_irq(unsigned int irq);
 void disable_8259A_irq(unsigned int irq);
 
+/* startup is the same as "enable", shutdown is same as "disable" */
+#define startup_8259A_irq	enable_8259A_irq
+#define shutdown_8259A_irq	disable_8259A_irq
+
 /*
  * Dummy controller type for unused interrupts
  */
@@ -91,8 +95,14 @@
 static void enable_none(unsigned int irq) { }
 static void disable_none(unsigned int irq) { }
 
+/* startup is the same as "enable", shutdown is same as "disable" */
+#define startup_none	enable_none
+#define shutdown_none	disable_none
+
 static struct hw_interrupt_type no_irq_type = {
 	"none",
+	startup_none,
+	shutdown_none,
 	do_none,
 	enable_none,
 	disable_none
@@ -100,6 +110,8 @@
 
 static struct hw_interrupt_type i8259A_irq_type = {
 	"XT-PIC",
+	startup_8259A_irq,
+	shutdown_8259A_irq,
 	do_8259A_IRQ,
 	enable_8259A_irq,
 	disable_8259A_irq
@@ -683,7 +695,8 @@
 	irq_desc[irq].handler->disable(irq);
 	spin_unlock_irqrestore(&irq_controller_lock, flags);
 
-	synchronize_irq();
+	if (irq_desc[irq].status & IRQ_INPROGRESS)
+		synchronize_irq();
 }
 
 void enable_irq(unsigned int irq)
@@ -699,7 +712,7 @@
 	 * install a handler for this interrupt (make irq autodetection
 	 * work by just looking at the status field for the irq)
 	 */
-	irq_desc[irq].status = 0;
+	irq_desc[irq].status &= ~(IRQ_DISABLED | IRQ_INPROGRESS);
 	irq_desc[irq].handler->enable(irq);
 	spin_unlock_irqrestore(&irq_controller_lock, flags);
 }
@@ -785,22 +798,8 @@
 	*p = new;
 
 	if (!shared) {
-		irq_desc[irq].status = 0;
-#ifdef __SMP__
-		if (IO_APIC_IRQ(irq)) {
-			/*
-			 * If it was on a 8259, disable it there
-			 * and move the "pendingness" onto the
-			 * new irq descriptor.
-			 */
-			if (irq < 16) {
-				disable_8259A_irq(irq);
-				if (i8259A_irq_pending(irq))
-					irq_desc[irq].status = IRQ_PENDING;
-			}
-		}
-#endif
-		irq_desc[irq].handler->enable(irq);
+		irq_desc[irq].status &= ~(IRQ_DISABLED | IRQ_INPROGRESS);
+		irq_desc[irq].handler->startup(irq);
 	}
 	spin_unlock_irqrestore(&irq_controller_lock,flags);
 	return 0;
@@ -857,7 +856,7 @@
 		kfree(action);
 		if (!irq_desc[irq].action) {
 			irq_desc[irq].status |= IRQ_DISABLED;
-			irq_desc[irq].handler->disable(irq);
+			irq_desc[irq].handler->shutdown(irq);
 		}
 		goto out;
 	}
@@ -886,8 +885,8 @@
 	for (i = NR_IRQS-1; i > 0; i--) {
 		if (!irq_desc[i].action) {
 			unsigned int status = irq_desc[i].status | IRQ_AUTODETECT;
-			irq_desc[i].status = status & ~(IRQ_INPROGRESS | IRQ_PENDING);
-			irq_desc[i].handler->enable(i);
+			irq_desc[i].status = status & ~IRQ_INPROGRESS;
+			irq_desc[i].handler->startup(i);
 		}
 	}
 	spin_unlock_irq(&irq_controller_lock);
@@ -911,7 +910,7 @@
 		/* It triggered already - consider it spurious. */
 		if (status & IRQ_INPROGRESS) {
 			irq_desc[i].status = status & ~IRQ_AUTODETECT;
-			irq_desc[i].handler->disable(i);
+			irq_desc[i].handler->shutdown(i);
 		}
 	}
 	spin_unlock_irq(&irq_controller_lock);
@@ -941,7 +940,7 @@
 			nr_irqs++;
 		}
 		irq_desc[i].status = status & ~IRQ_AUTODETECT;
-		irq_desc[i].handler->disable(i);
+		irq_desc[i].handler->shutdown(i);
 	}
 	spin_unlock_irq(&irq_controller_lock);
 
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/irq.h linux/arch/i386/kernel/irq.h
--- v2.1.121/linux/arch/i386/kernel/irq.h	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/kernel/irq.h	Tue Sep 15 12:03:20 1998
@@ -9,6 +9,8 @@
  */
 struct hw_interrupt_type {
 	const char * typename;
+	void (*startup)(unsigned int irq);
+	void (*shutdown)(unsigned int irq);
 	void (*handle)(unsigned int irq, struct pt_regs * regs);
 	void (*enable)(unsigned int irq);
 	void (*disable)(unsigned int irq);
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/ldt.c linux/arch/i386/kernel/ldt.c
--- v2.1.121/linux/arch/i386/kernel/ldt.c	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/kernel/ldt.c	Sun Sep 13 12:16:22 1998
@@ -15,8 +15,7 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/ldt.h>
-
-#include "desc.h"
+#include <asm/desc.h>
 
 static int read_ldt(void * ptr, unsigned long bytecount)
 {
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/mtrr.c linux/arch/i386/kernel/mtrr.c
--- v2.1.121/linux/arch/i386/kernel/mtrr.c	Wed Aug 26 11:37:33 1998
+++ linux/arch/i386/kernel/mtrr.c	Thu Sep 10 08:36:52 1998
@@ -117,6 +117,14 @@
     19980611   Richard Gooch <rgooch@atnf.csiro.au>
 	       Always define <main_lock>.
   v1.22
+    19980901   Richard Gooch <rgooch@atnf.csiro.au>
+	       Removed module support in order to tidy up code.
+	       Added sanity check for <mtrr_add>/<mtrr_del> before <mtrr_init>.
+	       Created addition queue for prior to SMP commence.
+  v1.23
+    19980910   Richard Gooch <rgooch@atnf.csiro.au>
+	       Removed sanity checks and addition queue: Linus prefers an OOPS.
+  v1.24
 */
 #include <linux/types.h>
 #include <linux/errno.h>
@@ -149,7 +157,7 @@
 #include <asm/bitops.h>
 #include <asm/atomic.h>
 
-#define MTRR_VERSION            "1.22 (19980611)"
+#define MTRR_VERSION            "1.24 (19980910)"
 
 #define TRUE  1
 #define FALSE 0
@@ -658,13 +666,8 @@
 }   /*  End Function set_mtrr_smp  */
 
 
-/* A warning that is common to the module and non-module cases. */
 /* Some BIOS's are fucked and don't set all MTRRs the same! */
-#ifdef MODULE
-static void mtrr_state_warn (unsigned long mask)
-#else
 __initfunc(static void mtrr_state_warn (unsigned long mask))
-#endif
 {
     if (!mask) return;
     if (mask & MTRR_CHANGE_MASK_FIXED)
@@ -676,37 +679,6 @@
     printk ("mtrr: probably your BIOS does not setup all CPUs\n");
 }   /*  End Function mtrr_state_warn  */
 
-#ifdef MODULE
-/* As a module, copy the MTRR state using an IPI handler. */
-
-static volatile unsigned long smp_changes_mask = 0;
-
-static void copy_mtrr_state_handler (struct set_mtrr_context *ctxt, void *info)
-{
-    unsigned long mask, count;
-    struct mtrr_state *smp_mtrr_state = info;
-
-    mask = set_mtrr_state (smp_mtrr_state, ctxt);
-    /*  Use the atomic bitops to update the global mask  */
-    for (count = 0; count < sizeof mask * 8; ++count)
-    {
-	if (mask & 0x01) set_bit (count, &smp_changes_mask);
-	mask >>= 1;
-    }
-}   /*  End Function copy_mtrr_state_handler  */
-
-/* Copies the entire MTRR state of this CPU to all the others. */
-static void copy_mtrr_state (void)
-{
-    struct mtrr_state ms;
- 
-    get_mtrr_state (&ms);
-    do_all_cpus (copy_mtrr_state_handler, &ms, FALSE);
-    finalize_mtrr_state (&ms);
-    mtrr_state_warn (smp_changes_mask);
-}   /*  End Function copy_mtrr_state  */
-
-#endif /* MODULE */
 #endif  /*  __SMP__  */
 
 static char *attrib_to_str (int x)
@@ -1163,7 +1135,7 @@
 EXPORT_SYMBOL(mtrr_add);
 EXPORT_SYMBOL(mtrr_del);
 
-#if defined(__SMP__) && !defined(MODULE)
+#ifdef __SMP__
 
 static volatile unsigned long smp_changes_mask __initdata = 0;
 static struct mtrr_state smp_mtrr_state __initdata = {0, 0};
@@ -1196,26 +1168,18 @@
     }
 }   /*  End Function mtrr_init_secondary_cpu  */
 
-#endif
+#endif  /*  __SMP__  */
 
-#ifdef MODULE
-int init_module (void)
-#else
 __initfunc(int mtrr_init(void))
-#endif
 {
     if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return 0;
-#  if !defined(__SMP__) || defined(MODULE) 
+#  ifndef __SMP__
     printk("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION);
 #  endif
 
 #  ifdef __SMP__
-#    ifdef MODULE
-    copy_mtrr_state ();
-#    else /* MODULE */
     finalize_mtrr_state (&smp_mtrr_state);
     mtrr_state_warn (smp_changes_mask);
-#    endif /* MODULE */
 #  endif /* __SMP__ */
 
 #  ifdef CONFIG_PROC_FS
@@ -1224,17 +1188,4 @@
 
     init_table ();
     return 0;
-}
-
-#ifdef	MODULE
-void cleanup_module (void)
-{
-    if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return;
-#  ifdef CONFIG_PROC_FS
-    proc_unregister (&proc_root, PROC_MTRR);
-#  endif
-#    ifdef __SMP__
-    mtrr_hook = NULL;
-#    endif
-}
-#endif
+}   /*  End Function mtrr_init  */
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v2.1.121/linux/arch/i386/kernel/process.c	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/kernel/process.c	Sun Sep 13 12:16:22 1998
@@ -41,12 +41,12 @@
 #include <asm/io.h>
 #include <asm/ldt.h>
 #include <asm/processor.h>
+#include <asm/desc.h>
 #ifdef CONFIG_MATH_EMULATION
 #include <asm/math_emu.h>
 #endif
 
 #include "irq.h"
-#include "desc.h"
 
 spinlock_t semaphore_wake_lock = SPIN_LOCK_UNLOCKED;
 
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v2.1.121/linux/arch/i386/kernel/ptrace.c	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/kernel/ptrace.c	Fri Sep 11 11:11:30 1998
@@ -507,10 +507,10 @@
 			else
 				child->flags &= ~PF_TRACESYS;
 			child->exit_code = data;
-			wake_up_process(child);
 	/* make sure the single step bit is not set. */
 			tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
 			put_stack_long(child, EFL_OFFSET,tmp);
+			wake_up_process(child);
 			ret = 0;
 			goto out;
 		}
@@ -526,11 +526,11 @@
 			ret = 0;
 			if (child->state == TASK_ZOMBIE)	/* already dead */
 				goto out;
-			wake_up_process(child);
 			child->exit_code = SIGKILL;
 	/* make sure the single step bit is not set. */
 			tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
 			put_stack_long(child, EFL_OFFSET, tmp);
+			wake_up_process(child);
 			goto out;
 		}
 
@@ -543,9 +543,9 @@
 			child->flags &= ~PF_TRACESYS;
 			tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG;
 			put_stack_long(child, EFL_OFFSET, tmp);
-			wake_up_process(child);
 			child->exit_code = data;
 	/* give it a chance to run. */
+			wake_up_process(child);
 			ret = 0;
 			goto out;
 		}
@@ -558,16 +558,16 @@
 			if ((unsigned long) data > _NSIG)
 				goto out;
 			child->flags &= ~(PF_PTRACED|PF_TRACESYS);
-			wake_up_process(child);
 			child->exit_code = data;
 			write_lock_irqsave(&tasklist_lock, flags);
 			REMOVE_LINKS(child);
 			child->p_pptr = child->p_opptr;
 			SET_LINKS(child);
 			write_unlock_irqrestore(&tasklist_lock, flags);
-			/* make sure the single step bit is not set. */
+	/* make sure the single step bit is not set. */
 			tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
 			put_stack_long(child, EFL_OFFSET, tmp);
+			wake_up_process(child);
 			ret = 0;
 			goto out;
 		}
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.1.121/linux/arch/i386/kernel/setup.c	Sat Sep  5 16:46:40 1998
+++ linux/arch/i386/kernel/setup.c	Sun Sep 13 09:45:10 1998
@@ -493,6 +493,7 @@
 			switch (edx) {
 				case 0x40:
 					cache_size = 0;
+					break;
 
 				case 0x41:
 					cache_size = 128;
@@ -532,7 +533,7 @@
 			    && (cpu_models[i].x86 == 6) 
 			    && (c->x86_model == 5)
 			    && (c->x86_cache_size == 0)) {
-				p = "Celeron";
+				p = "Celeron (Covington)";
 			}
 		}
 			
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v2.1.121/linux/arch/i386/kernel/signal.c	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/kernel/signal.c	Tue Sep 15 09:26:35 1998
@@ -28,7 +28,7 @@
 
 asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
 			 int options, unsigned long *ru);
-asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs);
+asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset));
 
 /*
  * Atomically swap in the new signal mask, and wait for a signal.
@@ -50,7 +50,7 @@
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(&saveset, regs))
+		if (do_signal(regs, &saveset))
 			return -EINTR;
 	}
 }
@@ -79,7 +79,7 @@
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(&saveset, regs))
+		if (do_signal(regs, &saveset))
 			return -EINTR;
 	}
 }
@@ -597,7 +597,7 @@
  * the kernel can handle, and then we build all the user-level signal handling
  * stack-frames in one go after that.
  */
-asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs)
+asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset)
 {
 	siginfo_t info;
 	struct k_sigaction *ka;
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v2.1.121/linux/arch/i386/kernel/traps.c	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/kernel/traps.c	Tue Sep 15 09:09:58 1998
@@ -27,8 +27,7 @@
 #include <asm/spinlock.h>
 #include <asm/atomic.h>
 #include <asm/debugreg.h>
-
-#include "desc.h"
+#include <asm/desc.h>
 
 asmlinkage int system_call(void);
 asmlinkage void lcall7(void);
diff -u --recursive --new-file v2.1.121/linux/arch/i386/kernel/vm86.c linux/arch/i386/kernel/vm86.c
--- v2.1.121/linux/arch/i386/kernel/vm86.c	Wed Jun 24 22:54:03 1998
+++ linux/arch/i386/kernel/vm86.c	Tue Sep 15 09:33:04 1998
@@ -62,6 +62,7 @@
         ( (unsigned)( & (((struct kernel_vm86_regs *)0)->VM86_REGS_PART2) ) )
 #define VM86_REGS_SIZE2 (sizeof(struct kernel_vm86_regs) - VM86_REGS_SIZE1)
 
+asmlinkage struct pt_regs * FASTCALL(save_v86_state(struct kernel_vm86_regs * regs));
 asmlinkage struct pt_regs * save_v86_state(struct kernel_vm86_regs * regs)
 {
 	struct pt_regs *ret;
diff -u --recursive --new-file v2.1.121/linux/arch/i386/lib/checksum.c linux/arch/i386/lib/checksum.c
--- v2.1.121/linux/arch/i386/lib/checksum.c	Wed Jun 24 22:54:03 1998
+++ linux/arch/i386/lib/checksum.c	Mon Sep 14 22:52:10 1998
@@ -16,6 +16,12 @@
  *
  * Changes:     Ingo Molnar, converted csum_partial_copy() to 2.1 exception
  *			     handling.
+ *		Andi Kleen,  add zeroing on error, fix constraints.
+ *
+ * To fix:
+ *		Convert to pure asm, because this file is too hard
+ *		for gcc's register allocator and it is not clear if the
+ *	 	contraints are correct.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -98,7 +104,7 @@
 7:	    "
 	: "=a"(sum)
 	: "0"(sum), "c"(len), "S"(buff)
-	: "bx", "cx", "dx", "si");
+	: "bx", "dx", "si", "cx", "memory");
 	return(sum);
 }
 
@@ -191,7 +197,7 @@
 80:          "
         : "=a"(sum)
         : "0"(sum), "c"(len), "S"(buf)
-        : "bx", "cx", "dx", "si");
+        : "bx", "dx", "cx", "si", "memory");
         return(sum);
 }
 
@@ -228,6 +234,7 @@
     __u32 tmp_var;
 
     __asm__ __volatile__ ( "
+		movl  %6,%%edi
 		testl $2, %%edi		# Check alignment. 
 		jz 2f			# Jump if alignment is ok.
 		subl $2, %%ecx		# Alignment uses up two bytes.
@@ -318,7 +325,12 @@
 						#
 	movl	%7, (%%ebx)			#
 						#
-# FIXME: do zeroing of rest of the buffer here. #
+# zero the complete destination - computing the rest
+# is too much work 
+	movl	%6, %%edi
+	movl	%9, %%ecx
+	xorl	%%eax,%%eax
+	rep ; stosb
 						#
 	jmp	5000b				#
 						#
@@ -337,9 +349,10 @@
 "
 	: "=a" (sum)
 	: "m" (src_err_ptr), "m" (dst_err_ptr),
-	  "0" (sum), "c" (len), "S" (src), "D" (dst),
-		"i" (-EFAULT), "m"(tmp_var)
-	: "bx", "cx", "dx", "si", "di" );
+	  "0" (sum), "c" (len), "S" (src), "m" (dst),
+		"i" (-EFAULT), "m"(tmp_var),
+		"m" (len)
+	: "bx", "dx", "si", "di", "cx", "memory" );
 
     return(sum);
 }
@@ -360,6 +373,7 @@
 				  int len, int sum, int *src_err_ptr, int *dst_err_ptr)
 {
 	__asm__ __volatile__ ("
+	movl %4,%%ecx
         movl %%ecx, %%edx  
         movl %%ecx, %%ebx  
         shrl $6, %%ecx     
@@ -396,7 +410,11 @@
 7:
 .section .fixup, \"ax\"
 6000:	movl	%7, (%%ebx)
-# FIXME: do zeroing of rest of the buffer here.
+# zero the complete destination (computing the rest is too much work)
+	movl	%8,%%edi
+	movl	%4,%%ecx
+	xorl	%%eax,%%eax
+	rep ; stosb
 	jmp	7b
 6001:	movl    %1, %%ebx	
 	jmp	6000b	
@@ -404,10 +422,12 @@
 	jmp	6000b
 .previous
         "
-	: "=a"(sum), "=m"(src_err_ptr), "=m"(dst_err_ptr)
-        : "0"(sum), "c"(len), "S"(src), "D" (dst),
-		"i" (-EFAULT)
-        : "bx", "cx", "dx", "si", "di" );
+	: "=a"(sum)
+        : "m"(src_err_ptr), "m"(dst_err_ptr), 
+	  "0"(sum), "m"(len), "S"(src), "D" (dst),
+	  "i" (-EFAULT),
+	  "m" (dst)
+        : "bx", "cx", "si", "di", "dx", "memory" );
 	return(sum);
 }
 
diff -u --recursive --new-file v2.1.121/linux/arch/i386/math-emu/fpu_entry.c linux/arch/i386/math-emu/fpu_entry.c
--- v2.1.121/linux/arch/i386/math-emu/fpu_entry.c	Tue Jul 21 00:15:30 1998
+++ linux/arch/i386/math-emu/fpu_entry.c	Sun Sep 13 12:17:48 1998
@@ -27,6 +27,7 @@
 #include <linux/signal.h>
 
 #include <asm/uaccess.h>
+#include <asm/desc.h>
 
 #include "fpu_system.h"
 #include "fpu_emu.h"
diff -u --recursive --new-file v2.1.121/linux/arch/i386/math-emu/get_address.c linux/arch/i386/math-emu/get_address.c
--- v2.1.121/linux/arch/i386/math-emu/get_address.c	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/math-emu/get_address.c	Sun Sep 13 12:18:03 1998
@@ -21,6 +21,7 @@
 #include <linux/stddef.h>
 
 #include <asm/uaccess.h>
+#include <asm/desc.h>
 
 #include "fpu_system.h"
 #include "exception.h"
diff -u --recursive --new-file v2.1.121/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v2.1.121/linux/drivers/block/Config.in	Thu Aug 27 19:56:29 1998
+++ linux/drivers/block/Config.in	Wed Sep 16 13:25:56 1998
@@ -41,6 +41,7 @@
           if [ "$CONFIG_BLK_DEV_IDEDMA" = "y" ]; then
             bool '     Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
             bool '     NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415
+            bool '     VIA82C586 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82C586
           fi
         fi
       fi
diff -u --recursive --new-file v2.1.121/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v2.1.121/linux/drivers/block/Makefile	Thu Aug 27 19:56:29 1998
+++ linux/drivers/block/Makefile	Wed Sep 16 13:25:56 1998
@@ -87,10 +87,10 @@
 endif
 
 ifeq ($(CONFIG_BLK_DEV_LOOP),y)
-L_OBJS += loop.o
+LX_OBJS += loop.o
 else
   ifeq ($(CONFIG_BLK_DEV_LOOP),m)
-  M_OBJS += loop.o
+  MX_OBJS += loop.o
   endif
 endif
 
@@ -152,6 +152,10 @@
 
 ifeq ($(CONFIG_BLK_DEV_NS87415),y)
 IDE_OBJS += ns87415.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_VIA82C586),y)
+IDE_OBJS += via82c586.o
 endif
 
 ifeq ($(CONFIG_BLK_DEV_CMD646),y)
diff -u --recursive --new-file v2.1.121/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v2.1.121/linux/drivers/block/floppy.c	Wed Sep  9 14:51:07 1998
+++ linux/drivers/block/floppy.c	Sun Sep 13 12:12:34 1998
@@ -4115,7 +4115,9 @@
 	for (drive = 0; drive < N_DRIVE; drive++) {
 		CLEARSTRUCT(UDRS);
 		CLEARSTRUCT(UDRWE);
-		UDRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE | FD_DISK_CHANGED;
+		USETF(FD_DISK_NEWCHANGE);
+		USETF(FD_DISK_CHANGED);
+		USETF(FD_VERIFY);
 		UDRS->fd_device = -1;
 		floppy_track_buffer = NULL;
 		max_buffer_sectors = 0;
diff -u --recursive --new-file v2.1.121/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v2.1.121/linux/drivers/block/genhd.c	Wed Aug 26 11:37:35 1998
+++ linux/drivers/block/genhd.c	Wed Sep 16 13:25:56 1998
@@ -16,6 +16,8 @@
  *  More flexible handling of extended partitions - aeb, 950831
  *
  *  Check partition table on IDE disks for common CHS translations
+ *
+ *  Added needed MAJORS for new pairs, {hdi,hdj}, {hdk,hdl}
  */
 
 #include <linux/config.h>
@@ -77,6 +79,10 @@
 	 * This requires special handling here.
 	 */
 	switch (hd->major) {
+		case IDE5_MAJOR:
+			unit += 2;
+		case IDE4_MAJOR:
+			unit += 2;
 		case IDE3_MAJOR:
 			unit += 2;
 		case IDE2_MAJOR:
@@ -420,9 +426,10 @@
 				   && (q->sector & 63) == 1
 				   && (q->end_sector & 63) == 63) {
 					unsigned int heads = q->end_head + 1;
-					if (heads == 32 || heads == 64 ||
-					    heads == 128 || heads == 255 ||
-					    heads == 240) {
+					if (heads == 15 || heads == 16 ||
+					    heads == 32 || heads == 64 ||
+					    heads == 128 || heads == 240 ||
+					    heads == 255) {
 						(void) ide_xlate_1024(dev, heads, " [PTBL]");
 						break;
 					}
diff -u --recursive --new-file v2.1.121/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.1.121/linux/drivers/block/ide-cd.c	Mon Aug  3 12:45:44 1998
+++ linux/drivers/block/ide-cd.c	Wed Sep 16 13:25:56 1998
@@ -1,4 +1,4 @@
-#define VERBOSE_IDE_CD_ERRORS 1
+#define VERBOSE_IDE_CD_ERRORS	1
 /*
  * linux/drivers/block/ide-cd.c
  * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
@@ -218,9 +218,12 @@
  *                         since the .pdf version doesn't seem to work...
  *                     -- Updated the TODO list to something more current.
  *
+ * 4.15  Aug 25, 1998  -- Updated ide-cd.h to respect mechine endianess, 
+ *                         patch thanks to "Eddie C. Dost" <ecd@skynet.be>
+ *
  *************************************************************************/
 
-#define IDECD_VERSION "4.14"
+#define IDECD_VERSION "4.15"
 
 #include <linux/module.h>
 #include <linux/types.h>
diff -u --recursive --new-file v2.1.121/linux/drivers/block/ide-cd.h linux/drivers/block/ide-cd.h
--- v2.1.121/linux/drivers/block/ide-cd.h	Mon Aug  3 12:45:44 1998
+++ linux/drivers/block/ide-cd.h	Wed Sep 16 13:53:27 1998
@@ -6,6 +6,8 @@
  *  Copyright (C) 1996  Erik Andersen
  */
 
+#include <asm/byteorder.h>
+
 /* Turn this on to have the driver print out the meanings of the
    ATAPI error codes.  This will use up additional kernel-space
    memory, though. */
@@ -148,13 +150,29 @@
 
 
 struct atapi_request_sense {
+#if defined(__BIG_ENDIAN_BITFIELD)
+	unsigned char valid      : 1;
+	unsigned char error_code : 7;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
 	unsigned char error_code : 7;
 	unsigned char valid      : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
 	byte reserved1;
+#if defined(__BIG_ENDIAN_BITFIELD)
+	unsigned char reserved3  : 2;
+	unsigned char ili        : 1;
+	unsigned char reserved2  : 1;
+	unsigned char sense_key  : 4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
 	unsigned char sense_key  : 4;
 	unsigned char reserved2  : 1;
 	unsigned char ili        : 1;
 	unsigned char reserved3  : 2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
 	byte info[4];
 	byte sense_len;
 	byte command_info[4];
@@ -190,8 +208,15 @@
 
 struct atapi_toc_entry {
 	byte reserved1;
-	unsigned control : 4;
-	unsigned adr     : 4;
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 adr     : 4;
+	__u8 control : 4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 control : 4;
+	__u8 adr     : 4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
 	byte track;
 	byte reserved2;
 	union {
@@ -218,8 +243,15 @@
  	u_short acdsc_length;
 	u_char  acdsc_format;
 
+#if defined(__BIG_ENDIAN_BITFIELD)
+	u_char  acdsc_ctrl:     4;
+	u_char  acdsc_adr:      4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
 	u_char  acdsc_adr:	4;
 	u_char  acdsc_ctrl:	4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
 	u_char  acdsc_trk;
 	u_char  acdsc_ind;
 	union {
@@ -243,79 +275,182 @@
 
 
 struct atapi_capabilities_page {
-	unsigned page_code           : 6;
-	unsigned reserved1           : 1;
-	unsigned parameters_saveable : 1;
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 parameters_saveable : 1;
+	__u8 reserved1           : 1;
+	__u8 page_code           : 6;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 page_code           : 6;
+	__u8 reserved1           : 1;
+	__u8 parameters_saveable : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
 
 	byte     page_length;
 
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 reserved2           : 5;
+	/* Drive supports reading CD-R discs with addressing method 2 */
+	__u8 method2             : 1; /* reserved in 1.2 */
+	/* Drive can read from CD-R/W (CD-E) discs (orange book, part III) */
+	__u8 cd_rw_read           : 1; /* reserved in 1.2 */
 	/* Drive supports read from CD-R discs (orange book, part II) */
-        unsigned cd_r_read           : 1; /* reserved in 1.2 */
-	/* Drive supports read from CD-R/W (CD-E) discs (orange book, part III) */
-        unsigned cd_rw_read           : 1; /* reserved in 1.2 */
+	__u8 cd_r_read           : 1; /* reserved in 1.2 */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	/* Drive supports read from CD-R discs (orange book, part II) */
+        __u8 cd_r_read           : 1; /* reserved in 1.2 */
+	/* Drive can read from CD-R/W (CD-E) discs (orange book, part III) */
+        __u8 cd_rw_read          : 1; /* reserved in 1.2 */
 	/* Drive supports reading CD-R discs with addressing method 2 */
-	unsigned method2             : 1; /* reserved in 1.2 */
-	unsigned reserved2           : 5;
+	__u8 method2             : 1; /* reserved in 1.2 */
+	__u8 reserved2           : 5;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
 
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 reserved3           : 6;
+	/* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */
+	__u8 cd_rw_write          : 1; /* reserved in 1.2 */
 	/* Drive supports write to CD-R discs (orange book, part II) */
-        unsigned cd_r_write          : 1; /* reserved in 1.2 */
-	/* Drive supports write to CD-R/W (CD-E) discs (orange book, part III) */
-        unsigned cd_rw_write          : 1; /* reserved in 1.2 */
-	unsigned reserved3           : 6;
+	__u8 cd_r_write          : 1; /* reserved in 1.2 */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+
+	/* Drive can write to CD-R discs (orange book, part II) */
+        __u8 cd_r_write          : 1; /* reserved in 1.2 */
+	/* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */
+        __u8 cd_rw_write          : 1; /* reserved in 1.2 */
+	__u8 reserved3           : 6;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
 
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 reserved4           : 1;
+	/* Drive can read multisession discs. */
+	__u8 multisession        : 1;
+	/* Drive can read mode 2, form 2 data. */
+	__u8 mode2_form2         : 1;
+	/* Drive can read mode 2, form 1 (XA) data. */
+	__u8 mode2_form1         : 1;
+	/* Drive supports digital output on port 2. */
+	__u8 digport2            : 1;
+	/* Drive supports digital output on port 1. */
+	__u8 digport1            : 1;
+	/* Drive can deliver a composite audio/video data stream. */
+	__u8 composite           : 1;
+	/* Drive supports audio play operations. */
+	__u8 audio_play          : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
 	/* Drive supports audio play operations. */
-	unsigned audio_play          : 1;
+	__u8 audio_play          : 1;
 	/* Drive can deliver a composite audio/video data stream. */
-	unsigned composite           : 1;
+	__u8 composite           : 1;
 	/* Drive supports digital output on port 1. */
-	unsigned digport1            : 1;
+	__u8 digport1            : 1;
 	/* Drive supports digital output on port 2. */
-	unsigned digport2            : 1;
+	__u8 digport2            : 1;
 	/* Drive can read mode 2, form 1 (XA) data. */
-	unsigned mode2_form1         : 1;
+	__u8 mode2_form1         : 1;
 	/* Drive can read mode 2, form 2 data. */
-	unsigned mode2_form2         : 1;
+	__u8 mode2_form2         : 1;
 	/* Drive can read multisession discs. */
-	unsigned multisession        : 1;
-	unsigned reserved4           : 1;
+	__u8 multisession        : 1;
+	__u8 reserved4           : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
 
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 reserved5           : 1;
+	/* Drive can return Media Catalog Number (UPC) info. */
+	__u8 upc                 : 1;
+	/* Drive can return International Standard Recording Code info. */
+	__u8 isrc                : 1;
+	/* Drive supports C2 error pointers. */
+	__u8 c2_pointers         : 1;
+	/* R-W data will be returned deinterleaved and error corrected. */
+	__u8 rw_corr             : 1;
+	/* Subchannel reads can return combined R-W information. */
+	__u8 rw_supported        : 1;
+	/* Drive can continue a read cdda operation from a loss of streaming.*/
+	__u8 cdda_accurate       : 1;
+	/* Drive can read Red Book audio data. */
+	__u8 cdda                : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
 	/* Drive can read Red Book audio data. */
-	unsigned cdda                : 1;
+	__u8 cdda                : 1;
 	/* Drive can continue a read cdda operation from a loss of streaming.*/
-	unsigned cdda_accurate       : 1;
+	__u8 cdda_accurate       : 1;
 	/* Subchannel reads can return combined R-W information. */
-	unsigned rw_supported        : 1;
+	__u8 rw_supported        : 1;
 	/* R-W data will be returned deinterleaved and error corrected. */
-	unsigned rw_corr             : 1;
+	__u8 rw_corr             : 1;
 	/* Drive supports C2 error pointers. */
-	unsigned c2_pointers         : 1;
+	__u8 c2_pointers         : 1;
 	/* Drive can return International Standard Recording Code info. */
-	unsigned isrc                : 1;
+	__u8 isrc                : 1;
 	/* Drive can return Media Catalog Number (UPC) info. */
-	unsigned upc                 : 1;
-	unsigned reserved5           : 1;
+	__u8 upc                 : 1;
+	__u8 reserved5           : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
 
+#if defined(__BIG_ENDIAN_BITFIELD)
+	/* Drive mechanism types. */
+	mechtype_t mechtype          : 3;
+	__u8 reserved6           : 1;
+	/* Drive can eject a disc or changer cartridge. */
+	__u8 eject               : 1;
+	/* State of prevent/allow jumper. */
+	__u8 prevent_jumper      : 1;
+	/* Present state of door lock. */
+	__u8 lock_state          : 1;
 	/* Drive can lock the door. */
-	unsigned lock                : 1;
+	__u8 lock                : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+
+	/* Drive can lock the door. */
+	__u8 lock                : 1;
 	/* Present state of door lock. */
-	unsigned lock_state          : 1;
+	__u8 lock_state          : 1;
 	/* State of prevent/allow jumper. */
-	unsigned prevent_jumper      : 1;
+	__u8 prevent_jumper      : 1;
 	/* Drive can eject a disc or changer cartridge. */
-	unsigned eject               : 1;
-	unsigned reserved6           : 1;
+	__u8 eject               : 1;
+	__u8 reserved6           : 1;
 	/* Drive mechanism types. */
 	mechtype_t mechtype          : 3;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 reserved7           : 4;
+	/* Drive supports software slot selection. */
+	__u8 sss                 : 1;  /* reserved in 1.2 */
+	/* Changer can report exact contents of slots. */
+	__u8 disc_present        : 1;  /* reserved in 1.2 */
+	/* Audio for each channel can be muted independently. */
+	__u8 separate_mute       : 1;
+	/* Audio level for each channel can be controlled independently. */
+	__u8 separate_volume     : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
 
 	/* Audio level for each channel can be controlled independently. */
-	unsigned separate_volume     : 1;
+	__u8 separate_volume     : 1;
 	/* Audio for each channel can be muted independently. */
-	unsigned separate_mute       : 1;
+	__u8 separate_mute       : 1;
 	/* Changer can report exact contents of slots. */
-	unsigned disc_present        : 1;  /* reserved in 1.2 */
+	__u8 disc_present        : 1;  /* reserved in 1.2 */
 	/* Drive supports software slot selection. */
-	unsigned sss                 : 1;  /* reserved in 1.2 */
-	unsigned reserved7           : 4;
+	__u8 sss                 : 1;  /* reserved in 1.2 */
+	__u8 reserved7           : 4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
 
 	/* Note: the following four fields are returned in big-endian form. */
 	/* Maximum speed (in kB/s). */
@@ -333,23 +468,46 @@
 
 
 struct atapi_mechstat_header {
-	unsigned curslot       : 5;
-	unsigned changer_state : 2;
-	unsigned fault         : 1;
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 fault         : 1;
+	__u8 changer_state : 2;
+	__u8 curslot       : 5;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 curslot       : 5;
+	__u8 changer_state : 2;
+	__u8 fault         : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
 
-	unsigned reserved1     : 5;
-	unsigned mech_state    : 3;
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 mech_state    : 3;
+	__u8 reserved1     : 5;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 reserved1     : 5;
+	__u8 mech_state    : 3;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
 
 	byte     curlba[3];
 	byte     nslots;
-	unsigned short slot_tablelen;
+	__u8 short slot_tablelen;
 };
 
 
 struct atapi_slot {
-	unsigned change       : 1;
-	unsigned reserved1    : 6;
-	unsigned disc_present : 1;
+#if defined(__BIG_ENDIAN_BITFIELD)
+	__u8 disc_present : 1;
+	__u8 reserved1    : 6;
+	__u8 change       : 1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 change       : 1;
+	__u8 reserved1    : 6;
+	__u8 disc_present : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
 
 	byte reserved2[3];
 };
diff -u --recursive --new-file v2.1.121/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- v2.1.121/linux/drivers/block/ide-disk.c	Wed May 20 19:10:37 1998
+++ linux/drivers/block/ide-disk.c	Wed Sep 16 13:25:56 1998
@@ -19,6 +19,7 @@
  * Version 1.05		add capacity support for ATA3 >= 8GB
  * Version 1.06		get boot-up messages to show full cyl count
  * Version 1.07		disable door-locking if it fails
+ * Version 1.07a	fixed mult_count enables
  */
 
 #define IDEDISK_VERSION	"1.07"
@@ -724,20 +725,24 @@
 	 drive->name, id->model, idedisk_capacity(drive)/2048L, id->buf_size/2,
 	 drive->bios_cyl, drive->bios_head, drive->bios_sect);
 	if (drive->using_dma) {
-		if ((id->field_valid & 4) && (id->dma_ultra & (id->dma_ultra >> 8) & 7))
-			printk(", UDMA");
-		else
+		if ((id->field_valid & 4) &&
+		    (id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
+			printk(", UDMA");	/* UDMA BIOS-enabled! */
+		} else if (id->field_valid & 4) {
+			printk(", (U)DMA");	/* Can be BIOS-enabled! */
+		} else {
 			printk(", DMA");
+		}
 	}
 	printk("\n");
 	drive->mult_count = 0;
 	if (id->max_multsect) {
-		drive->mult_req = INITIAL_MULT_COUNT;
-		if (drive->mult_req > id->max_multsect)
-			drive->mult_req = id->max_multsect;
-		if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
-			drive->special.b.set_multmode = 1;
+		id->multsect = ((id->max_multsect/2) > 1) ? id->max_multsect : 0;
+		id->multsect_valid = id->multsect ? 1 : 0;
+		drive->mult_req = id->multsect_valid ? id->max_multsect : INITIAL_MULT_COUNT;
+		drive->special.b.set_multmode = drive->mult_req ? 1 : 0;
 	}
+	drive->no_io_32bit = id->dword_io ? 1 : 0;
 }
 
 int idedisk_init (void)
diff -u --recursive --new-file v2.1.121/linux/drivers/block/ide-dma.c linux/drivers/block/ide-dma.c
--- v2.1.121/linux/drivers/block/ide-dma.c	Mon Aug  3 12:45:44 1998
+++ linux/drivers/block/ide-dma.c	Wed Sep 16 13:25:56 1998
@@ -62,6 +62,13 @@
  * for supplying a Promise UDMA board & WD UDMA drive for this work!
  *
  * And, yes, Intel Zappa boards really *do* use both PIIX IDE ports.
+ *
+ * ACARD ATP850UF Chipset "Modified SCSI Class" with other names
+ *       AEC6210 U/UF
+ *       SIIG's UltraIDE Pro CN-2449
+ * TTI   HPT343 Chipset "Modified SCSI Class" but reports as an
+ *       unknown storage device.
+ * NEW	check_drive_lists(ide_drive_t *drive, int good_bad)
  */
 #include <linux/config.h>
 #include <linux/types.h>
@@ -85,9 +92,19 @@
 const char *good_dma_drives[] = {"Micropolis 2112A",
 				 "CONNER CTMA 4000",
 				 "ST34342A",	/* for Sun Ultra */
+				 "WDC AC2340F",	/* DMA mode1 */
+				 "WDC AC2340H",	/* DMA mode1 */
 				 NULL};
 
 /*
+ * bad_dma_drives() lists the model names (from "hdparm -i")
+ * of drives which supposedly support (U)DMA but which are
+ * known to corrupt data with this interface under Linux.
+ */
+const char *bad_dma_drives[] = {"WDC AC22100H",
+ 				NULL};
+
+/*
  * Our Physical Region Descriptor (PRD) table should be large enough
  * to handle the biggest I/O request we are likely to see.  Since requests
  * can have no more than 256 sectors, and since the typical blocksize is
@@ -204,27 +221,57 @@
 	return count;
 }
 
-static int config_drive_for_dma (ide_drive_t *drive)
+/*
+ *  For both Blacklisted and Whitelisted drives.
+ *  This is setup to be called as an extern for future support
+ *  to other special driver code.
+ */
+int check_drive_lists (ide_drive_t *drive, int good_bad)
 {
 	const char **list;
 	struct hd_driveid *id = drive->id;
+
+	if (good_bad) {
+		/* Consult the list of known "good" drives */
+		list = good_dma_drives;
+		while (*list) {
+			if (!strcmp(*list++,id->model))
+				return 1;
+		}
+	} else {
+		/* Consult the list of known "bad" drives */
+		list = bad_dma_drives;
+		while (*list) {
+			if (!strcmp(*list++,id->model)) {
+				printk("%s: (U)DMA capability is broken for %s\n",
+					drive->name, id->model);
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+static int config_drive_for_dma (ide_drive_t *drive)
+{
+	struct hd_driveid *id = drive->id;
 	ide_hwif_t *hwif = HWIF(drive);
 
 	if (id && (id->capability & 1) && hwif->autodma) {
+		/* Consult the list of known "bad" drives */
+		if (check_drive_lists(drive, BAD_DMA_DRIVE))
+			return hwif->dmaproc(ide_dma_off, drive);
 		/* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
 		if (id->field_valid & 4)	/* UltraDMA */
 			if  ((id->dma_ultra & (id->dma_ultra >> 8) & 7))
 				return hwif->dmaproc(ide_dma_on, drive);
 		/* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
 		if (id->field_valid & 2)	/* regular DMA */
-			if  ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404)
+			if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404)
 				return hwif->dmaproc(ide_dma_on, drive);
 		/* Consult the list of known "good" drives */
-		list = good_dma_drives;
-		while (*list) {
-			if (!strcmp(*list++,id->model))
-				return hwif->dmaproc(ide_dma_on, drive);
-		}
+		if (check_drive_lists(drive, GOOD_DMA_DRIVE))
+			return hwif->dmaproc(ide_dma_on, drive);
 	}
 	return hwif->dmaproc(ide_dma_off_quietly, drive);
 }
@@ -354,7 +401,7 @@
 		}
 	}
 	if (dma_base) {
-		if (extra) /* PDC20246 */
+		if (extra) /* PDC20246 & HPT343 */
 			request_region(dma_base+16, extra, name);
 		dma_base += hwif->channel ? 8 : 0;
 		if (inb(dma_base+2) & 0x80) {
diff -u --recursive --new-file v2.1.121/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c
--- v2.1.121/linux/drivers/block/ide-floppy.c	Tue Jul 28 14:21:07 1998
+++ linux/drivers/block/ide-floppy.c	Wed Sep 16 13:25:56 1998
@@ -121,10 +121,19 @@
  *	Removable Block Access Capabilities Page
  */
 typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 	unsigned	page_code	:6;	/* Page code - Should be 0x1b */
 	unsigned	reserved1_6	:1;	/* Reserved */
 	unsigned	ps		:1;	/* Should be 0 */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned	ps		:1;	/* Should be 0 */
+	unsigned	reserved1_6	:1;	/* Reserved */
+	unsigned	page_code	:6;	/* Page code - Should be 0x1b */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 	u8		page_length;		/* Page Length - Should be 0xa */
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 	unsigned	reserved2	:6;
 	unsigned	srfp		:1;	/* Supports reporting progress of format */
 	unsigned	sflp		:1;	/* System floppy type device */
@@ -132,6 +141,17 @@
 	unsigned	reserved3	:3;
 	unsigned	sml		:1;	/* Single / Multiple lun supported */
 	unsigned	ncd		:1;	/* Non cd optical device */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned	sflp		:1;	/* System floppy type device */
+	unsigned	srfp		:1;	/* Supports reporting progress of format */
+	unsigned	reserved2	:6;
+	unsigned	ncd		:1;	/* Non cd optical device */
+	unsigned	sml		:1;	/* Single / Multiple lun supported */
+	unsigned	reserved3	:3;
+	unsigned	tlun		:3;	/* Total logical units supported by the device */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 	u8		reserved[8];
 } idefloppy_capabilities_page_t;
 
@@ -139,9 +159,17 @@
  *	Flexible disk page.
  */
 typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 	unsigned	page_code	:6;	/* Page code - Should be 0x5 */
 	unsigned	reserved1_6	:1;	/* Reserved */
 	unsigned	ps		:1;	/* The device is capable of saving the page */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned	ps		:1;	/* The device is capable of saving the page */
+	unsigned	reserved1_6	:1;	/* Reserved */
+	unsigned	page_code	:6;	/* Page code - Should be 0x5 */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 	u8		page_length;		/* Page Length - Should be 0x1e */
 	u16		transfer_rate;		/* In kilobits per second */
 	u8		heads, sectors;		/* Number of heads, Number of sectors per track */
@@ -164,8 +192,15 @@
 
 typedef struct {
 	u32		blocks;			/* Number of blocks */
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 	unsigned	dc		:2;	/* Descriptor Code */
 	unsigned	reserved	:6;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned	reserved	:6;
+	unsigned	dc		:2;	/* Descriptor Code */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 	u8		length_msb;		/* Block Length (MSB)*/
 	u16		length;			/* Block Length */
 } idefloppy_capacity_descriptor_t;
@@ -271,6 +306,7 @@
 typedef union {
 	unsigned all			:8;
 	struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 		unsigned check		:1;	/* Error occurred */
 		unsigned idx		:1;	/* Reserved */
 		unsigned corr		:1;	/* Correctable error occurred */
@@ -279,6 +315,18 @@
 		unsigned reserved5	:1;	/* Reserved */
 		unsigned drdy		:1;	/* Ignored for ATAPI commands (ready to accept ATA command) */
 		unsigned bsy		:1;	/* The device has access to the command block */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+		unsigned bsy		:1;	/* The device has access to the command block */
+		unsigned drdy		:1;	/* Ignored for ATAPI commands (ready to accept ATA command) */
+		unsigned reserved5	:1;	/* Reserved */
+		unsigned dsc		:1;	/* Media access command finished */
+		unsigned drq		:1;	/* Data is request by the device */
+		unsigned corr		:1;	/* Correctable error occurred */
+		unsigned idx		:1;	/* Reserved */
+		unsigned check		:1;	/* Error occurred */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 	} b;
 } idefloppy_status_reg_t;
 
@@ -288,11 +336,21 @@
 typedef union {
 	unsigned all			:8;
 	struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 		unsigned ili		:1;	/* Illegal Length Indication */
 		unsigned eom		:1;	/* End Of Media Detected */
 		unsigned abrt		:1;	/* Aborted command - As defined by ATA */
 		unsigned mcr		:1;	/* Media Change Requested - As defined by ATA */
 		unsigned sense_key	:4;	/* Sense key of the last failed packet command */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+		unsigned sense_key	:4;	/* Sense key of the last failed packet command */
+		unsigned mcr		:1;	/* Media Change Requested - As defined by ATA */
+		unsigned abrt		:1;	/* Aborted command - As defined by ATA */
+		unsigned eom		:1;	/* End Of Media Detected */
+		unsigned ili		:1;	/* Illegal Length Indication */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 	} b;
 } idefloppy_error_reg_t;
 
@@ -302,10 +360,19 @@
 typedef union {
 	unsigned all			:8;
 	struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 		unsigned dma		:1;	/* Using DMA or PIO */
 		unsigned reserved321	:3;	/* Reserved */
 		unsigned reserved654	:3;	/* Reserved (Tag Type) */
 		unsigned reserved7	:1;	/* Reserved */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+		unsigned reserved7	:1;	/* Reserved */
+		unsigned reserved654	:3;	/* Reserved (Tag Type) */
+		unsigned reserved321	:3;	/* Reserved */
+		unsigned dma		:1;	/* Using DMA or PIO */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 	} b;
 } idefloppy_feature_reg_t;
 
@@ -315,8 +382,15 @@
 typedef union {
 	unsigned all			:16;
 	struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 		unsigned low		:8;	/* LSB */
 		unsigned high		:8;	/* MSB */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+		unsigned high		:8;	/* MSB */
+		unsigned low		:8;	/* LSB */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 	} b;
 } idefloppy_bcount_reg_t;
 
@@ -326,9 +400,17 @@
 typedef union {
 	unsigned all			:8;
 	struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 		unsigned cod		:1;	/* Information transferred is command (1) or data (0) */
 		unsigned io		:1;	/* The device requests us to read (1) or write (0) */
 		unsigned reserved	:6;	/* Reserved */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+		unsigned reserved	:6;	/* Reserved */
+		unsigned io		:1;	/* The device requests us to read (1) or write (0) */
+		unsigned cod		:1;	/* Information transferred is command (1) or data (0) */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 	} b;
 } idefloppy_ireason_reg_t;
 
@@ -338,12 +420,23 @@
 typedef union {	
 	unsigned all			:8;
 	struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 		unsigned sam_lun	:3;	/* Logical unit number */
 		unsigned reserved3	:1;	/* Reserved */
 		unsigned drv		:1;	/* The responding drive will be drive 0 (0) or drive 1 (1) */
 		unsigned one5		:1;	/* Should be set to 1 */
 		unsigned reserved6	:1;	/* Reserved */
 		unsigned one7		:1;	/* Should be set to 1 */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+		unsigned one7		:1;	/* Should be set to 1 */
+		unsigned reserved6	:1;	/* Reserved */
+		unsigned one5		:1;	/* Should be set to 1 */
+		unsigned drv		:1;	/* The responding drive will be drive 0 (0) or drive 1 (1) */
+		unsigned reserved3	:1;	/* Reserved */
+		unsigned sam_lun	:3;	/* Logical unit number */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 	} b;
 } idefloppy_drivesel_reg_t;
 
@@ -353,11 +446,21 @@
 typedef union {			
 	unsigned all			:8;
 	struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 		unsigned zero0		:1;	/* Should be set to zero */
 		unsigned nien		:1;	/* Device interrupt is disabled (1) or enabled (0) */
 		unsigned srst		:1;	/* ATA software reset. ATAPI devices should use the new ATAPI srst. */
 		unsigned one3		:1;	/* Should be set to 1 */
 		unsigned reserved4567	:4;	/* Reserved */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+		unsigned reserved4567	:4;	/* Reserved */
+		unsigned one3		:1;	/* Should be set to 1 */
+		unsigned srst		:1;	/* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+		unsigned nien		:1;	/* Device interrupt is disabled (1) or enabled (0) */
+		unsigned zero0		:1;	/* Should be set to zero */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 	} b;
 } idefloppy_control_reg_t;
 
@@ -366,6 +469,7 @@
  *	the ATAPI IDENTIFY DEVICE command.
  */
 struct idefloppy_id_gcw {	
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 	unsigned packet_size		:2;	/* Packet Size */
 	unsigned reserved234		:3;	/* Reserved */
 	unsigned drq_type		:2;	/* Command packet DRQ type */
@@ -373,12 +477,24 @@
 	unsigned device_type		:5;	/* Device type */
 	unsigned reserved13		:1;	/* Reserved */
 	unsigned protocol		:2;	/* Protocol type */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned protocol		:2;	/* Protocol type */
+	unsigned reserved13		:1;	/* Reserved */
+	unsigned device_type		:5;	/* Device type */
+	unsigned removable		:1;	/* Removable media */
+	unsigned drq_type		:2;	/* Command packet DRQ type */
+	unsigned reserved234		:3;	/* Reserved */
+	unsigned packet_size		:2;	/* Packet Size */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 };
 
 /*
  *	INQUIRY packet command - Data Format
  */
 typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 	unsigned	device_type	:5;	/* Peripheral Device Type */
 	unsigned	reserved0_765	:3;	/* Peripheral Qualifier - Reserved */
 	unsigned	reserved1_6t0	:7;	/* Reserved */
@@ -390,6 +506,21 @@
 	unsigned	reserved3_45	:2;	/* Reserved */
 	unsigned	reserved3_6	:1;	/* TrmIOP - Reserved */
 	unsigned	reserved3_7	:1;	/* AENC - Reserved */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned	reserved0_765	:3;	/* Peripheral Qualifier - Reserved */
+	unsigned	device_type	:5;	/* Peripheral Device Type */
+	unsigned	rmb		:1;	/* Removable Medium Bit */
+	unsigned	reserved1_6t0	:7;	/* Reserved */
+	unsigned	iso_version	:2;	/* ISO Version */
+	unsigned	ecma_version	:3;	/* ECMA Version */
+	unsigned	ansi_version	:3;	/* ANSI Version */
+	unsigned	reserved3_7	:1;	/* AENC - Reserved */
+	unsigned	reserved3_6	:1;	/* TrmIOP - Reserved */
+	unsigned	reserved3_45	:2;	/* Reserved */
+	unsigned	response_format :4;	/* Response Data Format */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 	u8		additional_length;	/* Additional Length (total_length-4) */
 	u8		rsv5, rsv6, rsv7;	/* Reserved */
 	u8		vendor_id[8];		/* Vendor Identification */
@@ -404,6 +535,7 @@
  *	REQUEST SENSE packet command result - Data Format.
  */
 typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 	unsigned	error_code	:7;	/* Current error (0x70) */
 	unsigned	valid		:1;	/* The information field conforms to SFF-8070i */
 	u8		reserved1	:8;	/* Reserved */
@@ -411,6 +543,17 @@
 	unsigned	reserved2_4	:1;	/* Reserved */
 	unsigned	ili		:1;	/* Incorrect Length Indicator */
 	unsigned	reserved2_67	:2;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned	valid		:1;	/* The information field conforms to SFF-8070i */
+	unsigned	error_code	:7;	/* Current error (0x70) */
+	u8		reserved1	:8;	/* Reserved */
+	unsigned	reserved2_67	:2;
+	unsigned	ili		:1;	/* Incorrect Length Indicator */
+	unsigned	reserved2_4	:1;	/* Reserved */
+	unsigned	sense_key	:4;	/* Sense Key */
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 	u32		information __attribute__ ((packed));
 	u8		asl;			/* Additional sense length (n-7) */
 	u32		command_specific;	/* Additional command specific information */
@@ -433,8 +576,15 @@
 typedef struct {
 	u16		mode_data_length;	/* Length of the following data transfer */
 	u8		medium_type;		/* Medium Type */
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 	unsigned	reserved3	:7;
 	unsigned	wp		:1;	/* Write protect */
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	unsigned	wp		:1;	/* Write protect */
+	unsigned	reserved3	:7;
+#else
+#error "Bitfield endianness not defined! Check your byteorder.h"
+#endif
 	u8		reserved[4];
 } idefloppy_mode_parameter_header_t;
 
@@ -1371,7 +1521,8 @@
 	if (gcw.drq_type == 1)
 		set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags);
 	if (strcmp(drive->id->model, "IOMEGA ZIP 100 ATAPI") == 0 &&
-	    strcmp(drive->id->fw_rev, "21.D") == 0) {
+	    ((strcmp(drive->id->fw_rev, "21.D") == 0) ||
+	     (strcmp(drive->id->fw_rev, "23.D") == 0))) {
 		for (i = 0; i < 1 << PARTN_BITS; i++)
 			max_sectors[major][minor + i] = 64;
 	}
diff -u --recursive --new-file v2.1.121/linux/drivers/block/ide-pci.c linux/drivers/block/ide-pci.c
--- v2.1.121/linux/drivers/block/ide-pci.c	Thu Aug  6 14:06:31 1998
+++ linux/drivers/block/ide-pci.c	Wed Sep 16 14:06:56 1998
@@ -42,8 +42,10 @@
 #define DEVID_NS87410	((ide_pci_devid_t){PCI_VENDOR_ID_NS,      PCI_DEVICE_ID_NS_87410})
 #define DEVID_NS87415	((ide_pci_devid_t){PCI_VENDOR_ID_NS,      PCI_DEVICE_ID_NS_87415})
 #define DEVID_HT6565	((ide_pci_devid_t){PCI_VENDOR_ID_HOLTEK,  PCI_DEVICE_ID_HOLTEK_6565})
-#define DEVID_AEC6210	((ide_pci_devid_t){0x1191,                0x0005})
+#define DEVID_AEC6210	((ide_pci_devid_t){PCI_VENDOR_ID_ARTOP,   PCI_DEVICE_ID_ARTOP_ATP850UF})
 #define DEVID_W82C105	((ide_pci_devid_t){PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105})
+#define DEVID_UM8886BF	((ide_pci_devid_t){PCI_VENDOR_ID_UMC,     PCI_DEVICE_ID_UMC_UM8886BF})
+#define DEVID_HPT343	((ide_pci_devid_t){PCI_VENDOR_ID_TTI,     PCI_DEVICE_ID_TTI_HPT343})
 
 #define IDE_IGNORE	((void *)-1)
 
@@ -93,6 +95,13 @@
 #define INIT_RZ1000	IDE_IGNORE
 #endif
 
+#ifdef CONFIG_BLK_DEV_VIA82C586
+extern void ide_init_via82c586(ide_hwif_t *);
+#define	INIT_VIA82C586	&ide_init_via82c586
+#else
+#define	INIT_VIA82C586	NULL
+#endif
+
 typedef struct ide_pci_enablebit_s {
 	byte	reg;	/* byte pci reg holding the enable-bit */
 	byte	mask;	/* mask to isolate the enable-bit */
@@ -104,35 +113,80 @@
 	const char		*name;
 	void 			(*init_hwif)(ide_hwif_t *hwif);
 	ide_pci_enablebit_t	enablebits[2];
+	byte			bootable;
+	unsigned int		extra;
 } ide_pci_device_t;
 
 static ide_pci_device_t ide_pci_chipsets[] __initdata = {
-	{DEVID_PIIXa,	"PIIX",		NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
-	{DEVID_PIIXb,	"PIIX",		NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
-	{DEVID_PIIX3,	"PIIX3",	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
-	{DEVID_PIIX4,	"PIIX4",	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
-	{DEVID_VP_IDE,	"VP_IDE",	NULL,		{{0x40,0x02,0x02}, {0x40,0x01,0x01}} },
-	{DEVID_PDC20246,"PDC20246",	NULL,		{{0x50,0x02,0x02}, {0x50,0x04,0x04}} },
-	{DEVID_RZ1000,	"RZ1000",	INIT_RZ1000,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
-	{DEVID_RZ1001,	"RZ1001",	INIT_RZ1000,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
-	{DEVID_CMD640,	"CMD640",	IDE_IGNORE,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
-	{DEVID_NS87410,	"NS87410",	NULL,		{{0x43,0x08,0x08}, {0x47,0x08,0x08}} },
-	{DEVID_SIS5513,	"SIS5513",	NULL,		{{0x4a,0x02,0x02}, {0x4a,0x04,0x04}} },
-	{DEVID_CMD646,	"CMD646",	INIT_CMD646,	{{0x00,0x00,0x00}, {0x51,0x80,0x80}} },
-	{DEVID_HT6565,	"HT6565",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
-	{DEVID_OPTI621,	"OPTI621",	INIT_OPTI621,	{{0x45,0x80,0x00}, {0x40,0x08,0x00}} },
-	{DEVID_OPTI621X,"OPTI621X",	INIT_OPTI621,	{{0x45,0x80,0x00}, {0x40,0x08,0x00}} },
-	{DEVID_TRM290,	"TRM290",	INIT_TRM290,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
-	{DEVID_NS87415,	"NS87415",	INIT_NS87415,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
-	{DEVID_AEC6210,	"AEC6210",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
-	{DEVID_W82C105,	"W82C105",	INIT_W82C105,	{{0x40,0x01,0x01}, {0x40,0x10,0x10}} },
-	{IDE_PCI_DEVID_NULL, "PCI_IDE",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}} }};
+	{DEVID_PIIXa,	"PIIX",		NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}}, 	0x01,	0 },
+	{DEVID_PIIXb,	"PIIX",		NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}}, 	0x01,	0 },
+	{DEVID_PIIX3,	"PIIX3",	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}}, 	0x01,	0 },
+	{DEVID_PIIX4,	"PIIX4",	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}}, 	0x01,	0 },
+	{DEVID_VP_IDE,	"VP_IDE",	INIT_VIA82C586,	{{0x40,0x02,0x02}, {0x40,0x01,0x01}}, 	0x01,	0 },
+	{DEVID_PDC20246,"PDC20246",	NULL,		{{0x50,0x02,0x02}, {0x50,0x04,0x04}}, 	0x01,	16 },
+	{DEVID_RZ1000,	"RZ1000",	INIT_RZ1000,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	0x01,	0 },
+	{DEVID_RZ1001,	"RZ1001",	INIT_RZ1000,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	0x01,	0 },
+	{DEVID_CMD640,	"CMD640",	IDE_IGNORE,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	0x01,	0 },
+	{DEVID_NS87410,	"NS87410",	NULL,		{{0x43,0x08,0x08}, {0x47,0x08,0x08}}, 	0x01,	0 },
+	{DEVID_SIS5513,	"SIS5513",	NULL,		{{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, 	0x01,	0 },
+	{DEVID_CMD646,	"CMD646",	INIT_CMD646,	{{0x00,0x00,0x00}, {0x51,0x80,0x80}}, 	0x01,	0 },
+	{DEVID_HT6565,	"HT6565",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	0x01,	0 },
+	{DEVID_OPTI621,	"OPTI621",	INIT_OPTI621,	{{0x45,0x80,0x00}, {0x40,0x08,0x00}}, 	0x01,	0 },
+	{DEVID_OPTI621X,"OPTI621X",	INIT_OPTI621,	{{0x45,0x80,0x00}, {0x40,0x08,0x00}}, 	0x01,	0 },
+	{DEVID_TRM290,	"TRM290",	INIT_TRM290,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	0x01,	0 },
+	{DEVID_NS87415,	"NS87415",	INIT_NS87415,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	0x01,	0 },
+	{DEVID_AEC6210,	"AEC6210",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	0x01,	0 },
+	{DEVID_W82C105,	"W82C105",	INIT_W82C105,	{{0x40,0x01,0x01}, {0x40,0x10,0x10}}, 	0x01,	0 },
+	{DEVID_UM8886BF,"UM8886BF",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	0x01,	0 },
+	{DEVID_HPT343,	"HPT343",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	0x00,	16 },
+	{IDE_PCI_DEVID_NULL, "PCI_IDE",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	0x01,	0 }};
+
+/*
+ * This allows offboard ide-pci cards the enable a BIOS, verify interrupt
+ * settings of split-mirror pci-config space, place chipset into init-mode,
+ * and/or preserve an interrupt if the card is not native ide support.
+ */
+__initfunc(static unsigned int ide_special_settings (struct pci_dev *dev, const char *name))
+{
+	unsigned int addressbios = 0;
+
+	pci_read_config_dword(dev, PCI_ROM_ADDRESS, &addressbios);
+
+	switch(dev->device) {
+		case PCI_DEVICE_ID_ARTOP_ATP850UF:
+		case PCI_DEVICE_ID_PROMISE_20246:
+			pci_write_config_byte(dev, PCI_ROM_ADDRESS, PCI_ROM_ADDRESS_ENABLE);
+			printk("%s: ROM enabled ", name);
+
+			if (!addressbios) {
+				printk("but no address\n");
+			} else {
+				printk("at 0x%08x\n", addressbios);
+			}
+
+			if ((dev->class >> 8) == PCI_CLASS_STORAGE_RAID) {
+				unsigned char irq1 = 0, irq2 = 0;
+
+				pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq1);
+				pci_read_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, &irq2);	/* 0xbc */
+				if (irq1 != irq2) {
+					printk("%s: IRQ1 %d IRQ2 %d\n",
+						name, irq1, irq2);
+					pci_write_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, irq1);	/* 0xbc */
+				}
+			}
+			return dev->irq;
+		default:
+			break;
+	}
+	return 0;
+}
 
 /*
  * Match a PCI IDE port against an entry in ide_hwifs[],
  * based on io_base port if possible.
  */
-__initfunc(static ide_hwif_t *ide_match_hwif (unsigned long io_base, const char *name))
+__initfunc(static ide_hwif_t *ide_match_hwif (unsigned long io_base, byte bootable, const char *name))
 {
 	int h;
 	ide_hwif_t *hwif;
@@ -168,11 +222,22 @@
 	 * Give preference to claiming other slots before claiming ide0/ide1,
 	 * just in case there's another interface yet-to-be-scanned
 	 * which uses ports 1f0/170 (the ide0/ide1 defaults).
+	 *
+	 * Unless there is a bootable card that does not use the standard
+	 * ports 1f0/170 (the ide0/ide1 defaults). The (bootable) flag.
 	 */
-	for (h = 2; h < MAX_HWIFS; ++h) {
-		hwif = ide_hwifs + h;
-		if (hwif->chipset == ide_unknown)
-			return hwif;	/* pick an unused entry */
+	if (bootable) {
+		for (h = 0; h < MAX_HWIFS; ++h) {
+			hwif = &ide_hwifs[h];
+			if (hwif->chipset == ide_unknown)
+				return hwif;	/* pick an unused entry */
+		}
+	} else {
+		for (h = 2; h < MAX_HWIFS; ++h) {
+			hwif = ide_hwifs + h;
+			if (hwif->chipset == ide_unknown)
+				return hwif;	/* pick an unused entry */
+		}
 	}
 	for (h = 0; h < 2; ++h) {
 		hwif = ide_hwifs + h;
@@ -263,7 +328,7 @@
 	pciirq = dev->irq;
 	if ((dev->class & ~(0xfa)) != ((PCI_CLASS_STORAGE_IDE << 8) | 5)) {
 		printk("%s: not 100%% native mode: will probe irqs later\n", d->name);
-		pciirq = 0;
+		pciirq = ide_special_settings(dev, d->name);
 	} else if (tried_config) {
 		printk("%s: will probe irqs later\n", d->name);
 		pciirq = 0;
@@ -298,7 +363,7 @@
 			ctl = port ? 0x374 : 0x3f4;	/* use default value */
 		if (!base)
 			base = port ? 0x170 : 0x1f0;	/* use default value */
-		if ((hwif = ide_match_hwif(base, d->name)) == NULL)
+		if ((hwif = ide_match_hwif(base, d->bootable, d->name)) == NULL)
 			continue;	/* no room in ide_hwifs[] */
 		if (hwif->io_ports[IDE_DATA_OFFSET] != base) {
 			ide_init_hwif_ports(hwif->io_ports, base, NULL);
@@ -314,6 +379,10 @@
 		if (mate) {
 			hwif->mate = mate;
 			mate->mate = hwif;
+			if (IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210)) {
+				hwif->serialized = 1;
+				mate->serialized = 1;
+			}
 		}
 #ifdef CONFIG_BLK_DEV_IDEDMA
 		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_SIS5513))
@@ -321,9 +390,10 @@
 		if (autodma)
 			hwif->autodma = 1;
 		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) ||
+		    IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210) ||
+		    IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT343) ||
 		    ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 0x80))) {
-			unsigned int extra = (!mate && IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246)) ? 16 : 0;
-			unsigned long dma_base = ide_get_or_set_dma_base(hwif, extra, d->name);
+			unsigned long dma_base = ide_get_or_set_dma_base(hwif, (!mate && d->extra) ? d->extra : 0, d->name);
 			if (dma_base && !(pcicmd & PCI_COMMAND_MASTER)) {
 				/*
  	 			 * Set up BM-DMA capability (PnP BIOS should have done this)
diff -u --recursive --new-file v2.1.121/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c
--- v2.1.121/linux/drivers/block/ide-probe.c	Wed Sep  9 14:51:07 1998
+++ linux/drivers/block/ide-probe.c	Wed Sep 16 13:25:56 1998
@@ -273,6 +273,18 @@
 	{
 		if ((rc = try_to_identify(drive,cmd)))   /* send cmd and wait */
 			rc = try_to_identify(drive,cmd); /* failed: try again */
+		if (rc == 1 && cmd == WIN_PIDENTIFY && drive->autotune != 2) {
+			unsigned long timeout;
+			printk("%s: no response (status = 0x%02x), resetting drive\n", drive->name, GET_STAT());
+			delay_50ms();
+			OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+			delay_50ms();
+			OUT_BYTE(WIN_SRST, IDE_COMMAND_REG);
+			timeout = jiffies;
+			while ((GET_STAT() & BUSY_STAT) && jiffies < timeout + WAIT_WORSTCASE)
+				delay_50ms();
+			rc = try_to_identify(drive, cmd);
+		}
 		if (rc == 1)
 			printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT());
 		(void) GET_STAT();		/* ensure drive irq is clear */
diff -u --recursive --new-file v2.1.121/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.1.121/linux/drivers/block/ide.c	Sat Sep  5 16:46:40 1998
+++ linux/drivers/block/ide.c	Wed Sep 16 13:25:56 1998
@@ -1104,6 +1104,10 @@
 			if (sleep) {
 				if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep)) 
 					sleep = jiffies + WAIT_MIN_SLEEP;
+#if 1
+				if (hwgroup->timer.next || hwgroup->timer.prev)
+					printk("ide_set_handler: timer already active\n");
+#endif
 				mod_timer(&hwgroup->timer, sleep);
 			} else {
 				/* Ugly, but how can we sleep for the lock otherwise? perhaps from tq_scheduler? */
@@ -1253,7 +1257,7 @@
 	}
 	hwgroup->busy = 1;	/* should already be "1" */
 	hwgroup->handler = NULL;
-	del_timer(&hwgroup->timer);
+	del_timer(&hwgroup->timer);	/* Is this needed?? */
 	if (hwgroup->poll_timeout != 0) {	/* polling in progress? */
 		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
 		handler(drive);
@@ -1265,6 +1269,10 @@
 		if (drive->waiting_for_dma) {
 			(void) hwgroup->hwif->dmaproc(ide_dma_end, drive);
 			printk("%s: timeout waiting for DMA\n", drive->name);
+	/*
+	 *  need something here for HX PIIX3 UDMA and HPT343.......AMH
+	 *  irq timeout: status=0x58 { DriveReady SeekComplete DataRequest }
+	 */
 		}
 		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
 		ide_error(drive, "irq timeout", GET_STAT());
diff -u --recursive --new-file v2.1.121/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.1.121/linux/drivers/block/ide.h	Thu Aug  6 14:06:31 1998
+++ linux/drivers/block/ide.h	Wed Sep 16 14:07:30 1998
@@ -729,8 +729,11 @@
 void ide_scan_pcibus (void) __init;
 #endif
 #ifdef CONFIG_BLK_DEV_IDEDMA
+#define BAD_DMA_DRIVE		0
+#define GOOD_DMA_DRIVE		1
 int ide_build_dmatable (ide_drive_t *drive);
 void ide_dma_intr  (ide_drive_t *drive);
+int check_drive_lists (ide_drive_t *drive, int good_bad);
 int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive);
 void ide_setup_dma (ide_hwif_t *hwif, unsigned long dmabase, unsigned int num_ports) __init;
 unsigned long ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) __init;
diff -u --recursive --new-file v2.1.121/linux/drivers/block/loop.c linux/drivers/block/loop.c
--- v2.1.121/linux/drivers/block/loop.c	Wed Aug 26 11:37:35 1998
+++ linux/drivers/block/loop.c	Mon Sep 14 22:06:57 1998
@@ -15,30 +15,27 @@
  * Fixed do_loop_request() re-entrancy - Vincent.Renardias@waw.com Mar 20, 1997
  *
  * Handle sparse backing files correctly - Kenn Humborg, Jun 28, 1998
+ *
+ * Loadable modules and other fixes by AK, 1998
+ * 
+ * Still To Fix:
+ * - Advisory locking is ignored here. 
+ * - Should use an own CAP_* category instead of CAP_SYS_ADMIN 
  */
 
 #include <linux/module.h>
-
-#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/file.h>
 #include <linux/stat.h>
 #include <linux/errno.h>
 #include <linux/major.h>
+
 #include <linux/init.h>
 
 #include <asm/uaccess.h>
 
-#ifdef CONFIG_BLK_DEV_LOOP_DES
-# /*nodep*/ include <linux/des.h>
-#endif
-
-#ifdef CONFIG_BLK_DEV_LOOP_IDEA
-# /*nodep*/ include <linux/idea.h>
-#endif
-
-#include <linux/loop.h>		/* must follow des.h */
+#include <linux/loop.h>		
 
 #define MAJOR_NR LOOP_MAJOR
 
@@ -63,7 +60,6 @@
    backing file (can happen if the backing file is sparse) */
 static int create_missing_block(struct loop_device *lo, int block, int blksize);
 
-
 /*
  * Transfer functions
  */
@@ -97,80 +93,38 @@
 	return 0;
 }
 
-#ifdef DES_AVAILABLE
-static int transfer_des(struct loop_device *lo, int cmd, char *raw_buf,
-		  char *loop_buf, int size)
+static int none_status(struct loop_device *lo, struct loop_info *info)
 {
-	unsigned long tmp[2];
-	unsigned long x0,x1,p0,p1;
+	return 0; 
+} 
 
-	if (size & 7)
+static int xor_status(struct loop_device *lo, struct loop_info *info)
+{
+	if (info->lo_encrypt_key_size < 0)
 		return -EINVAL;
-	x0 = lo->lo_des_init[0];
-	x1 = lo->lo_des_init[1];
-	while (size) {
-		if (cmd == READ) {
-			tmp[0] = (p0 = ((unsigned long *) raw_buf)[0])^x0;
-			tmp[1] = (p1 = ((unsigned long *) raw_buf)[1])^x1;
-			des_ecb_encrypt((des_cblock *) tmp,(des_cblock *)
-			    loop_buf,lo->lo_des_key,DES_ENCRYPT);
-			x0 = p0^((unsigned long *) loop_buf)[0];
-			x1 = p1^((unsigned long *) loop_buf)[1];
-		}
-		else {
-			p0 = ((unsigned long *) loop_buf)[0];
-			p1 = ((unsigned long *) loop_buf)[1];
-			des_ecb_encrypt((des_cblock *) loop_buf,(des_cblock *)
-			    raw_buf,lo->lo_des_key,DES_DECRYPT);
-			((unsigned long *) raw_buf)[0] ^= x0;
-			((unsigned long *) raw_buf)[1] ^= x1;
-			x0 = p0^((unsigned long *) raw_buf)[0];
-			x1 = p1^((unsigned long *) raw_buf)[1];
-		}
-		size -= 8;
-		raw_buf += 8;
-		loop_buf += 8;
-	}
 	return 0;
 }
-#endif
-
-#ifdef IDEA_AVAILABLE
-
-extern void idea_encrypt_block(idea_key,char *,char *,int);
 
-static int transfer_idea(struct loop_device *lo, int cmd, char *raw_buf,
-		  char *loop_buf, int size)
-{
-  if (cmd==READ) {
-    idea_encrypt_block(lo->lo_idea_en_key,raw_buf,loop_buf,size);
-  }
-  else {
-    idea_encrypt_block(lo->lo_idea_de_key,loop_buf,raw_buf,size);
-  }
-  return 0;
-}
-#endif
-
-static transfer_proc_t xfer_funcs[MAX_LOOP] = {
-	transfer_none,		/* LO_CRYPT_NONE */
-	transfer_xor,		/* LO_CRYPT_XOR */
-#ifdef DES_AVAILABLE
-	transfer_des,		/* LO_CRYPT_DES */
-#else
-	NULL,			/* LO_CRYPT_DES */
-#endif
-#ifdef IDEA_AVAILABLE           /* LO_CRYPT_IDEA */
-	transfer_idea
-#else
-	NULL
-#endif
+struct loop_func_table none_funcs = { 
+	number: LO_CRYPT_NONE,
+	transfer: transfer_none,
+	init: none_status
+}; 	
+
+struct loop_func_table xor_funcs = { 
+	number: LO_CRYPT_XOR,
+	transfer: transfer_xor,
+	init: xor_status
+}; 	
+
+/* xfer_funcs[0] is special - its release function is never called */ 
+struct loop_func_table *xfer_funcs[MAX_LO_CRYPT] = {
+	&none_funcs,
+	&xor_funcs  
 };
 
-
 #define MAX_DISK_SIZE 1024*1024*1024
 
-
 static void figure_loop_size(struct loop_device *lo)
 {
 	int	size;
@@ -326,6 +280,7 @@
 	char            zero_buf[1] = { 0 };
 	ssize_t         retval;
 	mm_segment_t	old_fs;
+	struct inode	*inode;
 
 	file = lo->lo_backing_file;
 	if (file == NULL) {
@@ -349,16 +304,19 @@
 		file->f_version = ++event;
 	}
 
-	if (file->f_op->write == NULL) {
-		printk(KERN_WARNING "loop: cannot create block - no write file op\n");
+	if ((file->f_mode & FMODE_WRITE) == 0 ||  file->f_op->write == NULL) {
+		printk(KERN_WARNING "loop: cannot create block - file not writeable\n");
 		return FALSE;
 	}
 
 	old_fs = get_fs();
 	set_fs(get_ds());
 
+	inode = file->f_dentry->d_inode;
+	down(&inode->i_sem); 
 	retval = file->f_op->write(file, zero_buf, 1, &file->f_pos);
-
+	up(&inode->i_sem);
+	
 	set_fs(old_fs);
 
 	if (retval < 0) {
@@ -447,6 +405,7 @@
 	lo->lo_dentry = file->f_dentry;
 	lo->lo_dentry->d_count++;
 	lo->transfer = NULL;
+	lo->ioctl = NULL;
 	figure_loop_size(lo);
 
 out_putf:
@@ -457,6 +416,19 @@
 	return error;
 }
 
+static int loop_release_xfer(struct loop_device *lo)
+{
+	int err = 0; 
+	if (lo->lo_encrypt_type) {
+		if (xfer_funcs[lo->lo_encrypt_type] && 
+		    xfer_funcs[lo->lo_encrypt_type]->release) { 
+			err = xfer_funcs[lo->lo_encrypt_type]->release(lo);
+		}
+		lo->lo_encrypt_type = 0;
+	}
+	return err;
+}
+
 static int loop_clr_fd(struct loop_device *lo, kdev_t dev)
 {
 	struct dentry *dentry = lo->lo_dentry;
@@ -477,6 +449,8 @@
 		dput(dentry);
 	}
 
+	lo->transfer = NULL;
+	lo->ioctl = NULL;
 	lo->lo_device = 0;
 	lo->lo_encrypt_type = 0;
 	lo->lo_offset = 0;
@@ -491,60 +465,44 @@
 
 static int loop_set_status(struct loop_device *lo, struct loop_info *arg)
 {
-	struct loop_info info;
+	struct loop_info info; 
 	int err;
+	unsigned int type;
 
+	if (lo->lo_encrypt_key_size && lo->lo_key_owner != current->uid && 
+	    !capable(CAP_SYS_ADMIN))
+		return -EPERM;
 	if (!lo->lo_dentry)
 		return -ENXIO;
-	if (!arg)
-		return -EINVAL;
-	err = verify_area(VERIFY_READ, arg, sizeof(info));
-	if (err)
-		return err;
-	copy_from_user(&info, arg, sizeof(info));
+	if (copy_from_user(&info, arg, sizeof (struct loop_info)))
+		return -EFAULT; 
 	if ((unsigned int) info.lo_encrypt_key_size > LO_KEY_SIZE)
 		return -EINVAL;
-	switch (info.lo_encrypt_type) {
-	case LO_CRYPT_NONE:
-		break;
-	case LO_CRYPT_XOR:
-		if (info.lo_encrypt_key_size < 0)
-			return -EINVAL;
-		break;
-#ifdef DES_AVAILABLE
-	case LO_CRYPT_DES:
-		if (info.lo_encrypt_key_size != 8)
-			return -EINVAL;
-		des_set_key((des_cblock *) lo->lo_encrypt_key,
-		   lo->lo_des_key);
-		memcpy(lo->lo_des_init,info.lo_init,8);
-		break;
-#endif
-#ifdef IDEA_AVAILABLE
-	case LO_CRYPT_IDEA:
-	  {
-	        uint16 tmpkey[8];
-
-	        if (info.lo_encrypt_key_size != IDEAKEYSIZE)
-		        return -EINVAL;
-                /* create key in lo-> from info.lo_encrypt_key */
-		memcpy(tmpkey,info.lo_encrypt_key,sizeof(tmpkey));
-		en_key_idea(tmpkey,lo->lo_idea_en_key);
-		de_key_idea(lo->lo_idea_en_key,lo->lo_idea_de_key);
-		break;
-	  }
-#endif
-	default:
+	type = info.lo_encrypt_type; 
+	if (type >= MAX_LO_CRYPT || xfer_funcs[type] == NULL)
 		return -EINVAL;
-	}
+	err = loop_release_xfer(lo); 
+	if (err)
+		return err;
+	if (xfer_funcs[type]->init) 
+		err = xfer_funcs[type]->init(lo, &info);
+	if (err)
+		return err;
+
 	lo->lo_offset = info.lo_offset;
 	strncpy(lo->lo_name, info.lo_name, LO_NAME_SIZE);
-	lo->lo_encrypt_type = info.lo_encrypt_type;
-	lo->transfer = xfer_funcs[lo->lo_encrypt_type];
+	lo->lo_encrypt_type = type; 
+
+	lo->transfer = xfer_funcs[type]->transfer;
+	lo->ioctl = xfer_funcs[type]->ioctl;
 	lo->lo_encrypt_key_size = info.lo_encrypt_key_size;
-	if (info.lo_encrypt_key_size)
-		memcpy(lo->lo_encrypt_key, info.lo_encrypt_key,
+	lo->lo_init[0] = info.lo_init[0];
+	lo->lo_init[1] = info.lo_init[1];
+	if (info.lo_encrypt_key_size) {
+		memcpy(lo->lo_encrypt_key, info.lo_encrypt_key, 
 		       info.lo_encrypt_key_size);
+		lo->lo_key_owner = current->uid; 
+	}	
 	figure_loop_size(lo);
 	return 0;
 }
@@ -552,15 +510,11 @@
 static int loop_get_status(struct loop_device *lo, struct loop_info *arg)
 {
 	struct loop_info	info;
-	int err;
-	
+
 	if (!lo->lo_dentry)
 		return -ENXIO;
 	if (!arg)
 		return -EINVAL;
-	err = verify_area(VERIFY_WRITE, arg, sizeof(info));
-	if (err)
-		return err;
 	memset(&info, 0, sizeof(info));
 	info.lo_number = lo->lo_number;
 	info.lo_device = kdev_t_to_nr(lo->lo_dentry->d_inode->i_dev);
@@ -575,8 +529,7 @@
 		memcpy(info.lo_encrypt_key, lo->lo_encrypt_key,
 		       lo->lo_encrypt_key_size);
 	}
-	copy_to_user(arg, &info, sizeof(info));
-	return 0;
+	return copy_to_user(arg, &info, sizeof(info)) ? -EFAULT : 0;
 }
 
 static int lo_ioctl(struct inode * inode, struct file * file,
@@ -611,7 +564,7 @@
 			return -EINVAL;
 		return put_user(loop_sizes[lo->lo_number] << 1, (long *) arg);
 	default:
-		return -EINVAL;
+		return lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL;
 	}
 	return 0;
 }
@@ -628,8 +581,9 @@
 		return -ENODEV;
 	}
 	dev = MINOR(inode->i_rdev);
-	if (dev >= MAX_LOOP)
+	if (dev >= MAX_LOOP) {
 		return -ENODEV;
+	}
 	lo = &loop_dev[dev];
 	lo->lo_refcnt++;
 	MOD_INC_USE_COUNT;
@@ -639,7 +593,7 @@
 static int lo_release(struct inode *inode, struct file *file)
 {
 	struct loop_device *lo;
-	int	dev;
+	int	dev, err = 0;
 
 	if (!inode)
 		return 0;
@@ -650,15 +604,18 @@
 	dev = MINOR(inode->i_rdev);
 	if (dev >= MAX_LOOP)
 		return 0;
-	fsync_dev(inode->i_rdev);
+	err = fsync_dev(inode->i_rdev);
+	if (err < 0) 
+		return err; 
 	lo = &loop_dev[dev];
 	if (lo->lo_refcnt <= 0)
 		printk(KERN_ERR "lo_release: refcount(%d) <= 0\n", lo->lo_refcnt);
 	else  {
-		lo->lo_refcnt--;
+		if (--lo->lo_refcnt == 0) 
+			err = loop_release_xfer(lo);
 		MOD_DEC_USE_COUNT;
 	}
-	return 0;
+	return err;
 }
 
 static struct file_operations lo_fops = {
@@ -681,8 +638,28 @@
 #define loop_init init_module
 #endif
 
-__initfunc(int
-loop_init( void )) {
+int loop_register_transfer(struct loop_func_table *funcs)
+{
+	if ((unsigned)funcs->number > MAX_LO_CRYPT || xfer_funcs[funcs->number])
+		return -EINVAL;
+	xfer_funcs[funcs->number] = funcs;
+	return 0; 
+}
+
+/* Usage checking must be done by the caller - usually with MOD_INC/DEC_* */  
+int loop_unregister_transfer(int number)
+{
+	if ((unsigned)number >= MAX_LO_CRYPT)
+		return -EINVAL; 
+	xfer_funcs[number] = NULL; 
+	return 0; 
+}
+
+EXPORT_SYMBOL(loop_register_transfer);
+EXPORT_SYMBOL(loop_unregister_transfer);
+
+int __init loop_init(void) 
+{
 	int	i;
 
 	if (register_blkdev(MAJOR_NR, "loop", &lo_fops)) {
@@ -692,12 +669,6 @@
 	}
 #ifndef MODULE
 	printk(KERN_INFO "loop: registered device at major %d\n", MAJOR_NR);
-#ifdef DES_AVAILABLE
-	printk(KERN_INFO "loop: DES encryption available\n");
-#endif
-#ifdef IDEA_AVAILABLE
-	printk(KERN_INFO "loop: IDEA encryption available\n");
-#endif
 #endif
 
 	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
@@ -714,9 +685,9 @@
 }
 
 #ifdef MODULE
-void
-cleanup_module( void ) {
-  if (unregister_blkdev(MAJOR_NR, "loop") != 0)
-    printk(KERN_WARNING "loop: cleanup_module failed\n");
+void cleanup_module(void) 
+{
+	if (unregister_blkdev(MAJOR_NR, "loop") != 0)
+		printk(KERN_WARNING "loop: cannot unregister blkdev\n");
 }
 #endif
diff -u --recursive --new-file v2.1.121/linux/drivers/block/via82c586.c linux/drivers/block/via82c586.c
--- v2.1.121/linux/drivers/block/via82c586.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/via82c586.c	Wed Sep 16 13:25:56 1998
@@ -0,0 +1,86 @@
+/*
+ * linux/drivers/block/via82C586.c	Version 0.01	Aug 16, 1998
+ *
+ *  Copyright (C) 1998 Michel Aubry
+ *  Copyright (C) 1998 Andre Hedrick
+ *
+ *  The VIA MVP-3 is reported OK with UDMA.
+ *
+ *  VIA chips also have a single FIFO, with the same 64 bytes deep buffer (16 levels 
+ *  of 4 bytes each).
+ *  However, VIA chips can have the buffer split either 8:8 levels, 16:0 levels or 
+ *  0:16 levels between both channels. One could think of using this feature, as even
+ *  if no level of FIFO is given to a given channel, one can always reach ATAPI drives 
+ *  through it, or, if one channel is unused, configuration defaults to an even split 
+ *  FIFO levels.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include "ide.h"
+
+/*
+ *  Set VIA Chipset Timings for (U)DMA modes enabled.
+ *
+ *  VIA Apollo chipset has complete support for
+ *  setting up the timing parameters.
+ */
+static void set_via_timings (ide_hwif_t *hwif)
+{
+	struct pci_dev  *dev = hwif->pci_dev;
+	byte post  = hwif->channel ? 0xc0 : 0x30;
+	byte flush = hwif->channel ? 0xa0 : 0x50;
+	byte via_config = 0;
+	int rc = 0, errors = 0;
+
+	printk("%s: VIA Bus-Master ", hwif->name);
+
+	if (!hwif->dma_base) {
+		printk(" ERROR, NO DMA_BASE\n");
+		return;
+	}
+
+	/* setting IDE read prefetch buffer and IDE post write buffer.
+	 * (This feature allows prefetched reads and post writes).
+	 */
+	if ((rc = pci_read_config_byte(dev, 0x41, &via_config))) {
+		errors++;
+		goto via_error;
+	}
+	if ((rc = pci_write_config_byte(dev, 0x41, via_config | post))) {
+		errors++;
+		goto via_error;
+	}
+
+	/* setting Channel read and End-of-sector FIFO flush.
+	 * (This feature ensures that FIFO flush is enabled:
+         *  - for read DMA when interrupt asserts the given channel.
+         *  - at the end of each sector for the given channel.)
+	 */
+	if ((rc = pci_read_config_byte(dev, 0x46, &via_config))) {
+		errors++;
+		goto via_error;
+	}
+        if ((rc = pci_write_config_byte(dev, 0x46, via_config | flush))) {
+		errors++;
+		goto via_error;
+	}
+
+via_error:
+	printk("(U)DMA Timing Config %s\n", errors ? "ERROR" : "Success");
+}
+
+void ide_init_via82c586 (ide_hwif_t *hwif)
+{
+	set_via_timings(hwif);
+}
+
diff -u --recursive --new-file v2.1.121/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- v2.1.121/linux/drivers/cdrom/cdrom.c	Wed Sep  9 14:51:07 1998
+++ linux/drivers/cdrom/cdrom.c	Wed Sep 16 13:25:56 1998
@@ -67,7 +67,7 @@
 
 2.12  Jan  24, 1998 -- Erik Andersen <andersee@debian.org>
   -- Fixed a bug in the IOCTL_IN and IOCTL_OUT macros.  It turns out that
-  copy_*_user does not return EFAULT on error, but instead return the number 
+  copy_*_user does not return EFAULT on error, but instead returns the number 
   of bytes not copied.  I was returning whatever non-zero stuff came back from 
   the copy_*_user functions directly, which would result in strange errors.
 
@@ -80,10 +80,16 @@
   -- Fixed it so that the /proc entry now also shows up when cdrom is
   compiled into the kernel.  Before it only worked when loaded as a module.
 
+  2.14 August 17, 1998 -- Erik Andersen <andersee@debian.org>
+  -- Fixed a bug in cdrom_media_changed and handling of reporting that
+  the media had changed for devices that _don't_ implement media_changed.  
+  Thanks to Grant R. Guenther <grant@torque.net> for spotting this bug.
+  -- Made a few things more pedanticly correct.
+
 -------------------------------------------------------------------------*/
 
-#define REVISION "Revision: 2.13"
-#define VERSION "Id: cdrom.c 2.13 1998/07/17 erik"
+#define REVISION "Revision: 2.14"
+#define VERSION "Id: cdrom.c 2.14 1998/08/17 erik"
 
 /* I use an error-log mask to give fine grain control over the type of
    messages dumped to the system logs.  The available masks include: */
@@ -163,15 +169,6 @@
 static void sanitize_format(union cdrom_addr *addr, 
 		u_char * curr, u_char requested);
 static void cdrom_sysctl_register(void);
-typedef struct {
-	int data;
-	int audio;
-	int cdi;
-	int xa;
-	long error;
-} tracktype;
-
-static void cdrom_count_tracks(struct cdrom_device_info *cdi,tracktype* tracks);
 static struct cdrom_device_info *topCdromPtr = NULL;
 
 struct file_operations cdrom_fops =
@@ -312,6 +309,9 @@
 		ret = open_for_data(cdi);
 	if (!ret) cdi->use_count++;
 	cdinfo(CD_OPEN, "Use count for \"/dev/%s\" now %d\n", cdi->name, cdi->use_count);
+	/* Do this on open.  Don't wait for mount, because they might
+	    not be mounting, but opening with O_NONBLOCK */
+	check_disk_change(dev);
 	return ret;
 }
 
@@ -497,7 +497,10 @@
 		!(fp && fp->f_flags & O_NONBLOCK);
 	cdo->release(cdi);
 	if (cdi->use_count == 0) {      /* last process that closes dev*/
+		struct super_block *sb;
 		sync_dev(dev);
+		sb = get_super(dev);
+		if (sb) invalidate_inodes(sb);
 		invalidate_buffers(dev);
 		if (opened_for_data &&
 		    cdi->options & CDO_AUTO_EJECT &&
@@ -519,6 +522,8 @@
 	unsigned int mask = (1 << (queue & 1));
 	int ret = !!(cdi->mc_flags & mask);
 
+	if (!(cdi->ops->capability & ~cdi->mask & CDC_MEDIA_CHANGED))
+	    return ret;
 	/* changed since last call? */
 	if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) {
 		cdi->mc_flags = 0x3;    /* set bit on both queues */
@@ -532,14 +537,18 @@
 int cdrom_media_changed(kdev_t dev)
 {
 	struct cdrom_device_info *cdi = cdrom_find_device (dev);
+	/* This talks to the VFS, which doesn't like errors - just 1 or 0.  
+	 * Returning "0" is always safe (media hasn't been changed). Do that 
+	 * if the low-level cdrom driver dosn't support media changed. */ 
 	if (cdi == NULL)
-		return -ENODEV;
+		return 0;
 	if (cdi->ops->media_changed == NULL)
-		return -ENOSYS;
-	return media_changed(cdi, 0);
+		return 0;
+	if (!(cdi->ops->capability & ~cdi->mask & CDC_MEDIA_CHANGED))
+	    return 0;
+	return (media_changed(cdi, 0));
 }
 
-static
 void cdrom_count_tracks(struct cdrom_device_info *cdi, tracktype* tracks)
 {
 	struct cdrom_tochdr header;
@@ -950,6 +959,7 @@
 		return cdo->dev_ioctl(cdi, cmd, arg);
 }
 
+EXPORT_SYMBOL(cdrom_count_tracks);
 EXPORT_SYMBOL(register_cdrom);
 EXPORT_SYMBOL(unregister_cdrom);
 EXPORT_SYMBOL(cdrom_fops);
diff -u --recursive --new-file v2.1.121/linux/drivers/cdrom/cdu31a.c linux/drivers/cdrom/cdu31a.c
--- v2.1.121/linux/drivers/cdrom/cdu31a.c	Mon Aug  3 12:45:45 1998
+++ linux/drivers/cdrom/cdu31a.c	Mon Sep 14 11:34:01 1998
@@ -160,6 +160,7 @@
 #include <linux/string.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -1690,7 +1691,7 @@
    /* Make sure we have a valid TOC. */
    sony_get_toc(); 
 
-   sti();
+   spin_unlock_irq(&io_request_lock);
 
    /* Make sure the timer is cancelled. */
    del_timer(&cdu31a_abort_timer);
@@ -1849,7 +1850,7 @@
    }
 
 end_do_cdu31a_request:
-   cli();
+   spin_lock_irq(&io_request_lock);
 #if 0
    /* After finished, cancel any pending operations. */
    abort_read();
diff -u --recursive --new-file v2.1.121/linux/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c
--- v2.1.121/linux/drivers/cdrom/cm206.c	Sun Jun  7 11:16:29 1998
+++ linux/drivers/cdrom/cm206.c	Mon Sep 14 11:34:01 1998
@@ -186,6 +186,7 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 
 /* #include <linux/ucdrom.h> */
 
@@ -816,6 +817,7 @@
       end_request(0);
       continue;
     }
+    spin_unlock_irq(&io_request_lock);
     error=0;
     for (i=0; i<CURRENT->nr_sectors; i++) {
       int e1, e2;
@@ -838,6 +840,7 @@
 	debug(("cm206_request: %d %d\n", e1, e2));
       }
     }
+    spin_lock_irq(&io_request_lock);
     end_request(!error);
   }
 }
diff -u --recursive --new-file v2.1.121/linux/drivers/cdrom/sbpcd.c linux/drivers/cdrom/sbpcd.c
--- v2.1.121/linux/drivers/cdrom/sbpcd.c	Wed Aug 26 11:37:36 1998
+++ linux/drivers/cdrom/sbpcd.c	Mon Sep 14 11:34:01 1998
@@ -336,6 +336,7 @@
 #include <linux/string.h>
 #include <linux/vmalloc.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -1487,13 +1488,14 @@
 	
 	if (flags_cmd_out&f_putcmd)
 	{ 
+		unsigned long flags;
 		for (i=0;i<7;i++)
 			sprintf(&msgbuf[i*3], " %02X", drvcmd[i]);
 		msgbuf[i*3]=0;
 		msg(DBG_CMD,"cmd_out:%s\n", msgbuf);
-		cli();
+		save_flags(flags); cli();
 		for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
-		sti();
+		restore_flags(flags);
 	}
 	if (response_count!=0)
 	{
@@ -4824,7 +4826,7 @@
 	INIT_REQUEST;
 	req=CURRENT;		/* take out our request so no other */
 	CURRENT=req->next;	/* task can fuck it up         GTL  */
-	sti();
+	spin_unlock_irq(&io_request_lock);		/* FIXME!!!! */
 	
 	down(&ioctl_read_sem);
 	if (req->rq_status == RQ_INACTIVE)
@@ -4869,6 +4871,7 @@
 			xnr, req, req->sector, req->nr_sectors, jiffies);
 #endif
 		sbpcd_end_request(req, 1);
+		spin_lock_irq(&io_request_lock);		/* FIXME!!!! */
 		goto request_loop;
 	}
 
@@ -4908,6 +4911,7 @@
 				xnr, req, req->sector, req->nr_sectors, jiffies);
 #endif
 			sbpcd_end_request(req, 1);
+			spin_lock_irq(&io_request_lock);	/* FIXME!!!! */
 			goto request_loop;
 		}
 	}
@@ -4922,6 +4926,7 @@
 #endif
 	sbpcd_end_request(req, 0);
 	sbp_sleep(0);    /* wait a bit, try again */
+	spin_lock_irq(&io_request_lock);		/* FIXME!!!! */
 	goto request_loop;
 }
 /*==========================================================================*/
@@ -5098,13 +5103,11 @@
 		{
 			msg(DBG_INF,"sbp_data: CDi_status timeout (timed_out_data) (%02X).\n", j);
 			error_flag++;
-			break;
 		}
 		if (try==0)
 		{
 			msg(DBG_INF,"sbp_data: CDi_status timeout (try=0) (%02X).\n", j);
 			error_flag++;
-			break;
 		}
 		if (!(j&s_not_result_ready))
 		{
@@ -5119,10 +5122,10 @@
 				msg(DBG_INF, "CD contains no data tracks.\n");
 			else msg(DBG_INF, "sbp_data: DATA_READY timeout (%02X).\n", j);
 			error_flag++;
-			break;
 		}
 		SBPCD_STI;
-		error_flag=0;
+		if (error_flag) break;
+
 		msg(DBG_000, "sbp_data: beginning to read.\n");
 		p = D_S[d].sbp_buf + frame *  CD_FRAMESIZE;
 		if (sbpro_type==1) OUT(CDo_sel_i_d,1);
diff -u --recursive --new-file v2.1.121/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.121/linux/drivers/char/Config.in	Wed Aug 26 11:37:36 1998
+++ linux/drivers/char/Config.in	Thu Sep 10 16:33:15 1998
@@ -79,15 +79,6 @@
   fi
 fi
 
-bool 'Advanced Power Management BIOS support' CONFIG_APM
-if [ "$CONFIG_APM" = "y" ]; then
-  bool '   Ignore USER SUSPEND' CONFIG_APM_IGNORE_USER_SUSPEND
-  bool '   Enable PM at boot time' CONFIG_APM_DO_ENABLE
-  bool '   Make CPU Idle calls when idle' CONFIG_APM_CPU_IDLE
-  bool '   Enable console blanking using APM' CONFIG_APM_DISPLAY_BLANK
-  bool '   Power off on shutdown' CONFIG_APM_POWER_OFF
-  bool '   Ignore multiple suspend' CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
-fi
 bool 'Watchdog Timer Support'	CONFIG_WATCHDOG
 if [ "$CONFIG_WATCHDOG" != "n" ]; then
   bool '   Disable watchdog shutdown on close' CONFIG_WATCHDOG_NOWAYOUT
@@ -134,7 +125,7 @@
     hex '  SF16FMI I/O port (0x284 or 0x384)' CONFIG_RADIO_SF16FMI_PORT 284
   fi
   dep_tristate 'Zoltrix Radio' CONFIG_RADIO_ZOLTRIX $CONFIG_VIDEO_DEV
-  if [ "$CONFIG_RADIO_ZOLTRIX" != "n" ]; then
+  if [ "$CONFIG_RADIO_ZOLTRIX" = "y" ]; then
     hex '  ZOLTRIX I/O port (0x20c or 0x30c)' CONFIG_RADIO_ZOLTRIX_PORT 20c
   fi
 fi
diff -u --recursive --new-file v2.1.121/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.1.121/linux/drivers/char/Makefile	Wed Aug 26 11:37:36 1998
+++ linux/drivers/char/Makefile	Wed Sep  9 22:24:44 1998
@@ -435,13 +435,12 @@
   endif
 endif
 
-ifdef CONFIG_APM
-LX_OBJS += apm_bios.o
+ifdef CONFIG_H8
+LX_OBJS += h8.o
 M = y
 endif
 
-ifdef CONFIG_H8
-LX_OBJS += h8.o
+ifdef CONFIG_APM
 M = y
 endif
 
diff -u --recursive --new-file v2.1.121/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- v2.1.121/linux/drivers/char/apm_bios.c	Sat Sep  5 16:46:40 1998
+++ linux/drivers/char/apm_bios.c	Wed Dec 31 16:00:00 1969
@@ -1,1346 +0,0 @@
-/* -*- linux-c -*-
- * APM BIOS driver for Linux
- * Copyright 1994-1998 Stephen Rothwell
- *                     (Stephen.Rothwell@canb.auug.org.au)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * October 1995, Rik Faith (faith@cs.unc.edu):
- *    Minor enhancements and updates (to the patch set) for 1.3.x
- *    Documentation
- * January 1996, Rik Faith (faith@cs.unc.edu):
- *    Make /proc/apm easy to format (bump driver version)
- * March 1996, Rik Faith (faith@cs.unc.edu):
- *    Prohibit APM BIOS calls unless apm_enabled.
- *    (Thanks to Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>)
- * April 1996, Stephen Rothwell (Stephen.Rothwell@canb.auug.org.au)
- *    Version 1.0 and 1.1
- * May 1996, Version 1.2
- * Feb 1998, Version 1.3
- * Feb 1998, Version 1.4
- * Aug 1998, Version 1.5
- *
- * History:
- *    0.6b: first version in official kernel, Linux 1.3.46
- *    0.7: changed /proc/apm format, Linux 1.3.58
- *    0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
- *    0.9: only call bios if bios is present, Linux 1.3.72
- *    1.0: use fixed device number, consolidate /proc/apm into this file,
- *         Linux 1.3.85
- *    1.1: support user-space standby and suspend, power off after system
- *         halted, Linux 1.3.98
- *    1.2: When resetting RTC after resume, take care so that the time
- *         is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
- *         <jtoth@princeton.edu>); improve interaction between
- *         screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
- *    1.2a:Simple change to stop mysterious bug reports with SMP also added
- *	   levels to the printk calls. APM is not defined for SMP machines.
- *         The new replacment for it is, but Linux doesn't yet support this.
- *         Alan Cox Linux 2.1.55
- *    1.3: Set up a valid data descriptor 0x40 for buggy BIOS's
- *    1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by 
- *         Dean Gaudet <dgaudet@arctic.org>.
- *         C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87
- *    1.5: Fix segment register reloading (in case of bad segments saved
- *         across BIOS call).
- *         Stephen ROthwell
- *
- * APM 1.1 Reference:
- *
- *   Intel Corporation, Microsoft Corporation. Advanced Power Management
- *   (APM) BIOS Interface Specification, Revision 1.1, September 1993.
- *   Intel Order Number 241704-001.  Microsoft Part Number 781-110-X01.
- *
- * [This document is available free from Intel by calling 800.628.8686 (fax
- * 916.356.6100) or 800.548.4725; or via anonymous ftp from
- * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc.  It is also
- * available from Microsoft by calling 206.882.8080.]
- *
- * APM 1.2 Reference:
- *   Intel Corporation, Microsoft Corporation. Advanced Power Management
- *   (APM) BIOS Interface Specification, Revision 1.2, February 1996.
- *
- * [This document is available from Intel at:
- *    http://www.intel.com/IAL/powermgm
- *  or Microsoft at
- *    http://www.microsoft.com/windows/thirdparty/hardware/pcfuture.htm
- * ]
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-
-#include <asm/system.h>
-#include <asm/uaccess.h>
-
-#include <linux/poll.h>
-#include <linux/types.h>
-#include <linux/stddef.h>
-#include <linux/timer.h>
-#include <linux/fcntl.h>
-#include <linux/malloc.h>
-#include <linux/linkage.h>
-#ifdef CONFIG_PROC_FS
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-#endif
-#include <linux/miscdevice.h>
-#include <linux/apm_bios.h>
-#include <linux/init.h>
-
-EXPORT_SYMBOL(apm_register_callback);
-EXPORT_SYMBOL(apm_unregister_callback);
-
-extern unsigned long get_cmos_time(void);
-
-/*
- * The apm_bios device is one of the misc char devices.
- * This is its minor number.
- */
-#define	APM_MINOR_DEV	134
-
-/* Configurable options:
- *
- * CONFIG_APM_IGNORE_USER_SUSPEND: define to ignore USER SUSPEND requests.
- * This is necessary on the NEC Versa M series, which generates these when
- * resuming from SYSTEM SUSPEND.  However, enabling this on other laptops
- * will cause the laptop to generate a CRITICAL SUSPEND when an appropriate
- * USER SUSPEND is ignored -- this may prevent the APM driver from updating
- * the system time on a RESUME.
- *
- * CONFIG_APM_DO_ENABLE: enable APM features at boot time.  From page 36 of
- * the specification: "When disabled, the APM BIOS does not automatically
- * power manage devices, enter the Standby State, enter the Suspend State,
- * or take power saving steps in response to CPU Idle calls."  This driver
- * will make CPU Idle calls when Linux is idle (unless this feature is
- * turned off -- see below).  This should always save battery power, but
- * more complicated APM features will be dependent on your BIOS
- * implementation.  You may need to turn this option off if your computer
- * hangs at boot time when using APM support, or if it beeps continuously
- * instead of suspending.  Turn this off if you have a NEC UltraLite Versa
- * 33/C or a Toshiba T400CDT.  This is off by default since most machines
- * do fine without this feature.
- *
- * CONFIG_APM_CPU_IDLE: enable calls to APM CPU Idle/CPU Busy inside the
- * idle loop.  On some machines, this can activate improved power savings,
- * such as a slowed CPU clock rate, when the machine is idle.  These idle
- * call is made after the idle loop has run for some length of time (e.g.,
- * 333 mS).  On some machines, this will cause a hang at boot time or
- * whenever the CPU becomes idle.
- *
- * CONFIG_APM_DISPLAY_BLANK: enable console blanking using the APM.  Some
- * laptops can use this to turn of the LCD backlight when the VC screen
- * blanker blanks the screen.  Note that this is only used by the VC screen
- * blanker, and probably won't turn off the backlight when using X11.  Some
- * problems have been reported when using this option with gpm (if you'd
- * like to debug this, please do so).
- *
- * CONFIG_APM_IGNORE_MULTIPLE_SUSPEND: The IBM TP560 bios seems to insist
- * on returning multiple suspend/standby events whenever one occurs.  We
- * really only need one at a time, so just ignore any beyond the first.
- * This is probably safe on most laptops.
- *
- * If you are debugging the APM support for your laptop, note that code for
- * all of these options is contained in this file, so you can #define or
- * #undef these on the next line to avoid recompiling the whole kernel.
- *
- */
-
-/* KNOWN PROBLEM MACHINES:
- *
- * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
- *                         [Confirmed by TI representative]
- * U: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
- *                    [Confirmed by BIOS disassembly]
- *                    [This may work now ...]
- * P: Toshiba 1950S: battery life information only gets updated after resume
- * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking
- * 	broken in BIOS [Reported by Garst R. Reese <reese@isn.net>]
- *
- * Legend: U = unusable with APM patches
- *         P = partially usable with APM patches
- */
-
-/*
- * Define to have debug messages.
- */
-#undef APM_DEBUG
-
-/*
- * Define to always call the APM BIOS busy routine even if the clock was
- * not slowed by the idle routine.
- */
-#define ALWAYS_CALL_BUSY
-
-/*
- * Define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call
- * should turn interrupts on before it does a 'hlt').
- * This reportedly needs undefining for the ThinkPad 600.
- */
-#define APM_NOINTS
-
-/*
- * Define to make the APM BIOS calls zero all data segment registers (so
- * that an incorrect BIOS implementation will cause a kernel panic if it
- * tries to write to arbitrary memory).
- */
-#define APM_ZERO_SEGS
-
-/*
- * Define to make all set_limit calls use 64k limits.  The APM 1.1 BIOS is
- * supposed to provide limit information that it recognizes.  Many machines
- * do this correctly, but many others do not restrict themselves to their
- * claimed limit.  When this happens, they will cause a segmentation
- * violation in the kernel at boot time.  Most BIOS's, however, will
- * respect a 64k limit, so we use that.  If you want to be pedantic and
- * hold your BIOS to its claims, then undefine this.
- */
-#define APM_RELAX_SEGMENTS
-
-/*
- * Need to poll the APM BIOS every second
- */
-#define APM_CHECK_TIMEOUT	(HZ)
-
-/*
- * Save a segment register away
- */
-#define savesegment(seg, where)	__asm__ __volatile__("movw %%" #seg ", %0\n" : "=m" (where))
-
-/*
- * Forward declarations
- */
-static void	suspend(void);
-static void	standby(void);
-static void	set_time(void);
-
-static void	check_events(void);
-static void	do_apm_timer(unsigned long);
-
-static int	do_open(struct inode *, struct file *);
-static int	do_release(struct inode *, struct file *);
-static ssize_t	do_read(struct file *, char *, size_t , loff_t *);
-static unsigned int do_poll(struct file *, poll_table *);
-static int	do_ioctl(struct inode *, struct file *, u_int, u_long);
-
-#ifdef CONFIG_PROC_FS
-static int	apm_get_info(char *, char **, off_t, int, int);
-#endif
-
-extern int	apm_register_callback(int (*)(apm_event_t));
-extern void	apm_unregister_callback(int (*)(apm_event_t));
-
-/*
- * Local variables
- */
-static asmlinkage struct {
-	unsigned long	offset;
-	unsigned short	segment;
-}				apm_bios_entry;
-static int			apm_enabled = 0;
-#ifdef CONFIG_APM_CPU_IDLE
-static int			clock_slowed = 0;
-#endif
-static int			suspends_pending = 0;
-static int			standbys_pending = 0;
-#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
-static int			waiting_for_resume = 0;
-#endif
-
-static long			clock_cmos_diff;
-static int			got_clock_diff = 0;
-
-static struct wait_queue *	process_list = NULL;
-static struct apm_bios_struct *	user_list = NULL;
-
-static struct timer_list	apm_timer;
-
-static char			driver_version[] = "1.5";	/* no spaces */
-
-#ifdef APM_DEBUG
-static char *	apm_event_name[] = {
-	"system standby",
-	"system suspend",
-	"normal resume",
-	"critical resume",
-	"low battery",
-	"power status change",
-	"update time",
-	"critical suspend",
-	"user standby",
-	"user suspend",
-	"system standby resume",
-	"capabilities change"
-};
-#define NR_APM_EVENT_NAME	\
-		(sizeof(apm_event_name) / sizeof(apm_event_name[0]))
-#endif
-
-static struct file_operations apm_bios_fops = {
-	NULL,		/* lseek */
-	do_read,
-	NULL,		/* write */
-	NULL,		/* readdir */
-	do_poll,
-	do_ioctl,
-	NULL,		/* mmap */
-	do_open,
-	NULL,		/* flush */
-	do_release,
-	NULL,		/* fsync */
-	NULL		/* fasync */
-};
-
-static struct miscdevice apm_device = {
-	APM_MINOR_DEV,
-	"apm",
-	&apm_bios_fops
-};
-
-typedef struct callback_list_t {
-	int (*				callback)(apm_event_t);
-	struct callback_list_t *	next;
-} callback_list_t;
-
-static callback_list_t *	callback_list = NULL;
-
-typedef struct lookup_t {
-	int	key;
-	char *	msg;
-} lookup_t;
-
-static const lookup_t error_table[] = {
-/* N/A	{ APM_SUCCESS,		"Operation succeeded" }, */
-	{ APM_DISABLED,		"Power management disabled" },
-	{ APM_CONNECTED,	"Real mode interface already connected" },
-	{ APM_NOT_CONNECTED,	"Interface not connected" },
-	{ APM_16_CONNECTED,	"16 bit interface already connected" },
-/* N/A	{ APM_16_UNSUPPORTED,	"16 bit interface not supported" }, */
-	{ APM_32_CONNECTED,	"32 bit interface already connected" },
-	{ APM_32_UNSUPPORTED,	"32 bit interface not supported" },
-	{ APM_BAD_DEVICE,	"Unrecognized device ID" },
-	{ APM_BAD_PARAM,	"Parameter out of range" },
-	{ APM_NOT_ENGAGED,	"Interface not engaged" },
-	{ APM_BAD_FUNCTION,     "Function not supported" },
-	{ APM_RESUME_DISABLED,	"Resume timer disabled" },
-	{ APM_BAD_STATE,	"Unable to enter requested state" },
-/* N/A	{ APM_NO_EVENTS,	"No events pending" }, */
-	{ APM_NOT_PRESENT,	"No APM present" }
-};
-#define ERROR_COUNT	(sizeof(error_table)/sizeof(lookup_t))
-
-/*
- * These are the actual BIOS calls.  Depending on APM_ZERO_SEGS
- * and APM_NOINTS, we are being really paranoid here!  Not only are
- * interrupts disabled, but all the segment registers (except SS) are
- * saved and zeroed this means that if the BIOS tries to reference any
- * data without explicitly loading the segment registers, the kernel will
- * fault immediately rather than have some unforeseen circumstances for
- * the rest of the kernel.  And it will be very obvious!  :-) Doing this
- * depends on CS referring to the same physical memory as DS so that DS
- * can be zeroed before the call. Unfortunately, we can't do anything
- * about the stack segment/pointer.  Also, we tell the compiler that
- * everything could change.
- */
-
-static inline int apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in,
-	u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi)
-{
-	u16	old_fs;
-	u16	old_gs;
-	int	error;
-
-#ifdef APM_ZERO_SEGS
-	savesegment(fs, old_fs);
-	savesegment(gs, old_gs);
-#endif
-	__asm__ __volatile__(
-		"pushfl\n\t"
-#ifdef APM_NOINTS
-		"cli\n\t"
-#endif
-#ifdef APM_ZERO_SEGS
-		"pushl %%ds\n\t"
-		"pushl %%es\n\t"
-		"movw %9, %%ds\n\t"
-		"movw %9, %%es\n\t"
-		"movw %9, %%fs\n\t"
-		"movw %9, %%gs\n\t"
-#endif
-		"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
-		"movl $0, %%edi\n\t"
-		"jnc 1f\n\t"
-		"movl $1, %%edi\n"
-		"1:\tpopl %%es\n\t"
-		"popl %%ds\n\t"
-		"popfl\n\t"
-		:  "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx),
-		   "=S" (*esi), "=D" (error)
-		: "a" (eax_in), "b" (ebx_in), "c" (ecx_in)
-#ifdef APM_ZERO_SEGS
-		  , "r" (0)
-#endif
-		: "ax", "bx", "cx", "dx", "si", "di", "bp", "memory");
-#ifdef APM_ZERO_SEGS
-	loadsegment(fs, old_fs);
-	loadsegment(gs, old_gs);
-#endif
-	return error;
-}
-
-/*
- * This version only returns one value (usually an error code)
- */
-
-static inline int apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in, u32 *eax)
-{
-	u16	old_fs;
-	u16	old_gs;
-	int	error;
-
-#ifdef APM_ZERO_SEGS
-	savesegment(fs, old_fs);
-	savesegment(gs, old_gs);
-#endif
-	__asm__ __volatile__(
-		"pushfl\n\t"
-#ifdef APM_NOINTS
-		"cli\n\t"
-#endif
-#ifdef APM_ZERO_SEGS
-		"pushl %%ds\n\t"
-		"pushl %%es\n\t"
-		"movw %5, %%ds\n\t"
-		"movw %5, %%es\n\t"
-		"movw %5, %%fs\n\t"
-		"movw %5, %%gs\n\t"
-#endif
-		"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
-		"movl $0, %%edi\n\t"
-		"jnc 1f\n\t"
-		"movl $1, %%edi\n"
-		"1:\tpopl %%es\n\t"
-		"popl %%ds\n\t"
-		"popfl\n\t"
-		: "=a" (*eax), "=D" (error)
-		: "a" (eax_in), "b" (ebx_in), "c" (ecx_in)
-#ifdef APM_ZERO_SEGS
-		  , "r" (0)
-#endif
-		: "ax", "bx", "cx", "dx", "si", "di", "bp", "memory");
-#ifdef APM_ZERO_SEGS
-	loadsegment(fs, old_fs);
-	loadsegment(gs, old_gs);
-#endif
-	return error;
-}
-
-static int apm_driver_version(u_short *val)
-{
-	int	error;
-	u32	eax;
-
-	error = apm_bios_call_simple(0x530e, 0, *val, &eax);
-	if (error)
-		return (eax >> 8) & 0xff;
-	*val = eax;
-	return APM_SUCCESS;
-}
-
-static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info)
-{
-	int	error;
-	u32	eax;
-	u32	ebx;
-	u32	ecx;
-	u32	dummy;
-
-	error = apm_bios_call(0x530b, 0, 0, &eax, &ebx, &ecx, &dummy, &dummy);
-	if (error)
-		return (eax >> 8) & 0xff;
-	*event = ebx;
-	if (apm_bios_info.version < 0x0102)
-		*info = ~0; /* indicate info not valid */
-	else
-		*info = ecx;
-	return APM_SUCCESS;
-}
-
-static int set_power_state(u_short what, u_short state)
-{
-	int	error;
-	u32	eax;
-
-	error = apm_bios_call_simple(0x5307, what, state, &eax);
-	if (error)
-		return (eax >> 8) & 0xff;
-	return APM_SUCCESS;
-}
-
-int apm_set_power_state(u_short state)
-{
-	return set_power_state(0x0001, state);
-}
-
-#ifdef CONFIG_APM_DISPLAY_BLANK
-/* Called by apm_display_blank and apm_display_unblank when apm_enabled. */
-static int apm_set_display_power_state(u_short state)
-{
-	return set_power_state(0x01ff, state);
-}
-#endif
-
-#ifdef CONFIG_APM_DO_ENABLE
-/* Called by apm_setup if apm_enabled will be true. */
-static int apm_enable_power_management(void)
-{
-	int	error;
-	u32	eax;
-
-	error = apm_bios_call_simple(0x5308,
-			(apm_bios_info.version > 0x100) ? 0x0001 : 0xffff, 1, &eax);
-	if (error)
-		return (eax >> 8) & 0xff;
-	return APM_SUCCESS;
-}
-#endif
-
-static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
-{
-	int	error;
-	u32	eax;
-	u32	ebx;
-	u32	ecx;
-	u32	edx;
-	u32	dummy;
-
-	error = apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy);
-	if (error)
-		return (eax >> 8) & 0xff;
-	*status = ebx;
-	*bat = ecx;
-	*life = edx;
-	return APM_SUCCESS;
-}
-
-#if 0
-/* not used anywhere */
-static int apm_get_battery_status(u_short which, 
-				  u_short *bat, u_short *life, u_short *nbat)
-{
-	u_short status;
-	int	error;
-	u32	eax;
-	u32	ebx;
-	u32	ecx;
-	u32	edx;
-	u32	esi;
-
-	if (apm_bios_info.version < 0x0102) {
-		/* pretend we only have one battery. */
-		if (which != 1)
-			return APM_BAD_DEVICE;
-		*nbat = 1;
-		return apm_get_power_status(&status, bat, life);
-	}
-
-	error = apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax, &ebx, &ecx, &edx, &esi);
-	if (error)
-		return (eax >> 8) & 0xff;
-	*bat = ecx;
-	*life = edx;
-	*nbat = esi;
-	return APM_SUCCESS;
-}
-#endif
-
-static int apm_engage_power_management(u_short device)
-{
-	int	error;
-	u32	eax;
-
-	error = apm_bios_call_simple(0x530f, device, 1, &eax);
-	if (error)
-		return (eax >> 8) & 0xff;
-	return APM_SUCCESS;
-}
-
-static void apm_error(char *str, int err)
-{
-	int	i;
-
-	for (i = 0; i < ERROR_COUNT; i++)
-		if (error_table[i].key == err) break;
-	if (i < ERROR_COUNT)
-		printk(KERN_NOTICE "apm_bios: %s: %s\n", str, error_table[i].msg);
-	else
-		printk(KERN_NOTICE "apm_bios: %s: unknown error code %#2.2x\n", str, err);
-}
-
-/* Called from console driver -- must make sure apm_enabled. */
-int apm_display_blank(void)
-{
-#ifdef CONFIG_APM_DISPLAY_BLANK
-	int	error;
-
-	if (!apm_enabled)
-		return 0;
-	error = apm_set_display_power_state(APM_STATE_STANDBY);
-	if (error == APM_SUCCESS)
-		return 1;
-	apm_error("set display standby", error);
-#endif
-	return 0;
-}
-
-/* Called from console driver -- must make sure apm_enabled. */
-int apm_display_unblank(void)
-{
-#ifdef CONFIG_APM_DISPLAY_BLANK
-	int error;
-
-	if (!apm_enabled)
-		return 0;
-	error = apm_set_display_power_state(APM_STATE_READY);
-	if (error == APM_SUCCESS)
-		return 1;
-	apm_error("set display ready", error);
-#endif
-	return 0;
-}
-
-int apm_register_callback(int (*callback)(apm_event_t))
-{
-	callback_list_t *	new;
-
-	new = kmalloc(sizeof(callback_list_t), GFP_KERNEL);
-	if (new == NULL)
-		return -ENOMEM;
-	new->callback = callback;
-	new->next = callback_list;
-	callback_list = new;
-	return 0;
-}
-
-void apm_unregister_callback(int (*callback)(apm_event_t))
-{
-	callback_list_t **	ptr;
-	callback_list_t *	old;
-
-	ptr = &callback_list;
-	for (ptr = &callback_list; *ptr != NULL; ptr = &(*ptr)->next)
-		if ((*ptr)->callback == callback)
-			break;
-	old = *ptr;
-	*ptr = old->next;
-	kfree_s(old, sizeof(callback_list_t));
-}
-
-static int queue_empty(struct apm_bios_struct * as)
-{
-	return as->event_head == as->event_tail;
-}
-
-static apm_event_t get_queued_event(struct apm_bios_struct * as)
-{
-	as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
-	return as->events[as->event_tail];
-}
-
-static int queue_event(apm_event_t event, struct apm_bios_struct *sender)
-{
-	struct apm_bios_struct *	as;
-
-	if (user_list == NULL)
-		return 0;
-	for (as = user_list; as != NULL; as = as->next) {
-		if (as == sender)
-			continue;
-		as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
-		if (as->event_head == as->event_tail) {
-			static int notified;
-
-			if (notified == 0) {
-			    printk( "apm_bios: an event queue overflowed\n" );
-			    notified = 1;
-			}
-			as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
-		}
-		as->events[as->event_head] = event;
-		if (!as->suser)
-			continue;
-		switch (event) {
-		case APM_SYS_SUSPEND:
-		case APM_USER_SUSPEND:
-			as->suspends_pending++;
-			suspends_pending++;
-			break;
-
-		case APM_SYS_STANDBY:
-		case APM_USER_STANDBY:
-			as->standbys_pending++;
-			standbys_pending++;
-			break;
-		}
-	}
-	wake_up_interruptible(&process_list);
-	return 1;
-}
-
-static void set_time(void)
-{
-	unsigned long	flags;
-
-	if (!got_clock_diff)	/* Don't know time zone, can't set clock */
-		return;
-
-	save_flags(flags);
-	cli();
-	CURRENT_TIME = get_cmos_time() + clock_cmos_diff;
-	restore_flags(flags);
-}
-
-static void suspend(void)
-{
-	unsigned long	flags;
-	int		err;
-
-				/* Estimate time zone so that set_time can
-                                   update the clock */
-	save_flags(flags);
-	clock_cmos_diff = -get_cmos_time();
-	cli();
-	clock_cmos_diff += CURRENT_TIME;
-	got_clock_diff = 1;
-	restore_flags(flags);
-
-	err = apm_set_power_state(APM_STATE_SUSPEND);
-	if (err)
-		apm_error("suspend", err);
-	set_time();
-}
-
-static void standby(void)
-{
-	int	err;
-
-	err = apm_set_power_state(APM_STATE_STANDBY);
-	if (err)
-		apm_error("standby", err);
-}
-
-static apm_event_t get_event(void)
-{
-	int		error;
-	apm_event_t	event;
-	apm_eventinfo_t	info;
-
-	static int notified = 0;
-
-	/* we don't use the eventinfo */
-	error = apm_get_event(&event, &info);
-	if (error == APM_SUCCESS)
-		return event;
-
-	if ((error != APM_NO_EVENTS) && (notified++ == 0))
-		apm_error("get_event", error);
-
-	return 0;
-}
-
-static void send_event(apm_event_t event, apm_event_t undo,
-		       struct apm_bios_struct *sender)
-{
-	callback_list_t *	call;
-	callback_list_t *	fix;
-
-	for (call = callback_list; call != NULL; call = call->next) {
-		if (call->callback(event) && undo) {
-			for (fix = callback_list; fix != call; fix = fix->next)
-				fix->callback(undo);
-			if (apm_bios_info.version > 0x100)
-				apm_set_power_state(APM_STATE_REJECT);
-			return;
-		}
-	}
-
-	queue_event(event, sender);
-}
-
-static void check_events(void)
-{
-	apm_event_t	event;
-
-	while ((event = get_event()) != 0) {
-#ifdef APM_DEBUG
-		if (event <= NR_APM_EVENT_NAME)
-			printk(KERN_DEBUG "APM BIOS received %s notify\n",
-			       apm_event_name[event - 1]);
-		else
-			printk(KERN_DEBUG "APM BIOS received unknown "
-			       "event 0x%02x\n", event);
-#endif
-		switch (event) {
-		case APM_SYS_STANDBY:
-		case APM_USER_STANDBY:
-#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
-			if (waiting_for_resume) {
-			    return;
-			}
-			waiting_for_resume = 1;
-#endif
-			send_event(event, APM_STANDBY_RESUME, NULL);
-			if (standbys_pending <= 0)
-				standby();
-			break;
-
-		case APM_USER_SUSPEND:
-#ifdef CONFIG_APM_IGNORE_USER_SUSPEND
-			if (apm_bios_info.version > 0x100)
-				apm_set_power_state(APM_STATE_REJECT);
-			break;
-#endif
-		case APM_SYS_SUSPEND:
-#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
-			if (waiting_for_resume) {
-			    return;
-			}
-			waiting_for_resume = 1;
-#endif
-			send_event(event, APM_NORMAL_RESUME, NULL);
-			if (suspends_pending <= 0)
-				suspend();
-			break;
-
-		case APM_NORMAL_RESUME:
-		case APM_CRITICAL_RESUME:
-		case APM_STANDBY_RESUME:
-#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
-			waiting_for_resume = 0;
-#endif
-			set_time();
-			send_event(event, 0, NULL);
-			break;
-
-		case APM_LOW_BATTERY:
-		case APM_POWER_STATUS_CHANGE:
-		case APM_CAPABILITY_CHANGE:
-			send_event(event, 0, NULL);
-			break;
-
-		case APM_UPDATE_TIME:
-			set_time();
-			break;
-
-		case APM_CRITICAL_SUSPEND:
-			suspend();
-			break;
-		}
-	}
-}
-
-static void do_apm_timer(unsigned long unused)
-{
-	int	err;
-
-	static int	pending_count = 0;
-
-	if (((standbys_pending > 0) || (suspends_pending > 0))
-	    && (apm_bios_info.version > 0x100)
-	    && (pending_count-- <= 0)) {
-		pending_count = 4;
-
-		err = apm_set_power_state(APM_STATE_BUSY);
-		if (err)
-			apm_error("busy", err);
-	}
-
-	if (!(((standbys_pending > 0) || (suspends_pending > 0))
-	      && (apm_bios_info.version == 0x100)))
-		check_events();
-
-	init_timer(&apm_timer);
-	apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
-	add_timer(&apm_timer);
-}
-
-/* Called from sys_idle, must make sure apm_enabled. */
-int apm_do_idle(void)
-{
-#ifdef CONFIG_APM_CPU_IDLE
-	int	error;
-	u32	dummy;
-
-	if (!apm_enabled)
-		return 0;
-
-	error = apm_bios_call_simple(0x5305, 0, 0, &dummy);
-	if (error)
-		return 0;
-
-	clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
-	return 1;
-#else
-	return 0;
-#endif
-}
-
-/* Called from sys_idle, must make sure apm_enabled. */
-void apm_do_busy(void)
-{
-#ifdef CONFIG_APM_CPU_IDLE
-	u32	dummy;
-
-	if (apm_enabled
-#ifndef ALWAYS_CALL_BUSY
-		&& clock_slowed
-#endif
-	) {
-		(void) apm_bios_call_simple(0x5306, 0, 0, &dummy);
-		clock_slowed = 0;
-	}
-#endif
-}
-
-static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
-{
-	if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
-		printk(KERN_ERR "apm_bios: %s passed bad filp", func);
-		return 1;
-	}
-	return 0;
-}
-
-static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
-{
-	struct apm_bios_struct *	as;
-	int			i;
-	apm_event_t		event;
-	struct wait_queue	wait = { current,	NULL };
-
-	as = fp->private_data;
-	if (check_apm_bios_struct(as, "read"))
-		return -EIO;
-	if (count < sizeof(apm_event_t))
-		return -EINVAL;
-	if (queue_empty(as)) {
-		if (fp->f_flags & O_NONBLOCK)
-			return -EAGAIN;
-		add_wait_queue(&process_list, &wait);
-repeat:
-		current->state = TASK_INTERRUPTIBLE;
-		if (queue_empty(as) && !signal_pending(current)) {
-			schedule();
-			goto repeat;
-		}
-		current->state = TASK_RUNNING;
-		remove_wait_queue(&process_list, &wait);
-	}
-	i = count;
-	while ((i >= sizeof(event)) && !queue_empty(as)) {
-		event = get_queued_event(as);
-		copy_to_user(buf, &event, sizeof(event));
-		switch (event) {
-		case APM_SYS_SUSPEND:
-		case APM_USER_SUSPEND:
-			as->suspends_read++;
-			break;
-
-		case APM_SYS_STANDBY:
-		case APM_USER_STANDBY:
-			as->standbys_read++;
-			break;
-		}
-		buf += sizeof(event);
-		i -= sizeof(event);
-	}
-	if (i < count)
-		return count - i;
-	if (signal_pending(current))
-		return -ERESTARTSYS;
-	return 0;
-}
-
-static unsigned int do_poll(struct file *fp, poll_table * wait)
-{
-	struct apm_bios_struct * as;
-
-	as = fp->private_data;
-	if (check_apm_bios_struct(as, "select"))
-		return 0;
-	poll_wait(fp, &process_list, wait);
-	if (!queue_empty(as))
-		return POLLIN | POLLRDNORM;
-	return 0;
-}
-
-static int do_ioctl(struct inode * inode, struct file *filp,
-		    u_int cmd, u_long arg)
-{
-	struct apm_bios_struct *	as;
-
-	as = filp->private_data;
-	if (check_apm_bios_struct(as, "ioctl"))
-		return -EIO;
-	if (!as->suser)
-		return -EPERM;
-	switch (cmd) {
-	case APM_IOC_STANDBY:
-		if (as->standbys_read > 0) {
-			as->standbys_read--;
-			as->standbys_pending--;
-			standbys_pending--;
-		}
-		else
-			send_event(APM_USER_STANDBY, APM_STANDBY_RESUME, as);
-		if (standbys_pending <= 0)
-			standby();
-		break;
-	case APM_IOC_SUSPEND:
-		if (as->suspends_read > 0) {
-			as->suspends_read--;
-			as->suspends_pending--;
-			suspends_pending--;
-		}
-		else
-			send_event(APM_USER_SUSPEND, APM_NORMAL_RESUME, as);
-		if (suspends_pending <= 0)
-			suspend();
-		break;
-	default:
-		return -EINVAL;
-	}
-	return 0;
-}
-
-static int do_release(struct inode * inode, struct file * filp)
-{
-	struct apm_bios_struct *	as;
-
-	as = filp->private_data;
-	filp->private_data = NULL;
-	if (check_apm_bios_struct(as, "release"))
-		return 0;
-	if (as->standbys_pending > 0) {
-		standbys_pending -= as->standbys_pending;
-		if (standbys_pending <= 0)
-			standby();
-	}
-	if (as->suspends_pending > 0) {
-		suspends_pending -= as->suspends_pending;
-		if (suspends_pending <= 0)
-			suspend();
-	}
-	if (user_list == as)
-		user_list = as->next;
-	else {
-		struct apm_bios_struct *	as1;
-
-		for (as1 = user_list;
-		     (as1 != NULL) && (as1->next != as);
-		     as1 = as1->next)
-			;
-		if (as1 == NULL)
-			printk(KERN_ERR "apm_bios: filp not in user list");
-		else
-			as1->next = as->next;
-	}
-	kfree_s(as, sizeof(*as));
-	return 0;
-}
-
-static int do_open(struct inode * inode, struct file * filp)
-{
-	struct apm_bios_struct *	as;
-
-	as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
-	if (as == NULL) {
-		printk(KERN_ERR "apm_bios: cannot allocate struct of size %d bytes",
-		       sizeof(*as));
-		return -ENOMEM;
-	}
-	as->magic = APM_BIOS_MAGIC;
-	as->event_tail = as->event_head = 0;
-	as->suspends_pending = as->standbys_pending = 0;
-	as->suspends_read = as->standbys_read = 0;
-	/*
-	 * XXX - this is a tiny bit broken, when we consider BSD
-         * process accounting. If the device is opened by root, we
-	 * instantly flag that we used superuser privs. Who knows,
-	 * we might close the device immediately without doing a
-	 * privileged operation -- cevans
-	 */
-	as->suser = capable(CAP_SYS_ADMIN);
-	as->next = user_list;
-	user_list = as;
-	filp->private_data = as;
-	return 0;
-}
-
-#ifdef CONFIG_PROC_FS
-int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
-{
-	char *		p;
-	unsigned short	bx;
-	unsigned short	cx;
-	unsigned short	dx;
-	unsigned short	error;
-	unsigned short  ac_line_status = 0xff;
-	unsigned short  battery_status = 0xff;
-	unsigned short  battery_flag   = 0xff;
-	int		percentage     = -1;
-	int             time_units     = -1;
-	char            *units         = "?";
-
-	if (!apm_enabled)
-		return 0;
-	p = buf;
-
-	if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
-		ac_line_status = (bx >> 8) & 0xff;
-		battery_status = bx & 0xff;
-		if ((cx & 0xff) != 0xff)
-			percentage = cx & 0xff;
-
-		if (apm_bios_info.version > 0x100) {
-			battery_flag = (cx >> 8) & 0xff;
-			if (dx != 0xffff) {
-				if ((dx & 0x8000) == 0x8000) {
-					units = "min";
-					time_units = dx & 0x7ffe;
-				} else {
-					units = "sec";
-					time_units = dx & 0x7fff;
-				}
-			}
-		}
-	}
-	/* Arguments, with symbols from linux/apm_bios.h.  Information is
-	   from the Get Power Status (0x0a) call unless otherwise noted.
-
-	   0) Linux driver version (this will change if format changes)
-	   1) APM BIOS Version.  Usually 1.0 or 1.1.
-	   2) APM flags from APM Installation Check (0x00):
-	      bit 0: APM_16_BIT_SUPPORT
-	      bit 1: APM_32_BIT_SUPPORT
-	      bit 2: APM_IDLE_SLOWS_CLOCK
-	      bit 3: APM_BIOS_DISABLED
-	      bit 4: APM_BIOS_DISENGAGED
-	   3) AC line status
-	      0x00: Off-line
-	      0x01: On-line
-	      0x02: On backup power (APM BIOS 1.1 only)
-	      0xff: Unknown
-	   4) Battery status
-	      0x00: High
-	      0x01: Low
-	      0x02: Critical
-	      0x03: Charging
-	      0xff: Unknown
-	   5) Battery flag
-	      bit 0: High
-	      bit 1: Low
-	      bit 2: Critical
-	      bit 3: Charging
-	      bit 7: No system battery
-	      0xff: Unknown
-	   6) Remaining battery life (percentage of charge):
-	      0-100: valid
-	      -1: Unknown
-	   7) Remaining battery life (time units):
-	      Number of remaining minutes or seconds
-	      -1: Unknown
-	   8) min = minutes; sec = seconds */
-
-	p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
-		     driver_version,
-		     (apm_bios_info.version >> 8) & 0xff,
-		     apm_bios_info.version & 0xff,
-		     apm_bios_info.flags,
-		     ac_line_status,
-		     battery_status,
-		     battery_flag,
-		     percentage,
-		     time_units,
-		     units);
-
-	return p - buf;
-}
-#endif
-
-__initfunc(void apm_bios_init(void))
-{
-	unsigned short	bx;
-	unsigned short	cx;
-	unsigned short	dx;
-	unsigned short	error;
-	char *		power_stat;
-	char *		bat_stat;
-	static struct proc_dir_entry *ent;
-
-#ifdef __SMP__
-	if (smp_num_cpus > 1) {
-		printk(KERN_NOTICE "APM disabled: APM is not SMP safe.\n");
-		return;
-	}
-#endif
-	if (apm_bios_info.version == 0) {
-		printk(KERN_INFO "APM BIOS not found.\n");
-		return;
-	}
-	printk(KERN_INFO "APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n",
-	       ((apm_bios_info.version >> 8) & 0xff) + '0',
-	       (apm_bios_info.version & 0xff) + '0',
-	       apm_bios_info.flags,
-	       driver_version);
-	if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
-		printk(KERN_INFO "    No 32 bit BIOS support\n");
-		return;
-	}
-
-	/*
-	 * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
-	 * but is reportedly a 1.0 BIOS.
-	 */
-	if (apm_bios_info.version == 0x001)
-		apm_bios_info.version = 0x100;
-
-	/* BIOS < 1.2 doesn't set cseg_16_len */
-	if (apm_bios_info.version < 0x102)
-		apm_bios_info.cseg_16_len = 0xFFFF; /* 64k */
-
-	printk(KERN_INFO "    Entry %x:%lx cseg16 %x dseg %x",
-	       apm_bios_info.cseg, apm_bios_info.offset,
-	       apm_bios_info.cseg_16, apm_bios_info.dseg);
-	if (apm_bios_info.version > 0x100)
-		printk(" cseg len %x, cseg16 len %x, dseg len %x",
-		       apm_bios_info.cseg_len, apm_bios_info.cseg_16_len,
-		       apm_bios_info.dseg_len);
-	printk("\n");
-
-	/*
-	 * Set up a segment that references the real mode segment 0x40
-	 * that extends up to the end of page zero (that we have reserved).
-	 * This is for buggy BIOS's that refer to (real mode) segment 0x40
-	 * even though they are called in protected mode.
-	 */
-	set_base(gdt[APM_40 >> 3],
-		 __va((unsigned long)0x40 << 4));
-	set_limit(gdt[APM_40 >> 3], 4096 - (0x40 << 4));
-
-	apm_bios_entry.offset = apm_bios_info.offset;
-	apm_bios_entry.segment = APM_CS;
-	set_base(gdt[APM_CS >> 3],
-		 __va((unsigned long)apm_bios_info.cseg << 4));
-	set_base(gdt[APM_CS_16 >> 3],
-		 __va((unsigned long)apm_bios_info.cseg_16 << 4));
-	set_base(gdt[APM_DS >> 3],
-		 __va((unsigned long)apm_bios_info.dseg << 4));
-	if (apm_bios_info.version == 0x100) {
-		set_limit(gdt[APM_CS >> 3], 64 * 1024);
-		set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
-		set_limit(gdt[APM_DS >> 3], 64 * 1024);
-	} else {
-#ifdef APM_RELAX_SEGMENTS
-		/* For ASUS motherboard, Award BIOS rev 110 (and others?) */
-		set_limit(gdt[APM_CS >> 3], 64 * 1024);
-		/* For some unknown machine. */
-		set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
-		/* For the DEC Hinote Ultra CT475 (and others?) */
-		set_limit(gdt[APM_DS >> 3], 64 * 1024);
-#else
-		set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
-		set_limit(gdt[APM_CS_16 >> 3], apm_bios_info.cseg_16_len);
-		set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
-#endif
-		/* The APM 1.2 docs state that the apm_driver_version
-		 * call can fail if we try to connect as 1.2 to a 1.1 bios.
-		 */
-		apm_bios_info.version = 0x0102;
-		error = apm_driver_version(&apm_bios_info.version);
-		if (error != APM_SUCCESS) { /* Fall back to an APM 1.1 connection. */
-			apm_bios_info.version = 0x0101;
-			error = apm_driver_version(&apm_bios_info.version);
-		}
-		if (error != APM_SUCCESS) /* Fall back to an APM 1.0 connection. */
-			apm_bios_info.version = 0x100;
-		else {
-			apm_engage_power_management(0x0001);
-			printk( "    Connection version %d.%d\n",
-				(apm_bios_info.version >> 8) & 0xff,
-				apm_bios_info.version & 0xff );
-		}
-	}
-
-	error = apm_get_power_status(&bx, &cx, &dx);
-	if (error)
-		printk(KERN_INFO "    Power status not available\n");
-	else {
-		switch ((bx >> 8) & 0xff) {
-		case 0: power_stat = "off line"; break;
-		case 1: power_stat = "on line"; break;
-		case 2: power_stat = "on backup power"; break;
-		default: power_stat = "unknown"; break;
-		}
-		switch (bx & 0xff) {
-		case 0: bat_stat = "high"; break;
-		case 1: bat_stat = "low"; break;
-		case 2: bat_stat = "critical"; break;
-		case 3: bat_stat = "charging"; break;
-		default: bat_stat = "unknown"; break;
-		}
-		printk(KERN_INFO "    AC %s, battery status %s, battery life ",
-		       power_stat, bat_stat);
-		if ((cx & 0xff) == 0xff)
-			printk("unknown\n");
-		else
-			printk("%d%%\n", cx & 0xff);
-		if (apm_bios_info.version > 0x100) {
-			printk("    battery flag 0x%02x, battery life ",
-			       (cx >> 8) & 0xff);
-			if (dx == 0xffff)
-				printk("unknown\n");
-			else {
-				if ((dx & 0x8000))
-					printk("%d minutes\n", dx & 0x7ffe );
-				else
-					printk("%d seconds\n", dx & 0x7fff );
-			}
-		}
-	}
-
-#ifdef CONFIG_APM_DO_ENABLE
-	/*
-	 * This call causes my NEC UltraLite Versa 33/C to hang if it is
-	 * booted with PM disabled but not in the docking station.
-	 * Unfortunate ...
-	 */
-	error = apm_enable_power_management();
-	if (error)
-		apm_error("enable power management", error);
-	if (error == APM_DISABLED)
-		return;
-#endif
-
-	init_timer(&apm_timer);
-	apm_timer.function = do_apm_timer;
-	apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
-	add_timer(&apm_timer);
-
-#ifdef CONFIG_PROC_FS
-	ent = create_proc_entry("apm", 0, 0);
-	ent->get_info = apm_get_info;
-#endif
-
-	misc_register(&apm_device);
-
-	apm_enabled = 1;
-}
diff -u --recursive --new-file v2.1.121/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.1.121/linux/drivers/char/bttv.c	Wed Sep  9 14:51:07 1998
+++ linux/drivers/char/bttv.c	Thu Sep 10 16:33:15 1998
@@ -684,7 +684,7 @@
        BT848_COLOR_FMT_YCrCb422,
        BT848_COLOR_FMT_YCrCb411,
 };
-#define PALETTEFMT_MAX 11
+#define PALETTEFMT_MAX 15
 
 static int make_rawrisctab(struct bttv *btv, unsigned int *ro,
                             unsigned int *re, unsigned int *vbuf)
@@ -2924,7 +2924,7 @@
         printk("memory: 0x%08x.\n", btv->bt848_adr);
         
         btv->pll.pll_ifreq=0;
-        btv->pll.pll_ifreq=0;
+        btv->pll.pll_ofreq=0;
         btv->pll.pll_crystal=0;
         if(pll[btv->nr])
                 if (!(btv->id==848 && btv->revision==0x11))
diff -u --recursive --new-file v2.1.121/linux/drivers/net/3c523.c linux/drivers/net/3c523.c
--- v2.1.121/linux/drivers/net/3c523.c	Thu Mar 26 15:57:03 1998
+++ linux/drivers/net/3c523.c	Fri Sep 11 11:21:57 1998
@@ -474,7 +474,7 @@
 	to use.  I suspect that whoever sets the thing up initially would
 	prefer we don't screw with those things.
 
-	Note we we read the status info when we found the card...
+	Note that we read the status info when we found the card...
 
 	See 3c523.h for more details.
 	*/
diff -u --recursive --new-file v2.1.121/linux/drivers/net/82596.c linux/drivers/net/82596.c
--- v2.1.121/linux/drivers/net/82596.c	Tue Jul 21 00:15:31 1998
+++ linux/drivers/net/82596.c	Fri Sep 11 11:21:57 1998
@@ -1194,7 +1194,7 @@
 
 	while (lp->scb.command)
 		if (--boguscnt == 0) {
-			printk("%s: close1 timed timed out with status %4.4x, cmd %4.4x.\n",
+			printk("%s: close1 timed out with status %4.4x, cmd %4.4x.\n",
 			     dev->name, lp->scb.status, lp->scb.command);
 			break;
 		}
@@ -1205,7 +1205,7 @@
 
 	while (lp->scb.command)
 		if (--boguscnt == 0) {
-			printk("%s: close2 timed timed out with status %4.4x, cmd %4.4x.\n",
+			printk("%s: close2 timed out with status %4.4x, cmd %4.4x.\n",
 			     dev->name, lp->scb.status, lp->scb.command);
 			break;
 		}
diff -u --recursive --new-file v2.1.121/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.121/linux/drivers/net/Config.in	Wed Sep  9 14:51:08 1998
+++ linux/drivers/net/Config.in	Thu Sep 10 16:33:15 1998
@@ -152,19 +152,17 @@
 #
 # AppleTalk
 #
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  if [ "$CONFIG_ATALK" != "n" ]; then
-    tristate 'Apple/Farallon LocalTalk PC support' CONFIG_LTPC
-    tristate 'COPS LocalTalk PC support' CONFIG_COPS
-    if [ "$CONFIG_COPS" != "n" ]; then
-	bool 'Dayna firmware support' CONFIG_COPS_DAYNA
-	bool 'Tangent firmware support' CONFIG_COPS_TANGENT
-    fi
-    tristate 'Appletalk-IP driver support' CONFIG_IPDDP
-    if [ "$CONFIG_IPDDP" != "n" ]; then
-       bool 'IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP
-       bool 'Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP
-    fi
+if [ "$CONFIG_ATALK" != "n" ]; then
+  dep_tristate 'Apple/Farallon LocalTalk PC support' CONFIG_LTPC $CONFIG_ATALK
+  dep_tristate 'COPS LocalTalk PC support' CONFIG_COPS $CONFIG_ATALK
+  if [ "$CONFIG_COPS" != "n" ]; then
+     bool 'Dayna firmware support' CONFIG_COPS_DAYNA
+     bool 'Tangent firmware support' CONFIG_COPS_TANGENT
+  fi
+  dep_tristate 'Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_ATALK
+  if [ "$CONFIG_IPDDP" != "n" ]; then
+     bool 'IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP
+     bool 'Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP
   fi
 fi
 
diff -u --recursive --new-file v2.1.121/linux/drivers/net/arc-rimi.c linux/drivers/net/arc-rimi.c
--- v2.1.121/linux/drivers/net/arc-rimi.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/net/arc-rimi.c	Mon Sep 14 11:32:22 1998
@@ -4,9 +4,6 @@
         Written 1994-1996 by Avery Pennarun,
 	which was in turn derived from skeleton.c by Donald Becker.
 
-	Contact Avery at: apenwarr@bond.net or
-	RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
-
 	**********************
 
 	The original copyright of skeleton.c was as follows:
@@ -130,7 +127,7 @@
 #define SETCONF	writeb(lp->config,_CONFIG)
 
 static const char *version =
-"arc-rimi.c: v3.00 97/11/09 Avery Pennarun <apenwarr@bond.net> et al.\n";
+"arc-rimi.c: v3.00 97/11/09 Avery Pennarun <apenwarr@worldvisions.ca> et al.\n";
 
 /****************************************************************************
  *                                                                          *
diff -u --recursive --new-file v2.1.121/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c
--- v2.1.121/linux/drivers/net/arcnet.c	Sat Sep  5 16:46:40 1998
+++ linux/drivers/net/arcnet.c	Mon Sep 14 11:32:22 1998
@@ -3,9 +3,6 @@
 	Written 1994-1996 by Avery Pennarun,
 	derived from skeleton.c by Donald Becker.
 
-	Contact Avery at: apenwarr@bond.net or
-	RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
-
 	**********************
 
 	The original copyright was as follows:
@@ -88,7 +85,7 @@
 
 	The following has been SUMMARIZED.  The complete ChangeLog is
 	available in the full Linux-ARCnet package at
-		http://www.foxnet.net/~apenwarr/arcnet
+		http://www.worldvisions.ca/~apenwarr/arcnet
 
 	v2.50 (96/02/24)
 	  - Massively improved autoprobe routines; they now work even as a
@@ -181,7 +178,7 @@
 */
 
 static const char *version =
- "arcnet.c: v3.02 98/06/07 Avery Pennarun <apenwarr@bond.net> et al.\n";
+ "arcnet.c: v3.02 98/06/07 Avery Pennarun <apenwarr@worldvisions.ca> et al.\n";
 
 #include <linux/module.h>
 #include <linux/config.h>
diff -u --recursive --new-file v2.1.121/linux/drivers/net/ariadne.c linux/drivers/net/ariadne.c
--- v2.1.121/linux/drivers/net/ariadne.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/net/ariadne.c	Mon Sep 14 12:35:25 1998
@@ -545,6 +545,7 @@
     struct ariadne_private *priv = (struct ariadne_private *)dev->priv;
     struct AriadneBoard *board = priv->board;
     int entry;
+    unsigned long flags;
 
     /* Transmitter timeout, serious problems. */
     if (dev->tbusy) {
@@ -625,6 +626,9 @@
     printk(" data 0x%08x len %d\n", (int)skb->data, (int)skb->len);
 #endif
 
+    save_flags(flags);
+    cli();
+
     entry = priv->cur_tx % TX_RING_SIZE;
 
     /* Caution: the write order is important here, set the base address with
@@ -675,13 +679,12 @@
 
     dev->trans_start = jiffies;
 
-    cli();
     priv->lock = 0;
     if (lowb(priv->tx_ring[(entry+1) % TX_RING_SIZE]->TMD1) == 0)
 	dev->tbusy = 0;
     else
 	priv->tx_full = 1;
-    sti();
+    restore_flags(flags);
 
     return(0);
 }
@@ -780,13 +783,15 @@
     struct ariadne_private *priv = (struct ariadne_private *)dev->priv;
     struct AriadneBoard *board = priv->board;
     short saved_addr;
+    unsigned long flags;
 
+    save_flags(flags);
     cli();
     saved_addr = board->Lance.RAP;
     board->Lance.RAP = CSR112;	/* Missed Frame Count */
     priv->stats.rx_missed_errors = swapw(board->Lance.RDP);
     board->Lance.RAP = saved_addr;
-    sti();
+    restore_flags(flags);
 
     return(&priv->stats);
 }
diff -u --recursive --new-file v2.1.121/linux/drivers/net/com20020.c linux/drivers/net/com20020.c
--- v2.1.121/linux/drivers/net/com20020.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/net/com20020.c	Mon Sep 14 11:32:22 1998
@@ -6,9 +6,6 @@
 	Written 1994-1996 by Avery Pennarun,
 	which was in turn derived from skeleton.c by Donald Becker.
 
-	Contact Avery at: apenwarr@bond.net or
-	RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
-
 	**********************
 
 	The original copyright of skeleton.c was as follows:
@@ -214,7 +211,7 @@
 
 
 static const char *version =
- "com20020.c: v3.00 97/11/09 Avery Pennarun <apenwarr@bond.net> et al.\n";
+ "com20020.c: v3.00 97/11/09 Avery Pennarun <apenwarr@worldvisions.ca> et al.\n";
 
 /****************************************************************************
  *                                                                          *
diff -u --recursive --new-file v2.1.121/linux/drivers/net/com90io.c linux/drivers/net/com90io.c
--- v2.1.121/linux/drivers/net/com90io.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/net/com90io.c	Mon Sep 14 11:32:22 1998
@@ -6,9 +6,6 @@
 	Written 1994-1996 by Avery Pennarun,
 	which was in turn derived from skeleton.c by Donald Becker.
 
-	Contact Avery at: apenwarr@bond.net or
-	RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
-
 	**********************
 
 	The original copyright of skeleton.c was as follows:
@@ -183,7 +180,7 @@
 
 
 static const char *version =
- "com90io.c: v3.00 97/11/09 Avery Pennarun <apenwarr@bond.net> et al.\n";
+ "com90io.c: v3.00 97/11/09 Avery Pennarun <apenwarr@worldvisions.ca> et al.\n";
 
 
 /****************************************************************************
diff -u --recursive --new-file v2.1.121/linux/drivers/net/com90xx.c linux/drivers/net/com90xx.c
--- v2.1.121/linux/drivers/net/com90xx.c	Fri May  8 23:14:48 1998
+++ linux/drivers/net/com90xx.c	Mon Sep 14 11:32:22 1998
@@ -4,9 +4,6 @@
    Written 1994-1996 by Avery Pennarun,
    which was in turn derived from skeleton.c by Donald Becker.
 
-   Contact Avery at: apenwarr@bond.net or
-   RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
-
    **********************
 
    The original copyright of skeleton.c was as follows:
@@ -153,7 +150,7 @@
 #define ARCRESET	inb(_RESET)
 
 static const char *version =
-"com90xx.c: v3.00 97/11/09 Avery Pennarun <apenwarr@bond.net> et al.\n";
+"com90xx.c: v3.00 97/11/09 Avery Pennarun <apenwarr@worldvisions.ca> et al.\n";
 
 
 /****************************************************************************
diff -u --recursive --new-file v2.1.121/linux/drivers/net/daynaport.c linux/drivers/net/daynaport.c
--- v2.1.121/linux/drivers/net/daynaport.c	Mon Aug  3 12:45:45 1998
+++ linux/drivers/net/daynaport.c	Sun Sep 13 12:12:34 1998
@@ -30,6 +30,8 @@
 #include <linux/nubus.h>
 #include <asm/io.h>
 #include <asm/system.h>
+#include <asm/hwtest.h>
+#include <linux/delay.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -57,6 +59,13 @@
 static void sane_block_output(struct device *dev, int count,
 							const unsigned char *buf, const start_page);
 
+static void slow_sane_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+						int ring_page);
+static void slow_sane_block_input(struct device *dev, int count,
+						  struct sk_buff *skb, int ring_offset);
+static void slow_sane_block_output(struct device *dev, int count,
+						   const unsigned char *buf, const int start_page);
+
 
 #define WD_START_PG	0x00	/* First page of TX buffer */
 #define WD03_STOP_PG	0x20	/* Last page +1 of RX ring */
@@ -81,11 +90,11 @@
 	int regd;
 	int v;
 	
-	if(nubus_hwreg_present(&ptr[0x00])==0)
+	if(hwreg_present(&ptr[0x00])==0)
 		return -EIO;
-	if(nubus_hwreg_present(&ptr[0x0D<<scale])==0)
+	if(hwreg_present(&ptr[0x0D<<scale])==0)
 		return -EIO;
-	if(nubus_hwreg_present(&ptr[0x0D<<scale])==0)
+	if(hwreg_present(&ptr[0x0D<<scale])==0)
 		return -EIO;
 	ptr[0x00]=E8390_NODMA+E8390_PAGE1+E8390_STOP;
 	regd=ptr[0x0D<<scale];
@@ -136,6 +145,51 @@
 }
 
 /*
+ *	Memory probe for 8390 cards
+ */
+ 
+int apple_8390_mem_probe(volatile unsigned short *p)
+{
+	int i, j;
+	/*
+	 *	Algorithm.
+	 *	1.	Check each block size of memory doesn't fault
+	 *	2.	Write a value to it
+	 *	3.	Check all previous blocks are unaffected
+	 */
+	
+	for(i=0;i<2;i++)
+	{
+		volatile unsigned short *m=p+4096*i;
+		/* Unwriteable - we have a fully decoded card and the
+		   RAM end located */
+		   
+		if(hwreg_present(m)==0)
+			return 8192*i;
+			
+		*m=0xA5A0|i;
+		
+		for(j=0;j<i;j++)
+		{
+			/* Partial decode and wrap ? */
+			if(p[4096*j]!=(0xA5A0|j))
+			{
+				/* This is the first misdecode, so it had
+				   one less page than we tried */
+				return 8192*i;
+			}
+ 			j++;
+ 		}
+ 		/* Ok it still decodes.. move on 8K */
+ 	}
+ 	/* 
+ 	 *	We don't look past 16K. That should cover most cards
+ 	 *	and above 16K there isnt really any gain.
+ 	 */
+ 	return 16384;
+ }
+ 		
+/*
  *    Probe for 8390 cards.  
  *    The ns8390_probe1() routine initializes the card and fills the
  *    station address field. On entry base_addr is set, irq is set
@@ -216,16 +270,21 @@
 	/* Apple, Farallon, Asante */
 	if(id==NS8390_APPLE|| id==NS8390_FARALLON || id==NS8390_ASANTE)
 	{
+		int memsize;
+		
 		dev->base_addr=(int)(nubus_slot_addr(slot)+APPLE_8390_BASE);
 		dev->mem_start=(int)(nubus_slot_addr(slot)+APPLE_8390_MEM);
-		dev->mem_end=dev->mem_start+APPLE_MEMSIZE; /* 8K it seems */
+		
+		memsize = apple_8390_mem_probe((void *)dev->mem_start);
+		
+		dev->mem_end=dev->mem_start+memsize;
 		dev->irq=slot;
 		printk("apple/clone: testing board: ");
 
-		printk("memory - ");		
+		printk("%dK memory - ", memsize>>10);		
 
 		i=(void *)dev->mem_start;
-		memset((void *)i,0xAA, DAYNA_MEMSIZE);
+		memset((void *)i,0xAA, memsize);
 		while(i<(volatile unsigned short *)dev->mem_end)
 		{
 			if(*i!=0xAAAA)
@@ -366,8 +425,15 @@
 			ei_status.get_8390_hdr = &dayna_get_8390_hdr;
 			ei_status.reg_offset = fwrd4_offsets;
 			break;
-		case NS8390_APPLE:	/* Apple/Asante/Farallon */
 		case NS8390_FARALLON:
+		case NS8390_APPLE:	/* Apple/Asante/Farallon */
+			/*      16 bit card, register map is reversed */
+			ei_status.reset_8390 = &ns8390_no_reset;
+			ei_status.block_input = &slow_sane_block_input;
+			ei_status.block_output = &slow_sane_block_output;
+			ei_status.get_8390_hdr = &slow_sane_get_8390_hdr;
+			ei_status.reg_offset = back4_offsets;
+			break;
 		case NS8390_ASANTE:
 			/*      16 bit card, register map is reversed */
 			ei_status.reset_8390 = &ns8390_no_reset;
@@ -442,7 +508,7 @@
 {
 	unsigned char *target=nubus_slot_addr(dev->irq);
 	if (ei_debug > 1) 
-	printk("Need to reset the NS8390 t=%lu...", jiffies);
+		printk("Need to reset the NS8390 t=%lu...", jiffies);
 	ei_status.txing = 0;
 	/* This write resets the card */
 	target[0xC0000]=0;
@@ -585,12 +651,88 @@
 	}
 }
 
+
 static void sane_block_output(struct device *dev, int count, const unsigned char *buf,
 				int start_page)
 {
 	long shmem = (start_page - WD_START_PG)<<8;
 	
 	memcpy((char *)dev->mem_start+shmem, buf, count);
+}
+
+static void word_memcpy_tocard(void *tp, const void *fp, int count)
+{
+	volatile unsigned short *to = tp;
+	const unsigned short *from = fp;
+	
+	count++;
+	count/=2;
+	
+	while(count--)
+		*to++=*from++;
+}
+
+static void word_memcpy_fromcard(void *tp, const void *fp, int count)
+{
+	unsigned short *to = tp;
+	const volatile unsigned short *from = fp;
+	
+	count++;
+	count/=2;
+	
+	while(count--)
+		*to++=*from++;
+}
+
+static void slow_sane_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
+	word_memcpy_fromcard((void *)hdr, (char *)dev->mem_start+hdr_start, 4);
+	/* Register endianism - fix here rather than 8390.c */
+	hdr->count=(hdr->count&0xFF)<<8|(hdr->count>>8);
+}
+
+static void slow_sane_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
+	unsigned long xfer_start = xfer_base+dev->mem_start;
+
+	if (xfer_start + count > dev->rmem_end) 
+	{
+		/* We must wrap the input move. */
+		int semi_count = dev->rmem_end - xfer_start;
+		word_memcpy_fromcard(skb->data, (char *)dev->mem_start+xfer_base, semi_count);
+		count -= semi_count;
+		word_memcpy_fromcard(skb->data + semi_count, 
+			(char *)dev->rmem_start, count);
+	}
+	else
+	{
+		word_memcpy_fromcard(skb->data, (char *)dev->mem_start+xfer_base, count);
+	}
+}
+
+static void slow_sane_block_output(struct device *dev, int count, const unsigned char *buf,
+				int start_page)
+{
+	long shmem = (start_page - WD_START_PG)<<8;
+	
+	word_memcpy_tocard((char *)dev->mem_start+shmem, buf, count);
+#if 0
+	long shmem = (start_page - WD_START_PG)<<8;
+	volatile unsigned short *to=(unsigned short *)(dev->mem_start+shmem);
+	volatile int p;
+	unsigned short *bp=(unsigned short *)buf;
+	
+	count=(count+1)/2;
+	
+	while(count--)
+	{
+		*to++=*bp++;
+		for(p=0;p<10;p++)
+			p++;
+	}
+#endif	
 }
 
 /*
diff -u --recursive --new-file v2.1.121/linux/drivers/net/hamradio/dmascc.c linux/drivers/net/hamradio/dmascc.c
--- v2.1.121/linux/drivers/net/hamradio/dmascc.c	Tue Jun 23 10:01:23 1998
+++ linux/drivers/net/hamradio/dmascc.c	Thu Sep 10 16:33:15 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: dmascc.c,v 1.2.1.4 1998/06/10 02:24:11 kudielka Exp $
+ * $Id: dmascc.c,v 1.3 1998/09/07 04:41:56 kudielka Exp $
  *
  * Driver for high-speed SCC boards (those with DMA support)
  * Copyright (C) 1997 Klaus Kudielka
@@ -62,6 +62,14 @@
 #define test_and_set_bit(x,y) set_bit(x,y)
 #define register_netdevice(x) register_netdev(x)
 #define unregister_netdevice(x) unregister_netdev(x)
+#define dev_kfree_skb(x) dev_kfree_skb(x,FREE_WRITE)
+#define SET_DEV_INIT(x) (x=dmascc_dev_init)
+
+#define SHDLCE  0x01 /* WR15 */
+
+#define AUTOEOM 0x02 /* WR7' */
+#define RXFIFOH 0x08
+#define TXFIFOE 0x20
 
 static int dmascc_dev_init(struct device *dev)
 {
@@ -83,7 +91,7 @@
 #include <linux/init.h>
 #include <asm/uaccess.h>
 
-#define dmascc_dev_init NULL
+#define SET_DEV_INIT(x)
 
 
 #endif
@@ -286,7 +294,7 @@
 #ifdef MODULE
 
 
-MODULE_AUTHOR("Klaus Kudielka <oe1kib@oe1xtu.ampr.org>");
+MODULE_AUTHOR("Klaus Kudielka");
 MODULE_DESCRIPTION("Driver for high-speed SCC boards");
 MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NUM_DEVS) "i");
 
@@ -462,8 +470,8 @@
   /* Reset 8530 */
   write_scc(cmd, R9, FHWRES | MIE | NV);
 
-  /* Determine type of chip */
-  write_scc(cmd, R15, 1);
+  /* Determine type of chip by enabling SDLC/HDLC enhancements */
+  write_scc(cmd, R15, SHDLCE);
   if (!read_scc(cmd, R15)) {
     /* WR7' not present. This is an ordinary Z8530 SCC. */
     chip = Z8530;
@@ -575,7 +583,7 @@
     dev->hard_header = ax25_encapsulate;
     dev->rebuild_header = ax25_rebuild_header;
     dev->set_mac_address = scc_set_mac_address;
-    dev->init = dmascc_dev_init;
+    SET_DEV_INIT(dev->init);
     dev->type = ARPHRD_AX25;
     dev->hard_header_len = 73;
     dev->mtu = 1500;
@@ -662,17 +670,17 @@
   switch (info->chip) {
   case Z85C30:
     /* Select WR7' */
-    write_scc(cmd, R15, 1);
+    write_scc(cmd, R15, SHDLCE);
     /* Auto EOM reset */
-    write_scc(cmd, R7, 0x02);
+    write_scc(cmd, R7, AUTOEOM);
     write_scc(cmd, R15, 0);
     break;
   case Z85230:
     /* Select WR7' */
-    write_scc(cmd, R15, 1);
+    write_scc(cmd, R15, SHDLCE);
     /* RX FIFO half full (interrupt only), Auto EOM reset,
        TX FIFO empty (DMA only) */
-    write_scc(cmd, R7, dev->dma ? 0x22 : 0x0a);
+    write_scc(cmd, R7, AUTOEOM | (dev->dma ? TXFIFOE : RXFIFOH));
     write_scc(cmd, R15, 0);
     break;
   }
diff -u --recursive --new-file v2.1.121/linux/drivers/net/hamradio/z8530.h linux/drivers/net/hamradio/z8530.h
--- v2.1.121/linux/drivers/net/hamradio/z8530.h	Tue Oct 29 05:33:39 1996
+++ linux/drivers/net/hamradio/z8530.h	Thu Sep 10 16:33:15 1998
@@ -219,15 +219,17 @@
 
 /* Read Register 15 (value of WR 15) */
 
-/* 8580/85180/85280 Enhanced SCC register definitions */
+/* Z85C30/Z85230 Enhanced SCC register definitions */
 
 /* Write Register 7' (SDLC/HDLC Programmable Enhancements) */
 #define AUTOTXF	0x01		/* Auto Tx Flag */
 #define AUTOEOM 0x02		/* Auto EOM Latch Reset */
 #define AUTORTS	0x04		/* Auto RTS */
 #define TXDNRZI 0x08		/* TxD Pulled High in SDLC NRZI mode */
+#define RXFIFOH 0x08		/* Z85230: Int on RX FIFO half full */
 #define FASTDTR 0x10		/* Fast DTR/REQ Mode */
 #define CRCCBCR	0x20		/* CRC Check Bytes Completely Received */
+#define TXFIFOE 0x20		/* Z85230: Int on TX FIFO completely empty */
 #define EXTRDEN	0x40		/* Extended Read Enabled */
 
 /* Write Register 15 (external/status interrupt control) */
@@ -240,4 +242,4 @@
 /* Read Register 7 (frame status FIFO) */
 #define BCMSB	0x3f		/* MSB of 14 bits count */
 #define FDA	0x40		/* FIFO Data Available Status */
-#define FOY	0x80		/* FIFO Overflow Status */
+#define FOS	0x80		/* FIFO Overflow Status */
diff -u --recursive --new-file v2.1.121/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v2.1.121/linux/drivers/net/plip.c	Wed Sep  9 14:51:08 1998
+++ linux/drivers/net/plip.c	Mon Sep 14 12:50:54 1998
@@ -1175,7 +1175,7 @@
 		timid = 1;
 	} else {
 		if (ints[0] == 0 || ints[1] == 0) {
-			/* disable driver on "parport=" or "parport=0" */
+			/* disable driver on "plip=" or "plip=0" */
 			parport[0] = -2;
 		} else {
 			printk(KERN_WARNING "warning: 'plip=0x%x' ignored\n", 
diff -u --recursive --new-file v2.1.121/linux/drivers/nubus/nubus.c linux/drivers/nubus/nubus.c
--- v2.1.121/linux/drivers/nubus/nubus.c	Thu Mar 26 15:57:03 1998
+++ linux/drivers/nubus/nubus.c	Sun Sep 13 10:20:56 1998
@@ -14,8 +14,11 @@
 #include <asm/setup.h>
 #include <asm/system.h>
 #include <asm/page.h>
+#include <asm/hwtest.h>
 /* for LCIII stuff; better find a general way like MACH_HAS_NUBUS */
 #include <asm/macintosh.h>
+#include <linux/proc_fs.h>
+
 
 #undef LCIII_WEIRDNESS
  
@@ -26,49 +29,6 @@
  *				-- Alan
  */
  
- 
- 
-/* This function tests for the presence of an address, specially a
- * hardware register address. It is called very early in the kernel
- * initialization process, when the VBR register isn't set up yet. On
- * an Atari, it still points to address 0, which is unmapped. So a bus
- * error would cause another bus error while fetching the exception
- * vector, and the CPU would do nothing at all. So we needed to set up
- * a temporary VBR and a vector table for the duration of the test.
- *
- * See the atari/config.c code we nicked it from for more clues.
- */
-
-int nubus_hwreg_present( volatile void *regp )
-{
-    int	ret = 0;
-    long	save_sp, save_vbr;
-    long	tmp_vectors[3];
-    unsigned long flags;
-    
-    save_flags(flags);
-    cli();
-
-    __asm__ __volatile__
-	(	"movec	%/vbr,%2\n\t"
-		"movel	#Lberr1,%4@(8)\n\t"
-                "movec	%4,%/vbr\n\t"
-		"movel	%/sp,%1\n\t"
-		"moveq	#0,%0\n\t"
-		"tstb	%3@\n\t"  
-		"nop\n\t"
-		"moveq	#1,%0\n"
-                "Lberr1:\n\t"
-		"movel	%1,%/sp\n\t"
-		"movec	%2,%/vbr"
-		: "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
-		: "a" (regp), "a" (tmp_vectors)
-                );
-    restore_flags(flags);
-    return( ret );
-}
-
-
 
 /*
  *	Yes this sucks. The ROM can appear on arbitary bytes of the long
@@ -104,7 +64,7 @@
 {
 	unsigned char *p=*ptr;
 	
-	if(len>8192)
+	if(len>65536)
 		printk("rewind of %d!\n", len);
 	while(len)
 	{
@@ -121,7 +81,7 @@
 static void nubus_advance(unsigned char **ptr, int len, int map)
 {
 	unsigned char *p=*ptr;
-	if(len>8192)
+	if(len>65536)
 		printk("advance of %d!\n", len);
 	while(len)
 	{
@@ -375,7 +335,7 @@
 	{
 		rp--;
 		
-		if(!nubus_hwreg_present(rp))
+		if(!hwreg_present(rp))
 			continue;
 			
 		dp=*rp;
@@ -596,6 +556,52 @@
 	return ng;
 }
 
+#ifdef CONFIG_PROC_FS
+
+/*
+ *	/proc for Nubus devices
+ */
+ 
+static int sprint_nubus_config(int slot, char *ptr, int len)
+{
+	if(len<150)
+		return -1;
+	sprintf(ptr, "Device: %s %s\n", nubus_slots[slot].slot_cardname,
+		(nubus_slots[slot].slot_flags&NUBUS_DEVICE_ACTIVE)?
+			"[active]":"[unused]");
+	return strlen(ptr);
+}
+
+int get_nubus_list(char *buf)
+{
+	int nprinted, len, size;
+	int slot;
+#define MSG "\nwarning: page-size limit reached!\n"
+
+	/* reserve same for truncation warning message: */
+	size  = PAGE_SIZE - (strlen(MSG) + 1);
+	len   = sprintf(buf, "Nubus devices found:\n");
+
+	for (slot=0; slot< 16; slot++) 
+	{
+		if(!(nubus_slots[slot].slot_flags&NUBUS_DEVICE_PRESENT))
+			continue;
+		nprinted = sprint_nubus_config(slot, buf + len, size - len);
+		if (nprinted < 0) {
+			return len + sprintf(buf + len, MSG);
+		}
+		len += nprinted;
+	}
+	return len;
+}
+
+static struct proc_dir_entry proc_nubus = {
+	PROC_NUBUS, 5, "nubus",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
+};
+#endif
+
 void nubus_init(void)
 {
 	/* 
@@ -626,4 +632,7 @@
 	nubus_init_via();
 	printk("Scanning nubus slots.\n");
 	nubus_probe_bus();
+	proc_register(&proc_root, &proc_nubus);
 }
+
+	
\ No newline at end of file
diff -u --recursive --new-file v2.1.121/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c
--- v2.1.121/linux/drivers/pci/oldproc.c	Wed Aug 26 11:37:38 1998
+++ linux/drivers/pci/oldproc.c	Wed Sep 16 13:25:57 1998
@@ -255,6 +255,7 @@
 	DEVICE( WINBOND,	WINBOND_82C105,	"SL82C105"),
 	DEVICE( WINBOND,	WINBOND_83C553,	"W83C553"),
 	DEVICE( DATABOOK,      	DATABOOK_87144,	"DB87144"),
+	DEVICE(	PLX,		PLX_9050,	"PCI9050 I2O"),
 	DEVICE( PLX,		PLX_9080,	"PCI9080 I2O"),
 	DEVICE( MADGE,		MADGE_MK2,	"Smart 16/4 BM Mk2 Ringnode"),
 	DEVICE( MADGE,		MADGE_C155S,	"Collage 155 Server"),
@@ -314,6 +315,7 @@
 	DEVICE( TRUEVISION,	TRUEVISION_T1000,"TARGA 1000"),
 	DEVICE( INIT,		INIT_320P,	"320 P"),
 	DEVICE( INIT,		INIT_360P,	"360 P"),
+	DEVICE(	TTI,		TTI_HPT343,	"HPT343"),
 	DEVICE( VIA,		VIA_82C505,	"VT 82C505"),
 	DEVICE( VIA,		VIA_82C561,	"VT 82C561"),
 	DEVICE( VIA,		VIA_82C586_1,	"VT 82C586 Apollo IDE"),
@@ -389,6 +391,7 @@
 	DEVICE( TOSHIBA,	TOSHIBA_TOPIC95,"ToPIC95"),
 	DEVICE( TOSHIBA,	TOSHIBA_TOPIC97,"ToPIC97"),
 	DEVICE( RICOH,		RICOH_RL5C466,	"RL5C466"),
+	DEVICE(	ARTOP,		ARTOP_ATP8400,	"ATP8400"),
 	DEVICE( ARTOP,		ARTOP_ATP850UF,	"ATP850UF"),
 	DEVICE( ZEITNET,	ZEITNET_1221,	"1221"),
 	DEVICE( ZEITNET,	ZEITNET_1225,	"1225"),
@@ -722,6 +725,7 @@
 	      case PCI_VENDOR_ID_REALTEK:	return "Realtek";
 	      case PCI_VENDOR_ID_TRUEVISION:	return "Truevision";
 	      case PCI_VENDOR_ID_INIT:		return "Initio Corp";
+	      case PCI_VENDOR_ID_TTI:		return "Triones Technologies, Inc.";
 	      case PCI_VENDOR_ID_VIA:		return "VIA Technologies";
 	      case PCI_VENDOR_ID_VORTEX:	return "VORTEX";
 	      case PCI_VENDOR_ID_EF:		return "Efficient Networks";
diff -u --recursive --new-file v2.1.121/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.1.121/linux/drivers/scsi/Config.in	Thu Aug  6 14:06:32 1998
+++ linux/drivers/scsi/Config.in	Mon Sep 14 11:11:44 1998
@@ -50,6 +50,13 @@
 dep_tristate 'Future Domain 16xx SCSI/AHA 2920 support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI
 dep_tristate 'GDT SCSI Disk Array Controller support' CONFIG_SCSI_GDTH $CONFIG_SCSI
 dep_tristate 'Generic NCR5380/53c400 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 $CONFIG_SCSI
+if [ "$CONFIG_PARPORT" != "n" ]; then
+  dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PARPORT
+  if [ "$CONFIG_SCSI_PPA" != "n" ]; then
+    int  '  Pedantic EPP-checking'   CONFIG_SCSI_PPA_HAVE_PEDANTIC 2 0 3
+  fi
+  dep_tristate 'IOMEGA ZIP Plus drive SCSI support' CONFIG_SCSI_IMM $CONFIG_SCSI $CONFIG_PARPORT
+fi
 if [ "$CONFIG_SCSI_GENERIC_NCR5380" != "n" ]; then
 	bool '   Enable NCR53c400 extensions' CONFIG_SCSI_GENERIC_NCR53C400
 	choice 'NCR5380/53c400 mapping method (use Port for T130B)' \
@@ -86,12 +93,6 @@
   if [ "$CONFIG_SCSI_IBMMCA" != "n" ]; then
      bool '  Standard SCSI-order' CONFIG_IBMMCA_SCSI_ORDER_STANDARD
      bool '  Reset SCSI-devices at boottime' CONFIG_IBMMCA_SCSI_DEV_RESET
-  fi
-fi
-if [ "$CONFIG_PARPORT" != "n" ]; then
-  dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PARPORT
-  if [ "$CONFIG_SCSI_PPA" != "n" ]; then
-    int  '  Pedantic EPP-checking'   CONFIG_SCSI_PPA_HAVE_PEDANTIC 2 0 3
   fi
 fi
 dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI
diff -u --recursive --new-file v2.1.121/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.1.121/linux/drivers/scsi/Makefile	Wed Aug 26 11:37:38 1998
+++ linux/drivers/scsi/Makefile	Mon Sep 14 11:11:44 1998
@@ -243,6 +243,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_IMM),y)
+L_OBJS += imm.o
+else
+  ifeq ($(CONFIG_SCSI_IMM),m)
+  M_OBJS += imm.o
+  endif
+endif
+
 ifeq ($(CONFIG_SCSI_QLOGIC_FAS),y)
 L_OBJS += qlogicfas.o
 else
diff -u --recursive --new-file v2.1.121/linux/drivers/scsi/README.ppa linux/drivers/scsi/README.ppa
--- v2.1.121/linux/drivers/scsi/README.ppa	Mon Apr  8 05:47:34 1996
+++ linux/drivers/scsi/README.ppa	Mon Sep 14 11:11:44 1998
@@ -1,119 +1,16 @@
-README.ppa (c) 1996   Grant R. Guenther,  grant@torque.net
+-------- Terse where to get ZIP Drive help info --------
 
+General Iomega ZIP drive page for Linux:
+http://www.torque.net/~campbell/
 
-         The IOMEGA PPA3 parallel port SCSI Host Bus Adapter
+Driver achive for old drivers:
+http://www.torque.net/~campbell/ppa/
 
-                     as embedded in the ZIP drive
+Linux Parport page (parallel port)
+http://www.torque.net/parport/
 
+Email list for Linux Parport
+linux-parport@torque.net
 
-This README documents the Linux support for the parallel port version of
-IOMEGA's ZIP100.  The ZIP100 is an inexpensive and popular, but relatively 
-low performance, removable medium disk device.  The drive is also available
-as a regular SCSI device, but the driver documented here is for the
-parallel port version.  IOMEGA implemented the parallel port version by
-integrating (or emulating ?) their PPA3 parallel to SCSI converter into
-the ZIP drive.
-
-I have implemented a low-level driver, ppa.c,  for this parallel port 
-host bus adapter, thereby supporting the parallel port ZIP drive as a 
-regular SCSI device under Linux.
-
-It is possible that this driver will also work with the original PPA3
-device (to access a CDrom, for instance). But, the PPA3 is hard to find 
-and costs as much as the ZIP drive itself, so no-one has actually tried
-this, to the best of my knowledge.
-
-The driver was developed without the benefit of any technical specifications 
-for the interface.  Instead, a modified version of DOSemu was used to 
-monitor the protocol used by the DOS driver, 'guest.exe', for this adapter.
-I have no idea how my programming model relates to IOMEGA's design.
-(One technical consequence of this method:  I have never observed a
-SCSI message byte in the protocol transactions between guest.exe and
-the ZIP drive, so I do not know how they are delivered.  My working 
-hypothesis is that we don't have to worry about them if we don't
-send linked commands to the drive.)
-
-I'd like to thank Byron Jeff (byron@cc.gatech.edu) for publishing his
-observation that the 'guest' driver loads under DOSemu.  His remark was
-the stimulus that began this project.
-
-The ppa driver can detect and adapt to 4- and 8-bit parallel ports, but 
-there is currently no support for EPP or ECP ports, as I have been unable 
-to make the DOS drivers work in these modes on my test rig.
-
-The driver may be built in to the kernel, or loaded as a module.  It
-may be configured on the command line in both cases, although the syntax
-is different.  It may also be configured by editing the source file.
-
-Built-in drivers accept parameters using this LILO/LOADLIN command line
-syntax (omitted parameters retain their default values):
-
-        ppa=base[,speed_high[,speed_low[,nybble]]]
-
-For example:    ppa=0x378,0,3
-
-If a driver is loaded as a module the parameters may be set on the
-insmod command line, but each one must be specified by name:
-
-For example:    insmod ppa.o ppa_base=0x378 ppa_nybble=1
-
-(Notice the ppa_ prefix on each of the parameters in the insmod form.)
-
-Here are the parameters and their functions:
-
-Variable        Default    Description
-
-ppa_base        0x378   The base address of PPA's parallel port.
-ppa_speed_high  1       Microsecond i/o delay used in data transfers
-ppa_speed_low   6       Microsecond delay used in other operations
-ppa_nybble      0       1 to force the driver to use 4-bit mode.
-
-A word about the timing parameters:  the ppa_speed_low parameter controls 
-the widths of a large number of pulses that are sent over the parallel bus, 
-the narrower the pulses, the faster things go, but the greater the risk of 
-distortion by noise damping circuits in the parallel ports.  The 
-ppa_speed_high parameter controls the same delays, but during the data 
-transfer phase only.   In this phase, there is a lot of handshaking going 
-on and the pulse shaping should not be so much of an issue, but if you 
-see data corruption, you can increase this parameter as well.
-
-You might also want to reduce the timing values to attempt to increase
-the transfer rates on your system.  Please be careful to watch for
-SCSI timeout errors in your log files.  If you are getting timeouts, you
-have set these parameters too low.  The default values appear to be
-safe on most machines.
-
-If you have both the lp and ppa drivers in your kernel, you must ensure
-that they access different parallel ports.  By default, the lp driver is
-initialised early in the booting process, and it claims all parallel
-ports that it can find.  You may control this behaviour with a LILO or
-LOADLIN command line argument of the form:
-
-        lp=base0[,irq0[,base1[,irq1[,base2[,irq2]]]]]
-
-For example:    lp=0x278,7
-
-If you use this method, only the ports named will be adopted by the lp
-driver.  You can disable them all with lp=0 .
-
-So, if you have a printer on 0x3bc and a ZIP drive on 0x278 you would
-give the following options on your boot command:
-
-        lp=0x3bc ppa=0x278
-
-In this case lp would use the polling driver, since an interrupt was not 
-specified.
-
-If you want to share the same parallel port between a ZIP drive and a
-printer, you should build both the lp and ppa drivers as modules and
-load and unload one or the other as required.  This is clumsy but we
-currently have no protocol for synchronising access to shared parallel
-ports.
-
-For information about using the ZIP drive, please read the generic
-instructions in the SCSI-HOWTO and the man pages for the normal disk
-management tools,  fdisk, mkfs, mount, umount, etc.  There is a mini-HOWTO
-circulating concerning the use of the normal SCSI version of the ZIP
-drive, most of its comments will apply to disks connected through the
-ppa driver as well.
-
+Email for problems with ZIP or ZIP Plus drivers
+campbell@torque.net
diff -u --recursive --new-file v2.1.121/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v2.1.121/linux/drivers/scsi/hosts.c	Thu Aug 27 19:56:29 1998
+++ linux/drivers/scsi/hosts.c	Mon Sep 14 11:11:44 1998
@@ -197,10 +197,6 @@
 #include "AM53C974.h"
 #endif
 
-#ifdef CONFIG_SCSI_PPA
-#include "ppa.h"
-#endif
-
 #ifdef CONFIG_SCSI_SUNESP
 #include "esp.h"
 #endif
@@ -274,6 +270,20 @@
 #endif
 
 /*
+ * Moved ppa driver to the end of the probe list
+ * since it is a removable host adapter.
+ * This means the parallel ZIP drive will not bump
+ * the order of the /dev/sd devices - campbell@torque.net
+ */
+#ifdef CONFIG_SCSI_PPA
+#include "ppa.h"
+#endif
+
+#ifdef CONFIG_SCSI_IMM
+#include "imm.h"
+#endif
+
+/*
 static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/hosts.c,v 1.20 1996/12/12 19:18:32 davem Exp $";
 */
 
@@ -441,9 +451,6 @@
 #ifdef CONFIG_SCSI_AM53C974
     AM53C974,
 #endif
-#ifdef CONFIG_SCSI_PPA
-    PPA,
-#endif
 #ifdef CONFIG_SCSI_SUNESP
     SCSI_SPARC_ESP,
 #endif
@@ -484,6 +491,13 @@
 #ifdef CONFIG_SCSI_POWERTECSCSI
     POWERTECSCSI,
 #endif
+#endif
+/* "Removable host adapters" below this line (Parallel Port/USB/other) */
+#ifdef CONFIG_SCSI_PPA
+    PPA,
+#endif
+#ifdef CONFIG_SCSI_IMM
+    IMM,
 #endif
 #ifdef CONFIG_SCSI_DEBUG
     SCSI_DEBUG,
diff -u --recursive --new-file v2.1.121/linux/drivers/scsi/imm.c linux/drivers/scsi/imm.c
--- v2.1.121/linux/drivers/scsi/imm.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/imm.c	Mon Sep 14 11:11:44 1998
@@ -0,0 +1,1321 @@
+/* imm.c   --  low level driver for the IOMEGA MatchMaker
+ * parallel port SCSI host adapter.
+ * 
+ * (The IMM is the embedded controller in the ZIP Plus drive.)
+ * 
+ * Current Maintainer: David Campbell (Perth, Western Australia)
+ *                     campbell@gear.torque.net
+ *                     dcampbel@p01.as17.honeywell.com.au
+ *
+ * My unoffical company acronym list is 21 pages long:
+ *      FLA:    Four letter acronym with built in facility for
+ *              future expansion to five letters.
+ */
+
+#include <linux/config.h>
+
+/* The following #define is to avoid a clash with hosts.c */
+#define IMM_CODE 1
+#define IMM_PROBE_SPP   0x0001
+#define IMM_PROBE_PS2   0x0002
+#define IMM_PROBE_ECR   0x0010
+#define IMM_PROBE_EPP17 0x0100
+#define IMM_PROBE_EPP19 0x0200
+
+void imm_reset_pulse(unsigned int base);
+static int device_check(int host_no);
+
+#include <linux/blk.h>
+#include <asm/io.h>
+#include <linux/parport.h>
+#include "sd.h"
+#include "hosts.h"
+typedef struct {
+    struct pardevice *dev;      /* Parport device entry         */
+    int base;			/* Actual port address          */
+    int mode;			/* Transfer mode                */
+    int host;			/* Host number (for proc)       */
+    Scsi_Cmnd *cur_cmd;		/* Current queued command       */
+    struct tq_struct imm_tq;	/* Polling interupt stuff       */
+    unsigned long jstart;	/* Jiffies at start             */
+    unsigned failed:1;		/* Failure flag                 */
+    unsigned dp:1;		/* Data phase present           */
+    unsigned rd:1;		/* Read data in data phase      */
+    unsigned p_busy:1;		/* Parport sharing busy flag    */
+} imm_struct;
+
+#define IMM_EMPTY \
+{	dev:		NULL,		\
+	base:		-1,		\
+	mode:		IMM_AUTODETECT,	\
+	host:		-1,		\
+	cur_cmd:	NULL,		\
+	imm_tq:		{0, 0, imm_interrupt, NULL},    \
+	jstart:		0,		\
+	failed:		0,		\
+	dp:		0,		\
+	rd:		0,		\
+	p_busy:		0		\
+}
+
+#include "imm.h"
+#define NO_HOSTS 4
+static imm_struct imm_hosts[NO_HOSTS] =
+{IMM_EMPTY, IMM_EMPTY, IMM_EMPTY, IMM_EMPTY};
+
+#define IMM_BASE(x)	imm_hosts[(x)].base
+
+int base[NO_HOSTS] =
+{0x03bc, 0x0378, 0x0278, 0x0000};
+
+void imm_wakeup(void *ref)
+{
+    imm_struct *imm_dev = (imm_struct *) ref;
+
+    if (!imm_dev->p_busy)
+        return;
+
+    if (parport_claim(imm_dev->dev)) {
+        printk("imm: bug in imm_wakeup\n");
+        return;
+    }
+    imm_dev->p_busy = 0;
+    imm_dev->base = imm_dev->dev->port->base;
+    if (imm_dev->cur_cmd)
+        imm_dev->cur_cmd->SCp.phase++;
+    return;
+}
+
+int imm_release(struct Scsi_Host *host)
+{
+    int host_no = host->unique_id;
+
+    printk("Releasing imm%i\n", host_no);
+    parport_unregister_device(imm_hosts[host_no].dev);
+    return 0;
+}
+
+static int imm_pb_claim(int host_no)
+{
+    if (parport_claim(imm_hosts[host_no].dev)) {
+        imm_hosts[host_no].p_busy = 1;
+        return 1;
+    }
+
+    if (imm_hosts[host_no].cur_cmd)
+        imm_hosts[host_no].cur_cmd->SCp.phase++;
+    return 0;
+}
+
+#define imm_pb_release(x) parport_release(imm_hosts[(x)].dev)
+
+/***************************************************************************
+ *                   Parallel port probing routines                        *
+ ***************************************************************************/
+
+#ifndef MODULE
+/*
+ * Command line parameters (for built-in driver):
+ *
+ * Syntax:  imm=base[,mode[,use_sg]]
+ *
+ * For example:  imm=0x378   or   imm=0x378,0,3
+ *
+ */
+
+void imm_setup(char *str, int *ints)
+{
+    static int x = 0;
+
+    if (x == 0) {		/* Disable ALL known ports */
+	int i;
+
+	for (i = 0; i < NO_HOSTS; i++)
+	    parbus_base[i] = 0x0000;
+    }
+    switch (ints[0]) {
+    case 3:
+	imm_sg = ints[3];
+    case 2:
+	imm_hosts[x].mode = ints[2];
+	parbus_base[x] = ints[1];
+	break;
+    default:
+	printk("IMM: I only use between 2 to 3 parameters.\n");
+	break;
+    }
+    x++;
+}
+#else
+Scsi_Host_Template driver_template = IMM;
+#include  "scsi_module.c"
+#endif
+
+int imm_detect(Scsi_Host_Template * host)
+{
+    struct Scsi_Host *hreg;
+    int ports;
+    int i, nhosts, try_again;
+    struct parport *pb = parport_enumerate();
+
+    printk("imm: Version %s\n", IMM_VERSION);
+    nhosts = 0;
+    try_again = 0;
+
+    if (!pb) {
+        printk("imm: parport reports no devices.\n");
+        return 0;
+    }
+
+  retry_entry:
+    for (i = 0; pb; i++, pb = pb->next) {
+        int modes, ppb;
+
+        imm_hosts[i].dev =
+            parport_register_device(pb, "imm", NULL, imm_wakeup,
+                         NULL, PARPORT_DEV_TRAN, (void *) &imm_hosts[i]);
+
+        /* Claim the bus so it remembers what we do to the control
+         * registers. [ CTR and ECP ]
+         */
+        if (imm_pb_claim(i))
+            while (imm_hosts[i].p_busy)
+                schedule();     /* We are safe to schedule here */
+
+        ppb = IMM_BASE(i) = imm_hosts[i].dev->port->base;
+        w_ctr(ppb, 0x0c);
+        modes = imm_hosts[i].dev->port->modes;
+
+        /* Mode detection works up the chain of speed
+         * This avoids a nasty if-then-else-if-... tree
+         */
+        imm_hosts[i].mode = IMM_NIBBLE;
+
+        if (modes & PARPORT_MODE_PCPS2)
+            imm_hosts[i].mode = IMM_PS2;
+
+        if (modes & PARPORT_MODE_PCECPPS2) {
+            w_ecr(ppb, 0x20);
+            imm_hosts[i].mode = IMM_PS2;
+        }
+        if (modes & PARPORT_MODE_PCECPEPP)
+            w_ecr(ppb, 0x80);
+
+        /* Done configuration */
+        imm_pb_release(i);
+
+        if (imm_init(i)) {
+            parport_unregister_device(imm_hosts[i].dev);
+            continue;
+        }
+        /* now the glue ... */
+        switch (imm_hosts[i].mode) {
+        case IMM_NIBBLE:
+            ports = 3;
+            break;
+        case IMM_PS2:
+            ports = 3;
+            break;
+        case IMM_EPP_8:
+        case IMM_EPP_16:
+        case IMM_EPP_32:
+            ports = 8;
+            break;
+        default:                /* Never gets here */
+            continue;
+        }
+
+        host->can_queue = IMM_CAN_QUEUE;
+        host->sg_tablesize = imm_sg;
+        hreg = scsi_register(host, 0);
+        hreg->io_port = pb->base;
+        hreg->n_io_port = ports;
+        hreg->dma_channel = -1;
+        hreg->unique_id = i;
+        imm_hosts[i].host = hreg->host_no;
+        nhosts++;
+    }
+    if (nhosts == 0) {
+        if (try_again == 1)
+            return 0;
+        try_again = 1;
+        goto retry_entry;
+    } else
+        return 1;               /* return number of hosts detected */
+}
+
+/* This is to give the imm driver a way to modify the timings (and other
+ * parameters) by writing to the /proc/scsi/imm/0 file.
+ * Very simple method really... (To simple, no error checking :( )
+ * Reason: Kernel hackers HATE having to unload and reload modules for
+ * testing...
+ * Also gives a method to use a script to obtain optimum timings (TODO)
+ */
+
+static inline int imm_strncmp(const char *a, const char *b, int len)
+{
+    int loop;
+    for (loop = 0; loop < len; loop++)
+	if (a[loop] != b[loop])
+	    return 1;
+
+    return 0;
+}
+
+static inline int imm_proc_write(int hostno, char *buffer, int length)
+{
+    unsigned long x;
+
+    if ((length > 5) && (imm_strncmp(buffer, "mode=", 5) == 0)) {
+	x = simple_strtoul(buffer + 5, NULL, 0);
+	imm_hosts[hostno].mode = x;
+	return length;
+    }
+    printk("imm /proc: invalid variable\n");
+    return (-EINVAL);
+}
+
+int imm_proc_info(char *buffer, char **start, off_t offset,
+		  int length, int hostno, int inout)
+{
+    int i;
+    int len = 0;
+
+    for (i = 0; i < 4; i++)
+	if (imm_hosts[i].host == hostno)
+	    break;
+
+    if (inout)
+	return imm_proc_write(i, buffer, length);
+
+    len += sprintf(buffer + len, "Version : %s\n", IMM_VERSION);
+    len += sprintf(buffer + len, "Parport : %s\n", imm_hosts[i].dev->port->name);
+    len += sprintf(buffer + len, "Mode    : %s\n", IMM_MODE_STRING[imm_hosts[i].mode]);
+
+    /* Request for beyond end of buffer */
+    if (offset > len)
+	return 0;
+
+    *start = buffer + offset;
+    len -= offset;
+    if (len > length)
+	len = length;
+    return len;
+}
+
+#if IMM_DEBUG > 0
+#define imm_fail(x,y) printk("imm: imm_fail(%i) from %s at line %d\n",\
+	   y, __FUNCTION__, __LINE__); imm_fail_func(x,y);
+static inline void imm_fail_func(int host_no, int error_code)
+#else
+static inline void imm_fail(int host_no, int error_code)
+#endif
+{
+    /* If we fail a device then we trash status / message bytes */
+    if (imm_hosts[host_no].cur_cmd) {
+	imm_hosts[host_no].cur_cmd->result = error_code << 16;
+	imm_hosts[host_no].failed = 1;
+    }
+}
+
+/*
+ * Wait for the high bit to be set.
+ * 
+ * In principle, this could be tied to an interrupt, but the adapter
+ * doesn't appear to be designed to support interrupts.  We spin on
+ * the 0x80 ready bit. 
+ */
+static unsigned char imm_wait(int host_no)
+{
+    int k;
+    unsigned short ppb = IMM_BASE(host_no);
+    unsigned char r;
+
+    w_ctr(ppb, 0x0c);
+
+    k = IMM_SPIN_TMO;
+    do {
+	r = r_str(ppb);
+	k--;
+	udelay(1);
+    }
+    while (!(r & 0x80) && (k));
+
+    /*
+     * STR register (LPT base+1) to SCSI mapping:
+     *
+     * STR      imm     imm
+     * ===================================
+     * 0x80     S_REQ   S_REQ
+     * 0x40     !S_BSY  (????)
+     * 0x20     !S_CD   !S_CD
+     * 0x10     !S_IO   !S_IO
+     * 0x08     (????)  !S_BSY
+     *
+     * imm      imm     meaning
+     * ==================================
+     * 0xf0     0xb8    Bit mask
+     * 0xc0     0x88    ZIP wants more data
+     * 0xd0     0x98    ZIP wants to send more data
+     * 0xe0     0xa8    ZIP is expecting SCSI command data
+     * 0xf0     0xb8    end of transfer, ZIP is sending status
+     */
+    w_ctr(ppb, 0x04);
+    if (k)
+	return (r & 0xb8);
+
+    /* Counter expired - Time out occured */
+    imm_fail(host_no, DID_TIME_OUT);
+    printk("imm timeout in imm_wait\n");
+    return 0;			/* command timed out */
+}
+
+static int imm_negotiate(imm_struct * tmp)
+{
+    /*
+     * The following is supposedly the IEEE 1248-1994 negotiate
+     * sequence. I have yet to obtain a copy of the above standard
+     * so this is a bit of a guess...
+     *
+     * A fair chunk of this is based on the Linux parport implementation
+     * of IEEE 1284.
+     *
+     * Return 0 if data available
+     *        1 if no data available
+     */
+
+    unsigned short base = tmp->base;
+    unsigned char a, mode;
+
+    switch (tmp->mode) {
+    case IMM_NIBBLE:
+	mode = 0x00;
+	break;
+    case IMM_PS2:
+	mode = 0x01;
+	break;
+    default:
+	return 0;
+    }
+
+    w_ctr(base, 0x04);
+    udelay(5);
+    w_dtr(base, mode);
+    udelay(100);
+    w_ctr(base, 0x06);
+    udelay(5);
+    a = (r_str(base) & 0x20) ? 0 : 1;
+    udelay(5);
+    w_ctr(base, 0x07);
+    udelay(5);
+    w_ctr(base, 0x06);
+
+    if (a) {
+	printk("IMM: IEEE1284 negotiate indicates no data available.\n");
+	imm_fail(tmp->host, DID_ERROR);
+    }
+    return a;
+}
+
+static inline void epp_reset(unsigned short ppb)
+{
+    int i;
+
+    i = r_str(ppb);
+    w_str(ppb, i);
+    w_str(ppb, i & 0xfe);
+}
+
+static inline void ecp_sync(unsigned short ppb)
+{
+    int i;
+
+    if ((r_ecr(ppb) & 0xe0) != 0x80)
+        return;
+
+    for (i = 0; i < 100; i++) {
+        if (r_ecr(ppb) & 0x01)
+            return;
+        udelay(5);
+    }
+    printk("imm: ECP sync failed as data still present in FIFO.\n");
+}
+
+static inline int imm_byte_out(unsigned short base, const char *buffer, int len)
+{
+    int i;
+
+    w_ctr(base, 0x4);		/* aimmrently a sane mode */
+    for (i = len >> 1; i; i--) {
+	w_dtr(base, *buffer++);
+	w_ctr(base, 0x5);	/* Drop STROBE low */
+	w_dtr(base, *buffer++);
+	w_ctr(base, 0x0);	/* STROBE high + INIT low */
+    }
+    w_ctr(base, 0x4);		/* aimmrently a sane mode */
+    return 1;			/* All went well - we hope! */
+}
+
+static inline int imm_epp_out(unsigned short base, char *buffer, int len)
+{
+    int i;
+    for (i = len; i; i--)
+	w_epp(base, *buffer++);
+    return 1;
+}
+
+static inline int imm_nibble_in(unsigned short base, char *buffer, int len)
+{
+    unsigned char h, l;
+    int i;
+
+    /*
+     * The following is based on documented timing signals
+     */
+    w_ctr(base, 0x4);
+    for (i = len; i; i--) {
+	w_ctr(base, 0x6);
+	l = r_str(base);
+	w_ctr(base, 0x5);
+	h = r_str(base);
+	w_ctr(base, 0x4);
+	*buffer++ = (h & 0xf0) | ((l & 0xf0) >> 4);
+    }
+    return 1;			/* All went well - we hope! */
+}
+
+static inline int imm_byte_in(unsigned short base, char *buffer, int len)
+{
+    int i;
+
+    /*
+     * The following is based on documented timing signals
+     */
+    w_ctr(base, 0x4);
+    for (i = len; i; i--) {
+	w_ctr(base, 0x26);
+	*buffer++ = r_dtr(base);
+	w_ctr(base, 0x25);
+    }
+    return 1;			/* All went well - we hope! */
+}
+
+static inline int imm_epp_in(unsigned short base, char *buffer, int len)
+{
+    int i;
+    for (i = len; i; i--)
+	*buffer++ = r_epp(base);
+    return 1;
+}
+
+static int imm_out(int host_no, char *buffer, int len)
+{
+    int r;
+    unsigned short ppb = IMM_BASE(host_no);
+
+    r = imm_wait(host_no);
+
+    /*
+     * Make sure that:
+     * a) the SCSI bus is BUSY (device still listening)
+     * b) the device is listening
+     */
+    if ((r & 0x18) != 0x08) {
+	imm_fail(host_no, DID_ERROR);
+	printk("IMM: returned SCSI status %2x\n", r);
+	return 0;
+    }
+    switch (imm_hosts[host_no].mode) {
+    case IMM_EPP_32:
+    case IMM_EPP_16:
+    case IMM_EPP_8:
+        epp_reset(ppb);
+        w_ctr(ppb, 0x4);
+	r = imm_epp_out(ppb, buffer, len);
+        w_ctr(ppb, 0xc);
+        ecp_sync(ppb);
+	break;
+
+    case IMM_NIBBLE:
+    case IMM_PS2:
+	/* 8 bit output, with a loop */
+	r = imm_byte_out(ppb, buffer, len);
+	break;
+
+    default:
+	printk("IMM: bug in imm_out()\n");
+	r = 0;
+    }
+    return r;
+}
+
+static int imm_in(int host_no, char *buffer, int len)
+{
+    int r;
+    unsigned short ppb = IMM_BASE(host_no);
+
+    r = imm_wait(host_no);
+
+    /*
+     * Make sure that:
+     * a) the SCSI bus is BUSY (device still listening)
+     * b) the device is sending data
+     */
+    if ((r & 0x18) != 0x18) {
+	imm_fail(host_no, DID_ERROR);
+	return 0;
+    }
+    switch (imm_hosts[host_no].mode) {
+    case IMM_NIBBLE:
+	/* 4 bit input, with a loop */
+	r = imm_nibble_in(ppb, buffer, len);
+	w_ctr(ppb, 0xc);
+	break;
+
+    case IMM_PS2:
+	/* 8 bit input, with a loop */
+	r = imm_byte_in(ppb, buffer, len);
+	w_ctr(ppb, 0xc);
+	break;
+
+    case IMM_EPP_32:
+    case IMM_EPP_16:
+    case IMM_EPP_8:
+        epp_reset(ppb);
+        w_ctr(ppb, 0x24);
+	r = imm_epp_in(ppb, buffer, len);
+        w_ctr(ppb, 0x2c);
+        ecp_sync(ppb);
+	break;
+
+    default:
+	printk("IMM: bug in imm_ins()\n");
+	r = 0;
+	break;
+    }
+    return r;
+}
+
+static int imm_cpp(unsigned short ppb, unsigned char b)
+{
+    /*
+     * Comments on udelay values refer to the
+     * Command Packet Protocol (CPP) timing diagram.
+     */
+
+    unsigned char s1, s2, s3;
+    w_ctr(ppb, 0x0c);
+    udelay(2);			/* 1 usec - infinite */
+    w_dtr(ppb, 0xaa);
+    udelay(10);			/* 7 usec - infinite */
+    w_dtr(ppb, 0x55);
+    udelay(10);			/* 7 usec - infinite */
+    w_dtr(ppb, 0x00);
+    udelay(10);			/* 7 usec - infinite */
+    w_dtr(ppb, 0xff);
+    udelay(10);			/* 7 usec - infinite */
+    s1 = r_str(ppb) & 0xb8;
+    w_dtr(ppb, 0x87);
+    udelay(10);			/* 7 usec - infinite */
+    s2 = r_str(ppb) & 0xb8;
+    w_dtr(ppb, 0x78);
+    udelay(10);			/* 7 usec - infinite */
+    s3 = r_str(ppb) & 0x38;
+    /*
+     * Values for b are:
+     * 0000 00aa    Assign address aa to current device
+     * 0010 00aa    Select device aa in EPP Winbond mode
+     * 0010 10aa    Select device aa in EPP mode
+     * 0011 xxxx    Deselect all devices
+     * 0110 00aa    Test device aa
+     * 1101 00aa    Select device aa in ECP mode
+     * 1110 00aa    Select device aa in Compatible mode
+     */
+    w_dtr(ppb, b);
+    udelay(2);			/* 1 usec - infinite */
+    w_ctr(ppb, 0x0c);
+    udelay(10);			/* 7 usec - infinite */
+    w_ctr(ppb, 0x0d);
+    udelay(2);			/* 1 usec - infinite */
+    w_ctr(ppb, 0x0c);
+    udelay(10);			/* 7 usec - infinite */
+    w_dtr(ppb, 0xff);
+    udelay(10);			/* 7 usec - infinite */
+
+    /*
+     * The following table is electrical pin values.
+     * (BSY is inverted at the CTR register)
+     *
+     *       BSY  ACK  POut SEL  Fault
+     * S1    0    X    1    1    1
+     * S2    1    X    0    1    1
+     * S3    L    X    1    1    S
+     *
+     * L => Last device in chain
+     * S => Selected
+     *
+     * Observered values for S1,S2,S3 are:
+     * Disconnect => f8/58/78
+     * Connect    => f8/58/70
+     */
+    if ((s1 == 0xb8) && (s2 == 0x18) && (s3 == 0x30))
+	return 1;		/* Connected */
+    if ((s1 == 0xb8) && (s2 == 0x18) && (s3 == 0x38))
+	return 0;		/* Disconnected */
+
+    return -1;			/* No device present */
+}
+
+static inline int imm_connect(int host_no, int flag)
+{
+    unsigned short ppb = IMM_BASE(host_no);
+
+    imm_cpp(ppb, 0xe0);		/* Select device 0 in compatible mode */
+    imm_cpp(ppb, 0x30);		/* Disconnect all devices */
+
+    if ((imm_hosts[host_no].mode == IMM_EPP_8) ||
+	(imm_hosts[host_no].mode == IMM_EPP_16) ||
+	(imm_hosts[host_no].mode == IMM_EPP_32))
+	return imm_cpp(ppb, 0x28);	/* Select device 0 in EPP mode */
+    return imm_cpp(ppb, 0xe0);	/* Select device 0 in compatible mode */
+}
+
+static void imm_disconnect(int host_no)
+{
+    unsigned short ppb = IMM_BASE(host_no);
+
+    imm_cpp(ppb, 0x30);		/* Disconnect all devices */
+}
+
+static int imm_select(int host_no, int target)
+{
+    int k;
+    unsigned short ppb = IMM_BASE(host_no);
+
+    /*
+     * Firstly we want to make sure there is nothing
+     * holding onto the SCSI bus.
+     */
+    w_ctr(ppb, 0xc);
+
+    k = IMM_SELECT_TMO;
+    do {
+	k--;
+    } while ((r_str(ppb) & 0x08) && (k));
+
+    if (!k)
+	return 0;
+
+    /*
+     * Now assert the SCSI ID (HOST and TARGET) on the data bus
+     */
+    w_ctr(ppb, 0x4);
+    w_dtr(ppb, 0x80 | (1 << target));
+    udelay(1);
+
+    /*
+     * Deassert SELIN first followed by STROBE
+     */
+    w_ctr(ppb, 0xc);
+    w_ctr(ppb, 0xd);
+
+    /*
+     * ACK should drop low while SELIN is deasserted.
+     * FAULT should drop low when the SCSI device latches the bus.
+     */
+    k = IMM_SELECT_TMO;
+    do {
+	k--;
+    }
+    while (!(r_str(ppb) & 0x08) && (k));
+
+    /*
+     * Place the interface back into a sane state (status mode)
+     */
+    w_ctr(ppb, 0xc);
+    return (k) ? 1 : 0;
+}
+
+static int imm_init(int host_no)
+{
+    int retv;
+
+#if defined(CONFIG_PARPORT) || defined(CONFIG_PARPORT_MODULE)
+    if (imm_pb_claim(host_no))
+        while (imm_hosts[host_no].p_busy)
+            schedule();         /* We can safe schedule here */
+#endif
+    retv = imm_connect(host_no, 0);
+
+    if (retv == 1) {
+	imm_reset_pulse(IMM_BASE(host_no));
+	udelay(1000);		/* Delay to allow devices to settle */
+	imm_disconnect(host_no);
+	udelay(1000);		/* Another delay to allow devices to settle */
+	retv = device_check(host_no);
+	imm_pb_release(host_no);
+	return retv;
+    }
+
+    imm_pb_release(host_no);
+    return 1;
+}
+
+static inline int imm_send_command(Scsi_Cmnd * cmd)
+{
+    int host_no = cmd->host->unique_id;
+    int k;
+
+    /* NOTE: IMM uses byte pairs */
+    for (k = 0; k < cmd->cmd_len; k += 2)
+	if (!imm_out(host_no, &cmd->cmnd[k], 2))
+	    return 0;
+    return 1;
+}
+
+/*
+ * The bulk flag enables some optimisations in the data transfer loops,
+ * it should be true for any command that transfers data in integral
+ * numbers of sectors.
+ * 
+ * The driver appears to remain stable if we speed up the parallel port
+ * i/o in this function, but not elsewhere.
+ */
+static int imm_completion(Scsi_Cmnd * cmd)
+{
+    /* Return codes:
+     * -1     Error
+     *  0     Told to schedule
+     *  1     Finished data transfer
+     */
+    int host_no = cmd->host->unique_id;
+    unsigned short ppb = IMM_BASE(host_no);
+    unsigned long start_jiffies = jiffies;
+
+    unsigned char r, v;
+    int fast, bulk, status;
+
+    v = cmd->cmnd[0];
+    bulk = ((v == READ_6) ||
+	    (v == READ_10) ||
+	    (v == WRITE_6) ||
+	    (v == WRITE_10));
+
+    /*
+     * We only get here if the drive is ready to comunicate,
+     * hence no need for a full imm_wait.
+     */
+    w_ctr(ppb, 0x0c);
+    r = (r_str(ppb) & 0xb8);
+
+    /*
+     * while (device is not ready to send status byte)
+     *     loop;
+     */
+    while (r != (unsigned char) 0xb8) {
+	/*
+	 * If we have been running for more than a full timer tick
+	 * then take a rest.
+	 */
+	if (jiffies > start_jiffies + 1)
+	    return 0;
+
+	/*
+	 * FAIL if:
+	 * a) Drive status is screwy (!ready && !present)
+	 * b) Drive is requesting/sending more data than expected
+	 */
+	if (((r & 0x88) != 0x88) || (cmd->SCp.this_residual <= 0)) {
+	    imm_fail(host_no, DID_ERROR);
+	    return -1;		/* ERROR_RETURN */
+	}
+	/* determine if we should use burst I/O */
+	if (imm_hosts[host_no].rd == 0) {
+	    fast = (bulk && (cmd->SCp.this_residual >= IMM_BURST_SIZE)) ? IMM_BURST_SIZE : 2;
+	    status = imm_out(host_no, cmd->SCp.ptr, fast);
+	} else {
+	    fast = (bulk && (cmd->SCp.this_residual >= IMM_BURST_SIZE)) ? IMM_BURST_SIZE : 1;
+	    status = imm_in(host_no, cmd->SCp.ptr, fast);
+	}
+
+	cmd->SCp.ptr += fast;
+	cmd->SCp.this_residual -= fast;
+
+	if (!status) {
+	    imm_fail(host_no, DID_BUS_BUSY);
+	    return -1;		/* ERROR_RETURN */
+	}
+	if (cmd->SCp.buffer && !cmd->SCp.this_residual) {
+	    /* if scatter/gather, advance to the next segment */
+	    if (cmd->SCp.buffers_residual--) {
+		cmd->SCp.buffer++;
+		cmd->SCp.this_residual = cmd->SCp.buffer->length;
+		cmd->SCp.ptr = cmd->SCp.buffer->address;
+
+		/*
+		 * Make sure that we transfer even number of bytes
+		 * otherwise it makes imm_byte_out() messy.
+		 */
+		if (cmd->SCp.this_residual & 0x01) {
+		    cmd->SCp.this_residual++;
+		    printk("IMM: adjusted buffer for 16 bit transfer\n");
+		}
+	    }
+	}
+	/* Now check to see if the drive is ready to comunicate */
+	w_ctr(ppb, 0x0c);
+	r = (r_str(ppb) & 0xb8);
+
+	/* If not, drop back down to the scheduler and wait a timer tick */
+	if (!(r & 0x80))
+	    return 0;
+    }
+    return 1;			/* FINISH_RETURN */
+}
+
+/* deprecated synchronous interface */
+int imm_command(Scsi_Cmnd * cmd)
+{
+    static int first_pass = 1;
+    int host_no = cmd->host->unique_id;
+
+    if (first_pass) {
+	printk("imm: using non-queuing interface\n");
+	first_pass = 0;
+    }
+    if (imm_hosts[host_no].cur_cmd) {
+	printk("IMM: bug in imm_command\n");
+	return 0;
+    }
+    imm_hosts[host_no].failed = 0;
+    imm_hosts[host_no].jstart = jiffies;
+    imm_hosts[host_no].cur_cmd = cmd;
+    cmd->result = DID_ERROR << 16;	/* default return code */
+    cmd->SCp.phase = 0;
+
+    imm_pb_claim(host_no);
+
+    while (imm_engine(&imm_hosts[host_no], cmd))
+	schedule();
+
+    if (cmd->SCp.phase)		/* Only disconnect if we have connected */
+	imm_disconnect(cmd->host->unique_id);
+
+    imm_pb_release(host_no);
+    imm_hosts[host_no].cur_cmd = 0;
+    return cmd->result;
+}
+
+/*
+ * Since the IMM itself doesn't generate interrupts, we use
+ * the scheduler's task queue to generate a stream of call-backs and
+ * complete the request when the drive is ready.
+ */
+static void imm_interrupt(void *data)
+{
+    imm_struct *tmp = (imm_struct *) data;
+    Scsi_Cmnd *cmd = tmp->cur_cmd;
+
+    if (!cmd) {
+	printk("IMM: bug in imm_interrupt\n");
+	return;
+    }
+    if (imm_engine(tmp, cmd)) {
+	tmp->imm_tq.data = (void *) tmp;
+	tmp->imm_tq.sync = 0;
+	queue_task(&tmp->imm_tq, &tq_timer);
+	return;
+    }
+    /* Command must of completed hence it is safe to let go... */
+#if IMM_DEBUG > 0
+    switch ((cmd->result >> 16) & 0xff) {
+    case DID_OK:
+	break;
+    case DID_NO_CONNECT:
+	printk("imm: no device at SCSI ID %i\n", cmd->target);
+	break;
+    case DID_BUS_BUSY:
+	printk("imm: BUS BUSY - EPP timeout detected\n");
+	break;
+    case DID_TIME_OUT:
+	printk("imm: unknown timeout\n");
+	break;
+    case DID_ABORT:
+	printk("imm: told to abort\n");
+	break;
+    case DID_PARITY:
+	printk("imm: parity error (???)\n");
+	break;
+    case DID_ERROR:
+	printk("imm: internal driver error\n");
+	break;
+    case DID_RESET:
+	printk("imm: told to reset device\n");
+	break;
+    case DID_BAD_INTR:
+	printk("imm: bad interrupt (???)\n");
+	break;
+    default:
+	printk("imm: bad return code (%02x)\n", (cmd->result >> 16) & 0xff);
+    }
+#endif
+
+    if (cmd->SCp.phase > 1)
+	imm_disconnect(cmd->host->unique_id);
+    if (cmd->SCp.phase > 0)
+	imm_pb_release(cmd->host->unique_id);
+
+    tmp->cur_cmd = 0;
+    cmd->scsi_done(cmd);
+    return;
+}
+
+static int imm_engine(imm_struct * tmp, Scsi_Cmnd * cmd)
+{
+    int host_no = cmd->host->unique_id;
+    unsigned short ppb = IMM_BASE(host_no);
+    unsigned char l = 0, h = 0;
+    int retv, x;
+
+    /* First check for any errors that may of occured
+     * Here we check for internal errors
+     */
+    if (tmp->failed)
+	return 0;
+
+    switch (cmd->SCp.phase) {
+    case 0:			/* Phase 0 - Waiting for parport */
+	if ((jiffies - tmp->jstart) > HZ) {
+	    /*
+	     * We waited more than a second
+	     * for parport to call us
+	     */
+	    imm_fail(host_no, DID_BUS_BUSY);
+	    return 0;
+	}
+	return 1;		/* wait until imm_wakeup claims parport */
+	/* Phase 1 - Connected */
+    case 1:
+	imm_connect(host_no, CONNECT_EPP_MAYBE);
+	cmd->SCp.phase++;
+
+	/* Phase 2 - We are now talking to the scsi bus */
+    case 2:
+	if (!imm_select(host_no, cmd->target)) {
+	    imm_fail(host_no, DID_NO_CONNECT);
+	    return 0;
+	}
+	cmd->SCp.phase++;
+
+	/* Phase 3 - Ready to accept a command */
+    case 3:
+	w_ctr(ppb, 0x0c);
+	if (!(r_str(ppb) & 0x80))
+	    return 1;
+
+	if (!imm_send_command(cmd))
+	    return 0;
+	cmd->SCp.phase++;
+
+	/* Phase 4 - Setup scatter/gather buffers */
+    case 4:
+	if (cmd->use_sg) {
+	    /* if many buffers are available, start filling the first */
+	    cmd->SCp.buffer = (struct scatterlist *) cmd->request_buffer;
+	    cmd->SCp.this_residual = cmd->SCp.buffer->length;
+	    cmd->SCp.ptr = cmd->SCp.buffer->address;
+	} else {
+	    /* else fill the only available buffer */
+	    cmd->SCp.buffer = NULL;
+	    cmd->SCp.this_residual = cmd->request_bufflen;
+	    cmd->SCp.ptr = cmd->request_buffer;
+	}
+	cmd->SCp.buffers_residual = cmd->use_sg;
+	cmd->SCp.phase++;
+	if (cmd->SCp.this_residual & 0x01) {
+	    cmd->SCp.this_residual++;
+	    printk("IMM: adjusted buffer for 16 bit transfer\n");
+	}
+	/* Phase 5 - Pre-Data transfer stage */
+    case 5:
+	/* Spin lock for BUSY */
+	w_ctr(ppb, 0x0c);
+	if (!(r_str(ppb) & 0x80))
+	    return 1;
+
+	/* Require negotiation for read requests */
+	x = (r_str(ppb) & 0xb8);
+	tmp->rd = (x & 0x10) ? 1 : 0;
+	tmp->dp = (x & 0x20) ? 0 : 1;
+
+	if ((tmp->dp) && (tmp->rd))
+	    if (imm_negotiate(tmp))
+		return 0;
+	cmd->SCp.phase++;
+
+	/* Phase 6 - Data transfer stage */
+    case 6:
+	/* Spin lock for BUSY */
+	w_ctr(ppb, 0x0c);
+	if (!(r_str(ppb) & 0x80))
+	    return 1;
+
+	if (tmp->dp) {
+	    retv = imm_completion(cmd);
+	    if (retv == -1)
+		return 0;
+	    if (retv == 0)
+		return 1;
+	}
+	cmd->SCp.phase++;
+
+	/* Phase 7 - Post data transfer stage */
+    case 7:
+	if ((tmp->dp) && (tmp->rd)) {
+	    if ((tmp->mode == IMM_NIBBLE) || (tmp->mode == IMM_PS2)) {
+		w_ctr(ppb, 0x4);
+		w_ctr(ppb, 0xc);
+		w_ctr(ppb, 0xe);
+		w_ctr(ppb, 0x4);
+	    }
+	}
+	cmd->SCp.phase++;
+
+	/* Phase 8 - Read status/message */
+    case 8:
+	/* Check for data overrun */
+	if (imm_wait(host_no) != (unsigned char) 0xb8) {
+	    imm_fail(host_no, DID_ERROR);
+	    return 0;
+	}
+	if (imm_negotiate(tmp))
+	    return 0;
+	if (imm_in(host_no, &l, 1)) {	/* read status byte */
+	    /* Check for optional message byte */
+	    if (imm_wait(host_no) == (unsigned char) 0xb8)
+		imm_in(host_no, &h, 1);
+	    cmd->result = (DID_OK << 16) + (l & STATUS_MASK);
+	}
+	if ((tmp->mode == IMM_NIBBLE) || (tmp->mode == IMM_PS2)) {
+	    w_ctr(ppb, 0x4);
+	    w_ctr(ppb, 0xc);
+	    w_ctr(ppb, 0xe);
+	    w_ctr(ppb, 0x4);
+	}
+	return 0;		/* Finished */
+	break;
+
+    default:
+	printk("imm: Invalid scsi phase\n");
+    }
+    return 0;
+}
+
+int imm_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+    int host_no = cmd->host->unique_id;
+
+    if (imm_hosts[host_no].cur_cmd) {
+	printk("IMM: bug in imm_queuecommand\n");
+	return 0;
+    }
+    imm_hosts[host_no].failed = 0;
+    imm_hosts[host_no].jstart = jiffies;
+    imm_hosts[host_no].cur_cmd = cmd;
+    cmd->scsi_done = done;
+    cmd->result = DID_ERROR << 16;	/* default return code */
+    cmd->SCp.phase = 0;		/* bus free */
+
+    imm_pb_claim(host_no);
+
+    imm_hosts[host_no].imm_tq.data = imm_hosts + host_no;
+    imm_hosts[host_no].imm_tq.sync = 0;
+    queue_task(&imm_hosts[host_no].imm_tq, &tq_immediate);
+    mark_bh(IMMEDIATE_BH);
+
+    return 0;
+}
+
+/*
+ * Aimmrently the the disk->capacity attribute is off by 1 sector 
+ * for all disk drives.  We add the one here, but it should really
+ * be done in sd.c.  Even if it gets fixed there, this will still
+ * work.
+ */
+int imm_biosparam(Disk * disk, kdev_t dev, int ip[])
+{
+    ip[0] = 0x40;
+    ip[1] = 0x20;
+    ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
+    if (ip[2] > 1024) {
+	ip[0] = 0xff;
+	ip[1] = 0x3f;
+	ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
+	if (ip[2] > 1023)
+	    ip[2] = 1023;
+    }
+    return 0;
+}
+
+int imm_abort(Scsi_Cmnd * cmd)
+{
+    /*
+     * There is no method for aborting commands since Iomega
+     * have tied the SCSI_MESSAGE line high in the interface
+     */
+
+    switch (cmd->SCp.phase) {
+    case 0:			/* Do not have access to parport */
+    case 1:			/* Have not connected to interface */
+	cmd->result = DID_ABORT;
+	cmd->done(cmd);
+	return SCSI_ABORT_SUCCESS;
+	break;
+    default:			/* SCSI command sent, can not abort */
+	return SCSI_ABORT_BUSY;
+	break;
+    }
+}
+
+void imm_reset_pulse(unsigned int base)
+{
+    w_ctr(base, 0x04);
+    w_dtr(base, 0x40);
+    udelay(1);
+    w_ctr(base, 0x0c);
+    w_ctr(base, 0x0d);
+    udelay(50);
+    w_ctr(base, 0x0c);
+    w_ctr(base, 0x04);
+}
+
+int imm_reset(Scsi_Cmnd * cmd, unsigned int x)
+{
+    int host_no = cmd->host->unique_id;
+
+    /*
+     * PHASE1:
+     * Bring the interface crashing down on whatever is running
+     * hopefully this will kill the request.
+     * Bring back up the interface, reset the drive (and anything
+     * attached for that manner)
+     */
+    if (cmd)
+	if (cmd->SCp.phase)
+	    imm_disconnect(cmd->host->unique_id);
+
+    imm_connect(host_no, CONNECT_NORMAL);
+    imm_reset_pulse(IMM_BASE(host_no));
+    udelay(1000);		/* delay for devices to settle down */
+    imm_disconnect(host_no);
+    udelay(1000);		/* Additional delay to allow devices to settle down */
+
+    /*
+     * PHASE2:
+     * Sanity check for the sake of mid-level driver
+     */
+    if (!cmd) {
+	printk("imm bus reset called for invalid command.\n");
+	return SCSI_RESET_NOT_RUNNING;
+    }
+    /*
+     * PHASE3:
+     * Flag the current command as having died due to reset
+     */
+    imm_connect(host_no, CONNECT_NORMAL);
+    imm_fail(host_no, DID_RESET);
+
+    /* Since the command was already on the timer queue imm_interrupt
+     * will be called shortly.
+     */
+    return SCSI_RESET_PENDING;
+}
+
+static int device_check(int host_no)
+{
+    /* This routine looks for a device and then attempts to use EPP
+       to send a command. If all goes as planned then EPP is available. */
+
+    static char cmd[6] =
+    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    int loop, old_mode, status, k, ppb = IMM_BASE(host_no);
+    unsigned char l;
+
+    old_mode = imm_hosts[host_no].mode;
+    for (loop = 0; loop < 8; loop++) {
+        /* Attempt to use EPP for Test Unit Ready */
+        if ((ppb & 0x0007) == 0x0000)
+            imm_hosts[host_no].mode = IMM_EPP_32;
+
+      second_pass:
+        imm_connect(host_no, CONNECT_EPP_MAYBE);
+        /* Select SCSI device */
+        if (!imm_select(host_no, loop)) {
+            imm_disconnect(host_no);
+            continue;
+        }
+        printk("imm: Found device at ID %i, Attempting to use %s\n", loop,
+               IMM_MODE_STRING[imm_hosts[host_no].mode]);
+
+        /* Send SCSI command */
+        status = 1;
+        w_ctr(ppb, 0x0c);
+        for (l = 0; (l < 3) && (status); l++)
+            status = imm_out(host_no, &cmd[l<<1], 2);
+
+        if (!status) {
+            imm_disconnect(host_no);
+            imm_connect(host_no, CONNECT_EPP_MAYBE);
+            w_dtr(ppb, 0x40);
+            w_ctr(ppb, 0x08);
+            udelay(30);
+            w_ctr(ppb, 0x0c);
+            udelay(1000);
+            imm_disconnect(host_no);
+            udelay(1000);
+            if (imm_hosts[host_no].mode == IMM_EPP_32) {
+                imm_hosts[host_no].mode = old_mode;
+                goto second_pass;
+            }
+            printk("imm: Unable to establish communication, aborting driver load.\n");
+            return 1;
+        }
+        w_ctr(ppb, 0x0c);
+
+        k = 1000000;            /* 1 Second */
+        do {
+            l = r_str(ppb);
+            k--;
+            udelay(1);
+        } while (!(l & 0x80) && (k));
+
+        l &= 0xb8;
+
+        if (l != 0xb8) {
+            imm_disconnect(host_no);
+            imm_connect(host_no, CONNECT_EPP_MAYBE);
+	    imm_reset_pulse(IMM_BASE(host_no));
+            udelay(1000);
+            imm_disconnect(host_no);
+            udelay(1000);
+            if (imm_hosts[host_no].mode == IMM_EPP_32) {
+                imm_hosts[host_no].mode = old_mode;
+                goto second_pass;
+            }
+            printk("imm: Unable to establish communication, aborting driver load.\n");
+            return 1;
+        }
+        imm_disconnect(host_no);
+        printk("imm: Communication established with ID %i using %s\n", loop,
+               IMM_MODE_STRING[imm_hosts[host_no].mode]);
+        imm_connect(host_no, CONNECT_EPP_MAYBE);
+	imm_reset_pulse(IMM_BASE(host_no));
+        udelay(1000);
+        imm_disconnect(host_no);
+        udelay(1000);
+        return 0;
+    }
+    printk("imm: No devices found, aborting driver load.\n");
+    return 1;
+}
+
diff -u --recursive --new-file v2.1.121/linux/drivers/scsi/imm.h linux/drivers/scsi/imm.h
--- v2.1.121/linux/drivers/scsi/imm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/imm.h	Wed Sep 16 14:09:26 1998
@@ -0,0 +1,166 @@
+/*  Driver for the Iomega MatchMaker parallel port SCSI HBA embedded in 
+ * the Iomega ZIP Plus drive
+ * 
+ * (c) 1998     David Campbell	   campbell@torque.net
+ *
+ * Please note that I live in Perth, Western Australia. GMT+0800
+ */
+
+#ifndef _IMM_H
+#define _IMM_H
+
+#define   IMM_VERSION   "2.00"
+
+/* 
+ * 10 Apr 1998 (Good Friday) - Received EN144302 by email from Iomega.
+ * Scarry thing is the level of support from one of their managers.
+ * The onus is now on us (the developers) to shut up and start coding.
+ *						11Apr98	[ 0.10 ]
+ *
+ * --- SNIP ---
+ *
+ * It manages to find the drive which is a good start. Writing data during
+ * data phase is known to be broken (due to requirements of two byte writes).
+ * Removing "Phase" debug messages.
+ *
+ * PS: Took four hours of coding after I bought a drive.
+ *	ANZAC Day (Aus "War Veterans Holiday")	25Apr98 [ 0.14 ]
+ *
+ * Ten minutes later after a few fixes.... (LITERALLY!!!)
+ * Have mounted disk, copied file, dismounted disk, remount disk, diff file
+ *                    -----  It actually works!!! -----
+ *						25Apr98 [ 0.15 ]
+ *
+ * Twenty minutes of mucking around, rearanged the IEEE negotiate mechanism.
+ * Now have byte mode working (only EPP and ECP to go now... :=)
+ *						26Apr98 [ 0.16 ]
+ *
+ * Thirty minutes of further coding results in EPP working on my machine.
+ *						27Apr98 [ 0.17 ]
+ *
+ * Due to work commitments and inability to get a "true" ECP mode functioning
+ * I have decided to code the parport support into imm.
+ *						09Jun98 [ 0.18 ]
+ *
+ * Driver is now out of beta testing.
+ * Support for parport has been added.
+ * Now distributed with the ppa driver.
+ *						12Jun98 [ 2.00 ]
+ *
+ * Err.. It appears that imm-2.00 was broken....
+ *						18Jun98 [ 2.01 ]
+ *
+ * Patch applied to sync this against the Linux 2.1.x kernel code
+ * Included qboot_zip.sh
+ *						21Jun98 [ 2.02 ]
+ */
+/* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
+
+#ifdef IMM_CODE
+#include  <linux/stddef.h>
+#include  <linux/module.h>
+#include  <linux/kernel.h>
+#include  <linux/tqueue.h>
+#include  <linux/ioport.h>
+#include  <linux/delay.h>
+#include  <linux/proc_fs.h>
+#include  <linux/stat.h>
+#include  <linux/blk.h>
+#include  <linux/sched.h>
+#include  <linux/interrupt.h>
+
+#include  <asm/io.h>
+#include  "sd.h"
+#include  "hosts.h"
+/* batteries not included :-) */
+
+/*
+ * modes in which the driver can operate 
+ */
+#define   IMM_AUTODETECT        0	/* Autodetect mode                */
+#define   IMM_NIBBLE            1	/* work in standard 4 bit mode    */
+#define   IMM_PS2               2	/* PS/2 byte mode         */
+#define   IMM_EPP_8             3	/* EPP mode, 8 bit                */
+#define   IMM_EPP_16            4	/* EPP mode, 16 bit               */
+#define   IMM_EPP_32            5	/* EPP mode, 32 bit               */
+#define   IMM_UNKNOWN           6	/* Just in case...                */
+
+static char *IMM_MODE_STRING[] =
+{
+    "Autodetect",
+    "SPP",
+    "PS/2",
+    "EPP 8 bit",
+    "EPP 16 bit",
+    "EPP 32 bit",
+    "Unknown"};
+
+/* This is a global option */
+int imm_sg = SG_ALL;		/* enable/disable scatter-gather. */
+
+/* other options */
+#define IMM_CAN_QUEUE   1	/* use "queueing" interface */
+#define IMM_BURST_SIZE	512	/* data burst size */
+#define IMM_SELECT_TMO  500	/* 500 how long to wait for target ? */
+#define IMM_SPIN_TMO    5000	/* 50000 imm_wait loop limiter */
+#define IMM_DEBUG	0	/* debuging option */
+#define IN_EPP_MODE(x) (x == IMM_EPP_8 || x == IMM_EPP_16 || x == IMM_EPP_32)
+
+/* args to imm_connect */
+#define CONNECT_EPP_MAYBE 1
+#define CONNECT_NORMAL  0
+
+#define inb_x inb
+#define r_dtr(x)        (unsigned char)inb_x((x))
+#define r_str(x)        (unsigned char)inb_x((x)+1)
+#define r_ctr(x)        (unsigned char)inb_x((x)+2)
+#define r_epp(x)        (unsigned char)inb_x((x)+4)
+#define r_fifo(x)       (unsigned char)inb_x((x)+0x400)
+#define r_ecr(x)        (unsigned char)inb_x((x)+0x402)
+
+#define outb_x outb
+#define w_dtr(x,y)      outb_x(y, (x))
+#define w_str(x,y)      outb_x(y, (x)+1)
+#define w_ctr(x,y)      outb_x(y, (x)+2)
+#define w_epp(x,y)      outb_x(y, (x)+4)
+#define w_fifo(x,y)     outb_x(y, (x)+0x400)
+#define w_ecr(x,y)      outb_x(y, (x)+0x402)
+
+static int imm_engine(imm_struct *, Scsi_Cmnd *);
+static int imm_in(int, char *, int);
+static int imm_init(int);
+static void imm_interrupt(void *);
+static int imm_out(int, char *, int);
+
+struct proc_dir_entry proc_scsi_imm =
+{PROC_SCSI_PPA, 3, "imm", S_IFDIR | S_IRUGO | S_IXUGO, 2};
+#else
+extern struct proc_dir_entry proc_scsi_imm;
+#define imm_release 0
+#endif
+
+int imm_detect(Scsi_Host_Template *);
+const char *imm_info(struct Scsi_Host *);
+int imm_command(Scsi_Cmnd *);
+int imm_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
+int imm_abort(Scsi_Cmnd *);
+int imm_reset(Scsi_Cmnd *, unsigned int);
+int imm_proc_info(char *, char **, off_t, int, int, int);
+int imm_biosparam(Disk *, kdev_t, int *);
+
+#define IMM {	proc_dir:		&proc_scsi_imm,			\
+		proc_info:		imm_proc_info,			\
+		name:			"Iomega ZIP Plus drive",	\
+		detect:			imm_detect,			\
+		release:		imm_release,			\
+		command:		imm_command,			\
+		queuecommand:		imm_queuecommand,		\
+		abort:			imm_abort,			\
+		reset:			imm_reset,			\
+		bios_param:		imm_biosparam,			\
+		this_id:		7,				\
+		sg_tablesize:		SG_ALL,				\
+		cmd_per_lun:		1,				\
+		use_clustering:		ENABLE_CLUSTERING		\
+}
+#endif				/* _IMM_H */
diff -u --recursive --new-file v2.1.121/linux/drivers/scsi/ppa.c linux/drivers/scsi/ppa.c
--- v2.1.121/linux/drivers/scsi/ppa.c	Tue Jul 21 00:15:31 1998
+++ linux/drivers/scsi/ppa.c	Mon Sep 14 11:11:44 1998
@@ -22,6 +22,7 @@
 #include "sd.h"
 #include "hosts.h"
 int ppa_release(struct Scsi_Host *);
+static void ppa_reset_pulse(unsigned int base);
 
 typedef struct {
     struct pardevice *dev;	/* Parport device entry         */
@@ -35,16 +36,16 @@
     unsigned int p_busy:1;	/* Parport sharing busy flag    */
 } ppa_struct;
 
-#define PPA_EMPTY \
-{NULL,		/* dev */	\
--1,		/* base */	\
-PPA_AUTODETECT,	/* mode */	\
--1,		/* host */	\
-NULL,		/* cur_cmd */	\
-{0, 0, ppa_interrupt, NULL},	\
-0,		/* jstart */	\
-0,		/* failed */	\
-0		/* p_busy */	\
+#define PPA_EMPTY	\
+{	dev:		NULL,		\
+	base:		-1,		\
+	mode:		PPA_AUTODETECT,	\
+	host:		-1,		\
+	cur_cmd:	NULL,		\
+	ppa_tq:		{0, 0, ppa_interrupt, NULL},	\
+	jstart:		0,		\
+	failed:		0,		\
+	p_busy:		0		\
 }
 
 #include  "ppa.h"
@@ -139,7 +140,8 @@
 	 */
 	if (ppa_pb_claim(i))
 	    while (ppa_hosts[i].p_busy)
-		schedule(); /* Whe can safe schedule() here */
+		schedule();	/* We are safe to schedule here */
+
 	ppb = PPA_BASE(i) = ppa_hosts[i].dev->port->base;
 	w_ctr(ppb, 0x0c);
 	modes = ppa_hosts[i].dev->port->modes;
@@ -557,7 +559,7 @@
     int i;
     for (i = len; i; i--) {
 	outb(*buffer++, epp_p);
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 2
+#ifdef CONFIG_SCSI_PPA_HAVE_PEDANTIC
 	if (inb(str_p) & 0x01)
 	    return 0;
 #endif
@@ -588,14 +590,12 @@
     case PPA_EPP_8:
 	epp_reset(ppb);
 	w_ctr(ppb, 0x4);
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 1
+#ifdef CONFIG_SCSI_PPA_HAVE_PEDANTIC
 	r = ppa_epp_out(ppb + 4, ppb + 1, buffer, len);
 #else
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC == 0
 	if (!(((long) buffer | len) & 0x03))
 	    outsl(ppb + 4, buffer, len >> 2);
 	else
-#endif
 	    outsb(ppb + 4, buffer, len);
 	w_ctr(ppb, 0xc);
 	r = !(r_str(ppb) & 0x01);
@@ -616,7 +616,7 @@
     int i;
     for (i = len; i; i--) {
 	*buffer++ = inb(epp_p);
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 2
+#ifdef CONFIG_SCSI_PPA_HAVE_PEDANTIC
 	if (inb(str_p) & 0x01)
 	    return 0;
 #endif
@@ -655,14 +655,12 @@
     case PPA_EPP_8:
 	epp_reset(ppb);
 	w_ctr(ppb, 0x24);
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 1
+#ifdef CONFIG_SCSI_PPA_HAVE_PEDANTIC
 	r = ppa_epp_in(ppb + 4, ppb + 1, buffer, len);
 #else
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC == 0
 	if (!(((long) buffer | len) & 0x03))
 	    insl(ppb + 4, buffer, len >> 2);
 	else
-#endif
 	    insb(ppb + 4, buffer, len);
 	w_ctr(ppb, 0x2c);
 	r = !(r_str(ppb) & 0x01);
@@ -773,7 +771,7 @@
 #if defined(CONFIG_PARPORT) || defined(CONFIG_PARPORT_MODULE)
     if (ppa_pb_claim(host_no))
 	while (ppa_hosts[host_no].p_busy)
-	    schedule(); /* Whe can safe schedule() here */
+	    schedule();		/* We can safe schedule here */
 #endif
 
     ppa_disconnect(host_no);
@@ -789,16 +787,11 @@
     if ((r_str(ppb) & 0x08) == 0x00)
 	retv--;
 
-    /* This is a SCSI BUS reset signal */
-    if (!retv) {
-	w_dtr(ppb, 0x40);
-	w_ctr(ppb, 0x08);
-	udelay(30);
-	w_ctr(ppb, 0x0c);
-	mdelay(1);		/* Allow devices to settle down */
-    }
+    if (!retv)
+	ppa_reset_pulse(ppb);
+    udelay(1000);		/* Allow devices to settle down */
     ppa_disconnect(host_no);
-    mdelay(1);		/* Another delay to allow devices to settle */
+    udelay(1000);		/* Another delay to allow devices to settle */
 
     if (!retv)
 	retv = device_check(host_no);
@@ -989,6 +982,7 @@
 	ppa_disconnect(cmd->host->unique_id);
     if (cmd->SCp.phase > 0)
 	ppa_pb_release(cmd->host->unique_id);
+
     tmp->cur_cmd = 0;
     cmd->scsi_done(cmd);
     return;
@@ -1017,7 +1011,7 @@
 	    ppa_fail(host_no, DID_BUS_BUSY);
 	    return 0;
 	}
-	return 1; /* wait that ppa_wakeup claims parport */
+	return 1;		/* wait until ppa_wakeup claims parport */
     case 1:			/* Phase 1 - Connected */
 	{			/* Perform a sanity check for cable unplugged */
 	    int retv = 2;	/* Failed */
@@ -1032,7 +1026,7 @@
 	    if ((r_str(ppb) & 0x08) == 0x00)
 		retv--;
 
-	    if (retv) {
+	    if (retv)
 		if ((jiffies - tmp->jstart) > (1 * HZ)) {
 		    printk("ppa: Parallel port cable is unplugged!!\n");
 		    ppa_fail(host_no, DID_BUS_BUSY);
@@ -1041,7 +1035,6 @@
 		    ppa_disconnect(host_no);
 		    return 1;	/* Try again in a jiffy */
 		}
-	    }
 	    cmd->SCp.phase++;
 	}
 
@@ -1136,7 +1129,7 @@
 }
 
 /*
- * Apparently the disk->capacity attribute is off by 1 sector 
+ * Apparently the the disk->capacity attribute is off by 1 sector 
  * for all disk drives.  We add the one here, but it should really
  * be done in sd.c.  Even if it gets fixed there, this will still
  * work.
@@ -1158,6 +1151,7 @@
 
 int ppa_abort(Scsi_Cmnd * cmd)
 {
+    int host_no = cmd->host->unique_id;
     /*
      * There is no method for aborting commands since Iomega
      * have tied the SCSI_MESSAGE line high in the interface
@@ -1166,60 +1160,37 @@
     switch (cmd->SCp.phase) {
     case 0:			/* Do not have access to parport */
     case 1:			/* Have not connected to interface */
-	cmd->result = DID_ABORT;
-	cmd->done(cmd);
-	return SCSI_ABORT_SUCCESS;
+	ppa_hosts[host_no].cur_cmd = NULL;	/* Forget the problem */
+	return SUCCESS;
 	break;
     default:			/* SCSI command sent, can not abort */
-	return SCSI_ABORT_BUSY;
+	return FAILED;
 	break;
     }
 }
 
-int ppa_reset(Scsi_Cmnd * cmd, unsigned int x)
+static void ppa_reset_pulse(unsigned int base)
+{
+    w_dtr(base, 0x40);
+    w_ctr(base, 0x8);
+    udelay(30);
+    w_ctr(base, 0xc);
+}
+
+int ppa_reset(Scsi_Cmnd * cmd)
 {
     int host_no = cmd->host->unique_id;
-    int ppb = PPA_BASE(host_no);
 
-    /*
-     * PHASE1:
-     * Bring the interface crashing down on whatever is running
-     * hopefully this will kill the request.
-     * Bring back up the interface, reset the drive (and anything
-     * attached for that manner)
-     */
-    if (cmd)
-	if (cmd->SCp.phase)
-	    ppa_disconnect(cmd->host->unique_id);
+    if (cmd->SCp.phase)
+	ppa_disconnect(host_no);
+    ppa_hosts[host_no].cur_cmd = NULL;	/* Forget the problem */
 
     ppa_connect(host_no, CONNECT_NORMAL);
-    w_dtr(ppb, 0x40);
-    w_ctr(ppb, 0x8);
-    udelay(30);
-    w_ctr(ppb, 0xc);
-    mdelay(1);		/* delay for devices to settle down */
+    ppa_reset_pulse(PPA_BASE(host_no));
+    udelay(1000);		/* device settle delay */
     ppa_disconnect(host_no);
-    mdelay(1);		/* Additional delay to allow devices to settle down */
-
-    /*
-     * PHASE2:
-     * Sanity check for the sake of mid-level driver
-     */
-    if (!cmd) {
-	printk("ppa bus reset called for invalid command.\n");
-	return SCSI_RESET_NOT_RUNNING;
-    }
-    /*
-     * PHASE3:
-     * Flag the current command as having died due to reset
-     */
-    ppa_connect(host_no, CONNECT_NORMAL);
-    ppa_fail(host_no, DID_RESET);
-
-    /* Since the command was already on the timer queue ppa_interrupt
-     * will be called shortly.
-     */
-    return SCSI_RESET_PENDING;
+    udelay(1000);		/* device settle delay */
+    return SUCCESS;
 }
 
 static int device_check(int host_no)
@@ -1261,9 +1232,9 @@
 	    w_ctr(ppb, 0x08);
 	    udelay(30);
 	    w_ctr(ppb, 0x0c);
-	    mdelay(1);
+	    udelay(1000);
 	    ppa_disconnect(host_no);
-	    mdelay(1);
+	    udelay(1000);
 	    if (ppa_hosts[host_no].mode == PPA_EPP_32) {
 		ppa_hosts[host_no].mode = old_mode;
 		goto second_pass;
@@ -1284,13 +1255,10 @@
 	if (l != 0xf0) {
 	    ppa_disconnect(host_no);
 	    ppa_connect(host_no, CONNECT_EPP_MAYBE);
-	    w_dtr(ppb, 0x40);
-	    w_ctr(ppb, 0x08);
-	    udelay(30);
-	    w_ctr(ppb, 0x0c);
-	    mdelay(1);
+	    ppa_reset_pulse(ppb);
+	    udelay(1000);
 	    ppa_disconnect(host_no);
-	    mdelay(1);
+	    udelay(1000);
 	    if (ppa_hosts[host_no].mode == PPA_EPP_32) {
 		ppa_hosts[host_no].mode = old_mode;
 		goto second_pass;
@@ -1301,6 +1269,11 @@
 	ppa_disconnect(host_no);
 	printk("ppa: Communication established with ID %i using %s\n", loop,
 	       PPA_MODE_STRING[ppa_hosts[host_no].mode]);
+	ppa_connect(host_no, CONNECT_EPP_MAYBE);
+	ppa_reset_pulse(ppb);
+	udelay(1000);
+	ppa_disconnect(host_no);
+	udelay(1000);
 	return 0;
     }
     printk("ppa: No devices found, aborting driver load.\n");
diff -u --recursive --new-file v2.1.121/linux/drivers/scsi/ppa.h linux/drivers/scsi/ppa.h
--- v2.1.121/linux/drivers/scsi/ppa.h	Wed Sep  9 14:51:09 1998
+++ linux/drivers/scsi/ppa.h	Wed Sep 16 14:09:26 1998
@@ -10,15 +10,7 @@
 #ifndef _PPA_H
 #define _PPA_H
 
-#define   PPA_VERSION   "1.39a"
-
-/* Use the following to enable certain chipset support
- * Default is PEDANTIC = 3
- */
-#include <linux/config.h> /* for CONFIG_SCSI_PPA_HAVE_PEDANTIC */
-#ifndef CONFIG_SCSI_PPA_HAVE_PEDANTIC
-#define CONFIG_SCSI_PPA_HAVE_PEDANTIC	3
-#endif
+#define   PPA_VERSION   "2.01"
 
 /* 
  * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu)
@@ -32,34 +24,23 @@
  *
  * [ Stuff removed ]
  *
- * Compiled against 2.1.53.
- *	Rebuilt ppa_abort() function, should handle unplugged cable.
- *							[1.35s]
- *
- * PPA now auto probes for EPP on base address which are aligned on
- * 8 byte boundaries (0x278 & 0x378) using the attached devices.
- * This hopefully avoids the nasty problem of trying to detect EPP.
- *	Tested on 2.1.53				[1.36]
- *
- * The id_probe utility no longer performs read/write tests.
- * Additional code included for checking the Intel ECP bug
- * (Bit 0 of STR stuck low which fools the EPP detection routine)
- *							[1.37]
- *
- * Oops! Got the bit sign mixed up for the Intel bug check.
- * Found that an additional delay is required during SCSI resets
- * to allow devices to settle down.
- *							[1.38]
- *
- * Fixed all problems in the parport sharing scheme. Now ppa can be safe
- * used with lp or other parport devices on the same parallel port.
- *		1997 by Andrea Arcangeli
- *							[1.39]
- *
- * Little fix in ppa engine to ensure that ppa don' t release parport
- * or disconnect in wrong cases.
- *		1997 by Andrea Arcangeli
- *							[1.39a]
+ * Corrected ppa.h for 2.1.x kernels (>=2.1.85)
+ * Modified "Nat Semi Kludge" for extended chipsets
+ *							[1.41]
+ *
+ * Fixed id_probe for EPP 1.9 chipsets (misdetected as EPP 1.7)
+ *							[1.42]
+ *
+ * Development solely for 2.1.x kernels from now on!
+ *							[2.00]
+ *
+ * Hack and slash at the init code (EPP device check routine)
+ * Added INSANE option.
+ *							[2.01]
+ *
+ * Patch applied to sync against the 2.1.x kernel code
+ * Included qboot_zip.sh
+ *							[2.02]
  */
 /* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
 
@@ -117,6 +98,23 @@
 #define CONNECT_EPP_MAYBE 1
 #define CONNECT_NORMAL  0
 
+/* INSANE code */
+#define PPA_INSANE 0
+#if PPA_INSANE > 0
+#define r_dtr(x)        (unsigned char)inb_p((x))
+#define r_str(x)        (unsigned char)inb_p((x)+1)
+#define r_ctr(x)        (unsigned char)inb_p((x)+2)
+#define r_epp(x)        (unsigned char)inb_p((x)+4)
+#define r_fifo(x)       (unsigned char)inb_p((x)+0x400)
+#define r_ecr(x)        (unsigned char)inb_p((x)+0x402)
+
+#define w_dtr(x,y)      outb_p(y, (x))
+#define w_str(x,y)      outb_p(y, (x)+1)
+#define w_ctr(x,y)      outb_p(y, (x)+2)
+#define w_epp(x,y)      outb_p(y, (x)+4)
+#define w_fifo(x,y)     outb_p(y, (x)+0x400)
+#define w_ecr(x,y)      outb_p(y, (x)+0x402)
+#else /* PPA_INSANE */
 #define r_dtr(x)        (unsigned char)inb((x))
 #define r_str(x)        (unsigned char)inb((x)+1)
 #define r_ctr(x)        (unsigned char)inb((x)+2)
@@ -130,6 +128,7 @@
 #define w_epp(x,y)      outb(y, (x)+4)
 #define w_fifo(x,y)     outb(y, (x)+0x400)
 #define w_ecr(x,y)      outb(y, (x)+0x402)
+#endif	/* PPA_INSANE */
 
 static int ppa_engine(ppa_struct *, Scsi_Cmnd *);
 static int ppa_in(int, char *, int);
@@ -149,23 +148,25 @@
 int ppa_command(Scsi_Cmnd *);
 int ppa_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
 int ppa_abort(Scsi_Cmnd *);
-int ppa_reset(Scsi_Cmnd *, unsigned int);
+int ppa_reset(Scsi_Cmnd *);
 int ppa_proc_info(char *, char **, off_t, int, int, int);
 int ppa_biosparam(Disk *, kdev_t, int *);
 
-#define PPA {	proc_dir:		&proc_scsi_ppa,			\
-		proc_info:		ppa_proc_info,			\
-		name:			"Iomega parport ZIP drive",	\
-		detect:			ppa_detect,			\
-		release:		ppa_release,			\
-		command:		ppa_command,			\
-		queuecommand:		ppa_queuecommand,		\
-		abort:			ppa_abort,			\
-		reset:			ppa_reset,			\
-		bios_param:		ppa_biosparam,			\
-		this_id:		-1,				\
-		sg_tablesize:		SG_ALL,				\
-		cmd_per_lun:		1,				\
-		use_clustering:		ENABLE_CLUSTERING		\
+#define PPA {	proc_dir:			&proc_scsi_ppa,		\
+		proc_info:			ppa_proc_info,		\
+		name:				"Iomega parport ZIP drive",\
+		detect:				ppa_detect,		\
+		release:			ppa_release,		\
+		command:			ppa_command,		\
+		queuecommand:			ppa_queuecommand,	\
+		eh_abort_handler:		ppa_abort,		\
+		eh_device_reset_handler:	NULL,			\
+		eh_bus_reset_handler:		ppa_reset,		\
+		eh_host_reset_handler:		ppa_reset,		\
+		bios_param:			ppa_biosparam,		\
+		this_id:			-1,			\
+		sg_tablesize:			SG_ALL,			\
+		cmd_per_lun:			1,			\
+		use_clustering:			ENABLE_CLUSTERING	\
 }
 #endif				/* _PPA_H */
diff -u --recursive --new-file v2.1.121/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.1.121/linux/drivers/scsi/scsi.c	Tue Jul 28 14:21:08 1998
+++ linux/drivers/scsi/scsi.c	Fri Sep 11 11:12:03 1998
@@ -90,14 +90,6 @@
 # error You lose.
 #endif
 
-#define MAX_SCSI_DEVICE_CODE 10
-
-#ifdef DEBUG
-    #define SCSI_TIMEOUT (5*HZ)
-#else
-    #define SCSI_TIMEOUT (2*HZ)
-#endif
-
 #define MIN_RESET_DELAY (2*HZ)
 
 /* Do not call reset on error if we just did a reset within 15 sec. */
diff -u --recursive --new-file v2.1.121/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v2.1.121/linux/drivers/scsi/scsi.h	Fri Jul 31 17:07:58 1998
+++ linux/drivers/scsi/scsi.h	Wed Sep 16 14:08:31 1998
@@ -42,6 +42,12 @@
 #define MAX_SCSI_DEVICE_CODE 10
 extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
 
+#ifdef DEBUG
+    #define SCSI_TIMEOUT (5*HZ)
+#else
+    #define SCSI_TIMEOUT (2*HZ)
+#endif
+
 /*
  *  Use these to separate status msg and our bytes
  *
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.1.121/linux/drivers/sound/Config.in	Sat Sep  5 16:46:41 1998
+++ linux/drivers/sound/Config.in	Thu Sep 10 16:37:26 1998
@@ -17,36 +17,52 @@
 
 dep_tristate 'Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_MSNDCLAS $CONFIG_SOUND
 if [ "$CONFIG_SOUND_MSNDCLAS" = "y" -o "$CONFIG_SOUND_MSNDCLAS" = "m" ]; then
-   string '  Full pathname of MSNDINIT.BIN firmware file' CONFIG_MSNDCLAS_INIT_FILE "/etc/sound/msndinit.bin"
-   string '  Full pathname of MSNDPERM.BIN firmware file' CONFIG_MSNDCLAS_PERM_FILE "/etc/sound/msndperm.bin"
+   if [ "$CONFIG_SOUND_MSNDCLAS" = "y" ]; then
+     comment 'Compiled-in MSND Classic support requires firmware during compilation.'
+     define_bool CONFIG_MSNDCLAS_HAVE_BOOT y
+     string '  Full pathname of MSNDINIT.BIN firmware file' CONFIG_MSNDCLAS_INIT_FILE "/etc/sound/msndinit.bin"
+     string '  Full pathname of MSNDPERM.BIN firmware file' CONFIG_MSNDCLAS_PERM_FILE "/etc/sound/msndperm.bin"
+   else
+     define_bool CONFIG_MSNDCLAS_HAVE_BOOT n
+   fi
 fi
 if [ "$CONFIG_SOUND_MSNDCLAS" = "y" ]; then
    int 'MSND Classic IRQ 5,7,9,10,11,12' CONFIG_MSNDCLAS_IRQ 5
-   hex 'MSND Classic Memory B0000,C8000,D0000,D8000,E0000,E8000' CONFIG_MSNDCLAS_MEM D0000
+   hex 'MSND Classic memory B0000,C8000,D0000,D8000,E0000,E8000' CONFIG_MSNDCLAS_MEM D0000
    hex 'MSND Classic I/O 210,220,230,240,250,260,290,3E0' CONFIG_MSNDCLAS_IO 290
 fi
 
 dep_tristate 'Support for Turtle Beach MultiSound Pinnacle, Fiji' CONFIG_SOUND_MSNDPIN $CONFIG_SOUND
 if [ "$CONFIG_SOUND_MSNDPIN" = "y" -o "$CONFIG_SOUND_MSNDPIN" = "m" ]; then
-   string '  Full pathname of PNDSPINI.BIN firmware file' CONFIG_MSNDPIN_INIT_FILE "/etc/sound/pndspini.bin"
-   string '  Full pathname of PNDSPERM.BIN firmware file' CONFIG_MSNDPIN_PERM_FILE "/etc/sound/pndsperm.bin"
+   if [ "$CONFIG_SOUND_MSNDPIN" = "y" ]; then
+     comment 'Compiled-in MSND Pinnacle support requires firmware during compilation.'
+     string '  Full pathname of PNDSPINI.BIN firmware file' CONFIG_MSNDPIN_INIT_FILE "/etc/sound/pndspini.bin"
+     string '  Full pathname of PNDSPERM.BIN firmware file' CONFIG_MSNDPIN_PERM_FILE "/etc/sound/pndsperm.bin"
+     define_bool CONFIG_MSNDPIN_HAVE_BOOT y
+   else
+     define_bool CONFIG_MSNDPIN_HAVE_BOOT n
+   fi
 fi
 if [ "$CONFIG_SOUND_MSNDPIN" = "y" ]; then
    int 'MSND Pinnacle IRQ 5,7,9,10,11,12' CONFIG_MSNDPIN_IRQ 5
-   hex 'MSND Pinnacle Memory B0000,C8000,D0000,D8000,E0000,E8000' CONFIG_MSNDPIN_MEM D0000
+   hex 'MSND Pinnacle memory B0000,C8000,D0000,D8000,E0000,E8000' CONFIG_MSNDPIN_MEM D0000
    hex 'MSND Pinnacle I/O 210,220,230,240,250,260,290,3E0' CONFIG_MSNDPIN_IO 290
+   bool 'MSND Pinnacle has S/PDIF I/O' CONFIG_MSNDPIN_DIGITAL
    bool 'MSND Pinnacle Non-PnP Mode' CONFIG_MSNDPIN_NONPNP
    if [ "$CONFIG_MSNDPIN_NONPNP" = "y" ]; then
      comment 'MSND Pinnacle DSP section will be configured to above parameters.'
-     hex 'MSDN Pinnacle Config Port 250,260,270' CONFIG_MSNDPIN_CFG 250
+     hex 'MSDN Pinnacle config port 250,260,270' CONFIG_MSNDPIN_CFG 250
      comment 'Pinnacle-specific Device Configuration (0 disables)'
-     hex 'MSDN Pinnacle MPU I/O (e.g. 330)' CONFIG_MSNDPIN_MPU_IO 0
-     int 'MSDN Pinnacle MPU IRQ (e.g. 9)' CONFIG_MSNDPIN_MPU_IRQ 0
-     hex 'MSDN Pinnacle IDE I/O 0 (e.g. 170)' CONFIG_MSNDPIN_IDE_IO0 0
-     hex 'MSDN Pinnacle IDE I/O 1 (e.g. 376)' CONFIG_MSNDPIN_IDE_IO1 0
-     int 'MSDN Pinnacle IDE IRQ (e.g. 15)' CONFIG_MSNDPIN_IDE_IRQ 0
-     hex 'MSDN Pinnacle Joystick I/O (e.g. 200)' CONFIG_MSNDPIN_JOYSTICK_IO 0
+     hex 'MSND Pinnacle MPU I/O (e.g. 330)' CONFIG_MSNDPIN_MPU_IO 0
+     int 'MSND Pinnacle MPU IRQ (e.g. 9)' CONFIG_MSNDPIN_MPU_IRQ 0
+     hex 'MSND Pinnacle IDE I/O 0 (e.g. 170)' CONFIG_MSNDPIN_IDE_IO0 0
+     hex 'MSND Pinnacle IDE I/O 1 (e.g. 376)' CONFIG_MSNDPIN_IDE_IO1 0
+     int 'MSND Pinnacle IDE IRQ (e.g. 15)' CONFIG_MSNDPIN_IDE_IRQ 0
+     hex 'MSDN Pinnacle joystick I/O (e.g. 200)' CONFIG_MSNDPIN_JOYSTICK_IO 0
    fi
+fi
+if [ "$CONFIG_SOUND_MSNDPIN" = "y" -o "$CONFIG_SOUND_MSNDCLAS" = "y" ]; then
+   int 'MSND buffer size (kB)' CONFIG_MSND_FIFOSIZE 128
 fi
 
 dep_tristate 'OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v2.1.121/linux/drivers/sound/Makefile	Thu Aug 20 17:05:16 1998
+++ linux/drivers/sound/Makefile	Thu Sep 10 16:37:26 1998
@@ -235,6 +235,50 @@
 
 
 
+# Turtle Beach MultiSound
+
+ifeq ($(CONFIG_MSNDCLAS_HAVE_BOOT),y)
+    msnd_classic.o: msndperm.c msndinit.c
+
+    msndperm.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_PERM_FILE)) bin2hex
+	./bin2hex msndperm < $(CONFIG_MSNDCLAS_PERM_FILE) > $@
+	@ ( \
+	    echo 'ifeq ($(strip $(CONFIG_MSNDCLAS_HAVE_BOOT) $(CONFIG_MSNDCLAS_PERM_FILE)),$$(strip $$(CONFIG_MSNDCLAS_HAVE_BOOT) $$(CONFIG_MSNDCLAS_PERM_FILE)))'; \
+	    echo 'FILES_BOOT_UP_TO_DATE += $@'; \
+	    echo 'endif' \
+	) > .$@.boot
+
+    msndinit.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_INIT_FILE)) bin2hex
+	./bin2hex msndinit < $(CONFIG_MSNDCLAS_INIT_FILE) > $@
+	@ ( \
+	    echo 'ifeq ($(strip $(CONFIG_MSNDCLAS_HAVE_BOOT) $(CONFIG_MSNDCLAS_INIT_FILE)),$$(strip $$(CONFIG_MSNDCLAS_HAVE_BOOT) $$(CONFIG_MSNDCLAS_INIT_FILE)))'; \
+	    echo 'FILES_BOOT_UP_TO_DATE += $@'; \
+	    echo 'endif' \
+	) > .$@.boot
+endif
+
+ifeq ($(CONFIG_MSNDPIN_HAVE_BOOT),y)
+    msnd_pinnacle.o: pndsperm.c pndspini.c
+
+    pndsperm.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_PERM_FILE)) bin2hex
+	./bin2hex pndsperm < $(CONFIG_MSNDPIN_PERM_FILE) > $@
+	@ ( \
+	    echo 'ifeq ($(strip $(CONFIG_MSNDPIN_HAVE_BOOT) $(CONFIG_MSNDPIN_PERM_FILE)),$$(strip $$(CONFIG_MSNDPIN_HAVE_BOOT) $$(CONFIG_MSNDPIN_PERM_FILE)))'; \
+	    echo 'FILES_BOOT_UP_TO_DATE += $@'; \
+	    echo 'endif' \
+	) > .$@.boot
+
+    pndspini.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_INIT_FILE)) bin2hex
+	./bin2hex pndspini < $(CONFIG_MSNDPIN_INIT_FILE) > $@
+	@ ( \
+	    echo 'ifeq ($(strip $(CONFIG_MSNDPIN_HAVE_BOOT) $(CONFIG_MSNDPIN_INIT_FILE)),$$(strip $$(CONFIG_MSNDPIN_HAVE_BOOT) $$(CONFIG_MSNDPIN_INIT_FILE)))'; \
+	    echo 'FILES_BOOT_UP_TO_DATE += $@'; \
+	    echo 'endif' \
+	) > .$@.boot
+endif
+
+
+
 # PSS (ECHO-ADI2111)
 
 pss.o: pss_boot.h
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v2.1.121/linux/drivers/sound/ad1848.c	Sun Jul 26 11:57:16 1998
+++ linux/drivers/sound/ad1848.c	Thu Sep 10 16:37:26 1998
@@ -2401,7 +2401,6 @@
 
 void unload_ms_sound(struct address_info *hw_config)
 {
-	int mixer = audio_devs[hw_config->slots[0]]->mixer_dev;
 	ad1848_unload(hw_config->io_base + 4,
 		      hw_config->irq,
 		      hw_config->dma,
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/dev_table.c linux/drivers/sound/dev_table.c
--- v2.1.121/linux/drivers/sound/dev_table.c	Tue Jun  9 11:57:30 1998
+++ linux/drivers/sound/dev_table.c	Thu Sep 10 16:37:26 1998
@@ -534,6 +534,7 @@
 
 int sound_alloc_mididev(void)
 {
+#ifdef CONFIG_MIDI
 	int i = register_sound_midi(&oss_sound_fops);
 	if(i==-1)
 		return i;
@@ -541,6 +542,9 @@
 	if(i>=num_midis)
 		num_midis = i + 1;
 	return i;
+#else
+	return (-1);
+#endif
 }
 
 int sound_alloc_synthdev(void)
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/mad16.c linux/drivers/sound/mad16.c
--- v2.1.121/linux/drivers/sound/mad16.c	Sun Jul 26 11:57:16 1998
+++ linux/drivers/sound/mad16.c	Thu Sep 10 16:37:26 1998
@@ -849,7 +849,7 @@
 #if defined(CONFIG_MIDI) && defined(CONFIG_MAD16_OLDCARD)
 	if (board_type < C929)	/* Early chip. No MPU support. Just SB MIDI */
 	{
-		sb_dsp_unload(hw_config);
+		sb_dsp_unload(hw_config, 0);
 		return;
 	}
 #endif
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/msnd.c linux/drivers/sound/msnd.c
--- v2.1.121/linux/drivers/sound/msnd.c	Sat Sep  5 16:46:41 1998
+++ linux/drivers/sound/msnd.c	Thu Sep 10 16:37:26 1998
@@ -20,7 +20,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd.c,v 1.9 1998/09/04 18:41:27 andrewtv Exp $
+ * $Id: msnd.c,v 1.16 1998/09/08 04:05:56 andrewtv Exp $
  *
  ********************************************************************/
 
@@ -46,6 +46,7 @@
 #  include <asm/uaccess.h>
 #  include <asm/spinlock.h>
 #endif
+#include <asm/irq.h>
 #include "msnd.h"
 
 #define LOGNAME			"msnd"
@@ -224,7 +225,7 @@
 int msnd_wait_TXDE(multisound_dev_t *dev)
 {
 	register unsigned int io = dev->io;
-	register int timeout = 100;
+	register int timeout = 1000;
     
 	while(timeout-- > 0)
 		if (inb(io + HP_ISR) & HPISR_TXDE)
@@ -236,7 +237,7 @@
 int msnd_wait_HC0(multisound_dev_t *dev)
 {
 	register unsigned int io = dev->io;
-	register int timeout = 100;
+	register int timeout = 1000;
 
 	while(timeout-- > 0)
 		if (!(inb(io + HP_CVR) & HPCVR_HC))
@@ -248,17 +249,16 @@
 int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd)
 {
 	unsigned long flags;
-	
+
 	spin_lock_irqsave(&dev->lock, flags);
 	if (msnd_wait_HC0(dev) == 0) {
-
 		outb(cmd, dev->io + HP_CVR);
 		spin_unlock_irqrestore(&dev->lock, flags);
 		return 0;
 	}
 	spin_unlock_irqrestore(&dev->lock, flags);
 
-	printk(KERN_WARNING LOGNAME ": Send DSP command timeout\n");
+	printk(KERN_DEBUG LOGNAME ": Send DSP command timeout\n");
 	
 	return -EIO;
 }
@@ -269,14 +269,13 @@
 	register unsigned int io = dev->io;
 
 	if (msnd_wait_TXDE(dev) == 0) {
-		
 		outb(high, io + HP_TXH);
 		outb(mid, io + HP_TXM);
 		outb(low, io + HP_TXL);
 		return 0;
 	}
 
-	printk(KERN_WARNING LOGNAME ": Send host word timeout\n");
+	printk(KERN_DEBUG LOGNAME ": Send host word timeout\n");
 
 	return -EIO;
 }
@@ -286,7 +285,6 @@
 	int i;
 
 	if (len % 3 != 0) {
-		
 		printk(KERN_WARNING LOGNAME ": Upload host data not multiple of 3!\n");		
 		return -EINVAL;
 	}
@@ -303,31 +301,27 @@
 
 int msnd_enable_irq(multisound_dev_t *dev)
 {
-	printk(KERN_DEBUG LOGNAME ": enable_irq: count %d\n", dev->irq_ref);
+	unsigned long flags;
 
-	if (dev->irq_ref++ != 0)
+	if (dev->irq_ref++)
 		return 0;
 
 	printk(KERN_DEBUG LOGNAME ": Enabling IRQ\n");
-
+	
+	spin_lock_irqsave(&dev->lock, flags);
 	if (msnd_wait_TXDE(dev) == 0) {
-
-		unsigned long flags;
-		
-		spin_lock_irqsave(&dev->lock, flags);
-		
 		outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
-
 		if (dev->type == msndClassic)
 			outb(dev->irqid, dev->io + HP_IRQM);
-
 		outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR);
 		outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR);
-
+		enable_irq(dev->irq);
 		spin_unlock_irqrestore(&dev->lock, flags);
-
 		return 0;
 	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	printk(KERN_DEBUG LOGNAME ": Enable IRQ failed\n");
 
 	return -EIO;
 }
@@ -336,29 +330,28 @@
 {
 	unsigned long flags;
 
-	printk(KERN_DEBUG LOGNAME ": disable_irq: count %d\n", dev->irq_ref);
-
 	if (--dev->irq_ref > 0)
 		return 0;
 
-	if (dev->irq_ref < 0) {
-		printk(KERN_WARNING LOGNAME ": IRQ ref count is %d\n", dev->irq_ref);
-/*		dev->irq_ref = 0; */
-	}
+	if (dev->irq_ref < 0)
+		printk(KERN_DEBUG LOGNAME ": IRQ ref count is %d\n", dev->irq_ref);
 
 	printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n");
-	
-	udelay(50);
 
 	spin_lock_irqsave(&dev->lock, flags);
-	outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
-	
-	if (dev->type == msndClassic)
-		outb(HPIRQ_NONE, dev->io + HP_IRQM);
-
+	if (msnd_wait_TXDE(dev) == 0) {
+		outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
+		if (dev->type == msndClassic)
+			outb(HPIRQ_NONE, dev->io + HP_IRQM);
+		disable_irq(dev->irq);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return 0;
+	}
 	spin_unlock_irqrestore(&dev->lock, flags);
 
-	return 0;
+	printk(KERN_DEBUG LOGNAME ": Disable IRQ failed\n");
+
+	return -EIO;
 }
 
 #ifndef LINUX20
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/msnd.h linux/drivers/sound/msnd.h
--- v2.1.121/linux/drivers/sound/msnd.h	Sat Sep  5 16:46:41 1998
+++ linux/drivers/sound/msnd.h	Thu Sep 10 16:37:26 1998
@@ -24,19 +24,19 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd.h,v 1.18 1998/09/04 18:43:40 andrewtv Exp $
+ * $Id: msnd.h,v 1.31 1998/09/10 14:02:58 andrewtv Exp $
  *
  ********************************************************************/
 #ifndef __MSND_H
 #define __MSND_H
 
-#define VERSION			"0.7.13"
+#define VERSION			"0.8.2"
 
 #define DEFSAMPLERATE		DSP_DEFAULT_SPEED
 #define DEFSAMPLESIZE		AFMT_U8
 #define DEFCHANNELS		1
 
-#define DEFFIFOSIZE		64
+#define DEFFIFOSIZE		128
 
 #define SNDCARD_MSND		38
 
@@ -151,6 +151,7 @@
 
 #define PCTODSP_OFFSET(w)	(USHORT)((w)/2)
 #define PCTODSP_BASED(w)	(USHORT)(((w)/2) + DSP_BASE_ADDR)
+#define DSPTOPC_BASED(w)	(((w) - DSP_BASE_ADDR) * 2)
 
 #ifdef SLOWIO
 #  undef outb
@@ -207,14 +208,10 @@
 	int memid, irqid;
 	int irq, irq_ref;
 	unsigned char info;
-	char *base;
-#ifndef LINUX20
-	spinlock_t lock;
-#endif
+	volatile BYTE *base;
 
 	/* Motorola 56k DSP SMA */
 	volatile BYTE *SMA;
-	volatile BYTE *CurDAQD, *CurDARQD;
 	volatile BYTE *DAPQ, *DARQ, *MODQ, *MIDQ, *DSPQ;
 	volatile WORD *pwDSPQData, *pwMIDQData, *pwMODQData;
 
@@ -222,27 +219,32 @@
 	enum { msndClassic, msndPinnacle } type;
 	mode_t mode;
 	unsigned long flags;
-#define F_BANKONE			0
-#define F_INTERRUPT			1
-#define F_WRITING			2
-#define F_WRITEBLOCK			3
-#define F_READING			4
-#define F_READBLOCK			5
-#define F_AUDIO_INUSE			6
-#define F_EXT_MIDI_INUSE		7
-#define F_INT_MIDI_INUSE		8
-#define F_WRITEFLUSH			9
-#define F_HAVEDIGITAL			10
+#define F_RESETTING			0
+#define F_HAVEDIGITAL			1
+#define F_AUDIO_WRITE_INUSE		2
+#define F_WRITING			3
+#define F_WRITEBLOCK			4
+#define F_WRITEFLUSH			5
+#define F_AUDIO_READ_INUSE		6
+#define F_READING			7
+#define F_READBLOCK			8
+#define F_EXT_MIDI_INUSE		9
+#define F_INT_MIDI_INUSE		10
 	struct wait_queue *writeblock, *readblock;
 	struct wait_queue *writeflush;
+#ifndef LINUX20
+	spinlock_t lock;
+#endif
+	int nresets;
 	unsigned long recsrc;
 	int left_levels[16];
 	int right_levels[16];
 	int mixer_mod_count;
 	int calibrate_signal;
-	int sample_size;
-	int sample_rate;
-	int channels;
+	int play_sample_size, play_sample_rate, play_channels;
+	int play_ndelay;
+	int rec_sample_size, rec_sample_rate, rec_channels;
+	int rec_ndelay;
 	BYTE bCurrentMidiPatch;
 	void (*inc_ref)(void);
 	void (*dec_ref)(void);
@@ -250,7 +252,7 @@
 	/* Digital audio FIFOs */
 	msnd_fifo DAPF, DARF;
 	int fifosize;
-	int lastbank;
+	int last_playbank, last_recbank;
 
 	/* MIDI in callback */
 	void (*midi_in_interrupt)(struct multisound_dev *);
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/msnd_classic.h linux/drivers/sound/msnd_classic.h
--- v2.1.121/linux/drivers/sound/msnd_classic.h	Sat Sep  5 16:46:41 1998
+++ linux/drivers/sound/msnd_classic.h	Thu Sep 10 16:37:26 1998
@@ -24,7 +24,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  * 
- * $Id: msnd_classic.h,v 1.7 1998/09/03 06:39:47 andrewtv Exp $
+ * $Id: msnd_classic.h,v 1.9 1998/09/10 04:11:18 andrewtv Exp $
  *
  ********************************************************************/
 #ifndef __MSND_CLASSIC_H
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/msnd_pinnacle.c linux/drivers/sound/msnd_pinnacle.c
--- v2.1.121/linux/drivers/sound/msnd_pinnacle.c	Sat Sep  5 16:46:41 1998
+++ linux/drivers/sound/msnd_pinnacle.c	Thu Sep 10 16:38:44 1998
@@ -29,7 +29,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd_pinnacle.c,v 1.17 1998/09/04 18:41:27 andrewtv Exp $
+ * $Id: msnd_pinnacle.c,v 1.63 1998/09/10 18:37:19 andrewtv Exp $
  *
  ********************************************************************/
 
@@ -45,6 +45,7 @@
 #ifndef LINUX20
 #  include <linux/init.h>
 #endif
+#include <asm/irq.h>
 #include "sound_config.h"
 #include "sound_firmware.h"
 #ifdef MSND_CLASSIC
@@ -52,13 +53,29 @@
 #endif
 #include "msnd.h"
 #ifdef MSND_CLASSIC
+#  ifdef CONFIG_MSNDCLAS_HAVE_BOOT
+#    define HAVE_DSPCODEH
+#  endif
 #  include "msnd_classic.h"
 #  define LOGNAME			"msnd_classic"
 #else
+#  ifdef CONFIG_MSNDPIN_HAVE_BOOT
+#    define HAVE_DSPCODEH
+#  endif
 #  include "msnd_pinnacle.h"
 #  define LOGNAME			"msnd_pinnacle"
 #endif
 
+#define get_play_delay_jiffies(size)	((size) * HZ *			\
+					 dev.play_sample_size / 8 /	\
+					 dev.play_sample_rate /		\
+					 dev.play_channels)
+
+#define get_rec_delay_jiffies(size)	((size) * HZ *			\
+					 dev.rec_sample_size / 8 /	\
+					 dev.rec_sample_rate /		\
+					 dev.rec_channels)
+
 static multisound_dev_t			dev;
 
 #ifndef HAVE_DSPCODEH
@@ -66,71 +83,86 @@
 static int				sizeof_dspini, sizeof_permini;
 #endif
 
+static int				dsp_full_reset(void);
+static void				dsp_write_flush(void);
+
+static __inline__ int chk_send_dsp_cmd(multisound_dev_t *dev, register BYTE cmd)
+{
+	if (msnd_send_dsp_cmd(dev, cmd) == 0)
+		return 0;
+	dsp_full_reset();
+	return msnd_send_dsp_cmd(dev, cmd);
+}
+
 static void reset_play_queue(void)
 {
 	int n;
 	LPDAQD lpDAQ;
 
-	msnd_fifo_make_empty(&dev.DAPF);
-	writew(0, dev.DAPQ + JQS_wHead);
-	writew(PCTODSP_OFFSET(2 * DAPQ_STRUCT_SIZE), dev.DAPQ + JQS_wTail);
-	dev.CurDAQD = (LPDAQD)(dev.base + 1 * DAPQ_DATA_BUFF);
-	outb(HPBLKSEL_0, dev.io + HP_BLKS);
-	memset_io(dev.base, 0, DAP_BUFF_SIZE * 3);
+	dev.last_playbank = -1;
+	writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wHead);
+	writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wTail);
 
-	for (n = 0, lpDAQ = dev.CurDAQD; n < 3; ++n, lpDAQ += DAQDS__size) {
+	for (n = 0, lpDAQ = dev.base + DAPQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) {
 		writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), lpDAQ + DAQDS_wStart);
-		writew(DAP_BUFF_SIZE, lpDAQ + DAQDS_wSize);
+		writew(0, lpDAQ + DAQDS_wSize);
 		writew(1, lpDAQ + DAQDS_wFormat);
-		writew(dev.sample_size, lpDAQ + DAQDS_wSampleSize);
-		writew(dev.channels, lpDAQ + DAQDS_wChannels);
-		writew(dev.sample_rate, lpDAQ + DAQDS_wSampleRate);
+		writew(dev.play_sample_size, lpDAQ + DAQDS_wSampleSize);
+		writew(dev.play_channels, lpDAQ + DAQDS_wChannels);
+		writew(dev.play_sample_rate, lpDAQ + DAQDS_wSampleRate);
 		writew(HIMT_PLAY_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
-		writew(n + 1, lpDAQ + DAQDS_wFlags);
+		writew(n, lpDAQ + DAQDS_wFlags);
 	}
-	dev.lastbank = -1;
 }
 
 static void reset_record_queue(void)
 {
 	int n;
 	LPDAQD lpDAQ;
+	unsigned long flags;
 
-	msnd_fifo_make_empty(&dev.DARF);
-	writew(0, dev.DARQ + JQS_wHead);
-	writew(PCTODSP_OFFSET(2 * DARQ_STRUCT_SIZE), dev.DARQ + JQS_wTail);
-	dev.CurDARQD = (LPDAQD)(dev.base + 1 * DARQ_DATA_BUFF);
+	dev.last_recbank = 2;
+	writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DARQ + JQS_wHead);
+	writew(PCTODSP_OFFSET(dev.last_recbank * DAQDS__size), dev.DARQ + JQS_wTail);
+
+	/* Critical section: bank 1 access */
+	spin_lock_irqsave(&dev.lock, flags);
 	outb(HPBLKSEL_1, dev.io + HP_BLKS);
 	memset_io(dev.base, 0, DAR_BUFF_SIZE * 3);
 	outb(HPBLKSEL_0, dev.io + HP_BLKS);
+	spin_unlock_irqrestore(&dev.lock, flags);
 
-	for (n = 0, lpDAQ = dev.CurDARQD; n < 3; ++n, lpDAQ += DAQDS__size) {
+	for (n = 0, lpDAQ = dev.base + DARQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) {
 		writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, lpDAQ + DAQDS_wStart);
 		writew(DAR_BUFF_SIZE, lpDAQ + DAQDS_wSize);
 		writew(1, lpDAQ + DAQDS_wFormat);
-		writew(dev.sample_size, lpDAQ + DAQDS_wSampleSize);
-		writew(dev.channels, lpDAQ + DAQDS_wChannels);
-		writew(dev.sample_rate, lpDAQ + DAQDS_wSampleRate);
+		writew(dev.rec_sample_size, lpDAQ + DAQDS_wSampleSize);
+		writew(dev.rec_channels, lpDAQ + DAQDS_wChannels);
+		writew(dev.rec_sample_rate, lpDAQ + DAQDS_wSampleRate);
 		writew(HIMT_RECORD_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
-		writew(n + 1, lpDAQ + DAQDS_wFlags);
+		writew(n, lpDAQ + DAQDS_wFlags);
 	}
 }
 
 static void reset_queues(void)
 {
-	writew(0, dev.DSPQ + JQS_wHead);
-	writew(0, dev.DSPQ + JQS_wTail);
-	reset_play_queue();
-	reset_record_queue();
+	if (dev.mode & FMODE_WRITE) {
+		msnd_fifo_make_empty(&dev.DAPF);
+		reset_play_queue();
+	}
+	if (dev.mode & FMODE_READ) {
+		msnd_fifo_make_empty(&dev.DARF);
+		reset_record_queue();
+	}
 }
 
-static int dsp_set_format(int val)
+static int dsp_set_format(struct file *file, int val)
 {
 	int data, i;
 	LPDAQD lpDAQ, lpDARQ;
 
-	lpDAQ = (LPDAQD)(dev.base + DAPQ_DATA_BUFF);
-	lpDARQ = (LPDAQD)(dev.base + DARQ_DATA_BUFF);
+	lpDAQ = dev.base + DAPQ_DATA_BUFF;
+	lpDARQ = dev.base + DARQ_DATA_BUFF;
 
 	switch (val) {
 	case AFMT_U8:
@@ -143,28 +175,43 @@
 	}
 
 	for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
-
-		writew(data, lpDAQ + DAQDS_wSampleSize);
-		writew(data, lpDARQ + DAQDS_wSampleSize);
-	}
-		
-	dev.sample_size = data;
+		if (file->f_mode & FMODE_WRITE)
+			writew(data, lpDAQ + DAQDS_wSampleSize);
+		if (file->f_mode & FMODE_READ)
+			writew(data, lpDARQ + DAQDS_wSampleSize);
+	}
+	if (file->f_mode & FMODE_WRITE)
+		dev.play_sample_size = data;
+	if (file->f_mode & FMODE_READ)
+		dev.rec_sample_size = data;
 
 	return data;
 }
 
-static int dsp_ioctl(unsigned int cmd, unsigned long arg)
+static int dsp_get_frag_size(void)
+{
+	int size;
+	size = dev.fifosize / 4;
+	if (size > 32 * 1024)
+		size = 32 * 1024;
+	return size;
+}
+
+static int dsp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	int val, i, data, tmp;
 	LPDAQD lpDAQ, lpDARQ;
+        audio_buf_info abinfo;
+	unsigned long flags;
 
-	lpDAQ = (LPDAQD)(dev.base + DAPQ_DATA_BUFF);
-	lpDARQ = (LPDAQD)(dev.base + DARQ_DATA_BUFF);
+	lpDAQ = dev.base + DAPQ_DATA_BUFF;
+	lpDARQ = dev.base + DARQ_DATA_BUFF;
 
 	switch (cmd) {
 	case SNDCTL_DSP_SUBDIVIDE:
 	case SNDCTL_DSP_SETFRAGMENT:
 	case SNDCTL_DSP_SETDUPLEX:
+	case SNDCTL_DSP_POST:
 		return 0;
 
 	case SNDCTL_DSP_GETIPTR:
@@ -173,14 +220,39 @@
 	case SNDCTL_DSP_MAPOUTBUF:
 		return -EINVAL;
 
-	case SNDCTL_DSP_SYNC:
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&dev.lock, flags);
+		abinfo.fragsize = dsp_get_frag_size();
+                abinfo.bytes = dev.DAPF.n - dev.DAPF.len;
+                abinfo.fragstotal = dev.DAPF.n / abinfo.fragsize;
+                abinfo.fragments = abinfo.bytes / abinfo.fragsize;
+		spin_unlock_irqrestore(&dev.lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		spin_lock_irqsave(&dev.lock, flags);
+		abinfo.fragsize = dsp_get_frag_size();
+                abinfo.bytes = dev.DARF.n - dev.DARF.len;
+                abinfo.fragstotal = dev.DARF.n / abinfo.fragsize;
+                abinfo.fragments = abinfo.bytes / abinfo.fragsize;
+		spin_unlock_irqrestore(&dev.lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
 	case SNDCTL_DSP_RESET:
-		reset_play_queue();
-		reset_record_queue();
+		dev.nresets = 0;
+		reset_queues();
+		return 0;
+
+	case SNDCTL_DSP_SYNC:
+		dsp_write_flush();
 		return 0;
 		
 	case SNDCTL_DSP_GETBLKSIZE:
-		tmp = dev.fifosize / 4;
+		tmp = dsp_get_frag_size();
 		if (put_user(tmp, (int *)arg))
                         return -EFAULT;
 		return 0;
@@ -195,14 +267,24 @@
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
 
-		data = (val == AFMT_QUERY) ? dev.sample_size : dsp_set_format(val);
+		if (file->f_mode & FMODE_WRITE)
+			data = val == AFMT_QUERY
+				? dev.play_sample_size
+				: dsp_set_format(file, val);
+		else
+			data = val == AFMT_QUERY
+				? dev.rec_sample_size
+				: dsp_set_format(file, val);
 
 		if (put_user(data, (int *)arg))
 			return -EFAULT;
 		return 0;
 
 	case SNDCTL_DSP_NONBLOCK:
-		dev.mode |= O_NONBLOCK;
+		if (file->f_mode & FMODE_WRITE)
+			dev.play_ndelay = 1;
+		if (file->f_mode & FMODE_READ)
+			dev.rec_ndelay = 1;
 		return 0;
 
 	case SNDCTL_DSP_GETCAPS:
@@ -224,65 +306,58 @@
 		data = val;
 
 		for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
-			
-			writew(data, lpDAQ + DAQDS_wSampleRate);
-			writew(data, lpDARQ + DAQDS_wSampleRate);
+			if (file->f_mode & FMODE_WRITE)
+				writew(data, lpDAQ + DAQDS_wSampleRate);
+			if (file->f_mode & FMODE_READ)
+				writew(data, lpDARQ + DAQDS_wSampleRate);
 		}
-		
-		dev.sample_rate = data;
+		if (file->f_mode & FMODE_WRITE)
+			dev.play_sample_rate = data;
+		if (file->f_mode & FMODE_READ)
+			dev.rec_sample_rate = data;
 
 		if (put_user(data, (int *)arg))
 			return -EFAULT;
 		return 0;
 
 	case SNDCTL_DSP_CHANNELS:
-		if (get_user(val, (int *)arg))
-			return -EFAULT;
-			
-		switch (val) {
-		case 1:
-		case 2:
-			data = val;
-			break;
-		default:
-			val = data = 2;
-			break;
-		}
-									
-		for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
-
-			writew(data, lpDAQ + DAQDS_wChannels);
-			writew(data, lpDARQ + DAQDS_wChannels);
-		}
-
-		dev.channels = data;
-
-		if (put_user(val, (int *)arg))
-			return -EFAULT;
-		return 0;
-
 	case SNDCTL_DSP_STEREO:
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
-			
-		switch (val) {
-		case 0:
-			data = 1;
-			break;
-		default:
-			val = 1;
-		case 1:
-			data = 2;
-			break;
+
+		if (cmd == SNDCTL_DSP_CHANNELS) {
+			switch (val) {
+			case 1:
+			case 2:
+				data = val;
+				break;
+			default:
+				val = data = 2;
+				break;
+			}
+		} else {
+			switch (val) {
+			case 0:
+				data = 1;
+				break;
+			default:
+				val = 1;
+			case 1:
+				data = 2;
+				break;
+			}
 		}
 									
 		for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
-
-			writew(data, lpDAQ + DAQDS_wChannels);
-			writew(data, lpDARQ + DAQDS_wChannels);
+			if (file->f_mode & FMODE_WRITE)
+				writew(data, lpDAQ + DAQDS_wChannels);
+			if (file->f_mode & FMODE_READ)
+				writew(data, lpDARQ + DAQDS_wChannels);
 		}
-
-		dev.channels = data;
+		if (file->f_mode & FMODE_WRITE)
+			dev.play_channels = data;
+		if (file->f_mode & FMODE_READ)
+			dev.rec_channels = data;
 
 		if (put_user(val, (int *)arg))
 			return -EFAULT;
@@ -320,6 +395,12 @@
 	writew(dev.right_levels[a] * readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff / s,	\
 	       dev.SMA + SMA_##b##Right);
 
+#define update_pot(d,s,ar)						\
+	writeb(dev.left_levels[d] >> 8, dev.SMA + SMA_##s##Left);	\
+	writeb(dev.right_levels[d] >> 8, dev.SMA + SMA_##s##Right);	\
+	if (msnd_send_word(&dev, 0, 0, ar) == 0)			\
+		chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+
 static int mixer_set(int d, int value)
 {
 	int left = value & 0x000000ff;
@@ -350,7 +431,7 @@
 		writeb(bLeft, dev.SMA + SMA_bInPotPosLeft);
 		writeb(bRight, dev.SMA + SMA_bInPotPosRight);
 		if (msnd_send_word(&dev, 0, 0, HDEXAR_IN_SET_POTS) == 0)
-			msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
 		break;
 
 #ifndef MSND_CLASSIC
@@ -358,7 +439,7 @@
 		writeb(bLeft, dev.SMA + SMA_bMicPotPosLeft);
 		writeb(bRight, dev.SMA + SMA_bMicPotPosRight);
 		if (msnd_send_word(&dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0)
-			msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
 		break;
 #endif
 
@@ -366,7 +447,7 @@
 		writeb(bLeft, dev.SMA + SMA_bAuxPotPosLeft);
 		writeb(bRight, dev.SMA + SMA_bAuxPotPosRight);
 		if (msnd_send_word(&dev, 0, 0, HDEXAR_AUX_SET_POTS) == 0)
-			msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
 		break;
 
 		/* digital controls */
@@ -389,6 +470,20 @@
 	return mixer_get(d);
 }
 
+static void mixer_setup(void)
+{
+	update_pot(SOUND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);
+#ifndef MSND_CLASSIC
+	update_pot(SOUND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS);
+#endif
+	update_pot(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS);
+	update_vol(SOUND_MIXER_PCM, wCurrPlayVol, 1);
+	update_vol(SOUND_MIXER_IMIX, wCurrInVol, 1);
+#ifndef MSND_CLASSIC
+	update_vol(SOUND_MIXER_SYNTH, wCurrMHdrVol, 1);
+#endif
+}
+
 static unsigned long set_recsrc(unsigned long recsrc)
 {
 	if (dev.recsrc == recsrc)
@@ -403,17 +498,15 @@
 #ifndef MSND_CLASSIC
 	if (dev.recsrc & SOUND_MASK_LINE) {
 		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0)
-			msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
 	}
 	else if (dev.recsrc & SOUND_MASK_SYNTH) {
 		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_SYNTH_IN) == 0)
-			msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
 	}
 	else if ((dev.recsrc & SOUND_MASK_DIGITAL1) && test_bit(F_HAVEDIGITAL, &dev.flags)) {
-		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_DAT_IN) == 0) {
-			udelay(50);
-      			msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ);
-		}
+		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_DAT_IN) == 0)
+      			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
 	}
 	else {
 #ifdef HAVE_NORECSRC
@@ -422,7 +515,7 @@
 #else
 		dev.recsrc = SOUND_MASK_LINE;
 		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0)
-			msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ);
+			chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
 #endif
 	}
 #endif /* MSND_CLASSIC */
@@ -430,6 +523,12 @@
 	return dev.recsrc;
 }
 
+static unsigned long force_recsrc(unsigned long recsrc)
+{
+	dev.recsrc = 0;
+	return set_recsrc(recsrc);
+}
+
 #define set_mixer_info()							\
 		strncpy(info.id, "MSNDMIXER", sizeof(info.id));			\
 		strncpy(info.name, "MultiSound Mixer", sizeof(info.name));
@@ -447,10 +546,6 @@
 		set_mixer_info();
 		return copy_to_user((void *)arg, &info, sizeof(info));
 	}
-	else if (cmd == OSS_GETVERSION) {
-		int sound_version = SOUND_VERSION;
-		return put_user(sound_version, (int *)arg);
-	}
 	else if (((cmd >> 8) & 0xff) == 'M') {
 		int val = 0;
 		
@@ -521,77 +616,139 @@
 {
 	int minor = MINOR(inode->i_rdev);
 
+	if (cmd == OSS_GETVERSION) {
+		int sound_version = SOUND_VERSION;
+		return put_user(sound_version, (int *)arg);
+	}
+
 	if (minor == dev.dsp_minor)
-		return dsp_ioctl(cmd, arg);
+		return dsp_ioctl(file, cmd, arg);
 	else if (minor == dev.mixer_minor)
 		return mixer_ioctl(cmd, arg);
 
 	return -EINVAL;
 }
 
-static void dsp_halt(void)
+static void dsp_write_flush(void)
 {
-	mdelay(1);
-#ifdef LINUX20
-	if (test_bit(F_READING, &dev.flags)) {
-		clear_bit(F_READING, &dev.flags);
-#else
-	if (test_and_clear_bit(F_READING, &dev.flags)) {
-#endif
-		msnd_send_dsp_cmd(&dev, HDEX_RECORD_STOP);
-		msnd_disable_irq(&dev);
-
-	}
-	mdelay(1);
-#ifdef LINUX20
-	if (test_bit(F_WRITING, &dev.flags)) {
-		clear_bit(F_WRITING, &dev.flags);
-#else
-	if (test_and_clear_bit(F_WRITING, &dev.flags)) {
-#endif
-		set_bit(F_WRITEFLUSH, &dev.flags);
-		interruptible_sleep_on(&dev.writeflush);
+	if (!(dev.mode & FMODE_WRITE) || !test_bit(F_WRITING, &dev.flags))
+		return;
+	set_bit(F_WRITEFLUSH, &dev.flags);
+	current->timeout = jiffies + get_play_delay_jiffies(dev.DAPF.len) + HZ / 8;
+	interruptible_sleep_on(&dev.writeflush);
+	clear_bit(F_WRITEFLUSH, &dev.flags);
+	if (!signal_pending(current)) {
 		current->state = TASK_INTERRUPTIBLE;
-		current->timeout = 
-			jiffies + DAP_BUFF_SIZE / 2 * HZ /
-			dev.sample_rate / dev.channels;
+		current->timeout = jiffies + get_play_delay_jiffies(DAP_BUFF_SIZE);
 		schedule();
 		current->timeout = 0;
-		msnd_send_dsp_cmd(&dev, HDEX_PLAY_STOP);
-		msnd_disable_irq(&dev);
-		memset_io(dev.base, 0, DAP_BUFF_SIZE * 3);
+	} else
+		current->timeout = 0;
+	clear_bit(F_WRITING, &dev.flags);
+}
 
+static void dsp_halt(struct file *file)
+{
+	if ((file ? file->f_mode : dev.mode) & FMODE_READ) {
+		clear_bit(F_READING, &dev.flags);
+		chk_send_dsp_cmd(&dev, HDEX_RECORD_STOP);
+		msnd_disable_irq(&dev);
+		if (file) {
+			printk(KERN_DEBUG LOGNAME ": Stopping read for %p\n", file);
+			dev.mode &= ~FMODE_READ;
+		}
+		clear_bit(F_AUDIO_READ_INUSE, &dev.flags);
+	}
+	if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) {
+		if (test_bit(F_WRITING, &dev.flags)) {
+			dsp_write_flush();
+			chk_send_dsp_cmd(&dev, HDEX_PLAY_STOP);
+		}
+		msnd_disable_irq(&dev);
+		if (file) {
+			printk(KERN_DEBUG LOGNAME ": Stopping write for %p\n", file);
+			dev.mode &= ~FMODE_WRITE;
+		}
+		clear_bit(F_AUDIO_WRITE_INUSE, &dev.flags);
 	}
-	mdelay(1);
-	reset_queues();
 }
 
-static int dsp_open(struct file *file)
+static int dsp_release(struct file *file)
 {
-	dev.mode = file->f_mode;
-	set_bit(F_AUDIO_INUSE, &dev.flags);
-	reset_queues();
+	dsp_halt(file);
 	return 0;
 }
 
-static int dsp_close(void)
+static int dsp_open(struct file *file)
 {
-	dsp_halt();
-	clear_bit(F_AUDIO_INUSE, &dev.flags);
+	if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) {
+		set_bit(F_AUDIO_WRITE_INUSE, &dev.flags);
+		clear_bit(F_WRITING, &dev.flags);
+		msnd_fifo_make_empty(&dev.DAPF);
+		reset_play_queue();
+		if (file) {
+			printk(KERN_DEBUG LOGNAME ": Starting write for %p\n", file);
+			dev.mode |= FMODE_WRITE;
+		}
+		msnd_enable_irq(&dev);
+	}
+	if ((file ? file->f_mode : dev.mode) & FMODE_READ) {
+		set_bit(F_AUDIO_READ_INUSE, &dev.flags);
+		clear_bit(F_READING, &dev.flags);
+		msnd_fifo_make_empty(&dev.DARF);
+		reset_record_queue();
+		if (file) {
+			printk(KERN_DEBUG LOGNAME ": Starting read for %p\n", file);
+			dev.mode |= FMODE_READ;
+		}
+		msnd_enable_irq(&dev);
+	}
 	return 0;
 }
 
+static void set_default_play_audio_parameters(void)
+{
+	dev.play_sample_size = DEFSAMPLESIZE;
+	dev.play_sample_rate = DEFSAMPLERATE;
+	dev.play_channels = DEFCHANNELS;
+}
+
+static void set_default_rec_audio_parameters(void)
+{
+	dev.rec_sample_size = DEFSAMPLESIZE;
+	dev.rec_sample_rate = DEFSAMPLERATE;
+	dev.rec_channels = DEFCHANNELS;
+}
+
+static void set_default_audio_parameters(void)
+{
+	set_default_play_audio_parameters();
+	set_default_rec_audio_parameters();
+}
+
 static int dev_open(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
 	int err = 0;
 
 	if (minor == dev.dsp_minor) {
-
-		if (test_bit(F_AUDIO_INUSE, &dev.flags))
+		if ((file->f_mode & FMODE_WRITE &&
+		     test_bit(F_AUDIO_WRITE_INUSE, &dev.flags)) ||
+		    (file->f_mode & FMODE_READ &&
+		     test_bit(F_AUDIO_READ_INUSE, &dev.flags)))
 			return -EBUSY;
 
-		err = dsp_open(file);
+		if ((err = dsp_open(file)) >= 0) {
+			dev.nresets = 0;
+			if (file->f_mode & FMODE_WRITE) {
+				set_default_play_audio_parameters();
+				dev.play_ndelay = (file->f_mode & O_NDELAY) ? 1 : 0;
+			}
+			if (file->f_mode & FMODE_READ) {
+				set_default_rec_audio_parameters();
+				dev.rec_ndelay = (file->f_mode & O_NDELAY) ? 1 : 0;
+			}
+		}
 	}
 	else if (minor == dev.mixer_minor) {
 		/* nothing */
@@ -605,9 +762,9 @@
 }
 
 #ifdef LINUX20
-static void dev_close(struct inode *inode, struct file *file)
+static void dev_release(struct inode *inode, struct file *file)
 #else
-static int dev_close(struct inode *inode, struct file *file)
+static int dev_release(struct inode *inode, struct file *file)
 #endif
 {
 	int minor = MINOR(inode->i_rdev);
@@ -619,7 +776,7 @@
 #ifndef LINUX20
 		err = 
 #endif
-			dsp_close();
+			dsp_release(file);
 	}
 	else if (minor == dev.mixer_minor) {
 		/* nothing */
@@ -637,62 +794,137 @@
 #endif
 }
 
-static int DAPF_to_bank(int bank)
+static __inline__ int pack_DARQ_to_DARF(register int bank)
 {
-	return msnd_fifo_read(&dev.DAPF, dev.base + bank * DAP_BUFF_SIZE, DAP_BUFF_SIZE, 0);
+	register int size, n, timeout = 3;
+	register WORD wTmp;
+	LPDAQD DAQD;
+
+	/* Increment the tail and check for queue wrap */
+	wTmp = readw(dev.DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size);
+	if (wTmp > readw(dev.DARQ + JQS_wSize))
+		wTmp = 0;
+	while (wTmp == readw(dev.DARQ + JQS_wHead) && timeout--)
+		udelay(1);
+	writew(wTmp, dev.DARQ + JQS_wTail);
+
+	/* Get our digital audio queue struct */
+	DAQD = bank * DAQDS__size + dev.base + DARQ_DATA_BUFF;
+
+	/* Get length of data */
+	size = readw(DAQD + DAQDS_wSize);
+
+	/* Read data from the head (unprotected bank 1 access okay
+           since this is only called inside an interrupt) */
+	outb(HPBLKSEL_1, dev.io + HP_BLKS);
+	if ((n = msnd_fifo_write(
+		&dev.DARF,
+		(char *)(dev.base + bank * DAR_BUFF_SIZE),
+		size, 0)) < 0) {
+		outb(HPBLKSEL_0, dev.io + HP_BLKS);
+		return n;
+	}
+	outb(HPBLKSEL_0, dev.io + HP_BLKS);
+
+	return 1;
 }
 
-static int bank_to_DARF(int bank)
+static __inline__ int pack_DAPF_to_DAPQ(register int start)
 {
-	return msnd_fifo_write(&dev.DARF, dev.base + bank * DAR_BUFF_SIZE, DAR_BUFF_SIZE, 0);
+	register WORD DAPQ_tail;
+	register int protect = start, nbanks = 0;
+	LPDAQD DAQD;
+
+	DAPQ_tail = readw(dev.DAPQ + JQS_wTail);
+	while (DAPQ_tail != readw(dev.DAPQ + JQS_wHead) || start) {
+		register int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size);
+		register int n;
+		unsigned long flags;
+		
+		/* Write the data to the new tail */
+		if (protect) {
+			/* Critical section: protect fifo in non-interrupt */
+			spin_lock_irqsave(&dev.lock, flags);
+			if ((n = msnd_fifo_read(
+				&dev.DAPF,
+				(char *)(dev.base + bank_num * DAP_BUFF_SIZE),
+				DAP_BUFF_SIZE, 0)) < 0) {
+				spin_unlock_irqrestore(&dev.lock, flags);
+				return n;
+			}
+			spin_unlock_irqrestore(&dev.lock, flags);
+		} else {
+			if ((n = msnd_fifo_read(
+				&dev.DAPF,
+				(char *)(dev.base + bank_num * DAP_BUFF_SIZE),
+				DAP_BUFF_SIZE, 0)) < 0) {
+				return n;
+			}
+		}
+		if (!n)
+			break;
+
+		if (start)
+			start = 0;
+
+		/* Get our digital audio queue struct */
+		DAQD = bank_num * DAQDS__size + dev.base + DAPQ_DATA_BUFF;
+
+		/* Write size of this bank */
+		writew(n, DAQD + DAQDS_wSize);
+		++nbanks;
+
+		/* Then advance the tail */
+		DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size);
+		writew(DAPQ_tail, dev.DAPQ + JQS_wTail);
+		
+		/* Tell the DSP to play the bank */
+		msnd_send_dsp_cmd(&dev, HDEX_PLAY_START);
+	}
+	
+	return nbanks;
 }
 
 static int dsp_read(char *buf, size_t len)
 {
-	int err = 0;
 	int count = len;
 
 	while (count > 0) {
-		
 		int n;
+		unsigned long flags;
 
+		/* Critical section: protect fifo in non-interrupt */
+		spin_lock_irqsave(&dev.lock, flags);
 		if ((n = msnd_fifo_read(&dev.DARF, buf, count, 1)) < 0) {
-
 			printk(KERN_WARNING LOGNAME ": FIFO read error\n");
+			spin_unlock_irqrestore(&dev.lock, flags);
 			return n;
 		}
-
+		spin_unlock_irqrestore(&dev.lock, flags);
 		buf += n;
 		count -= n;
 
-#ifdef LINUX20		
-		if (!test_bit(F_READING, &dev.flags) && (dev.mode & FMODE_READ)) {
-			set_bit(F_READING, &dev.flags);
-#else
-		if (!test_and_set_bit(F_READING, &dev.flags) && (dev.mode & FMODE_READ)) {
-#endif
-			reset_record_queue();
-			msnd_enable_irq(&dev);
-			msnd_send_dsp_cmd(&dev, HDEX_RECORD_START);
-
+		if (!test_bit(F_READING, &dev.flags) && dev.mode & FMODE_READ) {
+			dev.last_recbank = -1;
+			if (chk_send_dsp_cmd(&dev, HDEX_RECORD_START) == 0)
+				set_bit(F_READING, &dev.flags);
 		}
 
-		if (dev.mode & O_NONBLOCK)
+		if (dev.rec_ndelay)
 			return count == len ? -EAGAIN : len - count;
 
 		if (count > 0) {
-
 			set_bit(F_READBLOCK, &dev.flags);
+			current->timeout = jiffies + get_rec_delay_jiffies(DAR_BUFF_SIZE);
 			interruptible_sleep_on(&dev.readblock);
+			if (current->timeout == 0)
+				clear_bit(F_READING, &dev.flags);
+			else
+				current->timeout = 0;
 			clear_bit(F_READBLOCK, &dev.flags);
-
 			if (signal_pending(current))
-				err = -EINTR;
-
+				return -EINTR;
 		}
-
-		if (err != 0)
-			return err;
 	}
 
 	return len - count;
@@ -700,50 +932,41 @@
 
 static int dsp_write(const char *buf, size_t len)
 {
-	int err = 0;
 	int count = len;
 
 	while (count > 0) {
-
 		int n;
+		unsigned long flags;
 
+		/* Critical section: protect fifo in non-interrupt */
+		spin_lock_irqsave(&dev.lock, flags);
 		if ((n = msnd_fifo_write(&dev.DAPF, buf, count, 1)) < 0) {
-
 			printk(KERN_WARNING LOGNAME ": FIFO write error\n");
+			spin_unlock_irqrestore(&dev.lock, flags);
 			return n;
 		}
-
+		spin_unlock_irqrestore(&dev.lock, flags);
 		buf += n;
 		count -= n;
 
-#ifdef LINUX20
 		if (!test_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) {
-			set_bit(F_WRITING, &dev.flags);
-#else
-		if (!test_and_set_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) {
-#endif
-			reset_play_queue();
-			msnd_enable_irq(&dev);
-			msnd_send_dsp_cmd(&dev, HDEX_PLAY_START);
-
+			dev.last_playbank = -1;
+			if (pack_DAPF_to_DAPQ(1) > 0)
+				set_bit(F_WRITING, &dev.flags);
 		}
 
-		if (dev.mode & O_NONBLOCK)
+		if (dev.play_ndelay)
 			return count == len ? -EAGAIN : len - count;
 
 		if (count > 0) {
-			
 			set_bit(F_WRITEBLOCK, &dev.flags);
+			current->timeout = jiffies + get_play_delay_jiffies(DAP_BUFF_SIZE);
 			interruptible_sleep_on(&dev.writeblock);
+			current->timeout = 0;
 			clear_bit(F_WRITEBLOCK, &dev.flags);
-
 			if (signal_pending(current))
-				err = -EINTR;
-
+				return -EINTR;
 		}
-
-		if (err != 0)
-			return err;
 	}
 	
 	return len - count;
@@ -758,12 +981,9 @@
 {
 	int minor = MINOR(file->f_dentry->d_inode->i_rdev);
 #endif
-
-	if (minor == dev.dsp_minor) {
-
+	if (minor == dev.dsp_minor)
 		return dsp_read(buf, count);
-
-	} else
+	else
 		return -EINVAL;
 }
 
@@ -776,44 +996,22 @@
 {
 	int minor = MINOR(file->f_dentry->d_inode->i_rdev);
 #endif
-
-	if (minor == dev.dsp_minor) {
-
+	if (minor == dev.dsp_minor)
 		return dsp_write(buf, count);
-
-	} else
+	else
 		return -EINVAL;
 }
 
-static void eval_dsp_msg(WORD wMessage)
+static __inline__ void eval_dsp_msg(register WORD wMessage)
 {
-	WORD wTmp;
-
 	switch (HIBYTE(wMessage)) {
 	case HIMT_PLAY_DONE:
-		if (dev.lastbank == LOBYTE(wMessage))
+		if (dev.last_playbank == LOBYTE(wMessage) || !test_bit(F_WRITING, &dev.flags))
 			break;
-		
-		dev.lastbank = LOBYTE(wMessage);
-
-		writew(DAP_BUFF_SIZE, dev.CurDAQD + DAQDS_wSize);
-
-		wTmp = readw(dev.DAPQ + JQS_wTail) + PCTODSP_OFFSET(DAPQ_STRUCT_SIZE);
-		if (wTmp > readw(dev.DAPQ + JQS_wSize))
-			writew(0, dev.DAPQ + JQS_wTail);
-		else
-			writew(wTmp, dev.DAPQ + JQS_wTail);
+		dev.last_playbank = LOBYTE(wMessage);
 
-		if ((dev.CurDAQD += DAQDS__size) > (LPDAQD)(dev.base + DAPQ_DATA_BUFF + 2 * DAPQ_STRUCT_SIZE))
-			dev.CurDAQD = (LPDAQD)(dev.base + DAPQ_DATA_BUFF);
-
-		if (dev.lastbank < 3) {
-			if (DAPF_to_bank(dev.lastbank) > 0) {
-				mdelay(1);
-				msnd_send_dsp_cmd(&dev, HDEX_PLAY_START);
-			} 
-			else if (!test_bit(F_WRITEBLOCK, &dev.flags)) {
-				clear_bit(F_WRITING, &dev.flags);
+		if (pack_DAPF_to_DAPQ(0) <= 0) {
+			if (!test_bit(F_WRITEBLOCK, &dev.flags)) {
 #ifdef LINUX20
 				if (test_bit(F_WRITEFLUSH, &dev.flags)) {
 					clear_bit(F_WRITEFLUSH, &dev.flags);
@@ -824,6 +1022,7 @@
 					wake_up_interruptible(&dev.writeflush);
 #endif
 			}
+			clear_bit(F_WRITING, &dev.flags);
 		}
 
 		if (test_bit(F_WRITEBLOCK, &dev.flags))
@@ -831,21 +1030,11 @@
 		break;
 
 	case HIMT_RECORD_DONE:
-		wTmp = readw(dev.DARQ + JQS_wTail) + DARQ_STRUCT_SIZE / 2;
-
-		if (wTmp > readw(dev.DARQ + JQS_wSize))
-			wTmp = 0;
-
-		while (wTmp == readw(dev.DARQ + JQS_wHead));
-
-		writew(wTmp, dev.DARQ + JQS_wTail);
+		if (dev.last_recbank == LOBYTE(wMessage))
+			break;
+		dev.last_recbank = LOBYTE(wMessage);
 
-		outb(HPBLKSEL_1, dev.io + HP_BLKS);
-		if (bank_to_DARF(LOBYTE(wMessage)) == 0 && !test_bit(F_READBLOCK, &dev.flags)) {
-			memset_io(dev.base, 0, DAR_BUFF_SIZE * 3);
-			clear_bit(F_READING, &dev.flags);
-		}
-		outb(HPBLKSEL_0, dev.io + HP_BLKS);
+		pack_DARQ_to_DARF(dev.last_recbank);
 
 		if (test_bit(F_READBLOCK, &dev.flags))
 			wake_up_interruptible(&dev.readblock);
@@ -857,17 +1046,17 @@
 		case HIDSP_PLAY_UNDER:
 #endif
 		case HIDSP_INT_PLAY_UNDER:
-/*			printk(KERN_INFO LOGNAME ": Write underflow\n"); */
-			reset_play_queue();
+/*			printk(KERN_DEBUG LOGNAME ": Play underflow\n"); */
+			clear_bit(F_WRITING, &dev.flags);
 			break;
 
 		case HIDSP_INT_RECORD_OVER:
-/*			printk(KERN_INFO LOGNAME ": Read overflow\n"); */
-			reset_record_queue();
+/*			printk(KERN_DEBUG LOGNAME ": Record overflow\n"); */
+			clear_bit(F_READING, &dev.flags);
 			break;
 
 		default:
-			printk(KERN_DEBUG LOGNAME ": DSP message %u\n", LOBYTE(wMessage));
+/*			printk(KERN_DEBUG LOGNAME ": DSP message %d 0x%02x\n", LOBYTE(wMessage), LOBYTE(wMessage)); */
 			break;
 		}
 		break;
@@ -877,85 +1066,66 @@
 			(*dev.midi_in_interrupt)(&dev);
 		break;
 
-	case HIMT_MIDI_OUT:
-		printk(KERN_DEBUG LOGNAME ": MIDI out event\n");
-		break;
-
 	default:
-		printk(KERN_DEBUG LOGNAME ": HIMT message %u\n", HIBYTE(wMessage));
+/*		printk(KERN_DEBUG LOGNAME ": HIMT message %d 0x%02x\n", HIBYTE(wMessage), HIBYTE(wMessage)); */
 		break;
 	}
 }
 
 static void intr(int irq, void *dev_id, struct pt_regs *regs)
 {
-	if (test_bit(F_INTERRUPT, &dev.flags))
-		return;
-
-	set_bit(F_INTERRUPT, &dev.flags);
-	
-	if (test_bit(F_BANKONE, &dev.flags))
-		outb(HPBLKSEL_0, dev.io + HP_BLKS);
-
+	/* Send ack to DSP */
 	inb(dev.io + HP_RXL);
- 
+
+	/* Evaluate queued DSP messages */
 	while (readw(dev.DSPQ + JQS_wTail) != readw(dev.DSPQ + JQS_wHead)) {
-		WORD wTmp;
+		register WORD wTmp;
 
-		eval_dsp_msg(*(dev.pwDSPQData + readw(dev.DSPQ + JQS_wHead)));
-		
-		wTmp = readw(dev.DSPQ + JQS_wHead) + 1;
-		if (wTmp > readw(dev.DSPQ + JQS_wSize))
+		eval_dsp_msg(readw(dev.pwDSPQData + readw(dev.DSPQ + JQS_wHead)));
+
+		if ((wTmp = readw(dev.DSPQ + JQS_wHead) + 1) > readw(dev.DSPQ + JQS_wSize))
 			writew(0, dev.DSPQ + JQS_wHead);
 		else
 			writew(wTmp, dev.DSPQ + JQS_wHead);
 	}
-
-	if (test_bit(F_BANKONE, &dev.flags))
-		outb(HPBLKSEL_1, dev.io + HP_BLKS);
-
-	clear_bit(F_INTERRUPT, &dev.flags);
 }
 
 static struct file_operations dev_fileops = {
-	NULL,
-	dev_read,
-	dev_write,
-	NULL,
-	NULL,
-	dev_ioctl,
-	NULL,
-	dev_open,
+	NULL,		/* llseek */
+	dev_read,	/* read */
+	dev_write,	/* write */
+	NULL,		/* readdir */
+	NULL,		/* poll */
+	dev_ioctl,	/* ioctl */
+	NULL,		/* mmap */
+	dev_open,	/* open */
+	NULL,		/* flush */
+	dev_release,	/* release */
+	NULL,		/* fsync */
+	NULL,		/* fasync */
+	NULL,		/* check_media_change */
+	NULL,		/* revalidate */
 #ifndef LINUX20
-#  if LINUX_VERSION_CODE >= 0x020100 + 118
-	NULL,
-#  endif /* >= 2.1.118 */
+	NULL,		/* lock */
 #endif
-	dev_close,
 };
 
-__initfunc(static int reset_dsp(void))
+static int reset_dsp(void)
 {
 	int timeout = 100;
 		
 	outb(HPDSPRESET_ON, dev.io + HP_DSPR);
-	
 	mdelay(1);
-
+#ifndef MSND_CLASSIC
 	dev.info = inb(dev.io + HP_INFO);
-
+#endif
 	outb(HPDSPRESET_OFF, dev.io + HP_DSPR);
-
 	mdelay(1);
-
 	while (timeout-- > 0) {
-
 		if (inb(dev.io + HP_CVR) == HP_CVR_DEF)
 			return 0;
-		
 		mdelay(1);
 	}
-
 	printk(KERN_ERR LOGNAME ": Cannot reset DSP\n");
 
 	return -EIO;
@@ -973,7 +1143,6 @@
 		printk(KERN_ERR LOGNAME ": I/O port conflict\n");
 		return -ENODEV;
 	}
-
 	request_region(dev.io, dev.numio, "probing");
 
 	if (reset_dsp() < 0) {
@@ -1024,127 +1193,82 @@
 	return 0;
 }
 
-__initfunc(static int init_sma(void))
+static void msnd_init_queue(volatile BYTE *base, int start, int size)
 {
-	int n;
-	LPDAQD lpDAQ;
+	writew(PCTODSP_BASED(start), base + JQS_wStart);
+	writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
+	writew(0, base + JQS_wHead);
+	writew(0, base + JQS_wTail);
+}
+
+static int init_sma(void)
+{
+	static int initted;
+	WORD mastVolLeft, mastVolRight;
+	unsigned long flags;
 
 #ifdef MSND_CLASSIC
 	outb(dev.memid, dev.io + HP_MEMM);
 #endif
 	outb(HPBLKSEL_0, dev.io + HP_BLKS);
+	if (initted) {
+		mastVolLeft = readw(dev.SMA + SMA_wCurrMastVolLeft);
+		mastVolRight = readw(dev.SMA + SMA_wCurrMastVolRight);
+	} else
+		mastVolLeft = mastVolRight = 0;
 	memset_io(dev.base, 0, 0x8000);
-	
+
+	/* Critical section: bank 1 access */
+	spin_lock_irqsave(&dev.lock, flags);
 	outb(HPBLKSEL_1, dev.io + HP_BLKS);
 	memset_io(dev.base, 0, 0x8000);
-	
 	outb(HPBLKSEL_0, dev.io + HP_BLKS);
+	spin_unlock_irqrestore(&dev.lock, flags);
 
-	dev.DAPQ = (BYTE *)(dev.base + DAPQ_OFFSET);
-	dev.DARQ = (BYTE *)(dev.base + DARQ_OFFSET);
-	dev.MODQ = (BYTE *)(dev.base + MODQ_OFFSET);
-	dev.MIDQ = (BYTE *)(dev.base + MIDQ_OFFSET);
-	dev.DSPQ = (BYTE *)(dev.base + DSPQ_OFFSET);
-	dev.SMA = (BYTE *)(dev.base + SMA_STRUCT_START);
-
-	dev.CurDAQD = (LPDAQD)(dev.base + DAPQ_DATA_BUFF);
-	dev.CurDARQD = (LPDAQD)(dev.base + DARQ_DATA_BUFF);
-
-	dev.sample_size = DEFSAMPLESIZE;
-	dev.sample_rate = DEFSAMPLERATE;
-	dev.channels = DEFCHANNELS;
-
-	for (n = 0, lpDAQ = dev.CurDAQD; n < 3; ++n, lpDAQ += DAQDS__size) {
-		
-		writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), lpDAQ + DAQDS_wStart);
-		writew(DAP_BUFF_SIZE, lpDAQ + DAQDS_wSize);
-		writew(1, lpDAQ + DAQDS_wFormat);
-		writew(dev.sample_size, lpDAQ + DAQDS_wSampleSize);
-		writew(dev.channels, lpDAQ + DAQDS_wChannels);
-		writew(dev.sample_rate, lpDAQ + DAQDS_wSampleRate);
-		writew(HIMT_PLAY_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
-		writew(n + 1, lpDAQ + DAQDS_wFlags);
-	}
-
-	for (n = 0, lpDAQ = dev.CurDARQD; n < 3; ++n, lpDAQ += DAQDS__size) {
+	dev.pwDSPQData = (volatile WORD *)(dev.base + DSPQ_DATA_BUFF);
+	dev.pwMODQData = (volatile WORD *)(dev.base + MODQ_DATA_BUFF);
+	dev.pwMIDQData = (volatile WORD *)(dev.base + MIDQ_DATA_BUFF);
 
-		writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, lpDAQ + DAQDS_wStart);
-		writew(DAR_BUFF_SIZE, lpDAQ + DAQDS_wSize);
-		writew(1, lpDAQ + DAQDS_wFormat);
-		writew(dev.sample_size, lpDAQ + DAQDS_wSampleSize);
-		writew(dev.channels, lpDAQ + DAQDS_wChannels);
-		writew(dev.sample_rate, lpDAQ + DAQDS_wSampleRate);
-		writew(HIMT_RECORD_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
-		writew(n + 1, lpDAQ + DAQDS_wFlags);
+	/* Motorola 56k shared memory base */
+	dev.SMA = dev.base + SMA_STRUCT_START;
 
-	}	
+	/* Digital audio play queue */
+	dev.DAPQ = dev.base + DAPQ_OFFSET;
+	msnd_init_queue(dev.DAPQ, DAPQ_DATA_BUFF, DAPQ_BUFF_SIZE);
 
-	dev.pwDSPQData = (WORD *)(dev.base + DSPQ_DATA_BUFF);
-	dev.pwMODQData = (WORD *)(dev.base + MODQ_DATA_BUFF);
-	dev.pwMIDQData = (WORD *)(dev.base + MIDQ_DATA_BUFF);
-
-	writew(PCTODSP_BASED(MIDQ_DATA_BUFF), dev.MIDQ + JQS_wStart);
-	writew(PCTODSP_OFFSET(MIDQ_BUFF_SIZE) - 1, dev.MIDQ + JQS_wSize);
-	writew(0, dev.MIDQ + JQS_wHead);
-	writew(0, dev.MIDQ + JQS_wTail);
-
-	writew(PCTODSP_BASED(MODQ_DATA_BUFF), dev.MODQ + JQS_wStart);
-	writew(PCTODSP_OFFSET(MODQ_BUFF_SIZE) - 1, dev.MODQ + JQS_wSize);
-	writew(0, dev.MODQ + JQS_wHead);
-	writew(0, dev.MODQ + JQS_wTail);
-
-	writew(PCTODSP_BASED(DAPQ_DATA_BUFF), dev.DAPQ + JQS_wStart);
-	writew(PCTODSP_OFFSET(DAPQ_BUFF_SIZE) - 1, dev.DAPQ + JQS_wSize);
-	writew(0, dev.DAPQ + JQS_wHead);
-	writew(0, dev.DAPQ + JQS_wTail);
-
-	writew(PCTODSP_BASED(DARQ_DATA_BUFF), dev.DARQ + JQS_wStart);
-	writew(PCTODSP_OFFSET(DARQ_BUFF_SIZE) - 1, dev.DARQ + JQS_wSize);
-	writew(0, dev.DARQ + JQS_wHead);
-	writew(0, dev.DARQ + JQS_wTail);
-
-	writew(PCTODSP_BASED(DSPQ_DATA_BUFF), dev.DSPQ + JQS_wStart);
-	writew(PCTODSP_OFFSET(DSPQ_BUFF_SIZE) - 1, dev.DSPQ + JQS_wSize);
-	writew(0, dev.DSPQ + JQS_wHead);
-	writew(0, dev.DSPQ + JQS_wTail);
-
-	writew(0, dev.SMA + SMA_wCurrPlayBytes);
-	writew(0, dev.SMA + SMA_wCurrRecordBytes);
+	/* Digital audio record queue */
+	dev.DARQ = dev.base + DARQ_OFFSET;
+	msnd_init_queue(dev.DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE);
 
-	writew(0, dev.SMA + SMA_wCurrPlayVolLeft);
-	writew(0, dev.SMA + SMA_wCurrPlayVolRight);
+	/* MIDI out queue */
+	dev.MODQ = dev.base + MODQ_OFFSET;
+	msnd_init_queue(dev.MODQ, MODQ_DATA_BUFF, MODQ_BUFF_SIZE);
 
-	writew(0, dev.SMA + SMA_wCurrInVolLeft);
-	writew(0, dev.SMA + SMA_wCurrInVolRight);
+	/* MIDI in queue */
+	dev.MIDQ = dev.base + MIDQ_OFFSET;
+	msnd_init_queue(dev.MIDQ, MIDQ_DATA_BUFF, MIDQ_BUFF_SIZE);
 
-	writew(0, dev.SMA + SMA_wCurrMastVolLeft);
-	writew(0, dev.SMA + SMA_wCurrMastVolRight);
+	/* DSP -> host message queue */
+	dev.DSPQ = dev.base + DSPQ_OFFSET;
+	msnd_init_queue(dev.DSPQ, DSPQ_DATA_BUFF, DSPQ_BUFF_SIZE);
 
+	/* Setup some DSP values */
+#ifndef MSND_CLASSIC
+	writew(1, dev.SMA + SMA_wCurrPlayFormat);
+	writew(dev.play_sample_size, dev.SMA + SMA_wCurrPlaySampleSize);
+	writew(dev.play_channels, dev.SMA + SMA_wCurrPlayChannels);
+	writew(dev.play_sample_rate, dev.SMA + SMA_wCurrPlaySampleRate);
+#endif
+	writew(dev.play_sample_rate, dev.SMA + SMA_wCalFreqAtoD);
+	writew(mastVolLeft, dev.SMA + SMA_wCurrMastVolLeft);
+	writew(mastVolRight, dev.SMA + SMA_wCurrMastVolRight);
 #ifndef MSND_CLASSIC
 	writel(0x00010000, dev.SMA + SMA_dwCurrPlayPitch);
 	writel(0x00000001, dev.SMA + SMA_dwCurrPlayRate);
 #endif
-
-	writew(0x0000, dev.SMA + SMA_wCurrDSPStatusFlags);
-	writew(0x0000, dev.SMA + SMA_wCurrHostStatusFlags);
-
 	writew(0x303, dev.SMA + SMA_wCurrInputTagBits);
-	writew(0, dev.SMA + SMA_wCurrLeftPeak);
-	writew(0, dev.SMA + SMA_wCurrRightPeak);
-
-	writeb(0, dev.SMA + SMA_bInPotPosRight);
-	writeb(0, dev.SMA + SMA_bInPotPosLeft);
 
-	writeb(0, dev.SMA + SMA_bAuxPotPosRight);
-	writeb(0, dev.SMA + SMA_bAuxPotPosLeft);
-
-#ifndef MSND_CLASSIC
-	writew(1, dev.SMA + SMA_wCurrPlayFormat);
-	writew(dev.sample_size, dev.SMA + SMA_wCurrPlaySampleSize);
-	writew(dev.channels, dev.SMA + SMA_wCurrPlayChannels);
-	writew(dev.sample_rate, dev.SMA + SMA_wCurrPlaySampleRate);
-#endif
-	writew(dev.sample_rate, dev.SMA + SMA_wCalFreqAtoD);
+	initted = 1;
 
 	return 0;
 }
@@ -1164,7 +1288,7 @@
 	writew(srate, dev.SMA + SMA_wCalFreqAtoD);
 
 	if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 &&
-	    msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) {
+	    chk_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) {
 		current->state = TASK_INTERRUPTIBLE;
 		current->timeout = jiffies + HZ / 3;
 		schedule();
@@ -1172,13 +1296,12 @@
 		printk("successful\n");
 		return 0;
 	}
-
 	printk("failed\n");
 
 	return -EIO;
 }
 
-__initfunc(static int upload_dsp_code(void))
+static int upload_dsp_code(void)
 {
 	outb(HPBLKSEL_0, dev.io + HP_BLKS);
 
@@ -1214,7 +1337,7 @@
 }
 
 #ifdef MSND_CLASSIC
-__initfunc(static void reset_proteus(void))
+static void reset_proteus(void)
 {
 	outb(HPPRORESET_ON, dev.io + HP_PROR);
 	mdelay(TIME_PRO_RESET);
@@ -1223,7 +1346,7 @@
 }
 #endif
 
-__initfunc(static int initialize(void))
+static int initialize(void)
 {
 	int err, timeout;
 
@@ -1233,7 +1356,6 @@
 
 	reset_proteus();
 #endif
-
 	if ((err = init_sma()) < 0) {
 		printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n");
 		return err;
@@ -1250,36 +1372,52 @@
 		printk(KERN_INFO LOGNAME ": DSP upload successful\n");
 
 	timeout = 200;
-
 	while (readw(dev.base)) {
 		mdelay(1);
-		if (--timeout < 0)
+		if (!timeout--) {
+			printk(KERN_DEBUG LOGNAME ": DSP reset timeout\n");
 			return -EIO;
+		}
 	}
 
+	mixer_setup();
+
 	return 0;
 }
 
+static int dsp_full_reset(void)
+{
+	int rv;
+
+	if (test_bit(F_RESETTING, &dev.flags) || ++dev.nresets > 10)
+		return 0;
+
+	printk(KERN_INFO LOGNAME ": Resetting DSP\n");
+	set_bit(F_RESETTING, &dev.flags);
+	dsp_halt(NULL);			/* Unconditionally halt */
+	if ((rv = initialize()))
+		printk(KERN_WARNING LOGNAME ": DSP reset failed\n");
+	force_recsrc(dev.recsrc);
+	dsp_open(NULL);
+	clear_bit(F_RESETTING, &dev.flags);
+
+	return rv;
+}
+
 __initfunc(static int attach_multisound(void))
 {
 	int err;
 
-	printk(KERN_DEBUG LOGNAME ": Intializing DSP\n");
-
-	if ((err = request_irq(dev.irq, intr, SA_SHIRQ, dev.name, &dev)) < 0) {
+	if ((err = request_irq(dev.irq, intr, 0, dev.name, &dev)) < 0) {
 		printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", dev.irq);
 		return err;
-	
 	}
-
 	request_region(dev.io, dev.numio, dev.name);
 
-        if ((err = initialize()) < 0) {
-		printk(KERN_WARNING LOGNAME ": Initialization failure\n");
+        if ((err = dsp_full_reset()) < 0) {
 		release_region(dev.io, dev.numio);
 		free_irq(dev.irq, &dev);
 		return err;
-
 	}
 
 	if ((err = msnd_register(&dev)) < 0) {
@@ -1307,15 +1445,17 @@
 	}
 	printk(KERN_INFO LOGNAME ": Using DSP minor %d, mixer minor %d\n", dev.dsp_minor, dev.mixer_minor);
 
-	calibrate_adc(dev.sample_rate);
+	disable_irq(dev.irq);
+	calibrate_adc(dev.play_sample_rate);
 #ifndef MSND_CLASSIC
 	printk(KERN_INFO LOGNAME ": Setting initial recording source to Line In\n");
-	set_recsrc(SOUND_MASK_LINE);
+	force_recsrc(SOUND_MASK_LINE);
 #endif
 	
 	return 0;
 }
 
+#ifdef MODULE
 static void unload_multisound(void)
 {
 	release_region(dev.io, dev.numio);
@@ -1324,6 +1464,7 @@
 	unregister_sound_dsp(dev.dsp_minor);
 	msnd_unregister(&dev);
 }
+#endif
 
 static void mod_inc_ref(void)
 {
@@ -1558,7 +1699,7 @@
 #endif
 static int cfg __initdata =		CONFIG_MSNDPIN_CFG;
 /* If not a module, we don't need to bother with reset=1 */
-static int reset __initdata;
+static int reset;
 
 /* Extra Peripheral Configuration (Default: Disable) */
 #ifndef CONFIG_MSNDPIN_MPU_IO
@@ -1626,10 +1767,8 @@
 	printk(KERN_INFO LOGNAME ": Turtle Beach " LONGNAME " Linux Driver Version "
 	       VERSION ", Copyright (C) 1998 Andrew Veliath\n");
 	
-	if (io == -1 || irq == -1 || mem == -1) {
-
+	if (io == -1 || irq == -1 || mem == -1)
 		printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n");
-	}
 
 	if (io == -1 ||
 	    !(io == 0x290 ||
@@ -1640,7 +1779,6 @@
 	      io == 0x220 ||
 	      io == 0x210 ||
 	      io == 0x3e0)) {
-
 		printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, or 0x3E0\n");
 		return -EINVAL;
 	}
@@ -1652,7 +1790,6 @@
 	      irq == 10 ||
 	      irq == 11 ||
 	      irq == 12)) {
-		
 		printk(KERN_ERR LOGNAME ": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n");
 		return -EINVAL;
 	}
@@ -1664,7 +1801,6 @@
 	      mem == 0xd8000 ||
 	      mem == 0xe0000 ||
 	      mem == 0xe8000)) {
-		
 		printk(KERN_ERR LOGNAME ": \"mem\" - must be set to "
 		       "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n");
 		return -EINVAL;
@@ -1734,9 +1870,10 @@
 	if (fifosize < 16)
 		fifosize = 16;
 
-	if (fifosize > 768)
-		fifosize = 768;
+	if (fifosize > 1024)
+		fifosize = 1024;
 
+	set_default_audio_parameters();
 #ifdef MSND_CLASSIC
 	dev.type = msndClassic;
 #else
@@ -1771,34 +1908,28 @@
 	printk(KERN_INFO LOGNAME ": Using %u byte digital audio FIFOs (x2)\n", dev.fifosize);
 
 	if ((err = msnd_fifo_alloc(&dev.DAPF, dev.fifosize)) < 0) {
-		
 		printk(KERN_ERR LOGNAME ": Couldn't allocate write FIFO\n");
 		return err;
 	}
 
 	if ((err = msnd_fifo_alloc(&dev.DARF, dev.fifosize)) < 0) {
-		
 		printk(KERN_ERR LOGNAME ": Couldn't allocate read FIFO\n");
 		msnd_fifo_free(&dev.DAPF);
 		return err;
 	}
 
 	if ((err = probe_multisound()) < 0) {
-
 		printk(KERN_ERR LOGNAME ": Probe failed\n");
 		msnd_fifo_free(&dev.DAPF);
 		msnd_fifo_free(&dev.DARF);
 		return err;
-
 	}
 	
 	if ((err = attach_multisound()) < 0) {
-
 		printk(KERN_ERR LOGNAME ": Attach failed\n");
 		msnd_fifo_free(&dev.DAPF);
 		msnd_fifo_free(&dev.DARF);
 		return err;
-
 	}
 
 	return 0;
@@ -1813,6 +1944,5 @@
 
 	msnd_fifo_free(&dev.DAPF);
 	msnd_fifo_free(&dev.DARF);
-
 }
 #endif
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/msnd_pinnacle.h linux/drivers/sound/msnd_pinnacle.h
--- v2.1.121/linux/drivers/sound/msnd_pinnacle.h	Sat Sep  5 16:46:41 1998
+++ linux/drivers/sound/msnd_pinnacle.h	Thu Sep 10 16:37:26 1998
@@ -24,7 +24,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd_pinnacle.h,v 1.8 1998/09/03 06:39:47 andrewtv Exp $
+ * $Id: msnd_pinnacle.h,v 1.10 1998/09/10 04:11:18 andrewtv Exp $
  *
  ********************************************************************/
 #ifndef __MSND_PINNACLE_H
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/pss.c linux/drivers/sound/pss.c
--- v2.1.121/linux/drivers/sound/pss.c	Thu Jul 16 18:09:27 1998
+++ linux/drivers/sound/pss.c	Thu Sep 10 16:37:26 1998
@@ -796,7 +796,7 @@
 			data = (unsigned short *)mbuf->data;
 			save_flags(flags);
 			cli();
-			for (i = 0; i < mbuf->len; i++) {
+			for (i = 0; i < sizeof(mbuf->data)/sizeof(unsigned short); i++) {
 				mbuf->len = i;	/* feed back number of WORDs read */
 				if (!pss_get_dspword(devc, data++)) {
 					if (i == 0)
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/sb.h linux/drivers/sound/sb.h
--- v2.1.121/linux/drivers/sound/sb.h	Thu Aug 20 17:05:16 1998
+++ linux/drivers/sound/sb.h	Thu Sep 10 16:37:26 1998
@@ -122,7 +122,7 @@
 unsigned int sb_getmixer (sb_devc *devc, unsigned int port);
 int sb_dsp_detect (struct address_info *hw_config);
 int sb_dsp_init (struct address_info *hw_config);
-void sb_dsp_unload(struct address_info *hw_config);
+void sb_dsp_unload(struct address_info *hw_config, int sbmpu);
 int sb_mixer_init(sb_devc *devc);
 void sb_mixer_set_stereo (sb_devc *devc, int mode);
 void smw_mixer_init(sb_devc *devc);
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/sb_card.c linux/drivers/sound/sb_card.c
--- v2.1.121/linux/drivers/sound/sb_card.c	Thu Aug 20 17:05:16 1998
+++ linux/drivers/sound/sb_card.c	Thu Sep 10 16:37:26 1998
@@ -22,6 +22,8 @@
 #include "sb_mixer.h"
 #include "sb.h"
 
+static int sbmpu = 0;
+
 void attach_sb_card(struct address_info *hw_config)
 {
 #if defined(CONFIG_AUDIO) || defined(CONFIG_MIDI)
@@ -43,7 +45,7 @@
 void unload_sb(struct address_info *hw_config)
 {
 	if(hw_config->slots[0]!=-1)
-		sb_dsp_unload(hw_config);
+		sb_dsp_unload(hw_config, sbmpu);
 }
 
 int sb_be_quiet=0;
@@ -82,8 +84,6 @@
 MODULE_PARM(pas2, "i");
 MODULE_PARM(sm_games, "i");
 
-static int sbmpu = 0;
-
 void *smw_free = NULL;
 
 int init_module(void)
@@ -113,8 +113,6 @@
 		config_mpu.io_base = mpu_io;
 		if (mpu_io && probe_sbmpu(&config_mpu))
 			sbmpu = 1;
-#endif
-#ifdef CONFIG_MIDI
 		if (sbmpu)
 			attach_sbmpu(&config_mpu);
 #endif
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.1.121/linux/drivers/sound/sb_common.c	Wed Aug 26 11:37:39 1998
+++ linux/drivers/sound/sb_common.c	Thu Sep 10 16:37:26 1998
@@ -932,7 +932,10 @@
 {
 }
 
-void sb_dsp_unload(struct address_info *hw_config)
+/* if (sbmpu) below we allow mpu401 to manage the midi devs
+   otherwise we have to unload them. (Andrzej Krzysztofowicz) */
+   
+void sb_dsp_unload(struct address_info *hw_config, int sbmpu)
 {
 	sb_devc *devc;
 
@@ -951,10 +954,13 @@
 		if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) && devc->irq > 0)
 		{
 			free_irq(devc->irq, devc);
-			sound_unload_mixerdev(devc->my_mixerdev);
+			if (devc->my_mixerdev)
+				sound_unload_mixerdev(devc->my_mixerdev);
 			/* We don't have to do this bit any more the UART401 is its own
 				master  -- Krzysztof Halasa */
-			/* sound_unload_mididev(devc->my_mididev); */
+			/* But we have to do it, if UART401 is not detected */
+			if (!sbmpu && devc->my_mididev)
+				sound_unload_mididev(devc->my_mididev);
 			sound_unload_audiodev(devc->my_dev);
 		}
 		kfree(devc);
@@ -1301,10 +1307,8 @@
 			}
 			hw_config->name = "Sound Blaster 16";
 			hw_config->irq = -devc->irq;
-#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
 			if (devc->minor > 12)		/* What is Vibra's version??? */
 				sb16_set_mpu_port(devc, hw_config);
-#endif
 			break;
 
 		case MDL_ESS:
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/sb_midi.c linux/drivers/sound/sb_midi.c
--- v2.1.121/linux/drivers/sound/sb_midi.c	Thu Jul 16 18:09:27 1998
+++ linux/drivers/sound/sb_midi.c	Thu Sep 10 16:37:26 1998
@@ -182,8 +182,6 @@
 		printk(KERN_ERR "sb_midi: too many MIDI devices detected\n");
 		return;
 	}
-	std_midi_synth.midi_dev = dev;
-	devc->my_mididev = dev;
 	std_midi_synth.midi_dev = devc->my_mididev = dev;
 	midi_devs[dev] = (struct midi_operations *)kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
 	if (midi_devs[dev] == NULL)
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/sb_mixer.c linux/drivers/sound/sb_mixer.c
--- v2.1.121/linux/drivers/sound/sb_mixer.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/sb_mixer.c	Thu Sep 10 16:37:26 1998
@@ -337,7 +337,10 @@
 				  
 			case SOUND_MIXER_STEREODEVS:
 				ret = devc->supported_devices;
-				if (devc->model != MDL_JAZZ && devc->model != MDL_SMW)
+				/* The ESS seems to have stereo mic controls */
+				if (devc->model == MDL_ESS)
+					ret &= ~(SOUND_MASK_SPEAKER|SOUND_MASK_IMIX);
+				else if (devc->model != MDL_JAZZ && devc->model != MDL_SMW)
 					ret &= ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
 				break;
 				  
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c
--- v2.1.121/linux/drivers/sound/sequencer.c	Thu Aug 20 17:05:16 1998
+++ linux/drivers/sound/sequencer.c	Thu Sep 10 16:37:26 1998
@@ -1540,12 +1540,12 @@
 			return 0;
 
 		case SNDCTL_MIDI_INFO:
-			if (get_user(dev, (int *)(&(((struct synth_info *)arg)->device))))
+			if (get_user(dev, (int *)(&(((struct midi_info *)arg)->device))))
 				return -EFAULT;
 			if (dev < 0 || dev >= max_mididev)
 				return -ENXIO;
 			midi_devs[dev]->info.device = dev;
-			return copy_to_user(arg, &midi_devs[dev]->info, sizeof(struct synth_info))?-EFAULT:0;
+			return copy_to_user(arg, &midi_devs[dev]->info, sizeof(struct midi_info))?-EFAULT:0;
 
 		case SNDCTL_SEQ_THRESHOLD:
 			if (get_user(val, (int *)arg))
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/trix.c linux/drivers/sound/trix.c
--- v2.1.121/linux/drivers/sound/trix.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/trix.c	Thu Sep 10 16:37:26 1998
@@ -39,6 +39,8 @@
 
 static int *trix_osp = NULL;
 
+static int mpu = 0;
+
 static unsigned char trix_read(int addr)
 {
 	outb(((unsigned char) addr), 0x390);	/* MT-0002-PC ASIC address */
@@ -445,7 +447,7 @@
 void unload_trix_sb(struct address_info *hw_config)
 {
 #ifdef CONFIG_SBDSP
-	sb_dsp_unload(hw_config);
+	sb_dsp_unload(hw_config, mpu);
 #endif
 }
 
@@ -479,7 +481,6 @@
 struct address_info sb_config;
 struct address_info mpu_config;
 
-static int      mpu = 0;
 static int      sb = 0;
 
 static int      fw_load;
diff -u --recursive --new-file v2.1.121/linux/drivers/sound/wavfront.c linux/drivers/sound/wavfront.c
--- v2.1.121/linux/drivers/sound/wavfront.c	Thu Aug 20 17:05:16 1998
+++ linux/drivers/sound/wavfront.c	Thu Sep 10 16:37:26 1998
@@ -49,7 +49,7 @@
  * aspects of configuring a WaveFront soundcard, particularly the
  * effects processor.
  *
- * $Id: wavfront.c,v 0.4 1998/07/22 02:12:11 pbd Exp $
+ * $Id: wavfront.c,v 0.5 1998/07/22 16:16:41 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
@@ -667,16 +667,16 @@
 /***********************************************************************
 WaveFront: data munging   
 
-Things here are weird.  All data written to the board cannot 
-have its most significant bit set.  Any data item with values 
+Things here are wierd. All data written to the board cannot 
+have its most significant bit set. Any data item with values 
 potentially > 0x7F (127) must be split across multiple bytes.
 
 Sometimes, we need to munge numeric values that are represented on
-the x86 side as 8- to 32-bit values.  Sometimes, we need to munge data
-that is represented on the x86 side as an array of bytes.  The most
+the x86 side as 8-32 bit values. Sometimes, we need to munge data
+that is represented on the x86 side as an array of bytes. The most
 efficient approach to handling both cases seems to be to use 2
-different functions for munging and 2 for de-munging.  This avoids
-weird casting and worrying about bit-level offsets.
+different functions for munging and 2 for de-munging. This avoids
+wierd casting and worrying about bit-level offsets.
 
 **********************************************************************/
 
@@ -1198,14 +1198,14 @@
 	shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleEndOffset),
 			     shptr, 4);
 	
-	/* This one is truly weird.  What kind of weirdo decided that in
-	   a system dominated by 16- and 32-bit integers, they would use
-	   just 12 bits ?
+	/* This one is truly wierd. What kind of wierdo decided that in
+	   a system dominated by 16 and 32 bit integers, they would use
+	   a just 12 bits ?
 	*/
 	
 	shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3);
 	
-	/* Why is this nybblified, when the MSB is *always* zero? 
+	/* Why is this nybblified, when the MSB is *always* zero ? 
 	   Anyway, we can't take address of bitfield, so make a
 	   good-faith guess at where it starts.
 	*/
@@ -2278,6 +2278,12 @@
 	int bits;
 	int hwv[2];
 
+	/* Check IRQ is legal */
+
+	if ((bits = wavefront_interrupt_bits (hw->irq)) < 0) {
+		return 1;
+	}
+
 	if (request_irq (hw->irq, wavefrontintr,
 			 0, "WaveFront", (void *) hw) < 0) {
 		printk (KERN_WARNING "WaveFront: IRQ %d not available!\n",
@@ -2325,10 +2331,6 @@
 	   plus external 9-pin MIDI interface selected
 	*/
 
-	if ((bits = wavefront_interrupt_bits (hw->irq)) < 0) {
-		return 1;
-	}
-
 	outb (0x80 | 0x40 | bits, hw->data_port);	
   
 	/* CONTROL REGISTER
@@ -2350,7 +2352,6 @@
 	   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
@@ -2797,51 +2798,50 @@
 	if (hw->israw || wf_raw) {
 		if (wavefront_download_firmware (hw, ospath)) {
 			goto gone_bad;
-			return 1;
 		}
-	}
 
-	if (fx_raw) {
-		wffx_init (hw);
-	}
-
-	/* If we loaded the OS, we now have to wait for it to be ready
-	   to roll. We can't guarantee that interrupts are enabled,
-	   because we might be reloading the module without forcing a
-	   reset/reload of the firmware.
-	   
-	   Rather than busy-wait, lets just turn interrupts on.
-	*/
-
-	outb (0x80|0x40|0x10|0x1, hw->control_port);
+		/* 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.
+		*/
 
-	wavefront_should_cause_interrupt (hw, WFC_NOOP,
+		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");
-		goto gone_bad;
-	}
-
-	/* Now, do it again ! */
+		
+		if (!hw->irq_ok) {
+			printk (KERN_WARNING
+				"WaveFront: no post-OS interrupt.\n");
+			goto gone_bad;
+		}
+		
+		/* Now, do it again ! */
+		
+		wavefront_should_cause_interrupt (hw, WFC_NOOP,
+						  hw->data_port, (10*HZ));
+		
+		if (!hw->irq_ok) {
+			printk (KERN_WARNING
+				"WaveFront: no post-OS interrupt(2).\n");
+			goto gone_bad;
+		}
 
-	wavefront_should_cause_interrupt (hw, WFC_NOOP,
-					  hw->data_port, (10*HZ));
-	
-	if (!hw->irq_ok) {
-		printk (KERN_WARNING "WaveFront: 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.
+		*/
+		
+		outb (0x80, hw->control_port); 
+		
+		/* No need for the IRQ anymore */
+		
+		free_irq (hw->irq, hw);
 	}
-	
-	/* OK, no (RX/TX) interrupts any more, but leave mute
-	   on. Master interrupts get enabled when we're done here.
-	*/
-
-	outb (0x80, hw->control_port); 
-
-	/* No need for the IRQ anymore */
 
-	free_irq (hw->irq, hw);
+	if (/*XXX has_fx_device() && */ fx_raw) {
+		wffx_init (hw);
+	}
 
 	/* SETUPSND.EXE asks for sample memory config here, but since i
 	   have no idea how to interpret the result, we'll forget
@@ -3109,14 +3109,14 @@
 
 /* YSS225 initialization.
 
-   This code was developed using DOSEmu.  The Turtle Beach SETUPSND
-   utility was run with I/O tracing in DOSEmu enabled, and a reconstruction
+   This code was developed using DOSEMU. The Turtle Beach SETUPSND
+   utility was run with I/O tracing in DOSEMU enabled, and a reconstruction
    of the port I/O done, using the Yamaha faxback document as a guide
-   to add more logic to the code.  It's really pretty weird.
+   to add more logic to the code. Its really pretty wierd.
 
    There was an alternative approach of just dumping the whole I/O
    sequence as a series of port/value pairs and a simple loop
-   that output it.  However, I hope that eventually I'll get more
+   that output it. However, I hope that eventually I'll get more
    control over what this code does, and so I tried to stick with
    a somewhat "algorithmic" approach.
 */
@@ -3592,7 +3592,7 @@
 
 {
 	printk ("Turtle Beach WaveFront Driver\n"
-		"Copyright (C) by Hannu Savolainen, "
+		"Copyright (C) by Hannu Solvainen, "
 		"Paul Barton-Davis 1993-1998.\n");
 
 	if (io == -1 || irq == -1) {
diff -u --recursive --new-file v2.1.121/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v2.1.121/linux/fs/binfmt_elf.c	Wed Aug 26 11:37:40 1998
+++ linux/fs/binfmt_elf.c	Thu Sep 10 16:33:15 1998
@@ -379,6 +379,8 @@
 	retval = read_exec(interpreter_dentry, offset, addr, text_data, 0);
 	if (retval < 0)
 		goto out;
+	flush_icache_range((unsigned long)addr,
+	                   (unsigned long)addr + text_data);
 
 	do_mmap(NULL, ELF_PAGESTART(text_data + ELF_EXEC_PAGESIZE - 1),
 		interp_ex->a_bss,
diff -u --recursive --new-file v2.1.121/linux/fs/devpts/root.c linux/fs/devpts/root.c
--- v2.1.121/linux/fs/devpts/root.c	Wed Aug 26 11:37:40 1998
+++ linux/fs/devpts/root.c	Wed Sep 16 09:06:19 1998
@@ -134,7 +134,8 @@
 static int devpts_root_lookup(struct inode * dir, struct dentry * dentry)
 {
 	struct devpts_sb_info *sbi = SBI(dir->i_sb);
-	int entry, i;
+	unsigned int entry;
+	int i;
 	const char *p;
 
 	if (!S_ISDIR(dir->i_mode))
@@ -160,11 +161,14 @@
 			entry += (*p++ - '0');
 		}
 	}
-	
+
+	if ( entry >= sbi->max_ptys )
+		return 0;
+
 	dentry->d_inode = sbi->inodes[entry];
 	if ( dentry->d_inode )
 		dentry->d_inode->i_count++;
-
+	
 	d_add(dentry, dentry->d_inode);
 
 	return 0;
diff -u --recursive --new-file v2.1.121/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v2.1.121/linux/fs/fat/inode.c	Mon Apr  6 17:41:01 1998
+++ linux/fs/fat/inode.c	Thu Sep 10 14:32:26 1998
@@ -221,13 +221,13 @@
 			if (value) ret = 0;
 			else opts->sys_immutable = 1;
 		}
-		else if (!strcmp(this_char,"codepage")) {
+		else if (!strcmp(this_char,"codepage") && value) {
 			opts->codepage = simple_strtoul(value,&value,0);
 			if (*value) ret = 0;
 			else printk ("MSDOS FS: Using codepage %d\n",
 					opts->codepage);
 		}
-		else if (!strcmp(this_char,"iocharset")) {
+		else if (!strcmp(this_char,"iocharset") && value) {
 			p = value;
 			while (*value && *value != ',') value++;
 			len = value - p;
diff -u --recursive --new-file v2.1.121/linux/fs/hfs/ChangeLog linux/fs/hfs/ChangeLog
--- v2.1.121/linux/fs/hfs/ChangeLog	Wed Jun 24 22:54:09 1998
+++ linux/fs/hfs/ChangeLog	Sat Sep 12 10:34:39 1998
@@ -1,3 +1,17 @@
+1998-09-11  a sun  <asun@purgatorius.zoology.washington.edu>
+
+	* mdb.c: altered mdb struct to reflect hfs plus usage.
+
+1998-08-27  a sun  <asun@purgatorius.zoology.washington.edu>
+
+	* file.c, file_hdr.c, file_cap.c: dealt with the remaining
+	copy_to/from_user() error cases.
+
+1998-08-26  a sun  <asun@purgatorius.zoology.washington.edu>
+
+	* super.c (hfs_read_super): fixed to deal with cdroms. why doesn't
+	the cdrom layer call the partition table code?
+
 Wed Jan 21 14:04:26 1998  a sun  <asun@zoology.washington.edu>
 
 	* inode.c, sysdep.c
diff -u --recursive --new-file v2.1.121/linux/fs/hfs/file.c linux/fs/hfs/file.c
--- v2.1.121/linux/fs/hfs/file.c	Wed Aug 26 11:37:41 1998
+++ linux/fs/hfs/file.c	Sat Sep 12 10:34:39 1998
@@ -267,15 +267,19 @@
  *
  * Like copy_from_user() while translating NL->CR;
  */
-static inline void xlate_from_user(char *data, const char *buf, int count)
+static inline int xlate_from_user(char *data, const char *buf, int count)
 {
-	count -= copy_from_user(data, buf, count);
+	int i;
+
+	i = copy_from_user(data, buf, count);
+	count -= i;
 	while (count--) {
 		if (*data == '\n') {
 			*data = '\r';
 		}
 		++data;
 	}
+	return i;
 }
 
 /*================ Global functions ================*/
@@ -404,6 +408,13 @@
 				xlate_to_user(buf, p, chars);
 			} else {
 				chars -= copy_to_user(buf, p, chars);
+				if (!chars) {
+					brelse(*bhe);
+					count = 0;
+					if (!read)
+						read = -EFAULT;
+					break;
+				}
 			}
 			brelse(*bhe);
 			count -= chars;
@@ -477,10 +488,13 @@
 			}
 		}
 		p = (pos % HFS_SECTOR_SIZE) + bh->b_data;
-		if (convert) {
-			xlate_from_user(p, buf, c);
-		} else {
-			c -= copy_from_user(p, buf, c);
+		c -= convert ? xlate_from_user(p, buf, c) :
+			copy_from_user(p, buf, c);
+		if (!c) {
+			brelse(bh);
+			if (!written)
+				written = -EFAULT;
+			break;
 		}
 		update_vm_cache(inode,pos,p,c);
 		pos += c;
diff -u --recursive --new-file v2.1.121/linux/fs/hfs/file_cap.c linux/fs/hfs/file_cap.c
--- v2.1.121/linux/fs/hfs/file_cap.c	Wed Aug 26 11:37:41 1998
+++ linux/fs/hfs/file_cap.c	Sat Sep 12 10:34:39 1998
@@ -236,7 +236,7 @@
 		end = pos + mem_count;
 
 		cap_build_meta(&meta, entry);
-		copy_from_user(((char *)&meta) + pos, buf, mem_count);
+		mem_count -= copy_from_user(((char *)&meta) + pos, buf, mem_count);
 
 		/* Update finder attributes if changed */
 		if (OVERLAPS(pos, end, struct hfs_cap_info, fi_fndr)) {
diff -u --recursive --new-file v2.1.121/linux/fs/hfs/file_hdr.c linux/fs/hfs/file_hdr.c
--- v2.1.121/linux/fs/hfs/file_hdr.c	Wed Aug 26 11:37:41 1998
+++ linux/fs/hfs/file_hdr.c	Sat Sep 12 10:34:39 1998
@@ -610,7 +610,7 @@
 			left = count;
 		}
 
-		copy_from_user(((char *)&meta) + pos, buf, left);
+		left -= copy_from_user(((char *)&meta) + pos, buf, left);
 		layout->magic   = hfs_get_nl(meta.magic);
 		layout->version = hfs_get_nl(meta.version);
 		layout->entries = hfs_get_hs(meta.entries);
@@ -642,7 +642,7 @@
 			left = count;
 		}
 
-		copy_from_user(((char *)&meta) + pos, buf, left);
+		left -= copy_from_user(((char *)&meta) + pos, buf, left);
 		init_layout(layout, meta.descrs);
 
 		count -= left;
@@ -782,7 +782,7 @@
 
 		/* transfer the data from user space */
 		if (p) {
-			copy_from_user(p + offset, buf, left);
+			left -= copy_from_user(p + offset, buf, left);
 		} else if (fork) {
 			left = hfs_do_write(inode, fork, offset, buf, left);
 		}
diff -u --recursive --new-file v2.1.121/linux/fs/hfs/mdb.c linux/fs/hfs/mdb.c
--- v2.1.121/linux/fs/hfs/mdb.c	Sun Jan  4 10:40:17 1998
+++ linux/fs/hfs/mdb.c	Sat Sep 12 10:34:39 1998
@@ -29,6 +29,8 @@
  * the HFS equivalent of a superblock.
  *
  * Reference: _Inside Macintosh: Files_ pages 2-59 through 2-62
+ *
+ * modified for HFS Extended
  */
 struct raw_mdb {
 	hfs_word_t	drSigWord;	/* Signature word indicating fs type */
@@ -60,9 +62,11 @@
 	hfs_lword_t	drFilCnt;	/* number of files in the fs */
 	hfs_lword_t	drDirCnt;	/* number of directories in the fs */
 	hfs_byte_t	drFndrInfo[32];	/* data used by the Finder */
-	hfs_word_t	drVCSize;	/* MacOS caching parameter */
-	hfs_word_t	drVCBMSize;	/* MacOS caching parameter */
-	hfs_word_t	drCtlCSize;	/* MacOS caching parameter */
+	hfs_word_t	drEmbedSigWord;	/* embedded volume signature */
+	hfs_lword_t     drEmbedExtent;  /* starting block number (xdrStABN) 
+					   and number of allocation blocks 
+					   (xdrNumABlks) occupied by embedded
+					   volume */
 	hfs_lword_t	drXTFlSize;	/* bytes in the extents B-tree */
 	hfs_byte_t	drXTExtRec[12];	/* extents B-tree's first 3 extents */
 	hfs_lword_t	drCTFlSize;	/* bytes in the catalog B-tree */
diff -u --recursive --new-file v2.1.121/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.1.121/linux/fs/isofs/inode.c	Wed Sep  9 14:51:09 1998
+++ linux/fs/isofs/inode.c	Thu Sep 10 08:40:30 1998
@@ -320,7 +320,7 @@
 			*value++ = 0;
 
 #ifdef CONFIG_JOLIET
-		if (!strcmp(this_char,"iocharset")) {
+		if (!strcmp(this_char,"iocharset") && value) {
 			popt->iocharset = value;
 			while (*value && *value != ',')
 				value++;
diff -u --recursive --new-file v2.1.121/linux/fs/isofs/namei.c linux/fs/isofs/namei.c
--- v2.1.121/linux/fs/isofs/namei.c	Sat Sep  5 16:46:41 1998
+++ linux/fs/isofs/namei.c	Sat Sep 12 10:40:39 1998
@@ -201,8 +201,8 @@
 		 * Skip hidden or associated files unless unhide is set 
 		 */
 		match = 0;
-		if(   !(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5)
-		   || dir->i_sb->u.isofs_sb.s_unhide == 'y' )
+		if ((!(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5)
+		    || dir->i_sb->u.isofs_sb.s_unhide == 'y') && dlen)
 		{
 			match = (isofs_cmp(dentry,dpnt,dlen) == 0);
 		}
diff -u --recursive --new-file v2.1.121/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
--- v2.1.121/linux/fs/msdos/namei.c	Fri Jan 23 18:10:32 1998
+++ linux/fs/msdos/namei.c	Sat Sep 12 10:32:35 1998
@@ -149,10 +149,10 @@
 static int msdos_find(struct inode *dir,const char *name,int len,
     struct buffer_head **bh,struct msdos_dir_entry **de,int *ino)
 {
-	char msdos_name[MSDOS_NAME];
 	int res;
 	char dotsOK;
 	char scantype;
+	char msdos_name[MSDOS_NAME];
 
 	dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK;
 	res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
@@ -225,7 +225,9 @@
 	NULL, 		/* d_revalidate */
 	msdos_hash,
 	msdos_cmp,
-	NULL		/* d_delete */
+	NULL,		/* d_delete */
+	NULL,
+	NULL
 };
 
 struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
@@ -253,49 +255,45 @@
 int msdos_lookup(struct inode *dir,struct dentry *dentry)
 {
 	struct super_block *sb = dir->i_sb;
-	int ino,res;
+	struct inode *inode = NULL;
 	struct msdos_dir_entry *de;
 	struct buffer_head *bh;
-	struct inode *inode;
+	int ino,res;
 	
 	PRINTK (("msdos_lookup\n"));
 
 	dentry->d_op = &msdos_dentry_operations;
 
-	if(!dir) { /* N.B. This test is bogus -- should never happen */
-		d_add(dentry, NULL);
-		return 0;
-	}
-
-	if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,&bh,&de,&ino)) < 0) {
-		if(res == -ENOENT) {
-			d_add(dentry, NULL);
-			res = 0;
-		}
-		return res;
-	}
-	PRINTK (("msdos_lookup 4\n"));
+	res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &bh,
+			&de, &ino);
+	if (res == -ENOENT)
+		goto add;
+	if (res < 0)
+		goto out;
 	if (bh)
 		fat_brelse(sb, bh);
-	PRINTK (("msdos_lookup 4.5\n"));
-	if (!(inode = iget(dir->i_sb,ino)))
-		return -EACCES;
-	PRINTK (("msdos_lookup 5\n"));
+
+	/* try to get the inode */
+	res = -EACCES;
+	inode = iget(dir->i_sb, ino);
+	if (!inode)
+		goto out;
 	if (!inode->i_sb ||
-	    (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
-		/* crossed a mount point into a non-msdos fs */
-		d_add(dentry, inode);
-		return 0;
+	   (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
+		printk(KERN_WARNING "msdos_lookup: foreign inode??\n");
 	}
-	if (MSDOS_I(inode)->i_busy) { /* mkdir in progress */
+	/* mkdir in progress? */
+	if (MSDOS_I(inode)->i_busy) {
+		printk(KERN_WARNING "msdos_lookup: %s/%s busy\n",
+			dentry->d_parent->d_name.name, dentry->d_name.name);
 		iput(inode);
-		d_add(dentry, NULL); /* N.B. Do we really want a negative? */
-		return 0;
+		goto out;
 	}
-	PRINTK (("msdos_lookup 6\n"));
+	res = 0;
+add:
 	d_add(dentry, inode);
-	PRINTK (("msdos_lookup 7\n"));
-	return 0;
+out:
+	return res;
 }
 
 
@@ -308,9 +306,6 @@
 	struct msdos_dir_entry *de;
 	int res,ino;
 
-	if(!dir)
-		return -ENOENT;
-
 	*result = NULL;
 	if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
 		if (res != -ENOENT) return res;
@@ -350,14 +345,14 @@
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
 	struct inode *inode;
-	char msdos_name[MSDOS_NAME];
 	int ino,res,is_hid;
+	char msdos_name[MSDOS_NAME];
 
-	if (!dir) return -ENOENT;
-	if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
-				     dentry->d_name.name,dentry->d_name.len,
-				     msdos_name,0,
-				     MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0)
+	res = msdos_format_name(MSDOS_SB(sb)->options.name_check,
+				dentry->d_name.name,dentry->d_name.len,
+				msdos_name,0,
+				MSDOS_SB(sb)->options.dotsOK);
+	if (res < 0)
 		return res;
 	is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
 	fat_lock_creation();
@@ -434,8 +429,8 @@
 /***** Remove a directory */
 int msdos_rmdir(struct inode *dir, struct dentry *dentry)
 {
-	struct inode *inode = dentry->d_inode;
 	struct super_block *sb = dir->i_sb;
+	struct inode *inode = dentry->d_inode;
 	int res,ino;
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
@@ -493,13 +488,14 @@
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
 	struct inode *inode,*dot;
-	char msdos_name[MSDOS_NAME];
 	int ino,res,is_hid;
+	char msdos_name[MSDOS_NAME];
 
-	if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
-				     dentry->d_name.name,dentry->d_name.len,
-				     msdos_name,0,
-				     MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0)
+	res = msdos_format_name(MSDOS_SB(sb)->options.name_check,
+				dentry->d_name.name,dentry->d_name.len,
+				msdos_name,0,
+				MSDOS_SB(sb)->options.dotsOK);
+	if (res < 0)
 		return res;
 	is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
 	fat_lock_creation();
@@ -537,6 +533,7 @@
 	iput(dot);
 	d_instantiate(dentry, inode);
 	return 0;
+
 mkdir_error:
 	if (msdos_rmdir(dir,dentry) < 0)
 		fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
@@ -546,10 +543,7 @@
 }
 
 /***** Unlink a file */
-static int msdos_unlinkx(
-	struct inode *dir,
-	struct dentry *dentry,
-	int nospc)	/* Flag special file ? */
+static int msdos_unlinkx( struct inode *dir, struct dentry *dentry, int nospc)
 {
 	struct super_block *sb = dir->i_sb;
 	struct inode *inode = dentry->d_inode;
@@ -558,22 +552,25 @@
 	struct msdos_dir_entry *de;
 
 	bh = NULL;
-	if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,
-			      &bh,&de,&ino)) < 0)
+	res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
+			&bh, &de, &ino);
+	if (res < 0)
 		goto unlink_done;
 	res = -EPERM;
 	if (!S_ISREG(inode->i_mode) && nospc)
 		goto unlink_done;
 	if (IS_IMMUTABLE(inode))
 		goto unlink_done;
+	/* N.B. check for busy files? */
+
 	inode->i_nlink = 0;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	MSDOS_I(inode)->i_busy = 1;
 	mark_inode_dirty(inode);
 	mark_inode_dirty(dir);
+	d_delete(dentry);	/* This also frees the inode */
 	de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, bh, 1);
-	d_delete(dentry);	/* This also frees the inode */
 	res = 0;
 unlink_done:
 	fat_brelse(sb, bh);
@@ -592,28 +589,40 @@
 	return msdos_unlinkx (dir,dentry,0);
 }
 
+#define MSDOS_CHECK_BUSY 1
+
 /***** Rename within a directory */
-static int rename_same_dir(struct inode *old_dir,char *old_name,
+static int msdos_rename_same(struct inode *old_dir,char *old_name,
     struct dentry *old_dentry,
     struct inode *new_dir,char *new_name,struct dentry *new_dentry,
     struct buffer_head *old_bh,
-    struct msdos_dir_entry *old_de,int old_ino,int is_hid)
+    struct msdos_dir_entry *old_de, int old_ino, int is_hid)
 {
 	struct super_block *sb = old_dir->i_sb;
 	struct buffer_head *new_bh;
 	struct msdos_dir_entry *new_de;
 	struct inode *new_inode,*old_inode;
-	int new_ino,exists,error;
+	int new_ino, exists, error;
+
+	if (!strncmp(old_name, new_name, MSDOS_NAME))
+		goto set_hid;
+	error = -ENOENT;
+	if (*(unsigned char *) old_de->name == DELETED_FLAG)
+		goto out;
 
-	if (!strncmp(old_name,new_name,MSDOS_NAME)) goto set_hid;
 	exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
-	if (*(unsigned char *) old_de->name == DELETED_FLAG) {
-		if (exists)
-			fat_brelse(sb, new_bh);
-		return -ENOENT;
-	}
 	if (exists) {
+		error = -EIO;
 		new_inode = new_dentry->d_inode;
+		/* Make sure it really exists ... */
+		if (!new_inode) {
+			printk(KERN_ERR
+				"msdos_rename_same: %s/%s inode NULL, ino=%d\n",
+				new_dentry->d_parent->d_name.name,
+				new_dentry->d_name.name, new_ino);
+			d_drop(new_dentry);
+			goto out_error;
+		}
 		error = S_ISDIR(new_inode->i_mode)
 			? (old_de->attr & ATTR_DIR)
 				? msdos_empty(new_inode)
@@ -621,11 +630,26 @@
 			: (old_de->attr & ATTR_DIR)
 				? -EPERM
 				: 0;
-		if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
-		if (error) {
-			fat_brelse(sb, new_bh);
-			return error;
+		if (error)
+			goto out_error;
+		error = -EPERM;
+		if ((old_de->attr & ATTR_SYS))
+			goto out_error;
+
+#ifdef MSDOS_CHECK_BUSY
+		/* check for a busy dentry */
+		error = -EBUSY;
+		if (new_dentry->d_count > 1) {
+			shrink_dcache_parent(new_dentry);
+			if (new_dentry->d_count > 1) {
+printk("msdos_rename_same: %s/%s busy, count=%d\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+new_dentry->d_count);
+				goto out_error;
+			}
 		}
+#endif
+
 		if (S_ISDIR(new_inode->i_mode)) {
 			new_dir->i_nlink--;
 			mark_inode_dirty(new_dir);
@@ -633,11 +657,16 @@
 		new_inode->i_nlink = 0;
 		MSDOS_I(new_inode)->i_busy = 1;
 		mark_inode_dirty(new_inode);
+#ifdef MSDOS_CHECK_BUSY
+		/* d_delete the dentry, as we killed its inode */
+		d_delete(new_dentry);
+#endif
+
 		new_de->name[0] = DELETED_FLAG;
 		fat_mark_buffer_dirty(sb, new_bh, 1);
 		fat_brelse(sb, new_bh);
 	}
-	memcpy(old_de->name,new_name,MSDOS_NAME);
+	memcpy(old_de->name, new_name, MSDOS_NAME);
 	/* Update the dcache */
 	d_move(old_dentry, new_dentry);
 set_hid:
@@ -650,51 +679,63 @@
 	MSDOS_I(old_inode)->i_attrs = is_hid
 		? (MSDOS_I(old_inode)->i_attrs |  ATTR_HIDDEN)
 		: (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
-	return 0;
+	error = 0;
+out:
+	return error;
+
+out_error:
+	fat_brelse(sb, new_bh);
+	goto out;
 }
 
 /***** Rename across directories - a nonphysical move */
-static int rename_diff_dir(struct inode *old_dir,char *old_name,
+static int msdos_rename_diff(struct inode *old_dir, char *old_name,
     struct dentry *old_dentry,
-    struct inode *new_dir,char *new_name,struct dentry *new_dentry,
+    struct inode *new_dir,char *new_name, struct dentry *new_dentry,
     struct buffer_head *old_bh,
-    struct msdos_dir_entry *old_de,int old_ino,int is_hid)
+    struct msdos_dir_entry *old_de, int old_ino, int is_hid)
 {
 	struct super_block *sb = old_dir->i_sb;
 	struct buffer_head *new_bh,*free_bh,*dotdot_bh;
 	struct msdos_dir_entry *new_de,*free_de,*dotdot_de;
 	struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode;
-	struct dentry *walk;
 	int new_ino,free_ino,dotdot_ino;
-	int error,exists;
+	int error, exists;
 
-	if (old_dir->i_dev != new_dir->i_dev) return -EINVAL;
-	if (old_ino == new_dir->i_ino) return -EINVAL;
-	walk = new_dentry;
+	error = -EINVAL;
+	if (old_ino == new_dir->i_ino)
+		goto out;
 	/* prevent moving directory below itself */
-	for (;;) {
-		if (walk == old_dentry) return -EINVAL;
-		if (walk == walk->d_parent) break;
-		walk = walk->d_parent;
-	}
+	if (is_subdir(new_dentry, old_dentry))
+		goto out;
+
+	error = -ENOENT;
+	if (*(unsigned char *) old_de->name == DELETED_FLAG)
+		goto out;
+
 	/* find free spot */
-	while ((error = fat_scan(new_dir,NULL,&free_bh,&free_de,&free_ino,
-	    SCAN_ANY)) < 0) {
-		if (error != -ENOENT) return error;
+	while ((error = fat_scan(new_dir, NULL, &free_bh, &free_de, &free_ino,
+					SCAN_ANY)) < 0) {
+		if (error != -ENOENT)
+			goto out;
 		error = fat_add_cluster(new_dir);
-		if (error) return error;
+		if (error)
+			goto out;
 	}
+
 	exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
-	old_inode = old_dentry->d_inode;
-	if (*(unsigned char *) old_de->name == DELETED_FLAG) {
-		fat_brelse(sb, free_bh);
-		if (exists)
-			fat_brelse(sb, new_bh);
-		return -ENOENT;
-	}
-	new_inode = NULL; /* to make GCC happy */
 	if (exists) {  /* Trash the old file! */
+		error = -EIO;
 		new_inode = new_dentry->d_inode;
+		/* Make sure it really exists ... */
+		if (!new_inode) {
+			printk(KERN_ERR
+				"msdos_rename_diff: %s/%s inode NULL, ino=%d\n",
+				new_dentry->d_parent->d_name.name,
+				new_dentry->d_name.name, new_ino);
+			d_drop(new_dentry);
+			goto out_new;
+		}
 		error = S_ISDIR(new_inode->i_mode)
 			? (old_de->attr & ATTR_DIR)
 				? msdos_empty(new_inode)
@@ -702,39 +743,87 @@
 			: (old_de->attr & ATTR_DIR)
 				? -EPERM
 				: 0;
-		if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
-		if (error) {
-			fat_brelse(sb, new_bh);
-			return error;
+		if (error)
+			goto out_new;
+ 		error = -EPERM;
+		if ((old_de->attr & ATTR_SYS))
+			goto out_new;
+
+#ifdef MSDOS_CHECK_BUSY
+		/* check for a busy dentry */
+		error = -EBUSY;
+		if (new_dentry->d_count > 1) {
+			shrink_dcache_parent(new_dentry);
+			if (new_dentry->d_count > 1) {
+printk("msdos_rename_diff: target %s/%s busy, count=%d\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+new_dentry->d_count);
+				goto out_new;
+			}
+		}
+#endif
+		if (S_ISDIR(new_inode->i_mode)) {
+			new_dir->i_nlink--;
+			mark_inode_dirty(new_dir);
 		}
 		new_inode->i_nlink = 0;
 		MSDOS_I(new_inode)->i_busy = 1;
 		mark_inode_dirty(new_inode);
+#ifdef MSDOS_CHECK_BUSY
+		/* d_delete the dentry, as we killed its inode */
+		d_delete(new_dentry);
+#endif
 		new_de->name[0] = DELETED_FLAG;
 		fat_mark_buffer_dirty(sb, new_bh, 1);
+		fat_brelse(sb, new_bh);
+	}
+
+	old_inode = old_dentry->d_inode;
+	/* Get the dotdot inode if we'll need it ... */
+	dotdot_bh = NULL;
+	dotdot_inode = NULL;
+	if (S_ISDIR(old_inode->i_mode)) {
+		error = fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh,
+				&dotdot_de, &dotdot_ino, SCAN_ANY);
+		if (error < 0)
+			goto rename_done;
+		error = -EIO;
+		dotdot_inode = iget(sb, dotdot_ino);
+		if (!dotdot_inode)
+			goto out_dotdot;
 	}
-	memcpy(free_de,old_de,sizeof(struct msdos_dir_entry));
-	memcpy(free_de->name,new_name,MSDOS_NAME);
+
+	/* get an inode for the new name */
+	memcpy(free_de, old_de, sizeof(struct msdos_dir_entry));
+	memcpy(free_de->name, new_name, MSDOS_NAME);
 	free_de->attr = is_hid
 		? (free_de->attr|ATTR_HIDDEN)
 		: (free_de->attr&~ATTR_HIDDEN);
-	if (!(free_inode = iget(new_dir->i_sb,free_ino))) {
-		free_de->name[0] = DELETED_FLAG;
-		/*
-		 * Don't mark free_bh as dirty. Both states 
-		 * are supposed to be equivalent.
-		 */
-		fat_brelse(sb, free_bh);
-		if (exists)
-			fat_brelse(sb, new_bh);
-		return -EIO;
-	}
-	if (exists && S_ISDIR(new_inode->i_mode)) {
-		new_dir->i_nlink--;
-		mark_inode_dirty(new_dir);
-	}
+
+	error = -EIO;
+	free_inode = iget(sb, free_ino);
+	if (!free_inode)
+		goto out_iput;
 	msdos_read_inode(free_inode);
 
+	/*
+	 * Make sure the old dentry isn't busy,
+	 * as we need to change inodes ...
+	 */
+	if (old_dentry->d_count > 1) {
+		shrink_dcache_parent(old_dentry);
+		if (old_dentry->d_count > 1) {
+printk("msdos_rename_diff: source %s/%s busy, count=%d\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+old_dentry->d_count);
+			goto out_iput;
+		}
+	}
+
+	/* keep the inode for a bit ... */
+	old_inode->i_count++;
+	d_delete(old_dentry);
+
 	free_inode->i_mode   = old_inode->i_mode;
 	free_inode->i_size   = old_inode->i_size;
 	free_inode->i_blocks = old_inode->i_blocks;
@@ -747,29 +836,20 @@
 	MSDOS_I(free_inode)->i_logstart = MSDOS_I(old_inode)->i_logstart;
 	MSDOS_I(free_inode)->i_attrs = MSDOS_I(old_inode)->i_attrs;
 
-	/* Detach d_alias from old inode and attach to new inode */
-	list_del(&old_dentry->d_alias);
+	/*
+	 * Install the new inode ...
+	 */
 	d_instantiate(old_dentry, free_inode);
-	iput(old_inode);
 
 	fat_cache_inval_inode(old_inode);
 	mark_inode_dirty(old_inode);
 	old_de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, old_bh, 1);
 	fat_mark_buffer_dirty(sb, free_bh, 1);
+	iput(old_inode);
 
-	if (exists) {
-		/* free_inode is put after putting new_inode and old_inode */
-		fat_brelse(sb, new_bh);
-	}
-	if (S_ISDIR(old_inode->i_mode)) {
-		if ((error = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
-		    &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done;
-		if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) {
-			fat_brelse(sb, dotdot_bh);
-			error = -EIO;
-			goto rename_done;
-		}
+	/* a directory? */
+	if (dotdot_bh) {
 		MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start;
 		MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart;
 		dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart);
@@ -787,9 +867,26 @@
 	/* Update the dcache */
 	d_move(old_dentry, new_dentry);
 	error = 0;
+
 rename_done:
 	fat_brelse(sb, free_bh);
+out:
 	return error;
+
+out_iput:
+	free_de->name[0] = DELETED_FLAG;
+	/*
+	 * Don't mark free_bh as dirty. Both states 
+	 * are supposed to be equivalent.
+	 */
+	iput(free_inode); /* may be NULL */
+	iput(dotdot_inode);
+out_dotdot:
+	fat_brelse(sb, dotdot_bh);
+	goto rename_done;
+out_new:
+	fat_brelse(sb, new_bh);
+	goto rename_done;
 }
 
 /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
@@ -797,36 +894,45 @@
 		 struct inode *new_dir,struct dentry *new_dentry)
 {
 	struct super_block *sb = old_dir->i_sb;
-	char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME];
 	struct buffer_head *old_bh;
 	struct msdos_dir_entry *old_de;
-	int old_ino,error;
+	int old_ino, error;
 	int is_hid,old_hid; /* if new file and old file are hidden */
+	char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
 
-	if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->options.name_check,
-				       old_dentry->d_name.name,
-				       old_dentry->d_name.len,old_msdos_name,1,
-				       MSDOS_SB(old_dir->i_sb)->options.dotsOK))
-	    < 0) goto rename_done;
-	if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->options.name_check,
-				       new_dentry->d_name.name,
-				       new_dentry->d_name.len,new_msdos_name,0,
-				       MSDOS_SB(new_dir->i_sb)->options.dotsOK))
-	    < 0) goto rename_done;
-	is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.');
+	error = -EINVAL;
+	if (sb != new_dir->i_sb)
+		goto rename_done;
+	error = msdos_format_name(MSDOS_SB(sb)->options.name_check,
+				old_dentry->d_name.name, old_dentry->d_name.len,
+				old_msdos_name, 1,MSDOS_SB(sb)->options.dotsOK);
+	if (error < 0)
+		goto rename_done;
+	error = msdos_format_name(MSDOS_SB(sb)->options.name_check,
+				new_dentry->d_name.name, new_dentry->d_name.len,
+				new_msdos_name, 0,MSDOS_SB(sb)->options.dotsOK);
+	if (error < 0)
+		goto rename_done;
+
+	is_hid  = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.');
 	old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.');
-	if ((error = fat_scan(old_dir,old_msdos_name,&old_bh,&old_de,
-	    &old_ino,old_hid?SCAN_HID:SCAN_NOTHID)) < 0) goto rename_done;
+	error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de,
+			&old_ino, old_hid?SCAN_HID:SCAN_NOTHID);
+	if (error < 0)
+		goto rename_done;
+
 	fat_lock_creation();
 	if (old_dir == new_dir)
-		error = rename_same_dir(old_dir,old_msdos_name,old_dentry,
-		    new_dir,new_msdos_name,new_dentry,
-		    old_bh,old_de,old_ino,is_hid);
-	else error = rename_diff_dir(old_dir,old_msdos_name,old_dentry,
-		     new_dir,new_msdos_name,new_dentry,
-		     old_bh,old_de,old_ino,is_hid);
+		error = msdos_rename_same(old_dir, old_msdos_name, old_dentry,
+					new_dir, new_msdos_name, new_dentry,
+		  			old_bh, old_de, (ino_t)old_ino, is_hid);
+	else
+		error = msdos_rename_diff(old_dir, old_msdos_name, old_dentry,
+					new_dir, new_msdos_name, new_dentry,
+					old_bh, old_de, (ino_t)old_ino, is_hid);
 	fat_unlock_creation();
 	fat_brelse(sb, old_bh);
+
 rename_done:
 	return error;
 }
diff -u --recursive --new-file v2.1.121/linux/fs/namei.c linux/fs/namei.c
--- v2.1.121/linux/fs/namei.c	Thu Aug  6 14:06:33 1998
+++ linux/fs/namei.c	Wed Sep  9 22:18:04 1998
@@ -84,6 +84,8 @@
 
 /* [16-Dec-97 Kevin Buhr] For security reasons, we change some symlink
  * semantics.  See the comments in "open_namei" and "do_link" below.
+ *
+ * [10-Sep-98 Alan Modra] Another symlink change.
  */
 
 static inline char * get_page(void)
@@ -992,20 +994,20 @@
 	struct inode *inode;
 	int error;
 
-	old_dentry = lookup_dentry(oldname, NULL, 1);
-	error = PTR_ERR(old_dentry);
-	if (IS_ERR(old_dentry))
-		goto exit;
-
 	/*
 	 * Hardlinks are often used in delicate situations.  We avoid
 	 * security-related surprises by not following symlinks on the
-	 * newname.  We *do* follow them on the oldname.  This is
-	 * the same as Digital Unix 4.0, for example.
+	 * newname.  --KAB
 	 *
-	 * Solaris 2.5.1 is similar, but for a laugh try linking from
-	 * a dangling symlink.                              --KAB
+	 * We don't follow them on the oldname either to be compatible
+	 * with linux 2.0, and to avoid hard-linking to directories
+	 * and other special files.  --ADM
 	 */
+	old_dentry = lookup_dentry(oldname, NULL, 0);
+	error = PTR_ERR(old_dentry);
+	if (IS_ERR(old_dentry))
+		goto exit;
+
 	new_dentry = lookup_dentry(newname, NULL, 0);
 	error = PTR_ERR(new_dentry);
 	if (IS_ERR(new_dentry))
diff -u --recursive --new-file v2.1.121/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.1.121/linux/fs/proc/array.c	Sat Sep  5 16:46:41 1998
+++ linux/fs/proc/array.c	Sun Sep 13 10:20:56 1998
@@ -844,7 +844,7 @@
 
 	return sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \
 %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu \
-%lu %lu %lu %lu %lu %lu %lu %lu\n",
+%lu %lu %lu %lu %lu %lu %lu %lu %d\n",
 		pid,
 		tsk->comm,
 		state,
@@ -885,7 +885,8 @@
 		sigcatch    .sig[0] & 0x7fffffffUL,
 		wchan,
 		tsk->nswap,
-		tsk->cnswap);
+		tsk->cnswap,
+		tsk->exit_signal);
 }
 		
 static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size,
@@ -1224,6 +1225,11 @@
   	        case PROC_PCI:
 			return get_pci_list(page);
 #endif
+
+#ifdef CONFIG_NUBUS
+		case PROC_NUBUS:
+			return get_nubus_list(page);
+#endif			
 			
 		case PROC_CPUINFO:
 			return get_cpuinfo(page);
diff -u --recursive --new-file v2.1.121/linux/fs/super.c linux/fs/super.c
--- v2.1.121/linux/fs/super.c	Wed Aug 26 11:37:43 1998
+++ linux/fs/super.c	Wed Sep  9 17:30:16 1998
@@ -1226,9 +1226,7 @@
 
 #ifdef CONFIG_BLK_DEV_INITRD
 
-extern int initmem_freed;
-
-static int __init do_change_root(kdev_t new_root_dev,const char *put_old)
+int __init change_root(kdev_t new_root_dev,const char *put_old)
 {
 	kdev_t old_root_dev;
 	struct vfsmount *vfsmnt;
@@ -1248,7 +1246,7 @@
 	dput(old_pwd);
 #if 1
 	shrink_dcache();
-	printk("do_change_root: old root has d_count=%d\n", old_root->d_count);
+	printk("change_root: old root has d_count=%d\n", old_root->d_count);
 #endif
 	/*
 	 * Get the new mount directory
@@ -1291,15 +1289,6 @@
 	}
 	printk(KERN_CRIT "Trouble: add_vfsmnt failed\n");
 	return -ENOMEM;
-}
-
-int change_root(kdev_t new_root_dev,const char *put_old)
-{
-	if (initmem_freed) {
-		printk (KERN_CRIT "Initmem has been already freed. Staying in initrd\n");
-		return -EBUSY;
-	}
-	return do_change_root(new_root_dev, put_old);
 }
 
 #endif
diff -u --recursive --new-file v2.1.121/linux/fs/sysv/balloc.c linux/fs/sysv/balloc.c
--- v2.1.121/linux/fs/sysv/balloc.c	Sun Nov 26 09:23:10 1995
+++ linux/fs/sysv/balloc.c	Sun Sep 13 10:27:07 1998
@@ -52,8 +52,8 @@
 	 * into this block being freed:
 	 */
 	if (*sb->sv_sb_flc_count == sb->sv_flc_size) {
-		unsigned short * flc_count;
-		unsigned long * flc_blocks;
+		u16 * flc_count;
+		u32 * flc_blocks;
 
 		bh = sv_getblk(sb, sb->s_dev, block);
 		if (!bh) {
@@ -154,8 +154,8 @@
 		return 0;
 	}
 	if (*sb->sv_sb_flc_count == 0) { /* the last block continues the free list */
-		unsigned short * flc_count;
-		unsigned long * flc_blocks;
+		u16 * flc_count;
+		u32 * flc_blocks;
 
 		if (!(bh = sv_bread(sb, sb->s_dev, block))) {
 			printk("sysv_new_block: cannot read free-list block\n");
@@ -247,8 +247,8 @@
 		}
 		/* block = sb->sv_sb_flc_blocks[0], the last block continues the free list */
 		while (1) {
-			unsigned short * flc_count;
-			unsigned long * flc_blocks;
+			u16 * flc_count;
+			u32 * flc_blocks;
 
 			if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
 				printk("sysv_count_free_blocks: new block %d is not in data zone\n",block);
diff -u --recursive --new-file v2.1.121/linux/fs/sysv/fsync.c linux/fs/sysv/fsync.c
--- v2.1.121/linux/fs/sysv/fsync.c	Sat Sep 13 11:07:29 1997
+++ linux/fs/sysv/fsync.c	Sun Sep 13 12:12:34 1998
@@ -26,10 +26,10 @@
 /* Sync one block. The block number is
  * from_coh_ulong(*blockp) if convert=1, *blockp if convert=0.
  */
-static int sync_block (struct inode * inode, unsigned long * blockp, int convert, int wait)
+static int sync_block (struct inode * inode, u32 *blockp, int convert, int wait)
 {
 	struct buffer_head * bh;
-	unsigned long tmp, block;
+	u32 tmp, block;
 	struct super_block * sb;
 
 	block = tmp = *blockp;
@@ -59,11 +59,11 @@
 }
 
 /* Sync one block full of indirect pointers and read it because we'll need it. */
-static int sync_iblock (struct inode * inode, unsigned long * iblockp, int convert,
+static int sync_iblock (struct inode * inode, u32 * iblockp, int convert,
 			struct buffer_head * *bh, int wait)
 {
 	int rc;
-	unsigned long tmp, block;
+	u32 tmp, block;
 
 	*bh = NULL;
 	block = tmp = *iblockp;
@@ -101,7 +101,7 @@
 	return err;
 }
 
-static int sync_indirect(struct inode *inode, unsigned long *iblockp, int convert, int wait)
+static int sync_indirect(struct inode *inode, u32 *iblockp, int convert, int wait)
 {
 	int i;
 	struct buffer_head * ind_bh;
@@ -115,7 +115,7 @@
 	sb = inode->i_sb;
 	for (i = 0; i < sb->sv_ind_per_block; i++) {
 		rc = sync_block (inode,
-				 ((unsigned long *) ind_bh->b_data) + i, sb->sv_convert,
+				 ((u32 *) ind_bh->b_data) + i, sb->sv_convert,
 				 wait);
 		if (rc > 0)
 			break;
@@ -126,7 +126,7 @@
 	return err;
 }
 
-static int sync_dindirect(struct inode *inode, unsigned long *diblockp, int convert,
+static int sync_dindirect(struct inode *inode, u32 *diblockp, int convert,
 			  int wait)
 {
 	int i;
@@ -141,7 +141,7 @@
 	sb = inode->i_sb;
 	for (i = 0; i < sb->sv_ind_per_block; i++) {
 		rc = sync_indirect (inode,
-				    ((unsigned long *) dind_bh->b_data) + i, sb->sv_convert,
+				    ((u32 *) dind_bh->b_data) + i, sb->sv_convert,
 				    wait);
 		if (rc > 0)
 			break;
@@ -152,7 +152,7 @@
 	return err;
 }
 
-static int sync_tindirect(struct inode *inode, unsigned long *tiblockp, int convert,
+static int sync_tindirect(struct inode *inode, u32 *tiblockp, int convert,
 			  int wait)
 {
 	int i;
@@ -167,7 +167,7 @@
 	sb = inode->i_sb;
 	for (i = 0; i < sb->sv_ind_per_block; i++) {
 		rc = sync_dindirect (inode,
-				     ((unsigned long *) tind_bh->b_data) + i, sb->sv_convert,
+				     ((u32 *) tind_bh->b_data) + i, sb->sv_convert,
 				     wait);
 		if (rc > 0)
 			break;
diff -u --recursive --new-file v2.1.121/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
--- v2.1.121/linux/fs/sysv/inode.c	Wed Apr  8 19:36:28 1998
+++ linux/fs/sysv/inode.c	Sun Sep 13 12:12:34 1998
@@ -30,7 +30,7 @@
 #include <linux/string.h>
 #include <linux/locks.h>
 #include <linux/init.h>
-
+#include <asm/byteorder.h>
 #include <asm/uaccess.h>
 
 #if 0
@@ -532,7 +532,7 @@
 
 void sysv_put_super(struct super_block *sb)
 {
-	/* we can assume sysv_write_super() has already been called, and
+	/* we can assume sysv_write_super() has already been called,
 	   and that the superblock is locked */
 	brelse(sb->sv_bh1);
 	if (sb->sv_bh1 != sb->sv_bh2) brelse(sb->sv_bh2);
@@ -648,8 +648,8 @@
 static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create)
 {
 	struct super_block *sb;
-	unsigned long tmp;
-	unsigned long *p;
+	u32 tmp;
+	u32 *p;
 	struct buffer_head * result;
 
 	sb = inode->i_sb;
@@ -684,7 +684,7 @@
 	struct buffer_head * bh, int nr, int create)
 {
 	struct super_block *sb;
-	unsigned long tmp, block;
+	u32 tmp, block;
 	sysv_zone_t *p;
 	struct buffer_head * result;
 
@@ -782,26 +782,43 @@
 	return NULL;
 }
 
+#ifdef __BIG_ENDIAN
+
+static inline unsigned long read3byte (unsigned char * p)
+{
+	return (p[2] | (p[1]<<8) | (p[0]<<16));
+}
+
+static inline void write3byte (unsigned char *p , unsigned long val)
+{
+	p[2]=val&0xFF;
+	p[1]=(val>>8)&0xFF;
+	p[0]=(val>>16)&0xFF;
+}
+
+#else
 
-static inline unsigned long read3byte (char * p)
+static inline unsigned long read3byte (unsigned char * p)
 {
 	return (unsigned long)(*(unsigned short *)p)
 	     | (unsigned long)(*(unsigned char *)(p+2)) << 16;
 }
 
-static inline void write3byte (char * p, unsigned long val)
+static inline void write3byte (unsigned char * p, unsigned long val)
 {
 	*(unsigned short *)p = (unsigned short) val;
 	*(unsigned char *)(p+2) = val >> 16;
 }
 
-static inline unsigned long coh_read3byte (char * p)
+#endif
+
+static inline unsigned long coh_read3byte (unsigned char * p)
 {
 	return (unsigned long)(*(unsigned char *)p) << 16
 	     | (unsigned long)(*(unsigned short *)(p+1));
 }
 
-static inline void coh_write3byte (char * p, unsigned long val)
+static inline void coh_write3byte (unsigned char * p, unsigned long val)
 {
 	*(unsigned char *)p = val >> 16;
 	*(unsigned short *)(p+1) = (unsigned short) val;
diff -u --recursive --new-file v2.1.121/linux/fs/sysv/truncate.c linux/fs/sysv/truncate.c
--- v2.1.121/linux/fs/sysv/truncate.c	Wed Jul  1 19:38:56 1998
+++ linux/fs/sysv/truncate.c	Sun Sep 13 10:27:07 1998
@@ -41,8 +41,8 @@
 {
 	struct super_block * sb;
 	unsigned int i;
-	unsigned long * p;
-	unsigned long block;
+	u32 * p;
+	u32 block;
 	struct buffer_head * bh;
 	int retry = 0;
 
@@ -71,7 +71,7 @@
 	return retry;
 }
 
-static int trunc_indirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
+static int trunc_indirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt)
 {
 	unsigned long indtmp, indblock;
 	struct super_block * sb;
@@ -140,14 +140,14 @@
 	return retry;
 }
 
-static int trunc_dindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
+static int trunc_dindirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt)
 {
-	unsigned long indtmp, indblock;
+	u32 indtmp, indblock;
 	struct super_block * sb;
 	struct buffer_head * indbh;
 	unsigned int i;
 	sysv_zone_t * ind;
-	unsigned long tmp, block;
+	u32 tmp, block;
 	int retry = 0;
 
 	indblock = indtmp = *p;
@@ -197,14 +197,14 @@
 	return retry;
 }
 
-static int trunc_tindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
+static int trunc_tindirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt)
 {
-	unsigned long indtmp, indblock;
+	u32 indtmp, indblock;
 	struct super_block * sb;
 	struct buffer_head * indbh;
 	unsigned int i;
 	sysv_zone_t * ind;
-	unsigned long tmp, block;
+	u32 tmp, block;
 	int retry = 0;
 
 	indblock = indtmp = *p;
diff -u --recursive --new-file v2.1.121/linux/fs/umsdos/README-WIP.txt linux/fs/umsdos/README-WIP.txt
--- v2.1.121/linux/fs/umsdos/README-WIP.txt	Sat Sep  5 16:46:41 1998
+++ linux/fs/umsdos/README-WIP.txt	Wed Sep 16 15:12:31 1998
@@ -1,21 +1,31 @@
 Changes by Matija Nalis (mnalis@jagor.srce.hr) on umsdos dentry fixing
 (started by Peter T. Waltenberg <peterw@karaka.chch.cri.nz>)
+Final conversion to dentries Bill Hawes <whawes@star.net>
 
 ---------- WARNING --------- WARNING --------- WARNING -----------
-THIS IS TRULY EXPERIMENTAL.  IT IS NOT BETA YET.  PLEASE EXCUSE MY
-YELLING, BUT ANY USE OF THIS MODULE MAY VERY WELL DESTROY YOUR
-UMSDOS FILESYSTEM, AND MAYBE EVEN OTHER FILESYSTEMS IN USE.
-YOU'VE BEEN WARNED.
+There is no warning any more.
+Both read-only and read-write stuff is fixed, both in
+msdos-compatibile mode, and in umsdos EMD mode, and it seems stable.
+There are still few symlink/hardlink nuisances, but those are not fatal.
+
+I'd call it wide beta, and ask for as many people as possible to
+come and test it! See notes below for some more information, or if
+you are trying to use UMSDOS as root partition.
 ---------- WARNING --------- WARNING --------- WARNING -----------
 
-Current status (980901) - UMSDOS dentry-WIP-Beta 0.82-7:
+Legend: those lines marked with '+' on the beggining of line indicates it
+passed all of my tests, and performed perfect in all of them.
+
+Current status (980915) - UMSDOS dentry-Beta 0.83:
 
 (1) pure MSDOS (no --linux-.--- EMD file):
 
+READ:
 + readdir			- works
 + lookup			- works
 + read file			- works
 
+WRITE:
 + creat file			- works
 + delete file			- works
 + write file			- works
@@ -24,145 +34,93 @@
 + rename dir (same dir)		- works
 + rename dir (dif. dir)		- works
 + mkdir				- works
-- rmdir 			- QUESTIONABLE. probable problem on non-empty dirs.
-
-Notes: possible very minor problems with dentry/inode/... kernel structures (very rare)
++ rmdir 			- works
 
 
 (2) umsdos (with --linux-.--- EMD file):
 
+READ:
 + readdir			- works
 + lookup 			- works
 + permissions/owners stuff	- works
 + long file names		- works
 + read file			- works
-- switching MSDOS/UMSDOS	- works?
-- switching UMSDOS/MSDOS	- works?
-- pseudo root things		- COMPLETELY UNTESTED (commented out currently!)
++ switching MSDOS/UMSDOS	- works
++ switching UMSDOS/MSDOS	- works
+- pseudo root things		- DOES NOT WORK. COMMENTED OUT. See Notes below.
 + resolve symlink		- works
 + dereference symlink		- works
-- hard links			- broken again...
-+ special files (block/char devices, FIFOs, sockets...)	- seems to work.
-- other ioctls			- some UNTESTED
 + dangling symlink		- works
+- hard links			- DOES NOT WORK CORRECTLY ALWAYS.
++ special files (block/char devices, FIFOs, sockets...)	- works
+- various umsdos ioctls		- works
 
-- create symlink		- seems to work both on short & long names now !
-- create hardlink		- WARNING: NOT FIXED YET!
-- create file			- seems to work both on short & long names now !
-- create special file		- seems to work both on short & long names now !
-- write to file			- seems to work both on short & long names now !
-- rename file (same dir)	- seems to work, but with i_count PROBLEMS
-- rename file (dif. dir)	- seems to work, but with i_count PROBLEMS
-- rename dir (same dir)		- seems to work, but with i_count PROBLEMS
-- rename dir (dif. dir)		- seems to work, but with i_count PROBLEMS
-- delete file			- seems to work fully now!
-- notify_change (chown,perms)	- seems to work!
-- delete hardlink		- WARNING: NOT FIXED YET!
-- mkdir				- seems to work both on short & long names now !
-- rmdir 			- may work, but readdir blocks linux afterwards. to be FIXED!
-- umssyncing			- seems to work, but NEEDS MORE TESTING
 
-+ CVF-FAT stuff (compressed DOS filesystem) - there is some support from Frank
+WRITE:
+- create symlink		- sometimes works, but see WARNING below
+- create hardlink		- works
++ create file			- works
++ create special file		- works
++ write to file			- works
++ rename file (same dir)	- works
++ rename file (dif. dir)	- works
+- rename hardlink (same dir)	-
+- rename hardlink (dif. dir)	-
+- rename symlink (same dir)	- 
+- rename symlink (dif. dir)	- problems sometimes. see warning below.
++ rename dir (same dir)		- works
++ rename dir (dif. dir)		- works
++ delete file			- works
++ notify_change (chown,perms)	- works
++ delete hardlink		- works
++ mkdir				- works
+- rmdir 			- HMMM. see with clean --linux-.--- files...
+- umssyncing (many ioctls)	- works
+
+
+- CVF-FAT stuff (compressed DOS filesystem) - there is some support from Frank
   Gockel <gockel@sent13.uni-duisburg.de> to use it even under umsdosfs, but I
   have no way of testing it -- please let me know if there are problems specific
   to umsdos (for instance, it works under msdosfs, but not under umsdosfs).
 
 
+Some current notes:
 
-Notes:  there is moderate trashing of dentry/inode kernel structures.  Probably
-some other kernel structures are compromised.  You should have SysRq support
-compiled in, and use Sync/Emergency-remount-RO.  If you don't try mounting
-read/write, you should have no big problems. When most things begin to work,
-I'll get to finding/fixing those inode/dentry ?_count leakages.
-
-Note 4:  rmdir(2) fails with EBUSY - sdir->i_count > 1 (like 7 ??).  It may be
-some error with dir->i_count++, or something related to iput().  See if
-number changes if we access the directory in different ways.
-
-Note 5:  there is a problem with unmounting umsdosfs.  It seems to stay
-registered or something.  Remounting the same device on any mount point with a
-different fstype (such as msdos or vfat) ignores the new fstype and umsdosfs
-kicks back in. Should be fixed in 0.82-6 (at least, with nothing in between)!
-Much of inode/dentry corruption is fixed in 0.82-7, especially when mounting
-read-only.
-
-Note 6:  also we screwed umount(2)-ing the fs at times (EBUSY), by missing
-some of those iput/dput's. When most of the rest of the code is fixed, we'll
-put them back at the correct places (hopefully). Also much better in 0.82-7.
+Note: creating and using pseudo-hardlinks is always non-perfect, especially
+in filesystems that might be externally modified like umsdos. There is
+example is specs file about it.
+
+Warning: moving symlinks around may break them until umount/remount.
+
+Warning: I seem to able to reproduce one problem with creting symlink after
+I rm -rf directory: it is manifested as symlink apperantly being regular
+file instead of symlink until next umount/mount pair. Tracking this one
+down...
+
+Wanted: I am currently looking for volunteers that already have UMSDOS
+filesystems in pseudo-root, or are able to get them, to test PSEUDO-ROOT
+stuff (and get new patches from me), which I currently can't do. Anyone know
+of URL of nice UMSDOS pseudo-root ready image ? As always, any patches or
+pointer to things done in wrong way (or ideas of better ways) are greatly
+appreciated !
+
+Note: If you are currently trying to use UMSDOS as root partition (with
+linux installed in c:\linux) it will not work. Pseudo-root is currently
+commented out. See 'wanted' above and contact me if you are interested in
+testing it.
 
 ------------------------------------------------------------------------------
 
 Some general notes:
 
-There is a great amount of kernel log messages.  Use SysRq log-level 5 to turn
-most of them off, or 4 to turn all but really fatal ones off.  Probably good
-idea to kill klogd/syslogd so it will only go to console. You can also
-comment out include/linux/umsdos_fs.h definition of UMS_DEBUG to get rid of
-most debugging messages. Please don't turn it off without good reason.
-
-It should work enough to test it, even enough to give you a few chances to
-umount/rmmod module, recompile it, and reinsert it again (but probably
-screaming about still used inodes on device and stuff).  This is first on
-my list of things to get fixed, as it would greatly improve speed of
-compile/test/reboot/set_environment/recompile cycle by removing
-'reboot/set_environment' component that now occurs every few cycles.
-
-I need some help from someone who knows more than I about the use of dentries
-and inodes.  If you can help, please contact me.  I'm mostly worried about
-iget/iput and dget/dput, and deallocating temporary dentries we create.
-Should we destroy temp dentries? using d_invalidate? using d_drop? just
-dput them?
-
-I'm unfortunately somewhat out of time to read linux-kernel, but I do check
-for messages having "UMSDOS" in the subject, and read them.  I might miss
-some in all that volume, though.  I should reply to any direct e-mail in few
-days.  If I don't, probably I never got your message.  You can try
-mnalis@voyager.hr; however mnalis@jagor.srce.hr is preferable.
-
-
-------------------------------------------------------------------------------
-some of my notes for myself /mn/:
+Good idea when running development kernels is to have SysRq support compiled
+in kernel, and use Sync/Emergency-remount-RO if you bump into problems (like
+not being able to umount(2) umsdosfs, and because of it root partition also,
+or panics which force you to reboot etc.)
+
+I'm unfortunately somewhat out of time to read linux-kernel@vger, but I do
+check for messages having "UMSDOS" in the subject, and read them.  I might
+miss some in all that volume, though.  I should reply to any direct e-mail
+in few days.  If I don't, probably I never got your message.  You can try
+mnalis-umsdos@voyager.hr; however mnalis@jagor.srce.hr is preferable.
 
-+ hardlinks/symlinks. test with files in not_the_same_dir
-- also test not_the_same_dir for other file operations like rename etc.
-- iput: device 00:00 inode 318 still has aliases! problem. Check in iput()
-  for device 0,0. Probably null pointer passed around when it shouldn't be ?
-- dput/iput problem...
-- What about .dotfiles? Are they working? How about multiple dots?
-- fix stuff like dir->i_count++ to atomic_inc(&dir->i_count) and similar?
-
-- should check what happen when multiple UMSDOSFS are mounted
-
-- chase down all "FIXME", "DELME", "CNT", check_dentry, check_inode, kill_dentry
-  and fix it properly.
-
-- umsdos_create_any - calling msdos_create will create dentry for short name. Hmmmm..?
-
-- what is dir->i_count++ ? locking directory ? should this be lock_parent or
-something ?
-
-+ i_binary=2 is for CVF (compressed filesystem).
-
-- SECURITY WARNING: short dentries should be invalidated, or they could be
-  accessed instead of proper long names.
-
-- I've put many check_dentry_path(), check_inode() calls to trace down
-  problems. those should be removed in final version.
-
-- iput()s with a "FIXME?" comment are uncommented and probably OK. Those with
-  "FIXME??" should be tested but probably work. Commented iput()s with
-  any "FIXME" comments should probably be uncommented and tested. At some
-  places we may need dput() instead of iput(), but that should be checked.
-
-+ as for iput(): (my only pointer so far. anyone else?)
-
->development I only know about iput. All functions that get an inode as
->argument and don't return it have to call iput on it before exit, i.e. when
->it is no longer needed and the code returns to vfs layer. The rest is quite
->new to me, but it might be similar for dput. Typical side effect of a
->missing iput was a memory runout (but no crash). You also couldn't unmount
->the filesystem later though no process was using it. On the other hand, one
->iput too much lead to serious pointer corruption and crashed the system
->very soon. I used to look at the FAT filesystem and copy those pieces of
->
-> Frank
diff -u --recursive --new-file v2.1.121/linux/fs/umsdos/dir.c linux/fs/umsdos/dir.c
--- v2.1.121/linux/fs/umsdos/dir.c	Wed Sep  9 14:51:09 1998
+++ linux/fs/umsdos/dir.c	Mon Sep 14 17:22:25 1998
@@ -22,7 +22,36 @@
 #define UMSDOS_SPECIAL_DIRFPOS	3
 extern struct inode *pseudo_root;
 
+/* #define UMSDOS_DEBUG_VERBOSE 1 */
 
+/*
+ * Dentry operations routines
+ */
+
+/* nothing for now ... */
+static int umsdos_dentry_validate(struct dentry *dentry)
+{
+	return 1;
+}
+
+/* for now, drop everything to force lookups ... */
+static void umsdos_dentry_dput(struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+	if (inode) {
+		d_drop(dentry);
+	}
+}
+
+static struct dentry_operations umsdos_dentry_operations =
+{
+	umsdos_dentry_validate,	/* d_validate(struct dentry *) */
+	NULL,			/* d_hash */
+	NULL,			/* d_compare */
+	umsdos_dentry_dput,	/* d_delete(struct dentry *) */
+	NULL,
+	NULL,
+};
 
 /*
  * This needs to have the parent dentry passed to it.
@@ -109,9 +138,9 @@
  * Return a negative value from linux/errno.h.
  * Return > 0 if success (the number of bytes written by filldir).
  * 
- * This function is used by the normal readdir VFS entry point and by
- * some function who try to find out info on a file from a pure MSDOS
- * inode. See umsdos_locate_ancestor() below.
+ * This function is used by the normal readdir VFS entry point,
+ * and in order to get the directory entry from a file's dentry.
+ * See umsdos_dentry_to_entry() below.
  */
  
 static int umsdos_readdir_x (struct inode *dir, struct file *filp,
@@ -175,8 +204,10 @@
 	if (IS_ERR(demd))
 		goto out_end;
 	ret = 0;
-	if (!demd->d_inode)
+	if (!demd->d_inode) {
+printk("no EMD file??\n");
 		goto out_dput;
+	}
 
 	/* set up our private filp ... */
 	fill_new_filp(&new_filp, demd);
@@ -189,35 +220,63 @@
 	ret = 0;
 	while (new_filp.f_pos < demd->d_inode->i_size) {
 		off_t cur_f_pos = new_filp.f_pos;
-		struct umsdos_info info;
 		struct dentry *dret;
+		struct inode *inode;
 		struct umsdos_dirent entry;
+		struct umsdos_info info;
 
 		ret = -EIO;
 		if (umsdos_emd_dir_readentry (&new_filp, &entry) != 0)
 			break;
-
 		if (entry.name_len == 0)
-			goto remove_name;
+			continue;
+#ifdef UMSDOS_DEBUG_VERBOSE
+if (entry.flags & UMSDOS_HLINK)
+printk("umsdos_readdir_x: %s/%s is hardlink\n",
+filp->f_dentry->d_name.name, entry.name);
+#endif
 
 		umsdos_parse (entry.name, entry.name_len, &info);
 		info.f_pos = cur_f_pos;
 		umsdos_manglename (&info);
+		/*
+		 * Do a real lookup on the short name.
+		 */
 		dret = umsdos_lookup_dentry(filp->f_dentry, info.fake.fname,
-						 info.fake.len);
+						 info.fake.len, 1);
 		ret = PTR_ERR(dret);
 		if (IS_ERR(dret))
 			break;
-
-Printk (("Looking for inode of %s/%s, flags=%x\n",
-dret->d_parent->d_name.name, info.fake.fname, entry.flags));
-		if ((entry.flags & UMSDOS_HLINK) && follow_hlink) {
+		/*
+		 * If the file wasn't found, remove it from the EMD.
+		 */
+		inode = dret->d_inode;
+		if (!inode)
+			goto remove_name;
+#ifdef UMSDOS_DEBUG_VERBOSE
+if (inode->u.umsdos_i.i_is_hlink)
+printk("umsdos_readdir_x: %s/%s already resolved, ino=%ld\n",
+dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino);
+#endif
+
+Printk (("Found %s/%s, ino=%ld, flags=%x\n",
+dret->d_parent->d_name.name, info.fake.fname, dret->d_inode->i_ino,
+entry.flags));
+		/* check whether to resolve a hard-link */
+		if ((entry.flags & UMSDOS_HLINK) && follow_hlink &&
+		    !inode->u.umsdos_i.i_is_hlink) {
 			dret = umsdos_solve_hlink (dret);
 			ret = PTR_ERR(dret);
 			if (IS_ERR(dret))
 				break;
+			inode = dret->d_inode;
+			if (!inode) {
+printk("umsdos_readdir_x: %s/%s negative after link\n",
+dret->d_parent->d_name.name, dret->d_name.name);
+				goto clean_up;
+			}
 		}
-					
+
 		/* #Specification:  pseudo root / reading real root
 		 * The pseudo root (/linux) is logically
 		 * erased from the real root.  This means that
@@ -225,20 +284,21 @@
 		 * infinite recursion (/DOS/linux/DOS/linux/...) while
 		 * walking the file system.
 		 */
-		if (dret->d_inode != pseudo_root &&
+		if (inode != pseudo_root &&
 		    (internal_read || !(entry.flags & UMSDOS_HIDDEN))) {
-			Printk ((KERN_DEBUG "filldir now\n"));
 			if (filldir (dirbuf, entry.name, entry.name_len,
-				 cur_f_pos, dret->d_inode->i_ino) < 0) {
+				 cur_f_pos, inode->i_ino) < 0) {
 				new_filp.f_pos = cur_f_pos;
 			}
-Printk (("Found %s/%s(%ld)\n",
-dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino));
+Printk(("umsdos_readdir_x: got %s/%s, ino=%ld\n",
+dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino));
 			if (u_entry != NULL)
 				*u_entry = entry;
 			dput(dret);
+			ret = 0;
 			break;
 		}
+	clean_up:
 		dput(dret);
 		continue;
 
@@ -248,11 +308,17 @@
 		 * in the MS-DOS directory any more, the entry is
 		 * removed from the EMD file silently.
 		 */
-		Printk (("'Silently' removing EMD for file\n"));
-		ret = umsdos_delentry(filp->f_dentry, &info, 1);
+#ifdef UMSDOS_PARANOIA
+printk("umsdos_readdir_x: %s/%s out of sync, erasing\n",
+filp->f_dentry->d_name.name, info.entry.name);
+#endif
+		ret = umsdos_delentry(filp->f_dentry, &info, 
+					S_ISDIR(info.entry.mode));
 		if (ret)
-			break;
-		continue;
+			printk(KERN_WARNING 
+				"umsdos_readdir_x: delentry %s, err=%d\n",
+				info.entry.name, ret);
+		goto clean_up;
 	}
 	/*
 	 * If the fillbuf has failed, f_pos is back to 0.
@@ -296,8 +362,6 @@
 		struct umsdos_dirent entry;
 
 		bufk.count = 0;
-		PRINTK (("UMSDOS_readdir: calling _x (%p,%p,%p,%d,%p,%d,%p)\n",
-			dir, filp, &bufk, 0, &entry, 1, umsdos_dir_once));
 		ret = umsdos_readdir_x (dir, filp, &bufk, 0, &entry, 1, 
 					umsdos_dir_once);
 		if (bufk.count == 0)
@@ -492,7 +556,6 @@
 		fat_readdir (&filp, &bufsrch, umsdos_dir_search);
 		if (bufsrch.found) {
 			ret = 0;
-			inode->u.umsdos_i.i_dir_owner = parent->d_inode->i_ino;
 			inode->u.umsdos_i.i_emd_owner = 0;
 if (!S_ISDIR(inode->i_mode))
 printk("UMSDOS: %s/%s not a directory!\n",
@@ -509,8 +572,8 @@
 		err = umsdos_readdir_x (parent->d_inode, &filp, &bufk, 1, 
 				entry, 0, umsdos_filldir_k);
 		if (err < 0) { 
-			printk ("UMSDOS: can't locate inode %ld in EMD??\n",
-				inode->i_ino);
+			printk ("umsdos_dentry_to_entry: ino=%ld, err=%d\n",
+				inode->i_ino, err);
 			break;
 		}
 		if (bufk.ino == inode->i_ino) {
@@ -523,73 +586,6 @@
 	return ret;
 }
 
-/*
- * Deprecated. Try to get rid of this soon!
- */
-int umsdos_inode2entry (struct inode *dir, struct inode *inode,
-			       struct umsdos_dirent *entry)
-{
-	int ret = -ENOENT;
-	struct inode *emddir;
-	struct dentry *i2e;
-	struct file filp;
-	struct UMSDOS_DIR_SEARCH bufsrch;
-	struct UMSDOS_DIRENT_K bufk;
-
-	if (pseudo_root && inode == pseudo_root) {
-		/*
-		 * Quick way to find the name.
-		 * Also umsdos_readdir_x won't show /linux anyway
-		 */
-		memcpy (entry->name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN + 1);
-		entry->name_len = UMSDOS_PSDROOT_LEN;
-		ret = 0;
-		goto out;
-	}
-
-	emddir = umsdos_emd_dir_lookup (dir, 0);
-	if (emddir == NULL) {
-		/* This is a DOS directory. */
-		i2e = creat_dentry ("@i2e.nul@", 9, dir, NULL);
-		fill_new_filp (&filp, i2e);
-		filp.f_reada = 1;
-		filp.f_pos = 0;
-		bufsrch.entry = entry;
-		bufsrch.search_ino = inode->i_ino;
-		fat_readdir (&filp, &bufsrch, umsdos_dir_search);
-		if (bufsrch.found) {
-			ret = 0;
-			inode->u.umsdos_i.i_dir_owner = dir->i_ino;
-			inode->u.umsdos_i.i_emd_owner = 0;
-			umsdos_setup_dir_inode (inode);
-		}
-		goto out;
-	}
-
-	/* skip . and .. see umsdos_readdir_x() */
-
-	i2e = creat_dentry ("@i2e.nn@", 8, dir, NULL);
-	fill_new_filp (&filp, i2e);
-	filp.f_reada = 1;
-	filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
-	while (1) {
-		if (umsdos_readdir_x (dir, &filp, &bufk, 1, 
-				entry, 0, umsdos_filldir_k) < 0) {
-			printk ("UMSDOS: can't locate inode %ld in EMD??\n",
-				inode->i_ino);
-			break;
-		}
-		if (bufk.ino == inode->i_ino) {
-			ret = 0;
-			umsdos_lookup_patch (dir, inode, entry, bufk.f_pos);
-			break;
-		}
-	}
-	iput (emddir);
-out:
-	return ret;
-}
-
 
 /*
  * Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
@@ -638,6 +634,11 @@
 	int ret = -ENOENT;
 	struct umsdos_info info;
 
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_lookup_x: looking for %s/%s\n", 
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+
 	umsdos_startlookup (dir);
 	/* this shouldn't happen ... */
 	if (len == 1 && name[0] == '.') {
@@ -664,38 +665,57 @@
 	}
 
 	ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
-	if (ret)
+	if (ret) {
+printk("umsdos_lookup_x: %s/%s parse failed, ret=%d\n", 
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
 		goto out;
+	}
+
 	ret = umsdos_findentry (dentry->d_parent, &info, 0);
+	if (ret) {
+if (ret != -ENOENT)
+printk("umsdos_lookup_x: %s/%s findentry failed, ret=%d\n", 
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
+		goto out;
+	}
 Printk (("lookup %.*s pos %lu ret %d len %d ", 
 info.fake.len, info.fake.fname, info.f_pos, ret, info.fake.len));
-	if (ret)
-		goto out;
-
 
+	/* do a real lookup to get the short name ... */
 	dret = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
-					info.fake.len);
+					info.fake.len, 1);
 	ret = PTR_ERR(dret);
-	if (IS_ERR(dret))
+	if (IS_ERR(dret)) {
+printk("umsdos_lookup_x: %s/%s real lookup failed, ret=%d\n", 
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
 		goto out;
-	if (!dret->d_inode)
+	}
+	inode = dret->d_inode;
+	if (!inode)
 		goto out_remove;
-
 	umsdos_lookup_patch_new(dret, &info.entry, info.f_pos);
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_lookup_x: found %s/%s, ino=%ld\n", 
+dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino);
+#endif
 
 	/* Check for a hard link */
-	if (info.entry.flags & UMSDOS_HLINK) {
-Printk (("checking hard link %s/%s, ino=%ld, flags=%x\n", 
-dret->d_parent->d_name.name, dret->d_name.name,
-dret->d_inode->i_ino, info.entry.flags));
+	if ((info.entry.flags & UMSDOS_HLINK) &&
+	    !inode->u.umsdos_i.i_is_hlink) {
 		dret = umsdos_solve_hlink (dret);
 		ret = PTR_ERR(dret);
 		if (IS_ERR(dret))
 			goto out;
+		ret = -ENOENT;
+		inode = dret->d_inode;
+		if (!inode) {
+printk("umsdos_lookup_x: %s/%s negative after link\n", 
+dret->d_parent->d_name.name, dret->d_name.name);
+			goto out_dput;
+		}
 	}
-	/* N.B. can dentry be negative after resolving hlinks? */
 
-	if (pseudo_root && dret->d_inode == pseudo_root && !nopseudo) {
+	if (inode == pseudo_root && !nopseudo) {
 		/* #Specification: pseudo root / dir lookup
 		 * For the same reason as readdir, a lookup in /DOS for
 		 * the pseudo root directory (linux) will fail.
@@ -705,23 +725,22 @@
 		 * which are recorded independently of the pseudo-root
 		 * mode.
 		 */
-		Printk (("umsdos_lookup_x: untested Pseudo_root\n"));
-		ret = -ENOENT;
+printk(KERN_WARNING "umsdos_lookup_x: untested, inode == Pseudo_root\n");
 		goto out_dput;
-	} else {
-		/* We've found it OK.  Now put inode in dentry. */
-		inode = dret->d_inode;
 	}
 
 	/*
-	 * Hash the dentry with the inode.
+	 * We've found it OK.  Now hash the dentry with the inode.
 	 */
 out_add:
 	inode->i_count++;
 	d_add (dentry, inode);
+	dentry->d_op = &umsdos_dentry_operations;
 	ret = 0;
 
 out_dput:
+	if (dret != dentry)
+		d_drop(dret);
 	dput(dret);
 out:
 	umsdos_endlookup (dir);
@@ -729,10 +748,10 @@
 
 out_remove:
 	printk(KERN_WARNING "UMSDOS:  entry %s/%s out of sync, erased\n",
-		dentry->d_name.name, info.fake.fname);
+		dentry->d_parent->d_name.name, dentry->d_name.name);
 	umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode));
 	ret = -ENOENT;
-	goto out;
+	goto out_dput;
 }
 
 
@@ -740,9 +759,7 @@
  * Check whether a file exists in the current directory.
  * Return 0 if OK, negative error code if not (ex: -ENOENT).
  * 
- * called by VFS. should fill dentry->d_inode (via d_add), and 
- * set (increment) dentry->d_inode->i_count.
- *
+ * Called by VFS; should fill dentry->d_inode via d_add.
  */
 
 int UMSDOS_lookup (struct inode *dir, struct dentry *dentry)
@@ -756,6 +773,7 @@
 		Printk ((KERN_DEBUG 
 			"UMSDOS_lookup: converting -ENOENT to negative\n"));
 		d_add (dentry, NULL);
+		dentry->d_op = &umsdos_dentry_operations;
 		ret = 0;
 	}
 	return ret;
@@ -768,7 +786,8 @@
  * We need to use this instead of lookup_dentry, as the 
  * directory semaphore lock is already held.
  */
-struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len)
+struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len,
+					int real)
 {
 	struct dentry *result, *dentry;
 	int error;
@@ -783,7 +802,9 @@
 		dentry = d_alloc(parent, &qstr);
 		if (dentry) {
 			result = dentry;
-			error = umsdos_real_lookup(parent->d_inode, result);
+			error = real ?
+				UMSDOS_rlookup(parent->d_inode, result) :
+				UMSDOS_lookup(parent->d_inode, result);
 			if (error)
 				goto out_fail;
 		}
@@ -797,111 +818,134 @@
 	goto out;
 }
 
+/*
+ * Return a path relative to our root.
+ */
+char * umsdos_d_path(struct dentry *dentry, char * buffer, int len)
+{
+	struct dentry * old_root = current->fs->root;
+	char * path;
+
+	/* N.B. not safe -- fix this soon! */
+	current->fs->root = dentry->d_sb->s_root;
+	path = d_path(dentry, buffer, len);
+	current->fs->root = old_root;
+	return path;
+}
+	
 
 /*
- * gets dentry which points to pseudo-hardlink
+ * Return the dentry which points to a pseudo-hardlink.
  *
  * it should try to find file it points to
- * if file is found, it should dput() original dentry and return new one
- * (with d_count = i_count = 1)
- * Otherwise, it should return with error, with dput()ed original dentry.
+ * if file is found, return new dentry/inode
+ * The resolved inode will have i_is_hlink set.
  *
+ * Note: the original dentry is always dput(), even if an error occurs.
  */
 
 struct dentry *umsdos_solve_hlink (struct dentry *hlink)
 {
 	/* root is our root for resolving pseudo-hardlink */
 	struct dentry *base = hlink->d_sb->s_root;
-	struct dentry *final, *dir, *dentry_dst;
+	struct dentry *dentry_dst;
 	char *path, *pt;
-	unsigned long len;
-	int ret = -EIO;
+	int len;
 	struct file filp;
 
-  	check_dentry_path (hlink, "HLINK BEGIN hlink");
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_solve_hlink: following %s/%s\n", 
+hlink->d_parent->d_name.name, hlink->d_name.name);
+#endif
 
-	final = ERR_PTR (-ENOMEM);
+	dentry_dst = ERR_PTR (-ENOMEM);
 	path = (char *) kmalloc (PATH_MAX, GFP_KERNEL);
 	if (path == NULL)
 		goto out;
 
 	fill_new_filp (&filp, hlink);
 	filp.f_flags = O_RDONLY;
-	filp.f_pos = 0;
 
-	Printk (("hlink2inode "));
 	len = umsdos_file_read_kmem (&filp, path, hlink->d_inode->i_size);
 	if (len != hlink->d_inode->i_size)
 		goto out_noread;
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: %s/%s is path %s\n",
+hlink->d_parent->d_name.name, hlink->d_name.name, path);
+#endif
 
 	/* start at root dentry */
-	dir = dget(base);
-	path[hlink->d_inode->i_size] = '\0';
-	pt = path;
+	dentry_dst = dget(base);
+	path[len] = '\0';
+	pt = path + 1; /* skip leading '/' */
 	while (1) {
+		struct dentry *dir = dentry_dst, *demd;
 		char *start = pt;
-		int len;
+		int real;
 
 		while (*pt != '\0' && *pt != '/') pt++;
 		len = (int) (pt - start);
 		if (*pt == '/') *pt++ = '\0';
 
-		dentry_dst = umsdos_lookup_dentry(dir, start, len);
+		real = (dir->d_inode->u.umsdos_i.i_emd_dir == 0);
+		/*
+		 * Hack alert! inode->u.umsdos_i.i_emd_dir isn't reliable,
+		 * so just check whether there's an EMD file ...
+		 */
+		real = 1;
+		demd = umsdos_get_emd_dentry(dir);
+		if (!IS_ERR(demd)) {
+			if (demd->d_inode)
+				real = 0;
+			dput(demd);
+		}
+
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: dir %s/%s, name=%s, emd_dir=%ld, real=%d\n",
+dir->d_parent->d_name.name, dir->d_name.name, start,
+dir->d_inode->u.umsdos_i.i_emd_dir, real);
+#endif
+		dentry_dst = umsdos_lookup_dentry(dir, start, len, real);
+		if (real)
+			d_drop(dir);
+		dput (dir);
 		if (IS_ERR(dentry_dst))
 			break;
-		if (dir->d_inode->u.umsdos_i.i_emd_dir == 0) {
-			/* This is a DOS directory */
-			ret = umsdos_rlookup_x (dir->d_inode, dentry_dst, 1);
-		} else {
-			ret = umsdos_lookup_x (dir->d_inode, dentry_dst, 1);
-		}
-		Printk (("  returned %d\n", ret));
-		dput (dir);	/* dir no longer needed */
-		dir = dentry_dst;
-
-		Printk (("h2n lookup :%s: -> %d ", start, ret));
-		final = ERR_PTR (ret);
-		if (ret != 0) {
-			/* path component not found! */
+		/* not found? stop search ... */
+		if (!dentry_dst->d_inode) {
 			break;
 		}
-		if (*pt == '\0') {	/* we're finished! */
-			final = umsdos_lookup_dentry(hlink->d_parent,
-						(char *) hlink->d_name.name,
-					 	hlink->d_name.len);
+		if (*pt == '\0')	/* we're finished! */
 			break;
-		}
 	} /* end while */
-	/*
-	 * See whether we found the path ...
-	 */
-	if (!IS_ERR(final)) {
-		if (!final->d_inode) {
-			d_instantiate(final, dir->d_inode);
-			/* we need inode to survive. */
-			dir->d_inode->i_count++;	
+
+	if (!IS_ERR(dentry_dst)) {
+		struct inode *inode = dentry_dst->d_inode;
+		if (inode) {
+			inode->u.umsdos_i.i_is_hlink = 1;
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: resolved link %s/%s, ino=%ld\n",
+dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name, inode->i_ino);
+#endif
 		} else {
-			printk ("umsdos_solve_hlink: %s/%s already exists\n",
-				final->d_parent->d_name.name,
-				final->d_name.name);
-		}
-printk ("umsdos_solve_hlink: ret = %d, %s/%s -> %s/%s\n",
-ret, hlink->d_parent->d_name.name, hlink->d_name.name,
-final->d_parent->d_name.name, final->d_name.name);
-	}
-	dput(dir);
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: resolved link %s/%s negative!\n",
+dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name);
+#endif
+		}
+	} else
+		printk(KERN_WARNING
+			"umsdos_solve_hlink: err=%ld\n", PTR_ERR(dentry_dst));
 
 out_free:
 	kfree (path);
 
 out:
 	dput(hlink);	/* original hlink no longer needed */
-  	check_dentry_path (hlink, "HLINK FIN hlink");
-  	check_dentry_path (final, "HLINK RET final");
-	return final;
+	return dentry_dst;
 
 out_noread:
-	printk("umsdos_solve_hlink: failed reading pseudolink!\n");
+	printk(KERN_WARNING "umsdos_solve_hlink: failed reading pseudolink!\n");
 	goto out_free;
 }	
 
@@ -943,5 +987,4 @@
 	NULL,			/* smap */
 	NULL,			/* updatepage */
 	NULL,			/* revalidate */
-
 };
diff -u --recursive --new-file v2.1.121/linux/fs/umsdos/emd.c linux/fs/umsdos/emd.c
--- v2.1.121/linux/fs/umsdos/emd.c	Wed Sep  9 14:51:09 1998
+++ linux/fs/umsdos/emd.c	Mon Sep 14 17:22:25 1998
@@ -30,46 +30,12 @@
 				char *buf,
 				size_t count)
 {
-	int ret;
-
+	ssize_t ret;
 	mm_segment_t old_fs = get_fs ();
 
 	set_fs (KERNEL_DS);
-
-	PRINTK ((KERN_DEBUG "umsdos_file_read_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d\n", filp, buf, count));
-	PRINTK ((KERN_DEBUG "  inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
-	PRINTK ((KERN_DEBUG "  f_pos=%Lu\n", filp->f_pos));
-	PRINTK ((KERN_DEBUG "  name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
-	PRINTK ((KERN_DEBUG "  i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary));
-	PRINTK ((KERN_DEBUG "  f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
-	PRINTK ((KERN_DEBUG "  f_owner=%d\n", filp->f_owner.uid));
-	PRINTK ((KERN_DEBUG "  f_version=%ld\n", filp->f_version));
-	PRINTK ((KERN_DEBUG "  f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
-
 	MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2;
-
 	ret = fat_file_read (filp, buf, count, &filp->f_pos);
-	PRINTK ((KERN_DEBUG "fat_file_read returned with %d!\n", ret));
-
-	PRINTK ((KERN_DEBUG "  (ret) inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
-	PRINTK ((KERN_DEBUG "  (ret) f_pos=%Lu\n", filp->f_pos));
-	PRINTK ((KERN_DEBUG "  (ret) name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
-	PRINTK ((KERN_DEBUG "  (ret) i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary));
-	PRINTK ((KERN_DEBUG "  (ret) f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
-	PRINTK ((KERN_DEBUG "  (ret) f_owner=%d\n", filp->f_owner.uid));
-	PRINTK ((KERN_DEBUG "  (ret) f_version=%ld\n", filp->f_version));
-	PRINTK ((KERN_DEBUG "  (ret) f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
-
-#if 0
-	{
-		struct umsdos_dirent *mydirent = buf;
-
-		Printk ((KERN_DEBUG "  (DDD) uid=%d\n", mydirent->uid));
-		Printk ((KERN_DEBUG "  (DDD) gid=%d\n", mydirent->gid));
-		Printk ((KERN_DEBUG "  (DDD) name=>%.20s<\n", mydirent->name));
-	}
-#endif
-
 	set_fs (old_fs);
 	return ret;
 }
@@ -85,34 +51,30 @@
 				     const char *buf,
 				     size_t count)
 {
-	ssize_t ret;
 	mm_segment_t old_fs = get_fs ();
-
-	set_fs (KERNEL_DS);
-
-	PRINTK ((KERN_DEBUG "umsdos_file_write_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d\n", filp, buf, count));
-	PRINTK ((KERN_DEBUG "  struct dentry=%p\n", filp->f_dentry));
-	PRINTK ((KERN_DEBUG "  struct inode=%p\n", filp->f_dentry->d_inode));
-	PRINTK ((KERN_DEBUG "  inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
-	PRINTK ((KERN_DEBUG "  f_pos=%Lu\n", filp->f_pos));
-	PRINTK ((KERN_DEBUG "  name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
-	PRINTK ((KERN_DEBUG "  i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary));
-	PRINTK ((KERN_DEBUG "  f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
-	PRINTK ((KERN_DEBUG "  f_owner=%d\n", filp->f_owner.uid));
-	PRINTK ((KERN_DEBUG "  f_version=%ld\n", filp->f_version));
-	PRINTK ((KERN_DEBUG "  f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
+	ssize_t ret;
 
 	/* note: i_binary=2 is for CVF-FAT. We put it here, instead of
-	 * umsdos_file_write_kmem, since it is also wise not to compress symlinks
-	 * (in the unlikely event that they are > 512 bytes and can be compressed 
-	 * FIXME: should we set it when reading symlinks too? */
+	 * umsdos_file_write_kmem, since it is also wise not to compress
+	 * symlinks (in the unlikely event that they are > 512 bytes and
+	 * can be compressed.
+	 * FIXME: should we set it when reading symlinks too?
+	 */
 
 	MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2;
 
+	set_fs (KERNEL_DS);
 	ret = fat_file_write (filp, buf, count, &filp->f_pos);
-	Printk ((KERN_DEBUG "fat_file_write returned with %ld!\n", (long int) ret));
-
 	set_fs (old_fs);
+	if (ret < 0) {
+		printk(KERN_WARNING "umsdos_file_write: ret=%d\n", ret);
+		goto out;
+	}
+#ifdef UMSDOS_PARANOIA
+if (ret != count)
+printk(KERN_WARNING "umsdos_file_write: count=%u, ret=%u\n", count, ret);
+#endif
+out:
 	return ret;
 }
 
@@ -125,9 +87,8 @@
 				const char *buf,
 				size_t count)
 {
-	int ret;
+	ssize_t ret;
 
-	Printk ((KERN_DEBUG " STARTED WRITE_KMEM /mn/\n"));
 	ret = umsdos_file_write_kmem_real (filp, buf, count);
 	return ret;
 }
@@ -166,7 +127,6 @@
 Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %d, %Ld\n",
 filp, buf, count, filp->f_pos));
 	written = umsdos_file_write_kmem (filp, buf, count);
-	Printk (("umsdos_emd_dir_write /mn/: write_kmem returned\n"));
 
 #ifdef __BIG_ENDIAN
 	d->nlink = le16_to_cpu (d->nlink);
@@ -179,13 +139,13 @@
 	d->mode = le16_to_cpu (d->mode);
 #endif
 
-#if UMS_DEBUG
+#ifdef UMSDOS_PARANOIA
 if (written != count)
 printk(KERN_ERR "umsdos_emd_dir_write: ERROR: written (%d) != count (%d)\n",
 written, count);
 #endif
 
-	return written != count ? -EIO : 0;
+	return (written != count) ? -EIO : 0;
 }
 
 
@@ -199,9 +159,7 @@
 
 ssize_t umsdos_emd_dir_read (struct file *filp, char *buf, size_t count)
 {
-	long int ret = 0;
-	int sizeread;
-
+	ssize_t sizeread, ret = 0;
 
 #ifdef __BIG_ENDIAN
 	struct umsdos_dirent *d = (struct umsdos_dirent *) buf;
@@ -211,8 +169,9 @@
 	filp->f_flags = 0;
 	sizeread = umsdos_file_read_kmem (filp, buf, count);
 	if (sizeread != count) {
-printk ("UMSDOS:  problem with EMD file:  can't read pos = %Ld (%d != %d)\n",
-filp->f_pos, sizeread, count);
+		printk (KERN_WARNING 
+			"UMSDOS: EMD problem, pos=%Ld, count=%d, read=%d\n",
+			filp->f_pos, count, sizeread);
 		ret = -EIO;
 	}
 #ifdef __BIG_ENDIAN
@@ -226,7 +185,6 @@
 	d->mode = le16_to_cpu (d->mode);
 #endif
 	return ret;
-
 }
 
 
@@ -237,8 +195,8 @@
 {
 	struct dentry *demd;
 
-	demd = umsdos_lookup_dentry (parent, UMSDOS_EMD_FILE, 
-					UMSDOS_EMD_NAMELEN);
+	demd = umsdos_lookup_dentry(parent, UMSDOS_EMD_FILE, 
+					UMSDOS_EMD_NAMELEN, 1);
 	return demd;
 }
 
@@ -260,7 +218,7 @@
 
 /*
  * Create the EMD file for a directory if it doesn't
- * already exist. Returns 0 or and error code.
+ * already exist. Returns 0 or an error code.
  */
 int umsdos_make_emd(struct dentry *parent)
 {
@@ -268,26 +226,30 @@
 	struct inode *inode;
 	int err = PTR_ERR(demd);
 
-	if (IS_ERR(demd))
+	if (IS_ERR(demd)) {
+		printk("umsdos_make_emd: can't get dentry in %s, err=%d\n",
+			parent->d_name.name, err);
 		goto out;
+	}
 
 	/* already created? */
+	err = 0;
 	inode = demd->d_inode;
-	if (inode) {
-		parent->d_inode->u.umsdos_i.i_emd_dir = inode->i_ino;
-		err = 0;
-		goto out_dput;
-	}
+	if (inode)
+		goto out_set;
 
-printk("umsdos_make_emd: creating %s/%s\n",
-parent->d_name.name, demd->d_name.name);
+Printk(("umsdos_make_emd: creating EMD %s/%s\n",
+parent->d_name.name, demd->d_name.name));
 
 	err = msdos_create(parent->d_inode, demd, S_IFREG | 0777);
 	if (err) {
-		printk (KERN_WARNING "UMSDOS: Can't create EMD file\n");
+		printk (KERN_WARNING
+			"UMSDOS: create %s/%s failed, err=%d\n",
+			parent->d_name.name, demd->d_name.name, err);
 		goto out_dput;
 	}
 	inode = demd->d_inode;
+out_set:
 	parent->d_inode->u.umsdos_i.i_emd_dir = inode->i_ino;
 	/* Disable UMSDOS_notify_change() for EMD file */
 	inode->u.umsdos_i.i_emd_owner = 0xffffffff;
@@ -338,7 +300,7 @@
 	dlook = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, d_dir);
 	if (!dlook)
 		goto out;
-	rv = umsdos_real_lookup (dir, dlook);
+	rv = msdos_lookup (dir, dlook);
 		
 	PRINTK ((KERN_DEBUG "-returned %d\n", rv));
 	Printk ((KERN_INFO "emd_dir_lookup "));
@@ -450,7 +412,7 @@
 
 	if (free_entry) {
 		/* #Specification: EMD file / empty entries
-		 * Unused entry in the EMD file are identified
+		 * Unused entries in the EMD file are identified
 		 * by the name_len field equal to 0. However to
 		 * help future extension (or bug correction :-( ),
 		 * empty entries are filled with 0.
@@ -662,6 +624,8 @@
 			}
 		}
 	}
+Printk(("umsdos_find: ready to mangle %s, len=%d, pos=%ld\n",
+entry->name, entry->name_len, (long)info->f_pos));
 	umsdos_manglename (info);
 
 out_dput:
@@ -795,8 +759,8 @@
 out_dput:
 	dput(demd);
 out:
-printk("umsdos_isempty: checked %s/%s, empty=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, ret);
+Printk(("umsdos_isempty: checked %s/%s, empty=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret));
 
 	return ret;
 }
@@ -805,11 +769,11 @@
  * Locate an entry in a EMD directory.
  * Return 0 if OK, error code if not, generally -ENOENT.
  *
- * does not change i_count
+ * expect argument:
+ * 	0: anything
+ * 	1: file
+ * 	2: directory
  */
-/* 0: anything */
-/* 1: file */
-/* 2: directory */
 
 int umsdos_findentry (struct dentry *parent, struct umsdos_info *info,
 			int expect)
@@ -820,14 +784,16 @@
 	if (ret)
 		goto out;
 
-	if (expect != 0) {
-		if (S_ISDIR (info->entry.mode)) {
-			if (expect != 2)
-				ret = -EISDIR;
-		} else if (expect == 2) {
+	switch (expect) {
+	case 1:
+		if (S_ISDIR (info->entry.mode))
+			ret = -EISDIR;
+		break;
+	case 2:
+		if (!S_ISDIR (info->entry.mode))
 			ret = -ENOTDIR;
-		}
 	}
+
 out:
 	Printk (("umsdos_findentry: returning %d\n", ret));
 	return ret;
diff -u --recursive --new-file v2.1.121/linux/fs/umsdos/file.c linux/fs/umsdos/file.c
--- v2.1.121/linux/fs/umsdos/file.c	Sat Sep  5 16:46:41 1998
+++ linux/fs/umsdos/file.c	Fri Sep 11 11:26:56 1998
@@ -32,15 +32,12 @@
 	struct dentry *dentry = filp->f_dentry;
 	struct inode *inode = dentry->d_inode;
 
-	/* We have to set the access time because msdos don't care */
-	/* FIXME */
 	int ret = fat_file_read (filp, buf, count, ppos);
 
+	/* We have to set the access time because msdos don't care */
 	if (!IS_RDONLY (inode)) {
 		inode->i_atime = CURRENT_TIME;
-		/* FIXME 
-		 * inode->i_dirt = 1;
-		 */
+		mark_inode_dirty(inode);
 	}
 	return ret;
 }
@@ -65,10 +62,11 @@
 static void UMSDOS_truncate (struct inode *inode)
 {
 	Printk (("UMSDOS_truncate\n"));
-	fat_truncate (inode);
-	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
-
-	/*FIXME inode->i_dirt = 1; */
+	if (!IS_RDONLY (inode)) {
+		fat_truncate (inode);
+		inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+		mark_inode_dirty(inode);
+	}
 }
 
 /* Function for normal file system (512 bytes hardware sector size) */
diff -u --recursive --new-file v2.1.121/linux/fs/umsdos/inode.c linux/fs/umsdos/inode.c
--- v2.1.121/linux/fs/umsdos/inode.c	Wed Sep  9 14:51:09 1998
+++ linux/fs/umsdos/inode.c	Wed Sep 16 15:12:31 1998
@@ -19,6 +19,7 @@
 #include <linux/umsdos_fs.h>
 #include <linux/list.h>
 
+extern struct dentry_operations umsdos_dentry_operations;
 extern struct inode_operations umsdos_rdir_inode_operations;
 
 
@@ -59,7 +60,7 @@
 void fill_new_filp (struct file *filp, struct dentry *dentry)
 {
 	if (!dentry)
-		printk("fill_new_filp: NULL dentry!\n");
+		printk(KERN_ERR "fill_new_filp: NULL dentry!\n");
 
 	memset (filp, 0, sizeof (struct file));
 	filp->f_reada = 1;
@@ -133,7 +134,7 @@
 		 ,inode->u.umsdos_i.i_emd_owner, inode->u.umsdos_i.pos
 		 ,inode->u.umsdos_i.i_emd_dir, inode->i_count));
 
-	if (inode && pseudo_root && inode == pseudo_root) {
+	if (inode == pseudo_root) {
 		printk (KERN_ERR "Umsdos: Oops releasing pseudo_root."
 			" Notify jacques@solucorp.qc.ca\n");
 	}
@@ -151,23 +152,6 @@
 
 
 /*
- * Call msdos_lookup, but set back the original msdos function table.
- * Return 0 if OK, or a negative error code if not.
- * Dentry will hold inode of the file, if successful
- */
-int umsdos_real_lookup (struct inode *dir, struct dentry *dentry)
-{
-	int ret;
-
-	PRINTK ((KERN_DEBUG "umsdos_real_lookup: looking for %s/%s /",
-		dentry->d_parent->d_name.name, dentry->d_name.name));
-	ret = msdos_lookup (dir, dentry);
-	PRINTK (("/ returned %d\n", ret));
-
-	return ret;
-}
-
-/*
  * Complete the setup of an directory dentry.
  * First, it completes the function pointers, then
  * it locates the EMD file. If the EMD is there, then plug the
@@ -230,6 +214,32 @@
 /*
  * Add some info into an inode so it can find its owner quickly
  */
+void umsdos_set_dirinfo_new (struct dentry *dentry, off_t f_pos)
+{
+	struct inode *inode = dentry->d_inode;
+	struct dentry *demd;
+
+	inode->u.umsdos_i.i_emd_owner = 0;
+	inode->u.umsdos_i.pos = f_pos;
+
+	/* now check the EMD file */
+	demd = umsdos_get_emd_dentry(dentry->d_parent);
+	if (IS_ERR(demd)) {
+		goto out;
+	}
+	if (demd->d_inode) {
+		inode->u.umsdos_i.i_emd_owner = demd->d_inode->i_ino;
+	}
+	dput (demd);
+out:
+	return;
+}
+
+
+/*
+ * Add some info into an inode so it can find its owner quickly
+ * Note: Deprecated; use above function if possible.
+ */
 void umsdos_set_dirinfo (struct inode *inode, struct inode *dir, off_t f_pos)
 {
 	struct inode *emd_owner = umsdos_emd_dir_lookup (dir, 1);
@@ -238,7 +248,6 @@
 		goto out;
 	Printk (("umsdos_set_dirinfo: emd_owner is %lu for dir %lu\n", 
 		emd_owner->i_ino, dir->i_ino));
-	inode->u.umsdos_i.i_dir_owner = dir->i_ino;
 	inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
 	inode->u.umsdos_i.pos = f_pos;
 	iput (emd_owner);
@@ -405,20 +414,23 @@
 }
 
 
-/* #Specification: notify_change / i_nlink > 0
- * notify change is only done for inode with nlink > 0. An inode
- * with nlink == 0 is no longer associated with any entry in
- * the EMD file, so there is nothing to update.
- */
-static int internal_notify_change (struct inode *inode, struct iattr *attr)
+int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr)
 {
-	unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner;
+	struct inode *inode = dentry->d_inode;
+	struct dentry *demd;
 	int ret;
+	struct file filp;
+	struct umsdos_dirent entry;
 
-	Printk ((KERN_DEBUG "UMSDOS_notify_change: entering\n"));
+Printk(("UMSDOS_notify_change: entering for %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name));
 
-	if ((ret = inode_change_ok (inode, attr)) != 0)
+	ret = inode_change_ok (inode, attr);
+	if (ret) {
+printk("UMSDOS_notify_change: %s/%s change not OK, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
 		goto out;
+	}
 
 	if (inode->i_nlink == 0)
 		goto out_nolink;
@@ -426,61 +438,77 @@
 	if (inode->i_ino == UMSDOS_ROOT_INO)
 		goto out_nolink;
 
-	if (i_emd_owner != 0xffffffff && i_emd_owner != 0) {
-		/* This inode is not a EMD file nor an inode used internally
-		 * by MSDOS, so we can update its status.
-		 * See emd.c
-		 */
-		struct inode *emd_owner;
-		struct file filp;
-		struct umsdos_dirent entry;
-		struct dentry *emd_dentry;
-
-		Printk (("notify change %p ", inode));
-		ret = -EPERM;
-		emd_owner = iget (inode->i_sb, i_emd_owner);
-		if (!emd_owner) {
-			printk ("UMSDOS: emd_owner = NULL ???");
-			goto out_nolink;
-		}
-		emd_dentry = geti_dentry (emd_owner); /* FIXME? */
-		fill_new_filp (&filp, emd_dentry);
+	/* get the EMD file dentry */
+	demd = umsdos_get_emd_dentry(dentry->d_parent);
+	ret = PTR_ERR(demd);
+	if (IS_ERR(demd))
+		goto out_nolink;
+	ret = -EPERM;
+	if (!demd->d_inode) {
+		printk(KERN_WARNING
+			"UMSDOS_notify_change: no EMD file %s/%s\n",
+			demd->d_parent->d_name.name, demd->d_name.name);
+		goto out_dput;
+	}
+
+	ret = 0;
+	/* don't do anything if this is the EMD itself */
+	if (inode == demd->d_inode)
+		goto out_dput;
+
+	/* This inode is not a EMD file nor an inode used internally
+	 * by MSDOS, so we can update its status.
+	 * See emd.c
+	 */
 
-		filp.f_pos = inode->u.umsdos_i.pos;
-		filp.f_reada = 0;
-		Printk (("pos = %Lu ", filp.f_pos));
-		/* Read only the start of the entry since we don't touch */
-		/* the name */
-		ret = umsdos_emd_dir_read (&filp, (char *) &entry, 
-						UMSDOS_REC_SIZE);
-		if (!ret) {
-			if (attr->ia_valid & ATTR_UID)
-				entry.uid = attr->ia_uid;
-			if (attr->ia_valid & ATTR_GID)
-				entry.gid = attr->ia_gid;
-			if (attr->ia_valid & ATTR_MODE)
-				entry.mode = attr->ia_mode;
-			if (attr->ia_valid & ATTR_ATIME)
-				entry.atime = attr->ia_atime;
-			if (attr->ia_valid & ATTR_MTIME)
-				entry.mtime = attr->ia_mtime;
-			if (attr->ia_valid & ATTR_CTIME)
-				entry.ctime = attr->ia_ctime;
-
-			entry.nlink = inode->i_nlink;
-			filp.f_pos = inode->u.umsdos_i.pos;
-			ret = umsdos_emd_dir_write (&filp, (char *) &entry,
-							 UMSDOS_REC_SIZE);
-
-			Printk (("notify pos %lu ret %d nlink %d ",
-				inode->u.umsdos_i.pos, ret, entry.nlink));
-			/* #Specification: notify_change / msdos fs
-			 * notify_change operation are done only on the
-			 * EMD file. The msdos fs is not even called.
-			 */
-		}
-		iput(emd_owner);
-	}
+	fill_new_filp (&filp, demd);
+	filp.f_pos = inode->u.umsdos_i.pos;
+Printk(("UMSDOS_notify_change: %s/%s reading at %u\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, filp.f_pos));
+
+	/* Read only the start of the entry since we don't touch the name */
+	ret = umsdos_emd_dir_read (&filp, (char *) &entry, UMSDOS_REC_SIZE);
+	if (ret) {
+		printk(KERN_WARNING
+			"umsdos_notify_change: %s/%s EMD read error, ret=%d\n",
+			dentry->d_parent->d_name.name, dentry->d_name.name,ret);
+		goto out_dput;
+	}
+	if (attr->ia_valid & ATTR_UID)
+		entry.uid = attr->ia_uid;
+	if (attr->ia_valid & ATTR_GID)
+		entry.gid = attr->ia_gid;
+	if (attr->ia_valid & ATTR_MODE)
+		entry.mode = attr->ia_mode;
+	if (attr->ia_valid & ATTR_ATIME)
+		entry.atime = attr->ia_atime;
+	if (attr->ia_valid & ATTR_MTIME)
+		entry.mtime = attr->ia_mtime;
+	if (attr->ia_valid & ATTR_CTIME)
+		entry.ctime = attr->ia_ctime;
+
+	entry.nlink = inode->i_nlink;
+	filp.f_pos = inode->u.umsdos_i.pos;
+	ret = umsdos_emd_dir_write (&filp, (char *) &entry, UMSDOS_REC_SIZE);
+	if (ret)
+		printk(KERN_WARNING
+			"umsdos_notify_change: %s/%s EMD write error, ret=%d\n",
+			dentry->d_parent->d_name.name, dentry->d_name.name,ret);
+
+	Printk (("notify pos %lu ret %d nlink %d ",
+		inode->u.umsdos_i.pos, ret, entry.nlink));
+	/* #Specification: notify_change / msdos fs
+	 * notify_change operation are done only on the
+	 * EMD file. The msdos fs is not even called.
+	 */
+#ifdef UMSDOS_DEBUG_VERBOSE
+if (entry.flags & UMSDOS_HIDDEN)
+printk("umsdos_notify_change: %s/%s hidden, nlink=%d, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, entry.nlink, ret);
+#endif
+
+out_dput:
+	dput(demd);
 out_nolink:
 	if (ret == 0)
 		inode_setattr (inode, attr);
@@ -489,11 +517,6 @@
 }
 
 
-int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr)
-{
-	return internal_notify_change (dentry->d_inode, attr);
-}
-
 /*
  * Update the disk with the inode content
  */
@@ -551,16 +574,15 @@
 	if (!res)
 		goto out_fail;
 
-	printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-7 "
+	printk (KERN_INFO "UMSDOS dentry-Beta 0.83 "
 		"(compatibility level %d.%d, fast msdos)\n", 
 		UMSDOS_VERSION, UMSDOS_RELEASE);
 
 	sb->s_op = &umsdos_sops;
 	MSDOS_SB(sb)->options.dotsOK = 0;	/* disable hidden==dotfile */
 
-	/* FIXME:?? clear d_op on root so it will not be inherited */
-	sb->s_root->d_op = NULL;
-
+	/* install our dentry operations ... */
+	sb->s_root->d_op = &umsdos_dentry_operations;
 	pseudo = sb->s_root->d_inode;
 	umsdos_setup_dir(sb->s_root);
 
@@ -599,7 +621,7 @@
 	sbin = creat_dentry ("sbin", 4, NULL, root);
 
 	Printk ((KERN_DEBUG "Mounting root\n"));
-	if (umsdos_real_lookup (pseudo, root) == 0
+	if (msdos_lookup (pseudo, root) == 0
 	    && (root->d_inode != NULL)
 	    && S_ISDIR (root->d_inode->i_mode)) {
 
@@ -609,7 +631,7 @@
 		etc = creat_dentry ("etc", 3, NULL, root);
 
 
-		if (umsdos_real_lookup (pseudo, etc) == 0
+		if (msdos_lookup (pseudo, etc) == 0
 		    && S_ISDIR (etc->d_inode->i_mode)) {
 
 Printk ((KERN_DEBUG "/%s/etc is there\n", UMSDOS_PSDROOT_NAME));
@@ -617,9 +639,9 @@
 			init = creat_dentry ("init", 4, NULL, etc);
 			etc_rc = creat_dentry ("rc", 2, NULL, etc);
 
-			if ((umsdos_real_lookup (pseudo, init) == 0
+			if ((msdos_lookup (pseudo, init) == 0
 			     && S_ISREG (init->d_inode->i_mode))
-			    || (umsdos_real_lookup (pseudo, etc_rc) == 0
+			    || (msdos_lookup (pseudo, etc_rc) == 0
 			 && S_ISREG (etc_rc->d_inode->i_mode))) {
 				pseudo_ok = 1;
 			}
@@ -629,12 +651,12 @@
 			/* iput(rc); */
 		}
 		if (!pseudo_ok
-		/* && umsdos_real_lookup (pseudo, "sbin", 4, sbin)==0 */
-		    && umsdos_real_lookup (pseudo, sbin) == 0
+		/* && msdos_lookup (pseudo, "sbin", 4, sbin)==0 */
+		    && msdos_lookup (pseudo, sbin) == 0
 		    && S_ISDIR (sbin->d_inode->i_mode)) {
 
 Printk ((KERN_DEBUG "/%s/sbin is there\n", UMSDOS_PSDROOT_NAME));
-			if (umsdos_real_lookup (pseudo, init) == 0
+			if (msdos_lookup (pseudo, init) == 0
 			    && S_ISREG (init->d_inode->i_mode)) {
 				pseudo_ok = 1;
 			}
diff -u --recursive --new-file v2.1.121/linux/fs/umsdos/ioctl.c linux/fs/umsdos/ioctl.c
--- v2.1.121/linux/fs/umsdos/ioctl.c	Wed Sep  9 14:51:09 1998
+++ linux/fs/umsdos/ioctl.c	Mon Sep 14 17:22:25 1998
@@ -78,6 +78,9 @@
 	struct file new_filp;
 	struct umsdos_ioctl data;
 
+Printk(("UMSDOS_ioctl_dir: %s/%s, cmd=%d, data=%08lx\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, cmd, data_ptr));
+
 	/* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */
 	if (cmd != UMSDOS_GETVERSION
 	    && cmd != UMSDOS_READDIR_DOS
@@ -164,20 +167,19 @@
 		ret = PTR_ERR(demd);
 		if (IS_ERR(demd))
 			goto out;
+		ret = 0;
+		if (!demd->d_inode)
+			goto read_dput;
+
 		fill_new_filp(&new_filp, demd);
 		new_filp.f_pos = filp->f_pos;
-
-		while (1) {
+		while (new_filp.f_pos < demd->d_inode->i_size) {
 			off_t f_pos = new_filp.f_pos;
 			struct umsdos_dirent entry;
 			struct umsdos_info info;
 
-			ret = 0;
-			if (new_filp.f_pos >= demd->d_inode->i_size)
-				break;
-
 			ret = umsdos_emd_dir_readentry (&new_filp, &entry);
-			if (ret < 0)
+			if (ret)
 				break;
 			if (entry.name_len <= 0)
 				continue;
@@ -198,6 +200,7 @@
 		}
 		/* update the original f_pos */
 		filp->f_pos = new_filp.f_pos;
+	read_dput:
 		d_drop(demd);
 		dput(demd);
 		goto out;
@@ -217,6 +220,8 @@
 		extern struct inode_operations umsdos_rdir_inode_operations;
 
 		ret = umsdos_make_emd(dentry);
+Printk(("UMSDOS_ioctl_dir: INIT_EMD %s/%s, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret));
 		dir->i_op = (ret == 0)
 		    ? &umsdos_dir_inode_operations
 		    : &umsdos_rdir_inode_operations;
@@ -262,13 +267,13 @@
 		 */
 		old_dentry = umsdos_lookup_dentry (dentry, 
 						data.dos_dirent.d_name,
-						data.dos_dirent.d_reclen);
+						data.dos_dirent.d_reclen ,1);
 		ret = PTR_ERR(old_dentry);
 		if (IS_ERR(old_dentry))
 			goto out;
 		new_dentry = umsdos_lookup_dentry (dentry,
 						data.umsdos_dirent.name,
-						data.umsdos_dirent.name_len);
+						data.umsdos_dirent.name_len, 1);
 		ret = PTR_ERR(new_dentry);
 		if (!IS_ERR(new_dentry)) {
 printk("umsdos_ioctl: renaming %s/%s to %s/%s\n",
@@ -314,7 +319,7 @@
 		 * Return 0 if success.
 		 */
 		temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
-						    data.dos_dirent.d_reclen);
+						data.dos_dirent.d_reclen, 1);
 		ret = PTR_ERR(temp);
 		if (IS_ERR(temp))
 			goto out;
@@ -336,7 +341,7 @@
 		 * Return 0 if success.
 		 */
 		temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
-						    data.dos_dirent.d_reclen);
+					    data.dos_dirent.d_reclen, 1);
 		ret = PTR_ERR(temp);
 		if (IS_ERR(temp))
 			goto out;
@@ -362,7 +367,7 @@
 		struct inode *inode;
 
 		dret = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
-						    data.dos_dirent.d_reclen);
+					    data.dos_dirent.d_reclen, 1);
 		ret = PTR_ERR(dret);
 		if (IS_ERR(dret))
 			goto out;
diff -u --recursive --new-file v2.1.121/linux/fs/umsdos/mangle.c linux/fs/umsdos/mangle.c
--- v2.1.121/linux/fs/umsdos/mangle.c	Wed Sep  9 14:51:09 1998
+++ linux/fs/umsdos/mangle.c	Mon Sep 14 17:22:25 1998
@@ -411,6 +411,7 @@
 		/* Why not use info->fake.len everywhere? Is it longer?
                  */
 		memcpy (info->entry.name, fname, len);
+		info->entry.name[len] = '\0';	/* for printk */
 		info->entry.name_len = len;
 		ret = 0;
 	}
diff -u --recursive --new-file v2.1.121/linux/fs/umsdos/namei.c linux/fs/umsdos/namei.c
--- v2.1.121/linux/fs/umsdos/namei.c	Wed Sep  9 14:51:09 1998
+++ linux/fs/umsdos/namei.c	Mon Sep 14 17:22:25 1998
@@ -224,8 +224,8 @@
  * 
  * Return the status of the operation. 0 mean success.
  *
- * #Specification: create / file exist in DOS
- * Here is a situation. Trying to create a file with
+ * #Specification: create / file exists in DOS
+ * Here is a situation: we are trying to create a file with
  * UMSDOS. The file is unknown to UMSDOS but already
  * exists in the DOS directory.
  * 
@@ -254,19 +254,9 @@
 	int ret;
 	struct umsdos_info info;
 
-if (dentry->d_inode)
-printk("umsdos_create_any: %s/%s not negative!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-
-Printk (("umsdos_create_any /mn/: create %.*s in dir=%lu - nevercreat=/",
-(int) dentry->d_name.len, dentry->d_name.name, dir->i_ino));
-
-	check_dentry_path (dentry, "umsdos_create_any");
 	ret = umsdos_nevercreat (dir, dentry, -EEXIST);
-	if (ret) {
-Printk (("%d/\n", ret));
+	if (ret)
 		goto out;
-	}
 
 	ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
 	if (ret)
@@ -284,12 +274,12 @@
 	if (ret)
 		goto out_unlock;
 
-	/* create short name dentry */
+	/* do a real lookup to get the short name dentry */
 	fake = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, 
-					info.fake.len);
+					info.fake.len, 1);
 	ret = PTR_ERR(fake);
 	if (IS_ERR(fake))
-		goto out_unlock;
+		goto out_remove;
 
 	/* should not exist yet ... */
 	ret = -EEXIST;
@@ -300,39 +290,25 @@
 	if (ret)
 		goto out_remove;
 
-	inode = fake->d_inode;
-	umsdos_lookup_patch_new(fake, &info.entry, info.f_pos);
-
-Printk (("inode %p[%lu], count=%d ", inode, inode->i_ino, inode->i_count));
-Printk (("Creation OK: [dir %lu] %.*s pid=%d pos %ld\n",
-dir->i_ino, info.fake.len, info.fake.fname, current->pid, info.f_pos));
-
-	check_dentry_path (dentry, "umsdos_create_any: BEG dentry");
-	check_dentry_path (fake, "umsdos_create_any: BEG fake");
-
 	/*
 	 * Note! The long and short name might be the same,
 	 * so check first before doing the instantiate ...
 	 */
 	if (dentry != fake) {
-		/* long name also gets inode info */
+		inode = fake->d_inode;
 		inode->i_count++;
 		d_instantiate (dentry, inode);
 	}
-
-	check_dentry_path (dentry, "umsdos_create_any: END dentry");
-	check_dentry_path (fake, "umsdos_create_any: END fake");
+	umsdos_lookup_patch_new(dentry, &info.entry, info.f_pos);
 	goto out_dput;
 
 out_remove:
-if (ret == -EEXIST)
-printk("UMSDOS: out of sync, error [%ld], deleting %.*s %d %d pos %ld\n",
-dir->i_ino ,info.fake.len, info.fake.fname, -ret, current->pid, info.f_pos);
-	umsdos_delentry (dentry->d_parent, &info, 0);
+	if (ret == -EEXIST)
+		printk(KERN_WARNING "UMSDOS: out of sync, deleting %s/%s\n",
+			dentry->d_parent->d_name.name, info.fake.fname);
+	umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode));
 
 out_dput:
-Printk (("umsdos_create %.*s ret = %d pos %ld\n",
-info.fake.len, info.fake.fname, ret, info.f_pos));
 	/* N.B. any value in keeping short name dentries? */
 	if (dentry != fake)
 		d_drop(fake);
@@ -345,11 +321,24 @@
 }
 
 /*
+ * Add a new file into the alternate directory.
+ * The file is added to the real MSDOS directory. If successful, it
+ * is then added to the EMD file.
+ * 
+ * Return the status of the operation. 0 mean success.
+ */
+int UMSDOS_create (struct inode *dir, struct dentry *dentry, int mode)
+{
+	return umsdos_create_any (dir, dentry, mode, 0, 0);
+}
+
+
+/*
  * Initialise the new_entry from the old for a rename operation.
  * (Only useful for umsdos_rename_f() below).
  */
 static void umsdos_ren_init (struct umsdos_info *new_info,
-			     struct umsdos_info *old_info, int flags)
+			     struct umsdos_info *old_info)
 {
 	/* != 0, this is the value of flags */
 	new_info->entry.mode = old_info->entry.mode;
@@ -359,10 +348,11 @@
 	new_info->entry.ctime = old_info->entry.ctime;
 	new_info->entry.atime = old_info->entry.atime;
 	new_info->entry.mtime = old_info->entry.mtime;
-	new_info->entry.flags = flags ? flags : old_info->entry.flags;
+	new_info->entry.flags = old_info->entry.flags;
 	new_info->entry.nlink = old_info->entry.nlink;
 }
 
+#ifdef OBSOLETE
 #define chkstk() \
 if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\
     printk(KERN_ALERT "UMSDOS: %s magic %x != %lx ligne %d\n" \
@@ -373,6 +363,7 @@
 
 #undef chkstk
 #define chkstk() do { } while (0);
+#endif
 
 /*
  * Rename a file (move) in the file system.
@@ -382,124 +373,199 @@
 			    struct inode *new_dir, struct dentry *new_dentry,
 			    int flags)
 {
-	int old_ret, new_ret;
-	struct dentry *old, *new, *dret;
-	struct inode *oldid = NULL;
-	int ret = -EPERM;
+	struct dentry *old, *new, *old_emd, *new_target = NULL;
+	int err, ret, rehash = 0;
 	struct umsdos_info old_info;
 	struct umsdos_info new_info;
 
-	old_ret = umsdos_parse (old_dentry->d_name.name,
+ 	ret = -EPERM;
+	err = umsdos_parse (old_dentry->d_name.name,
 				old_dentry->d_name.len, &old_info);
-	if (old_ret)
+	if (err)
 		goto out;
-	new_ret = umsdos_parse (new_dentry->d_name.name,
+	err = umsdos_parse (new_dentry->d_name.name,
 				new_dentry->d_name.len, &new_info);
-	if (new_ret)
+	if (err)
 		goto out;
 
-	check_dentry_path (old_dentry, "umsdos_rename_f OLD");
-	check_dentry_path (new_dentry, "umsdos_rename_f OLD");
+	/* Get the EMD dentry for the old parent */
+	old_emd = umsdos_get_emd_dentry(old_dentry->d_parent);
+	ret = PTR_ERR(old_emd);
+	if (IS_ERR(old_emd))
+		goto out;
 
-	chkstk ();
-Printk (("umsdos_rename %d %d ", old_ret, new_ret));
 	umsdos_lockcreate2 (old_dir, new_dir);
-	chkstk ();
 
-	ret = umsdos_findentry(old_dentry->d_parent, &old_info, 0);
-	chkstk ();
+	ret = umsdos_findentry(old_emd->d_parent, &old_info, 0);
 	if (ret) {
-Printk (("ret %d ", ret));
+		printk(KERN_ERR
+			"umsdos_rename_f: old entry %s/%s not in EMD, ret=%d\n",
+			old_dentry->d_parent->d_name.name, old_info.entry.name,
+			ret);
 		goto out_unlock;
 	}
 
 	/* check sticky bit on old_dir */
 	ret = -EPERM;
-	if (is_sticky(old_dir, old_info.entry.uid)) {
-		Printk (("sticky set on old "));
+	if (is_sticky(old_dir, old_info.entry.uid))
 		goto out_unlock;
-	}
 
-	/* Does new_name already exist? */
-	new_ret = umsdos_findentry(new_dentry->d_parent, &new_info, 0);
-	/* if destination file exists, are we allowed to replace it ? */
-	if (new_ret == 0 && is_sticky(new_dir, new_info.entry.uid)) {
-		Printk (("sticky set on new "));
-		goto out_unlock;
+	/*
+	 * Check whether the new_name already exists, and
+	 * if so whether we're allowed to replace it.
+	 */
+	err = umsdos_findentry(new_dentry->d_parent, &new_info, 0);
+	if (err == 0) {
+	 	/* Are we allowed to replace it? */
+		if (is_sticky(new_dir, new_info.entry.uid)) {
+Printk (("sticky set on new "));
+			goto out_unlock;
+		}
+		/* check whether it _really_ exists ... */
+		ret = -EEXIST;
+		if (new_dentry->d_inode)
+			goto out_unlock;
+
+		/* bogus lookup? complain and fix up the EMD ... */
+		printk(KERN_WARNING
+			"umsdos_rename_f: entry %s/%s exists, inode NULL??\n",
+			new_dentry->d_parent->d_name.name, new_info.entry.name);
+		err = umsdos_delentry(new_dentry->d_parent, &new_info,
+					S_ISDIR(new_info.entry.mode));
 	}
 
-	Printk (("new newentry "));
-	umsdos_ren_init (&new_info, &old_info, flags);
+Printk (("new newentry "));
+	/* create the new entry ... */
+	umsdos_ren_init (&new_info, &old_info);
+	if (flags)
+		new_info.entry.flags = flags;
 	ret = umsdos_newentry (new_dentry->d_parent, &new_info);
-	chkstk ();
 	if (ret) {
-Printk (("ret %d %d ", ret, new_info.fake.len));
+		printk(KERN_WARNING
+			"umsdos_rename_f: newentry %s/%s failed, ret=%d\n",
+			new_dentry->d_parent->d_name.name, new_info.entry.name,
+			ret);
 		goto out_unlock;
 	}
 
-	dret = umsdos_lookup_dentry(old_dentry->d_parent, old_info.fake.fname, 
-					old_info.fake.len);
-	ret = PTR_ERR(dret);
-	if (IS_ERR(dret))
+	/* If we're moving a hardlink, drop it first */
+	if (old_info.entry.flags & UMSDOS_HLINK) {
+		rehash = !list_empty(&old_dentry->d_hash);
+		d_drop(old_dentry);
+printk("umsdos_rename_f: moving hardlink %s/%s, rehash=%d\n",
+old_dentry->d_parent->d_name.name, old_info.entry.name, rehash);
+	}
+
+	/* Do a real lookup to get the old short name dentry */
+	old = umsdos_lookup_dentry(old_dentry->d_parent, old_info.fake.fname, 
+					old_info.fake.len, 1);
+	ret = PTR_ERR(old);
+	if (IS_ERR(old)) {
+		printk(KERN_WARNING
+			"umsdos_rename_f: lookup old dentry %s/%s, ret=%d\n",
+			old_dentry->d_parent->d_name.name, old_info.fake.fname,
+			ret);
 		goto out_unlock;
-#if 0
-	/* This is the same as dret */
-	oldid = dret->d_inode;
-	old = creat_dentry (old_info.fake.fname, old_info.fake.len,
-				oldid, old_dentry->d_parent);
-#endif
-	old = dret;
+	}
+	/* short and long name dentries match? */
+	if (old == old_dentry)
+		dput(old);
+
 	new = umsdos_lookup_dentry(new_dentry->d_parent, new_info.fake.fname, 
-					new_info.fake.len);
+					new_info.fake.len, 1);
 	ret = PTR_ERR(new);
-	if (IS_ERR(new))
+	if (IS_ERR(new)) {
+		printk(KERN_WARNING
+			"umsdos_rename_f: lookup new dentry %s/%s, ret=%d\n",
+			new_dentry->d_parent->d_name.name, new_info.fake.fname,
+			ret);
 		goto out_dput;
+	}
+	/*
+	 * Note! If the new short- and long-name dentries are
+	 * aliases, the target name will be destroyed by the
+	 * msdos-level rename. If in addition the old dentries
+	 * _aren't_ aliased, we'll need the original new name
+	 * for the final d_move, and so must make a copy here.
+	 *
+	 * Welcome to the mysteries of the dcache ...
+	 */
+	if (new == new_dentry) {
+		dput(new);
+		if (old != old_dentry) {
+			/* make a copy of the target dentry */
+			ret = -ENOMEM;
+			new_target = d_alloc(new_dentry->d_parent,
+						&new_dentry->d_name);
+			if (!new_target)
+				goto out_dput;
+		}
+	}
 
-	Printk (("msdos_rename "));
-	check_dentry_path (old, "umsdos_rename_f OLD2");
-	check_dentry_path (new, "umsdos_rename_f NEW2");
+	/* Do the msdos-level rename */
 	ret = msdos_rename (old_dir, old, new_dir, new);
-	chkstk ();
-printk("after m_rename ret %d ", ret);
-	/* dput(old); */
-	dput(new);
+Printk(("umsdos_rename_f: now %s/%s, ret=%d\n", 
+old->d_parent->d_name.name, old->d_name.name, ret));
+
+	if (new != new_dentry)
+		dput(new);
 
+	/* If the rename failed, remove the new EMD entry */
 	if (ret != 0) {
+Printk(("umsdos_rename_f: rename failed, ret=%d, removing %s/%s\n",
+ret, new_dentry->d_parent->d_name.name, new_info.entry.name));
 		umsdos_delentry (new_dentry->d_parent, &new_info,
 				 S_ISDIR (new_info.entry.mode));
-		chkstk ();
 		goto out_dput;
 	}
 
-	ret = umsdos_delentry (old_dentry->d_parent, &old_info,
-				 S_ISDIR (old_info.entry.mode));
-	chkstk ();
-	if (ret)
-		goto out_dput;
-#if 0
 	/*
-	 * Update f_pos so notify_change will succeed
-	 * if the file was already in use.
+	 * Rename successful ... remove the old name from the EMD.
+	 * Note that we use the EMD parent here, as the old dentry
+	 * may have moved to a new parent ...
 	 */
-	umsdos_set_dirinfo (new_dentry->d_inode, new_dir, new_info.f_pos);
-#endif
-	if (old_dentry == dret) {
-printk("umsdos_rename_f: old dentries match -- skipping d_move\n");
-		goto out_dput;
+	err = umsdos_delentry (old_emd->d_parent, &old_info,
+				S_ISDIR (old_info.entry.mode));
+	if (err) {
+		/* Failed? Complain a bit, but don't fail the operation */
+		printk(KERN_WARNING 
+			"umsdos_rename_f: delentry %s/%s failed, error=%d\n",
+			old_emd->d_parent->d_name.name, old_info.entry.name,
+			err);
 	}
-	d_move (old_dentry, new_dentry);
 
+	/* dput() the dentry if we haven't already */
 out_dput:
-	dput(dret);
+	if (old_dentry != old)
+		dput(old);
+	if (ret)
+		goto out_unlock;
+	/*
+	 * Check whether to update the dcache ... if both 
+	 * old and new dentries match, it's already correct.
+	 */
+	if (new_dentry != new) {
+		d_move(old_dentry, new_dentry);
+	} else if (old_dentry != old) {
+		/* new dentry was destroyed ... */
+		d_drop(new_dentry);
+		d_add(new_target, NULL);
+		d_move(old_dentry, new_target);
+	}
+
+	/*
+	 * Update f_pos so notify_change will succeed
+	 * if the file was already in use.
+	 */
+	umsdos_set_dirinfo_new(old_dentry, new_info.f_pos);
 
 out_unlock:
-	Printk ((KERN_DEBUG "umsdos_rename_f: unlocking dirs...\n"));
+	dput(new_target);
+	dput(old_emd);
 	umsdos_unlockcreate (old_dir);
 	umsdos_unlockcreate (new_dir);
 
 out:
-	check_dentry_path (old_dentry, "umsdos_rename_f OLD3");
-	check_dentry_path (new_dentry, "umsdos_rename_f NEW3");
 	Printk ((" _ret=%d\n", ret));
 	return ret;
 }
@@ -509,11 +575,11 @@
  * Return a negative error code or 0 if OK.
  */
 /* #Specification: symbolic links / strategy
- * A symbolic link is simply a file which hold a path. It is
+ * A symbolic link is simply a file which holds a path. It is
  * implemented as a normal MSDOS file (not very space efficient :-()
  * 
- * I see 2 different way to do it. One is to place the link data
- * in unused entry of the EMD file. The other is to have a separate
+ * I see two different ways to do this: One is to place the link data
+ * in unused entries of the EMD file; the other is to have a separate
  * file dedicated to hold all symbolic links data.
  * 
  * Let's go for simplicity...
@@ -525,37 +591,33 @@
 	int ret, len;
 	struct file filp;
 
+Printk(("umsdos_symlink: %s/%s to %s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, symname));
+
 	ret = umsdos_create_any (dir, dentry, mode, 0, flags);
 	if (ret) {
-Printk (("umsdos_symlink ret %d ", ret));
+printk("umsdos_symlink: create failed, ret=%d\n", ret);
 		goto out;
 	}
 
-	len = strlen (symname);
-
 	fill_new_filp (&filp, dentry);
-	filp.f_pos = 0;
-
-	/* Make the inode acceptable to MSDOS FIXME */
-Printk ((KERN_WARNING "   symname=%s ; dentry name=%.*s (ino=%lu)\n",
-symname, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino));
+	len = strlen (symname);
 	ret = umsdos_file_write_kmem_real (&filp, symname, len);
-	
 	if (ret >= 0) {
 		if (ret != len) {
 			ret = -EIO;
-			printk ("UMSDOS: "
-			     "Can't write symbolic link data\n");
+			printk (KERN_WARNING
+				"UMSDOS: Can't write symbolic link data\n");
 		} else {
 			ret = 0;
 		}
 	}
 	if (ret != 0) {
+printk("umsdos_symlink: write failed, unlinking\n");
 		UMSDOS_unlink (dir, dentry);
 	}
 
 out:
-	Printk (("\n"));
 	return ret;
 }
 
@@ -576,13 +638,20 @@
 		 struct dentry *dentry)
 {
 	struct inode *oldinode = olddentry->d_inode;
-	struct inode *olddir;
-	char *path;
+	struct inode *olddir = olddentry->d_parent->d_inode;
 	struct dentry *temp;
+	char *path;
 	unsigned long buffer;
 	int ret;
-	struct umsdos_dirent entry;
+	struct umsdos_info old_info;
+	struct umsdos_info hid_info;
 
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_link: new %s%s -> %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, 
+olddentry->d_parent->d_name.name, olddentry->d_name.name);
+#endif
+ 
 	ret = -EPERM;
 	if (S_ISDIR (oldinode->i_mode))
 		goto out;
@@ -596,48 +665,141 @@
 	if (!buffer)
 		goto out;
 
-	olddir = olddentry->d_parent->d_inode;
 	umsdos_lockcreate2 (dir, olddir);
 
-	/* get the entry for the old name */
-	ret = umsdos_dentry_to_entry(olddentry, &entry);
+	/*
+	 * Parse the name and get the visible directory entry.
+	 */
+	ret = umsdos_parse (olddentry->d_name.name, olddentry->d_name.len,
+				&old_info);
 	if (ret)
 		goto out_unlock;
-Printk (("umsdos_link :%.*s: ino %lu flags %d ",
-entry.name_len, entry.name ,oldinode->i_ino, entry.flags));
+	ret = umsdos_findentry (olddentry->d_parent, &old_info, 1);
+	if (ret) {
+printk("UMSDOS_link: %s/%s not in EMD, ret=%d\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, ret);
+		goto out_unlock;
+	}
 
-	if (!(entry.flags & UMSDOS_HIDDEN)) {
-		struct umsdos_info info;
+	/*
+	 * If the visible dentry is a pseudo-hardlink, the original
+	 * file must be already hidden.
+	 */
+	if (!(old_info.entry.flags & UMSDOS_HLINK)) {
+		int err;
 
-		ret = umsdos_newhidden (olddentry->d_parent, &info);
-		if (ret)
+		/* create a hidden link name */
+		ret = umsdos_newhidden (olddentry->d_parent, &hid_info);
+		if (ret) {
+printk("umsdos_link: can't make hidden %s/%s, ret=%d\n",
+olddentry->d_parent->d_name.name, hid_info.entry.name, ret);
 			goto out_unlock;
+		}
 
-		ret = umsdos_rename_f (olddentry->d_inode, olddentry, 
-					dir, dentry, UMSDOS_HIDDEN);
-		if (ret)
-			goto out_unlock;
-		path = d_path(olddentry, (char *) buffer, PAGE_SIZE);
-		if (!path)
-			goto out_unlock;
-		temp = umsdos_lookup_dentry(olddentry->d_parent, entry.name,
-						entry.name_len); 
-		if (IS_ERR(temp))
-			goto out_unlock;
-		ret = umsdos_symlink_x (olddir, temp, path, 
-					S_IFREG | 0777, UMSDOS_HLINK);
-		if (ret == 0) {
-			ret = umsdos_symlink_x (dir, dentry, path,
-					S_IFREG | 0777, UMSDOS_HLINK);
+		/*
+		 * Make a dentry and rename the original file ...
+		 */
+		temp = umsdos_lookup_dentry(olddentry->d_parent,
+						hid_info.entry.name,
+						hid_info.entry.name_len, 0); 
+		ret = PTR_ERR(temp);
+		if (IS_ERR(temp)) {
+printk("umsdos_link: lookup %s/%s failed, ret=%d\n",
+dentry->d_parent->d_name.name, hid_info.entry.name, ret);
+			goto cleanup;
 		}
+		/* rename the link to the hidden location ... */
+		ret = umsdos_rename_f (olddir, olddentry, olddir, temp,
+					UMSDOS_HIDDEN);
 		dput(temp);
+		if (ret) {
+printk("umsdos_link: rename to %s/%s failed, ret=%d\n",
+temp->d_parent->d_name.name, temp->d_name.name, ret);
+			goto cleanup;
+		}
+		/* mark the inode as a hardlink */
+		oldinode->u.umsdos_i.i_is_hlink = 1;
+
+		/*
+		 * Capture the path to the hidden link.
+		 */
+		path = umsdos_d_path(olddentry, (char *) buffer, PAGE_SIZE);
+Printk(("umsdos_link: hidden link path=%s\n", path));
+
+		/*
+		 * Recreate a dentry for the original name and symlink it,
+		 * then symlink the new dentry. Don't give up if one fails,
+		 * or we'll lose the file completely!
+		 *
+		 * Note: this counts as the "original" reference, so we 
+		 * don't increment i_nlink for this one.
+		 */ 
+		temp = umsdos_lookup_dentry(olddentry->d_parent,
+						old_info.entry.name,
+						old_info.entry.name_len, 0); 
+		ret = PTR_ERR(temp);
+		if (!IS_ERR(temp)) {
+			ret = umsdos_symlink_x (olddir, temp, path, 
+						S_IFREG | 0777, UMSDOS_HLINK);
+			dput(temp);
+		}
+
+		/* This symlink increments i_nlink (see below.) */
+		err = umsdos_symlink_x (dir, dentry, path,
+					S_IFREG | 0777, UMSDOS_HLINK);
+		/* fold the two errors */
+		if (!ret)
+			ret = err;
+		goto out_unlock;
+
+		/* creation failed ... remove the link entry */
+	cleanup:
+printk("umsdos_link: link failed, ret=%d, removing %s/%s\n",
+ret, olddentry->d_parent->d_name.name, hid_info.entry.name);
+		err = umsdos_delentry(olddentry->d_parent, &hid_info, 0);
 		goto out_unlock;
 	}
-	path = d_path(olddentry, (char *) buffer, PAGE_SIZE);
-	if (path) {
-		ret = umsdos_symlink_x (dir, dentry, path, 
-				S_IFREG | 0777, UMSDOS_HLINK);
-	}
+
+Printk(("UMSDOS_link: %s/%s already hidden\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name));
+	/*
+	 * The original file is already hidden, and we need to get 
+	 * the dentry for its real name, not the visible name.
+	 * N.B. make sure it's the hidden inode ...
+	 */
+	if (!oldinode->u.umsdos_i.i_is_hlink)
+		printk("UMSDOS_link: %s/%s hidden, ino=%ld not hlink??\n",
+			olddentry->d_parent->d_name.name,
+			olddentry->d_name.name, oldinode->i_ino);
+
+	/*
+	 * In order to get the correct (real) inode, we just drop
+	 * the original dentry.
+	 */ 
+	d_drop(olddentry);
+Printk(("UMSDOS_link: hard link %s/%s, fake=%s\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, old_info.fake.fname));
+
+	/* Do a real lookup to get the short name dentry */
+	temp = umsdos_lookup_dentry(olddentry->d_parent,
+					old_info.fake.fname, 
+					old_info.fake.len, 1);
+	ret = PTR_ERR(temp);
+	if (IS_ERR(temp))
+		goto out_unlock;
+
+	/* now resolve the link ... */
+	temp = umsdos_solve_hlink(temp);
+	ret = PTR_ERR(temp);
+	if (IS_ERR(temp))
+		goto out_unlock;
+	path = umsdos_d_path(temp, (char *) buffer, PAGE_SIZE);
+	dput(temp);
+Printk(("umsdos_link: %s/%s already hidden, path=%s\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, path));
+
+	/* finally we can symlink it ... */
+	ret = umsdos_symlink_x (dir, dentry, path, S_IFREG | 0777,UMSDOS_HLINK);
 
 out_unlock:
 	umsdos_unlockcreate (olddir);
@@ -647,7 +809,15 @@
 	if (ret == 0) {
 		struct iattr newattrs;
 
+#ifdef UMSDOS_PARANOIA
+if (!oldinode->u.umsdos_i.i_is_hlink)
+printk("UMSDOS_link: %s/%s, ino=%ld, not marked as hlink!\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, oldinode->i_ino);
+#endif
 		oldinode->i_nlink++;
+Printk(("UMSDOS_link: linked %s/%s, ino=%ld, nlink=%d\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name,
+oldinode->i_ino, oldinode->i_nlink));
 		newattrs.ia_valid = 0;
 		ret = UMSDOS_notify_change (olddentry, &newattrs);
 	}
@@ -657,24 +827,6 @@
 
 
 /*
- * Add a new file into the alternate directory.
- * The file is added to the real MSDOS directory. If successful, it
- * is then added to the EMD file.
- * 
- * Return the status of the operation. 0 mean success.
- */
-int UMSDOS_create (struct inode *dir, struct dentry *dentry, int mode)
-{
-	int ret;
-	Printk ((KERN_ERR "UMSDOS_create: entering\n"));
-	check_dentry_path (dentry, "UMSDOS_create START");
-	ret = umsdos_create_any (dir, dentry, mode, 0, 0);
-	check_dentry_path (dentry, "UMSDOS_create END");
-	return ret;
-}
-
-
-/*
  * Add a sub-directory in a directory
  */
 /* #Specification: mkdir / Directory already exist in DOS
@@ -699,10 +851,8 @@
 		goto out;
 
 	ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
-	if (ret) {
-Printk (("umsdos_mkdir %d\n", ret));
+	if (ret)
 		goto out;
-	}
 
 	umsdos_lockcreate (dir);
 	info.entry.mode = mode | S_IFDIR;
@@ -713,14 +863,12 @@
 	info.entry.flags = 0;
 	info.entry.nlink = 1;
 	ret = umsdos_newentry (dentry->d_parent, &info);
-	if (ret) {
-Printk (("newentry %d ", ret));
+	if (ret)
 		goto out_unlock;
-	}
 
 	/* lookup the short name dentry */
 	temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, 
-					info.fake.len);
+					info.fake.len, 1);
 	ret = PTR_ERR(temp);
 	if (IS_ERR(temp))
 		goto out_unlock;
@@ -737,36 +885,27 @@
 	if (ret)
 		goto out_remove;
 
-	inode = temp->d_inode;
-	umsdos_lookup_patch_new(temp, &info.entry, info.f_pos);
-
 	/*
 	 * Note! The long and short name might be the same,
 	 * so check first before doing the instantiate ...
 	 */
 	if (dentry != temp) {
-		if (!dentry->d_inode) {
-			inode->i_count++;
-			d_instantiate(dentry, inode);
-		} else {
-			printk("umsdos_mkdir: not negative??\n");
-		}
-	} else {
-		printk("umsdos_mkdir: dentries match, skipping inst\n");
+if (dentry->d_inode)
+printk("umsdos_mkdir: dentry not negative!\n");
+		inode = temp->d_inode;
+		inode->i_count++;
+		d_instantiate(dentry, inode);
 	}
-
-	/* create the EMD file */
-	err = umsdos_make_emd(dentry);
+	umsdos_lookup_patch_new(dentry, &info.entry, info.f_pos);
 
 	/* 
-	 * set up the dir so it is promoted to EMD,
-	 * with the EMD file invisible inside it.
+	 * Create the EMD file, and set up the dir so it is
+	 * promoted to EMD with the EMD file invisible.
+	 *
+	 * N.B. error return if EMD fails?
 	 */
-	umsdos_setup_dir(temp);
-	goto out_dput;
-
-out_remove:
-	umsdos_delentry (dentry->d_parent, &info, 1);
+	err = umsdos_make_emd(dentry);
+	umsdos_setup_dir(dentry);
 
 out_dput:
 	/* kill off the short name dentry */ 
@@ -776,9 +915,14 @@
 
 out_unlock:
 	umsdos_unlockcreate (dir);
-	Printk (("umsdos_mkdir %d\n", ret));
 out:
+	Printk (("umsdos_mkdir %d\n", ret));
 	return ret;
+
+	/* an error occurred ... remove EMD entry. */
+out_remove:
+	umsdos_delentry (dentry->d_parent, &info, 1);
+	goto out_dput;
 }
 
 /*
@@ -801,11 +945,7 @@
 int UMSDOS_mknod (struct inode *dir, struct dentry *dentry,
 		 int mode, int rdev)
 {
-	int ret;
-	check_dentry_path (dentry, "UMSDOS_mknod START");
-	ret = umsdos_create_any (dir, dentry, mode, rdev, 0);
-	check_dentry_path (dentry, "UMSDOS_mknod END");
-	return ret;
+	return umsdos_create_any (dir, dentry, mode, rdev, 0);
 }
 
 /*
@@ -821,13 +961,6 @@
 	if (ret)
 		goto out;
 
-#if 0	/* no need for lookup ... we have a dentry ... */
-	ret = umsdos_lookup_x (dir, dentry, 0);
-	Printk (("rmdir lookup %d ", ret));
-	if (ret != 0)
-		goto out;
-#endif
-
 	umsdos_lockcreate (dir);
 	ret = -EBUSY;
 	if (dentry->d_count > 1) {
@@ -839,6 +972,14 @@
 		}
 	}
 
+	/* check the sticky bit */
+	ret = -EPERM;
+	if (is_sticky(dir, dentry->d_inode->i_uid)) {
+printk("umsdos_rmdir: %s/%s is sticky\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+		goto out_unlock;
+	}
+
 	/* check whether the EMD is empty */
 	empty = umsdos_isempty (dentry);
 	ret = -ENOTEMPTY;
@@ -848,24 +989,21 @@
 	/* Have to remove the EMD file? */
 	if (empty == 1) {
 		struct dentry *demd;
-		/* check sticky bit */
-		ret = -EPERM;
-		if (is_sticky(dir, dentry->d_inode->i_uid)) {
-printk("umsdos_rmdir: %s/%s is sticky\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-			goto out_unlock;
-		}
+
+Printk (("UMSDOS_rmdir: unlinking empty EMD err=%d", err));
 
 		ret = -ENOTEMPTY;
 		/* see if there's an EMD file ... */
 		demd = umsdos_get_emd_dentry(dentry);
 		if (IS_ERR(demd))
 			goto out_unlock;
-printk("umsdos_rmdir: got EMD dentry %s/%s, inode=%p\n",
-demd->d_parent->d_name.name, demd->d_name.name, demd->d_inode);
 
 		err = msdos_unlink (dentry->d_inode, demd);
-Printk (("UMSDOS_rmdir: unlinking empty EMD err=%d", err));
+#ifdef UMSDOS_PARANOIA
+if (err)
+printk("umsdos_rmdir: EMD %s/%s unlink failed, err=%d\n",
+demd->d_parent->d_name.name, demd->d_name.name, err);
+#endif
 		dput(demd);
 		if (err)
 			goto out_unlock;
@@ -875,7 +1013,7 @@
 	/* Call findentry to complete the mangling */
 	umsdos_findentry (dentry->d_parent, &info, 2);
 	temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, 
-					info.fake.len);
+					info.fake.len, 1);
 	ret = PTR_ERR(temp);
 	if (IS_ERR(temp))
 		goto out_unlock;
@@ -884,8 +1022,8 @@
 	 */
 	if (temp == dentry) {
 		dput(temp);
-printk("umsdos_rmdir: %s/%s, short matches long\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
+Printk(("umsdos_rmdir: %s/%s, short matches long\n",
+dentry->d_parent->d_name.name, dentry->d_name.name));
 	}
 
 	/*
@@ -897,16 +1035,18 @@
 
 	/* OK so far ... remove the name from the EMD */
 	ret = umsdos_delentry (dentry->d_parent, &info, 1);
+#ifdef UMSDOS_PARANOIA
+if (ret)
+printk("umsdos_rmdir: delentry %s failed, ret=%d\n", info.entry.name, ret);
+#endif
 
-out_dput:
 	/* dput() temp if we didn't do it above */
+out_dput:
 	if (temp != dentry) {
 		d_drop(temp);
 		dput(temp);
 		if (!ret)
 			d_delete (dentry);
-printk("umsdos_rmdir: %s/%s, short=%s dput\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname);
 	}
 
 out_unlock:
@@ -922,8 +1062,8 @@
  * Remove a file from the directory.
  *
  * #Specification: hard link / deleting a link
- * When we delete a file, and this file is a link
- * we must subtract 1 to the nlink field of the
+ * When we delete a file and this file is a link,
+ * we must subtract 1 from the nlink field of the
  * hidden link.
  * 
  * If the count goes to 0, we delete this hidden
@@ -931,12 +1071,12 @@
  */
 int UMSDOS_unlink (struct inode *dir, struct dentry *dentry)
 {
-	struct dentry *temp;
+	struct dentry *temp, *link = NULL;
 	struct inode *inode;
-	int ret;
+	int ret, rehash = 0;
 	struct umsdos_info info;
 
-Printk (("UMSDOS_unlink: entering %s/%s\n",
+Printk(("UMSDOS_unlink: entering %s/%s\n",
 dentry->d_parent->d_name.name, dentry->d_name.name));
 
 	ret = umsdos_nevercreat (dir, dentry, -EPERM);
@@ -950,11 +1090,13 @@
 	umsdos_lockcreate (dir);
 	ret = umsdos_findentry (dentry->d_parent, &info, 1);
 	if (ret) {
-printk("UMSDOS_unlink: findentry returned %d\n", ret);
+printk("UMSDOS_unlink: %s/%s not in EMD, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
 		goto out_unlock;
 	}
 
 Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname));
+
 	ret = -EPERM;
 	/* check sticky bit */
 	if (is_sticky(dir, info.entry.uid)) {
@@ -963,65 +1105,54 @@
 		goto out_unlock;
 	}
 
-	ret = 0;
+	/*
+	 * Note! If this is a hardlink and the names are aliased,
+	 * the short-name lookup will return the hardlink dentry.
+	 * In order to get the correct (real) inode, we just drop
+	 * the original dentry.
+	 */ 
 	if (info.entry.flags & UMSDOS_HLINK) {
-printk("UMSDOS_unlink: hard link %s/%s, fake=%s\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname);
-		/*
-		 * First, get the inode of the hidden link
-		 * using the standard lookup function.
-		 */
-
-		ret = umsdos_lookup_x (dir, dentry, 0);
-		inode = dentry->d_inode;
-		if (ret)
-			goto out_unlock;
-
-		Printk (("unlink nlink = %d ", inode->i_nlink));
-		inode->i_nlink--;
-		if (inode->i_nlink == 0) {
-			struct umsdos_dirent entry;
-
-			ret = umsdos_dentry_to_entry (dentry, &entry);
-			if (ret == 0) {
-				ret = UMSDOS_unlink (dentry->d_parent->d_inode,
-							dentry);
-			}
-		} else {
-			struct iattr newattrs;
-			newattrs.ia_valid = 0;
-			ret = UMSDOS_notify_change (dentry, &newattrs);
-		}
+		rehash = !list_empty(&dentry->d_hash);
+		d_drop(dentry);
+Printk(("UMSDOS_unlink: hard link %s/%s, fake=%s, rehash=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname, rehash));
 	}
-	if (ret)
-		goto out_unlock;
 
-	/* get the short name dentry */
+	/* Do a real lookup to get the short name dentry */
 	temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, 
-					info.fake.len);
+					info.fake.len, 1);
+	ret = PTR_ERR(temp);
 	if (IS_ERR(temp))
 		goto out_unlock;
 
 	/*
-	 * If the short name matches the long,
-	 * dput() it now so it's not busy.
+	 * Resolve hardlinks now, but defer processing until later.
 	 */
-	if (temp == dentry) {
-printk("UMSDOS_unlink: %s/%s, short matches long\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-		dput(temp);
+	if (info.entry.flags & UMSDOS_HLINK) {
+		link = umsdos_solve_hlink(dget(temp));
 	}
 
+	/*
+	 * If the short and long names are aliased,
+	 * dput() it now so the dentry isn't busy.
+	 */
+	if (temp == dentry)
+		dput(temp);
+
+	/* Delete the EMD entry */
 	ret = umsdos_delentry (dentry->d_parent, &info, 0);
-	if (ret && ret != -ENOENT)
+	if (ret && ret != -ENOENT) {
+		printk(KERN_WARNING "UMSDOS_unlink: delentry %s, error=%d\n",
+			info.entry.name, ret);
 		goto out_dput;
+	}
 
-printk("UMSDOS: Before msdos_unlink %.*s ",
-info.fake.len, info.fake.fname);
 	ret = msdos_unlink_umsdos (dir, temp);
-
-Printk (("msdos_unlink %.*s %o ret %d ",
-info.fake.len, info.fake.fname ,info.entry.mode, ret));
+#ifdef UMSDOS_PARANOIA
+if (ret)
+printk("umsdos_unlink: %s/%s unlink failed, ret=%d\n",
+temp->d_parent->d_name.name, temp->d_name.name, ret);
+#endif
 
 	/* dput() temp if we didn't do it above */
 out_dput:
@@ -1030,12 +1161,60 @@
 		dput(temp);
 		if (!ret)
 			d_delete (dentry);
-printk("umsdos_unlink: %s/%s, short=%s dput\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname);
 	}
 
 out_unlock:
 	umsdos_unlockcreate (dir);
+
+	/*
+	 * Now check for deferred handling of a hardlink.
+	 */
+	if (!link)
+		goto out;
+
+	if (IS_ERR(link)) {
+printk("umsdos_unlink: failed to resolve %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+		if (!ret)
+			ret = PTR_ERR(link);
+		goto out;
+	}
+
+Printk(("umsdos_unlink: link %s/%s deferred, pending ret=%d\n",
+link->d_parent->d_name.name, link->d_name.name, ret));
+
+	/* already have an error? */
+	if (ret)
+		goto out_cleanup;
+
+	/* make sure the link exists ... */
+	inode = link->d_inode;
+	if (!inode) {
+		printk(KERN_WARNING "umsdos_unlink: hard link not found\n");
+		goto out_cleanup;
+	}
+
+	/*
+	 * If this was the last linked reference, delete it now.
+	 */
+	if (inode->i_nlink <= 1) {
+		ret = UMSDOS_unlink (link->d_parent->d_inode, link);
+		if (ret) {
+			printk(KERN_WARNING
+				"umsdos_unlink: link removal failed, ret=%d\n",
+				 ret);
+		}
+	} else {
+		struct iattr newattrs;
+		inode->i_nlink--;
+		newattrs.ia_valid = 0;
+		ret = UMSDOS_notify_change (link, &newattrs);
+	}
+
+out_cleanup:
+	d_drop(link);
+	dput(link);
+
 out:
 	Printk (("umsdos_unlink %d\n", ret));
 	return ret;
@@ -1058,20 +1237,28 @@
 	if (ret != -EEXIST)
 		goto out;
 
+	/*
+	 * Something seems to be giving negative lookups when
+	 * the file really exists ... track this down!
+	 */
+	ret = -EIO;
+	if (!new_dentry->d_inode) {
+printk("UMSDOS_rename: %s/%s negative, error EEXIST??\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
+		d_drop(new_dentry);
+		goto out;
+	}
+
 	/* This is not terribly efficient but should work. */
-	ret = UMSDOS_unlink (new_dir, new_dentry);
-	chkstk ();
-	Printk (("rename unlink ret %d -- ", ret));
-	if (ret == -EISDIR) {
+	if (S_ISDIR(new_dentry->d_inode->i_mode))
 		ret = UMSDOS_rmdir (new_dir, new_dentry);
-		chkstk ();
-		Printk (("rename rmdir ret %d -- ", ret));
-	}
+	else
+		ret = UMSDOS_unlink (new_dir, new_dentry);
 	if (ret)
 		goto out;
 
 	/* this time the rename should work ... */
-	ret = umsdos_rename_f (old_dir, old_dentry, new_dir, new_dentry, 0);
+	ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_dentry, 0);
 
 out:
 	return ret;
diff -u --recursive --new-file v2.1.121/linux/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c
--- v2.1.121/linux/fs/umsdos/rdir.c	Wed Sep  9 14:51:09 1998
+++ linux/fs/umsdos/rdir.c	Mon Sep 14 17:22:25 1998
@@ -20,6 +20,7 @@
 
 
 extern struct inode *pseudo_root;
+extern struct dentry_operations umsdos_dentry_operations;
 
 struct RDIR_FILLDIR {
 	void *dirbuf;
@@ -96,9 +97,14 @@
 		goto out;
 	}
 
-	ret = umsdos_real_lookup (dir, dentry);
+	ret = msdos_lookup (dir, dentry);
+	if (ret) {
+		printk(KERN_WARNING "umsdos_rlookup_x: lookup failed, ret=%d\n",
+			ret);
+		goto out;
+	}
 	inode = dentry->d_inode;
-	if ((ret == 0) && inode) {
+	if (inode) {
 		if (inode == pseudo_root && !nopseudo) {
 			/* #Specification: pseudo root / DOS/linux
 			 * Even in the real root directory (c:\), the directory
@@ -119,6 +125,8 @@
 		}
 	}
 out:
+	/* always install our dentry ops ... */
+	dentry->d_op = &umsdos_dentry_operations;
 	PRINTK ((KERN_DEBUG "umsdos_rlookup_x: returning %d\n", ret));
 	return ret;
 }
@@ -170,28 +178,19 @@
 
 	ret = msdos_rmdir (dir, dentry);
 	if (ret != -ENOTEMPTY)
-		goto out_check;
-
-#if 0	/* why do this? we have the dentry ... */
-	ret = UMSDOS_rlookup (dir, dentry);
-	PRINTK (("rrmdir lookup %d ", ret));
-	if (ret)
 		goto out_unlock;
-	ret = -ENOTEMPTY;
-#endif
 
 	empty = umsdos_isempty (dentry);
 	if (empty == 1) {
-		struct dentry *temp;
+		struct dentry *demd;
 		/* We have to remove the EMD file. */
-		temp = umsdos_lookup_dentry(dentry, UMSDOS_EMD_FILE, 
-						UMSDOS_EMD_NAMELEN);
-		ret = PTR_ERR(temp);
-		if (!IS_ERR(temp)) {
+		demd = umsdos_get_emd_dentry(dentry);
+		ret = PTR_ERR(demd);
+		if (!IS_ERR(demd)) {
 			ret = 0;
-			if (temp->d_inode)
-				ret = msdos_unlink (dentry->d_inode, temp);
-			dput(temp);
+			if (demd->d_inode)
+				ret = msdos_unlink (dentry->d_inode, demd);
+			dput(demd);
 		}
 		if (ret)
 			goto out_unlock;
@@ -199,15 +198,9 @@
 	/* now retry the original ... */
 	ret = msdos_rmdir (dir, dentry);
 
-out_check:
-	/* Check whether we succeeded ... */
-	if (!ret)
-		d_delete(dentry);
-
 out_unlock:
 	umsdos_unlockcreate (dir);
 out:
-	check_inode (dir);
 	return ret;
 }
 
diff -u --recursive --new-file v2.1.121/linux/fs/umsdos/specs linux/fs/umsdos/specs
--- v2.1.121/linux/fs/umsdos/specs	Wed Sep  9 14:51:09 1998
+++ linux/fs/umsdos/specs	Fri Sep 11 11:26:56 1998
@@ -147,6 +147,7 @@
  * same target at the same time. Again, I am not sure it
  * is a problem at all.
  */
+
 /* #Specification: hard link / strategy
  * Hard links are difficult to implement on top of an MS-DOS FAT file
  * system. Unlike Unix file systems, there are no inodes. A directory
@@ -180,6 +181,7 @@
  * The entry -LINK1 will be hidden. It will hold a link count.
  * When all link are erased, the hidden file is erased too.
  */
+
 /* #Specification: weakness / hard link
  * The strategy for hard link introduces a side effect that
  * may or may not be acceptable. Here is the sequence
diff -u --recursive --new-file v2.1.121/linux/fs/umsdos/symlink.c linux/fs/umsdos/symlink.c
--- v2.1.121/linux/fs/umsdos/symlink.c	Sat Sep  5 16:46:41 1998
+++ linux/fs/umsdos/symlink.c	Fri Sep 11 11:26:56 1998
@@ -32,52 +32,26 @@
 			     char *buffer,
 			     int bufsiz)
 {
-	int ret;
+	size_t size = dentry->d_inode->i_size;
 	loff_t loffs = 0;
+	ssize_t ret;
 	struct file filp;
 
-	ret = dentry->d_inode->i_size;
-
-	if (!(dentry->d_inode)) {
-		return -EBADF;
-	}
+Printk((KERN_DEBUG "UMSDOS_read: %s/%s, size=%u\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, size));
 
 	fill_new_filp (&filp, dentry);
-
 	filp.f_reada = 0;
 	filp.f_flags = O_RDONLY;
-	filp.f_op = &umsdos_symlink_operations;		/* /mn/ - we have to fill it with dummy values so we won't segfault */
-
-	if (ret > bufsiz)
-		ret = bufsiz;
-
-	PRINTK ((KERN_DEBUG "umsdos_readlink_x /mn/: Checkin: filp=%p, buffer=%p, size=%d, offs=%Lu\n", &filp, buffer, ret, loffs));
-	PRINTK ((KERN_DEBUG "  f_op=%p\n", filp.f_op));
-	PRINTK ((KERN_DEBUG "  inode=%lu, i_size=%lu\n", filp.f_dentry->d_inode->i_ino, filp.f_dentry->d_inode->i_size));
-	PRINTK ((KERN_DEBUG "  f_pos=%Lu\n", filp.f_pos));
-	PRINTK ((KERN_DEBUG "  name=%.*s\n", (int) filp.f_dentry->d_name.len, filp.f_dentry->d_name.name));
-	PRINTK ((KERN_DEBUG "  i_binary(sb)=%d\n", MSDOS_I (filp.f_dentry->d_inode)->i_binary));
-	PRINTK ((KERN_DEBUG "  f_count=%d, f_flags=%d\n", filp.f_count, filp.f_flags));
-	PRINTK ((KERN_DEBUG "  f_owner=%d\n", filp.f_owner.uid));
-	PRINTK ((KERN_DEBUG "  f_version=%ld\n", filp.f_version));
-	PRINTK ((KERN_DEBUG "  f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp.f_reada, filp.f_ramax, filp.f_raend, filp.f_ralen, filp.f_rawin));
+	filp.f_op    = &umsdos_symlink_operations;
 
+	if (size > bufsiz)
+		size = bufsiz;
 
-	PRINTK ((KERN_DEBUG "umsdos_readlink_x: FIXME /mn/: running fat_file_read (%p, %p, %d, %Lu)\n", &filp, buffer, ret, loffs));
-	if (fat_file_read (&filp, buffer, (size_t) ret, &loffs) != ret) {
+	ret = fat_file_read (&filp, buffer, size, &loffs);
+	if (ret != size) {
 		ret = -EIO;
 	}
-#if 0
-	{
-		struct umsdos_dirent *mydirent = buffer;
-
-		PRINTK ((KERN_DEBUG "  (DDD) uid=%d\n", mydirent->uid));
-		PRINTK ((KERN_DEBUG "  (DDD) gid=%d\n", mydirent->gid));
-		PRINTK ((KERN_DEBUG "  (DDD) name=>%.20s<\n", mydirent->name));
-	}
-#endif
-
-	PRINTK ((KERN_DEBUG "umsdos_readlink_x: FIXME /mn/: fat_file_read returned offs=%Lu ret=%d\n", loffs, ret));
 	return ret;
 }
 
@@ -85,48 +59,38 @@
 
 static int UMSDOS_readlink (struct dentry *dentry, char *buffer, int buflen)
 {
-	int ret;
-
-	PRINTK ((KERN_DEBUG "UMSDOS_readlink: calling umsdos_readlink_x for %.*s\n", (int) dentry->d_name.len, dentry->d_name.name));
-
-	ret = umsdos_readlink_x (dentry, buffer, buflen);
-	PRINTK ((KERN_DEBUG "readlink %d bufsiz %d\n", ret, buflen));
-	/* dput(dentry); / * FIXME /mn/? It seems it is unneeded. d_count is not changed by umsdos_readlink_x */
-
-	Printk ((KERN_WARNING "UMSDOS_readlink /mn/: FIXME! skipped dput(dentry). returning %d\n", ret));
-	return ret;
-
+	return umsdos_readlink_x (dentry, buffer, buflen);
 }
 
 /* this one mostly stolen from romfs :) */
-static struct dentry *UMSDOS_followlink (struct dentry *dentry, struct dentry *base)
+static struct dentry *UMSDOS_followlink (struct dentry *dentry, 
+					struct dentry *base)
 {
 	struct inode *inode = dentry->d_inode;
-	char *symname = NULL;
+	char *symname;
 	int len, cnt;
 	mm_segment_t old_fs = get_fs ();
 
-	Printk ((KERN_DEBUG "UMSDOS_followlink /mn/: (%.*s/%.*s)\n", (int) dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, (int) dentry->d_name.len, dentry->d_name.name));
+Printk((KERN_DEBUG "UMSDOS_followlink /mn/: (%s/%s)\n",
+dentry->d_parent->d_name.name, dentry->d_name.name));
 
 	len = inode->i_size;
 
 	if (!(symname = kmalloc (len + 1, GFP_KERNEL))) {
-		dentry = ERR_PTR (-EAGAIN);	/* correct? */
+		dentry = ERR_PTR (-ENOMEM);
 		goto outnobuf;
 	}
+
 	set_fs (KERNEL_DS);	/* we read into kernel space this time */
-	PRINTK ((KERN_DEBUG "UMSDOS_followlink /mn/: Here goes umsdos_readlink_x %p, %p, %d\n", dentry, symname, len));
 	cnt = umsdos_readlink_x (dentry, symname, len);
-	PRINTK ((KERN_DEBUG "UMSDOS_followlink /mn/: back from umsdos_readlink_x %p, %p, %d!\n", dentry, symname, len));
 	set_fs (old_fs);
-	Printk ((KERN_DEBUG "UMSDOS_followlink /mn/: link name is %.*s with len %d\n", cnt, symname, cnt));
 
 	if (len != cnt) {
 		dentry = ERR_PTR (-EIO);
 		goto out;
-	} else
-		symname[len] = 0;
+	}
 
+	symname[len] = 0;
 	dentry = lookup_dentry (symname, base, 1);
 	kfree (symname);
 
@@ -139,7 +103,7 @@
 	return dentry;
 }
 
-
+/* needed to patch the file structure */
 static struct file_operations umsdos_symlink_operations =
 {
 	NULL,			/* lseek - default */
@@ -158,7 +122,7 @@
 
 struct inode_operations umsdos_symlink_inode_operations =
 {
-	NULL,			/* default file operations */
+	NULL,			/* default file operations (none) */
 	NULL,			/* create */
 	NULL,			/* lookup */
 	NULL,			/* link */
@@ -169,14 +133,14 @@
 	NULL,			/* mknod */
 	NULL,			/* rename */
 	UMSDOS_readlink,	/* readlink */
-	UMSDOS_followlink,	/* followlink *//* /mn/ is this REALLY needed ? I recall seeing it working w/o it... */
-	generic_readpage,	/* readpage *//* in original NULL. changed to generic_readpage. FIXME? /mn/ */
+	UMSDOS_followlink,	/* followlink */
+	generic_readpage,	/* readpage */
 	NULL,			/* writepage */
-	fat_bmap,		/* bmap *//* in original NULL. changed to fat_bmap. FIXME? /mn/ */
+	fat_bmap,		/* bmap */
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */
 	NULL,			/* updatepage */
 	NULL			/* revalidate */
-
 };
+
diff -u --recursive --new-file v2.1.121/linux/include/asm-i386/checksum.h linux/include/asm-i386/checksum.h
--- v2.1.121/linux/include/asm-i386/checksum.h	Sat Sep  5 16:46:41 1998
+++ linux/include/asm-i386/checksum.h	Mon Sep 14 22:52:10 1998
@@ -27,22 +27,25 @@
 unsigned int csum_partial_copy_generic( const char *src, char *dst, int len, int sum,
 					int *src_err_ptr, int *dst_err_ptr);
 
+/*
+ *	Note: when you get a NULL pointer exception here this means someone
+ *	passed in an incorrect kernel address to one of these functions. 
+ *	
+ *	If you use these functions directly please don't forget the 
+ *	verify_area().
+ */
 extern __inline__
 unsigned int csum_partial_copy_nocheck ( const char *src, char *dst,
 					int len, int sum)
 {
-	int *src_err_ptr=NULL, *dst_err_ptr=NULL;
-
-	return csum_partial_copy_generic ( src, dst, len, sum, src_err_ptr, dst_err_ptr);
+	return csum_partial_copy_generic ( src, dst, len, sum, NULL, NULL);
 }
 
 extern __inline__
 unsigned int csum_partial_copy_from_user ( const char *src, char *dst,
 						int len, int sum, int *err_ptr)
 {
-	int *dst_err_ptr=NULL;
-
-	return csum_partial_copy_generic ( src, dst, len, sum, err_ptr, dst_err_ptr);
+	return csum_partial_copy_generic ( src, dst, len, sum, err_ptr, NULL);
 }
 
 #if 0
@@ -59,9 +62,7 @@
 unsigned int csum_partial_copy_to_user ( const char *src, char *dst,
 					int len, int sum, int *err_ptr)
 {
-	int *src_err_ptr=NULL;
-
-	return csum_partial_copy_generic ( src, dst, len, sum, src_err_ptr, err_ptr);
+	return csum_partial_copy_generic ( src, dst, len, sum, NULL, err_ptr);
 }
 #endif
 
@@ -202,15 +203,13 @@
 static __inline__ unsigned int csum_and_copy_to_user (const char *src, char *dst,
 				    int len, int sum, int *err_ptr)
 {
-	int *src_err_ptr=NULL;
-
-	if (verify_area(VERIFY_WRITE, dst, len) == 0)
-		return csum_partial_copy_generic(src, dst, len, sum, src_err_ptr, err_ptr);
+	if (access_ok(VERIFY_WRITE, dst, len))
+		return csum_partial_copy_generic(src, dst, len, sum, NULL, err_ptr);
 
 	if (len)
 		*err_ptr = -EFAULT;
 
-	return sum;
+	return -1; /* invalid checksum */
 }
 
 #endif
diff -u --recursive --new-file v2.1.121/linux/include/asm-i386/desc.h linux/include/asm-i386/desc.h
--- v2.1.121/linux/include/asm-i386/desc.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/desc.h	Wed Sep  9 08:56:58 1998
@@ -0,0 +1,61 @@
+#ifndef __ARCH_DESC_H
+#define __ARCH_DESC_H
+
+struct desc_struct {
+	unsigned long a,b;
+};
+
+extern struct desc_struct gdt_table[];
+extern struct desc_struct *idt, *gdt;
+
+struct Xgt_desc_struct {
+	unsigned short size;
+	unsigned long address __attribute__((packed));
+};
+
+#define idt_descr (*(struct Xgt_desc_struct *)((char *)&idt - 2))
+#define gdt_descr (*(struct Xgt_desc_struct *)((char *)&gdt - 2))
+
+/*
+ * Entry into gdt where to find first TSS. GDT layout:
+ *   0 - null
+ *   1 - not used
+ *   2 - kernel code segment
+ *   3 - kernel data segment
+ *   4 - user code segment
+ *   5 - user data segment
+ *   6 - not used
+ *   7 - not used
+ *   8 - APM BIOS support
+ *   9 - APM BIOS support
+ *  10 - APM BIOS support
+ *  11 - APM BIOS support
+ *  12 - TSS #0
+ *  13 - LDT #0
+ *  14 - TSS #1
+ *  15 - LDT #1
+ */
+#define FIRST_TSS_ENTRY 12
+#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
+#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
+#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
+#define load_TR(n) __asm__ __volatile__("ltr %%ax": /* no output */ :"a" (_TSS(n)))
+#define load_ldt(n) __asm__ __volatile__("lldt %%ax": /* no output */ :"a" (_LDT(n)))
+#define store_TR(n) \
+__asm__("str %%ax\n\t" \
+	"subl %2,%%eax\n\t" \
+	"shrl $4,%%eax" \
+	:"=a" (n) \
+	:"0" (0),"i" (FIRST_TSS_ENTRY<<3))
+
+extern void set_intr_gate(unsigned int irq, void * addr);
+extern void set_ldt_desc(unsigned int n, void *addr, unsigned int size);
+extern void set_tss_desc(unsigned int n, void *addr);
+
+/*
+ * This is the ldt that every process will get unless we need
+ * something other than this.
+ */
+extern struct desc_struct default_ldt;
+
+#endif
diff -u --recursive --new-file v2.1.121/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v2.1.121/linux/include/linux/cdrom.h	Mon Jan  5 00:06:26 1998
+++ linux/include/linux/cdrom.h	Wed Sep 16 13:25:57 1998
@@ -415,6 +415,14 @@
 
 extern int register_cdrom(struct cdrom_device_info *cdi);
 extern int unregister_cdrom(struct cdrom_device_info *cdi);
+typedef struct {
+    int data;
+    int audio;
+    int cdi;
+    int xa;
+    long error;
+} tracktype;
+extern void cdrom_count_tracks(struct cdrom_device_info *cdi,tracktype* tracks);
 #endif  /* End of kernel only stuff */ 
 
 #endif  /* _LINUX_CDROM_H */
diff -u --recursive --new-file v2.1.121/linux/include/linux/hdreg.h linux/include/linux/hdreg.h
--- v2.1.121/linux/include/linux/hdreg.h	Fri Jul 31 17:05:52 1998
+++ linux/include/linux/hdreg.h	Wed Sep 16 13:53:25 1998
@@ -42,6 +42,7 @@
 #define WIN_RESTORE		0x10
 #define WIN_READ		0x20
 #define WIN_WRITE		0x30
+#define WIN_WRITE_VERIFY	0x3C
 #define WIN_VERIFY		0x40
 #define WIN_FORMAT		0x50
 #define WIN_INIT		0x60
@@ -62,6 +63,9 @@
 #define WIN_READDMA		0xc8	/* read sectors using DMA transfers */
 #define WIN_WRITEDMA		0xca	/* write sectors using DMA transfers */
 
+#define WIN_READ_BUFFER		0xE4	/* force read only 1 sector */
+#define WIN_WRITE_BUFFER	0xE8	/* force write only 1 sector */
+
 #define WIN_SMART		0xb0	/* self-monitoring and reporting */
 
 /* Additional drive command codes used by ATAPI devices. */
@@ -85,6 +89,7 @@
 #define MARK_ERR	0x01	/* Bad address mark */
 #define TRK0_ERR	0x02	/* couldn't find track 0 */
 #define ABRT_ERR	0x04	/* Command aborted */
+#define MCR_ERR		0x08	/* media change request */
 #define ID_ERR		0x10	/* ID field not found */
 #define ECC_ERR		0x40	/* Uncorrectable ECC error */
 #define	BBD_ERR		0x80	/* pre-EIDE meaning:  block marked bad */
diff -u --recursive --new-file v2.1.121/linux/include/linux/loop.h linux/include/linux/loop.h
--- v2.1.121/linux/include/linux/loop.h	Tue Jul 21 00:15:33 1998
+++ linux/include/linux/loop.h	Sat Sep 12 10:33:27 1998
@@ -1,6 +1,8 @@
 #ifndef _LINUX_LOOP_H
 #define _LINUX_LOOP_H
 
+#include <linux/kdev_t.h>
+
 /*
  * include/linux/loop.h
  *
@@ -28,27 +30,41 @@
 				    char *raw_buf, char *loop_buf, int size);
 	char		lo_name[LO_NAME_SIZE];
 	char		lo_encrypt_key[LO_KEY_SIZE];
-#ifdef DES_AVAILABLE
-	des_key_schedule lo_des_key;
-	unsigned long	lo_des_init[2];
-#endif
-#ifdef IDEA_AVAILABLE
-        idea_key        lo_idea_en_key;
-        idea_key        lo_idea_de_key;
-#endif
+	__u32           lo_init[2];
+	uid_t		lo_key_owner;	/* Who set the key */
+	int		(*ioctl)(struct loop_device *, int cmd, 
+				 unsigned long arg); 
+
 	struct file *	lo_backing_file;
+	void		*key_data; 
+	char		key_reserved[48]; /* for use by the filter modules */
 };
 
 typedef	int (* transfer_proc_t)(struct loop_device *, int cmd,
 				char *raw_buf, char *loop_buf, int size);
 
+#endif /* __KERNEL__ */
+
 /*
  * Loop flags
  */
 #define LO_FLAGS_DO_BMAP	0x00000001
 #define LO_FLAGS_READ_ONLY	0x00000002
 
-#endif /* __KERNEL__ */
+/* 
+ * Note that this structure gets the wrong offsets when directly used
+ * from a glibc program, because glibc has a 32bit dev_t.
+ * Prevent people from shooting in their own foot.  
+ */
+#if __GLIBC__ >= 2 && !defined(dev_t)
+#error "Wrong dev_t in loop.h"
+#endif 
+
+/*
+ *	This uses kdev_t because glibc currently has no appropiate
+ *	conversion version for the loop ioctls. 
+ * 	The situation is very unpleasant	
+ */
 
 struct loop_info {
 	int		lo_number;	/* ioctl r/o */
@@ -66,15 +82,31 @@
 };
 
 /*
- * Loop encryption types --- LO_CRYPT_IDEA isn't supported yet
+ * Loop filter types
  */
 
 #define LO_CRYPT_NONE	0
 #define LO_CRYPT_XOR	1
 #define LO_CRYPT_DES	2
-#define LO_CRYPT_IDEA	3
-#define MAX_LO_CRYPT	4
+#define LO_CRYPT_DUMMY     9
+#define LO_CRYPT_SKIPJACK 10
+#define MAX_LO_CRYPT	20
 
+#ifdef __KERNEL__
+/* Support for loadable transfer modules */
+struct loop_func_table {
+	int number; 	/* filter type */ 
+	int (*transfer)(struct loop_device *lo, int cmd, 
+			char *raw_buf, char *loop_buf, int size);
+	int (*init)(struct loop_device *, struct loop_info *); 
+	int (*release)(struct loop_device *);  
+	int (*ioctl)(struct loop_device *, int cmd, unsigned long arg);
+}; 
+
+int  loop_register_transfer(struct loop_func_table *funcs);
+int loop_unregister_transfer(int number); 
+
+#endif
 /*
  * IOCTL commands --- we will commandeer 0x4C ('L')
  */
diff -u --recursive --new-file v2.1.121/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.1.121/linux/include/linux/pci.h	Wed Aug 26 11:37:44 1998
+++ linux/include/linux/pci.h	Wed Sep 16 13:53:25 1998
@@ -601,6 +601,7 @@
 #define PCI_DEVICE_ID_DATABOOK_87144	0xb106
 
 #define PCI_VENDOR_ID_PLX		0x10b5
+#define PCI_DEVICE_ID_PLX_9050		0x9050
 #define PCI_DEVICE_ID_PLX_9060		0x9060
 #define PCI_DEVICE_ID_PLX_9060ES	0x906E
 #define PCI_DEVICE_ID_PLX_9060SD	0x906D
@@ -703,6 +704,9 @@
 #define PCI_DEVICE_ID_INIT_320P		0x9100
 #define PCI_DEVICE_ID_INIT_360P		0x9500
 
+#define PCI_VENDOR_ID_TTI		0x1103
+#define PCI_DEVICE_ID_TTI_HPT343	0x0003
+
 #define PCI_VENDOR_ID_VIA		0x1106
 #define PCI_DEVICE_ID_VIA_82C505	0x0505
 #define PCI_DEVICE_ID_VIA_82C561	0x0561
@@ -812,6 +816,7 @@
 #define PCI_DEVICE_ID_RICOH_RL5C466	0x0466
 
 #define PCI_VENDOR_ID_ARTOP		0x1191
+#define PCI_DEVICE_ID_ARTOP_ATP8400	0x0004
 #define PCI_DEVICE_ID_ARTOP_ATP850UF	0x0005
 
 #define PCI_VENDOR_ID_ZEITNET		0x1193
diff -u --recursive --new-file v2.1.121/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.1.121/linux/include/linux/proc_fs.h	Sat Sep  5 16:46:41 1998
+++ linux/include/linux/proc_fs.h	Wed Sep 16 13:56:37 1998
@@ -23,6 +23,7 @@
 	PROC_CPUINFO,
 	PROC_PCI,
 	PROC_MCA,
+	PROC_NUBUS,
 	PROC_SELF,	/* will change inode # */
 	PROC_NET,
         PROC_SCSI,
diff -u --recursive --new-file v2.1.121/linux/include/linux/sysv_fs.h linux/include/linux/sysv_fs.h
--- v2.1.121/linux/include/linux/sysv_fs.h	Fri Jul 31 17:10:57 1998
+++ linux/include/linux/sysv_fs.h	Wed Sep 16 14:10:17 1998
@@ -37,28 +37,28 @@
    as { byte2, byte3, byte0, byte1 }. We need conversions.
 */
 
-typedef unsigned long coh_ulong;
+typedef u32 coh_ulong;
 
-static inline coh_ulong to_coh_ulong (unsigned long x)
+static inline coh_ulong to_coh_ulong (u32 x)
 {
 	return ((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16);
 }
 
-static inline unsigned long from_coh_ulong (coh_ulong x)
+static inline u32 from_coh_ulong (coh_ulong x)
 {
 	return ((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16);
 }
 
 /* inode numbers are 16 bit */
 
-typedef unsigned short sysv_ino_t;
+typedef u16 sysv_ino_t;
 
 /* Block numbers are 24 bit, sometimes stored in 32 bit.
    On Coherent FS, they are always stored in PDP-11 manner: the least
    significant 16 bits come last.
 */
 
-typedef unsigned long sysv_zone_t;
+typedef u32 sysv_zone_t;
 
 /* Among the blocks ... */
 /* Xenix FS, Coherent FS: block 0 is the boot block, block 1 the super-block.
@@ -75,36 +75,36 @@
 #define XENIX_NICINOD	100	/* number of inode cache entries */
 #define XENIX_NICFREE	100	/* number of free block list chunk entries */
 struct xenix_super_block {
-	unsigned short s_isize; /* index of first data zone */
-	unsigned long  s_fsize __packed2__; /* total number of zones of this fs */
+	u16		s_isize; /* index of first data zone */
+	u32		s_fsize __packed2__; /* total number of zones of this fs */
 	/* the start of the free block list: */
-	unsigned short s_nfree;	/* number of free blocks in s_free, <= XENIX_NICFREE */
-	unsigned long  s_free[XENIX_NICFREE]; /* first free block list chunk */
+	u16		s_nfree;	/* number of free blocks in s_free, <= XENIX_NICFREE */
+	u32		s_free[XENIX_NICFREE]; /* first free block list chunk */
 	/* the cache of free inodes: */
-	unsigned short s_ninode; /* number of free inodes in s_inode, <= XENIX_NICINOD */
-	sysv_ino_t     s_inode[XENIX_NICINOD]; /* some free inodes */
+	u16		s_ninode; /* number of free inodes in s_inode, <= XENIX_NICINOD */
+	sysv_ino_t	s_inode[XENIX_NICINOD]; /* some free inodes */
 	/* locks, not used by Linux: */
-	char	       s_flock;	/* lock during free block list manipulation */
-	char	       s_ilock;	/* lock during inode cache manipulation */
-	char	       s_fmod;	/* super-block modified flag */
-	char	       s_ronly;	/* flag whether fs is mounted read-only */
-	unsigned long  s_time __packed2__; /* time of last super block update */
-	unsigned long  s_tfree __packed2__; /* total number of free zones */
-	unsigned short s_tinode;	/* total number of free inodes */
-	short	       s_dinfo[4];	/* device information ?? */
-	char	       s_fname[6];	/* file system volume name */
-	char	       s_fpack[6];	/* file system pack name */
-	char	       s_clean;		/* set to 0x46 when filesystem is properly unmounted */
-	char	       s_fill[371];
-	long	       s_magic;		/* version of file system */
-	long	       s_type;		/* type of file system: 1 for 512 byte blocks
+	char		s_flock;	/* lock during free block list manipulation */
+	char		s_ilock;	/* lock during inode cache manipulation */
+	char		s_fmod;		/* super-block modified flag */
+	char		s_ronly;	/* flag whether fs is mounted read-only */
+	u32		s_time __packed2__; /* time of last super block update */
+	u32		s_tfree __packed2__; /* total number of free zones */
+	u16		s_tinode;	/* total number of free inodes */
+	s16		s_dinfo[4];	/* device information ?? */
+	char		s_fname[6];	/* file system volume name */
+	char		s_fpack[6];	/* file system pack name */
+	char		s_clean;	/* set to 0x46 when filesystem is properly unmounted */
+	char		s_fill[371];
+	s32		s_magic;	/* version of file system */
+	s32		s_type;		/* type of file system: 1 for 512 byte blocks
 								2 for 1024 byte blocks */
 };
 
 /* Xenix free list block on disk */
 struct xenix_freelist_chunk {
-	unsigned short fl_nfree;	/* number of free blocks in fl_free, <= XENIX_NICFREE] */
-	unsigned long  fl_free[XENIX_NICFREE] __packed2__;
+	u16	fl_nfree;	/* number of free blocks in fl_free, <= XENIX_NICFREE] */
+	u32	fl_free[XENIX_NICFREE] __packed2__;
 };
 
 /* SystemV FS comes in two variants:
@@ -116,116 +116,120 @@
 
 /* SystemV4 super-block data on disk */
 struct sysv4_super_block {
-	unsigned short s_isize; /* index of first data zone */
-	unsigned long  s_fsize;	/* total number of zones of this fs */
+	u16	s_isize;	/* index of first data zone */
+	u16	s_pad0;
+	u32	s_fsize;	/* total number of zones of this fs */
 	/* the start of the free block list: */
-	unsigned short s_nfree;	/* number of free blocks in s_free, <= SYSV_NICFREE */
-	unsigned long  s_free[SYSV_NICFREE]; /* first free block list chunk */
+	u16	s_nfree;	/* number of free blocks in s_free, <= SYSV_NICFREE */
+	u16	s_pad1;
+	u32	s_free[SYSV_NICFREE]; /* first free block list chunk */
 	/* the cache of free inodes: */
-	unsigned short s_ninode; /* number of free inodes in s_inode, <= SYSV_NICINOD */
+	u16	s_ninode;	/* number of free inodes in s_inode, <= SYSV_NICINOD */
+	u16	s_pad2;
 	sysv_ino_t     s_inode[SYSV_NICINOD]; /* some free inodes */
 	/* locks, not used by Linux: */
-	char	       s_flock;	/* lock during free block list manipulation */
-	char	       s_ilock;	/* lock during inode cache manipulation */
-	char	       s_fmod;	/* super-block modified flag */
-	char	       s_ronly;	/* flag whether fs is mounted read-only */
-	unsigned long  s_time;	/* time of last super block update */
-	short	       s_dinfo[4];	/* device information ?? */
-	unsigned long  s_tfree;	/* total number of free zones */
-	unsigned short s_tinode;	/* total number of free inodes */
-	char	       s_fname[6];	/* file system volume name */
-	char	       s_fpack[6];	/* file system pack name */
-	long	       s_fill[12];
-	long	       s_state;		/* file system state: 0x7c269d38-s_time means clean */
-	long	       s_magic;		/* version of file system */
-	long	       s_type;		/* type of file system: 1 for 512 byte blocks
+	char	s_flock;	/* lock during free block list manipulation */
+	char	s_ilock;	/* lock during inode cache manipulation */
+	char	s_fmod;		/* super-block modified flag */
+	char	s_ronly;	/* flag whether fs is mounted read-only */
+	u32	s_time;		/* time of last super block update */
+	s16	s_dinfo[4];	/* device information ?? */
+	u32	s_tfree;	/* total number of free zones */
+	u16	s_tinode;	/* total number of free inodes */
+	u16	s_pad3;
+	char	s_fname[6];	/* file system volume name */
+	char	s_fpack[6];	/* file system pack name */
+	s32	s_fill[12];
+	s32	s_state;	/* file system state: 0x7c269d38-s_time means clean */
+	s32	s_magic;	/* version of file system */
+	s32	s_type;		/* type of file system: 1 for 512 byte blocks
 								2 for 1024 byte blocks */
 };
 
 /* SystemV4 free list block on disk */
 struct sysv4_freelist_chunk {
-	unsigned short fl_nfree;	/* number of free blocks in fl_free, <= SYSV_NICFREE] */
-	unsigned long  fl_free[SYSV_NICFREE];
+	u16 fl_nfree;	/* number of free blocks in fl_free, <= SYSV_NICFREE] */
+	u32  fl_free[SYSV_NICFREE];
 };
 
 /* SystemV2 super-block data on disk */
 struct sysv2_super_block {
-	unsigned short s_isize; /* index of first data zone */
-	unsigned long  s_fsize __packed2__; /* total number of zones of this fs */
+	u16	s_isize; 		/* index of first data zone */
+	u32	s_fsize __packed2__;	/* total number of zones of this fs */
 	/* the start of the free block list: */
-	unsigned short s_nfree;	/* number of free blocks in s_free, <= SYSV_NICFREE */
-	unsigned long  s_free[SYSV_NICFREE]; /* first free block list chunk */
+	u16	s_nfree;		/* number of free blocks in s_free, <= SYSV_NICFREE */
+	u32	s_free[SYSV_NICFREE];	/* first free block list chunk */
 	/* the cache of free inodes: */
-	unsigned short s_ninode; /* number of free inodes in s_inode, <= SYSV_NICINOD */
+	u16	s_ninode;		/* number of free inodes in s_inode, <= SYSV_NICINOD */
 	sysv_ino_t     s_inode[SYSV_NICINOD]; /* some free inodes */
 	/* locks, not used by Linux: */
-	char	       s_flock;	/* lock during free block list manipulation */
-	char	       s_ilock;	/* lock during inode cache manipulation */
-	char	       s_fmod;	/* super-block modified flag */
-	char	       s_ronly;	/* flag whether fs is mounted read-only */
-	unsigned long  s_time __packed2__; /* time of last super block update */
-	short	       s_dinfo[4];	/* device information ?? */
-	unsigned long  s_tfree __packed2__; /* total number of free zones */
-	unsigned short s_tinode;	/* total number of free inodes */
-	char	       s_fname[6];	/* file system volume name */
-	char	       s_fpack[6];	/* file system pack name */
-	long	       s_fill[14];
-	long	       s_state;		/* file system state: 0xcb096f43 means clean */
-	long	       s_magic;		/* version of file system */
-	long	       s_type;		/* type of file system: 1 for 512 byte blocks
+	char	s_flock;		/* lock during free block list manipulation */
+	char	s_ilock;		/* lock during inode cache manipulation */
+	char	s_fmod;			/* super-block modified flag */
+	char	s_ronly;		/* flag whether fs is mounted read-only */
+	u32	s_time __packed2__;	/* time of last super block update */
+	s16	s_dinfo[4];		/* device information ?? */
+	u32	s_tfree __packed2__;	/* total number of free zones */
+	u16	s_tinode;		/* total number of free inodes */
+	char	s_fname[6];		/* file system volume name */
+	char	s_fpack[6];		/* file system pack name */
+	s32	s_fill[14];
+	s32	s_state;		/* file system state: 0xcb096f43 means clean */
+	s32	s_magic;		/* version of file system */
+	s32	s_type;			/* type of file system: 1 for 512 byte blocks
 								2 for 1024 byte blocks */
 };
 
 /* SystemV2 free list block on disk */
 struct sysv2_freelist_chunk {
-	unsigned short fl_nfree;	/* number of free blocks in fl_free, <= SYSV_NICFREE] */
-	unsigned long  fl_free[SYSV_NICFREE] __packed2__;
+	u16	fl_nfree;	/* number of free blocks in fl_free, <= SYSV_NICFREE] */
+	u32	fl_free[SYSV_NICFREE] __packed2__;
 };
 
 /* Coherent super-block data on disk */
 #define COH_NICINOD	100	/* number of inode cache entries */
 #define COH_NICFREE	64	/* number of free block list chunk entries */
 struct coh_super_block {
-	unsigned short s_isize; /* index of first data zone */
-	coh_ulong      s_fsize __packed2__; /* total number of zones of this fs */
+	u16		s_isize;	/* index of first data zone */
+	coh_ulong	s_fsize __packed2__; /* total number of zones of this fs */
 	/* the start of the free block list: */
-	unsigned short s_nfree;	/* number of free blocks in s_free, <= COH_NICFREE */
-	coh_ulong      s_free[COH_NICFREE] __packed2__; /* first free block list chunk */
+	u16 s_nfree;	/* number of free blocks in s_free, <= COH_NICFREE */
+	coh_ulong	s_free[COH_NICFREE] __packed2__; /* first free block list chunk */
 	/* the cache of free inodes: */
-	unsigned short s_ninode; /* number of free inodes in s_inode, <= COH_NICINOD */
-	sysv_ino_t     s_inode[COH_NICINOD]; /* some free inodes */
+	u16		s_ninode;	/* number of free inodes in s_inode, <= COH_NICINOD */
+	sysv_ino_t	s_inode[COH_NICINOD]; /* some free inodes */
 	/* locks, not used by Linux: */
-	char	       s_flock;	/* lock during free block list manipulation */
-	char	       s_ilock;	/* lock during inode cache manipulation */
-	char	       s_fmod;	/* super-block modified flag */
-	char	       s_ronly;	/* flag whether fs is mounted read-only */
-	coh_ulong      s_time __packed2__; /* time of last super block update */
-	coh_ulong      s_tfree __packed2__; /* total number of free zones */
-	unsigned short s_tinode;	/* total number of free inodes */
-	unsigned short s_interleave_m;	/* interleave factor */
-	unsigned short s_interleave_n;
-	char	       s_fname[6];	/* file system volume name */
-	char	       s_fpack[6];	/* file system pack name */
-	unsigned long  s_unique;	/* zero, not used */
+	char		s_flock;	/* lock during free block list manipulation */
+	char		s_ilock;	/* lock during inode cache manipulation */
+	char		s_fmod;		/* super-block modified flag */
+	char		s_ronly;	/* flag whether fs is mounted read-only */
+	coh_ulong	s_time __packed2__; /* time of last super block update */
+	coh_ulong	s_tfree __packed2__; /* total number of free zones */
+	u16		s_tinode;	/* total number of free inodes */
+	u16		s_interleave_m;	/* interleave factor */
+	u16		s_interleave_n;
+	char		s_fname[6];	/* file system volume name */
+	char		s_fpack[6];	/* file system pack name */
+	u32		s_unique;	/* zero, not used */
 };
 
 /* Coherent free list block on disk */
 struct coh_freelist_chunk {
-	unsigned short fl_nfree;	/* number of free blocks in fl_free, <= COH_NICFREE] */
-	unsigned long  fl_free[COH_NICFREE] __packed2__;
+	u16 fl_nfree;	/* number of free blocks in fl_free, <= COH_NICFREE] */
+	u32  fl_free[COH_NICFREE] __packed2__;
 };
 
 
 /* SystemV/Coherent inode data on disk */
 
 struct sysv_inode {
-	unsigned short i_mode;
-	unsigned short i_nlink;
-	unsigned short i_uid;
-	unsigned short i_gid;
-	unsigned long  i_size;
+	u16 i_mode;
+	u16 i_nlink;
+	u16 i_uid;
+	u16 i_gid;
+	u32 i_size;
 	union { /* directories, regular files, ... */
-		char i_addb[3*(10+1+1+1)+1]; /* zone numbers: max. 10 data blocks,
+		unsigned char i_addb[3*(10+1+1+1)+1]; /* zone numbers: max. 10 data blocks,
 					      * then 1 indirection block,
 					      * then 1 double indirection block,
 					      * then 1 triple indirection block.
@@ -236,14 +240,14 @@
 		/* named pipes on Coherent */
 		struct {
 			char p_addp[30];
-			short p_pnc;
-			short p_prx;
-			short p_pwx;
+			s16 p_pnc;
+			s16 p_prx;
+			s16 p_pwx;
 		} i_p;
 	} i_a;
-	unsigned long i_atime;	/* time of last access */
-	unsigned long i_mtime;	/* time of last modification */
-	unsigned long i_ctime;	/* time of creation */
+	u32 i_atime;	/* time of last access */
+	u32 i_mtime;	/* time of last modification */
+	u32 i_ctime;	/* time of creation */
 };
 
 /* The admissible values for i_mode are listed in <linux/stat.h> :
@@ -301,7 +305,7 @@
 /* The number of inodes per block is
    sb->sv_inodes_per_block = block_size / sizeof(struct sysv_inode) */
 /* The number of indirect pointers per block is
-   sb->sv_ind_per_block = block_size / sizeof(unsigned long) */
+   sb->sv_ind_per_block = block_size / sizeof(u32) */
 
 
 /* SystemV/Coherent directory entry on disk */
diff -u --recursive --new-file v2.1.121/linux/include/linux/sysv_fs_i.h linux/include/linux/sysv_fs_i.h
--- v2.1.121/linux/include/linux/sysv_fs_i.h	Sat Nov  5 03:32:13 1994
+++ linux/include/linux/sysv_fs_i.h	Sun Sep 13 10:27:07 1998
@@ -5,11 +5,11 @@
  * SystemV/Coherent FS inode data in memory
  */
 struct sysv_inode_info {
-	unsigned long i_data[10+1+1+1];	/* zone numbers: max. 10 data blocks,
-					 * then 1 indirection block,
-					 * then 1 double indirection block,
-					 * then 1 triple indirection block.
-					 */
+	u32 i_data[10+1+1+1];	/* zone numbers: max. 10 data blocks,
+				 * then 1 indirection block,
+				 * then 1 double indirection block,
+				 * then 1 triple indirection block.
+				 */
 };
 
 #endif
diff -u --recursive --new-file v2.1.121/linux/include/linux/sysv_fs_sb.h linux/include/linux/sysv_fs_sb.h
--- v2.1.121/linux/include/linux/sysv_fs_sb.h	Wed Feb  4 11:36:01 1998
+++ linux/include/linux/sysv_fs_sb.h	Sun Sep 13 10:27:07 1998
@@ -50,21 +50,21 @@
 	   different superblock layout. */
 	char *         s_sbd1;		/* entire superblock data, for part 1 */
 	char *         s_sbd2;		/* entire superblock data, for part 2 */
-	unsigned short *s_sb_fic_count;	/* pointer to s_sbd->s_ninode */
-        unsigned short *s_sb_fic_inodes; /* pointer to s_sbd->s_inode */
-	unsigned short *s_sb_total_free_inodes; /* pointer to s_sbd->s_tinode */
-	unsigned short *s_sb_flc_count;	/* pointer to s_sbd->s_nfree */
-	unsigned long  *s_sb_flc_blocks; /* pointer to s_sbd->s_free */
-	unsigned long  *s_sb_total_free_blocks;/* pointer to s_sbd->s_tfree */
-	unsigned long  *s_sb_time;	/* pointer to s_sbd->s_time */
-	unsigned long  *s_sb_state;	/* pointer to s_sbd->s_state, only FSTYPE_SYSV */
+	u16            *s_sb_fic_count;	/* pointer to s_sbd->s_ninode */
+        u16            *s_sb_fic_inodes; /* pointer to s_sbd->s_inode */
+	u16            *s_sb_total_free_inodes; /* pointer to s_sbd->s_tinode */
+	u16            *s_sb_flc_count;	/* pointer to s_sbd->s_nfree */
+	u32	       *s_sb_flc_blocks; /* pointer to s_sbd->s_free */
+	u32            *s_sb_total_free_blocks;/* pointer to s_sbd->s_tfree */
+	u32            *s_sb_time;	/* pointer to s_sbd->s_time */
+	u32            *s_sb_state;	/* pointer to s_sbd->s_state, only FSTYPE_SYSV */
 	/* We keep those superblock entities that don't change here;
 	   this saves us an indirection and perhaps a conversion. */
-	unsigned long  s_firstinodezone; /* index of first inode zone */
-	unsigned long  s_firstdatazone;	/* same as s_sbd->s_isize */
-	unsigned long  s_ninodes;	/* total number of inodes */
-	unsigned long  s_ndatazones;	/* total number of data zones */
-	unsigned long  s_nzones;	/* same as s_sbd->s_fsize */
+	u32            s_firstinodezone; /* index of first inode zone */
+	u32            s_firstdatazone;	/* same as s_sbd->s_isize */
+	u32            s_ninodes;	/* total number of inodes */
+	u32            s_ndatazones;	/* total number of data zones */
+	u32            s_nzones;	/* same as s_sbd->s_fsize */
 };
 /* The fields s_ind_per_block_2_1, s_toobig_block are currently unused. */
 
diff -u --recursive --new-file v2.1.121/linux/include/linux/umsdos_fs.p linux/include/linux/umsdos_fs.p
--- v2.1.121/linux/include/linux/umsdos_fs.p	Wed Sep  9 14:51:13 1998
+++ linux/include/linux/umsdos_fs.p	Fri Sep 11 11:26:56 1998
@@ -11,6 +11,7 @@
 	 char *buf,
 	 size_t size,
 	 loff_t *count);
+char * umsdos_d_path(struct dentry *, char *, int);
 void umsdos_lookup_patch_new(struct dentry *, struct umsdos_dirent *, off_t);
 void umsdos_lookup_patch (struct inode *dir,
 	 struct inode *inode,
@@ -27,7 +28,7 @@
 			    struct dentry *dentry,
 			    int nopseudo);
 int UMSDOS_lookup(struct inode *, struct dentry *);
-struct dentry *umsdos_lookup_dentry(struct dentry *, char *, int);
+struct dentry *umsdos_lookup_dentry(struct dentry *, char *, int, int);
 
 struct dentry *umsdos_solve_hlink (struct dentry *hlink);
 
@@ -80,15 +81,11 @@
 struct super_block *UMSDOS_read_super (struct super_block *, void *, int);
 void UMSDOS_put_super (struct super_block *);
 
-struct dentry *compat_umsdos_real_lookup (struct dentry *d_dir,
-	 const char *name,
-	 int len);
 int umsdos_real_lookup(struct inode *, struct dentry *);	 
 void umsdos_setup_dir(struct dentry *);
 void umsdos_setup_dir_inode (struct inode *inode);
-void umsdos_set_dirinfo (struct inode *inode,
-	 struct inode *dir,
-	 off_t f_pos);
+void umsdos_set_dirinfo_new(struct dentry *, off_t);
+void umsdos_set_dirinfo (struct inode *, struct inode *, off_t);
 int umsdos_isinit (struct inode *inode);
 void umsdos_patch_dentry_inode (struct dentry *, off_t);
 void umsdos_patch_inode (struct inode *, struct inode *, off_t);
diff -u --recursive --new-file v2.1.121/linux/include/linux/videodev.h linux/include/linux/videodev.h
--- v2.1.121/linux/include/linux/videodev.h	Wed Aug 26 11:37:44 1998
+++ linux/include/linux/videodev.h	Thu Sep 10 16:33:15 1998
@@ -48,6 +48,7 @@
 #define VID_TYPE_SCALES		128	/* Scalable */
 #define VID_TYPE_MONOCHROME	256	/* Monochrome only */
 #define VID_TYPE_SUBCAPTURE	512	/* Can capture subareas of the image */
+#define VID_TYPE_OUTPUT		1024	/* Can output video data */
 
 struct video_capability
 {
diff -u --recursive --new-file v2.1.121/linux/include/linux/wavefront.h linux/include/linux/wavefront.h
--- v2.1.121/linux/include/linux/wavefront.h	Sun Jul 26 11:57:19 1998
+++ linux/include/linux/wavefront.h	Thu Sep 10 16:37:27 1998
@@ -150,10 +150,6 @@
 #define WF_MIDI_VIRTUAL_IS_EXTERNAL 0x2
 #define WF_MIDI_IN_TO_SYNTH_DISABLED 0x4
 
-/* See wavefront.c for details */
-
-#define WAVEFRONT_MAX_DEVICES 1 /* How many WaveFront cards we can handle */
-
 /* slot indexes for struct address_info: makes code a little more mnemonic */
 
 #define WF_SYNTH_SLOT         0
@@ -502,11 +498,11 @@
     wavefront_drum d;
 } wavefront_any;
 
-/* Hannu Savolainen hoped that his "patch_info" struct in soundcard.h
-   might work for other wavetable-based patch-loading situations.
-   Alas, his fears were correct.  The WaveFront doesn't even come with
-   just "patches", but several different kinds of structures that
-   control the process of generating sound.
+/* Hannu Solvainen hoped that his "patch_info" struct in soundcard.h
+   might work for other wave-table based patch loading situations.
+   Alas, his fears were correct. The WaveFront doesn't even come with
+   just "patches", but several different kind of structures that
+   control the sound generation process.
  */
 
 typedef struct wf_patch_info {
@@ -662,7 +658,7 @@
 
 /* Allow direct user-space control over FX memory/coefficient data.
    In theory this could be used to download the FX microprogram,
-   but it would be a little slower, and involve some weird code.
+   but it would be a little slower, and involve some wierd code.
  */
 
 #define WFFX_MEMSET              69
diff -u --recursive --new-file v2.1.121/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.121/linux/include/net/sock.h	Sat Sep  5 16:46:42 1998
+++ linux/include/net/sock.h	Wed Sep 16 13:56:44 1998
@@ -238,6 +238,7 @@
  *	Slow start and congestion control (see also Nagle, and Karn & Partridge)
  */
  	__u32	snd_ssthresh;	/* Slow start size threshold		*/
+ 	__u16	snd_cwnd_cnt;	/* Linear increase counter		*/
 	__u8	dup_acks;	/* Consequetive duplicate acks seen from other end */
 	__u8	delayed_acks;
 	__u16	user_mss;  	/* mss requested by user in ioctl */
diff -u --recursive --new-file v2.1.121/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.1.121/linux/include/net/tcp.h	Sat Sep  5 16:46:42 1998
+++ linux/include/net/tcp.h	Wed Sep 16 13:56:44 1998
@@ -192,7 +192,7 @@
 	(((__u32)(__dport)<<16) | (__u32)(__sport))
 #endif
 
-#if defined(__alpha__) || defined(__sparc_v9__)
+#if (BITS_PER_LONG == 64)
 #ifdef __BIG_ENDIAN
 #define TCP_V4_ADDR_COOKIE(__name, __saddr, __daddr) \
 	__u64 __name = (((__u64)(__saddr))<<32)|((__u64)(__daddr));
@@ -750,12 +750,6 @@
 
 #define TCP_SKB_CB(__skb)	((struct tcp_skb_cb *)&((__skb)->cb[0]))
 
-/* We store the congestion window as a packet count, shifted by
- * a factor so that implementing the 1/2 MSS ssthresh rules
- * is easy.
- */
-#define TCP_CWND_SHIFT	1
-
 /* This determines how many packets are "in the network" to the best
  * of our knowledge.  In many cases it is conservative, but where
  * detailed information is available from the receiver (via SACK
@@ -801,7 +795,7 @@
 		nagle_check = 0;
 
 	return (nagle_check &&
-		(tcp_packets_in_flight(tp) < (tp->snd_cwnd>>TCP_CWND_SHIFT)) &&
+		(tcp_packets_in_flight(tp) < tp->snd_cwnd) &&
 		!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd) &&
 		tp->retransmits == 0);
 }
diff -u --recursive --new-file v2.1.121/linux/init/main.c linux/init/main.c
--- v2.1.121/linux/init/main.c	Wed Sep  9 14:51:13 1998
+++ linux/init/main.c	Wed Sep 16 13:25:57 1998
@@ -388,6 +388,10 @@
 	{ "hdf",     0x2140 },
 	{ "hdg",     0x2200 },
 	{ "hdh",     0x2240 },
+	{ "hdi",     0x3800 },
+	{ "hdj",     0x3840 },
+	{ "hdk",     0x3900 },
+	{ "hdl",     0x3940 },
 #endif
 #ifdef CONFIG_BLK_DEV_SD
 	{ "sda",     0x0800 },
diff -u --recursive --new-file v2.1.121/linux/kernel/exit.c linux/kernel/exit.c
--- v2.1.121/linux/kernel/exit.c	Tue Aug 18 22:02:08 1998
+++ linux/kernel/exit.c	Sun Sep 13 13:40:02 1998
@@ -239,7 +239,11 @@
 	struct signal_struct * sig = tsk->sig;
 
 	if (sig) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&tsk->sigmask_lock, flags);
 		tsk->sig = NULL;
+		spin_unlock_irqrestore(&tsk->sigmask_lock, flags);
 		if (atomic_dec_and_test(&sig->count))
 			kfree(sig);
 	}
diff -u --recursive --new-file v2.1.121/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.1.121/linux/kernel/ksyms.c	Tue Aug 18 22:02:08 1998
+++ linux/kernel/ksyms.c	Fri Sep 11 11:09:23 1998
@@ -146,6 +146,7 @@
 EXPORT_SYMBOL(d_instantiate);
 EXPORT_SYMBOL(d_alloc);
 EXPORT_SYMBOL(d_lookup);
+EXPORT_SYMBOL(d_path);
 EXPORT_SYMBOL(__mark_inode_dirty);
 EXPORT_SYMBOL(get_empty_filp);
 EXPORT_SYMBOL(init_private_file);
@@ -401,3 +402,6 @@
 
 /* time */
 EXPORT_SYMBOL(get_fast_time);
+
+/* library functions */
+EXPORT_SYMBOL(strnicmp);
diff -u --recursive --new-file v2.1.121/linux/kernel/signal.c linux/kernel/signal.c
--- v2.1.121/linux/kernel/signal.c	Wed Aug 26 11:37:45 1998
+++ linux/kernel/signal.c	Sun Sep 13 17:11:35 1998
@@ -213,10 +213,50 @@
 	return sig;
 }
 
+/*
+ * Determine whether a signal should be posted or not.
+ *
+ * Signals with SIG_IGN can be ignored, except for the
+ * special case of a SIGCHLD. 
+ *
+ * Some signals with SIG_DFL default to a non-action.
+ */
+static int ignored_signal(int sig, struct task_struct *t)
+{
+	struct signal_struct *signals;
+	struct k_sigaction *ka;
+
+	/* Don't ignore traced or blocked signals */
+	if ((t->flags & PF_PTRACED) || sigismember(&t->blocked, sig))
+		return 0;
+	
+	signals = t->sig;
+	if (!signals)
+		return 1;
+
+	ka = &signals->action[sig-1];
+	switch ((unsigned long) ka->sa.sa_handler) {
+	case (unsigned long) SIG_DFL:
+		if (sig == SIGCONT ||
+		    sig == SIGWINCH ||
+		    sig == SIGCHLD ||
+		    sig == SIGURG)
+			break;
+		return 0;
+
+	case (unsigned long) SIG_IGN:
+		if (sig != SIGCHLD)
+			break;
+	/* fallthrough */
+	default:
+		return 0;
+	}
+	return 1;
+}
+
 int
 send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
 {
-	struct k_sigaction *ka;
 	unsigned long flags;
 	int ret;
 
@@ -227,13 +267,6 @@
 	ret = -EINVAL;
 	if (sig < 0 || sig > _NSIG)
 		goto out_nolock;
-
-	/* If t->sig is gone, we must be trying to kill the task.  So
-	   pretend that it doesn't exist anymore.  */
-	ret = -ESRCH;
-	if (t->sig == NULL)
-		goto out_nolock;
-
 	/* The somewhat baroque permissions check... */
 	ret = -EPERM;
 	if ((!info || ((unsigned long)info != 1 && SI_FROMUSER(info)))
@@ -249,9 +282,7 @@
 	if (!sig)
 		goto out_nolock;
 
-	ka = &t->sig->action[sig-1];
 	spin_lock_irqsave(&t->sigmask_lock, flags);
-
 	switch (sig) {
 	case SIGKILL: case SIGCONT:
 		/* Wake up the process if stopped.  */
@@ -277,16 +308,8 @@
 	   handled immediately (ie non-blocked and untraced) and
 	   that is ignored (either explicitly or by default).  */
 
-	if (!(t->flags & PF_PTRACED) && !sigismember(&t->blocked, sig)
-	    /* Don't bother with ignored sigs (SIGCHLD is special) */
-	    && ((ka->sa.sa_handler == SIG_IGN && sig != SIGCHLD)
-		/* Some signals are ignored by default.. (but SIGCONT
-		   already did its deed) */
-		|| (ka->sa.sa_handler == SIG_DFL
-		    && (sig == SIGCONT || sig == SIGCHLD
-			|| sig == SIGWINCH || sig == SIGURG)))) {
+	if (ignored_signal(sig, t))
 		goto out;
-	}
 
 	if (sig < SIGRTMIN) {
 		/* Non-real-time signals are not queued.  */
@@ -372,12 +395,18 @@
 int
 force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
 {
-	if (t->sig == NULL)
+	unsigned long int flags;
+
+	spin_lock_irqsave(&t->sigmask_lock, flags);
+	if (t->sig == NULL) {
+		spin_unlock_irqrestore(&t->sigmask_lock, flags);
 		return -ESRCH;
+	}
 
 	if (t->sig->action[sig-1].sa.sa_handler == SIG_IGN)
 		t->sig->action[sig-1].sa.sa_handler = SIG_DFL;
 	sigdelset(&t->blocked, sig);
+	spin_unlock_irqrestore(&t->sigmask_lock, flags);
 
 	return send_sig_info(sig, info, t);
 }
diff -u --recursive --new-file v2.1.121/linux/mm/memory.c linux/mm/memory.c
--- v2.1.121/linux/mm/memory.c	Wed Sep  9 14:51:13 1998
+++ linux/mm/memory.c	Mon Sep 14 11:25:00 1998
@@ -634,7 +634,7 @@
  * and potentially makes it more efficient.
  */
 static void do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma,
-	unsigned long address, int write_access, pte_t *page_table)
+	unsigned long address, pte_t *page_table)
 {
 	pte_t pte;
 	unsigned long old_page, new_page;
@@ -914,7 +914,7 @@
 		flush_tlb_page(vma, address);
 		return;
 	}
-	do_wp_page(tsk, vma, address, write_access, pte);
+	do_wp_page(tsk, vma, address, pte);
 }
 
 /*
diff -u --recursive --new-file v2.1.121/linux/net/Config.in linux/net/Config.in
--- v2.1.121/linux/net/Config.in	Sat Sep  5 16:46:42 1998
+++ linux/net/Config.in	Sun Sep 13 10:22:17 1998
@@ -18,8 +18,8 @@
   source net/ipv4/Config.in
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
 #   Sorry, but IPv6 as module is still invalid.
-#   tristate 'The IPv6 protocol (EXPERIMENTAL)' CONFIG_IPV6
-    bool 'The IPv6 protocol (EXPERIMENTAL)' CONFIG_IPV6
+    tristate 'The IPv6 protocol (EXPERIMENTAL)' CONFIG_IPV6
+#   bool 'The IPv6 protocol (EXPERIMENTAL)' CONFIG_IPV6
     if [ "$CONFIG_IPV6" != "n" ]; then
 	    source net/ipv6/Config.in
     fi
diff -u --recursive --new-file v2.1.121/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.1.121/linux/net/ax25/af_ax25.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ax25/af_ax25.c	Thu Sep 10 16:33:15 1998
@@ -1290,10 +1290,12 @@
 		fsa.fsa_ax25.sax25_call   = sk->protinfo.ax25->dest_addr;
 		fsa.fsa_ax25.sax25_ndigis = 0;
 
-		ndigi = sk->protinfo.ax25->digipeat->ndigi;
-		fsa.fsa_ax25.sax25_ndigis = ndigi;
-		for (i = 0; i < ndigi; i++)
-			fsa.fsa_digipeater[i] = sk->protinfo.ax25->digipeat->calls[i];
+		if (sk->protinfo.ax25->digipeat != NULL) {
+			ndigi = sk->protinfo.ax25->digipeat->ndigi;
+			fsa.fsa_ax25.sax25_ndigis = ndigi;
+			for (i = 0; i < ndigi; i++)
+				fsa.fsa_digipeater[i] = sk->protinfo.ax25->digipeat->calls[i];
+		}
 	} else {
 		fsa.fsa_ax25.sax25_family = AF_AX25;
 		fsa.fsa_ax25.sax25_call   = sk->protinfo.ax25->source_addr;
diff -u --recursive --new-file v2.1.121/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v2.1.121/linux/net/core/skbuff.c	Sat Sep  5 16:46:42 1998
+++ linux/net/core/skbuff.c	Mon Sep 14 22:52:10 1998
@@ -4,7 +4,7 @@
  *	Authors:	Alan Cox <iiitac@pyr.swan.ac.uk>
  *			Florian La Roche <rzsfl@rz.uni-sb.de>
  *
- *	Version:	$Id: skbuff.c,v 1.53 1998/08/19 13:32:44 freitag Exp $
+ *	Version:	$Id: skbuff.c,v 1.54 1998/09/15 02:11:09 davem Exp $
  *
  *	Fixes:	
  *		Alan Cox	:	Fixed the worst of the load balancer bugs.
@@ -192,7 +192,7 @@
 	skb->ip_summed = 0;
 	skb->security = 0;	/* By default packets are insecure */
 	skb->dst = NULL;
-#ifdef CONFIG_IP_FIREWALL_CHAINS
+#ifdef CONFIG_IP_FIREWALL
         skb->fwmark = 0;
 #endif
 	memset(skb->cb, 0, sizeof(skb->cb));
diff -u --recursive --new-file v2.1.121/linux/net/ipv4/Config.in linux/net/ipv4/Config.in
--- v2.1.121/linux/net/ipv4/Config.in	Thu May 14 19:47:44 1998
+++ linux/net/ipv4/Config.in	Mon Sep 14 22:52:10 1998
@@ -74,3 +74,7 @@
 #bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF
 bool 'IP: Drop source routed frames' CONFIG_IP_NOSR
 bool 'IP: Allow large windows (not recommended if <16Mb of memory)' CONFIG_SKB_LARGE
+#if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then 
+#bool 'IP: support experimental checksum copy to user for UDP'  CONFIG_UDP_DELAY_CSUM
+#fi
+
diff -u --recursive --new-file v2.1.121/linux/net/ipv4/ip_nat_dumb.c linux/net/ipv4/ip_nat_dumb.c
--- v2.1.121/linux/net/ipv4/ip_nat_dumb.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv4/ip_nat_dumb.c	Mon Sep 14 22:52:10 1998
@@ -5,7 +5,7 @@
  *
  *		Dumb Network Address Translation.
  *
- * Version:	$Id: ip_nat_dumb.c,v 1.4 1998/08/26 12:03:49 davem Exp $
+ * Version:	$Id: ip_nat_dumb.c,v 1.5 1998/09/07 00:13:54 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
diff -u --recursive --new-file v2.1.121/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.1.121/linux/net/ipv4/ip_output.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv4/ip_output.c	Mon Sep 14 22:52:10 1998
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) output module.
  *
- * Version:	$Id: ip_output.c,v 1.61 1998/08/26 12:03:54 davem Exp $
+ * Version:	$Id: ip_output.c,v 1.62 1998/09/14 01:22:58 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -544,7 +544,7 @@
 		 *	Account for the fragment.
 		 */
 
-		if(!err && offset == 0 && 
+		if(!err &&
 		   call_out_firewall(PF_INET, rt->u.dst.dev, skb->nh.iph, NULL, &skb) < FW_ACCEPT)
 			err = -EPERM;
 
diff -u --recursive --new-file v2.1.121/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.121/linux/net/ipv4/tcp.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv4/tcp.c	Mon Sep 14 22:52:10 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.119 1998/08/26 12:04:14 davem Exp $
+ * Version:	$Id: tcp.c,v 1.121 1998/09/07 00:13:52 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
diff -u --recursive --new-file v2.1.121/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.1.121/linux/net/ipv4/tcp_input.c	Wed Sep  9 14:51:13 1998
+++ linux/net/ipv4/tcp_input.c	Mon Sep 14 22:52:10 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.127 1998/08/26 12:04:20 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.128 1998/09/15 02:11:18 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -166,7 +166,7 @@
 static __inline__ void tcp_set_rto(struct tcp_opt *tp)
 {
 	tp->rto = (tp->srtt >> 3) + tp->mdev;
-	tp->rto += (tp->rto >> 2) + (tp->rto >> ((tp->snd_cwnd>>TCP_CWND_SHIFT)-1));
+	tp->rto += (tp->rto >> 2) + (tp->rto >> (tp->snd_cwnd-1));
 }
  
 
@@ -231,16 +231,13 @@
 {
 	u32 end_window = tp->rcv_wup + tp->rcv_wnd;
 
-	if (tp->rcv_wnd) {
-		if (!before(seq, tp->rcv_nxt) && before(seq, end_window))
-			return 1;
-
-		if ((end_seq - seq) && after(end_seq, tp->rcv_nxt) &&
-		    !after(end_seq, end_window))
-			return 1;
-	}
-
-	return 0;
+	if (tp->rcv_wnd &&
+	    after(end_seq, tp->rcv_nxt) &&
+	    before(seq, end_window))
+		return 1;
+	if (seq != end_window)
+		return 0;
+	return (seq == end_seq);
 }
 
 /* This functions checks to see if the tcp header is actually acceptable. */
@@ -292,7 +289,7 @@
 			/* The retransmission queue is always in order, so
 			 * we can short-circuit the walk early.
 			 */
-			if(after(TCP_SKB_CB(skb)->end_seq, end_seq))
+			if(!before(start_seq, TCP_SKB_CB(skb)->end_seq))
 				break;
 
 			/* We play conservative, we don't allow SACKS to partially
@@ -442,7 +439,7 @@
 static __inline__ void clear_fast_retransmit(struct tcp_opt *tp)
 {
 	if (tp->dup_acks > 3)
-		tp->snd_cwnd = (tp->snd_ssthresh << TCP_CWND_SHIFT);
+		tp->snd_cwnd = (tp->snd_ssthresh);
 
 	tp->dup_acks = 0;
 }
@@ -471,11 +468,11 @@
                  * to one half the current congestion window, but no less 
                  * than two segments. Retransmit the missing segment.
                  */
-                tp->dup_acks++;
 		if (tp->high_seq == 0 || after(ack, tp->high_seq)) {
+			tp->dup_acks++;
 			if ((tp->fackets_out > 3) || (tp->dup_acks == 3)) {
-                                tp->snd_ssthresh = max(tp->snd_cwnd >> (TCP_CWND_SHIFT + 1), 2);
-                                tp->snd_cwnd = (tp->snd_ssthresh + 3) << TCP_CWND_SHIFT;
+                                tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2);
+                                tp->snd_cwnd = (tp->snd_ssthresh + 3);
 				tp->high_seq = tp->snd_nxt;
 				if(!tp->fackets_out)
 					tcp_retransmit_skb(sk, skb_peek(&sk->write_queue));
@@ -495,7 +492,7 @@
                  */
                 if (tp->dup_acks > 3) {
 			if(!tp->fackets_out) {
-				tp->snd_cwnd += (1 << TCP_CWND_SHIFT);
+				tp->snd_cwnd++;
 			} else {
 				/* Fill any further holes which may have appeared.
 				 * We may want to change this to run every further
@@ -567,14 +564,20 @@
  * be reduced to is not clear, since 1/2 the old window may
  * still be larger than the maximum sending rate we ever achieved.
  */
-static void tcp_cong_avoid(struct tcp_opt *tp, u32 seq, u32 ack, u32 seq_rtt)
+static void tcp_cong_avoid(struct tcp_opt *tp)
 {
-        if ((tp->snd_cwnd>>TCP_CWND_SHIFT) <= tp->snd_ssthresh) {
+        if (tp->snd_cwnd <= tp->snd_ssthresh) {
                 /* In "safe" area, increase. */
-                tp->snd_cwnd += (1 << TCP_CWND_SHIFT);
+                tp->snd_cwnd++;
 	} else {
-                /* In dangerous area, increase slowly. */
-		tp->snd_cwnd += 1;
+                /* In dangerous area, increase slowly.
+		 * In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd
+		 */
+		if (tp->snd_cwnd_cnt >= tp->snd_cwnd) {
+			tp->snd_cwnd++;
+			tp->snd_cwnd_cnt=0;
+		} else
+			tp->snd_cwnd_cnt++;
         }       
 }
 
@@ -684,7 +687,7 @@
 		}
 	} else {
 		tcp_set_rto(tp);
-		tcp_cong_avoid(tp, seq, ack, seq_rtt);
+		tcp_cong_avoid(tp);
 	}
 	/* NOTE: safe here so long as cong_ctl doesn't use rto */
 	tcp_bound_rto(tp);
@@ -803,7 +806,7 @@
 					tcp_set_rto(tp);
 					tcp_bound_rto(tp);
 				}
-				tcp_cong_avoid(tp, seq, ack, seq_rtt);
+				tcp_cong_avoid(tp);
 			}
 		}
 	}
@@ -1457,7 +1460,7 @@
 
 	if ((skb = tp->send_head)) {
 		if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd) &&
-		    tcp_packets_in_flight(tp) < (tp->snd_cwnd >> TCP_CWND_SHIFT)) {
+		    tcp_packets_in_flight(tp) < tp->snd_cwnd) {
 			/* Put more data onto the wire. */
 			tcp_write_xmit(sk);
 		} else if (tp->packets_out == 0 && !tp->pending) {
@@ -1711,10 +1714,6 @@
 	 */
 
 	if (flg == tp->pred_flags && TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {
-		if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
-			tcp_send_ack(sk);
-			goto discard;
-		}
 		if (len <= th->doff*4) {
 			/* Bulk data transfer: sender */
 			if (len == th->doff*4) {
@@ -1728,7 +1727,14 @@
 				goto discard;
 			}
 		} else if (TCP_SKB_CB(skb)->ack_seq == tp->snd_una) {
-			/* Bulk data transfer: receiver */
+			/* Bulk data transfer: receiver
+			 *
+			 * Check if the segment is out-of-window.
+			 * It may be a zero window probe.
+			 */
+			if (!before(TCP_SKB_CB(skb)->seq,
+					tp->rcv_wup + tp->rcv_wnd))
+				goto unacceptable_packet;
 			if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf)
 				goto discard;
 			
@@ -1772,6 +1778,7 @@
 				   TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq,
 				   tp->rcv_wup, tp->rcv_wnd);
 		}
+unacceptable_packet:
 		tcp_send_ack(sk);
 		goto discard;
 	}
diff -u --recursive --new-file v2.1.121/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.121/linux/net/ipv4/tcp_ipv4.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv4/tcp_ipv4.c	Mon Sep 14 22:52:10 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.157 1998/08/28 00:27:47 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.160 1998/09/15 02:11:27 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -817,8 +817,9 @@
 	switch (type) {
 	case ICMP_SOURCE_QUENCH:
 #ifndef OLD_SOURCE_QUENCH /* This is deprecated */
-		tp->snd_ssthresh = max(tp->snd_cwnd >> (1 + TCP_CWND_SHIFT), 2);
-		tp->snd_cwnd = (tp->snd_ssthresh << TCP_CWND_SHIFT);
+		tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2);
+		tp->snd_cwnd = tp->snd_ssthresh;
+		tp->snd_cwnd_cnt = 0;
 		tp->high_seq = tp->snd_nxt;
 #endif
 		return;
@@ -1285,13 +1286,14 @@
 		newtp->last_ack_sent = req->rcv_isn + 1;
 		newtp->backoff = 0;
 		newtp->mdev = TCP_TIMEOUT_INIT;
-		newtp->snd_cwnd = (1 << TCP_CWND_SHIFT);
+		newtp->snd_cwnd = 1;
 		newtp->rto = TCP_TIMEOUT_INIT;
 		newtp->packets_out = 0;
 		newtp->fackets_out = 0;
 		newtp->retrans_out = 0;
 		newtp->high_seq = 0;
 		newtp->snd_ssthresh = 0x7fffffff;
+		newtp->snd_cwnd_cnt = 0;
 		newtp->dup_acks = 0;
 		newtp->delayed_acks = 0;
 		init_timer(&newtp->retransmit_timer);
@@ -1760,7 +1762,8 @@
 	/* See draft-stevens-tcpca-spec-01 for discussion of the
 	 * initialization of these values.
 	 */
-	tp->snd_cwnd = (1 << TCP_CWND_SHIFT);
+	tp->snd_cwnd = 1;
+	tp->snd_cwnd_cnt = 0;
 	tp->snd_ssthresh = 0x7fffffff;	/* Infinity */
 
 	sk->state = TCP_CLOSE;
diff -u --recursive --new-file v2.1.121/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.1.121/linux/net/ipv4/tcp_output.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv4/tcp_output.c	Mon Sep 14 22:52:10 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_output.c,v 1.93 1998/08/26 12:04:32 davem Exp $
+ * Version:	$Id: tcp_output.c,v 1.94 1998/09/15 02:11:36 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -616,7 +616,7 @@
 			/* Stop retransmitting if we've hit the congestion
 			 * window limit.
 			 */
-			if (tp->retrans_out >= (tp->snd_cwnd >> TCP_CWND_SHIFT))
+			if (tp->retrans_out >= tp->snd_cwnd)
 				break;
 		} else {
 			update_retrans_head(sk);
@@ -646,7 +646,7 @@
 		if(tcp_retransmit_skb(sk, skb))
 			break;
 
-		if(tcp_packets_in_flight(tp) >= (tp->snd_cwnd >> TCP_CWND_SHIFT))
+		if(tcp_packets_in_flight(tp) >= tp->snd_cwnd)
 			break;
 next_packet:
 		packet_cnt++;
diff -u --recursive --new-file v2.1.121/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.1.121/linux/net/ipv4/tcp_timer.c	Thu Aug 27 19:56:30 1998
+++ linux/net/ipv4/tcp_timer.c	Mon Sep 14 22:52:10 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_timer.c,v 1.51 1998/05/02 15:19:26 davem Exp $
+ * Version:	$Id: tcp_timer.c,v 1.53 1998/09/15 02:11:39 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -466,8 +466,9 @@
 		/* remember window where we lost
 		 * "one half of the current window but at least 2 segments"
 		 */
-		tp->snd_ssthresh = max(tp->snd_cwnd >> (1 + TCP_CWND_SHIFT), 2);
-		tp->snd_cwnd = (1 << TCP_CWND_SHIFT);
+		tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2);
+		tp->snd_cwnd_cnt = 0;
+		tp->snd_cwnd = 1;
 	}
 
 	tp->retransmits++;
diff -u --recursive --new-file v2.1.121/linux/net/ipv4/timer.c linux/net/ipv4/timer.c
--- v2.1.121/linux/net/ipv4/timer.c	Thu Aug 27 19:56:30 1998
+++ linux/net/ipv4/timer.c	Mon Sep 14 22:52:10 1998
@@ -5,7 +5,7 @@
  *
  *		TIMER - implementation of software timers for IP.
  *
- * Version:	$Id: timer.c,v 1.11 1998/03/19 08:34:06 davem Exp $
+ * Version:	$Id: timer.c,v 1.12 1998/08/28 01:15:29 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
diff -u --recursive --new-file v2.1.121/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.1.121/linux/net/ipv4/udp.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv4/udp.c	Mon Sep 14 22:52:10 1998
@@ -5,7 +5,7 @@
  *
  *		The User Datagram Protocol (UDP).
  *
- * Version:	$Id: udp.c,v 1.61 1998/08/29 17:11:10 freitag Exp $
+ * Version:	$Id: udp.c,v 1.62 1998/09/15 02:11:32 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -500,6 +500,7 @@
 	}
   	
 	switch (type) {
+	case ICMP_TIME_EXCEEDED:
 	case ICMP_SOURCE_QUENCH:
 		return;
 	case ICMP_PARAMETERPROB:
diff -u --recursive --new-file v2.1.121/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.1.121/linux/net/ipv6/af_inet6.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv6/af_inet6.c	Mon Sep 14 22:52:10 1998
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/af_inet.c
  *
- *	$Id: af_inet6.c,v 1.37 1998/08/26 12:04:45 davem Exp $
+ *	$Id: af_inet6.c,v 1.38 1998/09/15 02:11:45 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -54,6 +54,15 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 
+static int unloadable = 0; /* XX: Turn to one when all is ok within the
+			      module for allowing unload */
+
+#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115
+MODULE_AUTHOR("Cast of dozens");
+MODULE_DESCRIPTION("IPv6 protocol stack for Linux");
+MODULE_PARM(unloadable, "i");
+#endif
+
 extern struct proto_ops inet6_stream_ops;
 extern struct proto_ops inet6_dgram_ops;
 
@@ -463,6 +472,7 @@
 #ifdef MODULE
 int ipv6_unload(void)
 {
+	if (!unloadable) return 1;
 	/* We keep internally 3 raw sockets */
 	return __this_module.usecount - 3;
 }
diff -u --recursive --new-file v2.1.121/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.1.121/linux/net/ipv6/tcp_ipv6.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv6/tcp_ipv6.c	Mon Sep 14 22:52:10 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.89 1998/08/28 00:27:54 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.92 1998/09/15 02:11:42 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -1518,7 +1518,8 @@
 	/* See draft-stevens-tcpca-spec-01 for discussion of the
 	 * initialization of these values.
 	 */
-	tp->snd_cwnd = (1 << TCP_CWND_SHIFT);
+	tp->snd_cwnd = 1;
+	tp->snd_cwnd_cnt = 0;
 	tp->snd_ssthresh = 0x7fffffff;
 
 	sk->state = TCP_CLOSE;
diff -u --recursive --new-file v2.1.121/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.1.121/linux/net/ipv6/udp.c	Sat Sep  5 16:46:42 1998
+++ linux/net/ipv6/udp.c	Mon Sep 14 22:52:10 1998
@@ -7,7 +7,7 @@
  *
  *	Based on linux/ipv4/udp.c
  *
- *	$Id: udp.c,v 1.33 1998/08/27 16:55:20 davem Exp $
+ *	$Id: udp.c,v 1.35 1998/09/07 00:13:57 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -317,43 +317,10 @@
 	destroy_sock(sk);
 }
 
-#ifdef CONFIG_FILTER
+#if defined(CONFIG_FILTER) || !defined(HAVE_CSUM_COPY_USER)
 #undef CONFIG_UDP_DELAY_CSUM
 #endif
 
-#ifdef CONFIG_UDP_DELAY_CSUM
-
-/* Please, read comments in net/checksum.h, asm/checksum.h
-
-   I commented out csum_partial_copy_to_user there because it did not
-   verify_area. Now I am even wondered, how clever was I that time 8)8)
-   If I did not it, I would step into this hole again.   --ANK
- */
-
-#ifndef _HAVE_ARCH_COPY_AND_CSUM_TO_USER
-#if defined(__i386__)
-static __inline__
-unsigned int csum_and_copy_to_user (const char *src, char *dst,
-				    int len, int sum, int *err_ptr)
-{
-	int *src_err_ptr=NULL;
-
-	if (verify_area(VERIFY_WRITE, dst, len) == 0)
-		return csum_partial_copy_generic(src, dst, len, sum, src_err_ptr, err_ptr);
-
-	if (len)
-		*err_ptr = -EFAULT;
-
-	return sum;
-}
-#elif defined(__sparc__)
-#define csum_and_copy_to_user csum_partial_copy_to_user
-#else
-#undef CONFIG_UDP_DELAY_CSUM
-#endif
-#endif
-#endif
-
 /*
  * 	This should be easy, if there is something there we
  * 	return it, otherwise we block.
@@ -905,7 +872,7 @@
 	0				/* highestinuse */
 };
 
-__initfunc(void udpv6_init(void))
+void __init udpv6_init(void)
 {
 	inet6_add_protocol(&udpv6_protocol);
 }
diff -u --recursive --new-file v2.1.121/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.121/linux/net/netsyms.c	Sat Sep  5 16:46:42 1998
+++ linux/net/netsyms.c	Sun Sep 13 10:22:17 1998
@@ -305,6 +305,7 @@
 EXPORT_SYMBOL(tcp_connect);
 EXPORT_SYMBOL(tcp_make_synack);
 EXPORT_SYMBOL(tcp_tw_death_row_slot);
+EXPORT_SYMBOL(tcp_sync_mss);
 EXPORT_SYMBOL(net_statistics); 
 
 EXPORT_SYMBOL(xrlim_allow);
diff -u --recursive --new-file v2.1.121/linux/net/sunrpc/svcsock.c linux/net/sunrpc/svcsock.c
--- v2.1.121/linux/net/sunrpc/svcsock.c	Wed Sep  9 14:51:13 1998
+++ linux/net/sunrpc/svcsock.c	Fri Sep 11 11:21:57 1998
@@ -677,7 +677,7 @@
 /*
  * Send out data on TCP socket.
  * FIXME: Make the sendto call non-blocking in order not to hang
- * a daemon on a a dead client. Requires write queue maintenance.
+ * a daemon on a dead client. Requires write queue maintenance.
  */
 static int
 svc_tcp_sendto(struct svc_rqst *rqstp)
diff -u --recursive --new-file v2.1.121/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.1.121/linux/net/unix/af_unix.c	Sat Sep  5 16:46:42 1998
+++ linux/net/unix/af_unix.c	Mon Sep 14 22:52:10 1998
@@ -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.68 1998/08/26 13:18:35 davem Exp $
+ * Version:	$Id: af_unix.c,v 1.69 1998/08/28 01:15:41 davem Exp $
  *
  * Fixes:
  *		Linus Torvalds	:	Assorted bug cures.
