diff -u --recursive --new-file v2.1.76/linux/CREDITS linux/CREDITS
--- v2.1.76/linux/CREDITS	Tue Dec 23 16:30:59 1997
+++ linux/CREDITS	Sun Dec 28 12:05:44 1997
@@ -572,10 +572,12 @@
 
 N: Grant Guenther
 E: grant@torque.net
-D: drivers for parallel port devices: ppa, ez, bpcd
-D: original architect of the parallel-port sharing scheme. 
-S: 906-1001 Bay St.
-S: Toronto, Ontario, M5S 3A6 
+W: http://www.torque.net/linux-pp.html
+D: original author of ppa driver for parallel port ZIP drive
+D: original architect of the parallel-port sharing scheme 
+D: PARIDE subsystem: drivers for parallel port IDE & ATAPI devices
+S: 44 St. Joseph St., Suite 506
+S: Toronto, Ontario, M4Y 2W4
 S: Canada
 
 N: Richard Günther
diff -u --recursive --new-file v2.1.76/linux/Documentation/00-INDEX linux/Documentation/00-INDEX
--- v2.1.76/linux/Documentation/00-INDEX	Wed Nov 26 16:24:01 1997
+++ linux/Documentation/00-INDEX	Sun Dec 28 12:05:44 1997
@@ -32,8 +32,6 @@
 	- info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards.
 exception.txt
 	- how linux v2.1 handles exceptions without verify_area etc.
-ez.txt
-	- documentation for the SyQuest parallel port EZ drive support.
 filesystems/
 	- directory with info on the various filesystems that Linux supports.
 ftape.txt
@@ -70,6 +68,8 @@
 	- short guide on setting up a diskless box with NFS root filesystem
 oops-tracing.txt
 	- how to decode those nasty internal kernel error dump messages.
+paride.txt
+	- information about the parallel port IDE subsystem.
 parport.txt
 	- how to use the parallel-port driver.
 ramdisk.txt
diff -u --recursive --new-file v2.1.76/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.76/linux/Documentation/Configure.help	Sun Dec 21 22:36:11 1997
+++ linux/Documentation/Configure.help	Wed Dec 31 11:38:59 1997
@@ -18,6 +18,9 @@
 # is a work-in-progress effort of the Italian translation team,
 # currently only for the 2.0 version of this file, maintained 
 # by rubini@linux.it.
+#   - http://www.cs.net.pl/~cezar/Kernel is the beginning of a Polish
+# translation of the 2.0 version of this file, maintained by Cezar
+# Cichocki (cezar@cs.net.pl).
 #
 # Information about what a kernel is, what it does, how to patch and
 # compile it and much more is contained in the Kernel-HOWTO, available
@@ -112,7 +115,7 @@
   running kernel whenever you want). The module will be called
   floppy.o. If you want to compile it as a module, say M here and read
   Documentation/modules.txt.
-  
+
 RAM disk support
 CONFIG_BLK_DEV_RAM
   Saying Y here will allow you to use a portion of your RAM memory as
@@ -150,12 +153,18 @@
 
 Network Block Device support
 CONFIG_BLK_DEV_NBD
-  Saying Y here will allow computer to serve as client for network
-  block device - it will be able to use block devices exported by
-  servers (mount filesystems on them etc.). It also allows you to run
-  a block-device in userland (making server and client physicaly the same
-  computer, communicating using loopback). Normal users say N
-  here. Read Documentation/nbd.txt.
+  Saying Y here will allow your computer to serve as a client for
+  network 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 a special file such as /dev/nd0. It also allows you to run
+  a block-device in userland (making server and client physically the
+  same computer, communicating using loopback). 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
+  nbd.o. Normal users say N here. Read Documentation/nbd.txt.
 
 Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support
 CONFIG_BLK_DEV_IDE
@@ -163,11 +172,11 @@
   interfaces, each being able to serve a "master" and a "slave"
   device, for a combination of up to eight IDE disk/cdrom/tape/floppy
   drives.  Useful information about large (>540MB) IDE disks,
-  soundcard IDE ports, module support, and other topics, is all
+  soundcard IDE ports, module support, and other topics, is 
   contained in Documentation/ide.txt.  If you have one or more IDE
   drives, say Y here.  If your system has no IDE drives, or if memory
   requirements are really tight, you could say N here, and select the
-  Old harddisk driver instead to save about 13kB of memory in the
+  "Old harddisk driver" instead to save about 13kB of memory in the
   kernel.  To fine-tune IDE drive/interface parameters for improved
   performance, look for the hdparm package at
   sunsite.unc.edu:/pub/Linux/kernel/patches/diskdrives/
@@ -248,18 +257,18 @@
 
 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,
-  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 through
-  the SCSI emulation). At boot time, the FLOPPY drive will be
-  identified along with other IDE devices, as "hdb" or "hdc", or
-  something similar. If you want to compile the driver as a module ( =
-  code which can be inserted in and removed from the running kernel
-  whenever you want), say M here and read Documentation/modules.txt.
-  The module will be called ide-floppy.o.
-  
+  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,
+  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 through the SCSI
+  emulation). At boot time, the FLOPPY drive will be identified along
+  with other IDE devices, as "hdb" or "hdc", or something similar. If
+  you want to compile the driver as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt.  The module will be
+  called ide-floppy.o.
+
 SCSI emulation support
 CONFIG_BLK_DEV_IDESCSI
   This will provide SCSI host adapter emulation for IDE ATAPI devices,
@@ -316,14 +325,25 @@
 Generic PCI bus-master DMA support
 CONFIG_BLK_DEV_IDEDMA
   If your PCI IDE controller is capable of bus-master DMA
-  (Direct Memory Access) transfers (most newer systems),
-  then you will want to enable this option to reduce CPU overhead.
+  (Direct Memory Access) transfers (most newer systems are),
+  then you will want to say Y here to reduce CPU overhead.
   With this option, Linux will automatically enable DMA transfers
   in most cases, noting this with "DMA" appended to the drive
   identification info.  You can also use the "hdparm" utility to
   enable DMA for drives which were not enabled automatically.
   You can get the latest version of the hdparm utility via anonymous
   FTP from sunsite.unc.edu/pub/Linux/system/hardware/
+   It is safe to say Y to this question.
+
+  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 enable this option to allow use of 
+  bus-mastering DMA data transfers. Read the comments at the
+  beginning of drivers/block/idedma.c and Documentation/ide.txt.
+  You can get the latest version of the hdparm utility via 
+  ftp (user: anonymous) from
+  sunsite.unc.edu/pub/Linux/kernel/patches/diskdrives/; it is
+  used to tune your harddisk. 
   It is safe to say Y to this question.
 
 Other IDE chipset support
@@ -341,8 +361,9 @@
   Certain older chipsets, including the Tekram 690CD, use a
   single set of I/O ports at 0x1f0 to control up to four drives,
   instead of the customary two drives per port.  Support for this
-  is enabled at runtime using the "ide0=four" kernel boot parameter.
-  
+  can be enabled at runtime using the "ide0=four" kernel boot
+  parameter if you say Y here.
+
 DTC-2278 support
 CONFIG_BLK_DEV_DTC2278
   This driver is enabled at runtime using the "ide0=dtc2278" kernel
@@ -368,17 +389,15 @@
   "ide0=dc4030" kernel boot parameter.  See the Documentation/ide.txt
   and drivers/block/pdc4030.c files for more info.
 
-Tekram TRM290 support (EXPERIMENTAL)
 CONFIG_BLK_DEV_TRM290
   This driver adds support for bus master DMA transfers
   using the Tekram TRM290 PCI IDE chip.  Volunteers are
   needed for further tweaking and development.
   Please read the comments at the top of drivers/block/trm290.c.
 
-OPTi 82C621 support (EXPERIMENTAL)
+OPTi 82C621 enhanced support (EXPERIMENTAL)
 CONFIG_BLK_DEV_OPTI621
-  This driver allows use of hdparm to change the PIO timings
-  for drives attached to an OPTi MIDE controller.
+  This is a driver for the OPTi 82C621 EIDE controller.
   Please read the comments at the top of drivers/block/opti621.c.
 
 NS87415 support (EXPERIMENTAL)
@@ -417,22 +436,166 @@
   Documentation/modules.txt. The module will be called xd.o. It's
   pretty unlikely that you have one of these: say N.
 
-SyQuest EZ parallel port disk support
-CONFIG_BLK_DEV_EZ
-  If you have a parallel port version of SyQuest's EZ135 or EZ230
-  removable media devices you can use this driver.  Answer Y to build
-  the driver into the kernel, or M if you would like to build it as a
-  loadable module. The module will be called ez.o.  Read the file
-  linux/Documentation/ez.txt.  It is possible to use several devices
-  with a single common parallel port (e.g. printer and EZ135); it is
-  safe to compile both drivers into the kernel.
+Parallel port IDE device support
+CONFIG_PARIDE
+  There are many external CD-ROM and disk devices that connect
+  through your computer's parallel port.  Most of them are actually
+  IDE devices using a parallel port IDE adapter.  This option enables
+  the PARIDE subsystem which contains drivers for many of these
+  external drives.  Read linux/Documentation/paride.txt for more
+  information.  If you have enabled the parallel port support general
+  configuration option, you may share a single port between your
+  printer and other parallel port devices.  Answer Y to build PARIDE 
+  support into your kernel, or M if you would like to build it as a 
+  loadable module.  If your parallel port support is in a loadable 
+  module, you must build PARIDE as a module.  If you built PARIDE
+  support into your kernel, you may still build the individual 
+  protocol modules and high-level drivers as loadable modules.  To
+  use the PARIDE support, you must have this module as well as at
+  least one protocol module and one high-level driver.  If you build
+  this support as a module, it will be called paride.o.
+
+Parallel port IDE disks
+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 
+  parallel port IDE driver, otherwise you should answer M to build 
+  it as a loadable module.  The module will be called pd.o.  You 
+  must also have at least one parallel port protocol driver in your 
+  system.  Among the devices supported by this driver are the SyQuest 
+  EZ-135, EZ-230 and SparQ drives, the Avatar Shark and the backpack
+  hardrives from MicroSolutions.
+
+Parallel port ATAPI CD-ROMs
+CONFIG_PARIDE_PCD 
+  This option enables the high-level driver for ATAPI CD-ROM devices
+  connected through a parallel port.  If you chose to build PARIDE
+  support into your kernel, you may answer Y here to build in the
+  parallel port ATAPI CD-ROM driver, otherwise you should answer M
+  to build it as a loadable module.  The module will be called pcd.o.
+  You must also have at least one parallel port protocol driver in
+  your system.  Among the devices supported by this driver are the
+  MicroSolutions backpack CD-ROM drives and the Freecom Power CD.
+
+Parallel port ATAPI disks
+CONFIG_PARIDE_PF 
+  This option enable the high-level driver for ATAPI disk devices
+  connected through a parallel port.  If you chose to build PARIDE
+  support into your kernel, you may answer Y here to build in the
+  parallel port ATAPI disk driver, otherwise you should answer M
+  to build it as a loadable module.  The module will be called pf.o.
+  You must also have at least one parallel port protocol driver in
+  your system.  Among the devices supported by this driver are the
+  MicroSolutions backpack PD/CD drive and the Imation Superdisk
+  LS-120 drive.
+
+ATEN EH-100 protocol
+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 
+  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 aten.o.  You must also 
+  have a high-level driver for the type of device that you want to 
+  support.
+
+MicroSolutions backpack protocol
+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
+  driver, otherwise you should answer M to build it as a loadable
+  module.  The module will be called bpck.o.  You must also have
+  a high-level driver for the type of device that you want to support.
+
+DataStor Commuter protocol
+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
+  driver, otherwise you should answer M to build it as a loadable
+  module.  The module will be called comm.o.  You must also have
+  a high-level driver for the type of device that you want to support.
+
+DataStor EP-2000 protocol
+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
+  driver, otherwise you should answer M to build it as a loadable
+  module.  The module will be called dstr.o.  You must also have
+  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.
+
+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.
+
+FreeCom power protocol
+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 
+  should answer M to build it as a loadable module.  The module will be 
+  called frpw.o.  You must also have a high-level driver for the type 
+  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.
+
+OnSpec 90c20 protocol
+CONFIG_PARIDE_ON20 
+  This option enables support for the (obsolete) 90c20 parallel port 
+  IDE protocol from OnSpec (often marketted under the ValuStore brand
+  name).  If you chose to build PARIDE support into your kernel, you 
+  may answer Y here to build in the protocol driver, otherwise you 
+  should answer M to build it as a loadable module.  The module will 
+  be called on20.o.  You must also have a high-level driver for the 
+  type of device that you want to support.
+
+OnSpec 90c26 protocol
+CONFIG_PARIDE_ON26 
+  This option enables support for the 90c26 parallel port IDE protocol 
+  from OnSpec Electronics (often marketted under the ValuStore brand
+  name).  If you chose to build PARIDE support into your kernel, you 
+  may answer Y here to build in the protocol driver, otherwise you 
+  should answer M to build it as a loadable module.  The module will 
+  be called on26.o.  You must also have a high-level driver for the 
+  type of device that you want to support.
 
 Multiple devices driver support
 CONFIG_BLK_DEV_MD
   This driver lets you combine several harddisk partitions into one
   logical block device. Information about how and why to use it and the
   necessary tools are available over ftp (user: anonymous) from
-  sweet-smoke.ufr-info-p7.ibp.fr/pub/public/Linux in the md package
+  sweet-smoke.ufr-info-p7.ibp.fr/pub/Linux in the md package
   and the md-FAQ. Please read drivers/block/README.md. If unsure, say
   N.
 
@@ -463,13 +626,18 @@
   A RAID-1 set consists of several disk drives which are exact copies
   of each other. In the event of a mirror failure, the RAID driver
   will continue to use the operational mirrors in the set, providing
-  an error free MD device to the higher levels of the kernel. In
-  a set with N drives, the available space is the capacity of a single
-  drive, and the set protects against a failure of (N - 1) drives.
-  raidtools, a set of user-space tools which create and maintain
-  RAID1/4/5 sets, is available at:
+  an error free MD (multiple device) to the higher levels of the
+  kernel. In a set with N drives, the available space is the capacity
+  of a single drive, and the set protects against a failure of (N - 1)
+  drives.  raidtools, a set of user-space tools which create and
+  maintain RAID1/4/5 sets, is available at:
   ftp://ftp.kernel.org/pub/linux/daemons/raid
   http://luthien.nuclecu.unam.mx/~miguel/raid
+  If you want to use such a RAID-1 set say Y. This code is also
+  available as a module called raid1.o ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt. If unsure, say Y.
 
 RAID-4/RAID-5 mode
 CONFIG_MD_RAID5
@@ -478,12 +646,17 @@
   of a single drive. For a given sector (row) number, (N - 1) drives
   contain data sectors, and one drive contains the parity protection.
   For a RAID-4 set, the parity blocks are present on a single drive,
-  while a RAID-5 set distributes the parity accross the drives in one
+  while a RAID-5 set distributes the parity across the drives in one
   of the available parity distribution methods.
   raidtools, a set of user-space tools which create and maintain
   RAID1/4/5 sets, is available at:
   ftp://ftp.kernel.org/pub/linux/daemons/raid
   http://luthien.nuclecu.unam.mx/~miguel/raid
+  If you want to use such a RAID-5 set, say Y. This code is also
+  available as a module called raid5.o ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt. If unsure, say Y.
 
 Support for Deskstation RPC44 
 CONFIG_DESKSTATION_RPC44
@@ -561,30 +734,7 @@
   kernel, you should consider updating your networking tools too
   because changes in the kernel and the tools often go hand in hand;
   see http://www.inka.de/sites/lina/linux/NetTools/index_en.html for
-  details.
-
-Packet socket
-CONFIG_PACKET
-  Packet protocol is used by applications directly communicating
-  to network devices f.e. tcpdump. You want them to work, choose Y.
-  If you run kerneld, you might want to choose M.
-
-Kernel/User netlink socket
-CONFIG_NETLINK
-  This protocol family is used for bi-directional communication
-  between kernel and user level daemons. This option is unlikely to
-  be useful for common workstation, but if you configure router or
-  firewall, do not hesitate: press Y.
-
-Routing messages
-CONFIG_RTNETLINK
-  One of netlink protocols used by kernel routing engine.
-  You will need it to use advanced routing features.
-
-Netlink device emulation
-CONFIG_NETLINK_DEV
-  It is backward compatibility option, choose Y for now.
-  This option will be removed soon.
+  details. 
 
 Network aliasing
 CONFIG_NET_ALIAS
@@ -603,7 +753,7 @@
 
 Socket filtering
 CONFIG_FILTER
-  The Linux Socket Filter is a deviation of the Berkely Packet Filter.
+  The Linux Socket Filter is derived from the Berkeley Packet Filter.
   Through Socket Filtering you can have the kernel decide whether the
   data is good and to continue processing it. Linux Socket Filtering 
   works on all socket types except TCP for now. See the text file
@@ -642,7 +792,7 @@
   to continue to connect, even when your machine is under attack.
   There is no need for the legitimate users to change their TCP/IP
   software; SYN cookies work transparently to them.  For technical
-  information about syn cookies, check out
+  information about SYN cookies, check out
   ftp://koobera.math.uic.edu/pub/docs/syncookies-archive.
   If you say Y here, note that SYN cookies aren't enabled by default:
   you need to add the command
@@ -781,11 +931,12 @@
 
 PCI BIOS support
 CONFIG_PCI_BIOS
-  If you have enabled PCI bus support above, you probably want to allow
-  Linux to use your PCI BIOS to detect the PCI devices and determine
-  their configuration. Note: some old PCI motherboards have BIOS bugs
-  and may crash if this switch is enabled -- for such motherboards,
-  you should disable PCI BIOS support and use direct PCI access instead.
+  If you have enabled PCI bus support above, you probably want to
+  allow Linux to use your PCI BIOS to detect the PCI devices and
+  determine their configuration. Note: some old PCI motherboards have
+  BIOS bugs and may crash if this switch is enabled -- for such
+  motherboards, you should say N here and say Y to "PCI direct access
+  support" instead.
   Except for some special cases (embedded systems with no BIOS), you
   probably should say Y here.
 
@@ -801,9 +952,9 @@
 PCI bridge optimization (experimental)
 CONFIG_PCI_OPTIMIZE
   This can improve access times for some hardware devices if you have
-  a really broken BIOS and your computer uses a PCI bus system. Set to Y
-  if you think it might help, but try turning it off if you experience
-  any problems with the PCI bus.
+  a really broken BIOS and your computer uses a PCI bus system. Set to
+  Y if you think it might help, but try turning it off if you
+  experience any problems with the PCI bus. N is the safe answer.
 
 MCA support
 CONFIG_MCA
@@ -856,7 +1007,7 @@
   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
   programs like lynx or netscape).  If you find that after upgrading
-  to Linux kernel 1.3 and saying Y here, you still can't run any ELF
+  from Linux kernel 1.2 and saying Y here, you still can't run any ELF
   binaries (they just crash), then you'll have to install the newest
   ELF runtime libraries, including ld.so (check the file
   Documentation/Changes for location and latest version). If you want
@@ -886,18 +1037,45 @@
   because some crucial programs on your system might still be in A.OUT
   format.
 
-Kernel support for JAVA binaries
+Kernel support for JAVA binaries (obsolete)
 CONFIG_BINFMT_JAVA
-  This option is obsolete. Use binfmt_misc instead. It is more 
-  flexible. 
+  JAVA(tm) is an object oriented programming language developed by
+  SUN; JAVA programs are compiled into "JAVA bytecode" binaries which
+  can then be interpreted by run time systems on many different
+  operating systems.  These JAVA binaries are becoming a universal
+  executable format. If you want to execute JAVA binaries, read the
+  Java on Linux HOWTO, available via ftp (user: anonymous) at
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. You will then need to install
+  the run time system contained in the Java Developers Kit (JDK) as
+  described in the HOWTO. This is completely independent of the Linux
+  kernel and you do NOT need to say Y here for this to work.
+  Saying Y here allows you to execute a JAVA bytecode binary just like
+  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
+  "<!--applet-->". If you want to use this, say Y here and read
+  Documentation/java.txt. If you disable this option it will reduce
+  your kernel by about 4kB. This is not much and by itself does not
+  warrant removing support. However its removal is a good idea if you
+  do not have the JDK installed. You may answer M for module support
+  and later load the module when you install the JDK or find an
+  interesting Java program that you can't live without. The module
+  will be called binfmt_java.o. 
+  The complete functionality of this Java support is also provided by
+  the more general option "Kernel support for MISC binaries",
+  below. This option is therefore considered obsolete and you probably
+  want to say N here and Y to "Kernel support for MISC binaries" if
+  you're interested in Java.
 
 Kernel support for Linux/Intel ELF binaries
 CONFIG_BINFMT_EM86
   Say Y here if you want to be able to execute Linux/Intel ELF
-  binaries just like native Alpha binaries on your machine. For this
-  to work, you need to have the emulator /usr/bin/em86 in place.  You
-  may answer M to compile the emulation support as a module and later
-  load the module when you want to use a Linux/Intel binary. The
+  binaries just like native Alpha binaries on your Alpha machine. For
+  this to work, you need to have the emulator /usr/bin/em86 in place.
+  You may answer M to compile the emulation support as a module and
+  later load the module when you want to use a Linux/Intel binary. The
   module will be called binfmt_em86.o.  If unsure, say Y.
 
 Kernel support for MISC binaries
@@ -919,6 +1097,15 @@
   you have use for it.  
   If you don't know what to answer at this point, say Y.
 
+Solaris binary emulation
+CONFIG_SOLARIS_EMUL
+  This is experimental code which will enable you to run (many)
+  Solaris binaries on your Sparc Linux machine.  This code 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 solaris.o. If you want to compile it as a module, say M here
+  and read Documentation/modules.txt.
+
 Processor family
 CONFIG_M386
   This is the processor type of your CPU. This information is used for
@@ -928,7 +1115,11 @@
   then the kernel will run on all of these CPUs: 486 and Pentium
   (=586) and Pentium Pro (=686). In rare cases, it can make sense to
   specify "Pentium" even if running on a 486: the kernel will be
-  smaller but slower. If you don't know what to do, say "386".
+  smaller but slower. 
+  If you have a multiple processor machine and want Linux to use all
+  the processors in parallel, set the SMP variable in the toplevel
+  kernel Makefile.
+  If you don't know what to do, say "386".
 
 Video mode selection support
 CONFIG_VIDEO_SELECT
@@ -950,23 +1141,34 @@
   If you want to use devices connected to your parallel port (the
   connector at the computers with 25 holes), e.g. printer, Zip drive,
   PLIP link etc., then you need to enable this option; please read
-  Documentation/parport.txt and drivers/misc/BUGS-parport.  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
-  whenever you want), say M here and read
-  Documentation/modules.txt. The module will be called parport.o.  If
-  you have more than one parallel port and want to specify which port
-  and IRQ to use by this driver at module load time, read
-  Documentation/networking/net-modules.txt.
+  Documentation/parport.txt and drivers/misc/BUGS-parport.  For
+  extensive information about drivers for many devices attaching to
+  the parallel port see http://www.torque.net/linux-pp.html on the WWW
+  (To browse the WWW, you need to have access to a machine on the
+  Internet that has a programs like lynx or netscape). 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 whenever you
+  want), say M here and read Documentation/modules.txt. The module
+  will be called parport.o.  If you have more than one parallel port
+  and want to specify which port and IRQ to use by this driver at
+  module load time, read Documentation/networking/net-modules.txt.
 
 PC-style hardware 
 CONFIG_PARPORT_PC
   You should enable this option if you have a PC-style parallel
   port. All IBM PC compatible computers and some Alphas have PC-style
-  parallel ports.  This driver is also available as a module which
-  will be called parport_pc.o.
+  parallel ports.  This code is also available as a module. If you
+  want to it as a module ( = code which can be inserted in and removed
+  from the running kernel whenever you want), say M here and read
+  Documentation/modules.txt. The module will be called parport_pc.o.
+
+Sun Ultra/AX-style hardware 
+CONFIG_PARPORT_AX
+  Say Y here if you need support for the parallel port hardware on Sun
+  Ultra/AX machines.  This code is also available as a module (say M),
+  called parport_ax.o.  If in doubt, saying N is the safe plan.
 
 Compile the kernel into the ELF object format 
 CONFIG_ELF_KERNEL
@@ -996,9 +1198,9 @@
 
 Auto-probe for parallel devices
 CONFIG_PNP_PARPORT
-  Some IEEE-1284 conformant parallel-port devices can identify themselves
-  when requested.  If this option is enabled the kernel will probe to see
-  what devices are connected at boot time.
+  Some IEEE-1284 conformant parallel-port devices can identify
+  themselves when requested.  Say Y to enable this feature, or M to
+  compile it as a module (parport_ieee1284.o).  If in doubt, say N.
 
 Plug and Play subsystem (EXPERIMENTAL)
 CONFIG_PNP_DRV
@@ -1031,7 +1233,7 @@
   Plug and Play ISA devices. This includes full support for PnP ISA,
   including the I/O range check feature.
 
-PnP ISA backwards-compatiblity support
+PnP ISA backwards-compatibility support
 CONFIG_PNP_ISA_COMPAT
   This option will enable partial backwards compatibility with drivers
   written using older versions (up to the last 0.2.x) of the PnP driver
@@ -1044,18 +1246,18 @@
   These cards used somewhat proprietary mechanisms for configuring
   IRQs, DMAs, IO addresses, and memory ranges. These devices (mainly
   network cards, but also some sound card) can be configured as any
-  other PnP device can by enabling this option, if appropriate drivers
+  other PnP device can by saying Y here, if appropriate drivers
   for these devices are available.
 
 PnP sysctl support (RECOMMENDED)
 CONFIG_PNP_SYSCTL
   This option enables support for the user-mode interface to the
-  kernel-mode PnP systems. It requires that you enable CONFIG_SYSCTL.
-  The only reason you might want to switch this off is if you aren't
-  going to use user-mode utilities to configure PnP, and you want to
-  save a couple of kilobytes of kernel space. Answer Y unless you know
-  what you are doing. User-mode utilities and a library for accessing
-  this interface may be found at
+  kernel-mode PnP systems. It requires that you said Y to "Sysctl
+  support" above.  The only reason you might want to switch this off
+  is if you aren't going to use user-mode utilities to configure PnP,
+  and you want to save a couple of kilobytes of kernel space. Answer Y
+  unless you know what you are doing. User-mode utilities and a
+  library for accessing this interface may be found at
   http://www.lpsg.demon.co.uk/pnp-linux.html.
 
 PnP auto-configures all devices on startup
@@ -1082,10 +1284,10 @@
   kernel. Saying Y here makes it possible, and safe, to use the
   same modules even after compiling a new kernel; this requires the
   program modprobe. All the software needed for module support is in
-  the modules package (check the file Documentation/Changes for
+  the modutils package (check the file Documentation/Changes for
   location and latest version).  NOTE: if you say Y here but don't
   have the program genksyms (which is also contained in the above
-  mentioned modules package), then the building of your kernel will
+  mentioned modutils package), then the building of your kernel will
   fail.  If you are going to use modules that are generated from
   non-kernel sources, you would benefit from this option. Otherwise
   it's not that important. So, N ought to be a safe bet.
@@ -1100,7 +1302,7 @@
   "kerneld" will also automatically unload all unused modules, so you
   don't have to use "rmmod" either.  kerneld will also provide support
   for different user-level beeper and screen blanker programs later
-  on.  The "kerneld" daemon is included in the modules package (check
+  on.  The "kerneld" daemon is included in the modutils package (check
   Documentation/Changes for latest version and location). You will
   probably want to read the kerneld mini-HOWTO, available via ftp
   (user: anonymous) from
@@ -1150,17 +1352,80 @@
   you need to have access to a machine on the Internet that has a
   program like lynx or netscape). Information about the multicast
   capabilities of the various network cards is contained in
-  drivers/net/README.multicast. For most people, it's safe to say N.
+  Documentation/networking/multicast.txt. For most people, it's safe
+  to say N.
+
+IP: advanced router
+CONFIG_IP_ADVANCED_ROUTER
+  If you intend to run your Linux box mostly as a router, i.e. as a
+  computer that forwards and redistributes network packets, say Y; you
+  will then be presented with several options that allow more precise
+  control about the routing process.
+  The answer to this question won't directly affect the kernel: saying
+  N will just cause this configure script to skip all the questions
+  about advanced routing.
+  Note that your box can only act as a router if you say Y to "/proc
+  filesystem support" below and if you enable IP forwarding in your
+  kernel; you can do this from within a boot-time script like so:
+    echo "1" > /proc/sys/net/ipv4/ip_forwarding
+  after the /proc filesystem has been mounted.  
+  If unsure, say N here.
+
+IP: policy routing
+CONFIG_IP_MULTIPLE_TABLES
+  Normally, a router decides what to do with a received packet based
+  solely on the packet's final destination address. If you say Y here,
+  routing can also take into account the originating address and the
+  network device from which the packet reached us.
+
+IP: equal cost multipath
+CONFIG_IP_ROUTE_MULTIPATH
+  Normally, the routing tables specify a single action to be taken in
+  a deterministic manner for a given packet. If you say Y here
+  however, it becomes possible to attach several actions to a packet
+  pattern, in effect specifying several alternative paths to travel
+  for those packets. The router considers all these paths to be of
+  equal "cost" and chooses one of them in a non-deterministic fashion
+  if a matching packet arrives.
+
+IP: use TOS value as routing key
+CONFIG_IP_ROUTE_TOS
+  The header of every IP packet carries a TOS (Type of Service) value
+  with which the packet requests a certain treatment, e.g. low latency
+  (for interactive traffic), high throughput, or high
+  reliability. Normally, these values are ignored, but if you say Y
+  here, you will be able to specify different routes for packets with
+  different TOS values.
+
+IP: verbose route monitoring
+CONFIG_IP_ROUTE_VERBOSE
+  If you say Y here, which is recommended, then the kernel will print
+  verbose messages regarding the routing, for example warnings about
+  received packets which look strange and could be evidence of an
+  attack or a misconfigured system somewhere. The information is
+  handled by the klogd demon which is responsible for kernel messages
+  ("man klogd").
+
+IP: large routing tables
+CONFIG_IP_ROUTE_LARGE_TABLES
+  If you have routing zones that grow to more than about 64 entries,
+  you may want to say Y here to speed up the routing process.
+
+IP: fast network address translation
+CONFIG_IP_ROUTE_NAT
+  If you say Y here, your router will be able to modify source and
+  destination addresses of packets that pass through it.
 
 IP: optimize as router not host
 CONFIG_IP_ROUTER
   Some Linux network drivers use a technique called copy and checksum
-  to optimize host performance. For a machine which is forwarding most
-  packets to another host this is however a loss. This parameter turns
-  off copy and checksum from devices. It may make other changes in the
-  future.
+  to optimize host performance. For a machine which acts a router most
+  of the time and is forwarding most packets to another host this is
+  however a loss. If you say Y here, copy and checksum will be
+  switched off. In the future, it may make other changes which
+  optimize for router operation.
   Note that your box can only act as a router if you say Y to "/proc
-  filesystem support" below and you enable IP forwarding in your
+  filesystem support" below and if you enable IP forwarding in your
   kernel; you can do this from within a boot-time script like so:
     echo "1" > /proc/sys/net/ipv4/ip_forwarding
   after the /proc filesystem has been mounted.  If unsure, say N here.
@@ -1179,21 +1444,22 @@
   support" below and IP forwarding is enabled in your kernel; do this
   from within a boot-time script like so: 
     echo "1" > /proc/sys/net/ipv4/ip_forwarding
-  after the /proc filesystem has been mounted.  You need to say Y to
-  "IP firewalling" in order to be able to use IP masquerading
-  (i.e. local computers can chat with an outside host, but that
-  outside host is made to think that it is talking to the firewall box
-  -- makes the local network completely invisible and avoids the need
-  to allocate valid IP host addresses for the machines on the local
-  net) and IP packet accounting (keeping track of what is using all
-  your network bandwidth) and IP transparent proxying (makes the
-  computers on the local 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). If unsure, say N.
+  after the /proc filesystem has been mounted.  
+  You need to say Y to "IP firewalling" in order to be able to use IP
+  masquerading (masquerading means that local computers can chat with
+  an outside host, but that outside host is made to think that it is
+  talking to the firewall box -- makes the local network completely
+  invisible and avoids the need to allocate valid IP host addresses
+  for the machines on the local net) and IP packet accounting (keeping
+  track of what is using all your network bandwidth) and IP
+  transparent proxying (makes the computers on the local 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). If
+  unsure, say N.
 
 IP: firewall packet netlink device
 CONFIG_IP_FIREWALL_NETLINK
-  If you say Y here and when packets hit your Linux firewall and are
+  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
@@ -1219,8 +1485,8 @@
   of the routing table during kernel boot, based on either information
   supplied at the kernel command line or by BOOTP or RARP protocols.
   You need to say Y only for diskless machines requiring network access
-  to boot (see CONFIG_ROOT_NFS for more information about root volume
-  mounted via NFS), because all other machines configure the network in
+  to boot (in which case you want to say Y to "Root file system on
+  NFS" as well), because all other machines configure the network in
   their startup scripts.
 
 BOOTP support
@@ -1265,18 +1531,22 @@
 
 IP: GRE tunnels over IP
 CONFIG_NET_IPGRE
-  Another kind of tunneling protocol - "Generic Routing Encapsulation".
-  It allows to tunnel any networking protocol over existing IPv4
-  infrastructure. At the moment only IPv4 and IPv6 are supported.
-  It is useful, if another endpoint is Cisco router: it likes
-  GRE much more than IPIP and, particularly, allows multicasts
-  redistribution over GRE tunnels.
+  Tunneling means encapsulating data of one protocol type within
+  another protocol and sending it over a channel that understands the
+  encapsulating protocol. This particular tunneling driver implements
+  GRE (Generic Routing Encapsulation) and at this time allows
+  encapsulating of IPv4 or IPv6 over existing IPv4
+  infrastructure. This driver is useful if the other endpoint is a
+  Cisco router: Cisco likes GRE much better than the other Linux
+  tunneling driver ("IP: tunneling" above). In addition, GRE allows
+  multicast redistribution through the tunnel.
 
 IP: broadcast GRE over IP
 CONFIG_NET_IPGRE_BROADCAST
-  One application of GRE/IP, allowing to construct broadcast LAN,
-  looking like ethernet network, distributed over the Internet.
-  It requires, that your domain supported multicast routing.
+  One application of GRE/IP is to construct a broadcast WAN (Wide Area
+  Network), which looks like a normal1 ethernet LAN (Local Area
+  Network), but can be distributed all over the Internet. If you want
+  to do that, say Y here and to "IP: multicast routing" below.
 
 IP: firewall packet logging
 CONFIG_IP_FIREWALL_VERBOSE
@@ -1312,7 +1582,7 @@
   Linux box to the Internet using SLiRP [SLiRP is a SLIP/PPP emulator
   that works if you have a regular dial up shell account on some UNIX
   computer; get it via ftp (user: anonymous) from
-  ftp://sunsite.unc.edu/pub/Linux/system/Network/serial/].)  Details
+  ftp://sunsite.unc.edu/pub/Linux/system/network/serial/].)  Details
   on how to set things up are contained in the IP Masquerading FAQ,
   available at http://www.indyramp.com/masq/. If you say Y here, then
   the modules ip_masq_ftp.o (for ftp transfers through the firewall),
@@ -1324,23 +1594,27 @@
 
 IP: ICMP masquerading
 CONFIG_IP_MASQUERADE_ICMP
-  The basic masquerade code described for CONFIG_IP_MASQUERADE only
+  The basic masquerade code described for "IP: masquerading" above only
   handles TCP or UDP packets (and ICMP errors for existing 
   connections).  This option adds additional support for masquerading
   ICMP packets, such as ping or the probes used by the Windows 95
   tracert program.
-  If you want this, say Y.
+  If you want this, say Y. 
 
 IP: ipautofw masquerade support
 CONFIG_IP_MASQUERADE_IPAUTOFW (Experimental)
-  ipautofw is a program by Richard Lynch allowing additional
-  support for masquerading protocols which do not (as yet)
-  have additional protocol helpers.  
-  Information and source for ipautofw is available from
+  ipautofw is a program by Richard Lynch allowing additional support
+  for masquerading protocols which do not (as yet) have their own
+  additional protocol helpers.  Information and source for ipautofw is
+  available via ftp (user: anonymous) from
   ftp://ftp.netis.com/pub/members/rlynch/
   The ipautofw code is still under development and so is currently
   marked EXPERIMENTAL.
-  If you want this, say Y.
+  If you want this, say Y. This code 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 ip_masq_autofw.o. If
+  you want to compile it as a module, say M here and read
+  Documentation/modules.txt.
 
 IP: ipportfw masquerade support
 CONFIG_IP_MASQUERADE_IPPORTFW
@@ -1348,10 +1622,16 @@
   to allow some forwarding of packets from outside to inside a
   firewall on given ports. Information and source for ipportfw is
   available from
-  http://www.monmouth.demon.co.uk/ipsubs/portforwarding.html
+  http://www.monmouth.demon.co.uk/ipsubs/portforwarding.html (to
+  browse the WWW, you need to have access to a machine on the Internet
+  that has a program like lynx or netscape).
   The portfw code is still under development and so is currently
   marked EXPERIMENTAL.
-  If you want this, say Y.
+  If you want this, say Y. This code 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 ip_masq_portfw.o. If
+  you want to compile it as a module, say M here and read
+  Documentation/modules.txt.
 
 IP: always defragment 
 CONFIG_IP_ALWAYS_DEFRAG
@@ -1397,23 +1677,26 @@
   audio and video broadcasts. In order to do that, you would most
   likely run the program mrouted. Information about the multicast
   capabilities of the various network cards is contained in
-  drivers/net/README.multicast. If you haven't heard about it, you
-  don't need it.
+  Documentation/networking/multicast.txt. If you haven't heard about
+  it, you don't need it.
 
 IP: PIM-SM version 1 support
 CONFIG_IP_PIMSM_V1
-  Kernel side support for Sparse Mode PIM version 1. This multicast
-  routing protocol is used widely due to Cisco supports it.
-  You need special software to use it (pimd-v1). Press N, if
-  you do not want to use PIM-SM v1. Note, that Dense Mode PIM
-  need not this option.
+  Kernel side support for Sparse Mode PIM (Protocol Independent
+  Multicast) version 1. This multicast routing protocol is used widely
+  because Cisco supports it.  You need special software to use it
+  (pimd-v1). Please see http://netweb.usc.edu/pim/ for more
+  information about PIM (to browse the WWW, you need to have access to
+  a machine on the Internet that has a program like lynx or
+  netscape). Say Y if you want to use PIM-SM v1. Note that you can say
+  N here if you just want to use Dense Mode PIM.
 
 IP: PIM-SM version 2 support
 CONFIG_IP_PIMSM_V2
-  Kernel side support for Sparse Mode PIM version 2. You need
-  experimental routing daemon supporting it (pimd or gated-5).
-  This protocol is not used widely, so that press Y, if you
-  do not want play with it.
+  Kernel side support for Sparse Mode PIM version 2. In order to use
+  this, you need an experimental routing daemon supporting it (pimd or
+  gated-5).  This routing protocol is not used widely, so say N unless
+  you want to play with it.
 
 PC/TCP compatibility mode
 CONFIG_INET_PCTCP
@@ -1435,16 +1718,18 @@
   rarp ("man rarp") on your box. If you actually want to use a
   diskless Sun 3 machine as an Xterminal to Linux, say Y here and
   fetch Linux-Xkernel from
-  ftp://sunsite.unc.edu/pub/Linux/system/Network/boot.net/.  Superior
+  ftp://sunsite.unc.edu/pub/Linux/system/network/boot.net/.  Superior
   solutions to the problem of booting and configuring machines over a
   net connection are given by the protocol BOOTP and its successor
   DHCP. See the DHCP FAQ
-  http://web.syr.edu/~jmwobus/comfaqs/dhcp.faq.html for details.  If
-  you want to compile RARP 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 rarp.o.  If you don't understand a word of the above, say N
-  and rest in peace.
+  http://web.syr.edu/~jmwobus/comfaqs/dhcp.faq.html for details (to
+  browse the WWW, you need to have access to a machine on the Internet
+  that has a program like lynx or netscape).  If you want to compile
+  RARP 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 rarp.o.
+  If you don't understand a word of the above, say N and rest in
+  peace.
 
 Assume subnets are local
 CONFIG_INET_SNARL
@@ -1517,10 +1802,16 @@
 
 Unix domain sockets
 CONFIG_UNIX
-  Y if you want Unix domain sockets.  Unless you are working on an
-  embedded system or somthing, you probably want to say Y.  If you try
-  building this as a module and you are running kerneld, you need to make
-  sure and add 'alias net-pf-1 unix' to your /etc/conf.module file.
+  This includes Unix domain sockets, the standard Unix mechanism for
+  establishing and accessing network connections.  Unless you are
+  working on an embedded system or something, you probably want to say
+  Y.  The socket 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 unix.o. If you want to compile it
+  as a module, say M here and read Documentation/modules.txt. If you
+  try building this as a module and you are running kerneld, be sure
+  to add 'alias net-pf-1 unix' to your /etc/conf.module file.  If
+  unsure, say Y.
 
 The IPv6 protocol
 CONFIG_IPV6
@@ -1528,31 +1819,35 @@
   Protocol IP version 6 (also called IPng "IP next
   generation"). Features of this new protocol include: expanded
   address space, authentication and privacy, and seamless
-  interoperability with the current version of IP. For general
-  information about IPv6, see
-  http://playground.sun.com/pub/ipng/html/ipng-main.html; for specific
-  information about IPv6 under Linux read the HOWTO at
-  http://www.terra.net/ipv6/ and the file net/ipv6/README in the
-  kernel source. If you want to use IPv6, please upgrade to the newest
-  net-tools as given in Documentation/Changes. The IPv6 support is
-  also available as a module ( = code which can be inserted in and
-  removed from the running kernel whenever you want). The module will
-  be called 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.
+  interoperability with the current version of IP (IP version 4). For
+  general information about IPv6, see
+  http://playground.sun.com/pub/ipng/html/ipng-main.html (to browse
+  the WWW, you need to have access to a machine on the Internet that
+  has a program like lynx or netscape); for specific information about
+  IPv6 under Linux read the HOWTO at http://www.terra.net/ipv6/ and
+  the file net/ipv6/README in the kernel source. If you want to use
+  IPv6, please upgrade to the newest net-tools as given in
+  Documentation/Changes. The IPv6 support is also available as a
+  module ( = code which can be inserted in and removed from the
+  running kernel whenever you want). The module will be called
+  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.
 
 IPv6: enable EUI-64 token format
 CONFIG_IPV6_EUI64
-  6bone is moving to new aggregatable address format and new link local
-  address assignment (EUI-64). Say Y, if your site already upgraded, or
-  started upgrade.
+  6bone, the network of computers using the IPv6 protocol, is moving
+  to a new aggregatable address format and a new link local address
+  assignment (EUI-64). Say Y, if your site has upgraded already, or
+  has started to upgrade.
 
 IPv6: disable provider based addresses
 CONFIG_IPV6_NO_PB
-  Linux tries to operate correctly, when site is moved to EUI-64
-  only partially. Unfortunately, these two formats ("provider based"
-  and "aggregatable") are incompatible. Say Y, if your site finished
-  upgrade, and/or you encountered some problems caused by presense of
-  two link-local addresses on an interface.
+  Linux tries to operate correctly when your site is moved to EUI-64
+  only partially. Unfortunately, the two address formats (old:
+  "provider based" and new: "aggregatable") are incompatible. Say Y,
+  if your site finished the upgrade to EUI-64, and/or you encountered
+  some problems caused by the presence of two link-local addresses on
+  an interface.
 
 The IPX protocol
 CONFIG_IPX
@@ -1560,13 +1855,13 @@
   used for local networks of Windows machines. You need it if you want
   to access Novell Netware file or print servers using the Linux
   Novell client ncpfs (available via ftp (user: anonymous) from
-  sunsite.unc.edu:/pub/Linux/system/Filesystems/) or from within the
+  sunsite.unc.edu:/pub/Linux/system/filesystems/) or from within the
   Linux DOS emulator dosemu (read the DOSEMU-HOWTO, available in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO). In order to do the former,
   you'll also have to say Y to "NCP filesystem support", below. To
   turn your Linux box into a fully featured Netware file server and
   IPX router, say Y here and fetch either lwared from
-  sunsite.unc.edu:/pub/Linux/system/Network/daemons/ or mars_nwe from
+  sunsite.unc.edu:/pub/Linux/system/network/daemons/ or mars_nwe from
   ftp.gwdg.de:/pub/linux/misc/ncpfs. For more information, read the
   IPX-HOWTO in sunsite.unc.edu:/pub/Linux/docs/howto. The IPX driver
   would enlarge your kernel by about 5 kB. This driver is also
@@ -1624,39 +1919,49 @@
   machine on the Internet that has a program like lynx or
   netscape). EtherTalk is the name used for appletalk over ethernet
   and the cheaper and slower LocalTalk is appletalk over a proprietary
-  apple network using serial links. Ethertalk and Localtalk is fully 
+  apple network using serial links. Ethertalk and Localtalk are fully 
   supported by Linux. The NET-2-HOWTO, available via ftp (user: anonymous)
   in sunsite.unc.edu:/pub/Linux/docs/HOWTO contains valuable information
   as well. This driver is also available as a module ( = code which
   can be inserted in and removed from the running kernel whenever you
-  want). The module will be called appletalk.o. If you want to compile
+  want). The module is called appletalk.o. If you want to compile
   it as a module, say M here and read Documentation/modules.txt. I
   hear that the GNU boycott of Apple is over, so even politically
   correct people are allowed to say Y here.
 
 Appletalk-IP driver support
 CONFIG_IPDDP
-  Appletalk-IP is a method Macintosh users use IP services. This driver
-  allows you to either send your IP traffic over an Appletalk network to
-  an Appletalk-IP router, or you can have your machine act as an Appletalk-IP
-  router and route Appletalk-IP traffic for your Macintosh users. Both
-  modes require seperate user-space support software, please see each
-  individual options help to get the correct URL of the software.
+  This allows IP networking for users who only have Appletalk
+  networking available.  This feature is experimental. With this
+  driver, you can either encapsulate IP inside Appletalk (e.g. if your
+  Linux box is stuck on an appletalk only network) or decapsulate
+  (e.g. if you want your Linux box to act as a internet gateway for a
+  zoo of appletalk connected Macs). You decide which one of the two
+  you want in the following two questions; you can say Y to only one
+  of them. Please see Documentation/networking/ipddp.txt for more
+  information.  This driver is also available as a module ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want). The module is called ipddp.o. If you want to
+  compile it as a module, say M here and read
+  Documentation/modules.txt.
 
-Appletalk-IP Encapsulation support
+IP to Appletalk-IP Encapsulation support
 CONFIG_IPDDP_ENCAP
-  This allows IP networking for users who only have Appletalk
-  networking available.  This feature is experimental. Please see
-  http://www.maths.unm.edu/~bradford/ltpc.html for support software.
+  If you say Y here, the kernel will be able to encapsulate IP packets
+  inside Appletalk frames; this is useful if your Linux box is stuck
+  on an appletalk network (which hopefully contains a decapsulator
+  somewhere). Please see Documentation/networking/ipddp.txt for more
+  information. If you say Y here, you cannot say Y to "Appletalk-IP to
+  IP Decapsulation support", below.
 
-Appletalk-IP Decapsulation support
+Appletalk-IP to IP Decapsulation support
 CONFIG_IPDDP_DECAP
-  This allows you to provide IP services to your Appletalk users.
-  It does not matter what interface the Macs are comming into your
-  Linux box on, be it Localtalk, Ethertalk, PPPtalk, etc. The only
-  dependent variable is if the Appletalk layer supports the protocol
-  you need. User space software is required to run this driver, you
-  can pick it up at http://spacs1.spacs.k12.wi.us/~jschlst/MacGate.html
+  If you say Y here, the kernel will be able to decapsulate
+  Appletalk-IP frames to IP packets; this is useful if you want your
+  Linux box to act as an Internet gateway for an appletalk
+  network. Please see Documentation/networking/ipddp.txt for more
+  information. If you say Y here, you cannot say Y to "IP to
+  Appletalk-IP Encapsulation support", above.
 
 Apple/Farallon LocalTalk PC card support
 CONFIG_LTPC
@@ -1665,8 +1970,7 @@
   If you are in doubt, this card is the one with the 65C02 chip on it.
   You also need version 1.3.3 or later of the netatalk package.
   This driver is experimental, which means that it may not work.
-  See README.ltpc in the drivers/net directory, and the web site
-  http://www.math.unm.edu/~bradford/ltpc.html
+  See the file Documentation/networking/ltpc.txt.
 
 COPS LocalTalk PC card support
 CONFIG_COPS
@@ -1675,9 +1979,7 @@
   package.  This driver is experimental, which means that it may not
   work.  This driver will only work if you choose "Appletalk DDP"
   networking support, above.
-  Please read the file Documentation/networking/README.cops.  See the
-  web site http://www.math.unm.edu/~bradford/ltpc.html for localtalk
-  IP tools.
+  Please read the file Documentation/networking/cops.txt. 
 
 Dayna firmware support
 CONFIG_COPS_DAYNA
@@ -1690,6 +1992,17 @@
   Support COPS compatible cards with Tangent style firmware (Tangent
   ATB_II, Novell NL-1000, Daystar Digital LT-200.
 
+Amateur Radio support
+CONFIG_HAMRADIO
+  If you want to connect your Linux computer to an amateur radio, say
+  Y here. You want to read 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) and the HAM-HOWTO
+  and the AX25-HOWTO, both available via ftp (user: anonymous) from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer to this
+  question won't directly affect the kernel: saying N will just cause
+  this configure script to skip all the questions about amateur radio.
+
 Amateur Radio AX.25 Level 2
 CONFIG_AX25
   This is the protocol used for computer communication over amateur
@@ -1783,19 +2096,20 @@
 CCITT X.25 Packet Layer
 CONFIG_X25
   X.25 is a set of standardized network protocols, similar in scope to
-  frame relay; the one physical line from your box to the entry point
-  to the X.25 network can carry several logical point-to-point
-  connections (called "virtual circuits") to other computers connected
-  to the X.25 network. Governments, banks, and other organizations
-  tend to use it to connect to each other or to form Wide Area
-  Networks. Many countries have public X.25 networks.  X.25 consists
-  of two protocols: the higher level Packet Layer Protocol (PLP) (say
-  Y here if you want that) and the lower level data link layer
-  protocol LAPB (say Y to "LAPB Data Link Driver" below if you want
-  that). You can read more about X.25 at
-  http://www.sangoma.com/x25.html and
-  http://www.cisco.com/univercd/data/doc/software/11_0/rpcg/cx25.htm.
-  Information about X.25 for Linux is contained in the files
+  frame relay; the one physical line from your box to the X.25 network
+  entry point can carry several logical point-to-point connections
+  (called "virtual circuits") to other computers connected to the X.25
+  network. Governments, banks, and other organizations tend to use it
+  to connect to each other or to form Wide Area Networks (WAN's). Many
+  countries have public X.25 networks.  X.25 consists of two
+  protocols: the higher level Packet Layer Protocol (PLP) (say Y here
+  if you want that) and the lower level data link layer protocol LAPB
+  (say Y to "LAPB Data Link Driver" below if you want that). You can
+  read more about X.25 at http://www.sangoma.com/x25.html and
+  http://www.cisco.com/univercd/data/doc/software/11_0/rpcg/cx25.htm
+  (to browse the WWW, you need to have access to a machine on the
+  Internet that has a program like lynx or netscape).  Information
+  about X.25 for Linux is contained in the files
   Documentation/networking/x25.txt and
   Documentation/networking/x25-iface.txt.  One connects to an X.25
   network either with a dedicated network card using the X.21 protocol
@@ -1831,7 +2145,7 @@
 CONFIG_LLC
   This is a Logical Link Layer protocol used for X.25 connections over
   ethernet, using ordinary ethernet cards. 
-  
+
 Bridging (EXPERIMENTAL)
 CONFIG_BRIDGE
   If you say Y here, then your Linux box will be able to act as an
@@ -1842,14 +2156,25 @@
   algorithm. As this is a standard, Linux bridges will interwork
   properly with other third party bridge products. In order to use
   this, you'll need the bridge configuration tools available via ftp
-  (user: anonymous) from shadow.cabi.net. Note that if your box acts
-  as a bridge, it probably contains several ethernet devices, but the
-  kernel is not able to recognize more than one at boot time without
-  help; for details read the Multiple-Ethernet-mini-HOWTO, available
-  via ftp (user: anonymous) in
+  (user: anonymous) from shadow.cabi.net in /pub/Linux. Note that if
+  your box acts as a bridge, it probably contains several ethernet
+  devices, but the kernel is not able to recognize more than one at
+  boot time without help; for details read the
+  Multiple-Ethernet-mini-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. The Bridging code is
   still in test. If unsure, say N.
 
+Packet socket
+CONFIG_PACKET
+  The Packet protocol is used by applications which communicate
+  directly with network devices without an intermediate network
+  protocol implemented in the kernel, e.g. tcpdump. If you want that
+  they work, choose Y. This driver is also available as a module
+  called af_packet.o ( = code which can be inserted in and removed
+  from the running kernel whenever you want). If you want to compile
+  it as a module, say M here and read Documentation/modules.txt. If
+  unsure, say Y.
+
 Kernel/User network link driver
 CONFIG_NETLINK
   This driver allows for two-way communication between certain parts
@@ -1861,7 +2186,9 @@
   to "Kernel/User network link driver" further down.  You also need to
   say Y here if you want to use arpd, a daemon that helps keep the
   internal ARP cache (a mapping between IP addresses and hardware
-  addresses on the local network) small. If unsure, say N.
+  addresses on the local network) small. The ethertap device, which
+  lets user space programs read and write raw ethernet frames, also
+  needs the network link driver. If unsure, say Y.
 
 Routing messages
 CONFIG_RTNETLINK
@@ -1870,6 +2197,11 @@
   you can read some network related routing information from that
   file. Everything you write to that file will be discarded.
 
+Netlink device emulation
+CONFIG_NETLINK_DEV
+  This is a backward compatibility option, choose Y for now.
+  This option will be removed soon.
+
 SCSI support?
 CONFIG_SCSI
   If you want to use a SCSI harddisk, SCSI tapedrive, SCSI CDROM or
@@ -1902,7 +2234,7 @@
   your root filesystem (the one containing the directory /) is located
   on a SCSI disk. In this case, do not compile the driver for your
   SCSI host adapter (below) as a module either.
-  
+
 SCSI tape support
 CONFIG_CHR_DEV_ST
   If you want to use a SCSI tapedrive under Linux, say Y and read the
@@ -1930,8 +2262,7 @@
   This enables the usage of vendor specific SCSI commands. This is
   required to support multisession CD's on with old NEC/TOSHIBA
   cdrom drives (and HP Writers). If you have such a drive and get
-  the first session only, try to turn this on. Most drives should
-  work fine without this.
+  the first session only, try to say Y here; everybody else says N.
 
 SCSI generic support
 CONFIG_CHR_DEV_SG
@@ -1972,14 +2303,14 @@
   This turns on a logging facility that can be used to debug a number
   of problems.  Normally no logging output will appear, but you can
   enable logging with a shell command like: 
-  'echo "scsi log token [level]" > /proc/scsi/scsi'
-  There are a number of things that can be used for 'token', and this
-  allows you to select the types of information you want, and the level
-  allows you to select the level of verbosity.  If you say 'N' here,
-  it may be harder to track down some types of scsi problems.  If
-  you say 'Y' here your kernel will be somewhat larger, but there
-  should be no noticable performance impact as long as you have logging
-  turned off.
+     echo "scsi log token [level]" > /proc/scsi/scsi
+  There are a number of things that can be used for 'token' (you can
+  find them in the source: drivers/scsi/scsi.c), and this allows you
+  to select the types of information you want, and the level allows
+  you to select the level of verbosity.  If you say 'N' here, it may
+  be harder to track down some types of scsi problems.  If you say 'Y'
+  here your kernel will be somewhat larger, but there should be no
+  noticeable performance impact as long as you have logging turned off.
 
 AdvanSys SCSI support
 CONFIG_SCSI_ADVANSYS
@@ -2015,7 +2346,7 @@
   inserted in and removed from the running kernel whenever you want),
   say M here and read Documentation/modules.txt. The module will be
   called aha1542.o.
- 
+
 Adaptec AHA1740 support
 CONFIG_SCSI_AHA1740
   This is support for a SCSI host adapter. It is explained in section
@@ -2057,21 +2388,22 @@
 
 Maximum number of commands per LUN
 CONFIG_AIC7XXX_CMDS_PER_LUN
-  If tagged queueing is enabled, then you may want to try increasing
-  the number of SCSI commands per LUN to more than 2.  By default, we
-  limit the commands per LUN to 2 with or without tagged queueing
-  enabled.  If tagged queueing is disabled, the sequencer in the host
-  adapter will keep the 2nd command in the input queue until the first
-  one completes - so it is OK to have more than 1 command queued.  If
-  tagged queueing is enabled, then the sequencer will attempt to send
-  the 2nd command block to the device while the first command block is
-  executing and the device is disconnected.  For adapters limited to 4
-  command blocks (SCB's), you may want to actually decrease the
-  commands per LUN to 1, if you often have more than 2 devices active
-  at the same time.  This will ensure that there will always be a free
-  SCB for up to 4 devices active at the same time.  When SCB paging is
-  enabled, set the commands per LUN to 8 or higher (see "SCB paging
-  support" below).  If unsure, go with the default for now.
+  By default, we limit the commands per LUN to 2 with or without
+  tagged queueing enabled.  If tagged queueing is enabled, the
+  sequencer in the host adapter will attempt to send the 2nd command
+  block to the device while the first command block is still executing
+  and the device is disconnected. If the devices don't complain, you
+  can thus try to increase the number of SCSI commands per LUN to more
+  than 2 in this case. If tagged queueing is disabled, the sequencer
+  in the host adapter will keep the 2nd command in its input queue
+  until the first one completes - so it is OK to have more than 1
+  command queued. However, for host adapters limited to 4 command
+  blocks (SCB's), you may want to actually decrease the commands per
+  LUN to 1, if you often have more than 2 devices active at the same
+  time.  This will ensure that there will always be a free SCB for up
+  to 4 devices active at the same time. When SCB paging is enabled,
+  set the commands per LUN to 8 or higher (see "SCB paging support"
+  below).  If unsure, go with the default for now.
 
 Enable SCB paging
 CONFIG_AIC7XXX_PAGE_ENABLE
@@ -2171,7 +2503,7 @@
   want). The module will be called u14-34f.o. If you want to compile
   it as a module, say M here and read Documentation/modules.txt.
 
-enable linked commands
+enable elevator sorting
 CONFIG_SCSI_U14_34F_LINKED_COMMANDS
   This is a feature of SCSI-2 which improves performance: the host
   adapter can send a whole list of commands to a device in one
@@ -2209,7 +2541,7 @@
   kernel whenever you want). The module will be called g_NCR5380.o. If
   you want to compile it as a module, say M here and read
   Documentation/modules.txt.
- 
+
 Enable NCR53c400 extensions
 CONFIG_SCSI_GENERIC_NCR53C400
   This enables certain optimizations for the NCR53c400 scsi cards. You
@@ -2249,7 +2581,7 @@
   This will enable 10MHz FAST-SCSI transfers with your host
   adapter. Some systems have problems with that speed, so it's safest
   to say N here.
- 
+
 allow DISCONNECT
 CONFIG_SCSI_NCR53C7xx_DISCONNECT
   This enables the disconnect/reconnect feature of the NCR SCSI
@@ -2394,6 +2726,12 @@
   say M here and read Documentation/modules.txt. The module will be
   called ibmmca.o.
 
+reset SCSI-devices while booting
+CONFIG_SCSI_IBMMCA_DEV_RESET
+  If you say Y here, each connected SCSI device will get a reset
+  command at boot time. This can be necessary for some special SCSI
+  devices. If unsure, say N.
+
 Always IN2000 SCSI support
 CONFIG_SCSI_IN2000
   This is support for an ISA bus SCSI host adapter. You'll find more
@@ -2416,6 +2754,36 @@
   pas16.o. If you want to compile it as a module, say M here and read
   Documentation/modules.txt.
 
+PCI2000 support
+CONFIG_SCSI_PCI2000
+  This is support for the PCI2000I EIDE interface card which acts as a
+  SCSI host adapter. Please read the SCSI-HOWTO, available via ftp
+  (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. This
+  driver is also available as a module called pci2000.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.
+
+PCI2220i support
+CONFIG_SCSI_PCI2220I
+  This is support for the PCI2220i EIDE interface card which acts as a
+  SCSI host adapter. Please read the SCSI-HOWTO, available via ftp
+  (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. This
+  driver is also available as a module called pci2220i.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.
+
+PSI240i support
+CONFIG_SCSI_PSI240I
+  This is support for the PSI240i EIDE interface card which acts as a
+  SCSI host adapter. Please read the SCSI-HOWTO, available via ftp
+  (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. This
+  driver is also available as a module called psi240i.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.
+
 Qlogic FAS SCSI support
 CONFIG_SCSI_QLOGIC_FAS
   This driver works only with the ISA, VLB, and PCMCIA versions of the
@@ -2484,15 +2852,15 @@
   Documentation/modules.txt. The module will be called ultrastor.o.
   Note that there is also another driver for the same hardware:
   "UltraStor 14F/34F support", above.
- 
+
 7000FASST SCSI support
 CONFIG_SCSI_7000FASST
-  This driver supports the Western Digital 7000 SCSI host adapter.
-  Some information is in the source: drivers/scsi/wd7000.c.  This
-  driver is also available as a module ( = code which can be inserted
-  in and removed from the running kernel whenever you want). The
-  module will be called wd7000.o. If you want to compile it as a
-  module, say M here and read Documentation/modules.txt.
+  This driver supports the Western Digital 7000 SCSI host adapter
+  family.  Some information is in the source: drivers/scsi/wd7000.c.
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you
+  want). The module will be called wd7000.o. If you want to compile it
+  as a module, say M here and read Documentation/modules.txt.
 
 EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support
 CONFIG_SCSI_EATA
@@ -2517,7 +2885,7 @@
   previous commands haven't finished yet. Some SCSI devices don't
   implement this properly, so the save answer is N.
 
-enable linked commands
+enable elevator sorting
 CONFIG_SCSI_EATA_LINKED_COMMANDS
   This is a feature of SCSI-2 which improves performance: the host
   adapter can send a whole list of commands to a device in one
@@ -2542,25 +2910,12 @@
   and read Documentation/modules.txt. The module will be called
   NCR53c406.o.
 
-Tekram DC390W/U/F (T) SCSI support
-CONFIG_SCSI_DC390W
-  This driver supports the Tekram DC390W/U/F (T) PCI SCSI host
-  adapters with the NCR/Symbios 53c825/875 chips. Say Y here if you
-  have one of those. If however you have a DC390 (T) adaptor with the
-  Am53C974A chip, use the DC390(T) driver "Tekram DC390(T) (AMD
-  PCscsi) SCSI support", 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 ???.o.
-  
 Tekram DC390(T) (AMD PCscsi) SCSI support
 CONFIG_SCSI_DC390T
   This driver supports the Tekram DC390(T) PCI SCSI host adapter with
   the Am53C974A chip, and perhaps other cards using the same chip.
-  This driver does _not_ support the DC390W/U/F adaptor with the 
-  NCR/Symbios chips; use "Tekram DC390W/U/F (T) SCSI support" for that
-  one.
+  This driver does _not_ support the DC390W/U/F adaptor with the
+  NCR/Symbios chips; use "NCR53C8XX SCSI support" for that one.
   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
@@ -2622,7 +2977,7 @@
   is that many hard to reproduce problems can be tested in a controlled
   environment where there is reduced risk of losing important data.
   This is primarily of use to people trying to debug the middle and upper
-  layers of the scsi subsystem.
+  layers of the scsi subsystem. If unsure, say N.
 
 Network device support?
 CONFIG_NETDEVICES
@@ -2634,20 +2989,21 @@
   shell account or a BBS, even using term (term is a program which
   gives you almost full Internet connectivity if you have a regular
   dial up shell account on some Internet connected Unix computer. Read
-  http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html).  You'll
-  have to say Y if your computer contains a network card that you want
-  to use under linux (make sure you know its name because you will be
-  asked for it and read the Ethernet-HOWTO; also, if you plan to use
-  more than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini) or if you want to use
-  SLIP (Serial Line Internet Protocol is the protocol used to send
-  Internet traffic over telephone lines or nullmodem cables) or CSLIP
-  (compressed SLIP) or PPP (Point to Point Protocol, a better and
-  newer replacement for SLIP) or PLIP (Parallel Line Internet Protocol
-  is mainly used to create a mini network by connecting the parallel
-  ports of two local machines) or AX.25/KISS (protocol for sending
-  internet traffic over radio links).  Make sure to read the
+  http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html (to browse
+  the WWW, you need to have access to a machine on the Internet that
+  has a program like lynx or netscape)).  You'll have to say Y if your
+  computer contains a network card that you want to use under linux
+  (make sure you know its name because you will be asked for it and
+  read the Ethernet-HOWTO; also, if you plan to use more than one
+  network card under linux, read the Multiple-Ethernet-mini-HOWTO,
+  available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini) or if you
+  want to use SLIP (Serial Line Internet Protocol is the protocol used
+  to send Internet traffic over telephone lines or nullmodem cables)
+  or CSLIP (compressed SLIP) or PPP (Point to Point Protocol, a better
+  and newer replacement for SLIP) or PLIP (Parallel Line Internet
+  Protocol is mainly used to create a mini network by connecting the
+  parallel ports of two local machines) or AX.25/KISS (protocol for
+  sending internet traffic over radio links).  Make sure to read the
   NET-2-HOWTO.  Eventually, you will have to read Olaf Kirch's
   excellent book "Network Administrator's Guide", to be found in
   sunsite.unc.edu:/pub/Linux/docs/LDP.  If unsure, say Y.
@@ -2669,7 +3025,7 @@
   want to use more than one dummy device at a time, you need to
   compile this driver as a module. Instead of 'dummy', the devices
   will then be called 'dummy0', 'dummy1' etc.
-  
+
 SLIP (serial line) support
 CONFIG_SLIP
   Say Y if you intend to use SLIP or CSLIP (compressed SLIP) to
@@ -2681,7 +3037,7 @@
   nullmodems). Normally, your access provider has to support SLIP in
   order for you to be able to use it, but there is now a SLIP emulator
   called SLiRP around (available via ftp (user: anonymous) from
-  sunsite.unc.edu:/pub/Linux/system/Network/serial/) which allows you
+  sunsite.unc.edu:/pub/Linux/system/network/serial/) which allows you
   to use SLIP over a regular dial up shell connection. If you plan to
   use SLiRP, make sure to say Y to CSLIP, below. The NET-2-HOWTO,
   available via ftp (user: anonymous) in
@@ -2690,14 +3046,16 @@
   term (term is a program which gives you almost full Internet
   connectivity if you have a regular dial up shell account on some
   Internet connected Unix computer. Read
-  http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html). SLIP
-  support will enlarge your kernel by about 4kB. If unsure, say N.  If
-  you want to compile this as a module ( = code which can be inserted
-  in and removed from the running kernel whenever you want), say M
-  here and read Documentation/modules.txt as well as
+  http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html (to browse
+  the WWW, you need to have access to a machine on the Internet that
+  has a program like lynx or netscape)). SLIP support will enlarge
+  your kernel by about 4kB. If unsure, say N.  If you want to compile
+  this as a module ( = code which can be inserted in and removed from
+  the running kernel whenever you want), say M here and read
+  Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt. The module will be called
   slip.o.
-  
+
 CSLIP compressed headers
 CONFIG_SLIP_COMPRESSED
   This protocol is faster than SLIP because it uses compression on the
@@ -2705,7 +3063,7 @@
   on both ends. Ask your access provider if you are not sure and say
   Y, just in case. You will still be able to use plain SLIP. If you
   plan to use SLiRP, the SLIP emulator (available via ftp (user:
-  anonymous) from sunsite.unc.edu:/pub/Linux/system/Network/serial/)
+  anonymous) from sunsite.unc.edu:/pub/Linux/system/network/serial/)
   which allows you to use SLIP over a regular dial up shell
   connection, you definitely want to say Y here. The NET-2-HOWTO,
   available via ftp (user: anonymous) in
@@ -2728,27 +3086,15 @@
   end of the link as well. It's good enough, for example, to run IP
   over the async ports of a Camtec JNT Pad. If unsure, say N.
 
-Radio network interfaces
+Wireless LAN (non-hamradio)
 CONFIG_NET_RADIO
-  Radio based interfaces for Linux. This includes amateur radio
-  (AX.25), support for wireless ethernet and other systems. Note that
-  the answer to this question won't directly affect the kernel: saying
-  N will just cause this configure script to skip all the questions
-  about radio interfaces. Some user-level drivers for scarab devices
-  which don't require special kernel support are available via ftp
-  (user: anonymous) from shadow.cabi.net. 
-  If unsure, say N.
-
-AX.25 network interfaces
-CONFIG_NET_HAM
-  Say Y here if you want support for a device that connects your Linux
-  box to your amateur radio (HAM). AX.25 is the protocol used for
-  digital traffic over amateur radio connections. You might want to
-  read the HAM-HOWTO and the AX25-HOWTO, both available via ftp (user:
-  anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the
-  answer to this question won't directly affect the kernel: saying N
-  will just cause this configure script to skip all the questions
-  about amateur radio interfaces.
+  Support for wireless LAN's and everything having to do with radio,
+  but not with amateur radio. Note that the answer to this question
+  won't directly affect the kernel: saying N will just cause this
+  configure script to skip all the questions about radio
+  interfaces. Some user-level drivers for scarab devices which don't
+  require special kernel support are available via ftp (user:
+  anonymous) from shadow.cabi.net in /pub/Linux.
 
 PPP (point-to-point) support
 CONFIG_PPP
@@ -2758,7 +3104,7 @@
   otherwise you can't use it (not quite true any more: the free
   program SLiRP can emulate a PPP line if you just have a regular dial
   up shell account on some UNIX computer; get it via ftp (user:
-  anonymous) from sunsite.unc.edu:/pub/Linux/system/Network/serial/).
+  anonymous) from sunsite.unc.edu:/pub/Linux/system/network/serial/).
   To use PPP, you need an additional program called pppd as described
   in Documentation/networking/ppp.txt and in the PPP-HOWTO, available
   from sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you upgrade from an
@@ -2767,20 +3113,22 @@
   program which gives you almost full Internet connectivity if you
   have a regular dial up shell account on some Internet connected UNIX
   computer. Read
-  http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html). The PPP
-  option enlarges your kernel by about 16kB. This driver is also
-  available as a module ( = code which can be inserted in and removed
-  from the running kernel whenever you want). If you said Y to
-  "Version information on all symbols" above, then you cannot compile
-  the PPP driver into the kernel; you can then only compile it as a
-  module. The module will be called ppp.o. If you want to compile it
-  as a module, say M here and read Documentation/modules.txt as well
-  as Documentation/networking/net-modules.txt. Note that, no matter
-  what you do, the BSD compression code (used to compress the IP
-  packets sent over the serial line; has to be supported at the other
-  end as well) will always be compiled as a module; it is called
-  bsd_comp.o and will show up in the directory modules once you have
-  said "make modules". If unsure, say N.
+  http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html (to browse
+  the WWW, you need to have access to a machine on the Internet that
+  has a program like lynx or netscape)). The PPP option enlarges your
+  kernel by about 16kB. This driver is also available as a module ( =
+  code which can be inserted in and removed from the running kernel
+  whenever you want). If you said Y to "Version information on all
+  symbols" above, then you cannot compile the PPP driver into the
+  kernel; you can then only compile it as a module. The module will be
+  called ppp.o. If you want to compile it as a module, say M here and
+  read Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt. Note that, no matter what
+  you do, the BSD compression code (used to compress the IP packets
+  sent over the serial line; has to be supported at the other end as
+  well) will always be compiled as a module; it is called bsd_comp.o
+  and will show up in the directory modules once you have said "make
+  modules". If unsure, say N.
 
 Shortwave radio modem driver
 CONFIG_HFMODEM
@@ -2806,7 +3154,9 @@
 CONFIG_STRIP
   Say Y if you have a Metricom radio and intend to use Starmode Radio
   IP.  STRIP is a radio protocol developed for the MosquitoNet project
-  (http://mosquitonet.stanford.edu/) to send Internet traffic using
+  (On the WWW at http://mosquitonet.stanford.edu/; to browse the WWW,
+  you need to have access to a machine on the Internet that has a
+  program like lynx or netscape) to send Internet traffic using
   Metricom radios.  Metricom radios are small, battery powered,
   100kbit/sec packet radio transceivers, about the size and weight of
   a cellular telephone.  (You may also have heard them called
@@ -2821,6 +3171,26 @@
   the running kernel whenever you want), say M here and read
   Documentation/modules.txt. The module will be called strip.o.
 
+Radio support
+CONFIG_MISC_RADIO
+  If you have a radio card (which enables your computer to receive
+  regular radio broadcasts), then you will want to say "y" here and
+  make a character device file (usually /dev/radio) with major number
+  10 and minor 152 using mknod ("man mknod").  And then, don't forget
+  to pick up some useful tools to use said device (you _might_ find
+  something at ftp.lmh.ox.ac.uk: /users/weejock/linux/, but I haven't
+  written anything too useful yet...)
+
+AIMSlab RadioTrack card
+CONFIG_RADIO_RTRACK
+  Choose Y here if you have one of these, and then fill in the port
+  address below.
+
+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.
+
 LAPB over Ethernet driver
 CONFIG_LAPBETHER
   This is a driver for a pseudo device (typically called /dev/lapb0)
@@ -2857,36 +3227,52 @@
   running kernel whenever you want), say M here and read
   Documentation/modules.txt. The module will be called scc.o.
 
+additional delay for PA0HZP OptoSCC compatible boards
+CONFIG_SCC_DELAY
+
+support for TRX that feedback the tx signal to rx
+CONFIG_SCC_TRXECHO
+###
+### Don't know what's going on here.
+###
+
 High-speed (DMA) SCC driver for AX.25
 CONFIG_DMASCC
-  This is a driver for high-speed SCC boards, i.e. those supporting
-  DMA on one port. Currently, only Ottawa PI/PI2 boards (see
-  http://hydra.carleton.ca/info/pi2.html) and Gracilis PackeTwin
-  boards (see http://www.paccomm.com/) are supported and detected
-  automatically. Multiple boards are operated simultaneously. If
-  you compile this driver as a module, it will be called dmascc.o.
-  If you don't give any parameter to the driver, all possible I/O
-  addresses are probed. This could irritate other devices that are
-  currently not in use. You may specify the list of addresses to be
-  probed by "dmascc=addr1,addr2,..." (when compiled into the kernel
-  image) or "io=addr1,addr2,..." (when loaded as a module).
-  The network interfaces will be called dmascc0 and dmascc1 for the
-  board detected first, dmascc2 and dmascc3 for the second one, and
-  so on. Before you configure each interface with ifconfig, you MUST
-  set certain parameters, such as channel access timing, clock mode,
-  and DMA channel. This is accomplished with a small utility program
-  called dmascc_cfg, which is part of the ax25-utils package.
-  Alternatively, you may download the utility from
+  This is a driver for high-speed SCC boards (used to connect your
+  computer to your amateur radio and send internet traffic over the
+  radio), i.e. those supporting DMA on one port. Currently, only
+  Ottawa PI/PI2 boards (see http://hydra.carleton.ca/info/pi2.html)
+  and Gracilis PackeTwin boards (see http://www.paccomm.com/; to
+  browse the WWW, you need to have access to a machine on the Internet
+  that has a program like lynx or netscape) are supported and detected
+  automatically. If you have one of these cards, you can say Y here
+  and should read the HAM-HOWTO, available via ftp (user: anonymous)
+  in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
+  This driver operates multiple boards simultaneously. If you compile
+  this driver as a module, it will be called dmascc.o.  If you don't
+  give any parameter to the driver, all possible I/O addresses are
+  probed. This could irritate other devices that are currently not in
+  use. You may specify the list of addresses to be probed by
+  "dmascc=addr1,addr2,..." (when compiled into the kernel image) or
+  "io=addr1,addr2,..." (when loaded as a module).  The network
+  interfaces will be called dmascc0 and dmascc1 for the board detected
+  first, dmascc2 and dmascc3 for the second one, and so on. Before you
+  configure each interface with ifconfig, you MUST set certain
+  parameters, such as channel access timing, clock mode, and DMA
+  channel. This is accomplished with a small utility program called
+  dmascc_cfg, which is part of the ax25-utils package.  Alternatively,
+  you may download the utility from
   http://www.oevsv.at/~oe1kib/Linux.html.
 
 BAYCOM picpar and par96 driver for AX.25
 CONFIG_BAYCOM_PAR
-  This is a driver for Baycom style simple amateur radio
-  modems that connect to a parallel interface. The driver 
-  supports the picpar and par96 designs. To configure the
-  driver, use the sethdlc utility available in the standard
-  ax25 utilities package. For information on the modems, see
-  http://www.baycom.de and Documentation/networking/baycom.txt. If you
+  This is a driver for Baycom style simple amateur radio modems that
+  connect to a parallel interface. The driver supports the picpar and
+  par96 designs. To configure the driver, use the sethdlc utility
+  available in the standard ax25 utilities package. For information on
+  the modems, see http://www.baycom.de (to browse the WWW, you need to
+  have access to a machine on the Internet that has a program like
+  lynx or netscape) and Documentation/networking/baycom.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),
   say M here and read Documentation/modules.txt. This is
@@ -2895,45 +3281,51 @@
 BAYCOM ser12 full duplex driver for AX.25
 CONFIG_BAYCOM_SER_FDX
   This is one of two drivers for Baycom style simple amateur radio
-  modems that connect to a serial interface. The driver supports
-  the ser12 design in full duplex mode. In addition, it allows the
+  modems that connect to a serial interface. The driver supports the
+  ser12 design in full duplex mode. In addition, it allows the
   baudrate to be set between 300 and 4800 baud (however not all modems
-  support all baudrates). This is the preferred driver. baycom_ser_hdx.o
-  is the old driver and still provided in case this driver does not work
-  with your serial interface chip. To configure the driver, use the
-  sethdlc utility available in the standard ax25 utilities package. 
-  For information on the modems, see http://www.baycom.de and
-  Documentation/networking/baycom.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),
-  say M here and read Documentation/modules.txt. This is
-  recommended. The module will be called baycom_ser_fdx.o.
+  support all baudrates). This is the preferred driver. The next
+  driver, "BAYCOM ser12 half duplex driver for AX.25" is the old
+  driver and still provided in case this driver does not work with
+  your serial interface chip. To configure the driver, use the sethdlc
+  utility available in the standard ax25 utilities package.  For
+  information on the modems, see http://www.baycom.de (to browse the
+  WWW, you need to have access to a machine on the Internet that has a
+  program like lynx or netscape) and
+  Documentation/networking/baycom.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), say M here and read
+  Documentation/modules.txt. This is recommended. The module will be
+  called baycom_ser_fdx.o.
 
 BAYCOM ser12 half duplex driver for AX.25
 CONFIG_BAYCOM_SER_HDX
   This is one of two drivers for Baycom style simple amateur radio
-  modems that connect to a serial interface. The driver supports
-  the ser12 design in full duplex mode. This is the old driver. 
-  It is still provided in case your serial interface chip does
-  not work with the full duplex driver. This driver is depreciated.
-  To configure the driver, use the sethdlc utility available
-  in the standard ax25 utilities package. For information
-  on the modems, see http://www.baycom.de and
-  Documentation/networking/baycom.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),
-  say M here and read Documentation/modules.txt. This is
-  recommended. The module will be called baycom_ser_hdx.o.
+  modems that connect to a serial interface. The driver supports the
+  ser12 design in full duplex mode. This is the old driver.  It is
+  still provided in case your serial interface chip does not work with
+  the full duplex driver. This driver is depreciated.  To configure
+  the driver, use the sethdlc utility available in the standard ax25
+  utilities package. For information on the modems, see
+  http://www.baycom.de (to browse the WWW, you need to have access to
+  a machine on the Internet that has a program like lynx or netscape)
+  and Documentation/networking/baycom.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), say M here and read
+  Documentation/modules.txt. This is recommended. The module will be
+  called baycom_ser_hdx.o.
 
 Soundcard modem driver for AX.25
 CONFIG_SOUNDMODEM
   This experimental driver allows a standard SoundBlaster or
   WindowsSoundSystem compatible soundcard to be used as a packet radio
-  modem, to send digital traffic over amateur radio. To configure the
-  driver, use the sethdlc, smdiag and smmixer utilities available in
-  the standard ax25 utilities package. For information on how to key
-  the transmitter, see
-  http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html and
+  modem (NOT as a telephone modem!), to send digital traffic over
+  amateur radio. To configure the driver, use the sethdlc, smdiag and
+  smmixer utilities available in the standard ax25 utilities
+  package. For information on how to key the transmitter, see
+  http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html (to browse
+  the WWW, you need to have access to a machine on the Internet that
+  has a program like lynx or netscape) and
   Documentation/networking/soundmodem.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), say M here and read
@@ -3015,26 +3407,6 @@
   can only use one protocol at a time, depending on what the other end
   can understand).
 
-Shortwave radio modem driver
-CONFIG_HFMODEM
-  This experimental driver is used by a package (to be released)
-  that implements the shortwave radio protocols RTTY, Sitor (Amtor),
-  Pactor 1 and GTOR using a standard PC soundcard. If unsure,
-  say N.
-
-Shortwave radio modem driver support for SoundBlaster and compatible cards
-CONFIG_HFMODEM_SBC
-  This option enables the hfmodem driver to use SoundBlaster and
-  compatible cards. It requires a 16bit capable card, i.e.
-  SB16 or better, or ESS1688 or newer.
-
-Shortwave radio modem driver support for WSS and Crystal cards
-CONFIG_HFMODEM_WSS
-  This option enables the hfmodem driver to use WindowsSoundSystem
-  compatible cards. These cards feature a codec chip from either
-  Analog Devices (such as AD1848, AD1845, AD1812) or Crystal
-  Semiconductors (such as CS4248, CS423x).
-
 Serial port KISS driver for AX.25
 CONFIG_MKISS
   KISS is the protocol used to send IP traffic over AX.25 radio
@@ -3049,28 +3421,31 @@
 
 PLIP (parallel port) support
 CONFIG_PLIP
-  PLIP (Parallel Line Internet Protocol) is used to create a mini
-  network consisting of two (or, rarely, more) local machines. The
-  parallel ports (the connectors at the computers with 25 holes) are
-  connected using "null printer" or "Turbo Laplink" cables which can
-  transmit 4 bits at a time or using special PLIP cables, to be used
-  on bidirectional parallel ports only, which can transmit 8 bits at a
-  time (you can find the wiring of these cables in
-  drivers/net/README?.plip). The cables can be up to 15m long. This
-  works also if one of the machines runs DOS/Windows and has some PLIP
-  software installed, e.g. the Crynwr PLIP packet driver
-  (http://sunsite.cnam.fr/packages/Telnet/PC/msdos/misc/pktdrvr.txt)
-  and winsock or NCSA's telnet.  If you want to use this, say Y and
-  read the PLIP mini-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini as well as the
-  NET-2-HOWTO in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the
-  PLIP protocol was changed and this PLIP driver won't work together
-  with the PLIP support in Linux versions 1.0.x.  This option enlarges
-  your kernel by about 8kB. If you want to compile this as a module (
-  = code which can be inserted in and removed from the running kernel
-  whenever you want), say M here and read Documentation/modules.txt as
-  well as Documentation/networking/net-modules.txt. The module will be
-  called plip.o.
+  PLIP (Parallel Line Internet Protocol) is used to create a
+  reasonably fast mini network consisting of two (or, rarely, more)
+  local machines. The PLIP driver has two modes, mode 0 and mode
+  1. The parallel ports (the connectors at the computers with 25
+  holes) are connected with "null printer" or "Turbo Laplink" cables
+  which can transmit 4 bits at a time (mode 0) or with special PLIP
+  cables, to be used on bidirectional parallel ports only, which can
+  transmit 8 bits at a time (mode 1); you can find the wiring of these
+  cables in Documentation/networking/PLIP.txt. The cables can be up to
+  15m long. Mode 0 works also if one of the machines runs DOS/Windows
+  and has some PLIP software installed, e.g. the Crynwr PLIP packet
+  driver (http://www.kanren.net/pktdrvr-info.html; to browse the WWW,
+  you need to have access to a machine on the Internet that has a
+  program like lynx or netscape) and winsock or NCSA's telnet.  If you
+  want to use PLIP, say Y and read the PLIP mini-HOWTO, available via
+  ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini
+  as well as the NET-2-HOWTO in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the PLIP protocol
+  was changed and this PLIP driver won't work together with the PLIP
+  support in Linux versions 1.0.x.  This option enlarges your kernel
+  by about 8kB. If you want to compile this as a module (= code which
+  can be inserted in and removed from the running kernel whenever you
+  want), say M here and read Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt. The module will be called
+  plip.o. If unsure, say Y or M, in case you buy a laptop later.
 
 EQL (serial line load balancing) support
 CONFIG_EQUALIZER
@@ -3081,11 +3456,32 @@
   like one double speed connection using this driver. Naturally, this
   has to be supported at the other end as well, either with a similar
   EQL Linux driver or with a Livingston Portmaster 2e. Say Y if you
-  want this and read drivers/net/README.eql. This driver is also
-  available as a module ( = code which can be inserted in and removed
-  from the running kernel whenever you want). The module will be
-  called eql.o. If you want to compile it as a module, say M here
-  and read Documentation/modules.txt.
+  want this and read Documentation/networking/eql.txt. This driver is
+  also available as a module ( = code which can be inserted in and
+  removed from the running kernel whenever you want). The module will
+  be called eql.o. If you want to compile it as a module, say M here
+  and read Documentation/modules.txt. If unsure, say N.
+
+Ethertap network tap
+CONFIG_ETHERTAP
+  If you say Y here (and have said Y to "Kernel/User network link
+  driver", above) and create a character special file /dev/tap0 with
+  major number 36 and minor number 16 using mknod ("man mknod"), you
+  will be able to have a user space program read and write raw
+  ethernet frames from/to that special file. tap0 can be configured
+  with ifconfig and route like any other ethernet device but it is not
+  connected to any physical LAN; everything written by the user to
+  /dev/tap0 is treated by the kernel as if it had come in from a LAN
+  to the device tap0; everything the kernel wants to send out over the
+  device tap0 can instead be read by the user from /dev/tap0: the user
+  mode program replaces the LAN that would be attached to an ordinary
+  ethernet device.  Please read the file
+  Documentation/networking/ethertap.txt for more information.  This
+  driver is also available as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want). The
+  module will be called ethertap.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt. If you don't
+  know what to use this for, you don't need it.
 
 Frame Relay (DLCI) support
 CONFIG_DLCI
@@ -3096,16 +3492,15 @@
   network, usually at the phone company) can carry several logical
   point-to-point connections to other computers connected to the frame
   relay network. For a general explanation of the protocol, check out
-  http://frame-relay.indiana.edu/4000/4000index.html on the WWW. (To
-  browse the WWW, you need to have access to a machine on the Internet
-  that has a program like lynx or netscape.) To use frame relay, you
-  need supporting hardware (FRAD) and certain programs from the
-  net-tools package as explained in
-  Documentation/networking/framerelay.txt. This driver is also
+  http://www.frforum.com/ on the WWW. (To browse the WWW, you need to
+  have access to a machine on the Internet that has a program like
+  lynx or netscape.) To use frame relay, you need supporting hardware
+  (FRAD) and certain programs from the net-tools package as explained
+  in Documentation/networking/framerelay.txt. This driver is also
   available as a module ( = code which can be inserted in and removed
   from the running kernel whenever you want). The module will be
-  called dlci.o. If you want to compile it as a module, say M here
-  and read Documentation/modules.txt.
+  called dlci.o. If you want to compile it as a module, say M here and
+  read Documentation/modules.txt.
 
 Max open DLCI
 CONFIG_DLCI_COUNT
@@ -3133,13 +3528,13 @@
 
 WAN Router
 CONFIG_WAN_ROUTER
-      Wide Area Networks (WANs), such as X.25, frame relay and leased
+  Wide Area Networks (WANs), such as X.25, frame relay and leased
   lines, are used to interconnect Local Area Networks (LANs) over vast
   distances with data transfer rates significantly higher than those
   achievable with commonly used asynchronous modem connections.
   Usually, a quite expensive external device called `WAN router' is
   needed to connect to a WAN.
-      As an alternative, WAN routing can be built into the Linux
+  As an alternative, WAN routing can be built into the Linux
   kernel.  With relatively inexpensive WAN interface cards available
   on the market, a perfectly usable router can be built for less than
   half the price of an external router.  If you have one of those
@@ -3148,38 +3543,147 @@
   need a wan-tools package available via FTP (user: anonymous) from
   ftp.sangoma.com.  Read Documentation/networking/wan-router.txt for
   more information.
-      WAN routing is always built as a module ( = code which can be
+  WAN routing is always built as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
   The module is called wanrouter.o.  For general information about 
   modules read Documentation/modules.txt.
 
+CPU is too slow to handle full bandwidth
+CONFIG_CPU_IS_SLOW
+###
+### How to know when the CPU is too slow?
+###
+
+QoS and/or fair queueing
+CONFIG_NET_SCHED
+  When the kernel has several packets to send out over the network
+  devices, it has to make a decision which one to send first. This is
+  especially important if some of the network devices are real time
+  devices that need a certain minimum data flow rate.  There are
+  several different algorithms how to do this "fairly"; they are
+  called packet schedulers. You can attach different schedulers to
+  different network devices. If you want to stick to the default
+  scheduling algorithm, say N here. If you want to experiment with a
+  couple of different algorithms, say Y. The available schedulers are
+  listed in the following questions; you can say Y to as many as you
+  like.  If unsure, say N now.
+
+CBQ packet scheduler
+CONFIG_NET_SCH_CBQ
+  Say Y here if you want to use the Class-Based Queueing (CBQ) packet
+  scheduling algorithm for some of your network devices. This
+  algorithm classifies the waiting packets into a tree-like hierarchy
+  of classes; the leaves of this tree are in turn scheduled by
+  separate algorithms (called "disciplines" in this context) which you
+  can choose below from among the "auxiliary disciplines".  See the top
+  of net/sched/sch_cbq.c for references about the CBQ algorithm.
+  This code is also available as a
+  module called sch_cbq.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.
+
+CSZ packet scheduler
+CONFIG_NET_SCH_CSZ
+  Say Y here if you want to use the Clark-Shenker-Zhang (CSZ) packet
+  scheduling algorithm for some of your network devices. At the
+  moment, this is the only algorithm that can guarantee service for
+  real-time applications (see the top of net/sched/sch_csz.c for
+  details and references about the algorithm). This code is also
+  available as a module called sch_csz.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.
+
+RED queueing discipline
+CONFIG_NET_SCH_RED
+  Say Y here if you want to use the Random Early Detection (RED)
+  packet scheduling algorithm for some of your network devices (see
+  the top of net/sched/sch_red.c for details and references about the
+  algorithm). This code is also available as a module called sch_red.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.
+
+SFQ queueing discipline
+CONFIG_NET_SCH_SFQ
+  Say Y here if you want to use the Stochastic Fairness Queueing (SFQ)
+  packet scheduling algorithm for some of your network devices or as a
+  leaf discipline for the CBQ scheduling algorithm (see the top of
+  net/sched/sch_sfq.c for details and references about the SFQ
+  algorithm). This code is also available as a module called sch_sfq.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.
+
+auxiliary TBF queue
+CONFIG_NET_SCH_TBF
+  Say Y here if you want to use the Simple Token Bucket Filter (TBF)
+  packet scheduling algorithm for some of your network devices or as a
+  leaf discipline for the CBQ scheduling algorithm (see the top of
+  net/sched/sch_tbf.c for a description of the TBF algorithm). This code
+  is also available as a module called sch_tbf.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.
+
+auxiliary FIFO queue
+CONFIG_NET_SCH_PFIFO
+  Say Y here if you want to use a simple FIFO (first in - first out)
+  packet "scheduler" for some of your network devices or as a leaf
+  discipline for the CBQ scheduling algorithm. This code is also
+  available as a module called sch_fifo.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.
+
+auxiliary PRIO queue
+CONFIG_NET_SCH_PRIO
+  Say Y here if you want to use an n-band priority queue packet
+  "scheduler" for some of your network devices or as a leaf discipline
+  for the CBQ scheduling algorithm. This code is also available as a
+  module called sch_prio.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.
+###
+### what user level programs are needed to administrate these packet 
+### schedulers?
+###
+
 WAN Drivers
 CONFIG_WAN_DRIVERS
-      Say 'Y' to this option if you are planning to use your Linux box
-  as a WAN router ( = device used to interconnect local area networks
-  over wide area communication links, such as leased lines and public
-  data networks, e.g. X.25 and frame relay) and you will be offered a
-  list of WAN drivers currently available.  For more information, read
-  Documentation/networking/wan-router.txt.
+  Say Y to this option if you are planning to use your Linux box as a
+  WAN ( = Wide Area Network) router ( = device used to interconnect
+  local area networks over wide area communication links, such as
+  leased lines and public data networks, e.g. X.25 and frame relay)
+  and you will be offered a list of WAN drivers currently available.
+  For more information, read
+  Documentation/networking/wan-router.txt. Note that the answer to
+  this question won't directly affect the kernel: saying N will just
+  cause this configure script to skip all the questions about WAN
+  drivers. If unsure, say N.
 
 Sangoma WANPIPE(tm) multiprotocol cards
 CONFIG_VENDOR_SANGOMA
-      WANPIPE from Sangoma Technologies Inc. (http://www.sangoma.com)
-  is a family of intelligent multiprotocol WAN adapters with data
-  transfer rates up to T1 (1.544 Mbps).  They are also known as
-  Synchronous Data Link Adapters (SDLA) and designated S502E(A), S503
-  or S508. These cards support the X.25, Frame Relay, and PPP
-  protocols. If you have one or more of these cards, say 'Y' to this
-  option.  The next questions will ask you about the protocols you
-  want the driver to support. The driver will be compiled as a module
-  ( = code which can be inserted in and removed from the running
-  kernel whenever you want).  The module will be called wanpipe.o.
-  For general information about modules read
+  WANPIPE from Sangoma Technologies Inc. (http://www.sangoma.com; to
+  browse the WWW, you need to have access to a machine on the Internet
+  that has a program like lynx or netscape) is a family of intelligent
+  multiprotocol WAN adapters with data transfer rates up to T1 (1.544
+  Mbps).  They are also known as Synchronous Data Link Adapters (SDLA)
+  and designated S502E(A), S503 or S508. These cards support the X.25,
+  Frame Relay, and PPP protocols. If you have one or more of these
+  cards, say Y to this option and read
+  Documentation/networking/wanpipe.txt.  The next questions will ask
+  you about the protocols you want the driver to support. The driver
+  will be compiled as a module ( = code which can be inserted in and
+  removed from the running kernel whenever you want).  The module will
+  be called wanpipe.o.  For general information about modules read
   Documentation/modules.txt.
 
 Maximum number of cards
 CONFIG_WANPIPE_CARDS
-      Enter number of WANPIPE adapters installed in your machine.  The
+  Enter number of WANPIPE adapters installed in your machine.  The
   driver can support up to 8 cards.  You may enter more than you
   actually have if you plan to add more cards in the future without
   re-compiling the driver, but remember that in this case you'll waste
@@ -3187,22 +3691,26 @@
 
 WANPIPE X.25 support
 CONFIG_WANPIPE_X25
-      Say 'Y' to this option, if you are planning to connect a WANPIPE
-  card to an X.25 network.  If you say 'N', the X.25 support will not
-  be included in the driver (saves about 16K of kernel memory).
+  Say Y to this option, if you are planning to connect a WANPIPE card
+  to an X.25 network. You should then also have said Y to "CCITT X.25
+  Packet Layer" and "LAPB Data Link Driver", above. If you say N, the
+  X.25 support will not be included in the driver (saves about 16K of
+  kernel memory).
 
 WANPIPE Frame Relay support
 CONFIG_WANPIPE_FR
-      Say 'Y' to this option, if you are planning to connect a WANPIPE
-  card to a frame relay network.  If you say 'N', the frame relay
+  Say Y to this option, if you are planning to connect a WANPIPE card
+  to a frame relay network. You should then also have said Y to "Frame
+  Relay (DLCI) support", above. If you say N, the frame relay
   support will not be included in the driver (saves about 16K of
   kernel memory).
 
 WANPIPE PPP support
 CONFIG_WANPIPE_PPP
-      Say 'Y' to this option, if you are planning to connect a WANPIPE
-  card to a leased line using Point-to-Point protocol (PPP).  If you
-  say 'N', the PPP support will not be included in the driver (saves
+  Say Y to this option, if you are planning to connect a WANPIPE card
+  to a leased line using Point-to-Point protocol (PPP). You should
+  then also have said Y to "PPP (point-to-point) support", above. If
+  you say N, the PPP support will not be included in the driver (saves
   about 16K of kernel memory).
 
 Sun LANCE Ethernet support
@@ -3246,10 +3754,7 @@
   the answer to this question doesn't directly affect the kernel:
   saying N will just cause this configure script to skip all the
   questions about Western Digital cards. If you say Y, you will be
-  asked for your specific card in the following questions. If you plan
-  to use more than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  asked for your specific card in the following questions.
 
 WD80*3 support
 CONFIG_WD80x3
@@ -3287,20 +3792,76 @@
   This is support for the SMC9xxx based Ethernet cards.  Choose this
   option if you have a DELL laptop with the docking station, or
   another SMC9192/9194 based chipset.  Say Y if you want it compiled
-  into the kernel, and read the the file drivers/net/README.smc9 and
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO.  This driver is also
-  available as a module ( = code which can be inserted in and removed
-  from the running kernel whenever you want). The module will be
-  called smc9194.o. If you want to compile it as a module, say M here
-  and read Documentation/modules.txt as well as
-  Documentation/networking/net-modules.txt. If you plan to use more
-  than one network card under linux, read the
+  into the kernel, and read the the file
+  Documentation/networking/smc9.txt and the Ethernet-HOWTO, available
+  via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you
+  want). The module will be called smc9194.o. If you want to compile
+  it as a module, say M here and read Documentation/modules.txt as
+  well as Documentation/networking/net-modules.txt. If you plan to use
+  more than one network card under linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
-AMD LANCE and PCnet (AT1500 and NE2100) support
-CONFIG_LANCE
+Racal-Interlan (Micom) NI cards
+CONFIG_NET_VENDOR_RACAL
+  If you have a network (ethernet) card belonging to this class, such
+  as the NI5010, NI5210 or NI6210, say Y and read the Ethernet-HOWTO,
+  available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than
+  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
+  available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Note that
+  the answer to this question doesn't directly affect the kernel:
+  saying N will just cause this configure script to skip all the
+  questions about NI cards. If you say Y, you will be asked for your
+  specific card in the following questions.
+
+NI5010 support
+CONFIG_NI5010
+  If you have a network (ethernet) card of this type, say Y and read
+  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that this is still
+  experimental code. This driver is also available
+  as a module ( = code which can be inserted in and removed from the
+  running kernel whenever you want). The module will be called
+  ni5010.o. If you want to compile it as a module, say M here and read
+  Documentation/modules.txt as well as
+xIO  Documentation/networking/net-modules.txt. If you plan to use more
+  than one network card under linux, read the
+  Multiple-Ethernet-mini-HOWTO, available from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+
+NI5210 support
+CONFIG_NI52
+  If you have a network (ethernet) card of this type, say Y and read
+  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
+  as a module ( = code which can be inserted in and removed from the
+  running kernel whenever you want). The module will be called
+  ni52.o. If you want to compile it as a module, say M here and read
+  Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt. If you plan to use more
+  than one network card under linux, read the
+  Multiple-Ethernet-mini-HOWTO, available from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+
+NI6510 support
+CONFIG_NI65
+  If you have a network (ethernet) card of this type, say Y and read
+  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
+  as a module ( = code which can be inserted in and removed from the
+  running kernel whenever you want). The module will be called
+  ni65.o. If you want to compile it as a module, say M here and read
+  Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt. If you plan to use more
+  than one network card under linux, read the
+  Multiple-Ethernet-mini-HOWTO, available from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+
+AMD LANCE and PCnet (AT1500 and NE2100) support
+CONFIG_LANCE
   If you have a network (ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. Some LinkSys cards are of
@@ -3444,7 +4005,7 @@
   (arguably) beautiful poetry in Documentation/networking/arcnet.txt.
   You need both this driver, and the driver for the particular ARCnet
   chipset of your card. If you don't know, then it's probably a 
-  COM90xx type card, so say Y (or M) to ARCnet COM90xx chipset support
+  COM90xx type card, so say Y (or M) to "ARCnet COM90xx chipset support"
   below.
   You might also want to have a look at the Ethernet-HOWTO, available
   via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO
@@ -3457,7 +4018,7 @@
   than one network card under linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
-  
+
 Enable arc0e (ARCnet "ether-encap" packet format)
 CONFIG_ARCNET_ETH
   This allows you to use "ethernet encapsulation" with your ARCnet
@@ -3483,30 +4044,50 @@
   documentation in Documentation/networking/arcnet.txt for more
   information about using arc0e and arc0s.
 
-ARCnet COM90xx chipset support
+ARCnet COM90xx (normal) chipset driver
 CONFIG_ARCNET_COM90xx
-  This is the chipset driver for the standard COM90xx cards. If you always
-  used the old arcnet driver without knowing what type of card you had, 
-  this is probably the one for you.
-
-ARCnet COM90xx IO mapped mode chipset support
-CONFIG_ARCNET_COM90xxIO
-  This is the chipset driver for the COM90xx cards, using them in IO-mapped
-  mode instead of memory-mapped mode. This is slower than the normal driver.
-  Only use it if your card doesn't support shared memory.
+  This is the chipset driver for the standard COM90xx cards. If you
+  have always used the old arcnet driver without knowing what type of
+  card you had, this is probably the one for you. This driver is also
+  available as a module ( = code which can be inserted in and removed
+  from the running kernel whenever you want). The module will be
+  called com90xx.o. If you want to compile it as a module, say M here
+  and read Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt.
 
-ARCnet RIM I chipset support
+ARCnet COM90xx (IO mapped) chipset driver
+CONFIG_ARCNET_COM90x
+  This is the chipset driver for the COM90xx cards, using them in
+  IO-mapped mode instead of memory-mapped mode. This is slower than
+  the normal driver.  Only use it if your card doesn't support shared
+  memory. This driver is also available as a module ( = code which can
+  be inserted in and removed from the running kernel whenever you
+  want). The module will be called com90io.o. If you want to compile
+  it as a module, say M here and read Documentation/modules.txt as
+  well as Documentation/networking/net-modules.txt.
+
+ARCnet COM90xx (RIM I) chipset driver
 CONFIG_ARCNET_RIM_I
-  This is yet another chipset driver for the COM90xx cards, but this time
-  only using memory-mapped mode, and no IO ports at all. This driver is
-  completely untested, so if you have one of these cards, please mail 
-  dwmw2@cam.ac.uk, especially if it works!
+  This is yet another chipset driver for the COM90xx cards, but this
+  time only using memory-mapped mode, and no IO ports at all. This
+  driver is completely untested, so if you have one of these cards,
+  please mail dwmw2@cam.ac.uk, especially if it works!
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you
+  want). The module will be called arc-rimi.o. If you want to compile
+  it as a module, say M here and read Documentation/modules.txt as
+  well as Documentation/networking/net-modules.txt.
 
-ARCnet COM20020 chipset support
+ARCnet COM20020 chipset driver
 CONFIG_ARCNET_COM20020
-  This is the driver for the new COM20020 chipset. It supports such things
-  as promiscuous mode, so packet sniffing is possible, and extra diagnostic
-  information.
+  This is the driver for the new COM20020 chipset. It supports such
+  things as promiscuous mode, so packet sniffing is possible, and
+  extra diagnostic information. This driver is also available as a
+  module ( = code which can be inserted in and removed from the
+  running kernel whenever you want). The module will be called
+  com20020.o. If you want to compile it as a module, say M here and
+  read Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt.
 
 Cabletron E21xx support
 CONFIG_E2100
@@ -3553,12 +4134,13 @@
 EtherWorks 3 support
 CONFIG_EWRK3
   This driver supports the DE203, DE204 and DE205 network (ethernet)
-  cards. If this is for you, say Y and read drivers/net/README.ewrk3
-  in the kernel source as well as the Ethernet-HOWTO, available via
-  ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO.
-  If you want to compile this as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want),
-  say M here and read Documentation/modules.txt as well as
+  cards. If this is for you, say Y and read
+  Documentation/networking/ewrk3.txt in the kernel source as well as
+  the Ethernet-HOWTO, available via ftp (user: anonymous) from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to compile this
+  as a module ( = code which can be inserted in and removed from the
+  running kernel whenever you want), say M here and read
+  Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt. The module will be called
   ewrk3.o. If you plan to use more than one network card under linux,
   read the Multiple-Ethernet-mini-HOWTO, available from
@@ -3633,40 +4215,6 @@
   linux, read the Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
-NI5010 support
-CONFIG_NI5010
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than
-  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
-  available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
-  Note that this is still experimental code. If you use this driver,
-  please contact the authors to join the development team.
-
-NI5210 support
-CONFIG_NI52
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
-  as a module ( = code which can be inserted in and removed from the
-  running kernel whenever you want). The module will be called
-  ni52.o. If you want to compile it as a module, say M here and read
-  Documentation/modules.txt as well as
-  Documentation/networking/net-modules.txt. If you plan to use more than
-  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
-  available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
-
-NI6510 support
-CONFIG_NI65
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile it as
-  a module, say M here and read Documentation/modules.txt as well as
-  Documentation/networking/net-modules.txt. The module will be called
-  ni65.o. If you plan to use more than one network card under linux,
-  read the Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
-
 AT&T WaveLAN & DEC RoamAbout DS support
 CONFIG_WAVELAN
   The Lucent Wavelan (formerly NCR and AT&T ; or DEC RoamAbout DS) is
@@ -3678,8 +4226,9 @@
   If you want to use a card of this type under Linux, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. Some more specific
-  information is contained in drivers/net/README.wavelan. You will
-  also need the wireless tools package available from
+  information is contained in
+  Documentation/networking/wavelan.txt. You will also need the
+  wireless tools package available from
   ftp://ftp.inka.de/pub/comp/Linux/networking/NetTools/contrib/.
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you
@@ -3759,18 +4308,18 @@
   This is another class of network cards which attach directly to the
   bus. If you have one of those, say Y and read the Ethernet-HOWTO,
   available via ftp (user: anonymous) from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO; if you are unsure, say
-  Y. Note that the answer to this question doesn't directly affect the
-  kernel: saying N will just cause this configure script to skip all
-  the questions about this class of network cards. If you say Y, you
-  will be asked for your specific card in the following questions.  If
-  you plan to use more than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you plan to use more than
+  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
+  available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you
+  are unsure, say Y. Note that the answer to this question doesn't
+  directly affect the kernel: saying N will just cause this configure
+  script to skip all the questions about this class of network
+  cards. If you say Y, you will be asked for your specific card in the
+  following questions.
 
 AMD PCnet32 (VLB and PCI) support
 CONFIG_PCNET32
-  if you have a PCnet32 or PCnetPCI based network (ethernet) card, say
+  If you have a PCnet32 or PCnetPCI based network (ethernet) card, say
   Y here and read the Ethernet-HOWTO, available via ftp (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you plan to
   use more than one network card under linux, read the
@@ -3825,11 +4374,11 @@
   models. If you have a network card of this type, say Y and read the
   Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  More specific information is
-  contained in drivers/net/README.de4x5. This driver is also available
-  as a module ( = code which can be inserted in and removed from the
-  running kernel whenever you want). The module will be called
-  de4x5.o. If you want to compile it as a module, say M here and read
-  Documentation/modules.txt as well as
+  contained in Documentation/networking/de4x5.txt. This driver is also
+  available as a module ( = code which can be inserted in and removed
+  from the running kernel whenever you want). The module will be
+  called de4x5.o. If you want to compile it as a module, say M here
+  and read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt. If you plan to use more
   than one network card under linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
@@ -3860,11 +4409,11 @@
   models. If you have a network card of this type, say Y and read the
   Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  More specific information is
-  contained in drivers/net/README.dgrs. This driver is also available
-  as a module ( = code which can be inserted in and removed from the
-  running kernel whenever you want). The module will be called
-  dgrs.o. If you want to compile it as a module, say M here and read
-  Documentation/modules.txt as well as
+  contained in Documentation/networking/dgrs.txt. This driver is also
+  available as a module ( = code which can be inserted in and removed
+  from the running kernel whenever you want). The module will be
+  called dgrs.o. If you want to compile it as a module, say M here and
+  read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt. If you plan to use more
   than one network card under linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
@@ -3948,8 +4497,8 @@
 D-Link DE600 pocket adapter support
 CONFIG_DE600
   This is a network (ethernet) device which attaches to your parallel
-  port. Read drivers/net/README.DLINK as well as the Ethernet-HOWTO,
-  available via ftp (user: anonymous) from
+  port. Read Documentation/networking/DLINK.txt as well as the
+  Ethernet-HOWTO, available via ftp (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. It is
   possible to have several devices share a single parallel port and it
   is safe to compile the corresponding drivers into the kernel. If you
@@ -3963,8 +4512,8 @@
 D-Link DE620 pocket adapter support
 CONFIG_DE620
   This is a network (ethernet) device which attaches to your parallel
-  port. Read drivers/net/README.DLINK as well as the Ethernet-HOWTO,
-  available via ftp (user: anonymous) from
+  port. Read Documentation/networking/DLINK.txt as well as the
+  Ethernet-HOWTO, available via ftp (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. It is
   possible to have several devices share a single parallel port and it
   is safe to compile the corresponding drivers into the kernel. If you
@@ -3974,7 +4523,7 @@
   will be called de620.o. If you plan to use more than one network
   card under linux, read the Multiple-Ethernet-mini-HOWTO, available
   from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
- 
+
 Token Ring driver support
 CONFIG_TR
   Token Ring is IBM's way of communication on a local network; the
@@ -4031,9 +4580,9 @@
   For each of these drivers, a file Documentation/cdrom/<driver_name>
   exists. Especially in cases where you do not know exactly which kind
   of drive you have you should read there.
-  Most of these drivers use a file drivers/cdrom/<driver_name>.h where
-  you can define your interface parameters and switch some internal
-  goodies.
+  Most of these drivers use a file drivers/cdrom/<driver_name>.h
+  where you can define your interface parameters and switch some
+  internal goodies.
   All these CDROM drivers are also usable as a module (= code which can
   be inserted in and removed from the running kernel whenever you want).
   If you want to compile them as module, say M instead of Y and read
@@ -4049,7 +4598,7 @@
   will not be auto detected by the kernel at boot time; you have to
   provide the interface address as an option to the kernel at boot
   time as described in Documentation/cdrom/cdu31a or fill in your
-  parameters into linux/drivers/cdrom/cdu31a.c. Try "man bootparam" or
+  parameters into drivers/cdrom/cdu31a.c. Try "man bootparam" or
   see the documentation of your boot loader (lilo or loadlin) about
   how to pass options to the kernel. The lilo procedure is also
   explained in the SCSI-HOWTO. If you say Y here, you should also say
@@ -4072,7 +4621,7 @@
   (PhotoCDs).  There is a new driver (next question) which can do
   this. If you want that one, say N here.
   If the driver doesn't work out of the box, you might want to have a
-  look at linux/drivers/cdrom/mcd.h.  If you say Y here, you should
+  look at drivers/cdrom/mcd.h.  If you say Y here, you should
   also say Y to "ISO9660 cdrom filesystem support" below, because
   that's the filesystem used on CDROMs. Please also read the file
   Documentation/cdrom/mcd. This driver is also available as a module (
@@ -4116,7 +4665,7 @@
   are not sure, but can consume some time during the boot process if
   none of the supported drives gets found.
   Once your drive got found, you should enter the reported parameters 
-  into linux/drivers/cdrom/sbpcd.h and set "DISTRIBUTION 0" there.
+  into drivers/cdrom/sbpcd.h and set "DISTRIBUTION 0" there.
   This driver can support up to four CDROM interface cards, and each
   card can support up to four CDROM drives; if you say Y here, you
   will be asked how many controllers you have. If compiled as a
@@ -4135,7 +4684,7 @@
   Say Y here only if you have two CDROM controller boards of this type
   (usually only if you have more than four drives). You should enter
   the parameters for the second, third and fourth interface card into
-  linux/drivers/cdrom/sbpcd.h before compiling the new kernel. Read
+  linux/include/linux/sbpcd.h before compiling the new kernel. Read
   the file Documentation/cdrom/sbpcd.
 
 Aztech/Orchid/Okano/Wearnes/TXC/CyDROM  CDROM support
@@ -4167,29 +4716,14 @@
 CONFIG_GSCD
   If this is your CDROM drive, say Y here.  As described in
   linux/Documentation/cdrom/gscd, you might have to change a setting
-  in the file linux/drivers/cdrom/gscd.h before compiling the kernel. 
-  Please read the file Documentation/cdrom/gscd. If you say Y here, 
-  you should also say Y to "ISO9660 cdrom filesystem support" below,
-  because that's the filesystem used on CDROMs. This driver is also
-  available as a module ( = code which can be inserted in and removed
-  from the running kernel whenever you want). The module will be
-  called gscd.o. If you want to compile it as a module, say M here and
-  read Documentation/modules.txt.
-
-MicroSolutions backpack CDROM support
-CONFIG_BPCD
-  MicroSolutions backpack CDROM is an external drive that connects to
-  the parallel port.  This driver supports model 164550 (and perhaps
-  other models).  Say Y if you have one of these, and read the file
-  Documentation/cdrom/bpcd. If you say Y here, you should also say Y
-  to "ISO9660 cdrom filesystem support" below, because that's the
-  filesystem used on CDROMs. It is possible for several devices to
-  share a parallel port and it is safe to compile the corresponding
-  drivers all into 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 will be called
-  bpcd.o. If you want to compile it as a module, say M here and read
-  Documentation/modules.txt.
+  in the file linux/drivers/cdrom/gscd.h before compiling the
+  kernel. Please read the file Documentation/cdrom/gscd. If you say Y
+  here, you should also say Y to "ISO9660 cdrom filesystem support"
+  below, because that's the filesystem used on CDROMs. This driver is
+  also available as a module ( = code which can be inserted in and
+  removed from the running kernel whenever you want). The module will
+  be called gscd.o. If you want to compile it as a module, say M here
+  and read Documentation/modules.txt.
 
 Philips/LMS CM206 CDROM support
 CONFIG_CM206
@@ -4263,10 +4797,10 @@
   If you say Y here, you will be able to set per user limits for disk
   usage (also called diskquotas). Currently, it works only for the
   ext2 filesystem. You need additional software in order to use quota
-  support; it is available via ftp (user: anonymous) from
-  ftp.funet.fi/pub/Linux/kernel/src/subsystems/quota/. Probably the
-  quota support is only useful for multi user systems. If unsure, say
-  N.
+  support; for details, read the Quota mini-HOWTO, available via ftp
+  (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Probably the quota
+  support is only useful for multi user systems. If unsure, say N.
 
 Online mirror support
 CONFIG_OMIRR
@@ -4461,6 +4995,33 @@
   compiled as a module, and so this could be dangerous. Most everyone
   wants to say Y here.
 
+ISO9660 cdrom filesystem support
+CONFIG_ISO9660_FS
+  This is the standard filesystem used on CDROMs. It was previously
+  known as "High Sierra Filesystem" and is called "hsfs" on other Unix
+  systems. The so-called Rock-Ridge extensions which allow for long
+  Unix filenames and symbolic links are also supported by this
+  driver. If you have a CDROM drive and want to do more with it than
+  just listen to audio CDs and watch its LEDs, say Y (and read
+  Documentation/filesystems/isofs.txt and the CDROM-HOWTO, available
+  via ftp (user: anonymous) from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO), thereby enlarging your
+  kernel by about 27 kB; otherwise say N.  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 isofs.o.
+
+Microsoft Joliet cdrom extensions
+CONFIG_JOLIET
+  Joliet is a Microsoft extension for the ISO9660 CDROM filesystem
+  which allows for long filenames in unicode format (unicode is the
+  new 16 bit character code, successor to ASCII, which encodes the
+  characters of almost all languages of the world; see
+  http://www.unicode.org for more information; to browse the WWW, you
+  need to have access to a machine on the Internet that has a program
+  like lynx or netscape). Say Y here if you want to be able to read
+  Joliet CDROMs under Linux.
+
 fat fs support
 CONFIG_FAT_FS
   If you want to use one of the FAT-based filesystems (the MS-DOS,
@@ -4485,7 +5046,7 @@
   Linux, you can either use the DOS emulator DOSEMU, described in the
   DOSEMU-HOWTO, available via ftp (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO, or try dmsdosfs in
-  sunsite.unc.edu:/pub/Linux/system/Filesystems/dosfs. If you intend
+  sunsite.unc.edu:/pub/Linux/system/filesystems/dosfs. If you intend
   to use dosemu with a non-compressed MSDOS partition, say Y here) and
   MSDOS floppies. This means that file access becomes transparent,
   i.e. the MSDOS files look and behave just like all other Unix files.
@@ -4541,154 +5102,6 @@
   umsdos.o. Note that the filesystem of your root partition cannot be
   a module, so this could be dangerous. If unsure, say N.
 
-nls: Native language codepages and Unicode support
-CONFIG_NLS
-  This is required by the FAT filesystems and by the ISO9660 filesystem
-  when it is compiled with Joliet support.  Joliet is a Microsoft
-  extension for CDROMs that supports Unicode.  This allows translation
-  between different character sets. When dealing with the FAT based
-  filesystems, there are two character sets that are important. The
-  first is the codepage.  Codepages are character sets that are used by
-  DOS to allow filenames to have native language characters when
-  character sets were limited to 256 characters. The codepage is the
-  character set that is used to store native language characters on
-  disk.  The two most common codepages are 437 in the United States and
-  850 in much of Europe. The second important character set is the
-  input/output character set. This is the character set that is
-  displayed on the screen. In the United States, this will almost always
-  be the ISO 8859-1 character set. This is the default. Linux will only
-  do a translation of the FAT filenames, not the contents of the files.
-
-nls iso8859-1
-CONFIG_NLS_ISO8859_1
-  ISO8859-1 is the Latin 1 character set, and it covers most West
-  European languages such as Albanian, Catalan, Danish, Dutch, English,
-  Faeroese, Finnish, French, German, Galician, Irish, Icelandic, Italian,
-  Norwegian, Portuguese, Spanish, Swedish, and Valencian.
-
-nls iso8859-2
-CONFIG_NLS_ISO8859_2
-  ISO8859-2 is the Latin 2 character set, and it works for most
-  Latin-written Slavic and Central European languages: Czech, German,
-  Hungarian, Polish, Rumanian, Croatian, Slovak, Slovene.
-
-nls iso8859-3
-CONFIG_NLS_ISO8859_3
-  ISO8859-3 is the Latin 3 character set, and it s popular with authors
-  of Esperanto, Galician, Maltese, and Turkish.
-
-nls iso8859-4
-CONFIG_NLS_ISO8859_4
-  ISO8859-4 is the Latin 4 character set, and it introduces letters
-  for Estonian, Latvian, and Lithuanian. It is an incomplete predecessor of
-  Latin 6.
-
-nls iso8859-5
-CONFIG_NLS_ISO8859_5
-  ISO8859-5 is a Cyrillic character set, and you can type Bulgarian,
-  Byelorussian, Macedonian, Russian, Serbian, and Ukrainian.
-  Note that the charset KOI8-R is preferred in Russia.
-
-nls iso8859-6
-CONFIG_NLS_ISO8859_6
-  ISO8859-6 is the Arabic character set.
-
-nls iso8859-7
-CONFIG_NLS_ISO8859_7
-  ISO8859-7 is the Modern Greek character set.
-
-nls iso8859-8
-CONFIG_NLS_ISO8859_8
-  ISO8859-8 is the Hebrew character set.
-
-nls iso8859-9
-CONFIG_NLS_ISO8859_9
-  ISO8859-9 is the Latin 5 character set, and it replaces the rarely
-  needed Icelandic letters in Latin 1 with the Turkish ones. Useful in
-  Turkey.
-
-nls iso8859-10
-CONFIG_NLS_ISO8859_10
-  ISO8859-10 is the Latin 6 character set, and it adds the last Inuit
-  (Greenlandic) and Sami (Lappish) letters that were missing in Latin 4 to
-  cover the entire Nordic area. 
-
-nls koi8-r
-CONFIG_NLS_KOI8_R
-  This is the preferred Russian character set.
-
-nls codepage 437
-CONFIG_NLS_CODEPAGE_437
-  This is the DOS codepage that is used in the United States and parts of
-  Canada.
-
-nls codepage 737
-CONFIG_NLS_CODEPAGE_737
-  This is the codepage used by DOS for Greek.
-
-nls codepage 775
-CONFIG_NLS_CODEPAGE_775
-  This is the codepage used by DOS for the Baltic Rim Languages.
-
-nls codepage 850
-CONFIG_NLS_CODEPAGE_850
-  This is the DOS codepage that is used in 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
-  codepage 437.
-
-nls codepage 852
-CONFIG_NLS_CODEPAGE_852
-  This is the Latin 2 codepage used by DOS for much of Central and
-  Eastern Europe.  It has all the required characters for these languages:
-  Albanian, Croatian, Czech, English, Finnish, Hungarian, Irish, German,
-  Polish, Romanian, Serbian (Latin transcription), Slovak, Slovenian, and
-  Sorbian.
-
-nls codepage 855
-CONFIG_NLS_CODEPAGE_855
-  This is the DOS codepage that is used for Cyrillic.
-
-nls codepage 857
-CONFIG_NLS_CODEPAGE_857
-  This is the DOS codepage that is used for Turkish.
-
-nls codepage 860
-CONFIG_NLS_CODEPAGE_860
-  This is the DOS codepage that is used for Portuguese.
-
-nls codepage 861
-CONFIG_NLS_CODEPAGE_861
-  This is the DOS codepage that is used for Icelandic.
-
-nls codepage 862
-CONFIG_NLS_CODEPAGE_862
-  This is the DOS codepage that is used for Hebrew.
-
-nls codepage 863
-CONFIG_NLS_CODEPAGE_863
-  This is the DOS codepage that is used for Canadian French.
-
-nls codepage 864
-CONFIG_NLS_CODEPAGE_864
-  This is the DOS codepage that is used for Arabic.
-
-nls codepage 865
-CONFIG_NLS_CODEPAGE_865
-  This is the DOS codepage that is used in the Nordic European countries.
-
-nls codepage 866
-CONFIG_NLS_CODEPAGE_866
-  This is the DOS codepage that is used for Cyrillic/Russian.
-
-nls codepage 869
-CONFIG_NLS_CODEPAGE_869
-  This is the DOS codepage that is used for Greek.
-
-nls codepage 874
-CONFIG_NLS_CODEPAGE_874
-  This is the DOS codepage that is used for Thai.
-
 /proc filesystem support
 CONFIG_PROC_FS
   This is a virtual filesystem providing information about the status
@@ -4697,16 +5110,17 @@
   them. Also, you cannot read the files with less: you need to use
   more or cat. The filesystem is explained in the Kernel Hacker's
   Guide at http://www.redhat.com:8080/HyperNews/get/khg.html on the
-  Web, and also on the proc(8) manpage ("man 8 proc").  This option
-  will enlarge your kernel by about 18 kB. It's totally cool; for
-  example, "cat /proc/interrupts" gives information about what the
-  different IRQs are used for at the moment (there is a small number
-  of Interrupt ReQuest lines in your computer that are used by the
-  attached devices to gain the CPU's attention - often a source of
-  trouble if two devices are mistakenly configured to use the same
-  IRQ). Several programs depend on this, so everyone should say Y
-  here.
-  
+  WWW (to browse the WWW, you need to have access to a machine on the
+  Internet that has a program like lynx or netscape), and also on the
+  proc(8) manpage ("man 8 proc").  This option will enlarge your
+  kernel by about 18 kB. It's totally cool; for example, "cat
+  /proc/interrupts" gives information about what the different IRQs
+  are used for at the moment (there is a small number of Interrupt
+  ReQuest lines in your computer that are used by the attached devices
+  to gain the CPU's attention - often a source of trouble if two
+  devices are mistakenly configured to use the same IRQ). Several
+  programs depend on this, so everyone should say Y here.
+
 NFS filesystem support
 CONFIG_NFS_FS
   If you are connected to some other (usually local) Unix computer
@@ -4730,7 +5144,7 @@
   Documentation/modules.txt. If you configure a diskless machine which
   will mount its root filesystem over nfs (in order to do that, check
   out the netboot package, available via ftp (user: anonymous) from
-  sunsite.unc.edu in /pub/Linux/system/Linux-boot/, extract with "tar
+  sunsite.unc.edu in /pub/Linux/system/boot/ethernet/, extract with "tar
   xzvf filename", and say Y to "Root file system on NFS" below), then
   you cannot compile this driver as a module. If you don't know what
   all this is about, say N.
@@ -4759,20 +5173,28 @@
   module, say M here and read Documentation/modules.txt. If unsure,
   say N.
 
-ISO9660 cdrom filesystem support
-CONFIG_ISO9660_FS
-  This is the standard filesystem used on CDROMs. It was previously
-  known as "High Sierra Filesystem" and is called "hsfs" on other Unix
-  systems. The so-called Rock-Ridge extensions which allow for long
-  Unix filenames are also supported by this driver. If you have a
-  CDROM drive and want to do more with it than just listen to audio
-  CDs and watch its LEDs, say Y (and read the CDROM-HOWTO, available
-  via ftp (user: anonymous) from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO), thereby enlarging your
-  kernel by about 27 kB; otherwise say N.  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 isofs.o.
+BOOTP support
+CONFIG_RNFS_BOOTP
+  If you want your Linux box to mount its whole root filesystem from
+  some other computer over the net via NFS and you want the IP address
+  of your computer to be discovered automatically at boot time using
+  the BOOTP protocol (a special protocol designed for doing this job),
+  say Y here. In case the boot ROM of your network card was designed
+  for booting Linux and does BOOTP itself, providing all necessary
+  information on the kernel command line, you can say N here.  If
+  unsure, say Y. Note that in case you want to use BOOTP, a BOOTP
+  server must be operating on your network. Read
+  Documentation/nfsroot.txt for details.
+
+RARP support
+CONFIG_RNFS_RARP
+  If you want your Linux box to mount its whole root filesystem from
+  some other computer over the net via NFS and you want the IP address
+  of your computer to be discovered automatically at boot time using
+  the RARP protocol (an older protocol which is being obsoleted by
+  BOOTP and DHCP), say Y here. Note that in case you want to use RARP,
+  a RARP server must be operating on your network. Read
+  Documentation/nfsroot.txt for details.
 
 OS/2 HPFS filesystem support (read only)
 CONFIG_HPFS_FS
@@ -4793,15 +5215,21 @@
   NTFS is the file system of Microsoft Windows NT. Say Y if you want
   to access partitions using this file system. The Linux NTFS driver
   supports most of the mount options of the VFAT driver, see
-  Documentation/filesystems/ntfs.txt. There is an experimental
-  write support available; use at your own risk.
+  Documentation/filesystems/ntfs.txt. Saying Y here will give you
+  read-only access to NTFS partitions. This code 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 ntfs.o. If you want to compile it as a module, say M here
+  and read Documentation/modules.txt.
 
 NTFS read-write support (experimental)
 CONFIG_NTFS_RW
-  The read-write support in NTFS is far from being complete and well
-  tested. If you enable this, be prepared to recover the NTFS volume
-  from tape.
-  
+  If you say Y here, you will (hopefully) be able to write to NTFS
+  file systems as well as to read from them.  The read-write support
+  in NTFS is far from being complete and is not well tested. If you
+  enable this, be prepared to recover the NTFS volume from tape. If
+  unsure, say N.
+
 System V and Coherent filesystem support
 CONFIG_SYSV_FS
   SCO, Xenix and Coherent are commercial Unix systems for intel
@@ -4819,15 +5247,15 @@
   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").  Note
-  also that this option has nothing whatsoever to do with the option
-  "System V IPC". Read about the System V filesystem in
-  Documentation/filesystems/sysv-fs.txt. This option will enlarge your
-  kernel by about 34 kB. 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 sysv.o. If you
-  haven't heard about all of this before, it's safe to say N.
+  operating systems) is given by the tar program ("man tar" or
+  preferably "info tar").  Note also that this option has nothing
+  whatsoever to do with the option "System V IPC". Read about the
+  System V filesystem in Documentation/filesystems/sysv-fs.txt. This
+  option will enlarge your kernel by about 34 kB. 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 sysv.o. If
+  you haven't heard about all of this before, it's safe to say N.
 
 Kernel automounter support (experimental)
 CONFIG_AUTOFS_FS
@@ -4848,42 +5276,54 @@
 CONFIG_UFS_FS
   BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD
   and NeXTstep) use a filesystem called UFS. Some System V Unixes can
-  create and mount partitions and diskettes using this filesystem as
-  well. Saying Y here allows you to mount these partitions and
-  diskettes read-only. If you only intend to mount files from some
-  other Unix over the network using NFS, you don't need the UFS
-  filesystem support (but you need nfs filesystem support
+  create and mount harddisk partitions and diskettes using this
+  filesystem as well. Saying Y here allows you to mount these
+  partitions and diskettes read-only. If you only intend to mount
+  files from some other Unix over the network using NFS, you don't
+  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"). When accessing NeXTstep files,
-  you may need to convert them from the NeXT character set to the
-  Latin1 character set; use GNU recode for this purpose.  Say Y to
-  build UFS support into your kernel. 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.
+  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
+  for this purpose.  Say Y to build UFS read support into your
+  kernel. 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.
 
 BSD disklabel (FreeBSD partition tables) support
 CONFIG_BSD_DISKLABEL
-  FreeBSD uses its own partition scheme on your PC. It requires only
-  one entry in the primary partition table of your disk and manages it
-  similarly to DOS extended partitions, putting in its first sector a
-  new partition table in disklabel format. Saying Y here allows you to
-  read these disklabels and further mount FreeBSD partitions on your
-  Linux box if you also have configured BSD ufs filesystem support. If
-  you don't know what all this is about, say N.
+  FreeBSD uses its own harddisk partition scheme on your PC. It
+  requires only one entry in the primary partition table of your disk
+  and manages it similarly to DOS extended partitions, putting in its
+  first sector a new partition table in disklabel format. Saying Y
+  here allows you to read these disklabels and further mount FreeBSD
+  partitions read-only from within Linux if you have also said Y to
+  "BSD ufs filesystem support", above. If you don't know what all this
+  is about, say N.
 
 SMD disklabel (Sun partition tables) support
 CONFIG_SMD_DISKLABEL
-  Like most systems, SunOS uses its own partition table format,
-  incompatible with all others. Saying Y here allows you to read these
-  partition tables and further mount SunOS disks on your Linux box if
-  you also have configured BSD ufs filesystem support. This is mainly
-  used to carry data from a Sparc under SunOS to your Linux box via a
-  removable medium like magneto-optical or ZIP drives. If you don't
-  know what all this is about, say N.
+  Like most systems, SunOS uses its own harddisk partition table
+  format, incompatible with all others. Saying Y here allows you to
+  read these partition tables and further mount SunOS disks read-only
+  from within Linux if you have also said Y to "BSD ufs filesystem
+  support", above. This is mainly used to carry data from a Sparc
+  under SunOS to your Linux box via a removable medium like
+  magneto-optical or ZIP drives; note however that 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"). If you don't know what all this is about,
+  say N.
+
+Macintosh partition map support
+CONFIG_MAC_PARTITION
+  Say Y here if you want your Linux system to be able to read
+  the partition tables of Macintosh hard drives, and thus use
+  partitions on those drives.
 
 SMB filesystem support (to mount WfW shares etc..)
 CONFIG_SMB_FS
@@ -4899,7 +5339,7 @@
   available to Windows clients (which need to have a TCP/IP stack),
   you don't need to say Y here; you can use the program samba
   (available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/system/Network/samba) for that. General
+  sunsite.unc.edu:/pub/Linux/system/network/samba) for that. General
   information about how to connect Linux, Windows machines and Macs is
   on the WWW at http://eats.com/linux_mac_win.html (to browse the WWW,
   you need to have access to a machine on the Internet that has a
@@ -4911,15 +5351,22 @@
 
 Coda filesystem support
 CONFIG_CODA_FS
-  CODA is an advanced network filesystem.  It has support for disconnected
-  operation for laptops, read/write server replication, persistent client
-  caches and write back caching. 
-  By choosing this option you are compiling kernel support for Coda clients
-  into the Linux kernel.  You will need user level code as well, both for 
-  the client and server. Server's are currently user level, i.e. need
-  no kernel support. For further information see
-  http://www.coda.cs.cmu.edu or contact Peter Braam <braam@cs.cmu.edu>. 
-
+  CODA is an advanced network filesystem.  It has support for
+  disconnected operation for laptops, read/write server replication,
+  persistent client caches and write back caching.
+  By saying Y here you are compiling kernel support for Coda clients
+  into the Linux kernel.  You will need user level code as well, both
+  for the client and server. Servers are currently user level,
+  i.e. need no kernel support. For technical information, read
+  Documentation/filesystems/coda.txt. 
+  If you want to compile the coda client support as a module ( = code
+  which can be inserted in and removed from the running kernel
+  whenever you want), say M here and read
+  Documentation/modules.txt. The module will be called coda.o. 
+  For further information see http://www.coda.cs.cmu.edu (to browse
+  the WWW, you need to have access to a machine on the Internet that
+  has a program like lynx or netscape) or contact Peter Braam
+  <braam@cs.cmu.edu>.
 
 SMB Win95 bug work-around
 CONFIG_SMB_WIN95
@@ -4946,20 +5393,21 @@
 CONFIG_AFFS_FS
   The Fast File System (FFS) is the common filesystem used on
   harddisks by Amiga(tm) Systems since AmigaOS Version 1.3
-  (34.20). With this driver you can also mount diskfiles used by the
-  Un*X Amiga Emulator by Bernd Schmidt
-  (http://www-users.informatik.rwth-aachen.de/~crux/uae.html).  If you
-  want to do the latter, you will also need to say Y to "Loop device
-  support", above. Say Y if you want to be able to read and write
-  files from and to an Amiga FFS partition on your harddrive. Amiga
-  floppies however cannot be read with this driver due to an
-  incompatibility of the floppy controller used in an Amiga and the
-  standard floppy controller in PCs and workstations. Read
-  Documentation/filesystems/affs.txt and fs/affs/Changes. This
-  filesystem is also available as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want).
-  The module is called affs.o. If you want to compile it as a module,
-  say M here and read Documentation/modules.txt.  If unsure, say N.
+  (34.20). With this driver you can also mount diskfiles used by Bernd
+  Schmidt's Un*X Amiga Emulator (http://www.freiburg.linux.de/~uae/;
+  to browse the WWW, you need to have access to a machine on the
+  Internet that has a program like lynx or netscape).  If you want to
+  do the latter, you will also need to say Y to "Loop device support",
+  above. Say Y if you want to be able to read and write files from and
+  to an Amiga FFS partition on your harddrive. Amiga floppies however
+  cannot be read with this driver due to an incompatibility of the
+  floppy controller used in an Amiga and the standard floppy
+  controller in PCs and workstations. Read
+  Documentation/filesystems/affs.txt and fs/affs/Changes. This filesystem is also
+  available as a module ( = code which can be inserted in and removed
+  from the running kernel whenever you want). The module is called
+  affs.o. If you want to compile it as a module, say M here and read
+  Documentation/modules.txt.  If unsure, say N.
 
 ROM filesystem support
 CONFIG_ROMFS_FS
@@ -4973,23 +5421,323 @@
   and read Documentation/modules.txt.  If you don't know whether you
   need it, then you don't need it: say N.
 
+nls: Native language codepages and Unicode support
+CONFIG_NLS
+  This is required by the FAT and NTFS filesystems and by the ISO9660
+  filesystem when it is compiled with Joliet support.  Joliet is a
+  Microsoft extension for CDROMs that supports Unicode.  This allows
+  translation between different character sets. When dealing with the
+  FAT based filesystems, there are two character sets that are
+  important. The first is the codepage.  Codepages are character sets
+  that are used by DOS to allow filenames to have native language
+  characters when character sets were limited to 256 characters. The
+  codepage is the character set that is used to store native language
+  characters on disk.  The two most common codepages are 437 in the
+  United States and 850 in much of Europe. The second important
+  character set is the input/output character set. This is the
+  character set that is displayed on the screen. In the United States,
+  this will almost always be the ISO 8859-1 character set. This is the
+  default. Linux will only do a translation of the FAT filenames, not
+  the contents of the files.
+
+nls codepage 437
+CONFIG_NLS_CODEPAGE_437
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored
+  in so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage that is used in
+  the United States and parts of Canada. This is recommended.
+
+nls codepage 737
+CONFIG_NLS_CODEPAGE_737
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored
+  in so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage that is used for
+  Greek. If unsure, say N.
+
+nls codepage 775
+CONFIG_NLS_CODEPAGE_775
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored
+  in so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage that is used for
+  for the Baltic Rim Languages. If unsure, say N.
+
+nls codepage 850
+CONFIG_NLS_CODEPAGE_850
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored
+  in so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage that is used for
+  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.
+
+nls codepage 852
+CONFIG_NLS_CODEPAGE_852
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored in
+  so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the Latin 2 codepage used by DOS
+  for much of Central and Eastern Europe.  It has all the required
+  characters for these languages: Albanian, Croatian, Czech, English,
+  Finnish, Hungarian, Irish, German, Polish, Romanian, Serbian (Latin
+  transcription), Slovak, Slovenian, and Sorbian.
+
+nls codepage 855
+CONFIG_NLS_CODEPAGE_855
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored in
+  so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage for Cyrillic.
+
+nls codepage 857
+CONFIG_NLS_CODEPAGE_857
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored in
+  so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage for Turkish.
+
+nls codepage 860
+CONFIG_NLS_CODEPAGE_860
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored in
+  so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage for Portuguese.
+
+nls codepage 861
+CONFIG_NLS_CODEPAGE_861
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored in
+  so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage for Icelandic.
+
+nls codepage 862
+CONFIG_NLS_CODEPAGE_862
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored in
+  so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage for Hebrew.
+
+nls codepage 863
+CONFIG_NLS_CODEPAGE_863
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored in
+  so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage for Canadian
+  French.
+
+nls codepage 864
+CONFIG_NLS_CODEPAGE_864
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored in
+  so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage for Arabic.
+
+nls codepage 865
+CONFIG_NLS_CODEPAGE_865
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored in
+  so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage for the Nordic
+  European countries.
+
+nls codepage 866
+CONFIG_NLS_CODEPAGE_866
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored in
+  so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage for
+  Cyrillic/Russian.
+
+nls codepage 869
+CONFIG_NLS_CODEPAGE_869
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored in
+  so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage for Greek.
+###
+### Why do we have two codepages for Greek and Cyrillic?
+###
+
+nls codepage 874
+CONFIG_NLS_CODEPAGE_874
+  The Microsoft fat filesystem family can deal with filenames in
+  native language character sets. These character sets are stored in
+  so-called DOS codepages. You need to include the appropriate
+  codepage if you want to be able to read/write these filenames on
+  DOS/Windows partitions correctly. This does apply to the filenames
+  only, not to the file contents. You can include several codepages;
+  say Y here if you want to include the DOS codepage for Thai.
+
+nls iso8859-1
+CONFIG_NLS_ISO8859_1
+  If you want to display filenames with native language characters
+  from the Microsoft fat filesystem family or from JOLIET CDROMs
+  correctly on the screen, you need to include the appropriate
+  input/output character sets. Say Y here for the Latin 1 character
+  set, which covers most West European languages such as Albanian,
+  Catalan, Danish, Dutch, English, Faeroese, Finnish, French, German,
+  Galician, Irish, Icelandic, Italian, Norwegian, Portuguese, Spanish,
+  Swedish, and Valencian. It is also the default for the US. If
+  unsure, say Y.
+
+nls iso8859-2
+CONFIG_NLS_ISO8859_2
+  If you want to display filenames with native language characters
+  from the Microsoft fat filesystem family or from JOLIET CDROMs
+  correctly on the screen, you need to include the appropriate
+  input/output character sets. Say Y here for the the Latin 2 character
+  set, which works for most Latin-written Slavic and Central European
+  languages: Czech, German, Hungarian, Polish, Rumanian, Croatian,
+  Slovak, Slovene.
+
+nls iso8859-3
+CONFIG_NLS_ISO8859_3
+  If you want to display filenames with native language characters
+  from the Microsoft fat filesystem family or from JOLIET CDROMs
+  correctly on the screen, you need to include the appropriate
+  input/output character sets. Say Y here for the Latin 3 character
+  set, which is popular with authors of Esperanto, Galician, Maltese,
+  and Turkish.
+
+nls iso8859-4
+CONFIG_NLS_ISO8859_4
+  If you want to display filenames with native language characters
+  from the Microsoft fat filesystem family or from JOLIET CDROMs
+  correctly on the screen, you need to include the appropriate
+  input/output character sets. Say Y here for the Latin 4 character
+  set which introduces letters for Estonian, Latvian, and
+  Lithuanian. It is an incomplete predecessor of Latin 6.
+
+nls iso8859-5
+CONFIG_NLS_ISO8859_5
+  If you want to display filenames with native language characters
+  from the Microsoft fat filesystem family or from JOLIET CDROMs
+  correctly on the screen, you need to include the appropriate
+  input/output character sets. Say Y here for ISO8859-5, a Cyrillic
+  character set with which you can type Bulgarian, Byelorussian,
+  Macedonian, Russian, Serbian, and Ukrainian.  Note that the charset
+  KOI8-R is preferred in Russia.
+
+nls iso8859-6
+CONFIG_NLS_ISO8859_6
+  If you want to display filenames with native language characters
+  from the Microsoft fat filesystem family or from JOLIET CDROMs
+  correctly on the screen, you need to include the appropriate
+  input/output character sets. Say Y here for ISO8859-6, the Arabic
+  character set.
+
+nls iso8859-7
+CONFIG_NLS_ISO8859_7
+  If you want to display filenames with native language characters
+  from the Microsoft fat filesystem family or from JOLIET CDROMs
+  correctly on the screen, you need to include the appropriate
+  input/output character sets. Say Y here for ISO8859-7, the Modern
+  Greek character set.
+
+nls iso8859-8
+CONFIG_NLS_ISO8859_8
+  If you want to display filenames with native language characters
+  from the Microsoft fat filesystem family or from JOLIET CDROMs
+  correctly on the screen, you need to include the appropriate
+  input/output character sets. Say Y here for ISO8859-8, the Hebrew
+  character set.
+
+nls iso8859-9
+CONFIG_NLS_ISO8859_9
+  If you want to display filenames with native language characters
+  from the Microsoft fat filesystem family or from JOLIET CDROMs
+  correctly on the screen, you need to include the appropriate
+  input/output character sets. Say Y here for the Latin 5 character
+  set, and it replaces the rarely needed Icelandic letters in Latin 1
+  with the Turkish ones. Useful in Turkey.
+
+nls iso8859-10
+CONFIG_NLS_ISO8859_10
+  If you want to display filenames with native language characters
+  from the Microsoft fat filesystem family or from JOLIET CDROMs
+  correctly on the screen, you need to include the appropriate
+  input/output character sets. Say Y here for the Latin 6 character
+  set, which adds the last Inuit (Greenlandic) and Sami (Lappish)
+  letters that were missing in Latin 4 to cover the entire Nordic
+  area.
+
+nls koi8-r
+CONFIG_NLS_KOI8_R
+  If you want to display filenames with native language characters
+  from the Microsoft fat filesystem family or from JOLIET CDROMs
+  correctly on the screen, you need to include the appropriate
+  input/output character sets. Say Y here for the preferred Russian
+  character set.
+
 Virtual terminal
 CONFIG_VT
-  This includes support for a terminal device using display and
+  This includes support for a terminal device with display and
   keyboard devices. Only people using embedded systems want to say N
-  here; most everybody says Y.
+  here; most everybody else says Y. If unsure, say Y, or else you
+  won't be able to do much with your new shiny Linux system :-)
 
-Console on virtual terminal
+Support for console on virtual terminal
 CONFIG_VT_CONSOLE
-  If you enable this option, by default all kernel messages will be sent
-  to the device /dev/tty0 which corresponds to the virtual terminal you
+  If you say Y here, by default all kernel messages will be sent to
+  the device /dev/tty0 which corresponds to the virtual terminal you
   have visible on your display. You should say Y here unless you only
-  want to have the kernel messages output on a serial port.
+  want to have the kernel messages output on a serial port (in which
+  case you probably want to say Y to "Console on serial port", below).
 
 Software generated cursor
 CONFIG_SOFTCURSOR
-  If you enable this option, you'll be able to do lots of nice things
-  with your cursor -- for example to turn it into a non-blinking one.
+  If you say Y here, you'll be able to do lots of nice things with the
+  cursors of your virtual consoles -- for example to turn them into
+  non-blinking block cursors which are more visible on laptop screens.
   See Documentation/VGA-softcursor.txt for more information.
 
 Standard/generic serial support
@@ -5011,14 +5759,14 @@
   that they can use serial mice, modems and similar devices connecting
   to the standard serial ports.
 
-Console on serial port
+Support for console on serial port
 CONFIG_SERIAL_CONSOLE
-  If you enable this option, it is possible to use a serial port as the
-  console. By default still the virtual console will be used at the
-  system console but you can alter that using a kernel command line
-  option. If you don't have a VGA card installed the kernel will
-  automatically use /dev/ttyS0 as system console if this option is
-  enabled.
+  If you say Y here, it is possible to use a serial port as the
+  console. By default still the currently visible virtual console will
+  be used as the system console but you can alter that using a kernel
+  command line option. If you don't have a VGA card installed and you
+  say Y here, the kernel will automatically use /dev/ttyS0 as system
+  console.
 
 Comtrol Rocketport support
 CONFIG_ROCKETPORT
@@ -5070,27 +5818,29 @@
   connect more than two modems to your linux box, for instance in
   order to become a BBS. If you have a card like that, say Y here and
   read the file Documentation/specialix.txt. Also it's possible to say
-  M here and compile this driver as kernel loadable module.
+  M here and compile this driver as kernel loadable module which will
+  be called specialix.o.
 
 Specialix DTR/RTS pin is RTS
 CONFIG_SPECIALIX_RTSCTS
-  The Specialix card can only support either RTS or DTR. When this
-  option is off, the driver will use the pin as "DTR" when the tty is
-  in software handshake mode. When this option is on or hardware 
-  handshake is on, it will always be RTS. Read the file 
-  Documentation/specialix.txt for more information. 
+  The Specialix card can only support either RTS or DTR. When you say
+  N here, the driver will use the pin as "DTR" when the tty is in
+  software handshake mode. When you say Y here or hardware handshake
+  is on, it will always be RTS. Read the file
+  Documentation/specialix.txt for more information.
 
 Cyclades async mux support
 CONFIG_CYCLADES
   This is a driver for a card that gives you many serial ports. You
   would need something like this to connect more than two modems to
-  your linux box, for instance in order to become a BBS. 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
-  cyclades.o. If you haven't heard about it, it's safe to say N. (As
-  of 1.3.9x kernels, this driver's minor numbers start at 0 instead of
-  32.)
+  your linux box, for instance in order to become a BBS. For
+  information about the Cyclades-Z card, read
+  drivers/char/README.cycladesZ. 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 cyclades.o. If
+  you haven't heard about it, it's safe to say N. (As of 1.3.9x
+  kernels, this driver's minor numbers start at 0 instead of 32.)
 
 Stallion multiport serial support 
 CONFIG_STALDRV
@@ -5100,7 +5850,7 @@
   asked for your specific card model in the next questions. Make sure
   to read drivers/char/README.stallion in this case. If you have never
   heard about all this, it's safe to say N.
- 
+
 Stallion EasyIO or EC8/32 support 
 CONFIG_STALLION
   If you have an EasyIO or EasyConnection 8/32 multiport Stallion
@@ -5260,12 +6010,13 @@
 PC110 digitizer pad support
 CONFIG_PC110_PAD
   This drives the digitizer pad on the IBM PC110 palmtop (see
-  http://toy.cabi.net). It can turn the digitizer pad into a PS/2
-  mouse emulation with tap gestures or into an absolute pad.  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
-  pc110pad.o.
+  http://toy.cabi.net; to browse the WWW, you need to have access to a
+  machine on the Internet that has a program like lynx or
+  netscape). It can turn the digitizer pad into a PS/2 mouse emulation
+  with tap gestures or into an absolute pad.  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 pc110pad.o.
 
 Microsoft busmouse support
 CONFIG_MS_BUSMOUSE
@@ -5321,97 +6072,90 @@
 
 Floppy tape drive (QIC-80/40/3010/3020/TR-1/TR-2/TR-3) support
 CONFIG_FTAPE
-    If you have a tape drive that is connected to your floppy
-  controller, say `Y' here. Some tape drives (like the Seagate "Tape
+  If you have a tape drive that is connected to your floppy
+  controller, say Y here. Some tape drives (like the Seagate "Tape
   Store 3200" or the Iomega "Ditto 3200" or the Exabyte "Eagle TR-3")
   come with a "high speed" controller of their own. These drives (and
-  their companion controllers) are also supported.
-    If you have a special controller (such as the CMS FC-10, FC-20,
+  their companion controllers) are also supported if you say Y here.
+  If you have a special controller (such as the CMS FC-10, FC-20,
   Mountain Mach-II, or any controller that is based on the Intel 82078
   FDC like the high speed controllers by Seagate and Exabyte and
   Iomega's "Ditto Dash") you must configure it by selecting the
-  appropriate entries from the "Floppy tape controllers" sub-menu and
-  possibly modify the default values for the IRQ and DMA channel and
-  the IO base in ftape's configuration menu. If you want to use your
-  floppy tape drive on a PCI-bus based system, please read the file
-  `./drivers/char/ftape/README.PCI'.
-    The ftape kernel driver is also available as a runtime loadable
+  appropriate entries from the "Floppy tape controllers" sub-menu
+  below and possibly modify the default values for the IRQ and DMA
+  channel and the IO base in ftape's configuration menu. If you want
+  to use your floppy tape drive on a PCI-bus based system, please read
+  the file drivers/char/ftape/README.PCI.
+  The ftape kernel driver is also available as a runtime loadable
   module ( = 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'.
-    Note that the `Ftape-HOWTO' is out of date (sorry), but there is a
-  web page with more recent documentation at
-  `http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/'. This page
+  module, say M here and read Documentation/modules.txt. The module
+  will be called ftape.o.
+  Note that the Ftape-HOWTO is out of date (sorry) and documents the
+  older version 2.08 of this software but still contains useful
+  information. There is a web page with more recent documentation at
+  http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/ . This page
   always contains the latest release of the ftape driver and useful
   information (backup software, ftape related patches and
-  documentation, FAQ). The Ftape-HOWTO still contains useful
-  information, though, but documents the older 2.08 version of ftape,
-  whereas this version of ftape is 3.04.
-    Note that the file system interface has changed quite a lot
-  compared to previous versions of ftape. Please read
-  `./Documentation/ftape.txt'
+  documentation, FAQ). (To browse the WWW, you need to have access to
+  a machine on the Internet that has a program like lynx or netscape.)
+  Note that the file system interface has changed quite a bit compared
+  to previous versions of ftape. Please read Documentation/ftape.txt.
 
 The file system interface for ftape
 CONFIG_ZFTAPE
-    Normally, you want to say `Y' or `M'. DON'T say `N' here or you
+  Normally, you want to say Y or M. DON'T say N here or you
   WON'T BE ABLE TO USE YOUR FLOPPY TAPE DRIVE.
-    The ftape module itself no longer contains the routines necessary
+  The ftape module itself no longer contains the routines necessary
   to interface with the kernel VFS layer (i.e. to actually write data
   to and read data from the tape drive). Instead the file system
   interface (i.e. the hardware independent part of the driver) has
   been moved to a separate module.
-    If you say `M' zftape will be compiled as as a runtime loadable
+  If you say M zftape will be compiled as a runtime loadable
   module ( = code which can be inserted in and removed from the
   running kernel whenever you want). In this case you should read
-  `./Documentation/modules.txt'.
-    There will also be another module called `zft-compressor.o' which
-  contains code to support user transparent on-the-fly compression
-  based on Ross William's lzrw3 algorithm. `zft-compressor.o' will be
-  compiled as a runtime loadable module only. If you have enabled
+  Documentation/modules.txt. The module will be called zftape.o.
+  Regardless of whether you say Y or M here, an additional runtime
+  loadable module called `zft-compressor.o' which contains code to
+  support user transparent on-the-fly compression based on Ross
+  William's lzrw3 algorithm will be produced. If you have enabled
   auto-loading of kernel modules via `kerneld' (i.e. have said `Y' to
   CONFIG_KERNELD) then `zft-compressor.o' will be loaded automatically
   by zftape when needed.
-    Despite of its name zftape does NOT use compression by
-  default. The file `./Documentation/ftape.txt' contains a short
-  description of the most important changes in the file system
-  interface compared to previous versions of ftape. The ftape home
-  page `http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/'
-  contains further information. IMPORTANT NOTE: zftape can read
-  archives created by previous versions of ftape and provide file mark
-  support (i.e. fast skipping between tape archives) but previous
-  version of ftape will lack file mark support when reading archives
-  produced by zftape.
-
-Builtin on-the-fly compression for zftape, based on lzrw3
-CONFIG_ZFT_COMPRESSOR
-    This module implements builtin on-the-fly compression for ftape's
-  file system interface zftape. `zft-compressor.o' is compiled as a
-  runtime loadable module only and will be loaded by zftape on demand
-  if support for auto-loading of modules via `kerneld' has been
-  enabled (CONFIG_KERNELD).
+  Despite of its name zftape does NOT use compression by default. The
+  file Documentation/ftape.txt contains a short description of the
+  most important changes in the file system interface compared to
+  previous versions of ftape. The ftape home page
+  http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/ contains
+  further information (to browse the WWW, you need to have access to a
+  machine on the Internet that has a program like lynx or netscape).
+  IMPORTANT NOTE: zftape can read archives created by previous
+  versions of ftape and provide file mark support (i.e. fast skipping
+  between tape archives) but previous version of ftape will lack file
+  mark support when reading archives produced by zftape.
 
 Default block size for zftape
 CONFIG_ZFT_DFLT_BLK_SZ
-    If unsure leave this at its default value, i.e. 10240.  Note that
+  If unsure leave this at its default value, i.e. 10240.  Note that
   you specify only the default block size here. The block size can be
   changed at run time using the MTSETBLK tape operation with the
   MTIOCTOP ioctl (i.e. with "mt -f /dev/qft0 setblk #BLKSZ" from the
   shell command line).
-    The probably most striking difference between zftape and previous
+  The probably most striking difference between zftape and previous
   versions of ftape is the fact that all data must be written or read
   in multiples of a fixed block size. The block size defaults to
   10240 which is what GNU tar uses. The values for the block size
   should be either 1 or multiples of 1024 up to a maximum value of
   63488 (i.e. 62k). If you specify `1' then zftape's builtin
   compression will be disabled.
-    Reasonable values are `10240' (GNU tar's default block size),
+  Reasonable values are `10240' (GNU tar's default block size),
   `5120' (afio's default block size), `32768' (default block size some
   backup programs assume for SCSI tape drives) or `1' (no restriction
   on block size, but disables builtin compression).
 
 Number of DMA buffers
 CONFIG_FT_NR_BUFFERS
-    Please leave this at `3"' unless you REALLY know what you are
+  Please leave this at `3' unless you REALLY know what you are
   doing. It is not necessary to change this value. Values below 3 make
   the proper use of ftape impossible, values greater than 3 are waste
   of memory. You can change the amount of DMA memory used by ftape at
@@ -5421,7 +6165,7 @@
 
 Procfs entry for ftape
 CONFIG_FT_PROC_FS
-    Optional. Saying `Y' will result in creation of a file
+  Optional. Saying `Y' will result in creation of a file
   `/proc/ftape' under the proc file system. This files can be viewed
   with your favorite pager (i.e. use "more /proc/ftape/history" or
   "less /proc/ftape/history" or simply "cat /proc/ftape/history"). The
@@ -5431,21 +6175,21 @@
   kernel driver. Saying `Y' will enlarge the size of the ftape driver
   by approximately 2k.
   WARNING: When compiling ftape as a module (i.e. saying `M' to
-  `CONFIG_FTAPE') it is dangerous to use ftape's proc file system
+  "Floppy tape drive") it is dangerous to use ftape's proc file system
   interface. Accessing `/proc/ftape' while the module is unloaded will
   result in a kernel Oops. This cannot be fixed from inside ftape.
 
 Controlling the amount of debugging output of ftape
 CONFIG_FT_NORMAL_DEBUG
-    This option controls the amount of debugging output the ftape driver
+  This option controls the amount of debugging output the ftape driver
   is ABLE to produce; it does not increase or diminish the debugging
   level itself. If unsure, leave this at its default setting,
   i.e. choose "Normal".
-    Ftape can print lots of debugging messages to the system console
+  Ftape can print lots of debugging messages to the system console
   resp. kernel log files. Reducing the amount of possible debugging
   output reduces the size of the kernel module by some kb, so it might
   be a good idea to use "None" for emergency boot floppies.
-    If you want to save memory then the following strategy is
+  If you want to save memory then the following strategy is
   recommended: leave this option at its default setting "Normal" until
   you know that the driver works as expected, afterwards reconfigure
   the kernel, this time specifying "Reduced" or "None" and recompile
@@ -5453,12 +6197,12 @@
   debugging output does not increase the amount of debugging output
   printed to the console but only makes it possible to produce
   "Excessive" debugging output.
-    Please read `./Documentation/ftape.txt' for a short description
+  Please read Documentation/ftape.txt for a short description
   how to control the amount of debugging output.
 
 The floppy drive controller for ftape
 CONFIG_FT_STD_FDC
-    Only change this setting if you have a special controller. If you
+  Only change this setting if you have a special controller. If you
   didn't plug any add-on card into your computer system but just
   plugged the floppy tape cable into the already existing floppy drive
   controller then you don't want to change the default setting,
@@ -5473,115 +6217,115 @@
   `2'. This is necessary for any controller card that is based on
   Intel's 82078 FDC such as Seagate's, Exabyte's and Iomega's "high
   speed" controllers.
-    If you choose something other than "Standard" then please make
+  If you choose something other than "Standard" then please make
   sure that the settings for the IO base address and the IRQ and DMA
   channel in the configuration menus below are correct. Use the manual
   of your tape drive to determine the correct settings!
-    If you are already successfully using your tape drive with another
+  If you are already successfully using your tape drive with another
   operating system then you definitely should use the same settings
   for the IO base, the IRQ and DMA channel that have proven to work
   with that other OS.
-    Note that this menu lets you specify only the default setting for
+  Note that this menu lets you specify only the default setting for
   the hardware setup. The hardware configuration can be changed at
   boot time (when ftape is compiled into the kernel, i.e. if you
-  have said `Y' to `CONFIG_FTAPE') or module load time (i.e. if you
-  have said `M' to `CONFIG_FTAPE').
-    Please read also the file `./Documentation/ftape.txt' which
+  have said Y to "Floppy tape drive") or module load time (i.e. if you
+  have said M to "Floppy tape drive").
+  Please read also the file Documentation/ftape.txt which
   contains a short description of the parameters that can be set at
   boot or load time. If you want to use your floppy tape drive on a
   PCI-bus based system, please read the file
-  `./drivers/char/ftape/README.PCI'.
+  drivers/char/ftape/README.PCI.
 
 IO base of the floppy disk controller used with Ftape
 CONFIG_FT_FDC_BASE
-    You don't need to specify a value if the following default
+  You don't need to specify a value if the following default
   settings for the base IO address are correct:
   <<< MACH-2     : 0x1E0 >>>
   <<< FC-10/FC-20: 0x180 >>>
   <<< Secondary  : 0x370 >>>
   Secondary refers to a secondary FDC controller like the "high speed"
   controllers delivered by Seagate or Exabyte or Iomega's Ditto Dash.
-    Please make sure that the setting for the IO base address
+  Please make sure that the setting for the IO base address
   specified here is correct. USE THE MANUAL OF YOUR TAPE DRIVE OR
   CONTROLLER CARD TO DETERMINE THE CORRECT SETTING. If you are already
   successfully using the tape drive with another operating system then
   you definitely should use the same settings for the IO base that has
   proven to work with that other OS.
-    Note that this menu lets you specify only the default setting for
+  Note that this menu lets you specify only the default setting for
   the IO base. The hardware configuration can be changed at boot time
-  (when ftape is compiled into the kernel, i.e. if you specify `Y' to
-  `CONFIG_FTAPE') or module load time (i.e. if you have say `M' to
-  `CONFIG_FTAPE').
-    Please read also the file `./Documentation/ftape.txt' which
-  contains a short description of the parameters that can be set at
-  boot or load time.
-  
+  (when ftape is compiled into the kernel, i.e. if you specified Y to
+  "Floppy tape drive") or module load time (i.e. if you have said M to
+  "Floppy tape drive").
+  Please read also the file Documentation/ftape.txt which contains a
+  short description of the parameters that can be set at boot or load
+  time.
+
 IRQ channel for the floppy disk controller used with Ftape
 CONFIG_FT_FDC_IRQ
-    You don't need to specify a value if the following default
+  You don't need to specify a value if the following default
   settings for the interrupt channel are correct:
   <<< MACH-2     : 6 >>>
   <<< FC-10/FC-20: 9 >>>
   <<< Secondary  : 6 >>>
   Secondary refers to secondary a FDC controller like the "high speed"
   controllers delivered by Seagate or Exabyte or Iomega's Ditto Dash.
-    Please make sure that the setting for the IO base address
+  Please make sure that the setting for the IO base address
   specified here is correct. USE THE MANUAL OF YOUR TAPE DRIVE OR
   CONTROLLER CARD TO DETERMINE THE CORRECT SETTING. If you are already
   successfully using the tape drive with another operating system then
   you definitely should use the same settings for the IO base that has
   proven to work with that other OS.
-    Note that this menu lets you specify only the default setting for
+  Note that this menu lets you specify only the default setting for
   the IRQ channel. The hardware configuration can be changed at boot
-  time (when ftape is compiled into the kernel, i.e. if you specify
-  `Y' to `CONFIG_FTAPE') or module load time (i.e. if you have say `M'
-  to `CONFIG_FTAPE').
-    Please read also the file `./Documentation/ftape.txt' which
-  contains a short description of the parameters that can be set at
-  boot or load time.
-  
+  time (when ftape is compiled into the kernel, i.e. if you specified
+  Y to "Floppy tape drive") or module load time (i.e. if you have said M
+  to "Floppy tape drive").
+  Please read also the file Documentation/ftape.txt which contains a
+  short description of the parameters that can be set at boot or load
+  time.
+
 DMA channel for the floppy disk controller used with Ftape
 CONFIG_FT_FDC_DMA
-    You don't need to specify a value if the following default
+  You don't need to specify a value if the following default
   settings for the DMA channel are correct:
   <<< MACH-2     : 2 >>>
   <<< FC-10/FC-20: 3 >>>
   <<< Secondary  : 2 >>>
   Secondary refers to a secondary FDC controller like the "high speed"
   controllers delivered by Seagate or Exabyte or Iomega's Ditto Dash.
-    Please make sure that the setting for the IO base address
+  Please make sure that the setting for the IO base address
   specified here is correct. USE THE MANUAL OF YOUR TAPE DRIVE OR
   CONTROLLER CARD TO DETERMINE THE CORRECT SETTING. If you are already
   successfully using the tape drive with another operating system then
   you definitely should use the same settings for the IO base that has
   proven to work with that other OS.
-    Note that this menu lets you specify only the default setting for
+  Note that this menu lets you specify only the default setting for
   the DMA channel. The hardware configuration can be changed at boot
-  time (when ftape is compiled into the kernel, i.e. if you specify
-  `Y' to `CONFIG_FTAPE') or module load time (i.e. if you have say `M'
-  to `CONFIG_FTAPE').
-    Please read also the file `./Documentation/ftape.txt' which
-  contains a short description of the parameters that can be set at
-  boot or load time.
+  time (when ftape is compiled into the kernel, i.e. if you specified
+  Y to "Floppy tape drive") or module load time (i.e. if you have said M
+  to "Floppy tape drive").
+  Please read also the file Documentation/ftape.txt which contains a
+  short description of the parameters that can be set at boot or load
+  time.
 
 FDC FIFO Threshold before requesting DMA service
 CONFIG_FT_FDC_THR
-    Set the FIFO threshold of the FDC. If this is higher the DMA
+  Set the FIFO threshold of the FDC. If this is higher the DMA
   controller may serve the FCD after a higher latency time. If this is
   lower, less DMA transfers occur leading to less bus contention.
-    You may try to tune this if ftape annoys you with "reduced data
+  You may try to tune this if ftape annoys you with "reduced data
   rate because of excessive overrun errors" messages. However, this
   doesn't seem to have too much an effect.
   If unsure, don't touch the initial value, i.e. leave it at "8".
 
 FDC maximum data rate
 CONFIG_FT_FDC_MAX_RATE
-    With some mother board/FDC combinations ftape will not be able to
+  With some mother board/FDC combinations ftape will not be able to
   run your FDC/tape drive combination at the highest available
   speed. If this is the case you'll encounter "reduced data rate
   because of excessive overrun errors" messages and lots of retries
   before ftape finally decides to reduce the data rate.
-    In this case it might be desirable to tell ftape beforehand that
+  In this case it might be desirable to tell ftape beforehand that
   it need not try to run the tape drive at the highest available
   speed. If unsure, leave this disabled, i.e. leave it at 2000
   bits/sec.
@@ -5605,20 +6349,22 @@
   USER RESUME operation, the /proc/apm device will provide battery
   status information, and user-space programs will receive
   notification of APM "events" (e.g., battery status
-  change). Supporting software can be gotten via ftp (user: anonymous)
-  from tsx-11.mit.edu/pub/linux/packages/laptops/apm/. This driver
-  does not spin down disk drives (see hdparm(8) for that); and it
-  doesn't turn off VESA-compliant "green" monitors.  This driver does
-  not support the TI 4000M TravelMate and the ACER 486/DX4/75 because
-  they don't have compliant BIOSes.  Many "green" desktop machines
-  also don't have compliant BIOSes, and this driver will cause those
-  machines to panic during the boot phase (typically, these machines
-  are using a data segment of 0040, which is reserved for the Linux
-  kernel). Generally, if you don't have a battery in your machine,
-  there isn't much point in using this driver and you should say N.
-  If you get random kernel OOPSes or reboots that don't seem to be
-  related to anything, try disabling/enabling this option. Some other
-  things to try when experiencing seemingly random, "weird" problems:
+  change). Supporting software is available; for more information,
+  read the Battery Powered Linux mini-HOWTO available via ftp (user:
+  anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  This driver does not spin down disk drives (see hdparm(8) for that);
+  and it doesn't turn off VESA-compliant "green" monitors.  This
+  driver does not support the TI 4000M TravelMate and the ACER
+  486/DX4/75 because they don't have compliant BIOSes.  Many "green"
+  desktop machines also don't have compliant BIOSes, and this driver
+  will cause those machines to panic during the boot phase (typically,
+  these machines are using a data segment of 0040, which is reserved
+  for the Linux kernel). Generally, if you don't have a battery in
+  your machine, there isn't much point in using this driver and you
+  should say N.  If you get random kernel OOPSes or reboots that don't
+  seem to be related to anything, try disabling/enabling this
+  option. Some other things to try when experiencing seemingly random,
+  "weird" problems:
    1) passing the "no-hlt" option to the kernel 
    2) passing the "no-387" option to the kernel 
    3) passing the "floppy=nodma" option to the kernel
@@ -5674,8 +6420,13 @@
 
 Power off on shutdown 
 CONFIG_APM_POWER_OFF
-  This option will power off the computer after the Linux kernel is halted
-  (e.g., with the halt(8) command).  As with the other APM options, this
+  Enable the ability to power off the computer after the Linux kernel
+  is halted.  You will need software (e.g., a suitable version of the
+  halt(8) command) to cause the computer to power down.  Recent
+  versions of the sysvinit package available from
+  ftp://sunsite.unc.edu/pub/Linux/system/daemons/init/ (user:
+  anonymous) contain support for this ("halt -p" shuts down Linux and
+  powers off the computer).  As with the other APM options, this
   option may not work reliably with some APM BIOS implementations.
 
 Watchdog Timer Support 
@@ -5766,7 +6517,7 @@
   inserted in and removed from the running kernel whenever you want).
   The module is called pscwdt.o. If you want to compile it as a module,
   say M here and read Documentation/modules.txt.  Most people will say N.
-  
+
 Enhanced Real Time Clock Support
 CONFIG_RTC
   If you say Y here and create a character special file /dev/rtc with
@@ -5782,6 +6533,13 @@
   have a use for such a device (such as periodic data sampling), then
   say Y here, and go read the file Documentation/rtc.txt for details.
 
+Tadpole ANA H8 Support
+CONFIG_H8
+  The Hitachi H8/337 is a microcontroller used to deal with the power
+  and thermal environment. If you say Y here, you will be able to
+  communicate with it via via a character special device. If unsure,
+  say N.
+
 /dev/nvram support
 CONFIG_NVRAM
   If you say Y here and create a character special file /dev/nvram
@@ -5802,18 +6560,17 @@
 
 PC joystick support
 CONFIG_JOYSTICK
-  If you have a PC compatible analog or digital joystick, you can
-  say Y here. If you then create a character special file under /dev
-  with major number 15 and minor number 0 or 1 (for the two joystick
-  ports) using mknod ("man mknod"), you can read the status of the
-  buttons and the x and y coordinates from that file. More
-  information, an example program and a calibration program are
-  contained in the joystick package which is available at:
-  ftp://sunsite.unc.edu/pub/Linux/kernel/patches/console
-  This driver is also available as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want).
-  The module will be called joystick.o. If you want to compile it as a
-  module, say M here and read Documentation/modules.txt.
+  If you have a PC compatible analog or digital joystick, you can say
+  Y here. If you then create a character special file under /dev with
+  major number 15 and minor number 0 or 1 (for the two joystick ports)
+  using mknod ("man mknod"), you can read the status of the buttons
+  and the x and y coordinates from that file. Please read the file
+  Documentation/joystick.txt which contains more information and the
+  location of the joystick package that you'll need.  This driver is
+  also available as a module ( = code which can be inserted in and
+  removed from the running kernel whenever you want).  The module will
+  be called joystick.o. If you want to compile it as a module, say M
+  here and read Documentation/modules.txt.
 
 ARC console time
 CONFIG_RTC_ARC
@@ -5864,6 +6621,9 @@
   these cards may cause trouble (I don't currently know of any such
   cards, however).
 
+Loopback MIDI device support
+CONFIG_VMIDI
+
 Gravis Ultrasound support
 CONFIG_GUS
   Say Y here for any type of Gravis Ultrasound card, including
@@ -5937,14 +6697,15 @@
   Answer Y if you have the AudioTriX Pro sound card manufactured
   by MediaTrix.
 
-Support for MAD16 and/or Mozart based cards
+Support for OPTi MAD16 and/or Mozart based cards
 CONFIG_MAD16
-  Answer Y if your card has a Mozart (OAK OTI-601) or MAD16
-  (OPTi 82C928 or 82C929) audio interface chip. These chips are
-  currently quite common so it's possible that many no-name cards
-  have one of them. In addition the MAD16 chip is used in some
-  cards made by known manufacturers such as Turtle Beach (Tropez),
-  Reveal (some models) and Diamond (latest ones).
+  Answer Y if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi
+  82C928 or 82C929 or 82C931) audio interface chip. For the 82C931,
+  please read drivers/sound/README.C931. These chips are currently
+  quite common so it's possible that many no-name cards have one of
+  them. In addition the MAD16 chip is used in some cards made by known
+  manufacturers such as Turtle Beach (Tropez), Reveal (some models)
+  and Diamond (latest ones).
 
 Support for Crystal CS4232 based (PnP) cards
 CONFIG_CS4232
@@ -5980,7 +6741,7 @@
 SB32/AWE support
 CONFIG_AWE32_SYNTH
   Say Y here if you have a SB32 or SB AWE soundcard. See
-  linux/drivers/sound/lowlevel/README.awe for more info.
+  drivers/sound/lowlevel/README.awe for more info.
 
 Additional low level drivers
 CONFIG_LOWLEVEL_SOUND
@@ -6002,23 +6763,23 @@
 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 an SBPro or a Microsoft Sound System card, so you should
-  have said Y to either "SoundBlaster (SB, SBPro, SB16, clones)
+  can emulate either an SBPro or a Microsoft Sound System card, so you
+  should have said Y to either "SoundBlaster (SB, SBPro, SB16, clones)
   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
+  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.
+  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.
 
 SC-6600 based audio cards (new Audio Excel DSP 16)
 CONFIG_SC6600
-  The SC6600 is the new version of DSP mounted on the Audio Excel DSP 16
-  cards. Find in the manual the FCC ID of your audio card and answer Y if
-  you have an SC6600 DSP.
+  The SC6600 is the new version of DSP mounted on the Audio Excel DSP
+  16 cards. Find in the manual the FCC ID of your audio card and
+  answer Y if you have an SC6600 DSP.
 
 Audio Excel DSP 16 (MSS emulation)
 CONFIG_AEDSP16_MSS
@@ -6040,9 +6801,10 @@
   read it, you need the readprofile package from sunsite.unc.edu. Its
   manpage gives information regarding the format of profiling data. To
   become a kernel hacker, you can start with the Kernel Hacker's Guide
-  at http://www.redhat.com:8080/HyperNews/get/khg.html. Mere mortals
-  say N.
- 
+  at http://www.redhat.com:8080/HyperNews/get/khg.html (to browse the
+  WWW, you need to have access to a machine on the Internet that has a
+  program like lynx or netscape). Mere mortals say N.
+
 Profile shift count
 CONFIG_PROFILE_SHIFT
   This is used to adjust the granularity with which the addresses of
@@ -6079,12 +6841,13 @@
   emulator. Network devices support autodial, channel-bundling,
   callback and caller-authentication without having a daemon
   running. A reduced T.70 protocol is supported with tty's suitable
-  for German BTX. On D-Channel, the protocols EDSS1 and 1TR6 are
-  supported. See Documentation/isdn/README for more information.  If
-  you want to compile the ISDN 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 isdn.o.  If unsure, say N.
+  for German BTX. On D-Channel, the protocols EDSS1 (Euro-ISDN) and
+  1TR6 (German style) are supported. See Documentation/isdn/README for
+  more information.  If you want to compile the ISDN code 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 isdn.o.  If
+  unsure, say N.
 
 Support synchronous PPP
 CONFIG_ISDN_PPP
@@ -6199,7 +6962,7 @@
 CONFIG_HISAX_NI1
   You should choose the D-channel protocol your local
   telephone service provider uses here by saying Y or N.
-  
+
 HiSax Support for German 1TR6
 CONFIG_HISAX_1TR6
   You should choose the D-channel protocol your local
@@ -6226,21 +6989,25 @@
   can be inserted in and removed from the running kernel whenever you
   want, details in Documentation/modules.txt); the module will be
   called sc.o.  See Documentation/isdn/README.sc and
-  http://www.spellcast.com for more information.
+  http://www.spellcast.com for more information (to browse the WWW,
+  you need to have access to a machine on the Internet that has a
+  program like lynx or netscape).
 
 AVM-B1 with CAPI2.0 support
 CONFIG_ISDN_DRV_AVMB1
   This enables support for the AVM B1 ISDN networking cards. In
   addition, a CAPI (Common ISDN Application Programming Interface, a
   standard making it easy for programs to access ISDN hardware, see
-  http://www.capi.org/) interface for this card is provided. In order
-  to use this card, additional firmware is necessary, which has to be
-  downloaded into the card using a utility which is distributed
-  separately. Please read the file Documentation/isdn/README.avmb1.
-  This code 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 avmb1.o. If you want to compile it
-  as a module, say M here and read Documentation/modules.txt.
+  http://www.capi.org/; to browse the WWW, you need to have access to
+  a machine on the Internet that has a program like lynx or netscape)
+  interface for this card is provided. In order to use this card,
+  additional firmware is necessary, which has to be downloaded into
+  the card using a utility which is distributed separately. Please
+  read the file Documentation/isdn/README.avmb1.  This code 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 avmb1.o. If you want to compile it as a module, say M here
+  and read Documentation/modules.txt.
 
 Verbose reason code reporting (kernel size +=7K)
 CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
@@ -6252,8 +7019,9 @@
 CONFIG_AP1000
   This enables support for a sparc based parallel multi-computer
   called AP1000+. For details on our efforts to port Linux to this
-  machine see http://cap.anu.edu.au/cap/projects/linux or mail to
-  hackers@cafe.anu.edu.au
+  machine see http://cap.anu.edu.au/cap/projects/linux (to browse the
+  WWW, you need to have access to a machine on the Internet that has a
+  program like lynx or netscape) or mail to hackers@cafe.anu.edu.au
 
 Sparc ESP SCSI support
 CONFIG_SCSI_SUNESP
@@ -6269,6 +7037,14 @@
   removed from the running kernel whenever you want), say M and read
   Documentation/modules.txt. If unsure, say Y.
 
+Mostek real time clock support
+CONFIG_SUN_MOSTEK_RTC
+
+Siemens SAB82532 serial support
+CONFIG_SAB82532
+###
+### Please someone fill these in.
+###
 
 # m68k-specific kernel options
 # Documented by Chris Lawrence <quango@themall.net> et al.
@@ -6507,8 +7283,8 @@
 
 Fastlane SCSI support
 CONFIG_FASTLANE_SCSI
-  If you have the Phase5 Fastlane Z3 SCSI controller, or plan to use one
-  in the near future, say Y to this question.  Otherwise, say N.
+  If you have the Phase5 Fastlane Z3 SCSI controller, or plan to use
+  one in the near future, say Y to this question.  Otherwise, say N.
 
 Atari native SCSI support
 CONFIG_ATARI_SCSI
@@ -6629,32 +7405,13 @@
   whenever you want). If you want to compile it as a module, say M
   here and read Documentation/modules.txt.
 
-Radio support
-CONFIG_MISC_RADIO
-  If you have a radio card (you will probably know if you do!), then
-  you will want to say "y" here and make a character device file 
-  (usually /dev/radio) with major number 10 and minor 152 using mknod
-  ("man mknod").  And then, don't forget to pick up some useful tools
-  to use said device (you _might_ find something at ftp.lmh.ox.ac.uk:
-  /users/weejock/linux/radio/, but I haven't written anything too
-  useful yet...)
-
-AIMSlab RadioTrack card
-CONFIG_RADIO_RTRACK
-  Choose "y" here if you have one of these, and then fill in the port
-  address below.
-
-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.
-
 Atari SCC serial DMA support
 CONFIG_ATARI_SCC_DMA
-  This enables DMA support for receiving data on channel A of the SCC. If
-  you have a TT you may say Y here and read drivers/char/atari_SCC.README.
-  All other users should say N here, because only the TT has SCC-DMA, even
-  if your machine keeps claiming so at boot time.
+  This enables DMA support for receiving data on channel A of the
+  SCC. If you have a TT you may say Y here and read
+  drivers/char/atari_SCC.README.  All other users should say N here,
+  because only the TT has SCC-DMA, even if your machine keeps claiming
+  so at boot time.
 
 Atari MIDI serial support
 CONFIG_ATARI_MIDI
@@ -6760,7 +7517,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.
+  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
@@ -6778,7 +7538,11 @@
   On Power Macintoshes (and clones) with two SCSI buses, the external
   SCSI bus is usually controlled by a 53C94 SCSI bus adaptor.  Older
   machines which only have one SCSI bus, such as the 7200, also use
-  the 53C94.  Say Y to include support for the 53C94.
+  the 53C94.  Say Y to include support for the 53C94.  This driver is
+  also available as a module called mac53c94.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.
 
 MACE (Power Mac ethernet) support
 CONFIG_MACE
@@ -6788,14 +7552,37 @@
 
 Video For Linux
 CONFIG_VIDEO_DEV
-  Support for audio/video capture and overlay devices. The exact capabilities
-  of each device vary. User tools for this are available from
-  ftp://ftp.uk.linux.org/pub/linux/video4linux
+  Support for audio/video capture and overlay devices. The exact
+  capabilities of each device vary. User tools for this are available
+  from ftp://ftp.uk.linux.org/pub/linux/video4linux.  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 here and read
+  Documentation/modules.txt.
 
 BT848 Video For Linux
 CONFIG_VIDEO_BT848
-  Support for BT848 based frame grabber/overlay boards. This includes the 
-  Miro, Hauppauge and STB boards.
+  Support for BT848 based frame grabber/overlay boards. This includes
+  the Miro, Hauppauge and STB boards. This driver is
+  also available as a module called bttv.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.
+
+Quickcam BW Video For Linux
+CONFIG_VIDEO_BWQCAM
+  Say Y have if you have such a thing. This driver is also available
+  as a module called bw-qcam.o ( = code which can be inserted in and
+  removed from the running kernel whenever you want). If you want to
+  compile it as a module, say M here and read
+  Documentation/modules.txt.
+
+Mediavision Pro Movie Studio Video For Linux
+CONFIG_VIDEO_PMS
+  Say Y if you have such a thing. This driver is also available as a
+  module called pms.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.
 
 # need an empty line after last entry, for sed script in Configure.
 
@@ -6924,18 +7711,36 @@
 # LocalWords:  zorro CAPI AVMB capi avmb VP SYN syncookies EM em pc Ethertalk
 # LocalWords:  Dayna DL Daynatalk LT PhoneNET ATB Daystar queueing CMDS SCBs ls
 # LocalWords:  SCB STATS Thinnet ThunderLAN TLAN Netelligent NetFlex tlan james
-# LocalWords:  caldera Preload dcache Preloading slowdowns schoebel uni
-# LocalWords:  stuttgart rdist TRANS hostnames mango jukeboxes ESS
+# LocalWords:  caldera Preload dcache Preloading slowdowns schoebel uni NBD nbd
+# LocalWords:  stuttgart rdist TRANS hostnames mango jukeboxes ESS userland PD
 # 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 SCB's RX
 # LocalWords:  dataless kerneltype SYSNAME Netbeui Comtrol Rocketport palmtop
 # LocalWords:  nvram SYSRQ SysRq PrintScreen sysrq NVRAMs NvRAM Shortwave RTTY
 # LocalWords:  HFMODEM shortwave Sitor Amtor Pactor GTOR hfmodem hayes TX TMOUT
-# LocalWords:  QIC TR CONFIG FTAPE Iomega CMS FC FDC Exabyte Iomega's DFLT
-# LocalWords:  tapedrive THR FCD IRQ DMA SZ PCI ftape README txt HOWTO
-# LocalWords:  http www rwth aachen LBFM claus FAQ mt ZFTAPE VFS
-# LocalWords:  zftape zft William's lzrw kerneld BLK zftape's tar's
-# LocalWords:  afio's MTSETBLK MTIOCTOP dev qft setblk BLKSZ NR
-# LocalWords:  setdrvbuffer kb NUMBUFFERS Procfs PROC FS proc resp STD
-# LocalWords:  Alt LocalWords
+# LocalWords:  IDEPCI IDEDMA idedma PDC pdc TRM trm raidtools luthien nuclecu
+# LocalWords:  unam mx miguel koobera uic EMUL solaris pp ieee lpsg co DMAs TOS
+# LocalWords:  BLDCONFIG preloading jumperless BOOTINIT modutils multipath GRE
+# LocalWords:  misconfigured autoconfiguration IPGRE ICMP tracert ipautofw PIM
+# LocalWords:  netis rlynch autofw ipportfw monmouth ipsubs portforwarding pimd
+# LocalWords:  portfw PIMSM netweb usc pim pf EUI aggregatable PB decapsulate
+# LocalWords:  ipddp Decapsulation DECAP bool HAMRADIO WAN's tcpdump af CD's tx
+# LocalWords:  ethertap multisession PPC MMIO GDT GDTH ICP gdth hamradio LAN's
+# LocalWords:  lmh weejock AIMSlab RadioTrack RTRACK HZP OptoSCC TRX rx TRXECHO
+# LocalWords:  DMASCC paccomm dmascc addr cfg oevsv oe kib picpar FDX baudrate
+# LocalWords:  baudrates fdx HDX hdx PSK kanren frforum QoS SCHED CBQ SCH sched
+# LocalWords:  sch cbq CSZ Shenker Zhang csz SFQ sfq TBF tbf PFIFO fifo PRIO RW
+# LocalWords:  prio Micom xIO dwmw rimi OMIRR omirr omirrd unicode ntfs cmu
+# LocalWords:  Braam braam Schmidt's freiburg nls codepages codepage Romanian
+# LocalWords:  Slovak Slovenian Sorbian Nordic iso Catalan Faeroese Galician SZ
+# LocalWords:  Valencian Slovene Esperanto Estonian Latvian Byelorussian KOI mt
+# LocalWords:  charset Inuit Greenlandic Sami Lappish koi SOFTCURSOR softcursor
+# LocalWords:  Specialix specialix DTR RTS RTSCTS cycladesZ Exabyte ftape's
+# LocalWords:  Iomega's LBFM claus ZFTAPE VFS zftape zft William's lzrw DFLT kb
+# LocalWords:  MTSETBLK MTIOCTOP qft setblk zftape's tar's afio's setdrvbuffer
+# LocalWords:  Procfs Exabyte's THR FCD sysvinit init PSC pscwdt VMIDI Euro SAB
+# LocalWords:  Mostek Fastlane PowerMac PReP PMAC PowerPC Macintoshes Starmax
+# LocalWords:  PowerStack Starmaxes MCOMMON DEVICETREE ATY IMS IMSTT videodev
+# LocalWords:  BT Hauppauge STB bttv Quickcam BW BWQCAM bw qcam Mediavision PMS
+# LocalWords:  pms
diff -u --recursive --new-file v2.1.76/linux/Documentation/cdrom/00-INDEX linux/Documentation/cdrom/00-INDEX
--- v2.1.76/linux/Documentation/cdrom/00-INDEX	Wed Dec 18 05:57:28 1996
+++ linux/Documentation/cdrom/00-INDEX	Sun Dec 28 12:05:44 1997
@@ -2,8 +2,6 @@
 	- this file (info on CD-ROMs and Linux)
 aztcd
 	- info on Aztech/Orchid/Okano/Wearnes/Conrad/CyCDROM driver.
-bpcd
-	- info on MicroSolutions backpack CDROM 
 
 cdrom-standard.tex
 	- LaTeX document on standardizing the CD-ROM programming interface.
diff -u --recursive --new-file v2.1.76/linux/Documentation/cdrom/bpcd linux/Documentation/cdrom/bpcd
--- v2.1.76/linux/Documentation/cdrom/bpcd	Wed Dec 18 05:57:28 1996
+++ linux/Documentation/cdrom/bpcd	Wed Dec 31 16:00:00 1969
@@ -1,74 +0,0 @@
-linux/Documentation/cdrom/bpcd	(c) 1996  Grant R. Guenther <grant@torque.net>
-
-This file documents the bpcd driver for the MicroSolutions backpack CDrom,
-an external parallel port device.  
-
-There are apparently two versions of the backpack protocol.  This 
-driver knows about the version 2 protocol - as is used in the 4x 
-and 6x products.  There is no support for the sound hardware that
-is included in some models.  It should not be difficult to add 
-support for the ATAPI audio play functions and the corresponding 
-ioctls.
-
-The driver was developed by reverse engineering the protocol
-and testing it on the backpack model 164550.  This model
-is actually a stock ATAPI drive packaged with a custom 
-ASIC that implements the IDE over parallel protocol.
-I tested with a backpack that happened to contain a Goldstar
-drive, but I've seen reports of Sony and Mitsumi drives as well.
-
-Before attempting to use the driver, you will need to
-create a new device special file.  The following commands will
-do that for you:
-
-		mknod /dev/bpcd b 41 0
-		chown root:disk /dev/bpcd
-		chmod 660 /dev/bpcd
-
-Afterward, you can mount a disk in the usual way:
-
-		mount -t iso9660 /dev/bpcd /cdrom
-
-(assuming you have made a directory /cdrom to use as a mount point).
-
-The driver will attempt to detect which parallel port your
-backpack is connected to.  If this fails for any reason, you
-can override it by specifying a port on the LILO command line
-(for built in drivers) or the insmod command (for drivers built
-as modules).   If your drive is on the port at 0x3bc, you would
-use one of these commands:
-
-		LILO:	   bpcd=0x3bc
-
-		insmod:    insmod bpcd bp_base=0x3bc
-
-The driver can detect if the parallel port supports 8-bit
-transfers.  If so, it will use them.  You can force it to use
-4-bit (nybble) mode by setting the variable bp_nybble to 1 on
-an insmod command, or using the following LILO parameters:
-
-		bpcd=0x3bc,1
-
-(you must specify the correct port address if you use this method.)
-
-There is currently no support for EPP or ECP modes.  Also,
-as far as I can tell, the MicroSolutions protocol does not
-support interrupts in the 4-bit and 8-bit modes.
-
-MicroSolutions' protocol allows for several drives to be
-chained together off the same parallel port.  Currently, this
-driver will recognise only one of them.  If you do have more
-than one drive, it will choose the one with the lowest id number,
-where the id number is the last two digits of the product's
-serial number.
-
-It is not currently possible to connect a printer to the chained
-port on the BackPack and expect Linux to use both devices at once.
-If you need to use this driver together with a printer on the
-same port, build both the bpcd and lp drivers as modules.
-
-Keep an eye on http://www.torque.net/bpcd.html for news and
-other information about the driver.  If you have any problems
-with this driver, please send me, grant@torque.net, some mail 
-directly before posting into the newsgroups or mailing lists.
-
diff -u --recursive --new-file v2.1.76/linux/Documentation/cdrom/cdrom-standard.tex linux/Documentation/cdrom/cdrom-standard.tex
--- v2.1.76/linux/Documentation/cdrom/cdrom-standard.tex	Tue Dec  2 16:45:17 1997
+++ linux/Documentation/cdrom/cdrom-standard.tex	Wed Dec 31 11:34:15 1997
@@ -1,5 +1,5 @@
 \documentclass{article}
-\def\version{$Id: cdrom-standard.tex,v 1.8 1997/11/19 21:58:33 david Exp $}
+\def\version{$Id: cdrom-standard.tex,v 1.9 1997/12/28 15:42:49 david Exp $}
 \newcommand{\newsection}[1]{\newpage\section{#1}}
 
 \evensidemargin=0pt
@@ -388,46 +388,6 @@
 }
 $$
 
-\subsection{$Disc_status$}
-\label{disc status}
-
-As a complement to $drive_status()$, this function can provide {\emph
-{some}} information about the current disc that is inserted in the
-drive. This function is now implemented internally in the \UCD, so the
-low-level drivers do not need to implement this functionality anymore.
-
-The history of development of the CD's use as a carrier medium for
-various digital information has lead to many different disc types. This
-$ioctl$ makes the false assumption that CDs have {\emph {only one}} type
-of data on them. While this is often the case, it is also very common
-for CDs to have some tracks with data, and some tracks with audio.
-Because this is an existing interface, rather than fixing this interface
-by changing the assumptions it was made under, thereby breaking all user
-applications that use this function, the \UCD\ implements this $ioctl$
-as follows: If the CD in question has audio tracks on it, and it has
-absolutly no CD-I, XA, or data tracks on it, it will be reported as
-$CDS_AUDIO$. Failing that, if the CD in question has any CD-I tracks
-on it, it will be reported as $CDS_XA_2_2$. Failing that, if the CD in
-question has any XA tracks on it, it will be reported as $CDS_XA_2_1$.
-Finally, if the CD in question has any data tracks on it, it will be
-reported as a data CD ($CDS_DATA_1$). 
-
-This function can return:
-$$
-\halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr
-CDS_NO_INFO& no information available\cr
-CDS_NO_DISC& no disc is inserted, or tray is opened\cr
-CDS_AUDIO& Audio disc (2352 audio bytes/frame)\cr
-CDS_DATA_1& data disc, mode 1 (2048 user bytes/frame)\cr
-CDS_DATA_2& data disc, mode 2 (2336 user bytes/frame)\cr
-CDS_XA_2_1& mixed data (XA), mode 2, form 1 (2048 user bytes)\cr
-CDS_XA_2_2& mixed data (XA), mode 2, form 1 (2324  user bytes)\cr
-}
-$$
-As far as I know, data \cdrom s are always of type $CDS_DATA_1$. For
-some information concerning frame layout of the various disc types, see
-a recent version of \cdromh. 
-
 \subsection{$Int\ media_changed(struct\ cdrom_device_info * cdi, int\ disc_nr)$}
 
 This function is very similar to the original function in $struct\ 
@@ -940,8 +900,46 @@
   given. The special value $CDSL_CURRENT$ requests that information
   about the currently selected slot is returned.
 \item[CDROM_DISC_STATUS] Returns the type of the disc currently in the
-  drive by a call to $disc_status()$. Return values are as defined in
-  section~\ref{disc status}.
+  drive.  It should be viewed as a complement to $CDROM_DRIVE_STATUS$.
+  This $ioctl$ can provide \emph {some} information about the current
+  disc that is inserted in the drive.  This functionality used to be
+  implemented in the low level drivers, but is now carried out
+  entirely in \UCD.
+  
+  The history of development of the CD's use as a carrier medium for
+  various digital information has lead to many different disc types.
+  This $ioctl$ is useful only in the case that CDs have \emph {only
+    one} type of data on them.  While this is often the case, it is
+  also very common for CDs to have some tracks with data, and some
+  tracks with audio.  Because this is an existing interface, rather
+  than fixing this interface by changing the assumptions it was made
+  under, thereby breaking all user applications that use this
+  function, the \UCD\ implements this $ioctl$ as follows: If the CD in
+  question has audio tracks on it, and it has absolutly no CD-I, XA,
+  or data tracks on it, it will be reported as $CDS_AUDIO$.  If it has
+  both audio and data tracks, it will return $CDS_MIXED$.  If there
+  are no audio tracks on the disc, and if the CD in question has any
+  CD-I tracks on it, it will be reported as $CDS_XA_2_2$.  Failing
+  that, if the CD in question has any XA tracks on it, it will be
+  reported as $CDS_XA_2_1$.  Finally, if the CD in question has any
+  data tracks on it, it will be reported as a data CD ($CDS_DATA_1$).
+
+  This $ioctl$ can return:
+  $$
+  \halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr
+    CDS_NO_INFO& no information available\cr
+    CDS_NO_DISC& no disc is inserted, or tray is opened\cr
+    CDS_AUDIO& Audio disc (2352 audio bytes/frame)\cr
+    CDS_DATA_1& data disc, mode 1 (2048 user bytes/frame)\cr
+    CDS_DATA_2& data disc, mode 2 (2336 user bytes/frame)\cr
+    CDS_XA_2_1& mixed data (XA), mode 2, form 1 (2048 user bytes)\cr
+    CDS_XA_2_2& mixed data (XA), mode 2, form 1 (2324  user bytes)\cr
+    CDS_MIXED& mixed audio/data disc\cr
+    }
+  $$
+  For some information concerning frame layout of the various disc
+  types, see a recent version of \cdromh.
+
 \item[CDROM_CHANGER_NSLOTS] Returns the number of slots in a
   juke-box. 
 \end{description}
@@ -1021,4 +1019,3 @@
 $ \version\ $
 \eject
 \end{document}
-
diff -u --recursive --new-file v2.1.76/linux/Documentation/ez.txt linux/Documentation/ez.txt
--- v2.1.76/linux/Documentation/ez.txt	Wed Dec 18 05:57:28 1996
+++ linux/Documentation/ez.txt	Wed Dec 31 16:00:00 1969
@@ -1,85 +0,0 @@
-linux/Documentation/ez.txt (c) 1996  Grant R. Guenther <grant@torque.net>
-
-This file documents the ez driver for the parallel port versions of 
-SyQuest's EZ135 and EZ230 removable media disk drives.  
-        
-Special thanks go to Pedro Soria-Rodriguez for his help testing 
-the EZFlyer 230 support.
-
-The drive is actually SyQuest's IDE product with a ShuttleTech 
-IDE <-> parallel converter chip built in.
-
-Before attempting to access the new driver, you will need to
-create some device special files.  The following commands will
-do that for you:
-
-                mknod /dev/eza  b 40 0
-                mknod /dev/eza1 b 40 1
-                mknod /dev/eza2 b 40 2
-                mknod /dev/eza3 b 40 3
-                mknod /dev/eza4 b 40 4
-                chown root:disk /dev/ez*
-                chmod 660 /dev/ez*
-
-You can make devices for more partitions (up to 15) if you need to.
-
-You can alter certain driver parameters on the LILO or LOADLIN 
-command line.  The general syntax is
-
-		ez=base[,irq]
-
-where base is the base address of the parallel port you want to use 
-and irq is the interrupt number for that port.   By default, the 
-driver uses the ports at 0x378 and irq 7.  You can disable the 
-interrupt by specifying it as 0.  For example, to run the driver 
-on port 0x3bc without an interrupt, you would append the following 
-to the LILO command line:
-
-		ez=0x3bc,0
-
-If you have configured the driver as a loadable module, you can 
-adjust these parameters on the insmod command line using the 
-variables ez_base and ez_irq.  For example:
-
-                insmod ez ez_base=0x3bc
-
-The driver can detect if the parallel port supports 8-bit
-transfers.  If so, it will use them.
-
-The driver can be used with or without interrupts.  If an IRQ
-is specified the driver will use it - if it can. If the irq 
-number is set to 0, an alternative, polling-based, strategy 
-will be used.  Polling consumes more CPU time, but may be more 
-stable on some systems.
-
-If you experience timeout errors while using this driver - and
-you have enabled interrupts - try disabling the interrupt.  I
-have heard reports of some parallel ports having exceptionally
-unreliable interrupts.  This could happen on misconfigured 
-systems in which an inactive sound card shares the same IRQ with 
-the parallel port. (Remember that most people do not use the
-parallel port interrupt for printing.)
-
-It would be advantageous to use multiple mode transfers,
-but ShuttleTech's driver does not appear to use them, so I'm not
-sure that the converter can handle it.
-
-It is not currently possible to connect a printer to the chained
-port on an EZ drive and expect Linux to use both devices at once.
-If you need to do this, build both the ez and lp drivers as modules
-and load one or the other as required.
-
-When the EZ230 powers on, the "standby timer" is set to about 6
-minutes:  if the drive is idle for that length of time, it will
-put itself into a low power standby mode.  It takes a couple of
-seconds for the drive to come out of standby mode.  So, if you
-load this driver while it is in standby mode, you will notice
-a "freeze" of a second or two as the driver waits for the EZ230
-to come back to life.  Once loaded, this driver disables the
-standby timer (until you next power up the EZ230 ...)
-
-Keep an eye on http://www.torque.net/ez135.html for news and
-other information about the driver.  If you have any problems
-with this driver, please send me, grant@torque.net, some mail 
-directly before posting into the newsgroups or mailing lists.
-
diff -u --recursive --new-file v2.1.76/linux/Documentation/modules.txt linux/Documentation/modules.txt
--- v2.1.76/linux/Documentation/modules.txt	Sat Nov 29 11:25:08 1997
+++ linux/Documentation/modules.txt	Sun Dec 28 12:05:44 1997
@@ -60,7 +60,6 @@
 		aztcd:     Aztech,Orchid,Okano,Wearnes
 		cm206:     Philips/LMS CM206
 		gscd:      Goldstar GCDR-420
-		bpcd:      MicroSolutions backpack CDrom
 		mcd, mcdx: Mitsumi LU005, FX001
 		optcd:     Optics Storage Dolphin 8000AT
 		sjcd:      Sanyo CDR-H94A
diff -u --recursive --new-file v2.1.76/linux/Documentation/networking/filter.txt linux/Documentation/networking/filter.txt
--- v2.1.76/linux/Documentation/networking/filter.txt	Sun Dec 21 22:36:12 1997
+++ linux/Documentation/networking/filter.txt	Mon Dec 29 10:22:43 1997
@@ -4,14 +4,14 @@
 Introduction
 ============
 
-	Linux Socket Filtering is a deviation of the Berkely
+	Linux Socket Filtering is derived from the Berkeley
 Packet Filter. There are some distinct differences between
 the BSD and Linux Kernel Filtering.
 
 Linux Socket Filtering (LSF) allows a user-space program to
 attach a filter onto any socket and allow or disallow certain
 types of data to come through the socket. LSF follows exactly
-the same filter code structure as the BSD Berkely Packet Filter
+the same filter code structure as the BSD Berkeley Packet Filter
 (BPF), so refering to the BSD bpf.4 manpage is very helpful in
 creating filters.
 
@@ -39,4 +39,4 @@
 setsockopt(sockfd, SOL_SOCKET, SO_DETACH_FILTER, &value, sizeof(value));
 
 See the BSD bpf.4 manpage and the BSD Packet Filter paper written by
-Steven McCanne and Van Jacobson of Lawrence Berkely Laboratory.
+Steven McCanne and Van Jacobson of Lawrence Berkeley Laboratory.
diff -u --recursive --new-file v2.1.76/linux/Documentation/paride.txt linux/Documentation/paride.txt
--- v2.1.76/linux/Documentation/paride.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/paride.txt	Sun Dec 28 12:05:44 1997
@@ -0,0 +1,338 @@
+
+		Linux and parallel port IDE devices
+
+PARIDE v1.0   (c) 1997  Grant Guenther <grant@torque.net>
+
+1. Introduction
+
+Owing to the simplicity and near universality of the parallel port interface
+to personal computers, many external devices such as portable hard-disk,
+CD-ROM, LS-120 and tape drives use the parallel port to connect to their
+host computer.  While some devices (notably scanners) use ad-hoc methods
+to pass commands and data through the parallel port interface, most 
+external devices are actually identical to an internal model, but with
+a parallel-port adapter chip added in.  Some of the original parallel port
+adapters were little more than mechanisms for mulitplexing a SCSI bus.
+(The Iomega PPA-3 adapter used in the ZIP drives is an example of this
+approach).  Most current designs, however, take a different approach.
+The adapter chip reproduces a small ISA or IDE bus in the external device
+and the communication protocol provides operations for reading and writing
+device registers, as well as data block transfer functions.  Sometimes,
+the device being addressed via the parallel cable is a standard SCSI
+controller like an NCR 5380.  The "ditto" family of external tape
+drives use the ISA replicator to interface a floppy disk controller,
+which is then connected to a floppy-tape mechanism.  The vast majority
+of external parallel port devices, however, are now based on standard
+IDE type devices, which require no intermediate controller.  If one
+were to open up a parallel port CD-ROM drive, for instance, one would
+find a standard ATAPI CD-ROM drive, a power supply, and a single adapter
+that interconnected a standard PC parallel port cable and a standard
+IDE cable.  It is usually possible to exchange the CD-ROM device with
+any other device using the IDE interface. 
+
+The document describes the support in Linux for parallel port IDE
+devices.  It does not cover parallel port SCSI devices, "ditto" tape
+drives or scanners.  Many different devices are supported by the 
+parallel port IDE subsystem, including:
+
+	MicroSolutions backpack CD-ROM
+	MicroSolutions backpack PD/CD
+	MicroSolutions backpack hard-drives
+	SyQuest EZ-135, EZ-230 & SparQ drives
+	Avatar Shark
+	Imation Superdisk LS-120
+	FreeCom Power CD
+	Hewlett-Packard 5GB tape drive
+
+as well as most of the clone and no-name products on the market.
+
+To support such a wide range of devices, PARIDE, the parallel port IDE
+subsystem, is actually structured in three parts.   There is a base
+paride module which provides a registry and some common methods for
+accessing the parallel ports.  The second component is a set of 
+high-level drivers for each of the different type of supported device: 
+
+	pd	IDE disk
+	pcd	ATAPI CD-ROM
+	pf	ATAPI disk
+	pt	ATAPI tape	(not yet available)
+
+(Support for ATAPI CD-R and CD-RW drives is not yet in development,
+but this may change.)
+
+The high-level drivers function according to the relevant standards.
+The third component of PARIDE is a set of low-level protocol drivers
+for each of the parallel port IDE adapter chips.  Thanks to the interest
+and encouragement of Linux users from many parts of the world, 
+support is available for almost all known adapter protocols:
+
+        aten    ATEN EH-100                            (HK)
+        bpck    Microsolutions backpack                (US)
+        comm    DataStor (old-type) "commuter" adapter (TW)
+        dstr    DataStor EP-2000                       (TW)
+        epat    Shuttle EPAT                           (UK)
+        epia    Shuttle EPIA                           (UK)
+        frpw    Freecom Power                          (DE)
+        kbic    KingByte KBIC-951A and KBIC-971A       (TW)
+        on20    OnSpec 90c20                           (US)
+        on26    OnSpec 90c26                           (US)
+
+(A driver for some modes of the Noveca RAP// protocol is also under 
+development).	
+
+
+2. Using the PARIDE subsystem
+
+While configuring the Linux kernel, you may choose either to build
+the PARIDE drivers into your kernel, or to build them as modules.
+
+In either case, you will need to select "Parallel port IDE device support"
+as well as at least one of the high-level drivers and at least one
+of the parallel port communication protocols.  If you do not know
+what kind of parallel port adapter is used in your drive, you could
+begin by checking the file names and any text files on your DOS 
+installation floppy.  Alternatively, you can look at the markings on
+the adapter chip itself.  That's usually sufficient to identify the
+correct device.  
+
+You can actually select all the protocol modules, and allow the PARIDE
+subsystem to try them all for you.
+
+For the "brand-name" products listed above, here are the protocol
+and high-level drivers that you would use:
+
+	Manufacturer		Model		Driver	Protocol
+	
+	MicroSolutions		CD-ROM		pcd	bpck
+	MicroSolutions		PD drive	pf	bpck
+	MicroSolutions		hard-drive	pd	bpck
+	SyQuest			EZ, SparQ	pd	epat
+	Imation			Superdisk	pf	epat
+	Avatar			Shark		pd	epat
+	FreeCom			CD-ROM		pcd	frpw
+	Hewlett-Packard		5GB Tape	pt	epat
+
+2.1  Configuring built-in drivers
+
+We recommend that you get to know how the drivers work and how to
+configure them as loadable modules, before attempting to compile a
+kernel with the drivers built-in.
+
+If you built all of your PARIDE support directly into your kernel,
+and you have just a single parallel port IDE device, your kernel should
+locate it automatically for you.  If you have more than one device,
+you may need to give some command line options to your bootloader
+(eg: LILO), how to do that is beyond the scope of this document.
+
+The high-level drivers accept a number of command line parameters, all
+of which are documented in the source files in linux/drivers/block/paride.
+By default, each driver will automatically try all parallel ports it
+can find, and all protocol types that have been installed, until it finds
+a parallel port IDE adapter.  Once it finds one, the probe stops.  So,
+if you have more than one device, you will need to tell the drivers
+how to identify them.  This requires specifying the port address, the
+protocol identification number and, for some devices, the drive's
+chain ID.  While your system is booting, a number of messages are
+displayed on the console.  Like all such messages, they can be
+reviewed with the 'dmesg' command.  Among those messages will be
+some lines like:
+
+	paride: bpck registered as protocol 0
+	paride: epat registered as protocol 1
+
+The numbers will always be the same until you build a new kernel with
+different protocol selections.  You should note these numbers as you
+will need them to identify the devices.
+
+If you happen to be using a MicroSolutions backpack device, you will
+also need to know the unit ID number for each drive.  This is usually
+the last two digits of the drive's serial number (but read MicroSolution's
+documentation about this).
+
+As an example, lets assume that you have a MicroSolutions PD/CD drive
+with unit ID number 36 connected to the parallel port at 0x378, a SyQuest 
+EZ-135 connected to the chained port on the PD/CD drive and also an 
+Imation Superdisk connected to port 0x278.  You could give the following 
+options on your boot command:
+
+	pd.drive0=0x378,1 pf.drive0=0x278,1 pf.drive1=0x378,0,36
+
+In the last option, pf.drive1 configures device /dev/pf1, the 0x378
+is the parallel port base address, the 0 is the protocol registration
+number and 36 is the chain ID.
+
+Please note:  while PARIDE will work both with and without the 
+PARPORT parallel port sharing system that is included by the
+"Parallel port support" option, PARPORT must be included and enabled
+if you want to use chains of devices on the same parallel port.
+
+2.2  Loading and configuring PARIDE as modules
+
+It is much faster and simpler to get to understand the PARIDE drivers
+if you use them as loadable kernel modules.   
+
+Note 1:  using these drivers with the "kerneld" automatic module loading
+system is not recommended, and is not documented here.  
+
+Note 2:  if you build PARPORT support as a loadable module, PARIDE must
+also be built as loadable modules, and PARPORT must be loaded before the
+PARIDE modules.
+
+To use PARIDE, you must begin by 
+
+	insmod paride
+
+this loads a base module which provides a registry for the protocols,
+among other tasks.
+
+Then, load as many of the protocol modules as you think you might need.
+As you load each module, it will register the protocols that it supports,
+and print a log message to your kernel log file and your console. For 
+example:
+
+	# insmod epat
+	paride: epat registered as protocol 0
+	# insmod kbic
+	paride: k951 registered as protocol 1
+        paride: k971 registered as protocol 2
+
+Finally, you can load high-level drivers for each kind of device that
+you have connected.  By default, each driver will autoprobe for a single 
+device, but you can support up to four similar devices by giving their
+individual co-ordinates when you load the driver.
+
+For example, if you had two no-name CD-ROM drives both using the
+KingByte KBIC-951A adapter, one on port 0x378 and the other on 0x3bc
+you could give the following command:
+
+	# insmod pcd drive0=0x378,1 drive1=0x3bc,1
+
+For most adapters, giving a port address and protocol number is sufficient,
+but check the source files in linux/drivers/block/paride for more 
+information.  (Hopefully someone will write some man pages one day !).
+
+As another example, here's what happens when PARPORT is installed, and
+a SyQuest EZ-135 is attached to port 0x378:
+
+	# insmod paride
+	paride: version 1.0 installed
+	# insmod epat
+	paride: epat registered as protocol 0
+	# insmod pd
+	pd: pd version 1.0, major 45, cluster 64, nice 0
+	pda: Sharing parport1 at 0x378
+	pda: epat 1.0, Shuttle EPAT chip c3 at 0x378, mode 5 (EPP-32), delay 1
+	pda: SyQuest EZ135A, 262144 blocks [128M], (512/16/32), removable media
+	 pda: pda1
+
+Note that the last line is the output from the generic partition table
+scanner - in this case it reports that it has found a disk with one partition.
+
+2.3  Using a PARIDE device
+
+Once the drivers have been loaded, you can access PARIDE devices in the
+same way as their traditional counterparts.  You will probably need to
+create the device "special files".  Here is a simple script that you can
+cut to a file and execute:
+
+#!/bin/bash
+#
+# mkd -- a script to create the device special files for the PARIDE subsystem
+#
+function mkdev {
+  mknod $1 $2 $3 $4 ; chmod 0660 $1 ; chown root:disk $1
+}
+#
+function pd {
+  D=$( printf \\$( printf "x%03x" $[ $1 + 97 ] ) )
+  mkdev pd$D b 45 $[ $1 * 16 ]
+  for P in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+  do mkdev pd$D$P b 45 $[ $1 * 16 + $P ]
+  done
+}
+#
+cd /dev
+#
+for u in 0 1 2 3 ; do pd $u ; done
+for u in 0 1 2 3 ; do mkdev pcd$u b 46 $u ; done 
+for u in 0 1 2 3 ; do mkdev pf$u  b 47 $u ; done 
+for u in 0 1 2 3 ; do mkdev pt$u  c 96 $u ; done 
+for u in 0 1 2 3 ; do mkdev npt$u c 96 $[ $u + 128 ] ; done 
+#
+# end of mkd
+
+With the device files and drivers in place, you can access PARIDE devices
+like any other Linux device.   For example, to mount a CD-ROM in pcd0, use:
+
+	mount /dev/pcd0 /cdrom
+
+If you have a fresh Avatar Shark cartridge, and the drive is pda, you
+might do something like:
+
+	fdisk /dev/pda		-- make a new partition table with
+				   partition 1 of type 83
+
+	mke2fs /dev/pda1	-- to build the file system
+
+	mkdir /shark		-- make a place to mount the disk
+
+	mount /dev/pda1 /shark
+
+Devices like the Imation superdisk work in the same way, except that
+they do not have a partition table.  For example to make a 120MB
+floppy that you could share with a DOS system:
+
+	mkdosfs /dev/pf0
+	mount /dev/pf0 /mnt
+
+
+3. Troubleshooting
+
+While a lot of testing has gone into these drivers to make them work
+as smoothly as possible, problems will arise.  If you do have problems,
+please check all the obvious things first:  does the drive work in
+DOS with the manufacturer's drivers ?  If that doesn't yield any useful
+clues, then please make sure that only one drive is hooked to your system,
+and that either (a) PARPORT is enabled or (b) no other device driver
+is using your parallel port (check in /proc/ioports).  Then, load the
+appropriate drivers (you can load several protocol modules if you want)
+as in:
+
+	# insmod paride
+	# insmod epat
+	# insmod bpck
+	# insmod kbic
+	...
+	# insmod pd verbose=1
+
+(using the correct driver for the type of device you have, of course).
+The verbose=1 parameter will cause the drivers to log a trace of their
+activity as they attempt to locate your drive.
+
+Use 'dmesg' to capture a log of all the PARIDE messages (any messages
+beginning with paride:, a protocol module's name or a driver's name) and
+include that with your bug report.  You can submit a bug report in one
+of two ways.  Either send it directly to the author of the PARIDE suite,
+by e-mail to grant@torque.net, or join the linux-parport mailing list
+and post your report there.
+
+You can join the linux-parport mailing list by sending a mail message
+to 
+		linux-parport-request@torque.net
+
+with the single word 
+
+		subscribe
+
+in the body of the mail message (not in the subject line).   Please be
+sure that your mail program is correctly set up when you do this,  as
+the list manager is a robot that will subscribe you using the reply
+address in your mail headers.  REMOVE any anti-spam gimmicks you may
+have in your mail headers, when sending mail to the list server.
+
+You might also find some useful information on the linux-parport
+web pages (although they are not always up to date) at
+
+	http://www.torque.net/linux-pp.html
+
+
diff -u --recursive --new-file v2.1.76/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.76/linux/MAINTAINERS	Sun Dec 21 16:17:44 1997
+++ linux/MAINTAINERS	Sun Dec 28 12:05:44 1997
@@ -511,6 +511,13 @@
 W:	http://www.cage.curtin.edu.au/~campbell/parbus/
 S:	Maintained
 
+PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES
+P:	Grant Guenther
+M:	grant@torque.net
+L:	linux-parport@torque.net
+W:	http://www.torque.net/linux-pp.html
+S:	Maintained
+
 PNP SUPPORT
 P:	Tom Lees
 M:	tom@lpsg.demon.co.uk
diff -u --recursive --new-file v2.1.76/linux/Makefile linux/Makefile
--- v2.1.76/linux/Makefile	Tue Dec 23 16:30:59 1997
+++ linux/Makefile	Wed Dec 31 11:55:14 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 76
+SUBLEVEL = 77
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
@@ -157,6 +157,10 @@
 DRIVERS := $(DRIVERS) drivers/pnp/pnp.a
 endif
 
+ifeq ($(CONFIG_PARIDE),y)
+DRIVERS := $(DRIVERS) drivers/block/paride/paride.a
+endif
+
 ifdef CONFIG_HAMRADIO
 DRIVERS := $(DRIVERS) drivers/net/hamradio/hamradio.a
 endif
@@ -346,10 +350,10 @@
 
 mrproper: clean
 	rm -f include/linux/autoconf.h include/linux/version.h
-	rm -f drivers/net/soundmodem/sm_tbl_{afsk1200,afsk2666,fsk9600}.h
-	rm -f drivers/net/soundmodem/sm_tbl_{hapn4800,psk4800}.h
-	rm -f drivers/net/soundmodem/sm_tbl_{afsk2400_7,afsk2400_8}.h
-	rm -f drivers/net/soundmodem/gentbl
+	rm -f drivers/net/hamradio/soundmodem/sm_tbl_{afsk1200,afsk2666,fsk9600}.h
+	rm -f drivers/net/hamradio/soundmodem/sm_tbl_{hapn4800,psk4800}.h
+	rm -f drivers/net/hamradio/soundmodem/sm_tbl_{afsk2400_7,afsk2400_8}.h
+	rm -f drivers/net/hamradio/soundmodem/gentbl
 	rm -f drivers/char/hfmodem/gentbl drivers/char/hfmodem/tables.h
 	rm -f drivers/sound/*_boot.h drivers/sound/.*.boot
 	rm -f .version .config* config.in config.old
diff -u --recursive --new-file v2.1.76/linux/arch/alpha/kernel/osf_sys.c linux/arch/alpha/kernel/osf_sys.c
--- v2.1.76/linux/arch/alpha/kernel/osf_sys.c	Wed Dec 10 11:12:43 1997
+++ linux/arch/alpha/kernel/osf_sys.c	Sun Dec 28 11:48:03 1997
@@ -966,7 +966,7 @@
 
 static inline long put_it32(struct itimerval32 *o, struct itimerval *i)
 {
-	return (!access_ok(VERIFY_WRITE, i, sizeof(*i)) ||
+	return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) ||
 		(__put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) |
 		 __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) |
 		 __put_user(i->it_value.tv_sec, &o->it_value.tv_sec) |
@@ -1241,7 +1241,7 @@
  * seems to be a timeval pointer, and I suspect the second
  * one is the time remaining.. Ho humm.. No documentation.
  */
-asmlinkage int osf_usleep_thread(struct timeval *sleep, struct timeval *remain)
+asmlinkage int osf_usleep_thread(struct timeval32 *sleep, struct timeval32 *remain)
 {
 	struct timeval tmp;
 	unsigned long ticks;
diff -u --recursive --new-file v2.1.76/linux/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- v2.1.76/linux/arch/i386/boot/setup.S	Tue Dec  2 09:49:38 1997
+++ linux/arch/i386/boot/setup.S	Mon Dec 29 10:23:54 1997
@@ -274,6 +274,8 @@
 
 oldstylemem:
 	pop	ebx
+#else
+	mov     dword ptr [0x1e0], #0
 #endif
 	mov	ah,#0x88
 	int	0x15
diff -u --recursive --new-file v2.1.76/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.76/linux/arch/i386/defconfig	Sun Dec 21 22:36:12 1997
+++ linux/arch/i386/defconfig	Tue Dec 30 11:38:26 1997
@@ -73,7 +73,8 @@
 # CONFIG_BLK_DEV_MD is not set
 # CONFIG_BLK_DEV_RAM is not set
 # CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_EZ is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
 # CONFIG_BLK_DEV_HD is not set
 
 #
@@ -240,10 +241,6 @@
 CONFIG_AUTOFS_FS=y
 # CONFIG_UFS_FS is not set
 # CONFIG_MAC_PARTITION is not set
-
-#
-# Native Language Support
-#
 # CONFIG_NLS is not set
 
 #
@@ -269,6 +266,7 @@
 # CONFIG_RTC is not set
 # CONFIG_VIDEO_DEV is not set
 # CONFIG_VIDEO_BT848 is not set
+# CONFIG_VIDEO_BWQCAM is not set
 # CONFIG_VIDEO_PMS is not set
 # CONFIG_NVRAM is not set
 # CONFIG_JOYSTICK is not set
diff -u --recursive --new-file v2.1.76/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v2.1.76/linux/arch/i386/kernel/ptrace.c	Fri Dec 19 15:52:48 1997
+++ linux/arch/i386/kernel/ptrace.c	Tue Dec 23 20:23:53 1997
@@ -611,7 +611,7 @@
 			  child->tss.i387.hard.twd = 0xffffffff;
 			}
 #ifdef CONFIG_MATH_EMULATION
-			if ( hard_math ) {
+			if ( boot_cpu_data.hard_math ) {
 #endif
 			  if (last_task_used_math == child) {
 			    clts();
@@ -639,7 +639,7 @@
 			  }
 			child->used_math = 1;
 #ifdef CONFIG_MATH_EMULATION
-			if ( hard_math ) {
+			if ( boot_cpu_data.hard_math ) {
 #endif
 			  if (last_task_used_math == child) {
 			    /* Discard the state of the FPU */
diff -u --recursive --new-file v2.1.76/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v2.1.76/linux/arch/i386/kernel/signal.c	Sun Dec 21 22:36:12 1997
+++ linux/arch/i386/kernel/signal.c	Tue Dec 30 07:58:00 1997
@@ -416,7 +416,7 @@
 	{
 		unsigned long seg = __USER_DS;
 		__asm__("mov %w0,%%fs ; mov %w0,%%gs": "=r"(seg) : "0"(seg));
-		set_fs(MAKE_MM_SEG(seg));
+		set_fs(USER_DS);
 		regs->xds = seg;
 		regs->xes = seg;
 		regs->xss = seg;
@@ -488,7 +488,7 @@
 	{
 		unsigned long seg = __USER_DS;
 		__asm__("mov %w0,%%fs ; mov %w0,%%gs": "=r"(seg) : "0"(seg));
-		set_fs(MAKE_MM_SEG(seg));
+		set_fs(USER_DS);
 		regs->xds = seg;
 		regs->xes = seg;
 		regs->xss = seg;
diff -u --recursive --new-file v2.1.76/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.1.76/linux/arch/i386/kernel/smp.c	Sun Dec 21 22:36:12 1997
+++ linux/arch/i386/kernel/smp.c	Mon Dec 29 10:22:46 1997
@@ -25,6 +25,7 @@
  *		Alan Cox	:	Dumb bug: 'B' step PPro's are fine
  *		Ingo Molnar	:	Added APIC timers, based on code
  *					from Jose Renau
+ *		Alan Cox	:	Added EBDA scanning
  */
 
 #include <linux/kernel.h>
@@ -924,7 +925,7 @@
 	if (!max_cpus)
 	{
 		smp_found_config = 0;
-		printk("SMP mode deactivated, forcing use of dummy APIC emulation.\n");
+		printk(KERN_INFO "SMP mode deactivated, forcing use of dummy APIC emulation.\n");
 	}
 
 	/*
@@ -1063,7 +1064,7 @@
 			if(cpu_present_map&(1<<i))
 				bogosum+=cpu_data[i].loops_per_sec;
 		}
-		printk("Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
+		printk(KERN_INFO "Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
 			cpucount+1,
 			(bogosum+2500)/500000,
 			((bogosum+2500)/5000)%100);
diff -u --recursive --new-file v2.1.76/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v2.1.76/linux/arch/i386/mm/init.c	Sun Dec 21 22:36:12 1997
+++ linux/arch/i386/mm/init.c	Mon Dec 29 10:22:46 1997
@@ -178,14 +178,21 @@
 		 *	the error...
 		 */
 		if (!smp_scan_config(639*0x400,0x400))	/* Scan the top 1K of base RAM */
-			smp_scan_config(0xF0000,0x10000);	/* Scan the 64K of bios */
+		{
+			if(!smp_scan_config(0xF0000,0x10000))	/* Scan the 64K of bios */
+			{
+				/*
+				 *	If it is an SMP machine we should know now, unless the configuration
+				 *	is in an EISA/MCA bus machine with an extended bios data area. 
+				 */
+		 
+				address = *(unsigned short *)phys_to_virt(0x40E); /* EBDA */
+				address<<=4;	/* Real mode segments to physical */
+				smp_scan_config(address, 0x1000);	/* Scan the EBDA */
+			}
+		}
 	}
-	/*
-	 *	If it is an SMP machine we should know now, unless the configuration
-	 *	is in an EISA/MCA bus machine with an extended bios data area. I don't
-	 *	have such a machine so someone else can fill in the check of the EBDA
-	 *	here.
-	 */
+			
 /*	smp_alloc_memory(8192); */
 #endif
 	start_mem = PAGE_ALIGN(start_mem);
diff -u --recursive --new-file v2.1.76/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v2.1.76/linux/drivers/block/Config.in	Fri Dec 19 15:52:56 1997
+++ linux/drivers/block/Config.in	Wed Dec 31 16:42:56 1997
@@ -27,9 +27,11 @@
       if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
         bool '     Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA
         if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-          bool '     Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
           bool '     OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621
-          bool '     NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415
+          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
+          fi
         fi
       fi
     fi
@@ -68,7 +70,16 @@
   bool '   Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD
 fi
 tristate 'XT harddisk support' CONFIG_BLK_DEV_XD
-tristate 'SyQuest EZ parallel port disk support' CONFIG_BLK_DEV_EZ
+
+if [ "$CONFIG_PARPORT" = "y" -o "$CONFIG_PARPORT" = "n" ] ; then
+   define_bool CONFIG_PARIDE_PARPORT y
+else
+   define_bool CONFIG_PARIDE_PARPORT m
+fi
+dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARIDE_PARPORT
+if [ "$CONFIG_PARIDE" != "n" ]; then
+  source drivers/block/paride/Config.in
+fi
 
 if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then
   define_bool CONFIG_BLK_DEV_HD y
diff -u --recursive --new-file v2.1.76/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v2.1.76/linux/drivers/block/Makefile	Fri Dec 19 15:52:56 1997
+++ linux/drivers/block/Makefile	Sun Dec 28 12:05:44 1997
@@ -14,6 +14,10 @@
 # In the future, some of these should be built conditionally.
 #
 
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS) paride
+
 
 L_TARGET := block.a
 L_OBJS   := ll_rw_blk.o genhd.o
@@ -191,14 +195,6 @@
   endif
 endif
 
-ifeq ($(CONFIG_BLK_DEV_EZ),y)
-L_OBJS += ez.o
-else
-  ifeq ($(CONFIG_BLK_DEV_EZ),m)
-  M_OBJS += ez.o
-  endif
-endif
-
 ifeq ($(CONFIG_BLK_DEV_MD),y)
 LX_OBJS += md.o
 
@@ -241,6 +237,15 @@
 else
   ifeq ($(CONFIG_BLK_DEV_NBD),m)
   M_OBJS += nbd.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE),y)
+SUB_DIRS	+= paride
+MOD_SUB_DIRS	+= paride
+else
+  ifeq ($(CONFIG_PARIDE),m)
+  MOD_SUB_DIRS	+= paride
   endif
 endif
 
diff -u --recursive --new-file v2.1.76/linux/drivers/block/ez.c linux/drivers/block/ez.c
--- v2.1.76/linux/drivers/block/ez.c	Thu May 29 21:53:05 1997
+++ linux/drivers/block/ez.c	Wed Dec 31 16:00:00 1969
@@ -1,1022 +0,0 @@
-/* 
-	ez.c	(c) 1996  Grant R. Guenther <grant@torque.net>
-		          Under the terms of the GNU public license.
-
-	This is a driver for the parallel port versions of SyQuest's 
-        EZ135 and EZ230 removable media disk drives.  
-        
-        Special thanks go to Pedro Soria-Rodriguez for his help testing 
-	the EZFlyer 230 support.
-
-	The drive is actually SyQuest's IDE product with a
-        ShuttleTech IDE <-> parallel converter chip built in.
-
-	To compile the driver, ensure that /usr/include/linux and
-        /usr/include/asm are links to the correct include files for 
-        the target system. Then compile the driver with 
-
-		cc -D__KERNEL__ -DMODULE -O2 -c ez.c
-
-        If you are using MODVERSIONS, add the following to the cc command:
-
-		-DMODVERSIONS -I /usr/include/linux/modversions.h
-
-	You must then load it with insmod.
-
-	Before attempting to access the new driver, you will need to
-        create some device special files.  The following commands will
-	do that for you:
-
-		mknod /dev/eza  b 40 0
-		mknod /dev/eza1 b 40 1
-		mknod /dev/eza2 b 40 2
-		mknod /dev/eza3 b 40 3
-		mknod /dev/eza4 b 40 4
-		chown root:disk /dev/ez*
-		chmod 660 /dev/ez*
-
-	You can make devices for more partitions (up to 15) if you need to.
-
-	You can alter the port used by the driver in two ways:  either
-        change the definition of EZ_BASE or modify the ez_base variable
-        on the insmod command line, for example:
-
-		insmod ez ez_base=0x3bc
-
-	The driver can detect if the parallel port supports 8-bit
-        transfers.  If so, it will use them.  You can force it to use
-        4-bit (nybble) mode by setting the variable ez_nybble to 1.
-
-	The driver can be used with or without interrupts.  If an IRQ
-        is specified in the variable ez_irq, the driver will use it.
-        If ez_irq is set to 0, an alternative, polling-based, strategy 
-	will be used.
-
-	If you experience timeout errors while using this driver - and
-        you have enabled interrupts - try disabling the interrupt.  I
-        have heard reports of some parallel ports having exceptionally
-        unreliable interrupts.  This could happen on misconfigured 
-        systems in which an inactive sound card shares the same IRQ with 
-        the parallel port. (Remember that most people do not use the
-        parallel port interrupt for printing.)
-
-	It would be advantageous to use multiple mode transfers,
-        but ShuttleTech's driver does not appear to use them, so I'm not
-        sure that the converter can handle it.
-
-	It is not currently possible to connect a printer to the chained
-        port on the EZ135p and expect Linux to use both devices at once.
-
-	When the EZ230 powers on, the "standby timer" is set to about 6
-        minutes:  if the drive is idle for that length of time, it will
-        put itself into a low power standby mode.  It takes a couple of
-        seconds for the drive to come out of standby mode.  So, if you
-        load this driver while it is in standby mode, you will notice
-        a "freeze" of a second or two as the driver waits for the EZ230
-        to come back to life.  Once loaded, this driver disables the
-        standby timer (until you next power up the EZ230 ...)
-
-	Keep an eye on http://www.torque.net/ez135.html for news and
-        other information about the driver.  If you have any problems
-        with this driver, please send me, grant@torque.net, some mail 
-        directly before posting into the newsgroups or mailing lists.
-
-*/
-
-#define	EZ_VERSION	"0.11"
-
-#define	EZ_BASE		0x378
-#define EZ_IRQ		7
-#define EZ_REP		4
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/tqueue.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/genhd.h>
-#include <linux/hdreg.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-
-#define EZ_BITS	   4			/* compatible with SCSI version */
-#define EZ_MAJOR   40			/* as assigned by hpa */
-
-#define MAJOR_NR EZ_MAJOR
-
-/* set up defines for blk.h,  why don't all drivers do it this way ? */
-
-#define DEVICE_NAME "ez"
-#define DEVICE_REQUEST do_ez_request
-#define DEVICE_NR(device) (MINOR(device)>>EZ_BITS)
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#include <linux/blk.h>
-
-#define EZ_PARTNS  (1<<EZ_BITS)
-
-#define EZ_LOG_HEADS	64		
-#define EZ_LOG_SECTS	32		/* SCSI compatible logical geometry */
-
-#define EZ_SIGOFF	54
-#define EZ_SIG		"ySuQse tZE"
-#define EZ_SIGLEN	10
-#define EZ_ID_LEN	14
-
-#define EZ_TMO  	250		/* interrupt timeout in jiffies */
-
-#define EZ_SPIN_DEL     50		/* spin delay in micro-seconds  */
-
-#define EZ_SPIN		(10000/EZ_SPIN_DEL)*EZ_TMO  
-#define EZ_ISPIN	(10000/EZ_SPIN_DEL)*20
-#define EZ_DELAY        udelay(EZ_SPIN_DEL)
-
-#define STAT_ERR	0x00001
-#define STAT_INDEX	0x00002
-#define STAT_ECC	0x00004
-#define STAT_DRQ	0x00008
-#define STAT_SEEK	0x00010
-#define STAT_WRERR	0x00020
-#define STAT_READY	0x00040
-#define STAT_BUSY	0x00080
-
-#define ERR_AMNF	0x00100
-#define ERR_TK0NF	0x00200
-#define ERR_ABRT	0x00400
-#define ERR_MCR		0x00800
-#define ERR_IDNF	0x01000
-#define ERR_MC		0x02000
-#define ERR_UNC		0x04000
-#define ERR_TMO		0x10000
-
-#define IDE_READ	0x20
-#define IDE_WRITE	0x30
-#define IDE_STANDBY     0x96
-#define IDE_DOORLOCK	0xde
-#define IDE_DOORUNLOCK  0xdf
-#define IDE_ACKCHANGE   0xdb
-#define IDE_IDENTIFY    0xec
-
-int ez_init(void);
-void ez_setup(char * str, int * ints);
-#ifdef MODULE
-void cleanup_module( void );
-#endif
-static void ez_geninit(struct gendisk *ignored);
-static int ez_open(struct inode *inode, struct file *file);
-static void do_ez_request(void);
-static int ez_ioctl(struct inode *inode,struct file *file,
-                    unsigned int cmd, unsigned long arg);
-static int ez_release (struct inode *inode, struct file *file);
-static int ez_revalidate(kdev_t dev);
-static int ez_check_media(kdev_t dev);
-static void ez_get_capacity( void );
-static int ez_detect(void);
-static void do_ez_read(void);
-static void do_ez_write(void);
-static void ez_media_check(void);
-static void ez_doorlock(int func);
-static void ez_interrupt( int irq, void * dev_id, struct pt_regs * regs);
-static void ez_pseudo( void *data);
-static void ez_timer_int( unsigned long data);
-static void do_ez_read_drq( void );
-static void do_ez_write_done( void );
-
-static struct hd_struct ez[EZ_PARTNS];
-static int ez_sizes[EZ_PARTNS];
-static int ez_blocksizes[EZ_PARTNS];
-
-static int	ez_base = EZ_BASE;
-static int      ez_irq = EZ_IRQ;
-static int	ez_rep = EZ_REP;
-static int	ez_nybble = 0;		/* force 4-bit mode ? */
-
-static int ez_valid = 0;		/* OK to open */
-static int ez_access = 0;		/* count of active opens ... */
-static int ez_changed = 0;		/* Did we see new media on open ? */
-static int ez_capacity = 512*16*32;     /* Size of this volume in sectors */
-static int ez_heads = 16;		/* physical geometry */
-static int ez_sectors = 32;
-static int ez_mode = 1;			/* 4- or 8-bit mode */
-static int ez_loops = 0;		/* counter for pseudo-interrupts */
-static int ez_timeout = 0;		/* did the interrupt time out ? */
-static int ez_int_seen = 0;		/* have we ever seen an interrupt ? */
-static int ez_busy = 0;			/* request being processed ? */
-static int ez_block;			/* address of next requested block */
-static int ez_count;			/* number of blocks still to do */
-static char * ez_buf;			/* buffer for request in progress */
-static char ez_scratch[512];		/* scratch block buffer */
-static void (*ez_continuation)(void);	/* i/o completion handler */
-
-char	*ez_errs[17] = { "ERR","INDEX","ECC","DRQ","SEEK","WRERR",
-			 "READY","BUSY","AMNF","TK0NF","ABRT","MCR",
-			 "IDNF","MC","UNC","???","TMO"};
-
-static struct tq_struct ez_tq = {0,0,ez_pseudo,NULL};
-static struct timer_list ez_timer = {0,0,0,0,ez_timer_int};
-static struct wait_queue *ez_wait_open = NULL;
-
-/* kernel glue structures */
-
-static struct gendisk ez_gendisk = {
-	MAJOR_NR,	/* Major number */
-	"ez",		/* Major name */
-	EZ_BITS,	/* Bits to shift to get real from partition */
-	EZ_PARTNS,      /* Number of partitions per real */
-	1,		/* maximum number of real */
-	ez_geninit,	/* init function */
-	ez,		/* hd struct */
-	ez_sizes,	/* block sizes */
-	0,		/* number */
-        NULL,		/* internal */
-	NULL		/* next */
-};
-
-static struct file_operations ez_fops = {
-	NULL,			/* lseek - default */
-	block_read,		/* read - general block-dev read */
-	block_write,		/* write - general block-dev write */
-	NULL,			/* readdir - bad */
-	NULL,			/* select */
-	ez_ioctl,		/* ioctl */
-	NULL,			/* mmap */
-	ez_open,		/* open */
-	ez_release,		/* release */
-	block_fsync,		/* fsync */
-	NULL,			/* fasync */
-	ez_check_media,         /* media change ? */
-	ez_revalidate		/* revalidate new media */
-};
-
-__initfunc(int ez_init (void))	/* preliminary initialisation */
-
-{	
-	if (register_blkdev(MAJOR_NR,"ez",&ez_fops)) {
-		printk("ez_init: unable to get major number %d\n",MAJOR_NR);
-		return -1;
-	}
-	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-	read_ahead[MAJOR_NR] = 8;	/* 8 sector (4kB) read ahead */
-	ez_gendisk.next = gendisk_head;
-	gendisk_head = &ez_gendisk;
-
-	return 0;
-}
-
-__initfunc(static void ez_geninit (struct gendisk *ignored))    /* real init */
-
-{	int i;
-
-	ez_gendisk.nr_real = 0;
-	
-	if (ez_detect()) {
-		ez_busy = 0;
-		ez_valid = 1;
-		ez_gendisk.nr_real = 1;
-		ez[0].nr_sects = ez_capacity;
-		for(i=0;i<EZ_PARTNS;i++) ez_blocksizes[i] = 1024;
-		blksize_size[MAJOR_NR] = ez_blocksizes;
-	} 
-#ifdef MODULE
-	  else cleanup_module();
-#endif
-}
-
-static int ez_open (struct inode *inode, struct file *file)
-
-{	int dev = DEVICE_NR(inode->i_rdev);
-
-	if (dev >= ez_gendisk.nr_real) return -ENODEV;
-
-	MOD_INC_USE_COUNT;
-
-	while (!ez_valid) sleep_on(&ez_wait_open);
-	ez_access++;
-	ez_media_check();
-	ez_doorlock(IDE_DOORLOCK);
-	return 0;
-}
-
-static void do_ez_request (void)
-
-{       int	dev;
-
-	if (ez_busy) return;
-repeat:
-	if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
-	INIT_REQUEST;
-
-	dev = MINOR(CURRENT->rq_dev);
-	ez_block = CURRENT->sector;
-	ez_count = CURRENT->nr_sectors;
-
-	if ((dev >= EZ_PARTNS) || ((ez_block+ez_count) > ez[dev].nr_sects)) {
-		end_request(0);
-		goto repeat;
-	}
-
-	ez_block += ez[dev].start_sect;
-	ez_buf = CURRENT->buffer;
-
-	if (CURRENT->cmd == READ) do_ez_read();
-	else if (CURRENT->cmd == WRITE) do_ez_write();
-	else {  end_request(0);
-		goto repeat;
-	}
-}
-
-static int ez_ioctl(struct inode *inode,struct file *file,
-		    unsigned int cmd, unsigned long arg)
-
-{	struct hd_geometry *geo = (struct hd_geometry *) arg;
-	int dev, err;
-
-	if ((!inode) || (!inode->i_rdev)) return -EINVAL;
-	dev = MINOR(inode->i_rdev);
-	if (dev >= EZ_PARTNS) return -EINVAL;
-
-	switch (cmd) {
-	    case HDIO_GETGEO:
-		if (!geo) return -EINVAL;
-		err = verify_area(VERIFY_WRITE,geo,sizeof(*geo));
-		if (err) return err;
-		put_user(ez_capacity/(EZ_LOG_HEADS*EZ_LOG_SECTS),
-			 (short *) &geo->cylinders);
-		put_user(EZ_LOG_HEADS, (char *) &geo->heads);
-		put_user(EZ_LOG_SECTS, (char *) &geo->sectors);
-	        put_user(ez[dev].start_sect,(long *)&geo->start);
-		return 0;
-	    case BLKRASET:
-		if(!suser()) return -EACCES;
-		if(!(inode->i_rdev)) return -EINVAL;
-		if(arg > 0xff) return -EINVAL;
-		read_ahead[MAJOR(inode->i_rdev)] = arg;
-		return 0;
-            case BLKRAGET:
-		if (!arg) return -EINVAL;
-		err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
-		if (err) return (err);
-		put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
-		return (0);
-	    case BLKGETSIZE:
-		if (!arg) return -EINVAL;
-		err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
-		if (err) return (err);
-		put_user(ez[dev].nr_sects,(long *) arg);
-		return (0);
-	    case BLKFLSBUF:
-		if(!suser())  return -EACCES;
-		if(!(inode->i_rdev)) return -EINVAL;
-		fsync_dev(inode->i_rdev);
-	        invalidate_buffers(inode->i_rdev);
-		return 0;
-	    case BLKRRPART:
-		return ez_revalidate(inode->i_rdev);
-	    RO_IOCTLS(inode->i_rdev,arg);
-	    default:
-	        return -EINVAL;
-	}
-}
-
-static int ez_release (struct inode *inode, struct file *file)
-
-{	kdev_t devp;
-
-	devp = inode->i_rdev;
-	if (DEVICE_NR(devp) == 0)  {
-		fsync_dev(devp);
-		invalidate_inodes(devp);
-		invalidate_buffers(devp);
-		ez_access--;
-		if (!ez_access) ez_doorlock(IDE_DOORUNLOCK);
-		MOD_DEC_USE_COUNT;
-	}
-	return 0;
-}
-
-static int ez_check_media( kdev_t dev)
-
-{       int	t;
-
-	t = ez_changed;
-	ez_changed = 0;
-	return t;
-}
-
-static int ez_revalidate(kdev_t dev)
-
-{	int p;
-	long flags;
-	kdev_t devp;
-
-	save_flags(flags);
-	cli(); 
-	if (ez_access > 1) {
-		restore_flags(flags);
-		return -EBUSY;
-	}
-	ez_valid = 0;
-	restore_flags(flags);	
-
-	for (p=(EZ_PARTNS-1);p>=0;p--) {
-		devp = MKDEV(MAJOR_NR, p);
-		fsync_dev(devp);
-		invalidate_inodes(devp);
-		invalidate_buffers(devp);
-		ez[p].start_sect = 0;
-		ez[p].nr_sects = 0;
-	}
-
-	ez_get_capacity();
-	ez[0].nr_sects = ez_capacity;
-	resetup_one_dev(&ez_gendisk,0);
-
-	ez_valid = 1;
-	wake_up(&ez_wait_open);
-
-	return 0;
-}
-
-#ifdef MODULE
-
-/* Glue for modules ... */
-
-void	cleanup_module(void);
-
-int	init_module(void)
-
-{	int	err;
-	long    flags;
-
-	save_flags(flags);
-	cli();
-
-	err = ez_init();
-	if (err) {
-	    restore_flags(flags);
-	    return err;
-	}
-	ez_geninit(&ez_gendisk);
-
-	if (!ez_gendisk.nr_real) {
-		restore_flags(flags);
-		return -1;
-	}
-
-	ez_valid = 0;
-	resetup_one_dev(&ez_gendisk,0);
-	ez_valid = 1;
-
-	restore_flags(flags);
-	return 0;
-}
-
-void	cleanup_module(void)
-
-{	struct gendisk **gdp;
-	long flags;
-
-	save_flags(flags);
-	cli();
-
-	unregister_blkdev(MAJOR_NR,"ez");
-
-	for(gdp=&gendisk_head;*gdp;gdp=&((*gdp)->next))
-		if (*gdp == &ez_gendisk) break;
-	if (*gdp) *gdp = (*gdp)->next;
-
-	if (ez_gendisk.nr_real) {
-		release_region(ez_base,3);
-		if (ez_irq) free_irq(ez_irq,NULL);
-	}
-
-	restore_flags(flags);
-}
-
-#else 
-
-/* ez_setup:  process lilo command parameters ...
-
-   syntax:	ez=base[,irq[,rep[,nybble]]]
-*/
-
-__initfunc(void ez_setup(char *str, int *ints))
-
-{       if (ints[0] > 0) ez_base = ints[1];
-        if (ints[0] > 1) ez_irq = ints[2];
-        if (ints[0] > 2) ez_rep = ints[3];
-        if (ints[0] > 3) ez_nybble = ints[4];
-} 
-
-#endif
-
-/* Now the actual hardware interface to the EZ135p */
-
-static void    out_p( short port, char byte)
-
-{       int i;
-
-	for(i=0;i<ez_rep;i++) outb(byte,ez_base+port);
-}
-
-static int     in_p( short port)
-
-{       int i;
-	char c;
-
-	c=inb(ez_base+port);
-	for(i=1;i<ez_rep;i++) c=inb(ez_base+port);
-	return c & 0xff;
-}
-
-#define w0(byte)  out_p(0,byte)
-#define w2(byte)  out_p(2,byte)
-#define r0()      (in_p(0) & 0xff)
-#define r1()      (in_p(1) & 0xff)
-
-/*  register access functions */
-
-static int read_regr( char regr )
-
-{	int h, l;
-
-	if (ez_mode == 1) {	/* nybble mode */
-	    w0(regr);
-	    w2(1); w2(3);
-	    l = r1() >> 4;
-	    w2(4);
-	    h = r1() & 0xf0;
-	    return h + l;
-	} else {		/* byte mode */
-	    w0(regr+0x20);
-	    w2(1); w2(0x25);
-	    h = r0();
-	    w2(4);
-	    return h;
-	}
-}	
-
-static void write_regr( char regr, char val )
-
-{	w0(regr);
-	w2(1);
-	w0(val);
-	w2(4);
-}
-
-/* connect / disconnect code */
-
-static void prefix( char byte )
-
-{	w2(4); w0(0x22); w0(0xaa); w0(0x55); w0(0); 
-	w0(0xff); w0(0x87); w0(0x78); w0(byte);
-        w2(5); w2(4); w0(0xff); 
-}
-
-static void connect ( void  )
-
-{	prefix(0x40); prefix(0x50); prefix(0xe0);
-        w0(0); w2(1); w2(4);
-	read_regr(0xd);
-	write_regr(0x6d,0xe8);
-	write_regr(0x6c,0x1c);
-	write_regr(0x72,0x10);
-	write_regr(0x6a,0x38);
-	write_regr(0x68,0x10);
-	read_regr(0x12);
-	write_regr(0x72,0x10);
-	read_regr(0xd);
-	write_regr(0x6d,0xaa);
-	write_regr(0x6d,0xaa);
-}
-
-static void disconnect ( void )
-
-{	read_regr(0xd);
-	write_regr(0x6d,0xa8);
-	prefix(0x30);
-} 
-
-/* basic i/o */
-
-static void read_block( char * buf )
-
-/* the nybble mode read has a curious optimisation in it: there are actually
-   five bits available on each read.  The extra bit is used to signal that
-   the next nybble is identical ...  I wonder how much research went into
-   designing this use of the extra bit ?
-*/
-
-{	int	j, k, n0, n1, n2, n3;
-
-	read_regr(0xd); write_regr(0x6d,0xe9);
-
-	j = 0;
-	if (ez_mode == 1) {		/* nybble mode */
-
-	    w0(7); w2(1); w2(3); w0(0xff);
-	    for(k=0;k<256;k++) {
-		w2(6); n0 = r1();
-		if (n0 & 8) n1 = n0; else { w2(4); n1 = r1(); }
-		w2(7); n2 = r1();
-		if (n2 & 8) n3 = n2; else { w2(5); n3 = r1(); }
-		buf[j++] = (n0 >> 4) + (n1 & 0xf0);
-		buf[j++] = (n2 >> 4) + (n3 & 0xf0);
-	    }
-
-	} else {			/* byte mode */
-
-	    w0(0x27); w2(1); w2(0x25); w0(0);
-	    for(k=0;k<256;k++) {
-		w2(0x24); buf[j++] = r0();
-       		w2(0x25); buf[j++] = r0();
-	    }
-	    w2(0x26); w2(0x27); w0(0); w2(0x25); w2(4);
-
-	}
-}
-
-static void write_block( char * buf )
-
-{	int	j;
-
-	read_regr(0xd); write_regr(0x6d,0xe9);
-
-	w0(0x67); w2(1); w2(5);
-	for(j=0;j<256;j++) {
-		w0(buf[2*j]); w2(4);
-		w0(buf[2*j+1]); w2(5);
-	}
-	w2(7); w2(4);
-}
-
-/*  ide command interface */
-
-void	ez_print_error( char * msg, int status )
-
-{	char	*e, *p;
-        int	i;
-
-	e = ez_scratch;
-	for(i=0;i<18;i++) if (status & (1<<i)) {
-		p = ez_errs[i];
-		while ((*e++=*p++));
-		*(e-1) = ' ';
-	}
-	if (status) e--;
-	*e = 0;
-	printk("ez: %s: status = 0x%x (%s)\n",msg,status,ez_scratch);
-}
-
-static int wait_for( int w, char * msg )    /* polled wait */
-
-{	int	k, r, e;
-
-	k=0;
-	while(k < EZ_SPIN) { 
-	    r = read_regr(0x1f);
-            k++;
-	    if (ez_timeout) break;
-	    if (((r & w) == w) && !(r & STAT_BUSY)) break;
-	    EZ_DELAY;
-	}
-	e = (read_regr(0x19)<<8) + r;
-	if ((k >= EZ_SPIN) || ez_timeout) e |= (ERR_TMO|STAT_ERR);
-	if ((e & STAT_ERR) & (msg != NULL)) ez_print_error(msg,e);
-	return e;
-}
-
-static void send_command( int n, int s, int h, int c0, int c1, int func )
-
-{
-	read_regr(0xd); write_regr(0x6d,0xa9);
-
-	write_regr(0x76,0);		
-	write_regr(0x79,0);	/* the IDE task file */
-	write_regr(0x7a,n);
-	write_regr(0x7b,s);
-	write_regr(0x7c,c0);
-	write_regr(0x7d,c1);
-	write_regr(0x7e,0xa0+h);
-	write_regr(0x7f,func);
-
-	udelay(1);
-}
-
-static void ez_ide_command( int func, int block )
-
-{	int c1, c0, h, s;
-
-	s  = ( block % ez_sectors) + 1;
-	h  = ( block / ez_sectors) % ez_heads;
-	c0 = ( block / (ez_sectors*ez_heads)) % 256;
-	c1 = ( block / (ez_sectors*ez_heads*256));
-
-	send_command(1,s,h,c0,c1,func);
-}
-
-static void ez_gate_intr( int flag )
-
-{	if (flag) write_regr(0x6d,0x39);  /* gate interrupt line to bus */
-     	if (flag && ez_irq) w2(0x14);	  /* enable IRQ */
-	if (!flag) w2(4);	          /* disable IRQ */
-}
-
-static int check_int( void )	/* is the interrupt bit set  ?  */
-
-{	return (r1() & 0x40);
-}
-
-static void ez_doorlock( int func )
-
-{	connect();
-	if (wait_for(STAT_READY,"Lock") & STAT_ERR) {
-		disconnect();
-		return;
-	}
-	ez_ide_command(func,0);
-	wait_for(STAT_READY,"Lock done");
-	disconnect();
-}
-
-/* ez_media_check: check for and acknowledge the MC flag */
-
-__initfunc(static void ez_media_check( void ))
-
-{	int r;
-
-	ez_changed = 0;
-	connect();
-	r = wait_for(STAT_READY,"Media check ready");
-	if (!(r & STAT_ERR)) {
-		ez_ide_command(IDE_READ,0);  /* try to read block 0 */
-		r = wait_for(STAT_DRQ,"Media check");
-		if (!(r & STAT_ERR)) read_block(ez_scratch);
-	} else ez_changed = 1;   /* say changed if other error */
-	if (r & ERR_MC) {
-		ez_changed = 1;
-		ez_ide_command(IDE_ACKCHANGE,0);
-		wait_for(STAT_READY,"Ack. media change");
-	}
-	disconnect();
-}
-
-__initfunc(static int ez_identify( void ))
-
-
-{	int	k, r;
-
-	connect();
-	wait_for(0,NULL);  /* wait until not busy, quietly */
-	ez_ide_command(IDE_IDENTIFY,0);
-
-	if (ez_irq) {	  		/* check that the interrupt works */
-		ez_gate_intr(1);
-		k = 0;
-		while ((k++ < EZ_ISPIN) && !ez_int_seen) EZ_DELAY;
-		ez_gate_intr(0);
-		r = read_regr(0x1f);
-		if ((!ez_int_seen) || !(r & STAT_DRQ)) {
-			free_irq(ez_irq,NULL);
-			ez_irq = 0;
-		}
-	}
-
-        if (wait_for(STAT_DRQ,NULL) & STAT_ERR) {
-		disconnect();
-		return 0;
-	}
-	read_block(ez_scratch);
-	disconnect();
-	return 1;
-}
-
-#define  word_val(n) 	(ez_scratch[2*n]+256*ez_scratch[2*n+1])
-
-__initfunc(static void ez_get_capacity( void ))
-
-{	int	ez_cylinders;
-
-	connect();
-	wait_for(0,NULL);
-	ez_ide_command(IDE_IDENTIFY,0);
-	if (wait_for(STAT_DRQ,"Get capacity") & STAT_ERR) {
-		disconnect();
-		return;
-	}
-	read_block(ez_scratch);
-	disconnect();
-	ez_sectors = word_val(6);
-	ez_heads = word_val(3);
-	ez_cylinders  = word_val(1);
-	ez_capacity = ez_sectors*ez_heads*ez_cylinders;
-	printk("ez: Capacity = %d, (%d/%d/%d)\n",ez_capacity,ez_cylinders,
-		ez_heads,ez_sectors);
-}
-
-__initfunc(static void ez_standby_off( void ))
-
-{	connect();
-	wait_for(0,NULL);
-	send_command(0,0,0,0,0,IDE_STANDBY);
-	wait_for(0,NULL);
-	disconnect();
-}
-
-__initfunc(static int ez_port_check( void )) 	/* check for 8-bit port */
-
-{	int	r;
-
-        w2(0); 
-	w0(0x55); if (r0() != 0x55) return 0;
-	w0(0xaa); if (r0() != 0xaa) return 0;
-	w2(0x20); w0(0x55); r = r0(); w0(0xaa);
-	if (r0() == r) return 2;
-	if (r0() == 0xaa) return 1;
-	return 0;
-}
-
-__initfunc(static int ez_detect( void ))
-
-{	int j, k;
-	char sig[EZ_SIGLEN] = EZ_SIG;
-	char id[EZ_ID_LEN+1];
-	long	flags;
-
-	if (check_region(ez_base,3)) {
-		printk("ez: Ports at 0x%x are not available\n",ez_base);
-		return 0;
-	}
-
-	ez_mode = ez_port_check();
-	if (!ez_mode) {
-		printk("ez: No parallel port at 0x%x\n",ez_base);
-		return 0;
-	}
-
-	if (ez_irq && request_irq(ez_irq,ez_interrupt,0,"ez",NULL)) ez_irq = 0;
-
-	if (ez_nybble) ez_mode = 1;
-	    
-	request_region(ez_base,3,"ez");
-
-	save_flags(flags);
-	sti();
-
-	k = 0;
-	if (ez_identify()) {
-		k = 1;
-		for(j=0;j<EZ_SIGLEN;j++) 
-		   k &= (ez_scratch[j+EZ_SIGOFF] == sig[j]);
-	}
-	if (k) { 
-	    for(j=0;j<EZ_ID_LEN;j++) id[j^1] = ez_scratch[j+EZ_SIGOFF];
-	    id[EZ_ID_LEN] = 0;
-	    if (!ez_irq) printk("ez %s: %s at 0x%x, %d-bit mode.\n",
-				EZ_VERSION,id,ez_base,4*ez_mode);
-	    else printk("ez %s: %s at 0x%x, IRQ %d, %d-bit mode.\n",
-                        EZ_VERSION,id,ez_base,ez_irq,4*ez_mode);
-	    ez_standby_off();
-	    ez_media_check();
-	    ez_get_capacity();
-	    restore_flags(flags);
-	    return 1;
-	}
-	restore_flags(flags);
-	release_region(ez_base,3);
-	if (ez_irq) free_irq(ez_irq,NULL);
-	printk("ez: Drive not detected\n");
-	return 0;
-}
-
-/* interrupt management */
-
-static void ez_set_intr( void (*continuation)(void) )
-
-{	ez_continuation = continuation;
-	ez_loops = 1;  ez_timeout = 0;
-	ez_gate_intr(1);
-	if (ez_irq) {
-		ez_timer.expires = jiffies + EZ_TMO;
-		add_timer(&ez_timer);
-	} else queue_task(&ez_tq,&tq_scheduler); 	
-}
-
-static void ez_pseudo( void *data )
-
-{	void (*con)(void);
-
-	ez_timeout = (ez_loops >= EZ_TMO);
-	if (check_int() || ez_timeout) {
-		con = ez_continuation;
-		ez_continuation = NULL;
-		if (con) con();
-	} else {	
-		ez_loops++;
-	        queue_task(&ez_tq,&tq_scheduler);
-	}
-}
-
-static void ez_timer_int( unsigned long data)
-
-{	void  (*con)(void);
-
-	con = ez_continuation;
-	if (!con) return;
-	ez_continuation = NULL;
-	ez_gate_intr(0);
-	ez_timeout = 1;
-	con();
-}
-
-static void ez_interrupt( int irq, void * dev_id, struct pt_regs * regs)
-
-{	void  (*con)(void);
-
-	ez_int_seen = 1;
-	con = ez_continuation;
-	if (!con) return;
-	ez_gate_intr(0);
-	del_timer(&ez_timer);
-	ez_continuation = NULL;
-	con();
-}
-
-/* The i/o request engine */
-
-#define EZ_DONE(s) { disconnect(); end_request(s); ez_busy = 0;\
-		     cli(); do_ez_request(); return; }
-
-static void do_ez_read( void )
-
-{	ez_busy = 1;
-	if (!ez_count) {
-		ez_busy = 0;
-		return;
-	}
-	sti();
-	connect();
-	if (wait_for(STAT_READY,"do_ez_read") & STAT_ERR) EZ_DONE(0);
-	ez_ide_command(IDE_READ,ez_block);
-	ez_set_intr(do_ez_read_drq);
-}
-
-static void do_ez_read_drq( void )
-
-{	sti();
-	if (wait_for(STAT_DRQ,"do_ez_read_drq") & STAT_ERR) EZ_DONE(0);
-	read_block(ez_buf);
-	ez_count--;
-	if (ez_count) {
-		ez_buf += 512;
-		ez_block++;
-		disconnect();
-		do_ez_read();
-		return;
-	}
-	EZ_DONE(1);
-}
-
-static void do_ez_write( void )
-
-{	ez_busy = 1;
-	if (!ez_count) {
-		ez_busy = 0;
-		return;
-	}
-	sti();
-	connect();
-	if (wait_for(STAT_READY,"do_ez_write") & STAT_ERR) 
-	   EZ_DONE(0);
-	ez_ide_command(IDE_WRITE,ez_block);
-	if (wait_for(STAT_DRQ,"do_ez_write_drq") & STAT_ERR) 
-	   EZ_DONE(0);
-	write_block(ez_buf);
-	ez_set_intr(do_ez_write_done);
-}
-
-static void do_ez_write_done( void )
-
-{	sti();
-	if (wait_for(STAT_READY,"do_ez_write_done") & STAT_ERR) EZ_DONE(0);
-	ez_count--;
-	if (ez_count) {
-		ez_buf += 512;
-		ez_block++;
-		disconnect();
-		do_ez_write();
-		return;
-	}
-	EZ_DONE(1);
-}
-
-/* end of ez.c */
diff -u --recursive --new-file v2.1.76/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v2.1.76/linux/drivers/block/genhd.c	Mon Aug 18 18:19:45 1997
+++ linux/drivers/block/genhd.c	Wed Dec 31 16:47:55 1997
@@ -2,7 +2,7 @@
  *  Code extracted from
  *  linux/kernel/hd.c
  *
- *  Copyright (C) 1991, 1992  Linus Torvalds
+ *  Copyright (C) 1991-1998  Linus Torvalds
  *
  *
  *  Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
@@ -105,6 +105,7 @@
 static inline int is_extended_partition(struct partition *p)
 {
 	return (SYS_IND(p) == DOS_EXTENDED_PARTITION ||
+		SYS_IND(p) == WIN98_EXTENDED_PARTITION ||
 		SYS_IND(p) == LINUX_EXTENDED_PARTITION);
 }
 
diff -u --recursive --new-file v2.1.76/linux/drivers/block/ide-dma.c linux/drivers/block/ide-dma.c
--- v2.1.76/linux/drivers/block/ide-dma.c	Fri Dec 19 15:52:56 1997
+++ linux/drivers/block/ide-dma.c	Wed Dec 31 16:42:56 1997
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide-dma.c	Version 4.07  December 5, 1997
+ *  linux/drivers/block/ide-dma.c	Version 4.08  December 31, 1997
  *
  *  Copyright (c) 1995-1998  Mark Lord
  *  May be copied or modified under the terms of the GNU General Public License
@@ -171,8 +171,8 @@
 		}
 		/*
 		 * Fill in the dma table, without crossing any 64kB boundaries.
-		 * The hardware requires 16-bit alignment of all blocks
-		 * (trm290 requires 32-bit alignment).
+		 * Most hardware requires 16-bit alignment of all blocks,
+		 * but the trm290 requires 32-bit alignment.
 		 */
 		if ((addr & 3)) {
 			printk("%s: misaligned DMA buffer\n", drive->name);
@@ -247,7 +247,7 @@
 int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
 {
 	ide_hwif_t *hwif = HWIF(drive);
-	unsigned int dma_base = hwif->dma_base;
+	unsigned long dma_base = hwif->dma_base;
 	unsigned int count, reading = 0;
 
 	switch (func) {
@@ -288,12 +288,12 @@
 	}
 }
 
-void ide_setup_dma (ide_hwif_t *hwif, unsigned int dma_base, unsigned int num_ports) /* __init */
+void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_ports) /* __init */
 {
 	static unsigned long dmatable = 0;
 	static unsigned leftover = 0;
 
-	printk("    %s: BM-DMA at 0x%04x-0x%04x", hwif->name, dma_base, dma_base + num_ports - 1);
+	printk("    %s: BM-DMA at 0x%04lx-0x%04lx", hwif->name, dma_base, dma_base + num_ports - 1);
 	if (check_region(dma_base, num_ports)) {
 		printk(" -- ERROR, PORT ADDRESSES ALREADY IN USE\n");
 		return;
@@ -356,25 +356,26 @@
 /*
  * Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space:
  */
-unsigned int ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) /* __init */
+unsigned long ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) /* __init */
 {
-	unsigned int new, dma_base = 0;
+	unsigned long new, dma_base = 0;
 	byte bus = hwif->pci_bus, fn = hwif->pci_fn;
 
 	if (hwif->mate && hwif->mate->dma_base) {
 		dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8);
-	} else if (pcibios_read_config_dword(bus, fn, 0x20, &dma_base)) {
+	} else if (pcibios_read_config_dword(bus, fn, 0x20, (unsigned int *)&dma_base)) {
 		printk("%s: failed to read dma_base\n", name);
 		dma_base = 0;
 	} else if ((dma_base &= ~0xf) == 0 || dma_base == ~0xf) {
-		printk("%s: dma_base is invalid (0x%04x, BIOS problem)\n", name, dma_base);
+		printk("%s: dma_base is invalid (0x%04lx, BIOS problem)\n", name, dma_base);
 		new = ide_find_free_region(16 + extra);
 		hwif->no_autodma = 1;	/* default DMA off if we had to configure it here */
 		if (new) {
-			printk("%s: setting dma_base to 0x%04x\n", name, new);
+			printk("%s: setting dma_base to 0x%04lx\n", name, new);
 			new |= 1;
 			(void) pcibios_write_config_dword(bus, fn, 0x20, new);
-			(void) pcibios_read_config_dword(bus, fn, 0x20, &dma_base);
+			dma_base = 0;
+			(void) pcibios_read_config_dword(bus, fn, 0x20, (unsigned int *)&dma_base);
 			if (dma_base != new) {
 				if (bus == 0) {
 					printk("%s: operation failed, bypassing BIOS to try again\n", name);
diff -u --recursive --new-file v2.1.76/linux/drivers/block/ide-pci.c linux/drivers/block/ide-pci.c
--- v2.1.76/linux/drivers/block/ide-pci.c	Fri Dec 19 15:52:57 1997
+++ linux/drivers/block/ide-pci.c	Wed Dec 31 16:42:56 1997
@@ -1,13 +1,13 @@
 /*
- *  linux/drivers/block/ide-pci.c	Version 1.00  December 8, 1997
+ *  linux/drivers/block/ide-pci.c	Version 1.02  December 29, 1997
  *
  *  Copyright (c) 1995-1998  Mark Lord
  *  May be copied or modified under the terms of the GNU General Public License
  */
 
 /*
- * This modules provides support for automatic detection and
- * configuration of all PCI IDE interfaces present in a system.  
+ *  This module provides support for automatic detection and
+ *  configuration of all PCI IDE interfaces present in a system.  
  */
 
 #include <linux/config.h>
@@ -41,6 +41,7 @@
 #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 IDE_IGNORE	((void *)-1)
 
@@ -102,6 +103,7 @@
 	{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}} },
 	{IDE_PCI_DEVID_NULL, "PCI_IDE",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}} }};
 
 /*
@@ -111,7 +113,7 @@
  * by the BIOS, to avoid conflicts later in the init cycle,
  * but we don't.	FIXME
  */
-unsigned int ide_find_free_region (unsigned short size) /* __init */
+unsigned long ide_find_free_region (unsigned short size) /* __init */
 {
 	static unsigned short base = 0x5800;	/* it works for me */
 	unsigned short i;
@@ -228,10 +230,11 @@
 __initfunc(static void ide_setup_pci_device (byte bus, byte fn, unsigned int ccode, ide_pci_device_t *d))
 {
 	unsigned int port, at_least_one_hwif_enabled = 0, no_autodma = 0;
-	unsigned short pcicmd = 0;
+	unsigned short pcicmd = 0, tried_config = 0;
 	byte tmp = 0, progif = 0, pciirq = 0;
 	ide_hwif_t *hwif, *mate = NULL;
 
+check_if_enabled:
 	if (pcibios_read_config_word(bus, fn, 0x04, &pcicmd)
 	 || pcibios_read_config_byte(bus, fn, 0x09, &progif)
 	 || pcibios_read_config_byte(bus, fn, 0x3c, &pciirq))
@@ -247,21 +250,32 @@
 		 * Maybe the user deliberately *disabled* the device,
 		 * but we'll eventually ignore it again if no drives respond.
 		 */
-		if (ide_setup_pci_baseregs(bus, fn, d->name)
-		 || pcibios_write_config_word(bus, fn, 0x04, pcicmd|1)
-		 || pcibios_read_config_word(bus, fn, 0x04, &pcicmd)
-		 || !(pcicmd & 1))
+		if (tried_config++
+		 || ide_setup_pci_baseregs(bus, fn, d->name)
+		 || pcibios_write_config_word(bus, fn, 0x04, pcicmd|1))
 		{
 			printk("%s: device disabled (BIOS)\n", d->name);
 			return;
 		}
 		no_autodma = 1;	/* default DMA off if we had to configure it here */
-		printk("%s: device enabled (Linux)\n", d->name);
+		goto check_if_enabled;
 	}
-	if (!pciirq || pciirq >= NR_IRQS) {	/* is pciirq invalid? */
-		if (pciirq || (progif & 0x5))	/* don't complain if using "legacy" mode */
-			printk("%s: BIOS returned %d for IRQ (ignored)\n", d->name, pciirq);
-		pciirq = 0;	/* probe for it instead */
+	if (tried_config)
+		printk("%s: device enabled (Linux)\n", d->name);
+	/*
+	 * Can we trust the reported IRQ?
+	 */
+	if ((ccode >> 16) != PCI_CLASS_STORAGE_IDE || (progif & 5) != 5) {
+		printk("%s: not 100%% native mode: will probe irqs later\n", d->name);
+		pciirq = 0;
+	} else if (tried_config) {
+		printk("%s: will probe irqs later\n", d->name);
+		pciirq = 0;
+	} else if (!pciirq || pciirq >= NR_IRQS) {
+		printk("%s: bad irq from BIOS (%d): will probe later\n", d->name, pciirq);
+		pciirq = 0;
+	} else {
+		printk("%s: 100%% native mode on irq %d\n", d->name, pciirq);
 	}
 	/*
 	 * Set up the IDE ports
@@ -286,7 +300,8 @@
 		hwif->pci_fn = fn;
 		hwif->pci_devid = d->devid;
 		hwif->channel = port;
-		hwif->irq = pciirq;
+		if (!hwif->irq)
+			hwif->irq = pciirq;
 		if (mate) {
 			hwif->mate = mate;
 			mate->mate = hwif;
@@ -296,7 +311,7 @@
 #ifdef CONFIG_BLK_DEV_IDEDMA
 		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) || ((ccode >> 16) == PCI_CLASS_STORAGE_IDE && (ccode & 0x8000))) {
 			unsigned int extra = (!mate && IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246)) ? 16 : 0;
-			unsigned int dma_base = ide_get_or_set_dma_base(hwif, extra, d->name);
+			unsigned long dma_base = ide_get_or_set_dma_base(hwif, extra, d->name);
 			if (dma_base && !(pcicmd & 4)) {
 				/*
  	 			 * Set up BM-DMA capability (PnP BIOS should have done this)
@@ -311,7 +326,7 @@
 			if (dma_base)
 				ide_setup_dma(hwif, dma_base, 8);
 			else
-				printk("%s: %s Bus-Master DMA disabled (BIOS), pcicmd=0x%04x, ccode=0x%04x, dma_base=0x%04x\n",
+				printk("%s: %s Bus-Master DMA disabled (BIOS), pcicmd=0x%04x, ccode=0x%04x, dma_base=0x%04lx\n",
 				 hwif->name, d->name, pcicmd, ccode, dma_base);
 		}
 #endif	/* CONFIG_BLK_DEV_IDEDMA */
@@ -352,10 +367,10 @@
 			continue;	/* OPTI Viper-M uses same devid for functions 0 and 1 */
 		else if (!IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL) || (ccode >> 16) == PCI_CLASS_STORAGE_IDE) {
 			if (IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL))
-				printk("%s: unknown IDE device on PCI bus %d function %d, VID=%04x, DID=%04x\n",
+				printk("%s: unknown IDE controller on PCI bus %d function %d, VID=%04x, DID=%04x\n",
 					d->name, bus, fn, devid.vid, devid.did);
 			else
-				printk("%s: PCI bus %d function %d\n", d->name, bus, fn);
+				printk("%s: IDE controller on PCI bus %d function %d\n", d->name, bus, fn);
 			ide_setup_pci_device(bus, fn, ccode, d);
 		}
 	} while (hedt == 0x80 && (++fn & 7));
@@ -379,4 +394,3 @@
 		}
 	}
 }
-
diff -u --recursive --new-file v2.1.76/linux/drivers/block/ide-proc.c linux/drivers/block/ide-proc.c
--- v2.1.76/linux/drivers/block/ide-proc.c	Sun Dec 21 22:36:12 1997
+++ linux/drivers/block/ide-proc.c	Wed Dec 31 16:45:47 1997
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/proc_ide.c	Version 1.01	December 12, 1997
+ *  linux/drivers/block/ide-proc.c	Version 1.02	December 31, 1997
  *
  *  Copyright (C) 1997-1998	Mark Lord
  */
@@ -13,15 +13,30 @@
  * This should provide better utilities, and less kernel bloat.
  *
  * The entire pci config space for a PCI interface chipset can be
- * retrieved by just reading it.  e.g.    "cat /proc/ide3/pci"
+ * retrieved by just reading it.  e.g.    "cat /proc/ide3/config"
  *
- * To modify registers, do something like:
- *   echo "40:88" >/proc/ide/ide3/pci
+ * To modify registers *safely*, do something like:
+ *   echo "P40:88" >/proc/ide/ide3/config
  * That expression writes 0x88 to pci config register 0x40
  * on the chip which controls ide3.  Multiple tuples can be issued,
  * and the writes will be completed as an atomic set:
- *   echo "40:88 41:35 42:00 43:00" >/proc/ide/ide3/pci
- * All numbers must be pairs of ascii hex digits.
+ *   echo "P40:88 P41:35 P42:00 P43:00" >/proc/ide/ide3/config
+ *
+ * All numbers must be specified using pairs of ascii hex digits.
+ * It is important to note that these writes will be performed
+ * after waiting for the IDE controller (both interfaces)
+ * to be completely idle, to ensure no corruption of I/O in progress.
+ *
+ * Non-PCI registers can also be written, using "R" in place of "P"
+ * in the above examples.  The size of the port transfer is determined
+ * by the number of pairs of hex digits given for the data.  If a two
+ * digit value is given, the write will be a byte operation; if four
+ * digits are used, the write will be performed as a 16-bit operation;
+ * and if eight digits are specified, a 32-bit "dword" write will be
+ * performed.  Odd numbers of digits are not permitted.
+ *
+ * If there is an error *anywhere* in the string of registers/data
+ * then *none* of the writes will be performed.
  *
  * Also useful, "cat /proc/ide0/hda/identify" will issue an IDENTIFY
  * (or PACKET_IDENTIFY) command to /dev/hda, and then dump out the
@@ -44,6 +59,7 @@
 #include <linux/pci.h>
 #include <linux/bios32.h>
 #include <linux/ctype.h>
+#include <asm/io.h>
 #include "ide.h"
 
 #ifndef MIN
@@ -64,13 +80,14 @@
 	return digit;
 }
 
-
-static int xx_xx_parse_error (const char *start, unsigned long maxlen)
+static int xx_xx_parse_error (const char *data, unsigned long len, const char *msg)
 {
-	char errbuf[7];
-	int i, len = MIN(6, maxlen);
+	char errbuf[16];
+	int i;
+	if (len >= sizeof(errbuf))
+		len = sizeof(errbuf) - 1;
 	for (i = 0; i < len; ++i) {
-		char c = start[i];
+		char c = data[i];
 		if (!c || c == '\n')
 			c = '\0';
 		else if (iscntrl(c))
@@ -78,17 +95,17 @@
 		errbuf[i] = c;
 	}
 	errbuf[i] = '\0';
-	printk("proc_ide: error: expected 'xx:xx', but got '%s'\n", errbuf);
+	printk("proc_ide: error: %s: '%s'\n", msg, errbuf);
 	return -EINVAL;
 }
 
-static int proc_ide_write_pci
+static int proc_ide_write_config
 	(struct file *file, const char *buffer, unsigned long count, void *data)
 {
 	ide_hwif_t	*hwif = (ide_hwif_t *)data;
 	int		for_real = 0;
-	unsigned long	n, flags;
-	const char	*start;
+	unsigned long	startn = 0, n, flags;
+	const char	*start = NULL, *msg = NULL;
 
 	if (!suser())
 		return -EACCES;
@@ -105,56 +122,109 @@
 	 */
 	save_flags(flags);
 	do {
-		const char *p = buffer;
-		n = count;
+		const char *p;
 		if (for_real) {
 			unsigned long timeout = jiffies + (3 * HZ);
+			ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup);
+			ide_hwgroup_t *mategroup = NULL;
+			if (hwif->mate && hwif->mate->hwgroup)
+				mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup);
 			cli();	/* ensure all PCI writes are done together */
-			while (((ide_hwgroup_t *)(hwif->hwgroup))->active || (hwif->mate && ((ide_hwgroup_t *)(hwif->mate->hwgroup))->active)) {
-				sti();
+			while (mygroup->active || (mategroup && mategroup->active)) {
+				restore_flags(flags);
 				if (0 < (signed long)(jiffies - timeout)) {
 					printk("/proc/ide/%s/pci: channel(s) busy, cannot write\n", hwif->name);
-					restore_flags(flags);
 					return -EBUSY;
 				}
 				cli();
 			}
 		}
-		while (n) {
-			int d1, d2, rc;
-			byte reg, val;
+		p = buffer;
+		n = count;
+		while (n > 0) {
+			int d, digits;
+			unsigned int reg = 0, val = 0, is_pci;
 			start = p;
-#if 0
-			printk("loop(%d): n=%ld, input=%.5s\n", for_real, n, p);
-#endif
-			if (n < 5)
-				goto parse_error;
-			if (0 > (d1 = ide_getxdigit(*p++)) || 0 > (d2 = ide_getxdigit(*p++)))
+			startn = n--;
+			switch (*p++) {
+				case 'R':	is_pci = 0;
+						break;
+				case 'P':	is_pci = 1;
+						break;
+				default:	msg = "expected 'R' or 'P'";
+						goto parse_error;
+			}
+			digits = 0;
+			while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
+				reg = (reg << 4) | d;
+				--n;
+				++p;
+				++digits;
+			}
+			if (!digits || (digits > 4) || (is_pci && reg > 0xff)) {
+				msg = "bad/missing register number";
 				goto parse_error;
-			reg = (d1 << 4) | d2;
-			if (*p++ != ':')
+			}
+			if (--n < 0 || *p++ != ':') {
+				msg = "missing ':'";
 				goto parse_error;
-			if (0 > (d1 = ide_getxdigit(*p++)) || 0 > (d2 = ide_getxdigit(*p++)))
+			}
+			digits = 0;
+			while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
+				val = (val << 4) | d;
+				--n;
+				++p;
+				++digits;
+			}
+			if (digits != 2 && digits != 4 && digits != 8) {
+				msg = "bad data, 2/4/8 digits required";
 				goto parse_error;
-			val = (d1 << 4) | d2;
-			if (n > 5 && !isspace(*p))
+			}
+			if (n > 0 && !isspace(*p)) {
+				msg = "expected whitespace after data";
 				goto parse_error;
-			n -= 5;
-			while (n && isspace(*p)) {
+			}
+			while (n > 0 && isspace(*p)) {
 				--n;
 				++p;
 			}
+			if (is_pci && (reg & ((digits >> 1) - 1))) {
+				msg = "misaligned access";
+				goto parse_error;
+			}
 			if (for_real) {
 #if 0
-				printk("proc_ide_write_pci: reg=0x%02x, val=0x%02x\n", reg, val);
+				printk("proc_ide_write_config: type=%c, reg=0x%x, val=0x%x, digits=%d\n", is_pci ? 'PCI' : 'non-PCI', reg, val, digits);
 #endif
-				rc = pcibios_write_config_byte(hwif->pci_bus, hwif->pci_fn, reg, val);
-				if (rc) {
-					restore_flags(flags);
-					printk("proc_ide_write_pci: error writing bus %d fn %d reg 0x%02x value 0x%02x\n",
-						hwif->pci_bus, hwif->pci_fn, reg, val);
-					printk("proc_ide_write_pci: %s\n", pcibios_strerror(rc));
-					return -EIO;
+				if (is_pci) {
+					int rc = 0;
+					switch (digits) {
+						case 2:	msg = "byte";
+							rc = pcibios_write_config_byte(hwif->pci_bus, hwif->pci_fn, reg, val);
+							break;
+						case 4:	msg = "word";
+							rc = pcibios_write_config_word(hwif->pci_bus, hwif->pci_fn, reg, val);
+							break;
+						case 8:	msg = "dword";
+							rc = pcibios_write_config_dword(hwif->pci_bus, hwif->pci_fn, reg, val);
+							break;
+					}
+					if (rc) {
+						restore_flags(flags);
+						printk("proc_ide_write_config: error writing %s at bus %d fn %d reg 0x%x value 0x%x\n",
+							msg, hwif->pci_bus, hwif->pci_fn, reg, val);
+						printk("proc_ide_write_config: %s\n", pcibios_strerror(rc));
+						return -EIO;
+					}
+				} else {	/* not pci */
+					switch (digits) {
+						case 2:	outb(val, reg);
+							break;
+						case 4:	outw(val, reg);
+							break;
+						case 8:	outl(val, reg);
+							break;
+					}
 				}
 			}
 		}
@@ -163,25 +233,26 @@
 	return count;
 parse_error:
 	restore_flags(flags);
-	return xx_xx_parse_error(start, n);
+	printk("parse error\n");
+	return xx_xx_parse_error(start, startn, msg);
 }
 
-static int proc_ide_read_pci
+static int proc_ide_read_config
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
 	ide_hwif_t	*hwif = (ide_hwif_t *)data;
 	char		*out = page;
 	int		len, reg = 0;
 
-	out += sprintf(out, "Bus %d Function %d Vendor %04x Device %04x Channel %d\n",
+	out += sprintf(out, "pci bus %d function %d vendor %04x device %04x channel %d\n",
 		hwif->pci_bus, hwif->pci_fn, hwif->pci_devid.vid, hwif->pci_devid.did, hwif->channel);
 	do {
 		byte val;
 		int rc = pcibios_read_config_byte(hwif->pci_bus, hwif->pci_fn, reg, &val);
 		if (rc) {
-			printk("proc_ide_read_pci: error reading bus %d fn %d reg 0x%02x\n",
+			printk("proc_ide_read_config: error reading bus %d fn %d reg 0x%02x\n",
 				hwif->pci_bus, hwif->pci_fn, reg);
-			printk("proc_ide_read_pci: %s\n", pcibios_strerror(rc));
+			printk("proc_ide_read_config: %s\n", pcibios_strerror(rc));
 			return -EIO;
 			out += sprintf(out, "??%c", (++reg & 0xf) ? ' ' : '\n');
 		} else
@@ -455,11 +526,11 @@
 		if (!hwif_ent) return;
 #ifdef CONFIG_PCI
 		if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL)) {
-			ent = create_proc_entry("pci", 0, hwif_ent);
+			ent = create_proc_entry("config", 0, hwif_ent);
 			if (!ent) return;
 			ent->data = hwif;
-			ent->read_proc  = proc_ide_read_pci;
-			ent->write_proc = proc_ide_write_pci;;
+			ent->read_proc  = proc_ide_read_config;
+			ent->write_proc = proc_ide_write_config;;
 
 			ent = create_proc_entry("model", 0, hwif_ent);
 			if (!ent) return;
diff -u --recursive --new-file v2.1.76/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.1.76/linux/drivers/block/ide.h	Sun Dec 21 22:36:13 1997
+++ linux/drivers/block/ide.h	Fri Jan  2 14:01:42 1998
@@ -321,9 +321,9 @@
 	ide_dmaproc_t	*dmaproc;	/* dma read/write/abort routine */
 	unsigned long	*dmatable;	/* dma physical region descriptor table */
 	struct hwif_s	*mate;		/* other hwif from same PCI chip */
-	unsigned int	dma_base;	/* base addr for dma ports */
-	unsigned int	config_data;	/* for use by chipset-specific code */
-	unsigned int	select_data;	/* for use by chipset-specific code */
+	unsigned long	dma_base;	/* base addr for dma ports */
+	unsigned long	config_data;	/* for use by chipset-specific code */
+	unsigned long	select_data;	/* for use by chipset-specific code */
 	struct proc_dir_entry *proc;	/* /proc/ide/ directory entry */
 	int		irq;		/* our irq number */
 	byte		major;		/* our major number */
@@ -365,7 +365,7 @@
  * /proc/ide interface
  */
 typedef struct {
-	char *name;
+	const char *name;
 	read_proc_t *read_proc;
 	write_proc_t *write_proc;
 } ide_proc_entry_t;
@@ -656,15 +656,15 @@
 int ide_unregister_subdriver (ide_drive_t *drive);
 
 #ifdef CONFIG_BLK_DEV_IDEPCI
-unsigned int ide_find_free_region (unsigned short size) __init;
+unsigned long ide_find_free_region (unsigned short size) __init;
 void ide_scan_pcibus (void) __init;
 #endif
 #ifdef CONFIG_BLK_DEV_IDEDMA
 int ide_build_dmatable (ide_drive_t *drive);
 void ide_dma_intr  (ide_drive_t *drive);
 int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive);
-void ide_setup_dma (ide_hwif_t *hwif, unsigned int dmabase, unsigned int num_ports) __init;
-unsigned int ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) __init;
+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;
 #endif
 
 #ifdef CONFIG_BLK_DEV_IDE
diff -u --recursive --new-file v2.1.76/linux/drivers/block/ide_modes.h linux/drivers/block/ide_modes.h
--- v2.1.76/linux/drivers/block/ide_modes.h	Mon Jul  7 16:02:14 1997
+++ linux/drivers/block/ide_modes.h	Fri Jan  2 14:01:42 1998
@@ -15,7 +15,7 @@
  * breaking the fragile cmd640.c support.
  */
 
-#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS)
+#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) || defined(CONFIG_BLK_DEV_OPTI621)
 
 /*
  * Standard (generic) timings for PIO modes, from ATA2 specification.
@@ -222,5 +222,5 @@
 }
 
 #endif /* _IDE_C */
-#endif /* defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) */
+#endif /* defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) || defined(CONFIG_BLK_DEV_OPTI621) */
 #endif /* _IDE_MODES_H */
diff -u --recursive --new-file v2.1.76/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.1.76/linux/drivers/block/ll_rw_blk.c	Sun Dec 21 22:36:13 1997
+++ linux/drivers/block/ll_rw_blk.c	Sun Dec 28 12:05:44 1997
@@ -665,12 +665,6 @@
 		}
 	}
 }
-#ifdef CONFIG_BLK_DEV_EZ
-extern void ez_init( void );
-#endif
-#ifdef CONFIG_BPCD
-extern void bpcd_init( void );
-#endif
 
 __initfunc(int blk_dev_init(void))
 {
@@ -724,8 +718,8 @@
 #ifdef CONFIG_BLK_DEV_XD
 	xd_init();
 #endif
-#ifdef CONFIG_BLK_DEV_EZ
-	ez_init();
+#ifdef CONFIG_PARIDE
+	{ extern void paride_init(void); paride_init(); };
 #endif
 #ifdef CONFIG_MAC_FLOPPY
 	swim3_init();
@@ -761,9 +755,6 @@
 #ifdef CONFIG_GSCD
 	gscd_init();
 #endif CONFIG_GSCD
-#ifdef CONFIG_BPCD
-	bpcd_init();
-#endif CONFIG_BPCD
 #ifdef CONFIG_CM206
 	cm206_init();
 #endif
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/Config.in linux/drivers/block/paride/Config.in
--- v2.1.76/linux/drivers/block/paride/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/Config.in	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,19 @@
+#
+# PARIDE configuration
+#
+comment 'Parallel IDE high-level drivers'
+dep_tristate '  Parallel port IDE disks' CONFIG_PARIDE_PD $CONFIG_PARIDE
+dep_tristate '  Parallel port ATAPI CD-ROMs' CONFIG_PARIDE_PCD $CONFIG_PARIDE
+dep_tristate '  Parallel port ATAPI disks' CONFIG_PARIDE_PF $CONFIG_PARIDE
+comment 'Parallel IDE protocol modules'
+dep_tristate '    ATEN EH-100 protocol' CONFIG_PARIDE_ATEN $CONFIG_PARIDE
+dep_tristate '    MicroSolutions backpack protocol' CONFIG_PARIDE_BPCK $CONFIG_PARIDE
+dep_tristate '    DataStor Commuter protocol' CONFIG_PARIDE_COMM $CONFIG_PARIDE
+dep_tristate '    DataStor EP-2000 protocol' CONFIG_PARIDE_DSTR $CONFIG_PARIDE
+dep_tristate '    Shuttle EPAT/EPEZ protocol' CONFIG_PARIDE_EPAT $CONFIG_PARIDE
+dep_tristate '    Shuttle EPIA protocol' CONFIG_PARIDE_EPIA $CONFIG_PARIDE
+dep_tristate '    FreeCom power protocol' CONFIG_PARIDE_FRPW $CONFIG_PARIDE
+dep_tristate '    KingByte KBIC-951A/971A protocols' CONFIG_PARIDE_KBIC $CONFIG_PARIDE
+dep_tristate '    OnSpec 90c20 protocol' CONFIG_PARIDE_ON20 $CONFIG_PARIDE
+dep_tristate '    OnSpec 90c26 protocol' CONFIG_PARIDE_ON26 $CONFIG_PARIDE
+#
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/Makefile linux/drivers/block/paride/Makefile
--- v2.1.76/linux/drivers/block/paride/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/Makefile	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,133 @@
+#
+# Makefile for PARIDE
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := paride.a
+MX_OBJS  :=
+LX_OBJS  := 
+MI_OBJS  :=
+MIX_OBJS :=
+
+ifeq ($(CONFIG_PARIDE),y)
+  LX_OBJS += paride.o
+else
+  ifeq ($(CONFIG_PARIDE),m)
+    MX_OBJS += paride.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PD),y)
+  LX_OBJS += pd.o
+else
+  ifeq ($(CONFIG_PARIDE_PD),m)
+    MX_OBJS += pd.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PCD),y)
+  LX_OBJS += pcd.o
+else
+  ifeq ($(CONFIG_PARIDE_PCD),m)
+    MX_OBJS += pcd.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PF),y)
+  LX_OBJS += pf.o
+else
+  ifeq ($(CONFIG_PARIDE_PF),m)
+    MX_OBJS += pf.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ATEN),y)
+  LX_OBJS += aten.o
+else
+  ifeq ($(CONFIG_PARIDE_ATEN),m)
+    MX_OBJS += aten.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_BPCK),y)
+  LX_OBJS += bpck.o
+else
+  ifeq ($(CONFIG_PARIDE_BPCK),m)
+    MX_OBJS += bpck.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_COMM),y)
+  LX_OBJS += comm.o
+else
+  ifeq ($(CONFIG_PARIDE_COMM),m)
+    MX_OBJS += comm.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_DSTR),y)
+  LX_OBJS += dstr.o
+else
+  ifeq ($(CONFIG_PARIDE_DSTR),m)
+    MX_OBJS += dstr.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_KBIC),y)
+  LX_OBJS += kbic.o
+else
+  ifeq ($(CONFIG_PARIDE_KBIC),m)
+    MX_OBJS += kbic.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_EPAT),y)
+  LX_OBJS += epat.o
+else
+  ifeq ($(CONFIG_PARIDE_EPAT),m)
+    MX_OBJS += epat.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_EPIA),y)
+  LX_OBJS += epia.o
+else
+  ifeq ($(CONFIG_PARIDE_EPIA),m)
+    MX_OBJS += epia.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_FRPW),y)
+  LX_OBJS += frpw.o
+else
+  ifeq ($(CONFIG_PARIDE_FRPW),m)
+    MX_OBJS += frpw.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ON20),y)
+  LX_OBJS += on20.o
+else
+  ifeq ($(CONFIG_PARIDE_ON20),m)
+    MX_OBJS += on20.o
+  endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ON26),y)
+  LX_OBJS += on26.o
+else
+  ifeq ($(CONFIG_PARIDE_ON26),m)
+    MX_OBJS += on26.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/aten.c linux/drivers/block/paride/aten.c
--- v2.1.76/linux/drivers/block/paride/aten.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/aten.c	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,166 @@
+/* 
+        aten.c        (c) 1997  Grant R. Guenther <grant@torque.net>
+                          Under the terms of the GNU public license.
+
+	aten.c is a low-level protocol driver for the ATEN EH-100
+	parallel port adapter.  The EH-100 supports 4-bit and 8-bit
+        modes only.  There is also an EH-132 which supports EPP mode
+        transfers.  The EH-132 is not yet supported.
+
+*/
+
+#define ATEN_VERSION      "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define j44(a,b)                ((((a>>4)&0x0f)|(b&0xf0))^0x88)
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x08, 0x20 };
+
+static void  aten_write_regr( PIA *pi, int cont, int regr, int val)
+
+{	int r;
+
+	r = regr + cont_map[cont] + 0x80;
+
+	w0(r); w2(0xe); w2(6); w0(val); w2(7); w2(6); w2(0xc);
+}
+
+static int aten_read_regr( PIA *pi, int cont, int regr )
+
+{	int  a, b, r;
+
+        r = regr + cont_map[cont] + 0x40;
+
+	switch (pi->mode) {
+
+        case 0: w0(r); w2(0xe); w2(6); 
+		w2(7); w2(6); w2(0);
+		a = r1(); w0(0x10); b = r1(); w2(0xc);
+		return j44(a,b);
+
+        case 1: r |= 0x10;
+		w0(r); w2(0xe); w2(6); w0(0xff); 
+		w2(0x27); w2(0x26); w2(0x20);
+		a = r0();
+		w2(0x26); w2(0xc);
+		return a;
+	}
+	return -1;
+}
+
+static void aten_read_block( PIA *pi, char * buf, int count )
+
+{	int  k, a, b, c, d;
+
+	switch (pi->mode) {
+
+	case 0:	w0(0x48); w2(0xe); w2(6);
+		for (k=0;k<count/2;k++) {
+			w2(7); w2(6); w2(2);
+			a = r1(); w0(0x58); b = r1();
+			w2(0); d = r1(); w0(0x48); c = r1();
+			buf[2*k] = j44(c,d);
+			buf[2*k+1] = j44(a,b);
+		}
+		w2(0xc);
+		break;
+
+	case 1: w0(0x58); w2(0xe); w2(6);
+		for (k=0;k<count/2;k++) {
+			w2(0x27); w2(0x26); w2(0x22);
+			a = r0(); w2(0x20); b = r0();
+			buf[2*k] = b; buf[2*k+1] = a;
+		}
+		w2(0x26); w2(0xc);
+		break;
+	}
+}
+
+static void aten_write_block( PIA *pi, char * buf, int count )
+
+{	int k;
+
+	w0(0x88); w2(0xe); w2(6);
+	for (k=0;k<count/2;k++) {
+		w0(buf[2*k+1]); w2(0xe); w2(6);
+		w0(buf[2*k]); w2(7); w2(6);
+	}
+	w2(0xc);
+}
+
+static void aten_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+	w2(0xc);	
+}
+
+static void aten_disconnect ( PIA *pi )
+
+{       w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void aten_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[2] = {"4-bit","8-bit"};
+
+        printk("%s: aten %s, ATEN EH-100 at 0x%x, ",
+                pi->device,ATEN_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void aten_inc_use ( void )
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void aten_dec_use ( void )
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol aten = {"aten",0,2,2,1,1,
+                           aten_write_regr,
+                           aten_read_regr,
+                           aten_write_block,
+                           aten_read_block,
+                           aten_connect,
+                           aten_disconnect,
+                           0,
+                           0,
+                           0,
+                           aten_log_adapter,
+                           aten_inc_use, 
+                           aten_dec_use 
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &aten ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &aten );
+}
+
+#endif
+
+/* end of aten.c */
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/bpck.c linux/drivers/block/paride/bpck.c
--- v2.1.76/linux/drivers/block/paride/bpck.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/bpck.c	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,476 @@
+/* 
+	bpck.c	(c) 1996,1997  Grant R. Guenther <grant@torque.net>
+		               Under the terms of the GNU public license.
+
+	bpck.c is a low-level protocol driver for the MicroSolutions 
+	"backpack" parallel port IDE adapter.  
+
+*/
+
+#define	BPCK_VERSION	"1.0" 
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#undef r2
+#undef w2
+
+#define PC			pi->private
+#define r2()			(PC=(in_p(2) & 0xff))
+#define w2(byte)  		{out_p(2,byte); PC = byte;}
+#define t2(pat)   		{PC ^= pat; out_p(2,PC);}
+#define e2()			{PC &= 0xfe; out_p(2,PC);}
+#define o2()			{PC |= 1; out_p(2,PC);}
+
+#define j44(l,h)     (((l>>3)&0x7)|((l>>4)&0x8)|((h<<1)&0x70)|(h&0x80))
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+   cont = 2 - use internal bpck register addressing
+*/
+
+static int  cont_map[3] = { 0x40, 0x48, 0 };
+
+static int bpck_read_regr( PIA *pi, int cont, int regr )
+
+{       int r, l, h;
+
+	r = regr + cont_map[cont];
+
+	switch (pi->mode) {
+
+	case 0: w0(r & 0xf); w0(r); t2(2); t2(4);
+	        l = r1();
+        	t2(4);
+        	h = r1();
+        	return j44(l,h);
+
+	case 1: w0(r & 0xf); w0(r); t2(2);
+	        e2(); t2(0x20);
+		t2(4); h = r0();
+	        t2(1); t2(0x20);
+	        return h;
+
+	case 2:
+	case 3:
+	case 4: w0(r); w2(9); w2(0); w2(0x20);
+		h = r4();
+		w2(0);
+		return h;
+
+	}
+	return -1;
+}	
+
+static void bpck_write_regr( PIA *pi, int cont, int regr, int val )
+
+{	int	r;
+
+        r = regr + cont_map[cont];
+
+	switch (pi->mode) {
+
+	case 0:
+	case 1: w0(r);
+		t2(2);
+		w0(val);
+		o2(); t2(4); t2(1);
+		break;
+
+	case 2:
+	case 3:
+	case 4: w0(r); w2(9); w2(0);
+		w0(val); w2(1); w2(3); w2(0);
+		break;
+
+	}
+}
+
+/* These macros access the bpck registers in native addressing */
+
+#define WR(r,v)		bpck_write_regr(pi,2,r,v)
+#define RR(r)		(bpck_read_regr(pi,2,r))
+
+static void bpck_write_block( PIA *pi, char * buf, int count )
+
+{	int i;
+
+	switch (pi->mode) {
+
+	case 0: WR(4,0x40);
+		w0(0x40); t2(2); t2(1);
+		for (i=0;i<count;i++) { w0(buf[i]); t2(4); }
+		WR(4,0);
+		break;
+
+	case 1: WR(4,0x50);
+                w0(0x40); t2(2); t2(1);
+                for (i=0;i<count;i++) { w0(buf[i]); t2(4); }
+                WR(4,0x10);
+		break;
+
+	case 2: WR(4,0x48);
+		w0(0x40); w2(9); w2(0); w2(1);
+		for (i=0;i<count;i++) w4(buf[i]);
+		w2(0);
+		WR(4,8);
+		break;
+
+        case 3: WR(4,0x48);
+                w0(0x40); w2(9); w2(0); w2(1);
+                for (i=0;i<count/2;i++) w4w(((u16 *)buf)[i]);
+                w2(0);
+                WR(4,8);
+                break;
+ 
+        case 4: WR(4,0x48);
+                w0(0x40); w2(9); w2(0); w2(1);
+                for (i=0;i<count/4;i++) w4l(((u32 *)buf)[i]);
+                w2(0);
+                WR(4,8);
+                break;
+ 	}
+}
+
+static void bpck_read_block( PIA *pi, char * buf, int count )
+
+{	int i, l, h;
+
+	switch (pi->mode) {
+
+      	case 0: WR(4,0x40);
+		w0(0x40); t2(2);
+		for (i=0;i<count;i++) {
+		    t2(4); l = r1();
+		    t2(4); h = r1();
+		    buf[i] = j44(l,h);
+		}
+		WR(4,0);
+		break;
+
+	case 1: WR(4,0x50);
+		w0(0x40); t2(2); t2(0x20);
+      	        for(i=0;i<count;i++) { t2(4); buf[i] = r0(); }
+	        t2(1); t2(0x20);
+	        WR(4,0x10);
+		break;
+
+	case 2: WR(4,0x48);
+		w0(0x40); w2(9); w2(0); w2(0x20);
+		for (i=0;i<count;i++) buf[i] = r4();
+		w2(0);
+		WR(4,8);
+		break;
+
+        case 3: WR(4,0x48);
+                w0(0x40); w2(9); w2(0); w2(0x20);
+                for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w();
+                w2(0);
+                WR(4,8);
+                break;
+
+        case 4: WR(4,0x48);
+                w0(0x40); w2(9); w2(0); w2(0x20);
+                for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l();
+                w2(0);
+                WR(4,8);
+                break;
+
+	}
+}
+
+static int bpck_probe_unit ( PIA *pi )
+
+{	int o1, o0, f7, id;
+	int t, s;
+
+	id = pi->unit;
+	s = 0;
+	w2(4); w2(0xe); r2(); t2(2); 
+	o1 = r1()&0xf8;
+	o0 = r0();
+	w0(255-id); w2(4); w0(id);
+	t2(8); t2(8); t2(8);
+	t2(2); t = r1()&0xf8;
+	f7 = ((id % 8) == 7);
+	if ((f7) || (t != o1)) { t2(2); s = r1()&0xf8; }
+	if ((t == o1) && ((!f7) || (s == o1)))  {
+		w2(0x4c); w0(o0);
+		return 0;	
+	}
+	t2(8); w0(0); t2(2); w2(0x4c); w0(o0);
+	return 1;
+}
+	
+static void bpck_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+	w0(0xff-pi->unit); w2(4); w0(pi->unit);
+	t2(8); t2(8); t2(8); 
+	t2(2); t2(2);
+	
+	switch (pi->mode) {
+
+	case 0: t2(8); WR(4,0);
+		break;
+
+	case 1: t2(8); WR(4,0x10);
+		break;
+
+	case 2:
+        case 3:
+	case 4: w2(0); WR(4,8);
+		break;
+
+	}
+
+	WR(5,8);
+
+	if (pi->devtype == PI_PCD) {
+		WR(0x46,0x10);		/* fiddle with ESS logic ??? */
+		WR(0x4c,0x38);
+		WR(0x4d,0x88);
+		WR(0x46,0xa0);
+		WR(0x41,0);
+		WR(0x4e,8);
+		}
+}
+
+static void bpck_disconnect ( PIA *pi )
+
+{	w0(0); 
+	if (pi->mode >= 2) { w2(9); w2(0); } else t2(2);
+	w2(0x4c); w0(pi->saved_r0);
+} 
+
+static void bpck_force_spp ( PIA *pi )
+
+/* This fakes the EPP protocol to turn off EPP ... */
+
+{       pi->saved_r0 = r0();
+        w0(0xff-pi->unit); w2(4); w0(pi->unit);
+        t2(8); t2(8); t2(8); 
+        t2(2); t2(2);
+
+        w2(0); 
+        w0(4); w2(9); w2(0); 
+        w0(0); w2(1); w2(3); w2(0);     
+        w0(0); w2(9); w2(0);
+        w2(0x4c); w0(pi->saved_r0);
+}
+
+#define TEST_LEN  16
+
+static int bpck_test_proto( PIA *pi, char * scratch, int verbose )
+
+{	int i, e, l, h, om;
+	char buf[TEST_LEN];
+
+	bpck_force_spp(pi);
+
+	switch (pi->mode) {
+
+	case 0: bpck_connect(pi);
+		WR(0x13,0x7f);
+		w0(0x13); t2(2);
+		for(i=0;i<TEST_LEN;i++) {
+                    t2(4); l = r1();
+                    t2(4); h = r1();
+                    buf[i] = j44(l,h);
+		}
+		bpck_disconnect(pi);
+		break;
+
+        case 1: bpck_connect(pi);
+		WR(0x13,0x7f);
+                w0(0x13); t2(2); t2(0x20);
+                for(i=0;i<TEST_LEN;i++) { t2(4); buf[i] = r0(); }
+                t2(1); t2(0x20);
+		bpck_disconnect(pi);
+		break;
+
+	case 2:
+	case 3:
+	case 4: om = pi->mode;
+		pi->mode = 0;
+		bpck_connect(pi);
+		WR(7,3);
+		WR(4,8);
+		bpck_disconnect(pi);
+
+		pi->mode = om;
+		bpck_connect(pi);
+		w0(0x13); w2(9); w2(1); w0(0); w2(3); w2(0); w2(0xe0);
+
+		switch (pi->mode) {
+		  case 2: for (i=0;i<TEST_LEN;i++) buf[i] = r4();
+			  break;
+		  case 3: for (i=0;i<TEST_LEN/2;i++) ((u16 *)buf)[i] = r4w();
+                          break;
+		  case 4: for (i=0;i<TEST_LEN/4;i++) ((u32 *)buf)[i] = r4l();
+                          break;
+		}
+
+		w2(0);
+		WR(7,0);
+		bpck_disconnect(pi);
+
+		break;
+
+	}
+
+	if (verbose) {
+	    printk("%s: bpck: 0x%x unit %d mode %d: ",
+		   pi->device,pi->port,pi->unit,pi->mode);
+	    for (i=0;i<TEST_LEN;i++) printk("%3d",buf[i]);
+	    printk("\n");
+	}
+
+	e = 0;
+	for (i=0;i<TEST_LEN;i++) if (buf[i] != (i+1)) e++;
+	return e;
+}
+
+static void bpck_read_eeprom ( PIA *pi, char * buf )
+
+{       int i,j,k,n,p,v,f, om;
+
+	bpck_force_spp(pi);
+
+	om = pi->mode;
+	pi->mode = 0;
+
+	bpck_connect(pi);
+	
+	n = 0;
+	WR(4,0);
+	for (i=0;i<64;i++) {
+	    WR(6,8);  
+	    WR(6,0xc);
+	    p = 0x100;
+	    for (k=0;k<9;k++) {
+		f = (((i + 0x180) & p) != 0) * 2;
+		WR(6,f+0xc); 
+		WR(6,f+0xd); 
+		WR(6,f+0xc);
+		p = (p >> 1);
+	    }
+	    for (j=0;j<2;j++) {
+		v = 0;
+		for (k=0;k<8;k++) {
+		    WR(6,0xc); 
+		    WR(6,0xd); 
+		    WR(6,0xc); 
+		    f = RR(0);
+		    v = 2*v + (f == 0x84);
+		}
+		buf[2*i+1-j] = v;
+	    }
+	}
+	WR(6,8);
+	WR(6,0);
+	WR(5,8);
+
+	bpck_disconnect(pi);
+
+        if (om >= 2) {
+                bpck_connect(pi);
+                WR(7,3);
+                WR(4,8);
+                bpck_disconnect(pi);
+        }
+
+	pi->mode = om;
+}
+
+static int bpck_test_port ( PIA *pi ) 	/* check for 8-bit port */
+
+{	int	i, r, m;
+
+	w2(0x2c); i = r0(); w0(255-i); r = r0(); w0(i);
+	m = -1;
+	if (r == i) m = 2;
+	if (r == (255-i)) m = 0;
+
+	w2(0xc); i = r0(); w0(255-i); r = r0(); w0(i);
+	if (r != (255-i)) m = -1;
+	
+	if (m == 0) { w2(6); w2(0xc); r = r0(); w0(0xaa); w0(r); w0(0xaa); }
+	if (m == 2) { w2(0x26); w2(0xc); }
+
+	if (m == -1) return 0;
+	return 5;
+}
+
+static void bpck_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{	char	*mode_string[5] = { "4-bit","8-bit","EPP-8",
+				    "EPP-16","EPP-32" };
+
+#ifdef DUMP_EEPROM
+	int i;
+#endif
+
+	bpck_read_eeprom(pi,scratch);
+
+#ifdef DUMP_EEPROM
+	if (verbose) {
+	   for(i=0;i<128;i++)
+		if ((scratch[i] < ' ') || (scratch[i] > '~'))
+		    scratch[i] = '.';
+	   printk("%s: bpck EEPROM: %64.64s\n",pi->device,scratch);
+	   printk("%s:              %64.64s\n",pi->device,&scratch[64]);
+	}
+#endif
+
+	printk("%s: bpck %s, backpack %8.8s unit %d",
+		pi->device,BPCK_VERSION,&scratch[110],pi->unit);
+	printk(" at 0x%x, mode %d (%s), delay %d\n",pi->port,
+		pi->mode,mode_string[pi->mode],pi->delay);
+}
+
+static void bpck_inc_use( void )
+
+{	MOD_INC_USE_COUNT;
+}
+
+static void bpck_dec_use( void )
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol bpck = { "bpck",0,5,2,4,256,
+			  bpck_write_regr,
+			  bpck_read_regr,
+			  bpck_write_block,
+			  bpck_read_block,
+			  bpck_connect,
+			  bpck_disconnect,
+			  bpck_test_port,
+			  bpck_probe_unit,
+		          bpck_test_proto,
+			  bpck_log_adapter,
+			  bpck_inc_use,
+			  bpck_dec_use
+			};
+
+#ifdef MODULE
+
+int	init_module(void)
+
+{	return pi_register(&bpck) - 1;
+}
+
+void	cleanup_module(void)
+
+{	pi_unregister(&bpck);
+}
+
+#endif
+
+/* end of bpck.c */
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/comm.c linux/drivers/block/paride/comm.c
--- v2.1.76/linux/drivers/block/paride/comm.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/comm.c	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,222 @@
+/* 
+        comm.c    (c) 1997  Grant R. Guenther <grant@torque.net>
+                            Under the terms of the GNU public license.
+
+	comm.c is a low-level protocol driver for some older models
+	of the DataStor "Commuter" parallel to IDE adapter.  Some of
+	the parallel port devices marketed by Arista currently
+	use this adapter.
+*/
+
+#define COMM_VERSION      "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes:  0  nybble reads, 8-bit writes
+                1  8-bit reads and writes
+                2  8-bit EPP mode
+*/
+
+#define j44(a,b)	(((a>>3)&0x0f)|((b<<1)&0xf0))
+
+#define P1	w2(5);w2(0xd);w2(0xd);w2(5);w2(4);
+#define P2	w2(5);w2(7);w2(7);w2(5);w2(4);
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x08, 0x10 };
+
+static int comm_read_regr( PIA *pi, int cont, int regr )
+
+{       int     l, h, r;
+
+        r = regr + cont_map[cont];
+
+        switch (pi->mode)  {
+
+        case 0: w0(r); P1; w0(0);
+        	w2(6); l = r1(); w0(0x80); h = r1(); w2(4);
+                return j44(l,h);
+
+        case 1: w0(r+0x20); P1; 
+        	w0(0); w2(0x26); h = r0(); w2(4);
+                return h;
+
+	case 2:
+	case 3:
+        case 4: w3(r+0x20); r1(); 
+        	w2(0x24); h = r4(); w2(4);
+                return h;
+
+        }
+        return -1;
+}       
+
+static void comm_write_regr( PIA *pi, int cont, int regr, int val )
+
+{       int  r;
+
+        r = regr + cont_map[cont];
+
+        switch (pi->mode)  {
+
+        case 0:
+        case 1: w0(r); P1; w0(val); P2;
+		break;
+
+	case 2:
+	case 3:
+        case 4: w3(r); r1(); w4(val); 
+                break;
+        }
+}
+
+static void comm_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+        w2(4); w0(0xff); w2(6);
+        w2(4); w0(0xaa); w2(6);
+        w2(4); w0(0x00); w2(6);
+        w2(4); w0(0x87); w2(6);
+        w2(4); w0(0xe0); w2(0xc); w2(0xc); w2(4);
+}
+
+static void comm_disconnect ( PIA *pi )
+
+{       w2(0); w2(0); w2(0); w2(4); 
+	w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void comm_read_block( PIA *pi, char * buf, int count )
+
+{       int     i, l, h;
+
+        switch (pi->mode) {
+        
+        case 0: w0(0x48); P1;
+                for(i=0;i<count;i++) {
+                        w0(0); w2(6); l = r1();
+                        w0(0x80); h = r1(); w2(4);
+                        buf[i] = j44(l,h);
+                }
+                break;
+
+        case 1: w0(0x68); P1; w0(0);
+                for(i=0;i<count;i++) {
+                        w2(0x26); buf[i] = r0(); w2(0x24);
+                }
+		w2(4);
+		break;
+		
+	case 2: w3(0x68); r1(); w2(0x24);
+		for (i=0;i<count;i++) buf[i] = r4();
+		w2(4);
+		break;
+
+        case 3: w3(0x68); r1(); w2(0x24);
+                for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w();
+                w2(4);
+                break;
+
+        case 4: w3(0x68); r1(); w2(0x24);
+                for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l();
+                w2(4);
+                break;
+		
+	}
+}
+
+/* NB: Watch out for the byte swapped writes ! */
+
+static void comm_write_block( PIA *pi, char * buf, int count )
+
+{       int	k;
+
+        switch (pi->mode) {
+
+        case 0:
+        case 1: w0(0x68); P1;
+        	for (k=0;k<count;k++) {
+                        w2(5); w0(buf[k^1]); w2(7);
+                }
+                w2(5); w2(4);
+                break;
+
+        case 2: w3(0x48); r1();
+                for (k=0;k<count;k++) w4(buf[k^1]);
+                break;
+
+        case 3: w3(0x48); r1();
+                for (k=0;k<count/2;k++) w4w(pi_swab16(buf,k));
+                break;
+
+        case 4: w3(0x48); r1();
+                for (k=0;k<count/4;k++) w4l(pi_swab32(buf,k));
+                break;
+
+
+        }
+}
+
+static void comm_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[5] = {"4-bit","8-bit","EPP-8","EPP-16","EPP-32"};
+
+        printk("%s: comm %s, DataStor Commuter at 0x%x, ",
+                pi->device,COMM_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void comm_inc_use ( void )
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void comm_dec_use ( void )
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol comm = {"comm",0,5,2,1,1,
+                           comm_write_regr,
+                           comm_read_regr,
+                           comm_write_block,
+                           comm_read_block,
+                           comm_connect,
+                           comm_disconnect,
+                           0,
+                           0,
+                           0,
+                           comm_log_adapter,
+                           comm_inc_use, 
+                           comm_dec_use 
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &comm ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &comm );
+}
+
+#endif
+
+/* end of comm.c */
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/dstr.c linux/drivers/block/paride/dstr.c
--- v2.1.76/linux/drivers/block/paride/dstr.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/dstr.c	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,237 @@
+/* 
+        dstr.c    (c) 1997  Grant R. Guenther <grant@torque.net>
+                            Under the terms of the GNU public license.
+
+        dstr.c is a low-level protocol driver for the 
+        DataStor EP2000 parallel to IDE adapter chip.
+
+*/
+
+#define DSTR_VERSION      "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes:  0  nybble reads, 8-bit writes
+                1  8-bit reads and writes
+                2  8-bit EPP mode
+		3  EPP-16
+		4  EPP-32
+*/
+
+#define j44(a,b)  (((a>>3)&0x07)|((~a>>4)&0x08)|((b<<1)&0x70)|((~b)&0x80))
+
+#define P1	w2(5);w2(0xd);w2(5);w2(4);
+#define P2	w2(5);w2(7);w2(5);w2(4);
+#define P3      w2(6);w2(4);w2(6);w2(4);
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x20, 0x40 };
+
+static int dstr_read_regr( PIA *pi, int cont, int regr )
+
+{       int     a, b, r;
+
+        r = regr + cont_map[cont];
+
+	w0(0x81); P1;
+	if (pi->mode) { w0(0x11); } else { w0(1); }
+	P2; w0(r); P1;
+
+        switch (pi->mode)  {
+
+        case 0: w2(6); a = r1(); w2(4); w2(6); b = r1(); w2(4);
+                return j44(a,b);
+
+        case 1: w0(0); w2(0x26); a = r0(); w2(4);
+                return a;
+
+	case 2:
+	case 3:
+        case 4: w2(0x24); a = r4(); w2(4);
+                return a;
+
+        }
+        return -1;
+}       
+
+static void dstr_write_regr(  PIA *pi, int cont, int regr, int val )
+
+{       int  r;
+
+        r = regr + cont_map[cont];
+
+	w0(0x81); P1; 
+	if (pi->mode >= 2) { w0(0x11); } else { w0(1); }
+	P2; w0(r); P1;
+	
+        switch (pi->mode)  {
+
+        case 0:
+        case 1: w0(val); w2(5); w2(7); w2(5); w2(4);
+		break;
+
+	case 2:
+	case 3:
+        case 4: w4(val); 
+                break;
+        }
+}
+
+#define  CCP(x)  w0(0xff);w2(0xc);w2(4);\
+		 w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);w0(0x78);\
+		 w0(x);w2(5);w2(4);
+
+static void dstr_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+        w2(4); CCP(0xe0); w0(0xff);
+}
+
+static void dstr_disconnect ( PIA *pi )
+
+{       CCP(0x30);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void dstr_read_block( PIA *pi, char * buf, int count )
+
+{       int     k, a, b;
+
+        w0(0x81); P1;
+        if (pi->mode) { w0(0x19); } else { w0(9); }
+	P2; w0(0x82); P1; P3; w0(0x20); P1;
+
+        switch (pi->mode) {
+
+        case 0: for (k=0;k<count;k++) {
+                        w2(6); a = r1(); w2(4);
+                        w2(6); b = r1(); w2(4);
+                        buf[k] = j44(a,b);
+                } 
+                break;
+
+        case 1: w0(0);
+                for (k=0;k<count;k++) {
+                        w2(0x26); buf[k] = r0(); w2(0x24);
+                }
+                w2(4);
+                break;
+
+        case 2: w2(0x24); 
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(4);
+                break;
+
+        case 3: w2(0x24); 
+                for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+                w2(4);
+                break;
+
+        case 4: w2(0x24); 
+                for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+                w2(4);
+                break;
+
+        }
+}
+
+static void dstr_write_block( PIA *pi, char * buf, int count )
+
+{       int	k;
+
+        w0(0x81); P1;
+        if (pi->mode) { w0(0x19); } else { w0(9); }
+        P2; w0(0x82); P1; P3; w0(0x20); P1;
+
+        switch (pi->mode) {
+
+        case 0:
+        case 1: for (k=0;k<count;k++) {
+                        w2(5); w0(buf[k]); w2(7);
+                }
+                w2(5); w2(4);
+                break;
+
+        case 2: w2(0xc5);
+                for (k=0;k<count;k++) w4(buf[k]);
+		w2(0xc4);
+                break;
+
+        case 3: w2(0xc5);
+                for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+                w2(0xc4);
+                break;
+
+        case 4: w2(0xc5);
+                for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+                w2(0xc4);
+                break;
+
+        }
+}
+
+
+static void dstr_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[5] = {"4-bit","8-bit","EPP-8",
+				   "EPP-16","EPP-32"};
+
+        printk("%s: dstr %s, DataStor EP2000 at 0x%x, ",
+                pi->device,DSTR_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void dstr_inc_use ( void )
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void dstr_dec_use ( void )
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol dstr = {"dstr",0,5,2,1,1,
+                           dstr_write_regr,
+                           dstr_read_regr,
+                           dstr_write_block,
+                           dstr_read_block,
+                           dstr_connect,
+                           dstr_disconnect,
+                           0,
+                           0,
+                           0,
+                           dstr_log_adapter,
+                           dstr_inc_use, 
+                           dstr_dec_use 
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &dstr ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &dstr );
+}
+
+#endif
+
+/* end of dstr.c */
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/epat.c linux/drivers/block/paride/epat.c
--- v2.1.76/linux/drivers/block/paride/epat.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/epat.c	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,315 @@
+/* 
+        epat.c  (c) 1997  Grant R. Guenther <grant@torque.net>
+                          Under the terms of the GNU public license.
+
+	This is the low level protocol driver for the EPAT parallel
+        to IDE adapter from Shuttle Technologies.  This adapter is
+        used in many popular parallel port disk products such as the
+        SyQuest EZ drives, the Avatar Shark and the Imation SuperDisk.
+	
+*/
+
+#define EPAT_VERSION      "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define j44(a,b)		(((a>>4)&0x0f)+(b&0xf0))
+#define j53(a,b)		(((a>>3)&0x1f)+((b<<4)&0xe0))
+
+/* cont =  0   IDE register file
+   cont =  1   IDE control registers
+   cont =  2   internal EPAT registers
+*/
+
+static int cont_map[3] = { 0x18, 0x10, 0 };
+
+static void epat_write_regr( PIA *pi, int cont, int regr, int val)
+
+{	int r;
+
+	r = regr + cont_map[cont];
+
+	switch (pi->mode) {
+
+	case 0:
+	case 1:
+	case 2:	w0(0x60+r); w2(1); w0(val); w2(4);
+		break;
+
+	case 3:
+	case 4:
+	case 5: w3(0x40+r); w4(val);
+		break;
+
+	}
+}
+
+static int epat_read_regr( PIA *pi, int cont, int regr )
+
+{	int  a, b, r;
+
+	r = regr + cont_map[cont];
+
+	switch (pi->mode) {
+
+	case 0:	w0(r); w2(1); w2(3); 
+		a = r1(); w2(4); b = r1();
+		return j44(a,b);
+
+	case 1: w0(0x40+r); w2(1); w2(4);
+		a = r1(); b = r2(); w0(0xff);
+		return j53(a,b);
+
+	case 2: w0(0x20+r); w2(1); w2(0x25);
+		a = r0(); w2(4);
+		return a;
+
+	case 3:
+	case 4:
+	case 5: w3(r); w2(0x24); a = r4(); w2(4);
+		return a;
+
+	}
+	return -1;	/* never gets here */
+}
+
+static void epat_read_block( PIA *pi, char * buf, int count )
+
+{	int  k, ph, a, b;
+
+	switch (pi->mode) {
+
+	case 0:	w0(7); w2(1); w2(3); w0(0xff);
+		ph = 0;
+		for(k=0;k<count;k++) {
+			if (k == count-1) w0(0xfd);
+			w2(6+ph); a = r1();
+			if (a & 8) b = a; 
+			  else { w2(4+ph); b = r1(); }
+			buf[k] = j44(a,b);
+			ph =  1 - ph;
+		}
+		w0(0); w2(4);
+		break;
+
+	case 1: w0(0x47); w2(1); w2(5); w0(0xff);
+		ph = 0;
+		for(k=0;k<count;k++) {
+			if (k == count-1) w0(0xfd); 
+			w2(4+ph);
+			a = r1(); b = r2();
+			buf[k] = j53(a,b);
+			ph = 1 - ph;
+		}
+		w0(0); w2(4);
+		break;
+
+	case 2: w0(0x27); w2(1); w2(0x25); w0(0);
+		ph = 0;
+		for(k=0;k<count-1;k++) {
+			w2(0x24+ph);
+			buf[k] = r0();
+			ph = 1 - ph;
+		}
+		w2(0x26); w2(0x27); buf[count-1] = r0(); 
+		w2(0x25); w2(4);
+		break;
+
+	case 3: w3(0x80); w2(0x24);
+		for(k=0;k<count-1;k++) buf[k] = r4();
+		w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
+		w2(4);
+		break;
+
+	case 4: w3(0x80); w2(0x24);
+		for(k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
+		buf[count-2] = r4();
+		w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
+		w2(4);
+		break;
+
+	case 5: w3(0x80); w2(0x24);
+		for(k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
+		for(k=count-4;k<count-1;k++) buf[k] = r4();
+		w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
+		w2(4);
+		break;
+
+	}
+}
+
+static void epat_write_block( PIA *pi, char * buf, int count )   
+
+{	int ph, k;
+
+	switch (pi->mode) {
+
+	case 0:
+	case 1:
+	case 2: w0(0x67); w2(1); w2(5);
+		ph = 0;
+		for(k=0;k<count;k++) {
+		  	w0(buf[k]);
+			w2(4+ph);
+			ph = 1 - ph;
+		}
+		w2(7); w2(4);
+		break;
+
+	case 3: w3(0xc0); 
+		for(k=0;k<count;k++) w4(buf[k]);
+		w2(4);
+		break;
+
+	case 4: w3(0xc0); 
+		for(k=0;k<(count/2);k++) w4w(((u16 *)buf)[k]);
+		w2(4);
+		break;
+
+	case 5: w3(0xc0); 
+		for(k=0;k<(count/4);k++) w4l(((u32 *)buf)[k]);
+		w2(4);
+		break;
+
+	}
+}
+
+/* these macros access the EPAT registers in native addressing */
+
+#define	WR(r,v)		epat_write_regr(pi,2,r,v)
+#define	RR(r)		(epat_read_regr(pi,2,r))
+
+/* and these access the IDE task file */
+
+#define WRi(r,v)         epat_write_regr(pi,0,r,v)
+#define RRi(r)           (epat_read_regr(pi,0,r))
+
+/* FIXME:  the CCP stuff should be fixed to handle multiple EPATs on a chain */
+
+#define CCP(x) 	w2(4);w0(0x22);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
+                w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff);
+
+static void epat_connect ( PIA *pi )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+	CCP(0); CCP(0xe0);
+	w0(0); w2(1); w2(4);
+	if (pi->mode >= 3) {
+		w0(0); w2(1); w2(4); w2(0xc);
+		w0(0x40); w2(6); w2(7); w2(4); w2(0xc); w2(4);
+	}
+	WR(8,0x10); WR(0xc,0x14); WR(0xa,0x38); WR(0x12,0x10);
+}
+
+static void epat_disconnect ( PIA *pi )
+
+{       CCP(0x30);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static int epat_test_proto( PIA *pi, char * scratch, int verbose )
+
+{       int     k, j, f, cc;
+	int	e[2] = {0,0};
+
+        epat_connect(pi);
+	cc = RR(0xd);
+	epat_disconnect(pi);
+
+	epat_connect(pi);
+	for (j=0;j<2;j++) {
+  	    WRi(6,0xa0+j*0x10);
+            for (k=0;k<256;k++) {
+                WRi(2,k^0xaa);
+                WRi(3,k^0x55);
+                if (RRi(2) != (k^0xaa)) e[j]++;
+                }
+	    }
+        epat_disconnect(pi);
+
+        f = 0;
+        epat_connect(pi);
+        WR(0x13,1); WR(0x13,0); WR(0xa,0x11);
+        epat_read_block(pi,scratch,512);
+	
+        for (k=0;k<256;k++) {
+            if ((scratch[2*k] & 0xff) != k) f++;
+            if ((scratch[2*k+1] & 0xff) != (0xff-k)) f++;
+        }
+        epat_disconnect(pi);
+
+        if (verbose)  {
+            printk("%s: epat: port 0x%x, mode %d, ccr %x, test=(%d,%d,%d)\n",
+		   pi->device,pi->port,pi->mode,cc,e[0],e[1],f);
+	}
+	
+        return (e[0] && e[1]) || f;
+}
+
+static void epat_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{	int	ver;
+        char    *mode_string[6] = 
+		   {"4-bit","5/3","8-bit","EPP-8","EPP-16","EPP-32"};
+
+	epat_connect(pi);
+	WR(0xa,0x38);		/* read the version code */
+        ver = RR(0xb);
+        epat_disconnect(pi);
+
+	printk("%s: epat %s, Shuttle EPAT chip %x at 0x%x, ",
+		pi->device,EPAT_VERSION,ver,pi->port);
+	printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void epat_inc_use ( void )
+
+{  	MOD_INC_USE_COUNT;
+}
+
+static void epat_dec_use ( void )
+
+{	MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol epat = {"epat",0,6,3,1,1,
+			   epat_write_regr,
+			   epat_read_regr,
+			   epat_write_block,
+			   epat_read_block,
+			   epat_connect,
+			   epat_disconnect,
+			   0,
+			   0,
+			   epat_test_proto,
+			   epat_log_adapter,
+			   epat_inc_use, 
+			   epat_dec_use 
+			  };
+
+
+#ifdef MODULE
+
+int	init_module(void)
+
+{	return pi_register( &epat) - 1;
+}
+
+void	cleanup_module(void)
+
+{	pi_unregister( &epat);
+}
+
+#endif
+
+/* end of epat.c */
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/epia.c linux/drivers/block/paride/epia.c
--- v2.1.76/linux/drivers/block/paride/epia.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/epia.c	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,318 @@
+/* 
+        epia.c    (c) 1997  Grant R. Guenther <grant@torque.net>
+                            Under the terms of the GNU public license.
+
+        epia.c is a low-level protocol driver for Shuttle Technologies 
+	EPIA parallel to IDE adapter chip.  This device is now obsolete
+	and has been replaced with the EPAT chip, which is supported
+	by epat.c, however, some devices based on EPIA are still
+	available.
+
+*/
+
+#define EPIA_VERSION      "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes:  0  nybble reads on port 1, 8-bit writes
+                1  5/3 reads on ports 1 & 2, 8-bit writes
+                2  8-bit reads and writes
+                3  8-bit EPP mode
+		4  16-bit EPP
+		5  32-bit EPP
+*/
+
+#define j44(a,b)                (((a>>4)&0x0f)+(b&0xf0))
+#define j53(a,b)                (((a>>3)&0x1f)+((b<<4)&0xe0))
+
+/* cont =  0   IDE register file
+   cont =  1   IDE control registers
+*/
+
+static int cont_map[2] = { 0, 0x80 };
+
+static int epia_read_regr( PIA *pi, int cont, int regr )
+
+{       int     a, b, r;
+
+	regr += cont_map[cont];
+
+        switch (pi->mode)  {
+
+        case 0: r = regr^0x39;
+                w0(r); w2(1); w2(3); w0(r);
+                a = r1(); w2(1); b = r1(); w2(4);
+                return j44(a,b);
+
+        case 1: r = regr^0x31;
+                w0(r); w2(1); w0(r&0x37); 
+                w2(3); w2(5); w0(r|0xf0);
+                a = r1(); b = r2(); w2(4);
+                return j53(a,b);
+
+        case 2: r = regr^0x29;
+                w0(r); w2(1); w2(0X21); w2(0x23); 
+                a = r0(); w2(4);
+                return a;
+
+	case 3:
+	case 4:
+        case 5: w3(regr); w2(0x24); a = r4(); w2(4);
+                return a;
+
+        }
+        return -1;
+}       
+
+static void epia_write_regr( PIA *pi, int cont, int regr, int val)
+
+{       int  r;
+
+	regr += cont_map[cont];
+
+        switch (pi->mode)  {
+
+        case 0:
+        case 1:
+        case 2: r = regr^0x19;
+                w0(r); w2(1); w0(val); w2(3); w2(4);
+                break;
+
+	case 3:
+	case 4:
+        case 5: r = regr^0x40;
+                w3(r); w4(val); w2(4);
+                break;
+        }
+}
+
+#define WR(r,v)         epia_write_regr(pi,0,r,v)
+#define RR(r)           (epia_read_regr(pi,0,r))
+
+/* The use of register 0x84 is entirely unclear - it seems to control
+   some EPP counters ...  currently we know about 3 different block
+   sizes:  the standard 512 byte reads and writes, 12 byte writes and 
+   2048 byte reads (the last two being used in the CDrom drivers.
+*/
+
+static void epia_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+
+        w2(4); w0(0xa0); w0(0x50); w0(0xc0); w0(0x30); w0(0xa0); w0(0);
+        w2(1); w2(4);
+        if (pi->mode >= 3) { 
+                w0(0xa); w2(1); w2(4); w0(0x82); w2(4); w2(0xc); w2(4);
+                w2(0x24); w2(0x26); w2(4);
+        }
+        WR(0x86,8);  
+}
+
+static void epia_disconnect ( PIA *pi )
+
+{       WR(0x84,0x10);
+        w0(pi->saved_r0);
+        w2(1); w2(4);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void epia_read_block( PIA *pi, char * buf, int count )
+
+{       int     k, ph, a, b;
+
+        switch (pi->mode) {
+
+        case 0: w0(0x81); w2(1); w2(3); w0(0xc1);
+                ph = 1;
+                for (k=0;k<count;k++) {
+                        w2(2+ph); a = r1();
+                        w2(4+ph); b = r1();
+                        buf[k] = j44(a,b);
+                        ph = 1 - ph;
+                } 
+                w0(0); w2(4);
+                break;
+
+        case 1: w0(0x91); w2(1); w0(0x10); w2(3); 
+                w0(0x51); w2(5); w0(0xd1); 
+                ph = 1;
+                for (k=0;k<count;k++) {
+                        w2(4+ph);
+                        a = r1(); b = r2();
+                        buf[k] = j53(a,b);
+                        ph = 1 - ph;
+                }
+                w0(0); w2(4);
+                break;
+
+        case 2: w0(0x89); w2(1); w2(0x23); w2(0x21); 
+                ph = 1;
+                for (k=0;k<count;k++) {
+                        w2(0x24+ph);
+                        buf[k] = r0();
+                        ph = 1 - ph;
+                }
+                w2(6); w2(4);
+                break;
+
+        case 3: if (count > 512) WR(0x84,3);
+		w3(0); w2(0x24);
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(4); WR(0x84,0);
+                break;
+
+        case 4: if (count > 512) WR(0x84,3);
+		w3(0); w2(0x24);
+		for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+                w2(4); WR(0x84,0);
+                break;
+
+        case 5: if (count > 512) WR(0x84,3);
+		w3(0); w2(0x24);
+                for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+                w2(4); WR(0x84,0);
+                break;
+
+        }
+}
+
+static void epia_write_block( PIA *pi, char * buf, int count )
+
+{       int     ph, k, last, d;
+
+        switch (pi->mode) {
+
+        case 0:
+        case 1:
+        case 2: w0(0xa1); w2(1); w2(3); w2(1); w2(5);
+                ph = 0;  last = 0x8000;
+                for (k=0;k<count;k++) {
+                        d = buf[k];
+                        if (d != last) { last = d; w0(d); }
+                        w2(4+ph);
+                        ph = 1 - ph;
+                }
+                w2(7); w2(4);
+                break;
+
+        case 3: if (count < 512) WR(0x84,1);
+		w3(0x40);
+                for (k=0;k<count;k++) w4(buf[k]);
+		if (count < 512) WR(0x84,0);
+                break;
+
+        case 4: if (count < 512) WR(0x84,1);
+		w3(0x40);
+                for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+		if (count < 512) WR(0x84,0);
+                break;
+
+        case 5: if (count < 512) WR(0x84,1);
+		w3(0x40);
+                for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+		if (count < 512) WR(0x84,0);
+                break;
+
+        }
+
+}
+
+static int epia_test_proto( PIA *pi, char * scratch, int verbose )
+
+{       int     j, k, f;
+	int	e[2] = {0,0};
+
+        epia_connect(pi);
+        for (j=0;j<2;j++) {
+            WR(6,0xa0+j*0x10);
+            for (k=0;k<256;k++) {
+                WR(2,k^0xaa);
+                WR(3,k^0x55);
+                if (RR(2) != (k^0xaa)) e[j]++;
+                }
+            }
+        epia_disconnect(pi);
+
+        f = 0;
+        epia_connect(pi);
+        WR(0x84,8);
+        epia_read_block(pi,scratch,512);
+        for (k=0;k<256;k++) {
+            if ((scratch[2*k] & 0xff) != ((k+1) & 0xff)) f++;
+            if ((scratch[2*k+1] & 0xff) != ((-2-k) & 0xff)) f++;
+        }
+        WR(0x84,0);
+        epia_disconnect(pi);
+
+        if (verbose)  {
+            printk("%s: epia: port 0x%x, mode %d, test=(%d,%d,%d)\n",
+                   pi->device,pi->port,pi->mode,e[0],e[1],f);
+        }
+        
+        return (e[0] && e[1]) || f;
+
+}
+
+
+static void epia_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[6] = {"4-bit","5/3","8-bit",
+				   "EPP-8","EPP-16","EPP-32"};
+
+        printk("%s: epia %s, Shuttle EPIA at 0x%x, ",
+                pi->device,EPIA_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void epia_inc_use ( void )
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void epia_dec_use ( void )
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol epia = {"epia",0,6,3,1,1,
+                           epia_write_regr,
+                           epia_read_regr,
+                           epia_write_block,
+                           epia_read_block,
+                           epia_connect,
+                           epia_disconnect,
+                           0,
+                           0,
+                           epia_test_proto,
+                           epia_log_adapter,
+                           epia_inc_use, 
+                           epia_dec_use 
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &epia ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &epia );
+}
+
+#endif
+
+/* end of epia.c */
+
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/frpw.c linux/drivers/block/paride/frpw.c
--- v2.1.76/linux/drivers/block/paride/frpw.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/frpw.c	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,256 @@
+/* 
+	frpw.c	(c) 1997  Grant R. Guenther <grant@torque.net>
+		          Under the terms of the GNU public license
+
+	frpw.c is a low-level protocol driver for the Freecom "Power"
+	parallel port IDE adapter.
+	
+*/
+
+#define	FRPW_VERSION	"1.0" 
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define cec4		w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4);
+#define j44(l,h)	(((l>>4)&0x0f)|(h&0xf0))
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x08, 0x10 };
+
+static int frpw_read_regr( PIA *pi, int cont, int regr )
+
+{	int	h,l,r;
+
+	r = regr + cont_map[cont];
+
+	w2(4);
+	w0(r); cec4;
+	w2(6); l = r1();
+	w2(4); h = r1();
+	w2(4); 
+
+	return j44(l,h);
+
+}
+
+static void frpw_write_regr( PIA *pi, int cont, int regr, int val)
+
+{	int r;
+
+        r = regr + cont_map[cont];
+
+	w2(4); w0(r); cec4; 
+	w0(val);
+	w2(5);w2(7);w2(5);w2(4);
+}
+
+static void frpw_read_block_int( PIA *pi, char * buf, int count, int regr )
+
+{       int     h, l, k, ph;
+
+        switch(pi->mode) {
+
+        case 0: w2(4); w0(regr); cec4;
+                for (k=0;k<count;k++) {
+                        w2(6); l = r1();
+                        w2(4); h = r1();
+                        buf[k] = j44(l,h);
+                }
+                w2(4);
+                break;
+
+        case 1: ph = 2;
+                w2(4); w0(regr + 0xc0); cec4;
+                w0(0xff);
+                for (k=0;k<count;k++) {
+                        w2(0xa4 + ph); 
+                        buf[k] = r0();
+                        ph = 2 - ph;
+                } 
+                w2(0xac); w2(0xa4); w2(4);
+                break;
+
+        case 2: w2(4); w0(regr + 0x80); cec4;
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(0xac); w2(0xa4);
+                w2(4);
+                break;
+
+	case 3: w2(4); w0(regr + 0x80); cec4;
+		for (k=0;k<count-2;k++) buf[k] = r4();
+		w2(0xac); w2(0xa4);
+		buf[count-2] = r4();
+		buf[count-1] = r4();
+		w2(4);
+		break;
+
+        }
+}
+
+static void frpw_read_block( PIA *pi, char * buf, int count)
+
+{	frpw_read_block_int(pi,buf,count,0x08);
+}
+
+static void frpw_write_block( PIA *pi, char * buf, int count )
+ 
+{	int	k;
+
+	switch(pi->mode) {
+
+	case 0:
+	case 1:
+	case 2: w2(4); w0(8); cec4; w2(5);
+        	for (k=0;k<count;k++) {
+			w0(buf[k]);
+			w2(7);w2(5);
+		}
+		w2(4);
+		break;
+
+	case 3: w2(4); w0(0xc8); cec4; w2(5);
+		for (k=0;k<count;k++) w4(buf[k]);
+		w2(4);
+		break;
+	}
+}
+
+static void frpw_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+	w2(4);
+}
+
+static void frpw_disconnect ( PIA *pi )
+
+{       w2(4); w0(0x20); cec4;
+	w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+/* Stub logic to see if PNP string is available - used to distinguish
+   between the Xilinx and ASIC implementations of the Freecom adapter.
+*/
+
+static int frpw_test_pnp ( PIA *pi )
+
+{	int olddelay, a, b;
+
+	olddelay = pi->delay;
+	pi->delay = 10;
+
+	pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+	
+	w2(4); w0(4); w2(6); w2(7);
+	a = r1() & 0xff; w2(4); b = r1() & 0xff;
+	w2(0xc); w2(0xe); w2(4);
+
+	pi->delay = olddelay;
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+
+	return ((~a&0x40) && (b&0x40));
+} 
+
+/*  We use pi->private to record the chip type:  
+	0 = untested, 2 = Xilinx, 3 = ASIC
+*/  
+
+static int frpw_test_proto( PIA *pi, char * scratch, int verbose )
+
+{       int     k, r;
+
+	if (!pi->private) pi->private = frpw_test_pnp(pi) + 2;
+
+	if ((pi->private == 2) && (pi->mode > 2)) {
+	   if (verbose) 
+		printk("%s: frpw: Xilinx does not support mode %d\n",
+			pi->device, pi->mode);
+	   return 1;
+	}
+
+	if ((pi->private == 3) && (pi->mode == 2)) {
+	   if (verbose)
+		printk("%s: frpw: ASIC does not support mode 2\n",
+			pi->device);
+	   return 1;
+	}
+
+	frpw_connect(pi);
+        frpw_read_block_int(pi,scratch,512,0x10);
+        r = 0;
+        for (k=0;k<128;k++) if (scratch[k] != k) r++;
+	frpw_disconnect(pi);
+
+        if (verbose)  {
+            printk("%s: frpw: port 0x%x, mode %d, test=%d\n",
+                   pi->device,pi->port,pi->mode,r);
+        }
+
+        return r;
+}
+
+
+static void frpw_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[4] = {"4-bit","8-bit","EPP-X","EPP-A"};
+
+        printk("%s: frpw %s, Freecom (%s) adapter at 0x%x, ", pi->device,
+		FRPW_VERSION,(pi->private == 2)?"Xilinx":"ASIC",pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void frpw_inc_use ( void )
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void frpw_dec_use ( void )
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol frpw = {"frpw",0,4,2,2,1,
+                           frpw_write_regr,
+                           frpw_read_regr,
+                           frpw_write_block,
+                           frpw_read_block,
+                           frpw_connect,
+                           frpw_disconnect,
+                           0,
+                           0,
+                           frpw_test_proto,
+                           frpw_log_adapter,
+                           frpw_inc_use, 
+                           frpw_dec_use 
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &frpw ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &frpw );
+}
+
+#endif
+
+/* end of frpw.c */
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/kbic.c linux/drivers/block/paride/kbic.c
--- v2.1.76/linux/drivers/block/paride/kbic.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/kbic.c	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,305 @@
+/*
+        kbic.c    (c) 1997  Grant R. Guenther <grant@torque.net>
+                            Under the terms of the GNU public license.
+
+        This is a low-level driver for the KBIC-951A and KBIC-971A
+        parallel to IDE adapter chips from KingByte Information Systems.
+
+	The chips are almost identical, however, the wakeup code 
+	required for the 971A interferes with the correct operation of
+        the 951A, so this driver registers itself twice, once for
+	each chip.
+
+*/
+
+#define KBIC_VERSION      "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define r12w()			(delay_p,inw(pi->port+1)&0xffff) 
+
+#define j44(a,b)                ((((a>>4)&0x0f)|(b&0xf0))^0x88)
+#define j53(w)                  (((w>>3)&0x1f)|((w>>4)&0xe0))
+
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x80, 0x40 };
+
+static int kbic_read_regr( PIA *pi, int cont, int regr )
+
+{       int     a, b, s;
+
+        s = cont_map[cont];
+
+	switch (pi->mode) {
+
+	case 0: w0(regr|0x18|s); w2(4); w2(6); w2(4); w2(1); w0(8);
+	        a = r1(); w0(0x28); b = r1(); w2(4);
+		return j44(a,b);
+
+	case 1: w0(regr|0x38|s); w2(4); w2(6); w2(4); w2(5); w0(8);
+		a = r12w(); w2(4);
+		return j53(a);
+
+	case 2: w0(regr|0x08|s); w2(4); w2(6); w2(4); w2(0xa5); w2(0xa1);
+		a = r0(); w2(4);
+       		return a;
+
+	case 3:
+	case 4:
+	case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr);
+		a = r4(); b = r4(); w2(4); w2(0); w2(4);
+		return a;
+
+	}
+	return -1;
+}       
+
+static void  kbic_write_regr( PIA *pi, int cont, int regr, int val)
+
+{       int  s;
+
+        s = cont_map[cont];
+
+        switch (pi->mode) {
+
+	case 0: 
+        case 1:
+	case 2:	w0(regr|0x10|s); w2(4); w2(6); w2(4); 
+		w0(val); w2(5); w2(4);
+		break;
+
+	case 3:
+	case 4:
+	case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr);
+		w4(val); w4(val);
+		w2(4); w2(0); w2(4);
+                break;
+
+	}
+}
+
+static void k951_connect ( PIA *pi  )
+
+{ 	pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+        w2(4); 
+}
+
+static void k951_disconnect ( PIA *pi )
+
+{      	w0(pi->saved_r0);
+        w2(pi->saved_r2);
+}
+
+#define	CCP(x)	w2(0xc4);w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);\
+		w0(0x78);w0(x);w2(0xc5);w2(0xc4);w0(0xff);
+
+static void k971_connect ( PIA *pi  )
+
+{ 	pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+	CCP(0x20);
+        w2(4); 
+}
+
+static void k971_disconnect ( PIA *pi )
+
+{       CCP(0x30);
+	w0(pi->saved_r0);
+        w2(pi->saved_r2);
+}
+
+/* counts must be congruent to 0 MOD 4, but all known applications
+   have this property.
+*/
+
+static void kbic_read_block( PIA *pi, char * buf, int count )
+
+{       int     k, a, b;
+
+        switch (pi->mode) {
+
+        case 0: w0(0x98); w2(4); w2(6); w2(4);
+                for (k=0;k<count/2;k++) {
+			w2(1); w0(8);    a = r1();
+			       w0(0x28); b = r1();
+			buf[2*k]   = j44(a,b);
+			w2(5);           b = r1();
+			       w0(8);    a = r1();
+			buf[2*k+1] = j44(a,b);
+			w2(4);
+                } 
+                break;
+
+        case 1: w0(0xb8); w2(4); w2(6); w2(4); 
+                for (k=0;k<count/4;k++) {
+                        w0(0xb8); 
+			w2(4); w2(5); 
+                        w0(8);    buf[4*k]   = j53(r12w());
+			w0(0xb8); buf[4*k+1] = j53(r12w());
+			w2(4); w2(5);
+			          buf[4*k+3] = j53(r12w());
+			w0(8);    buf[4*k+2] = j53(r12w());
+                }
+                w2(4);
+                break;
+
+        case 2: w0(0x88); w2(4); w2(6); w2(4);
+                for (k=0;k<count/2;k++) {
+                        w2(0xa0); w2(0xa1); buf[2*k] = r0();
+                        w2(0xa5); buf[2*k+1] = r0();
+                }
+                w2(4);
+                break;
+
+        case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(4); w2(0); w2(4);
+                break;
+
+	case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+                w2(4); w2(0); w2(4);
+                break;
+
+        case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+                w2(4); w2(0); w2(4);
+                break;
+
+
+        }
+}
+
+static void kbic_write_block( PIA *pi, char * buf, int count )
+
+{       int     k;
+
+        switch (pi->mode) {
+
+        case 0:
+        case 1:
+        case 2: w0(0x90); w2(4); w2(6); w2(4); 
+		for(k=0;k<count/2;k++) {
+			w0(buf[2*k+1]); w2(0); w2(4); 
+			w0(buf[2*k]);   w2(5); w2(4); 
+		}
+		break;
+
+        case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+		for(k=0;k<count/2;k++) {
+			w4(buf[2*k+1]); 
+                        w4(buf[2*k]);
+                }
+		w2(4); w2(0); w2(4);
+		break;
+
+	case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for(k=0;k<count/2;k++) w4w(pi_swab16(buf,k));
+                w2(4); w2(0); w2(4);
+                break;
+
+        case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
+                for(k=0;k<count/4;k++) w4l(pi_swab32(buf,k));
+                w2(4); w2(0); w2(4);
+                break;
+
+        }
+
+}
+
+static void kbic_log_adapter( PIA *pi, char * scratch, 
+			      int verbose, char * chip )
+
+{       char    *mode_string[6] = {"4-bit","5/3","8-bit",
+				   "EPP-8","EPP_16","EPP-32"};
+
+        printk("%s: kbic %s, KingByte %s at 0x%x, ",
+                pi->device,KBIC_VERSION,chip,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void k951_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{	kbic_log_adapter(pi,scratch,verbose,"KBIC-951A");
+}
+
+static void k971_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       kbic_log_adapter(pi,scratch,verbose,"KBIC-971A");
+}
+
+static void kbic_inc_use ( void )
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void kbic_dec_use ( void )
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol k951 = {"k951",0,6,3,1,1,
+                           kbic_write_regr,
+                           kbic_read_regr,
+                           kbic_write_block,
+                           kbic_read_block,
+                           k951_connect,
+                           k951_disconnect,
+                           0,
+                           0,
+                           0,
+                           k951_log_adapter,
+                           kbic_inc_use, 
+                           kbic_dec_use 
+  			  };
+
+
+struct pi_protocol k971 = {"k971",0,6,3,1,1,
+                           kbic_write_regr,
+                           kbic_read_regr,
+                           kbic_write_block,
+                           kbic_read_block,
+                           k971_connect,
+                           k971_disconnect,
+                           0,
+                           0,
+                           0,
+                           k971_log_adapter,
+                           kbic_inc_use, 
+                           kbic_dec_use 
+                          };
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       int s5,s7;
+
+	s5 = pi_register(&k951);
+	s7 = pi_register(&k971);
+
+	return (s5 || s7) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &k951 );
+	pi_unregister( &k971 );
+}
+
+#endif
+
+/* end of kbic.c */
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/on20.c linux/drivers/block/paride/on20.c
--- v2.1.76/linux/drivers/block/paride/on20.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/on20.c	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,157 @@
+/* 
+	on20.c	(c) 1996  Grant R. Guenther <grant@torque.net>
+		          Under the terms of the GNU public license.
+
+        on20.c is a low-level protocol driver for the
+        Onspec 90c20 parallel to IDE adapter. 
+*/
+
+#define	ON20_VERSION	"1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define op(f)	w2(4);w0(f);w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4);
+#define vl(v)	w2(4);w0(v);w2(5);w2(7);w2(5);w2(4);
+
+#define j44(a,b)  (((a>>4)&0x0f)|(b&0xf0))
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int on20_read_regr( PIA *pi, int cont, int regr )
+
+{	int h,l, r ;
+
+        r = (regr<<2) + 1 + cont;
+
+        op(1); vl(r); op(0);
+
+	switch (pi->mode)  {
+
+        case 0:  w2(4); w2(6); l = r1();
+                 w2(4); w2(6); h = r1();
+                 w2(4); w2(6); w2(4); w2(6); w2(4);
+		 return j44(l,h);
+
+	case 1:  w2(4); w2(0x26); r = r0(); 
+                 w2(4); w2(0x26); w2(4);
+		 return r;
+
+	}
+	return -1;
+}	
+
+static void on20_write_regr( PIA *pi, int cont, int regr, int val )
+
+{	int r;
+
+	r = (regr<<2) + 1 + cont;
+
+	op(1); vl(r); 
+	op(0); vl(val); 
+	op(0); vl(val);
+}
+
+static void on20_connect ( PIA *pi)
+
+{	pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+
+	w2(4);w0(0);w2(0xc);w2(4);w2(6);w2(4);w2(6);w2(4); 
+	if (pi->mode) { op(2); vl(8); op(2); vl(9); }
+	       else   { op(2); vl(0); op(2); vl(8); }
+}
+
+static void on20_disconnect ( PIA *pi )
+
+{	w2(4);w0(7);w2(4);w2(0xc);w2(4);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void on20_read_block( PIA *pi, char * buf, int count )
+
+{	int     k, l, h; 
+
+	op(1); vl(1); op(0);
+
+	for (k=0;k<count;k++) 
+	    if (pi->mode) {
+		w2(4); w2(0x26); buf[k] = r0();
+	    } else {
+		w2(6); l = r1(); w2(4);
+		w2(6); h = r1(); w2(4);
+		buf[k] = j44(l,h);
+	    }
+	w2(4);
+}
+
+static void on20_write_block(  PIA *pi, char * buf, int count )
+
+{	int	k;
+
+	op(1); vl(1); op(0);
+
+	for (k=0;k<count;k++) { w2(5); w0(buf[k]); w2(7); }
+	w2(4);
+}
+
+static void on20_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[2] = {"4-bit","8-bit"};
+
+        printk("%s: on20 %s, OnSpec 90c20 at 0x%x, ",
+                pi->device,ON20_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void on20_inc_use ( void )
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void on20_dec_use ( void )
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol on20 = {"on20",0,2,2,1,1,
+                           on20_write_regr,
+                           on20_read_regr,
+                           on20_write_block,
+                           on20_read_block,
+                           on20_connect,
+                           on20_disconnect,
+                           0,
+                           0,
+                           0,
+                           on20_log_adapter,
+                           on20_inc_use, 
+                           on20_dec_use 
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &on20 ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &on20 );
+}
+
+#endif
+
+/* end of on20.c */
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/on26.c linux/drivers/block/paride/on26.c
--- v2.1.76/linux/drivers/block/paride/on26.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/on26.c	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,261 @@
+/* 
+        on26.c    (c) 1997  Grant R. Guenther <grant@torque.net>
+                            Under the terms of the GNU public license.
+
+        on26.c is a low-level protocol driver for the 
+        OnSpec 90c26 parallel to IDE adapter chip.
+
+*/
+
+#define ON26_VERSION      "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+/* mode codes:  0  nybble reads, 8-bit writes
+                1  8-bit reads and writes
+                2  8-bit EPP mode
+		3  EPP-16
+		4  EPP-32
+*/
+
+#define j44(a,b)  (((a>>4)&0x0f)|(b&0xf0))
+
+#define P1	w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4);
+#define P2	w2(5);w2(7);w2(5);w2(4);
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int on26_read_regr( PIA *pi, int cont, int regr )
+
+{       int     a, b, r;
+
+	r = (regr<<2) + 1 + cont;
+
+        switch (pi->mode)  {
+
+        case 0: w0(1); P1; w0(r); P2; w0(0); P1; 
+		w2(6); a = r1(); w2(4);
+		w2(6); b = r1(); w2(4);
+		w2(6); w2(4); w2(6); w2(4);
+                return j44(a,b);
+
+        case 1: w0(1); P1; w0(r); P2; w0(0); P1;
+		w2(0x26); a = r0(); w2(4); w2(0x26); w2(4);
+                return a;
+
+	case 2:
+	case 3:
+        case 4: w3(1); w3(1); w2(5); w4(r); w2(4);
+		w3(0); w3(0); w2(0x24); a = r4(); w2(4);
+		w2(0x24); r4(); w2(4);
+                return a;
+
+        }
+        return -1;
+}       
+
+static void on26_write_regr( PIA *pi, int cont, int regr, int val )
+
+{       int  r;
+
+        r = (regr<<2) + 1 + cont;
+
+        switch (pi->mode)  {
+
+        case 0:
+        case 1: w0(1); P1; w0(r); P2; w0(0); P1;
+		w0(val); P2; w0(val); P2;
+		break;
+
+	case 2:
+	case 3:
+        case 4: w3(1); w3(1); w2(5); w4(r); w2(4);
+		w3(0); w3(0); 
+		w2(5); w4(val); w2(4);
+		w2(5); w4(val); w2(4);
+                break;
+        }
+}
+
+#define  CCP(x)  w0(0xff);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
+		 w0(0x87);w0(0x78);w0(x);w2(4);
+
+static void on26_connect ( PIA *pi )
+
+{       int	x;
+
+	pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+
+        CCP(0x20);
+	w2(0xcd); w2(0xcc); w0(0xff);
+	x = 8; if (pi->mode) x = 9;
+
+	w0(2); P1; w0(8); P2;
+	w0(2); P1; w0(x); P2;
+}
+
+static void on26_disconnect ( PIA *pi )
+
+{       if (pi->mode >= 2) { w3(4); w3(4); w3(4); w3(4); }
+	              else { w0(4); P1; w0(4); P1; }
+	CCP(0x30);
+        w2(0xcd); w2(0xcc); w0(0xff);
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static void on26_read_block( PIA *pi, char * buf, int count )
+
+{       int     k, a, b;
+
+        switch (pi->mode) {
+
+        case 0: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x18); P2; w0(0); P1;
+		udelay(10);
+		for (k=0;k<count;k++) {
+                        w2(6); a = r1();
+                        w2(4); b = r1();
+                        buf[k] = j44(a,b);
+                }
+		w0(2); P1; w0(8); P2; 
+                break;
+
+        case 1: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x19); P2; w0(0); P1;
+		udelay(10);
+                for (k=0;k<count/2;k++) {
+                        w2(0x26); buf[2*k] = r0(); 
+			w2(0x24); buf[2*k+1] = r0();
+                }
+                w0(2); P1; w0(9); P2;
+                break;
+
+        case 2: w3(1); w3(1); w2(5); w4(1); w2(4);
+		w3(0); w3(0); w2(0x24);
+		udelay(10);
+                for (k=0;k<count;k++) buf[k] = r4();
+                w2(4);
+                break;
+
+        case 3: w3(1); w3(1); w2(5); w4(1); w2(4);
+                w3(0); w3(0); w2(0x24);
+                udelay(10);
+                for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
+                w2(4);
+                break;
+
+        case 4: w3(1); w3(1); w2(5); w4(1); w2(4);
+                w3(0); w3(0); w2(0x24);
+                udelay(10);
+                for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
+                w2(4);
+                break;
+
+        }
+}
+
+static void on26_write_block( PIA *pi, char * buf, int count )
+
+{       int	k;
+
+        switch (pi->mode) {
+
+        case 0: 
+        case 1: w0(1); P1; w0(1); P2; 
+		w0(2); P1; w0(0x18+pi->mode); P2; w0(0); P1;
+		udelay(10);
+		for (k=0;k<count/2;k++) {
+                        w2(5); w0(buf[2*k]); 
+			w2(7); w0(buf[2*k+1]);
+                }
+                w2(5); w2(4);
+		w0(2); P1; w0(8+pi->mode); P2;
+                break;
+
+        case 2: w3(1); w3(1); w2(5); w4(1); w2(4);
+		w3(0); w3(0); w2(0xc5);
+		udelay(10);
+                for (k=0;k<count;k++) w4(buf[k]);
+		w2(0xc4);
+                break;
+
+        case 3: w3(1); w3(1); w2(5); w4(1); w2(4);
+                w3(0); w3(0); w2(0xc5);
+                udelay(10);
+                for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+                w2(0xc4);
+                break;
+
+        case 4: w3(1); w3(1); w2(5); w4(1); w2(4);
+                w3(0); w3(0); w2(0xc5);
+                udelay(10);
+                for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+                w2(0xc4);
+                break;
+
+        }
+
+}
+
+static void on26_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[5] = {"4-bit","8-bit","EPP-8",
+				   "EPP-16","EPP-32"};
+
+        printk("%s: on26 %s, OnSpec 90c26 at 0x%x, ",
+                pi->device,ON26_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+}
+
+static void on26_inc_use ( void )
+
+{       MOD_INC_USE_COUNT;
+}
+
+static void on26_dec_use ( void )
+
+{       MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol on26 = {"on26",0,5,2,1,1,
+                           on26_write_regr,
+                           on26_read_regr,
+                           on26_write_block,
+                           on26_read_block,
+                           on26_connect,
+                           on26_disconnect,
+                           0,
+                           0,
+                           0,
+                           on26_log_adapter,
+                           on26_inc_use, 
+                           on26_dec_use 
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &on26 ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &on26 );
+}
+
+#endif
+
+/* end of on26.c */
+
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/paride.c linux/drivers/block/paride/paride.c
--- v2.1.76/linux/drivers/block/paride/paride.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/paride.c	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,480 @@
+/* 
+        paride.c  (c) 1997  Grant R. Guenther <grant@torque.net>
+                            Under the terms of the GNU public license.
+
+	This is the base module for the family of device drivers
+        that support parallel port IDE devices.  
+
+*/
+
+#define PI_VERSION      "1.0"
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+
+#ifdef CONFIG_PARPORT_MODULE
+#define CONFIG_PARPORT
+#endif
+
+#ifdef CONFIG_PARPORT
+#include <linux/parport.h>
+#endif
+
+#include "paride.h"
+
+#define MAX_PROTOS	32
+
+static struct pi_protocol	*protocols[MAX_PROTOS];
+
+
+void pi_write_regr( PIA *pi, int cont, int regr, int val)
+
+{	pi->proto->write_regr(pi,cont,regr,val);
+}
+
+int pi_read_regr( PIA *pi, int cont, int regr)
+
+{	return pi->proto->read_regr(pi,cont,regr);
+}
+
+void pi_write_block( PIA *pi, char * buf, int count)
+
+{	pi->proto->write_block(pi,buf,count);
+}
+
+void pi_read_block( PIA *pi, char * buf, int count)
+
+{       pi->proto->read_block(pi,buf,count);
+}
+
+#ifdef CONFIG_PARPORT
+
+static void pi_wake_up( void *p)
+
+{       PIA  *pi = (PIA *) p;
+	long flags;
+	void (*cont)(void) = NULL;
+
+	save_flags(flags);
+	cli();
+
+	if (pi->claim_cont && !parport_claim(pi->pardev)) {
+		cont = pi->claim_cont;
+		pi->claim_cont = NULL;
+		pi->claimed = 1;
+	}
+
+	restore_flags(flags);	
+	wake_up(&(pi->parq));
+	if (cont) cont();
+}
+
+#endif
+
+void pi_do_claimed( PIA *pi, void(*cont)(void))
+
+#ifdef CONFIG_PARPORT
+
+{	long flags;
+
+	save_flags(flags);
+	cli();
+
+	if (!pi->pardev || !parport_claim(pi->pardev)) {
+		pi->claimed = 1;
+		restore_flags(flags);
+		cont();
+	} else {
+		pi->claim_cont = cont;
+		restore_flags(flags);
+	}
+}
+
+#else
+
+{	cont();
+}
+
+#endif
+
+static void pi_claim( PIA *pi)
+
+{	if (pi->claimed) return;
+	pi->claimed = 1;
+#ifdef CONFIG_PARPORT
+        if (pi->pardev)
+          while (parport_claim((struct pardevice *)(pi->pardev)))
+            sleep_on(&(pi->parq));
+#endif
+}
+
+static void pi_unclaim( PIA *pi)
+
+{	pi->claimed = 0;
+#ifdef CONFIG_PARPORT
+        if (pi->pardev) parport_release((struct pardevice *)(pi->pardev));
+#endif 
+}
+
+void pi_connect( PIA *pi)
+
+{	pi_claim(pi);
+	pi->proto->connect(pi);
+}
+
+void pi_disconnect( PIA *pi)
+
+{	pi->proto->disconnect(pi);
+	pi_unclaim(pi);
+}
+
+static void pi_unregister_parport( PIA *pi)
+
+{
+#ifdef CONFIG_PARPORT
+        if (pi->pardev) {
+           parport_unregister_device((struct pardevice *)(pi->pardev));
+	   pi->pardev = NULL;
+	}
+#endif
+}
+
+void pi_release( PIA *pi)
+
+{	pi_unregister_parport(pi);
+	if ((!pi->pardev)&&(pi->reserved)) 
+		release_region(pi->port,pi->reserved);
+	pi->proto->dec_use();
+}
+
+#define WR(r,v)         pi_write_regr(pi,0,r,v)
+#define RR(r)           (pi_read_regr(pi,0,r))
+
+static int pi_test_proto( PIA *pi, char * scratch, int verbose )
+
+{       int     j, k;
+        int     e[2] = {0,0};
+
+	if (pi->proto->test_proto) {
+		pi_claim(pi);
+		j = pi->proto->test_proto(pi,scratch,verbose);
+		pi_unclaim(pi);
+		return j;
+	}
+
+        pi_connect(pi);
+
+        for (j=0;j<2;j++) {
+                WR(6,0xa0+j*0x10);
+                for (k=0;k<256;k++) {
+                        WR(2,k^0xaa);
+                        WR(3,k^0x55);
+                        if (RR(2) != (k^0xaa)) e[j]++;
+                        }
+                }
+
+        pi_disconnect(pi);
+
+        if (verbose)
+                printk("%s: %s: port 0x%x, mode  %d, test=(%d,%d)\n",
+                        pi->device,pi->proto->name,pi->port,
+			pi->mode,e[0],e[1]);
+
+        return (e[0] && e[1]);  /* not here if both > 0 */
+}
+
+int  pi_register( PIP *pr)
+
+{	int k;
+
+	for (k=0;k<MAX_PROTOS;k++) 
+	   if (protocols[k] && !strcmp(pr->name,protocols[k]->name)) {
+		printk("paride: %s protocol already registered\n",pr->name);
+		return 0;
+	   }
+	k = 0;
+	while((k<MAX_PROTOS) && (protocols[k])) k++;
+	if (k == MAX_PROTOS) {
+		printk("paride: protocol table full\n");
+		return 0;
+	}
+	MOD_INC_USE_COUNT;
+	protocols[k] = pr;
+	pr->index = k;
+	printk("paride: %s registered as protocol %d\n",pr->name,k);
+	return 1;
+}	
+
+void pi_unregister( PIP *pr)
+
+{	if (!pr) return;
+	if (protocols[pr->index] != pr) {
+		printk("paride: %s not registered\n",pr->name);
+		return;
+	} 
+	protocols[pr->index] = 0;
+	MOD_DEC_USE_COUNT;
+}
+
+static void pi_register_parport( PIA *pi, int verbose)
+
+{
+#ifdef CONFIG_PARPORT
+
+	struct parport	*pp;
+
+	pp = parport_enumerate();
+
+	while((pp)&&(pp->base != pi->port)) pp = pp->next;
+
+	if (!pp) return;
+
+	pi->pardev = (void *) parport_register_device(
+	      pp,pi->device,NULL,pi_wake_up,NULL,0,(void *)pi);
+
+	pi->parq = NULL;
+
+	if (verbose) printk("%s: 0x%x is %s\n",pi->device,pi->port,pp->name);
+	
+	pi->parname = pp->name;
+
+#endif
+}
+
+static int pi_probe_mode( PIA *pi, int max, char * scratch, int verbose)
+
+{	int	best, range;
+
+	if (pi->mode != -1) {
+		if (pi->mode >= max) return 0;
+		range = 3;
+		if (pi->mode >= pi->proto->epp_first) range = 8;
+		if ((range == 8) && (pi->port % 8)) return 0;
+		if ((!pi->pardev) && check_region(pi->port,range)) return 0;
+		pi->reserved = range;
+		return (!pi_test_proto(pi,scratch,verbose));
+	}
+	best = -1;
+	for(pi->mode=0;pi->mode<max;pi->mode++) {
+		range = 3;
+		if (pi->mode >= pi->proto->epp_first) range = 8;
+		if ((range == 8) && (pi->port % 8)) break;
+		if ((!pi->pardev) && check_region(pi->port,range)) break;
+		pi->reserved = range;
+		if (!pi_test_proto(pi,scratch,verbose)) best = pi->mode;
+	}
+	pi->mode = best;
+	return (best > -1);
+}
+
+static int pi_probe_unit( PIA *pi, int unit, char * scratch, int verbose)
+
+{	int max,s,e;
+
+	s = unit; e = s+1;
+
+	if (s == -1) { 
+		s = 0; 
+		e = pi->proto->max_units; 
+	}
+
+	pi_register_parport(pi,verbose);
+
+	if ((!pi->pardev) && check_region(pi->port,3)) return 0;
+
+	if (pi->proto->test_port) {
+		pi_claim(pi);
+		max = pi->proto->test_port(pi);
+		pi_unclaim(pi);
+	}
+	else max = pi->proto->max_mode;
+
+	if (pi->proto->probe_unit) {
+	   pi_claim(pi);
+	   for (pi->unit=s;pi->unit<e;pi->unit++)
+	      if (pi->proto->probe_unit(pi)) {
+		 pi_unclaim(pi);
+		 if (pi_probe_mode(pi,max,scratch,verbose)) return 1;
+	         pi_unregister_parport(pi); 
+		 return 0;
+	         }
+	   pi_unclaim(pi);
+	   pi_unregister_parport(pi); 
+	   return 0;
+	   }
+
+	if (!pi_probe_mode(pi,max,scratch,verbose)) {
+	   pi_unregister_parport(pi); 
+	   return 0;
+	}
+	return 1;
+		
+}
+
+int pi_init(PIA *pi, int autoprobe, int port, int mode, 
+	    int unit, int protocol, int delay, char * scratch, 
+	    int devtype, int verbose, char *device )
+
+{	int p,k,s,e;
+	int lpts[7] = {0x3bc,0x378,0x278,0x268,0x27c,0x26c,0};
+
+	s = protocol; e = s+1;
+
+	if (autoprobe) {
+		s = 0; 
+		e = MAX_PROTOS;
+	} else if ((s < 0) || (s >= MAX_PROTOS) || (port <= 0) ||
+		    (!protocols[s]) || (unit < 0) || 
+		    (unit >= protocols[s]->max_units)) {
+			printk("%s: Invalid parameters\n",device);
+			return 0;
+		        }
+
+	for (p=s;p<e;p++) {
+	  if (protocols[p]) {
+		pi->proto = protocols[p];
+		pi->proto->inc_use();
+		if (delay == -1) pi->delay = pi->proto->default_delay;
+		  else pi->delay = delay;
+		pi->devtype = devtype;
+		pi->device = device;
+		pi->private = 0;
+
+		pi->parname = NULL;
+		pi->pardev = NULL;
+		pi->parq = NULL;
+		pi->claimed = 0;
+		pi->claim_cont = NULL;
+	        
+		pi->mode = mode;
+		if (port != -1) {
+			pi->port = port;
+			if (pi_probe_unit(pi,unit,scratch,verbose)) break;
+			pi->port = 0;
+		} else { 
+			k = 0;
+			while ((pi->port = lpts[k++]))
+			   if (pi_probe_unit(pi,unit,scratch,verbose)) break;
+      			if (pi->port) break;
+		}
+		pi->proto->dec_use();
+	  }
+	}
+
+	if (!pi->port) {
+		if (autoprobe) printk("%s: Autoprobe failed\n",device);
+		else printk("%s: Adapter not found\n",device);
+		return 0;
+	}
+
+	if (!pi->pardev)
+	   request_region(pi->port,pi->reserved,pi->device);
+
+	if (pi->parname)
+	   printk("%s: Sharing %s at 0x%x\n",pi->device,
+			pi->parname,pi->port);
+
+	pi->proto->log_adapter(pi,scratch,verbose);
+
+	return 1;
+}
+
+#ifdef MODULE
+
+int	init_module(void)
+
+{	int k;
+
+	for (k=0;k<MAX_PROTOS;k++) protocols[k] = 0;
+	printk("paride: version %s installed\n",PI_VERSION);
+	return 0;
+}
+
+void	cleanup_module(void)
+
+{
+}
+
+#else
+
+void	paride_init( void )
+
+{
+
+#ifdef CONFIG_PARIDE_ATEN
+	{ extern struct pi_protocol aten;
+	  pi_register(&aten);
+	};
+#endif
+#ifdef CONFIG_PARIDE_BPCK
+        { extern struct pi_protocol bpck;
+          pi_register(&bpck);
+        };
+#endif
+#ifdef CONFIG_PARIDE_COMM
+        { extern struct pi_protocol comm;
+          pi_register(&comm);
+        };
+#endif
+#ifdef CONFIG_PARIDE_DSTR
+        { extern struct pi_protocol dstr;
+          pi_register(&dstr);
+        };
+#endif
+#ifdef CONFIG_PARIDE_EPAT
+        { extern struct pi_protocol epat;
+          pi_register(&epat);
+        };
+#endif
+#ifdef CONFIG_PARIDE_EPIA
+        { extern struct pi_protocol epia;
+          pi_register(&epia);
+        };
+#endif
+#ifdef CONFIG_PARIDE_FRPW
+        { extern struct pi_protocol frpw;
+          pi_register(&frpw);
+        };
+#endif
+#ifdef CONFIG_PARIDE_KBIC
+        { extern struct pi_protocol k951;
+          extern struct pi_protocol k971;
+          pi_register(&k951);
+          pi_register(&k971);
+        };
+#endif
+#ifdef CONFIG_PARIDE_ON20
+        { extern struct pi_protocol on20;
+          pi_register(&on20);
+        };
+#endif
+#ifdef CONFIG_PARIDE_ON26
+        { extern struct pi_protocol on26;
+          pi_register(&on26);
+        };
+#endif
+
+#ifdef CONFIG_PARIDE_PD
+	{ extern int pd_init(void);
+	  pd_init();
+	};
+#endif
+#ifdef CONFIG_PARIDE_PCD
+        { extern int pcd_init(void);
+          pcd_init();
+        };
+#endif
+#ifdef CONFIG_PARIDE_PF
+        { extern int pf_init(void);
+          pf_init();
+        };
+#endif
+}
+
+#endif
+
+/* end of paride.c */
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/paride.h linux/drivers/block/paride/paride.h
--- v2.1.76/linux/drivers/block/paride/paride.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/paride.h	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,157 @@
+/* paride.h	(c) 1997  Grant R. Guenther <grant@torque.net>
+   		          Under the terms of the GPL.
+
+   This file defines the interface between the high-level parallel
+   IDE device drivers (pd, pf, pcd, pt) and the adapter chips.
+
+*/
+
+#define PARIDE_H_VERSION 	"1.0"
+
+/* Some adapters need to know what kind of device they are in
+
+   Values for devtype:
+*/
+
+#define	PI_PD	0	/* IDE disk */
+#define PI_PCD	1	/* ATAPI CDrom */
+#define PI_PF   2	/* ATAPI disk */
+#define PI_PT	3	/* ATAPI tape */
+
+/* The paride module contains no state, instead the drivers allocate
+   a pi_adapter data structure and pass it to paride in every operation.
+
+*/
+
+struct pi_adapter  {
+
+	struct pi_protocol *proto;   /* adapter protocol */
+	int	port;		     /* base address of parallel port */
+	int	mode;		     /* transfer mode in use */
+	int     delay;		     /* adapter delay setting */
+	int	devtype;	     /* device type: PI_PD etc. */
+	char    *device;	     /* name of driver */
+	int     unit;		     /* unit number for chained adapters */
+	int	saved_r0;	     /* saved port state */
+	int	saved_r2;	     /* saved port state */
+	int	reserved;	     /* number of ports reserved */
+	int	private;	     /* for protocol module */
+
+	struct wait_queue *parq;     /* semaphore for parport sharing */
+	void	*pardev;	     /* pointer to pardevice */
+	char	*parname;	     /* parport name */
+	int	claimed;	     /* parport has already been claimed */
+	void (*claim_cont)(void);    /* continuation for parport wait */
+};
+
+typedef struct pi_adapter PIA;
+
+/* functions exported by paride to the high level drivers */
+
+extern int pi_init(PIA *pi, 
+	int autoprobe,		/* 1 to autoprobe */
+	int port, 		/* base port address */
+	int mode, 		/* -1 for autoprobe */
+	int unit,		/* unit number, if supported */
+	int protocol, 		/* protocol to use */
+	int delay, 		/* -1 to use adapter specific default */
+	char * scratch, 	/* address of 512 byte buffer */
+	int devtype,		/* device type: PI_PD, PI_PCD, etc ... */
+	int verbose,		/* log verbose data while probing */
+	char *device		/* name of the driver */
+	);			/* returns 0 on failure, 1 on success */
+
+extern void pi_release(PIA *pi);
+
+/* registers are addressed as (cont,regr)
+
+       	cont: 0 for command register file, 1 for control register(s)
+	regr: 0-7 for register number.
+
+*/
+
+extern void pi_write_regr(PIA *pi, int cont, int regr, int val);
+
+extern int pi_read_regr(PIA *pi, int cont, int regr);
+
+extern void pi_write_block(PIA *pi, char * buf, int count);
+
+extern void pi_read_block(PIA *pi, char * buf, int count);
+
+extern void pi_connect(PIA *pi);
+
+extern void pi_disconnect(PIA *pi);
+
+extern void pi_do_claimed(PIA *pi, void (*cont)(void));
+
+/* macros and functions exported to the protocol modules */
+
+#define delay_p			(pi->delay?udelay(pi->delay):0)
+#define out_p(offs,byte)	outb(byte,pi->port+offs); delay_p;
+#define in_p(offs)		(delay_p,inb(pi->port+offs))
+
+#define w0(byte)                {out_p(0,byte);}
+#define r0()                    (in_p(0) & 0xff)
+#define w1(byte)                {out_p(1,byte);}
+#define r1()                    (in_p(1) & 0xff)
+#define w2(byte)                {out_p(2,byte);}
+#define r2()                    (in_p(2) & 0xff)
+#define w3(byte)                {out_p(3,byte);}
+#define w4(byte)                {out_p(4,byte);}
+#define r4()                    (in_p(4) & 0xff)
+#define w4w(data)     		{outw(data,pi->port+4); delay_p;}
+#define w4l(data)     		{outl(data,pi->port+4); delay_p;}
+#define r4w()         		(delay_p,inw(pi->port+4)&0xffff)
+#define r4l()         		(delay_p,inl(pi->port+4)&0xffffffff)
+
+static inline u16 pi_swab16( char *b, int k)
+
+{ 	union { u16 u; char t[2]; } r;
+
+	r.t[0]=b[2*k+1]; r.t[1]=b[2*k];
+        return r.u;
+}
+
+static inline u32 pi_swab32( char *b, int k)
+
+{ 	union { u32 u; char f[4]; } r;
+
+	r.f[0]=b[4*k+1]; r.f[1]=b[4*k];
+	r.f[2]=b[4*k+3]; r.f[3]=b[4*k+2];
+        return r.u;
+}
+
+struct pi_protocol {
+
+	char	name[8];	/* name for this protocol */
+	int	index;		/* index into protocol table */
+
+	int	max_mode;	/* max mode number */
+	int	epp_first;	/* modes >= this use 8 ports */
+	
+	int	default_delay;  /* delay parameter if not specified */
+	int	max_units;	/* max chained units probed for */
+
+	void (*write_regr)(PIA *,int,int,int);
+	int  (*read_regr)(PIA *,int,int);
+	void (*write_block)(PIA *,char *,int);
+	void (*read_block)(PIA *,char *,int);
+
+	void (*connect)(PIA *);
+	void (*disconnect)(PIA *);
+	
+	int  (*test_port)(PIA *);
+	int  (*probe_unit)(PIA *);
+	int  (*test_proto)(PIA *,char *,int);
+	void (*log_adapter)(PIA *,char *,int);
+	
+	void (*inc_use)(void);
+	void (*dec_use)(void);
+};
+
+typedef struct pi_protocol PIP;
+
+extern int pi_register( PIP * );
+extern void pi_unregister ( PIP * );
+
+/* end of paride.h */
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/pcd.c linux/drivers/block/paride/pcd.c
--- v2.1.76/linux/drivers/block/paride/pcd.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/pcd.c	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,786 @@
+/* 
+	pcd.c	(c) 1997  Grant R. Guenther <grant@torque.net>
+		          Under the terms of the GNU public license.
+
+	This is high-level driver for parallel port ATAPI CDrom
+        drives based on chips supported by the paride module.
+
+        By default, the driver will autoprobe for a single parallel
+        port ATAPI CDrom drive, but if their individual parameters are
+        specified, the driver can handle up to 4 drives.
+
+        The behaviour of the pcd driver can be altered by setting
+        some parameters from the insmod command line.  The following
+        parameters are adjustable:
+
+            drive0      These four arguments can be arrays of       
+            drive1      1-6 integers as follows:
+            drive2
+            drive3      <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
+
+                        Where,
+
+                <prt>   is the base of the parallel port address for
+                        the corresponding drive.  (required)
+
+                <pro>   is the protocol number for the adapter that
+                        supports this drive.  These numbers are
+                        logged by 'paride' when the protocol modules
+                        are initialised.  (0 if not given)
+
+                <uni>   for those adapters that support chained
+                        devices, this is the unit selector for the
+                        chain of devices on the given port.  It should
+                        be zero for devices that don't support chaining.
+                        (0 if not given)
+
+                <mod>   this can be -1 to choose the best mode, or one
+                        of the mode numbers supported by the adapter.
+                        (-1 if not given)
+
+		<slv>   ATAPI CDroms can be jumpered to master or slave.
+			Set this to 0 to choose the master drive, 1 to
+                        choose the slave, -1 (the default) to choose the
+			first drive found.
+
+                <dly>   some parallel ports require the driver to 
+                        go more slowly.  -1 sets a default value that
+                        should work with the chosen protocol.  Otherwise,
+                        set this to a small integer, the larger it is
+                        the slower the port i/o.  In some cases, setting
+                        this to zero will speed up the device. (default -1)
+                        
+            major       You may use this parameter to overide the
+                        default major number (46) that this driver
+                        will use.  Be sure to change the device
+                        name as well.
+
+            name        This parameter is a character string that
+                        contains the name the kernel will use for this
+                        device (in /proc output, for instance).
+                        (default "pcd")
+
+            verbose     This parameter controls the amount of logging
+                        that is done while the driver probes for
+                        devices.  Set it to 0 for a quiet load, or 1 to
+                        see all the progress messages.  (default 0)
+
+            nice        This parameter controls the driver's use of
+                        idle CPU time, at the expense of some speed.
+ 
+	If this driver is built into the kernel, you can use kernel
+        the following command line parameters, with the same values
+        as the corresponding module parameters listed above:
+
+	    pcd.drive0
+	    pcd.drive1
+	    pcd.drive2
+	    pcd.drive3
+	    pcd.nice
+
+        In addition, you can use the parameter pcd.disable to disable
+        the driver entirely.
+
+*/
+
+#define	PCD_VERSION	"1.0"
+#define PCD_MAJOR	46
+#define PCD_NAME	"pcd"
+#define PCD_UNITS	4
+
+/* Here are things one can override from the insmod command.
+   Most are autoprobed by paride unless set here.  Verbose is on
+   by default.
+
+*/
+
+static int      verbose = 0;
+static int      major = PCD_MAJOR;
+static char     *name = PCD_NAME;
+static int      nice = 0;
+static int      disable = 0;
+
+static int drive0[6] = {0,0,0,-1,-1,-1};
+static int drive1[6] = {0,0,0,-1,-1,-1};
+static int drive2[6] = {0,0,0,-1,-1,-1};
+static int drive3[6] = {0,0,0,-1,-1,-1};
+
+static int (*drives[4])[6] = {&drive0,&drive1,&drive2,&drive3};
+static int pcd_drive_count;
+
+#define D_PRT   0
+#define D_PRO   1
+#define D_UNI   2
+#define D_MOD   3
+#define D_SLV   4
+#define D_DLY   5
+
+#define DU              (*drives[unit])
+
+/* end of parameters */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/cdrom.h>
+
+#include <asm/uaccess.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pcd_stt[6] = {{"drive0",6,drive0},
+                         {"drive1",6,drive1},
+                         {"drive2",6,drive2},
+                         {"drive3",6,drive3},
+			 {"disable",1,&disable},
+                         {"nice",1,&nice}};
+
+void pcd_setup( char *str, int *ints)
+
+{       generic_setup(pcd_stt,6,str);
+}
+
+#endif
+
+MODULE_PARM(verbose,"i");
+MODULE_PARM(major,"i");
+MODULE_PARM(name,"s");
+MODULE_PARM(nice,"i");
+MODULE_PARM(drive0,"1-6i");
+MODULE_PARM(drive1,"1-6i");
+MODULE_PARM(drive2,"1-6i");
+MODULE_PARM(drive3,"1-6i");
+
+#include "paride.h"
+
+/* set up defines for blk.h,  why don't all drivers do it this way ? */
+
+#define MAJOR_NR	major
+#define DEVICE_NAME "PCD"
+#define DEVICE_REQUEST do_pcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#include "pseudo.h"
+
+#define PCD_RETRIES	     5
+#define PCD_TMO		   800		/* timeout in jiffies */
+#define PCD_DELAY           50          /* spin delay in uS */
+
+#define PCD_SPIN		(10000/PCD_DELAY)*PCD_TMO
+
+#define IDE_ERR		0x01
+#define IDE_DRQ         0x08
+#define IDE_READY       0x40
+#define IDE_BUSY        0x80
+
+int pcd_init(void);
+void cleanup_module( void );
+
+static int 	pcd_open(struct inode *inode, struct file *file);
+static void 	do_pcd_request(void);
+static void 	do_pcd_read(int unit);
+static int 	pcd_ioctl(struct inode *inode,struct file *file,
+                         unsigned int cmd, unsigned long arg);
+
+static int pcd_release (struct inode *inode, struct file *file);
+
+static int 	pcd_detect(void);
+static void     pcd_lock(int unit);
+static void     pcd_unlock(int unit);
+static void     pcd_eject(int unit);
+static int      pcd_check_media(int unit);
+static void     do_pcd_read_drq(void);
+
+static int pcd_blocksizes[PCD_UNITS];
+
+#define PCD_NAMELEN	8
+
+struct pcd_unit {
+	struct pi_adapter pia;	/* interface to paride layer */
+	struct pi_adapter *pi;
+	int drive;		/* master/slave */
+	int access;		/* count of active opens */
+	int present;		/* does this unit exist ? */
+	char name[PCD_NAMELEN];	/* pcd0, pcd1, etc */
+	};
+
+struct pcd_unit pcd[PCD_UNITS];
+
+/*  'unit' must be defined in all functions - either as a local or a param */
+
+#define PCD pcd[unit]
+#define PI PCD.pi
+
+static char pcd_scratch[64];
+static char pcd_buffer[2048];           /* raw block buffer */
+static int pcd_bufblk = -1;             /* block in buffer, in CD units,
+                                           -1 for nothing there. See also
+					   pd_unit.
+					 */
+
+/* the variables below are used mainly in the I/O request engine, which
+   processes only one request at a time.
+*/
+
+static int pcd_unit = -1;		/* unit of current request & bufblk */
+static int pcd_retries;			/* retries on current request */
+static int pcd_busy = 0;		/* request being processed ? */
+static int pcd_sector;			/* address of next requested sector */
+static int pcd_count;			/* number of blocks still to do */
+static char * pcd_buf;			/* buffer for request in progress */
+
+/* kernel glue structures */
+
+static struct file_operations pcd_fops = {
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	pcd_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	pcd_open,		/* open */
+	pcd_release,		/* release */
+	block_fsync,		/* fsync */
+	NULL,			/* fasync */
+	NULL,                   /* media change ? */
+	NULL			/* revalidate new media */
+};
+
+static void pcd_init_units( void )
+
+{       int     unit, j;
+
+        pcd_drive_count = 0;
+        for (unit=0;unit<PCD_UNITS;unit++) {
+                PCD.pi = & PCD.pia;
+                PCD.access = 0;
+                PCD.present = 0;
+                j = 0;
+                while ((j < PCD_NAMELEN-2) && (PCD.name[j]=name[j])) j++;
+                PCD.name[j++] = '0' + unit;
+                PCD.name[j] = 0;
+                PCD.drive = DU[D_SLV];
+                if (DU[D_PRT]) pcd_drive_count++;
+        }
+}
+
+int pcd_init (void)	/* preliminary initialisation */
+
+{       int 	i;
+
+	if (disable) return -1;
+
+	pcd_init_units();
+
+	if (pcd_detect()) return -1;
+
+	if (register_blkdev(MAJOR_NR,name,&pcd_fops)) {
+		printk("pcd: unable to get major number %d\n",MAJOR_NR);
+		return -1;
+	}
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 8;	/* 8 sector (4kB) read ahead */
+
+	for (i=0;i<PCD_UNITS;i++) pcd_blocksizes[i] = 1024;
+        blksize_size[MAJOR_NR] = pcd_blocksizes;
+
+	return 0;
+}
+
+static int pcd_open (struct inode *inode, struct file *file)
+
+{	int unit = DEVICE_NR(inode->i_rdev);
+
+	if  ((unit >= PCD_UNITS) || (!PCD.present)) return -ENODEV;
+
+	if (file->f_mode & 2) return -EROFS;  /* wants to write ? */
+
+	MOD_INC_USE_COUNT;
+
+	if (pcd_check_media(unit)) {
+		MOD_DEC_USE_COUNT;
+		return -ENXIO;
+	}
+
+	pcd_lock(unit);
+
+	PCD.access++;
+	return 0;
+}
+
+static void do_pcd_request (void)
+
+{       int unit;
+
+	if (pcd_busy) return;
+        while (1) {
+	    if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+	    INIT_REQUEST;
+	    if (CURRENT->cmd == READ) {
+		unit = MINOR(CURRENT->rq_dev);
+		if (unit != pcd_unit) {
+			pcd_bufblk = -1;
+			pcd_unit = unit;
+		}
+	        pcd_sector = CURRENT->sector;
+	        pcd_count = CURRENT->nr_sectors;
+	        pcd_buf = CURRENT->buffer;
+	        do_pcd_read(unit);
+		if (pcd_busy) return;
+	    } 
+	    else end_request(0);
+	}
+}
+
+static int pcd_ioctl(struct inode *inode,struct file *file,
+		    unsigned int cmd, unsigned long arg)
+
+/* we currently support only the EJECT ioctl. */
+
+{	int unit = DEVICE_NR(inode->i_rdev);
+	if  ((unit >= PCD_UNITS) || (!PCD.present)) return -ENODEV;
+
+	switch (cmd) {
+            case CDROMEJECT: if (PCD.access == 1) {
+				pcd_eject(unit);
+				return 0;
+			     }
+	    default:
+	        return -EINVAL;
+	}
+}
+
+static int pcd_release (struct inode *inode, struct file *file)
+
+{	kdev_t	devp;
+	int 	unit;
+
+        struct super_block *sb;
+
+	devp = inode->i_rdev;
+	unit = DEVICE_NR(devp);
+
+	if  ((unit >= PCD_UNITS) || (PCD.access <= 0)) 
+			return -EINVAL;
+       
+	PCD.access--;
+
+	if (!PCD.access) { 
+		fsync_dev(devp);
+
+                sb = get_super(devp);
+                if (sb) invalidate_inodes(sb);
+
+	    	invalidate_buffers(devp);
+	    	pcd_unlock(unit);
+
+	}
+
+	MOD_DEC_USE_COUNT;
+
+        return 0;
+
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+int	init_module(void)
+
+{	int	err;
+	long    flags;
+
+	save_flags(flags);
+	cli();
+
+	err = pcd_init();
+
+	restore_flags(flags);
+	return err;
+}
+
+void	cleanup_module(void)
+
+{	long flags;
+	int unit;
+
+	save_flags(flags);
+	cli();
+	unregister_blkdev(MAJOR_NR,name);
+	
+        for (unit=0;unit<PCD_UNITS;unit++) 
+           if (PCD.present) pi_release(PI);
+
+	restore_flags(flags);
+}
+
+#endif
+
+#define WR(c,r,v)       pi_write_regr(PI,c,r,v)
+#define RR(c,r)         (pi_read_regr(PI,c,r))
+
+static int pcd_wait( int unit, int go, int stop, char * fun, char * msg )
+
+{	int j, r, e, s, p;
+
+	j = 0;
+	while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PCD_SPIN))
+		udelay(PCD_DELAY);
+
+	if ((r&(IDE_ERR&stop))||(j>=PCD_SPIN)) {
+	   s = RR(0,7);
+	   e = RR(0,1);
+	   p = RR(0,2);
+       	   if (j >= PCD_SPIN) e |= 0x100;
+           if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
+			   " loop=%d phase=%d\n",
+			    PCD.name,fun,msg,r,s,e,j,p);
+	   return (s<<8)+r;
+	}
+	return 0;
+}
+
+static int pcd_command( int unit, char * cmd, int dlen, char * fun )
+
+{	pi_connect(PI);
+
+        WR(0,6,0xa0 + 0x10*PCD.drive);
+
+	if (pcd_wait(unit,IDE_BUSY|IDE_DRQ,0,fun,"before command")) {
+		pi_disconnect(PI);
+		return -1;
+	}
+
+        WR(0,4,dlen % 256);
+        WR(0,5,dlen / 256);
+        WR(0,7,0xa0);          /* ATAPI packet command */
+
+        if (pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_ERR,fun,"command DRQ")) {
+		pi_disconnect(PI);
+		return -1;
+	}
+
+        if (RR(0,2) != 1) {
+           printk("%s: %s: command phase error\n",PCD.name,fun);
+	   pi_disconnect(PI);
+           return -1;
+        }
+
+	pi_write_block(PI,cmd,12);
+
+	return 0;
+}
+
+static int pcd_completion( int unit, char * buf,  char * fun )
+
+{	int r, s, n;
+
+	r = pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_READY|IDE_ERR,fun,"completion");
+
+	if ((RR(0,2)&2) && (RR(0,7)&IDE_DRQ)) { 
+	        n = (RR(0,4)+256*RR(0,5));
+	        pi_read_block(PI,buf,n);
+	}
+
+	s = pcd_wait(unit,IDE_BUSY,IDE_READY|IDE_ERR,fun,"data done");
+
+	pi_disconnect(PI); 
+
+	return (r?r:s);
+}
+
+static void pcd_req_sense( int unit, int quiet )
+
+{	char	rs_cmd[12] = { 0x03,0,0,0,16,0,0,0,0,0,0,0 };
+	char	buf[16];
+	int 	r;
+
+	r = pcd_command(unit,rs_cmd,16,"Request sense");
+	udelay(1000);
+	if (!r) pcd_completion(unit,buf,"Request sense");
+
+        if ((!r)&&(!quiet)) 
+		printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
+	               PCD.name,buf[2]&0xf,buf[12],buf[13]);
+}
+
+static int pcd_atapi( int unit, char * cmd, int dlen, char * buf, char * fun )
+
+{	int r;
+
+	r = pcd_command(unit,cmd,dlen,fun);
+	udelay(1000);
+	if (!r) r = pcd_completion(unit,buf,fun);
+	if (r) pcd_req_sense(unit,!fun);
+	
+	return r;
+}
+
+#define DBMSG(msg)	NULL
+
+static void pcd_lock(int unit)
+
+{	char	lo_cmd[12] = { 0x1e,0,0,0,1,0,0,0,0,0,0,0 };
+	char	cl_cmd[12] = { 0x1b,0,0,0,3,0,0,0,0,0,0,0 };
+
+	pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd1")); 
+	pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd2"));
+	pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd3"));
+	pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd4"));
+	pcd_atapi(unit,cl_cmd,0,pcd_scratch,"close door");
+
+        pcd_atapi(unit,lo_cmd,0,pcd_scratch,DBMSG("ld"));
+        pcd_atapi(unit,lo_cmd,0,pcd_scratch,"lock door");
+}
+
+static void pcd_unlock( int unit )
+
+{	char	un_cmd[12] = { 0x1e,0,0,0,0,0,0,0,0,0,0,0 };
+
+	pcd_atapi(unit,un_cmd,0,pcd_scratch,"unlock door");
+}
+
+static void pcd_eject( int unit)
+
+{	char	ej_cmd[12] = { 0x1b,0,0,0,2,0,0,0,0,0,0,0 };
+
+	pcd_unlock(unit);
+	pcd_atapi(unit,ej_cmd,0,pcd_scratch,"eject");
+}
+
+#define PCD_RESET_TMO	30		/* in tenths of a second */
+
+static void pcd_sleep( int cs )
+
+{       current->state = TASK_INTERRUPTIBLE;
+        current->timeout = jiffies + cs;
+        schedule();
+}
+
+static int pcd_reset( int unit )
+
+/* the ATAPI standard actually specifies the contents of all 7 registers
+   after a reset, but the specification is ambiguous concerning the last
+   two bytes, and different drives interpret the standard differently.
+*/
+
+{	int	i, k, flg;
+	int	expect[5] = {1,1,1,0x14,0xeb};
+	long	flags;
+
+	pi_connect(PI);
+	WR(0,6,0xa0 + 0x10*PCD.drive);
+	WR(0,7,8);
+
+	save_flags(flags);
+	sti();
+
+	pcd_sleep(2);  		/* delay a bit*/
+
+	k = 0;
+	while ((k++ < PCD_RESET_TMO) && (RR(1,6)&IDE_BUSY))
+		pcd_sleep(10);
+
+	restore_flags(flags);
+
+	flg = 1;
+	for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+	if (verbose) {
+		printk("%s: Reset (%d) signature = ",PCD.name,k);
+		for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+		if (!flg) printk(" (incorrect)");
+		printk("\n");
+	}
+	
+	pi_disconnect(PI);
+	return flg-1;	
+}
+
+static int pcd_check_media( int unit )
+
+{	char    rc_cmd[12] = { 0x25,0,0,0,0,0,0,0,0,0,0,0};
+
+	pcd_atapi(unit,rc_cmd,8,pcd_scratch,DBMSG("cm1"));
+	pcd_atapi(unit,rc_cmd,8,pcd_scratch,DBMSG("cm2"));
+	return (pcd_atapi(unit,rc_cmd,8,pcd_scratch,DBMSG("cm3")));
+}
+
+static int pcd_identify( int unit, char * id )
+
+{	int k, s;
+	char   id_cmd[12] = {0x12,0,0,0,36,0,0,0,0,0,0,0};
+
+	pcd_bufblk = -1;
+
+        s = pcd_atapi(unit,id_cmd,36,pcd_buffer,"identify");
+
+	if (s) return -1;
+	if ((pcd_buffer[0] & 0x1f) != 5) {
+	  if (verbose) printk("%s: %s is not a CDrom\n",
+			PCD.name,PCD.drive?"Slave":"Master");
+	  return -1;
+	}
+	for (k=0;k<16;k++) id[k] = pcd_buffer[16+k]; id[16] = 0;
+	k = 16; while ((k >= 0) && (id[k] <= 0x20)) { id[k] = 0; k--; }
+
+	printk("%s: %s: %s\n",PCD.name,PCD.drive?"Slave":"Master",id);
+
+	return 0;
+}
+
+static int pcd_probe( int unit, int ms, char * id )
+
+/*	returns  0, with id set if drive is detected
+	        -1, if drive detection failed
+*/
+
+{	if (ms == -1) {
+            for (PCD.drive=0;PCD.drive<=1;PCD.drive++)
+	       if (!pcd_reset(unit) && !pcd_identify(unit,id)) 
+		  return 0;
+	} else {
+	    PCD.drive = ms;
+            if (!pcd_reset(unit) && !pcd_identify(unit,id)) 
+		return 0;
+	}
+	return -1;
+}
+
+static int pcd_detect( void )
+
+{	char    id[18];
+	int	k, unit;
+
+	printk("%s: %s version %s, major %d, nice %d\n",
+		name,name,PCD_VERSION,major,nice);
+
+	k = 0;
+	if (pcd_drive_count == 0) {  /* nothing spec'd - so autoprobe for 1 */
+	    unit = 0;
+	    if (pi_init(PI,1,-1,-1,-1,-1,-1,pcd_buffer,
+                     PI_PCD,verbose,PCD.name)) {
+		if (!pcd_probe(unit,-1,id)) {
+			PCD.present = 1;
+			k++;
+		} else pi_release(PI);
+	    }
+
+	} else for (unit=0;unit<PCD_UNITS;unit++) if (DU[D_PRT])
+	    if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+                        DU[D_PRO],DU[D_DLY],pcd_buffer,PI_PCD,verbose,
+                        PCD.name)) {
+		if (!pcd_probe(unit,DU[D_SLV],id)) {
+                        PCD.present = 1;
+                        k++;
+                } else pi_release(PI);
+            }
+
+	if (k) return 0;
+	
+	printk("%s: No CDrom drive found\n",name);
+	return -1;
+}
+
+/* I/O request processing */
+
+static int pcd_ready( void )
+
+{	int	unit = pcd_unit;
+
+	return (((RR(1,6)&(IDE_BUSY|IDE_DRQ))==IDE_DRQ)) ;
+}
+
+static void pcd_transfer( void )
+
+{	int	k, o;
+
+	while (pcd_count && (pcd_sector/4 == pcd_bufblk)) {
+		o = (pcd_sector % 4) * 512;
+		for(k=0;k<512;k++) pcd_buf[k] = pcd_buffer[o+k];
+		pcd_count--;
+		pcd_buf += 512;
+		pcd_sector++;
+	}
+}
+
+static void pcd_start( void )
+
+{	int	unit = pcd_unit;
+	int	b, i;
+	char	rd_cmd[12] = {0xa8,0,0,0,0,0,0,0,0,1,0,0};
+
+	pcd_bufblk = pcd_sector / 4;
+        b = pcd_bufblk;
+	for(i=0;i<4;i++) { 
+	   rd_cmd[5-i] = b & 0xff;
+	   b = b >> 8;
+	}
+
+
+	if (pcd_command(unit,rd_cmd,2048,"read block")) {
+		pcd_bufblk = -1; 
+		pcd_busy = 0;
+		cli();
+		end_request(0);
+		do_pcd_request();
+		return;
+	}
+
+	udelay(1000);
+
+	ps_set_intr(do_pcd_read_drq,pcd_ready,PCD_TMO,nice);
+
+}
+
+static void do_pcd_read( int unit )
+
+{	pcd_busy = 1;
+	pcd_retries = 0;
+	pcd_transfer();
+	if (!pcd_count) {
+		end_request(1);
+		pcd_busy = 0;
+		return;
+	}
+	sti();
+
+	pi_do_claimed(PI,pcd_start);
+}
+
+static void do_pcd_read_drq( void )
+
+{	int	unit = pcd_unit;
+
+	sti();
+
+	if (pcd_completion(unit,pcd_buffer,"read block")) {
+                if (pcd_retries < PCD_RETRIES) {
+                        udelay(1000);
+                        pcd_retries++;
+			pi_do_claimed(PI,pcd_start);
+                        return;
+                        }
+		cli();
+		pcd_busy = 0;
+		pcd_bufblk = -1;
+		end_request(0);
+		do_pcd_request();
+		return;
+	}
+
+	do_pcd_read(unit);
+	do_pcd_request();
+}
+	
+/* end of pcd.c */
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/pd.c linux/drivers/block/paride/pd.c
--- v2.1.76/linux/drivers/block/paride/pd.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/pd.c	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,1062 @@
+/* 
+        pd.c    (c) 1997  Grant R. Guenther <grant@torque.net>
+                          Under the terms of the GNU public license.
+
+        This is the high-level driver for parallel port IDE hard
+        drives based on chips supported by the paride module.
+
+	By default, the driver will autoprobe for a single parallel
+	port IDE drive, but if their individual parameters are
+        specified, the driver can handle up to 4 drives.
+
+        The behaviour of the pd driver can be altered by setting
+        some parameters from the insmod command line.  The following
+        parameters are adjustable:
+ 
+	    drive0  	These four arguments can be arrays of	    
+	    drive1	1-7 integers as follows:
+	    drive2
+	    drive3	<prt>,<pro>,<uni>,<mod>,<geo>,<sby>,<dly>
+
+			Where,
+
+		<prt>	is the base of the parallel port address for
+			the corresponding drive.  (required)
+
+		<pro>   is the protocol number for the adapter that
+			supports this drive.  These numbers are
+                        logged by 'paride' when the protocol modules
+			are initialised.  (0 if not given)
+
+		<uni>   for those adapters that support chained
+			devices, this is the unit selector for the
+		        chain of devices on the given port.  It should
+			be zero for devices that don't support chaining.
+			(0 if not given)
+
+		<mod>   this can be -1 to choose the best mode, or one
+		        of the mode numbers supported by the adapter.
+			(-1 if not given)
+
+		<geo>   this defaults to 0 to indicate that the driver
+			should use the CHS geometry provided by the drive
+			itself.  If set to 1, the driver will provide
+			a logical geometry with 64 heads and 32 sectors
+			per track, to be consistent with most SCSI
+		        drivers.  (0 if not given)
+
+		<sby>   set this to zero to disable the power saving
+			standby mode, if needed.  (1 if not given)
+
+		<dly>   some parallel ports require the driver to 
+			go more slowly.  -1 sets a default value that
+			should work with the chosen protocol.  Otherwise,
+			set this to a small integer, the larger it is
+			the slower the port i/o.  In some cases, setting
+			this to zero will speed up the device. (default -1)
+			
+
+            major       You may use this parameter to overide the
+                        default major number (45) that this driver
+                        will use.  Be sure to change the device
+                        name as well.
+
+            name        This parameter is a character string that
+                        contains the name the kernel will use for this
+                        device (in /proc output, for instance).
+			(default "pd")
+
+	    cluster	The driver will attempt to aggregate requests
+			for adjacent blocks into larger multi-block
+			clusters.  The maximum cluster size (in 512
+			byte sectors) is set with this parameter.
+			(default 64)
+
+	    verbose	This parameter controls the amount of logging
+			that is done while the driver probes for
+			devices.  Set it to 0 for a quiet load, or to 1
+			see all the progress messages.  (default 0)
+
+            nice        This parameter controls the driver's use of
+                        idle CPU time, at the expense of some speed.
+
+        If this driver is built into the kernel, you can use kernel
+        the following command line parameters, with the same values
+        as the corresponding module parameters listed above:
+
+            pd.drive0
+            pd.drive1
+            pd.drive2
+            pd.drive3
+            pd.cluster
+            pd.nice
+
+        In addition, you can use the parameter pd.disable to disable
+        the driver entirely.
+ 
+*/
+
+#define PD_VERSION      "1.0"
+#define PD_MAJOR	45
+#define PD_NAME		"pd"
+#define PD_UNITS	4
+
+/* Here are things one can override from the insmod command.
+   Most are autoprobed by paride unless set here.  Verbose is on
+   by default.
+
+*/
+
+static int	verbose = 0;
+static int	major = PD_MAJOR;
+static char	*name = PD_NAME;
+static int	cluster = 64;	
+static int      nice = 0;
+static int      disable = 0;
+
+static int drive0[7] = {0,0,0,-1,0,1,-1};
+static int drive1[7] = {0,0,0,-1,0,1,-1};
+static int drive2[7] = {0,0,0,-1,0,1,-1};
+static int drive3[7] = {0,0,0,-1,0,1,-1};
+
+static int (*drives[4])[7] = {&drive0,&drive1,&drive2,&drive3};
+static int pd_drive_count;
+
+#define D_PRT	0
+#define D_PRO   1
+#define D_UNI	2
+#define D_MOD	3
+#define D_GEO	4
+#define D_SBY	5
+#define D_DLY	6
+
+#define	DU		(*drives[unit])
+
+/* end of parameters */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+
+#include <asm/uaccess.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pd_stt[7] = {{"drive0",7,drive0},
+		        {"drive1",7,drive1},
+		        {"drive2",7,drive2},
+		        {"drive3",7,drive3},
+			{"disable",1,&disable},
+		        {"cluster",1,&cluster},
+		        {"nice",1,&nice}};
+
+void pd_setup( char *str, int *ints)
+
+{	generic_setup(pd_stt,7,str);
+}
+
+#endif
+
+MODULE_PARM(verbose,"i");
+MODULE_PARM(major,"i");
+MODULE_PARM(name,"s");
+MODULE_PARM(cluster,"i");
+MODULE_PARM(nice,"i");
+MODULE_PARM(drive0,"1-7i");
+MODULE_PARM(drive1,"1-7i");
+MODULE_PARM(drive2,"1-7i");
+MODULE_PARM(drive3,"1-7i");
+
+#include "paride.h"
+
+#define PD_BITS    4
+
+/* set up defines for blk.h,  why don't all drivers do it this way ? */
+
+#define MAJOR_NR   major
+#define DEVICE_NAME "PD"
+#define DEVICE_REQUEST do_pd_request
+#define DEVICE_NR(device) (MINOR(device)>>PD_BITS)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#include "pseudo.h"
+
+#define PD_PARTNS  	(1<<PD_BITS)
+#define PD_DEVS		PD_PARTNS*PD_UNITS
+
+/* numbers for "SCSI" geometry */
+
+#define PD_LOG_HEADS    64
+#define PD_LOG_SECTS    32
+
+#define PD_ID_OFF       54
+#define PD_ID_LEN       14
+
+#define PD_MAX_RETRIES  5
+#define PD_TMO          800             /* interrupt timeout in jiffies */
+#define PD_SPIN_DEL     50              /* spin delay in micro-seconds  */
+
+#define PD_SPIN         (10000/PD_SPIN_DEL)*PD_TMO  
+
+#define STAT_ERR        0x00001
+#define STAT_INDEX      0x00002
+#define STAT_ECC        0x00004
+#define STAT_DRQ        0x00008
+#define STAT_SEEK       0x00010
+#define STAT_WRERR      0x00020
+#define STAT_READY      0x00040
+#define STAT_BUSY       0x00080
+
+#define ERR_AMNF        0x00100
+#define ERR_TK0NF       0x00200
+#define ERR_ABRT        0x00400
+#define ERR_MCR         0x00800
+#define ERR_IDNF        0x01000
+#define ERR_MC          0x02000
+#define ERR_UNC         0x04000
+#define ERR_TMO         0x10000
+
+#define IDE_READ        	0x20
+#define IDE_WRITE       	0x30
+#define IDE_READ_VRFY		0x40
+#define IDE_INIT_DEV_PARMS	0x91
+#define IDE_STANDBY     	0x96
+#define IDE_ACKCHANGE   	0xdb
+#define IDE_DOORLOCK    	0xde
+#define IDE_DOORUNLOCK  	0xdf
+#define IDE_IDENTIFY    	0xec
+
+int pd_init(void);
+void pd_setup(char * str, int * ints);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+static void pd_geninit(struct gendisk *ignored);
+static int pd_open(struct inode *inode, struct file *file);
+static void do_pd_request(void);
+static int pd_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg);
+static int pd_release (struct inode *inode, struct file *file);
+static int pd_revalidate(kdev_t dev);
+static int pd_detect(void);
+static void do_pd_read(void);
+static void do_pd_write(void);
+static void do_pd_read_drq( void );
+static void do_pd_write_done( void );
+
+static int pd_identify (int unit);
+static void pd_media_check(int unit);
+static void pd_doorlock(int unit, int func);
+static int pd_check_media(kdev_t dev);
+
+static struct hd_struct pd_hd[PD_DEVS];
+static int pd_sizes[PD_DEVS];
+static int pd_blocksizes[PD_DEVS];
+
+#define PD_NAMELEN	8
+
+struct pd_unit {
+	struct pi_adapter pia;		/* interface to paride layer */
+	struct pi_adapter *pi;
+	int access;               	/* count of active opens ... */
+	int capacity;             	/* Size of this volume in sectors */
+	int heads;                	/* physical geometry */
+	int sectors;
+	int cylinders;
+	int changed;			/* Have we seen a disk change ? */
+	int removable;			/* removable media device  ?  */
+	int standby;
+	int alt_geom;
+	int present;
+	char name[PD_NAMELEN];		/* pda, pdb, etc ... */
+	};
+
+struct pd_unit pd[PD_UNITS];
+
+/*  'unit' must be defined in all functions - either as a local or a param */
+
+#define PD pd[unit]
+#define PI PD.pi
+
+static int pd_valid = 1;		/* serialise partition checks */
+static char pd_scratch[512];            /* scratch block buffer */
+
+/* the variables below are used mainly in the I/O request engine, which
+   processes only one request at a time.
+*/
+
+static int pd_retries = 0;              /* i/o error retry count */
+static int pd_busy = 0;                 /* request being processed ? */
+static int pd_block;                    /* address of next requested block */
+static int pd_count;                    /* number of blocks still to do */
+static int pd_run;			/* sectors in current cluster */
+static int pd_cmd;			/* current command READ/WRITE */
+static int pd_unit;			/* unit of current request */
+static int pd_dev;			/* minor of current request */
+static int pd_poffs;			/* partition offset of current minor */
+static char * pd_buf;                   /* buffer for request in progress */
+
+static struct wait_queue *pd_wait_open = NULL;
+
+static char *pd_errs[17] = { "ERR","INDEX","ECC","DRQ","SEEK","WRERR",
+                             "READY","BUSY","AMNF","TK0NF","ABRT","MCR",
+                             "IDNF","MC","UNC","???","TMO"};
+
+/* kernel glue structures */
+
+static struct gendisk pd_gendisk = {
+        PD_MAJOR,       /* Major number */
+        PD_NAME,        /* Major name */
+        PD_BITS,        /* Bits to shift to get real from partition */
+        PD_PARTNS,      /* Number of partitions per real */
+        PD_UNITS,       /* maximum number of real */
+        pd_geninit,     /* init function */
+        pd_hd,          /* hd struct */
+        pd_sizes,       /* block sizes */
+        0,              /* number */
+        NULL,           /* internal */
+        NULL            /* next */
+};
+
+static struct file_operations pd_fops = {
+        NULL,                   /* lseek - default */
+        block_read,             /* read - general block-dev read */
+        block_write,            /* write - general block-dev write */
+        NULL,                   /* readdir - bad */
+        NULL,                   /* select */
+        pd_ioctl,               /* ioctl */
+        NULL,                   /* mmap */
+        pd_open,                /* open */
+        pd_release,             /* release */
+        block_fsync,            /* fsync */
+        NULL,                   /* fasync */
+        pd_check_media,         /* media change ? */
+        pd_revalidate           /* revalidate new media */
+};
+
+void pd_init_units( void )
+
+{	int	unit, j;
+
+	pd_drive_count = 0;
+	for (unit=0;unit<PD_UNITS;unit++) {
+		PD.pi = & PD.pia;
+		PD.access = 0;
+		PD.changed = 1;
+		PD.capacity = 0;
+		PD.present = 0;
+		j = 0;
+		while ((j < PD_NAMELEN-2) && (PD.name[j]=name[j])) j++;
+		PD.name[j++] = 'a' + unit;
+		PD.name[j] = 0;
+		PD.alt_geom = DU[D_GEO];
+		PD.standby = DU[D_SBY];
+		if (DU[D_PRT]) pd_drive_count++;
+	}
+}
+
+int pd_init (void)
+
+{       int i;
+
+	if (disable) return -1;
+
+        if (register_blkdev(MAJOR_NR,name,&pd_fops)) {
+                printk("%s: unable to get major number %d\n",
+                        name,major);
+                return -1;
+        }
+        blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+        read_ahead[MAJOR_NR] = 8;       /* 8 sector (4kB) read ahead */
+        
+	pd_gendisk.major = major;
+	pd_gendisk.major_name = name;
+	pd_gendisk.next = gendisk_head;
+        gendisk_head = &pd_gendisk;
+
+        for(i=0;i<PD_DEVS;i++) pd_blocksizes[i] = 1024;
+        blksize_size[MAJOR_NR] = pd_blocksizes;
+
+        printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
+                name,name,PD_VERSION,major,cluster,nice);
+	
+        return 0;
+}
+
+static void pd_geninit (struct gendisk *ignored)
+
+{	pd_init_units();
+	pd_gendisk.nr_real = pd_detect();
+
+#ifdef MODULE
+        if (!pd_gendisk.nr_real) cleanup_module();
+#endif
+
+}
+
+static int pd_open (struct inode *inode, struct file *file)
+
+{       int unit = DEVICE_NR(inode->i_rdev);
+
+        if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+
+        MOD_INC_USE_COUNT;
+
+        while (!pd_valid) sleep_on(&pd_wait_open);
+
+        PD.access++;
+
+        if (PD.removable) {
+		pd_media_check(unit);
+		pd_doorlock(unit,IDE_DOORLOCK);
+	}
+        return 0;
+}
+
+static int pd_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg)
+
+{       struct hd_geometry *geo = (struct hd_geometry *) arg;
+        int dev, err, unit;
+
+        if ((!inode) || (!inode->i_rdev)) return -EINVAL;
+        dev = MINOR(inode->i_rdev);
+	unit = DEVICE_NR(inode->i_rdev);
+        if (dev >= PD_DEVS) return -EINVAL;
+	if (!PD.present) return -ENODEV;
+
+        switch (cmd) {
+            case HDIO_GETGEO:
+                if (!geo) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,geo,sizeof(*geo));
+                if (err) return err;
+
+		if (PD.alt_geom) {
+                    put_user(PD.capacity/(PD_LOG_HEADS*PD_LOG_SECTS), 
+		    		(short *) &geo->cylinders);
+                    put_user(PD_LOG_HEADS, (char *) &geo->heads);
+                    put_user(PD_LOG_SECTS, (char *) &geo->sectors);
+		} else {
+                    put_user(PD.cylinders, (short *) &geo->cylinders);
+                    put_user(PD.heads, (char *) &geo->heads);
+                    put_user(PD.sectors, (char *) &geo->sectors);
+		}
+                put_user(pd_hd[dev].start_sect,(long *)&geo->start);
+                return 0;
+            case BLKRASET:
+                if(!suser()) return -EACCES;
+                if(!(inode->i_rdev)) return -EINVAL;
+                if(arg > 0xff) return -EINVAL;
+                read_ahead[MAJOR(inode->i_rdev)] = arg;
+                return 0;
+            case BLKRAGET:
+                if (!arg) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+                if (err) return (err);
+                put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
+                return (0);
+            case BLKGETSIZE:
+                if (!arg) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+                if (err) return (err);
+                put_user(pd_hd[dev].nr_sects,(long *) arg);
+                return (0);
+            case BLKFLSBUF:
+                if(!suser())  return -EACCES;
+                if(!(inode->i_rdev)) return -EINVAL;
+                fsync_dev(inode->i_rdev);
+                invalidate_buffers(inode->i_rdev);
+                return 0;
+            case BLKRRPART:
+                return pd_revalidate(inode->i_rdev);
+            RO_IOCTLS(inode->i_rdev,arg);
+            default:
+                return -EINVAL;
+        }
+}
+
+static int pd_release (struct inode *inode, struct file *file)
+
+{       kdev_t devp;
+	int	unit;
+
+	struct super_block *sb;
+
+        devp = inode->i_rdev;
+	unit = DEVICE_NR(devp);
+
+	if ((unit >= PD_UNITS) || (PD.access <= 0)) 
+		return -EINVAL;
+
+	PD.access--;
+
+        if (!PD.access)  {
+                fsync_dev(devp);
+
+		sb = get_super(devp);
+		if (sb) invalidate_inodes(sb);
+
+                invalidate_buffers(devp);
+		if (PD.removable) pd_doorlock(unit,IDE_DOORUNLOCK);
+	}
+
+        MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+static int pd_check_media( kdev_t dev)
+
+{       int r, unit;
+
+	unit = DEVICE_NR(dev);
+	if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+        if (!PD.removable) return 0;
+	pd_media_check(unit);
+	r = PD.changed;
+	PD.changed = 0;
+	return r;
+}
+
+static int pd_revalidate(kdev_t dev)
+
+{       int p, unit, minor;
+        long flags;
+        kdev_t devp;
+
+	struct super_block *sb;
+
+        unit = DEVICE_NR(dev);
+        if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+
+        save_flags(flags);
+        cli(); 
+        if (PD.access > 1) {
+                restore_flags(flags);
+                return -EBUSY;
+        }
+        pd_valid = 0;
+        restore_flags(flags);   
+
+        for (p=(PD_PARTNS-1);p>=0;p--) {
+		minor = p + unit*PD_PARTNS;
+                devp = MKDEV(MAJOR_NR, minor);
+                fsync_dev(devp);
+
+                sb = get_super(devp);
+                if (sb) invalidate_inodes(sb);
+
+                invalidate_buffers(devp);
+                pd_hd[minor].start_sect = 0;
+                pd_hd[minor].nr_sects = 0;
+        }
+
+	pd_identify(unit);
+        resetup_one_dev(&pd_gendisk,unit);
+
+        pd_valid = 1;
+        wake_up(&pd_wait_open);
+
+        return 0;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void    cleanup_module(void);
+
+int     init_module(void)
+
+{       int     err, unit;
+        long    flags;
+
+        save_flags(flags);
+        cli();
+
+        err = pd_init();
+        if (err) {
+            restore_flags(flags);
+            return err;
+        }
+
+	pd_geninit(&pd_gendisk);
+
+        if (!pd_gendisk.nr_real) {
+                restore_flags(flags);
+                return -1;
+        }
+
+        pd_valid = 0;
+	for (unit=0;unit<PD_UNITS;unit++) 
+          if (PD.present) resetup_one_dev(&pd_gendisk,unit);
+        pd_valid = 1;
+
+        restore_flags(flags);
+        return 0;
+}
+
+void    cleanup_module(void)
+
+{       struct gendisk **gdp;
+        long flags;
+	int unit;
+
+        save_flags(flags);
+        cli();
+
+        unregister_blkdev(MAJOR_NR,name);
+
+        for(gdp=&gendisk_head;*gdp;gdp=&((*gdp)->next))
+                if (*gdp == &pd_gendisk) break;
+        if (*gdp) *gdp = (*gdp)->next;
+
+	for (unit=0;unit<PD_UNITS;unit++) 
+	   if (PD.present) pi_release(PI);
+
+        restore_flags(flags);
+}
+
+#endif
+
+#define	WR(c,r,v)	pi_write_regr(PI,c,r,v)
+#define	RR(c,r)		(pi_read_regr(PI,c,r))
+
+/*  ide command interface */
+
+static void pd_print_error( int unit, char * msg, int status )
+
+{       int     i;
+
+	printk("%s: %s: status = 0x%x =",PD.name,msg,status);
+        for(i=0;i<18;i++) if (status & (1<<i)) printk(" %s",pd_errs[i]);
+	printk("\n");
+}
+
+/*
+static void pd_reset( int unit )
+
+{       pi_connect(PI);
+	WR(1,6,4);
+        udelay(50);
+        WR(1,6,0);
+	pi_disconnect(PI);
+}
+*/
+
+#define DBMSG(msg)	NULL
+
+static int pd_wait_for( int unit, int w, char * msg )    /* polled wait */
+
+{       int     k, r, e;
+
+        k=0;
+        while(k < PD_SPIN) { 
+            r = RR(1,6);
+            k++;
+            if (((r & w) == w) && !(r & STAT_BUSY)) break;
+            udelay(PD_SPIN_DEL);
+        }
+        e = (RR(0,1)<<8) + RR(0,7);
+        if (k >= PD_SPIN)  e |= ERR_TMO;
+        if ((e & (STAT_ERR|ERR_TMO)) && (msg != NULL)) 
+		pd_print_error(unit,msg,e);
+        return e;
+}
+
+static void pd_send_command( int unit, int n, int s, int h, 
+			     int c0, int c1, int func )
+
+{
+        WR(0,6,0xa0+h);
+        WR(0,1,0);                /* the IDE task file */
+        WR(0,2,n);
+        WR(0,3,s);
+        WR(0,4,c0);
+        WR(0,5,c1);
+        WR(0,7,func);
+
+        udelay(1);
+}
+
+static void pd_ide_command( int unit, int func, int block, int count )
+
+/* Don't use this call if the capacity is zero. */
+
+{       int c1, c0, h, s;
+
+        s  = ( block % PD.sectors) + 1;
+        h  = ( block / PD.sectors) % PD.heads;
+        c0 = ( block / (PD.sectors*PD.heads)) % 256;
+        c1 = ( block / (PD.sectors*PD.heads*256));
+
+        pd_send_command(unit,count,s,h,c0,c1,func);
+}
+
+/* According to the ATA standard, the default CHS geometry should be
+   available following a reset.  Some Western Digital drives come up
+   in a mode where only LBA addresses are accepted until the device
+   parameters are initialised.
+*/
+
+static void pd_init_dev_parms( int unit )
+ 
+{       pi_connect(PI);
+        pd_wait_for(unit,0,DBMSG("before init_dev_parms"));
+        pd_send_command(unit,PD.sectors,0,PD.heads-1,0,0,IDE_INIT_DEV_PARMS);
+        udelay(300);
+        pd_wait_for(unit,0,"Initialise device parameters");
+        pi_disconnect(PI);
+}
+
+static void pd_doorlock( int unit, int func )
+
+{       pi_connect(PI);
+        if (pd_wait_for(unit,STAT_READY,"Lock") & STAT_ERR) {
+                pi_disconnect(PI);
+                return;
+        }
+        pd_send_command(unit,1,0,0,0,0,func);
+        pd_wait_for(unit,STAT_READY,"Lock done");
+        pi_disconnect(PI);
+}
+
+static void pd_media_check( int unit )
+
+{       int 	r;
+
+        pi_connect(PI);
+        r = pd_wait_for(unit,STAT_READY,DBMSG("before media_check"));
+        if (!(r & STAT_ERR)) {
+                pd_send_command(unit,1,1,0,0,0,IDE_READ_VRFY);  
+                r = pd_wait_for(unit,STAT_READY,DBMSG("RDY after READ_VRFY"));
+        } else PD.changed = 1;   /* say changed if other error */
+        if (r & ERR_MC) {
+                PD.changed = 1;
+                pd_send_command(unit,1,0,0,0,0,IDE_ACKCHANGE);
+                pd_wait_for(unit,STAT_READY,DBMSG("RDY after ACKCHANGE"));
+		pd_send_command(unit,1,1,0,0,0,IDE_READ_VRFY);
+                r = pd_wait_for(unit,STAT_READY,DBMSG("RDY after VRFY"));
+        }
+        pi_disconnect(PI);
+
+}
+
+static void pd_standby_off( int unit )
+
+{       pi_connect(PI);
+        pd_wait_for(unit,0,DBMSG("before STANDBY"));
+        pd_send_command(unit,0,0,0,0,0,IDE_STANDBY);
+        pd_wait_for(unit,0,DBMSG("after STANDBY"));
+        pi_disconnect(PI);
+}
+
+#define  word_val(n) ((pd_scratch[2*n]&0xff)+256*(pd_scratch[2*n+1]&0xff))
+
+static int pd_identify( int unit )
+
+{       int	j;
+	char id[PD_ID_LEN+1];
+
+        pi_connect(PI);
+	WR(0,6,0xa0);
+        pd_wait_for(unit,0,DBMSG("before IDENT"));  
+        pd_send_command(unit,1,0,0,0,0,IDE_IDENTIFY);
+
+        if (pd_wait_for(unit,STAT_DRQ,DBMSG("IDENT DRQ")) & STAT_ERR) {
+                pi_disconnect(PI);
+                return 0;
+        }
+        pi_read_block(PI,pd_scratch,512);
+        pi_disconnect(PI);
+        PD.sectors = word_val(6);
+        PD.heads = word_val(3);
+        PD.cylinders  = word_val(1);
+        PD.capacity = PD.sectors*PD.heads*PD.cylinders;
+
+        for(j=0;j<PD_ID_LEN;j++) id[j^1] = pd_scratch[j+PD_ID_OFF];
+        j = PD_ID_LEN-1;
+        while ((j >= 0) && (id[j] <= 0x20)) j--;
+        j++; id[j] = 0;
+
+        PD.removable = (word_val(0) & 0x80);
+ 
+        printk("%s: %s, %d blocks [%dM], (%d/%d/%d), %s media\n",
+                    PD.name,id,PD.capacity,PD.capacity/2048,
+                    PD.cylinders,PD.heads,PD.sectors,
+                    PD.removable?"removable":"fixed");
+
+        if (PD.capacity) pd_init_dev_parms(unit);
+        if (!PD.standby) pd_standby_off(unit);
+
+	pd_hd[unit<<PD_BITS].nr_sects = PD.capacity;
+	pd_hd[unit<<PD_BITS].start_sect = 0;
+	
+        return 1;
+}
+
+static int pd_detect( void )
+
+{       long 	flags;
+	int	k, unit;
+
+	k = 0;
+	if (pd_drive_count == 0) {  /* nothing spec'd - so autoprobe for 1 */
+	    unit = 0;
+	    if (pi_init(PI,1,-1,-1,-1,-1,-1,pd_scratch,
+	             PI_PD,verbose,PD.name)) {
+		save_flags(flags);
+		sti();
+		if (pd_identify(unit)) {
+			PD.present = 1;
+			k = 1;
+		} else pi_release(PI);
+		restore_flags(flags);
+	    }
+
+   	} else for (unit=0;unit<PD_UNITS;unit++) if (DU[D_PRT])
+	    if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+			DU[D_PRO],DU[D_DLY],pd_scratch,
+			PI_PD,verbose,PD.name)) {
+                save_flags(flags);
+                sti();
+                if (pd_identify(unit)) {
+                        PD.present = 1;
+                        k = unit+1;
+                } else pi_release(PI);
+                restore_flags(flags);
+            }
+
+/* We lie about the number of drives found, as the generic partition
+   scanner assumes that the drives are numbered sequentially from 0.
+   This can result in some bogus error messages if non-sequential
+   drive numbers are used.
+*/
+	
+	if (k) return k; 
+
+        printk("%s: no valid drive found\n",name);
+        return 0;
+}
+
+/* The i/o request engine */
+
+static int pd_ready( void )
+
+{ 	int unit = pd_unit;
+
+	return (!(RR(1,6) & STAT_BUSY)) ;
+}
+
+static void do_pd_request (void)
+
+{       struct buffer_head * bh;
+	struct request * req;
+	int	unit;
+
+        if (pd_busy) return;
+repeat:
+        if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+        INIT_REQUEST;
+
+        pd_dev = MINOR(CURRENT->rq_dev);
+	pd_unit = unit = DEVICE_NR(CURRENT->rq_dev);
+        pd_block = CURRENT->sector;
+        pd_count = CURRENT->nr_sectors;
+
+	bh = CURRENT->bh;
+	req = CURRENT;
+	if (bh->b_reqnext)
+		printk("%s: OUCH: b_reqnext != NULL\n",PD.name);
+
+        if ((pd_dev >= PD_DEVS) || 
+	    ((pd_block+pd_count) > pd_hd[pd_dev].nr_sects)) {
+                end_request(0);
+                goto repeat;
+        }
+
+	pd_cmd = CURRENT->cmd;
+	pd_run = pd_count;
+        while ((pd_run <= cluster) &&
+	       (req = req->next) && 
+	       (pd_block+pd_run == req->sector) &&
+	       (pd_cmd == req->cmd) &&
+	       (pd_dev == MINOR(req->rq_dev)))
+			pd_run += req->nr_sectors;
+
+	pd_poffs = pd_hd[pd_dev].start_sect;
+        pd_block += pd_poffs;
+        pd_buf = CURRENT->buffer;
+        pd_retries = 0;
+
+        if (pd_cmd == READ) pi_do_claimed(PI,do_pd_read);
+        else if (pd_cmd == WRITE) pi_do_claimed(PI,do_pd_write);
+        else {  end_request(0);
+                goto repeat;
+        }
+}
+
+static void pd_next_buf( int unit )
+
+{	cli();
+	end_request(1);
+	if (!pd_run) { sti(); return; }
+	
+/* paranoia */
+
+	if ((!CURRENT) ||
+	    (CURRENT->cmd != pd_cmd) ||
+	    (MINOR(CURRENT->rq_dev) != pd_dev) ||
+	    (CURRENT->rq_status == RQ_INACTIVE) ||
+	    (CURRENT->sector+pd_poffs != pd_block)) 
+		printk("%s: OUCH: request list changed unexpectedly\n",
+			PD.name);
+
+	pd_count = CURRENT->nr_sectors;
+	pd_buf = CURRENT->buffer;
+	sti();
+}
+
+static void do_pd_read( void )
+
+{       int	unit = pd_unit;
+
+	pd_busy = 1;
+
+        sti();
+
+        pi_connect(PI);
+        if (pd_wait_for(unit,STAT_READY,"do_pd_read") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+                        pi_do_claimed(PI,do_pd_read);
+			return;
+                }
+                end_request(0);
+                pd_busy = 0;
+                cli();
+                do_pd_request();
+                return;
+        }
+        pd_ide_command(unit,IDE_READ,pd_block,pd_run);
+        ps_set_intr(do_pd_read_drq,pd_ready,PD_TMO,nice);
+}
+
+static void do_pd_read_drq( void )
+
+{       int	unit = pd_unit;
+
+	sti();
+
+	while (1) {
+            if (pd_wait_for(unit,STAT_DRQ,"do_pd_read_drq") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+                        pi_do_claimed(PI,do_pd_read);
+                        return;
+                }
+                end_request(0);
+                pd_busy = 0;
+                cli();
+                do_pd_request();
+                return;
+            }
+            pi_read_block(PI,pd_buf,512);
+            pd_count--; pd_run--;
+            pd_buf += 512;
+	    pd_block++;
+	    if (!pd_run) break;
+	    if (!pd_count) pd_next_buf(unit);
+        }
+        pi_disconnect(PI);
+        end_request(1);
+        pd_busy = 0;
+        cli();
+        do_pd_request();
+}
+
+static void do_pd_write( void )
+
+{       int 	unit = pd_unit;
+
+	pd_busy = 1;
+
+        sti();
+
+        pi_connect(PI);
+        if (pd_wait_for(unit,STAT_READY,"do_pd_write") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+			pi_do_claimed(PI,do_pd_write);
+                        return;
+                }
+                end_request(0);
+                pd_busy = 0;
+                cli();
+                do_pd_request();
+                return;
+        }
+        pd_ide_command(unit,IDE_WRITE,pd_block,pd_run);
+	while (1) {
+            if (pd_wait_for(unit,STAT_DRQ,"do_pd_write_drq") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+                        pi_do_claimed(PI,do_pd_write);
+                        return;
+                }
+                end_request(0);
+                pd_busy = 0;
+                cli();
+                do_pd_request();
+                return;
+            }
+            pi_write_block(PI,pd_buf,512);
+	    pd_count--; pd_run--;
+	    pd_buf += 512;
+	    pd_block++;
+	    if (!pd_run) break;
+	    if (!pd_count) pd_next_buf(unit);
+	}
+        ps_set_intr(do_pd_write_done,pd_ready,PD_TMO,nice);
+}
+
+static void do_pd_write_done( void )
+
+{       int	unit = pd_unit;
+
+	sti();
+        if (pd_wait_for(unit,STAT_READY,"do_pd_write_done") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pd_retries < PD_MAX_RETRIES) {
+                        pd_retries++;
+                        pi_do_claimed(PI,do_pd_write);
+                        return;
+                }
+                end_request(0);
+                pd_busy = 0;
+                cli();
+                do_pd_request();
+                return;
+        }
+        pi_disconnect(PI);
+        end_request(1);
+        pd_busy = 0;
+        cli();
+        do_pd_request();
+}
+
+/* end of pd.c */
+
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/pf.c linux/drivers/block/paride/pf.c
--- v2.1.76/linux/drivers/block/paride/pf.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/pf.c	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,1072 @@
+/* 
+        pf.c    (c) 1997  Grant R. Guenther <grant@torque.net>
+                          Under the terms of the GNU public license.
+
+        This is the high-level driver for parallel port ATAPI disk
+        drives based on chips supported by the paride module.
+
+        By default, the driver will autoprobe for a single parallel
+        port ATAPI disk drive, but if their individual parameters are
+        specified, the driver can handle up to 4 drives.
+
+        The behaviour of the pf driver can be altered by setting
+        some parameters from the insmod command line.  The following
+        parameters are adjustable:
+
+            drive0      These four arguments can be arrays of       
+            drive1      1-7 integers as follows:
+            drive2
+            drive3      <prt>,<pro>,<uni>,<mod>,<slv>,<lun>,<dly>
+
+                        Where,
+
+                <prt>   is the base of the parallel port address for
+                        the corresponding drive.  (required)
+
+                <pro>   is the protocol number for the adapter that
+                        supports this drive.  These numbers are
+                        logged by 'paride' when the protocol modules
+                        are initialised.  (0 if not given)
+
+                <uni>   for those adapters that support chained
+                        devices, this is the unit selector for the
+                        chain of devices on the given port.  It should
+                        be zero for devices that don't support chaining.
+                        (0 if not given)
+
+                <mod>   this can be -1 to choose the best mode, or one
+                        of the mode numbers supported by the adapter.
+                        (-1 if not given)
+
+                <slv>   ATAPI CDroms can be jumpered to master or slave.
+                        Set this to 0 to choose the master drive, 1 to
+                        choose the slave, -1 (the default) to choose the
+                        first drive found.
+
+		<lun>   Some ATAPI devices support multiple LUNs.
+                        One example is the ATAPI PD/CD drive from
+                        Matshita/Panasonic.  This device has a 
+                        CD drive on LUN 0 and a PD drive on LUN 1.
+                        By default, the driver will search for the
+                        first LUN with a supported device.  Set 
+                        this parameter to force it to use a specific
+                        LUN.  (default -1)
+
+                <dly>   some parallel ports require the driver to 
+                        go more slowly.  -1 sets a default value that
+                        should work with the chosen protocol.  Otherwise,
+                        set this to a small integer, the larger it is
+                        the slower the port i/o.  In some cases, setting
+                        this to zero will speed up the device. (default -1)
+
+	    major	You may use this parameter to overide the
+			default major number (47) that this driver
+			will use.  Be sure to change the device
+			name as well.
+
+	    name	This parameter is a character string that
+			contains the name the kernel will use for this
+			device (in /proc output, for instance).
+			(default "pf").
+
+            cluster     The driver will attempt to aggregate requests
+                        for adjacent blocks into larger multi-block
+                        clusters.  The maximum cluster size (in 512
+                        byte sectors) is set with this parameter.
+                        (default 64)
+
+            verbose     This parameter controls the amount of logging
+                        that is done while the driver probes for
+                        devices.  Set it to 0 for a quiet load, or 1 to
+                        see all the progress messages.  (default 0)
+
+	    nice        This parameter controls the driver's use of
+			idle CPU time, at the expense of some speed.
+
+        If this driver is built into the kernel, you can use kernel
+        the following command line parameters, with the same values
+        as the corresponding module parameters listed above:
+
+            pf.drive0
+            pf.drive1
+            pf.drive2
+            pf.drive3
+	    pf.cluster
+            pf.nice
+
+        In addition, you can use the parameter pf.disable to disable
+        the driver entirely.
+
+*/
+
+#define PF_VERSION      "1.0"
+#define PF_MAJOR	47
+#define PF_NAME		"pf"
+#define PF_UNITS	4
+
+/* Here are things one can override from the insmod command.
+   Most are autoprobed by paride unless set here.  Verbose is on
+   by default.
+
+*/
+
+static int	verbose = 0;
+static int	major = PF_MAJOR;
+static char	*name = PF_NAME;
+static int      cluster = 64;
+static int      nice = 0;
+static int      disable = 0;
+
+static int drive0[7] = {0,0,0,-1,-1,-1,-1};
+static int drive1[7] = {0,0,0,-1,-1,-1,-1};
+static int drive2[7] = {0,0,0,-1,-1,-1,-1};
+static int drive3[7] = {0,0,0,-1,-1,-1,-1};
+
+static int (*drives[4])[7] = {&drive0,&drive1,&drive2,&drive3};
+static int pf_drive_count;
+
+#define D_PRT   0
+#define D_PRO   1
+#define D_UNI   2
+#define D_MOD   3
+#define D_SLV   4
+#define D_LUN   5
+#define D_DLY   6
+
+#define DU              (*drives[unit])
+
+/* end of parameters */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/cdrom.h>
+
+#include <asm/uaccess.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pf_stt[7] = {{"drive0",7,drive0},
+                        {"drive1",7,drive1},
+                        {"drive2",7,drive2},
+                        {"drive3",7,drive3},
+			{"disable",1,&disable},
+                        {"cluster",1,&cluster},
+                        {"nice",1,&nice}};
+
+void pf_setup( char *str, int *ints)
+
+{       generic_setup(pf_stt,7,str);
+}
+
+#endif
+
+MODULE_PARM(verbose,"i");
+MODULE_PARM(major,"i");
+MODULE_PARM(name,"s");
+MODULE_PARM(cluster,"i");
+MODULE_PARM(nice,"i");
+MODULE_PARM(drive0,"1-7i");
+MODULE_PARM(drive1,"1-7i");
+MODULE_PARM(drive2,"1-7i");
+MODULE_PARM(drive3,"1-7i");
+
+#include "paride.h"
+
+/* set up defines for blk.h,  why don't all drivers do it this way ? */
+
+#define MAJOR_NR   major
+#define DEVICE_NAME "PF"
+#define DEVICE_REQUEST do_pf_request
+#define DEVICE_NR(device) MINOR(device)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#include "pseudo.h"
+
+/* constants for faking geometry numbers */
+
+#define PF_FD_MAX	8192		/* use FD geometry under this size */
+#define PF_FD_HDS	2
+#define PF_FD_SPT	18
+#define PF_HD_HDS	64
+#define PF_HD_SPT	32
+
+#define PF_MAX_RETRIES  5
+#define PF_TMO          800             /* interrupt timeout in jiffies */
+#define PF_SPIN_DEL     50              /* spin delay in micro-seconds  */
+
+#define PF_SPIN         (10000/PF_SPIN_DEL)*PF_TMO  
+
+#define STAT_ERR        0x00001
+#define STAT_INDEX      0x00002
+#define STAT_ECC        0x00004
+#define STAT_DRQ        0x00008
+#define STAT_SEEK       0x00010
+#define STAT_WRERR      0x00020
+#define STAT_READY      0x00040
+#define STAT_BUSY       0x00080
+
+#define ATAPI_REQ_SENSE		0x03
+#define ATAPI_LOCK		0x1e
+#define ATAPI_DOOR		0x1b
+#define ATAPI_MODE_SENSE	0x5a
+#define ATAPI_CAPACITY		0x25
+#define ATAPI_IDENTIFY		0x12
+#define ATAPI_READ_10		0x28
+#define ATAPI_WRITE_10		0x2a
+
+int pf_init(void);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+static int pf_open(struct inode *inode, struct file *file);
+static void do_pf_request(void);
+static int pf_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg);
+
+static int pf_release (struct inode *inode, struct file *file);
+
+static int pf_detect(void);
+static void do_pf_read(void);
+static void do_pf_write(void);
+static void do_pf_read_drq( void );
+static void do_pf_write_done( void );
+
+static int pf_identify (int unit);
+static void pf_lock(int unit, int func);
+static void pf_eject(int unit);
+static int pf_check_media(kdev_t dev);
+
+static int pf_blocksizes[PF_UNITS];
+
+#define PF_NM           0
+#define PF_RO           1
+#define PF_RW           2
+
+#define PF_NAMELEN      8
+
+struct pf_unit {
+	struct pi_adapter pia;    /* interface to paride layer */
+	struct pi_adapter *pi;
+	int removable;		  /* removable media device  ?  */
+	int media_status;	  /* media present ?  WP ? */
+	int drive;		  /* drive */
+	int lun;
+	int access;               /* count of active opens ... */
+	int capacity;             /* Size of this volume in sectors */
+	int present;		  /* device present ? */
+	char name[PF_NAMELEN];	  /* pf0, pf1, ... */
+	};
+
+struct pf_unit pf[PF_UNITS];
+
+/*  'unit' must be defined in all functions - either as a local or a param */
+
+#define PF pf[unit]
+#define PI PF.pi
+
+static char pf_scratch[512];            /* scratch block buffer */
+
+/* the variables below are used mainly in the I/O request engine, which
+   processes only one request at a time.
+*/
+
+static int pf_retries = 0;              /* i/o error retry count */
+static int pf_busy = 0;                 /* request being processed ? */
+static int pf_block;                    /* address of next requested block */
+static int pf_count;                    /* number of blocks still to do */
+static int pf_run;			/* sectors in current cluster */
+static int pf_cmd;			/* current command READ/WRITE */
+static int pf_unit;			/* unit of current request */
+static int pf_mask;			/* stopper for pseudo-int */
+static char * pf_buf;                   /* buffer for request in progress */
+
+/* kernel glue structures */
+
+static struct file_operations pf_fops = {
+        NULL,                   /* lseek - default */
+        block_read,             /* read - general block-dev read */
+        block_write,            /* write - general block-dev write */
+        NULL,                   /* readdir - bad */
+        NULL,                   /* select */
+        pf_ioctl,               /* ioctl */
+        NULL,                   /* mmap */
+        pf_open,                /* open */
+        pf_release,             /* release */
+        block_fsync,            /* fsync */
+        NULL,                   /* fasync */
+        pf_check_media,         /* media change ? */
+        NULL                    /* revalidate new media */
+};
+
+void pf_init_units( void )
+
+{       int     unit, j;
+
+        pf_drive_count = 0;
+        for (unit=0;unit<PF_UNITS;unit++) {
+                PF.pi = & PF.pia;
+                PF.access = 0;
+                PF.media_status = PF_NM;
+                PF.capacity = 0;
+                PF.present = 0;
+		PF.drive = DU[D_SLV];
+		PF.lun = DU[D_LUN];
+                j = 0;
+                while ((j < PF_NAMELEN-2) && (PF.name[j]=name[j])) j++;
+                PF.name[j++] = '0' + unit;
+                PF.name[j] = 0;
+                if (DU[D_PRT]) pf_drive_count++;
+        }
+} 
+
+int pf_init (void)      /* preliminary initialisation */
+
+{       int i;
+
+	if (disable) return -1;
+
+	pf_init_units();
+
+	if (pf_detect()) return -1;
+	pf_busy = 0;
+
+        if (register_blkdev(MAJOR_NR,name,&pf_fops)) {
+                printk("pf_init: unable to get major number %d\n",
+                        major);
+                return -1;
+        }
+        blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+        read_ahead[MAJOR_NR] = 8;       /* 8 sector (4kB) read ahead */
+        
+	for (i=0;i<PF_UNITS;i++) pf_blocksizes[i] = 1024;
+	blksize_size[MAJOR_NR] = pf_blocksizes;
+
+        return 0;
+}
+
+static int pf_open (struct inode *inode, struct file *file)
+
+{       int	unit = DEVICE_NR(inode->i_rdev);
+
+        if ((unit >= PF_UNITS) || (!PF.present)) return -ENODEV;
+
+        MOD_INC_USE_COUNT;
+
+	pf_identify(unit);
+
+	if (PF.media_status == PF_NM) {
+		MOD_DEC_USE_COUNT;
+		return -ENODEV;
+		}
+
+	if ((PF.media_status == PF_RO) && (file ->f_mode & 2)) {
+		MOD_DEC_USE_COUNT;
+		return -EROFS;
+		}
+
+        PF.access++;
+        if (PF.removable) pf_lock(unit,1);
+
+        return 0;
+}
+
+static int pf_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg)
+
+{       int err, unit;
+	struct hd_geometry *geo = (struct hd_geometry *) arg;
+
+        if ((!inode) || (!inode->i_rdev)) return -EINVAL;
+        unit = DEVICE_NR(inode->i_rdev);
+        if (unit >= PF_UNITS) return -EINVAL;
+        if (!PF.present) return -ENODEV;
+
+        switch (cmd) {
+	    case CDROMEJECT: 
+		if (PF.access == 1) {
+			pf_eject(unit);
+			return 0;
+			}
+	    case HDIO_GETGEO:
+                if (!geo) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,geo,sizeof(*geo));
+                if (err) return err;
+                if (PF.capacity < PF_FD_MAX) {
+                    put_user(PF.capacity/(PF_FD_HDS*PF_FD_SPT),
+                                (short *) &geo->cylinders);
+                    put_user(PF_FD_HDS, (char *) &geo->heads);
+                    put_user(PF_FD_SPT, (char *) &geo->sectors);
+                } else {
+                    put_user(PF.capacity/(PF_HD_HDS*PF_HD_SPT), 
+				(short *) &geo->cylinders);
+                    put_user(PF_HD_HDS, (char *) &geo->heads);
+                    put_user(PF_HD_SPT, (char *) &geo->sectors);
+                }
+                put_user(0,(long *)&geo->start);
+                return 0;
+            case BLKRASET:
+                if(!suser()) return -EACCES;
+                if(!(inode->i_rdev)) return -EINVAL;
+                if(arg > 0xff) return -EINVAL;
+                read_ahead[MAJOR(inode->i_rdev)] = arg;
+                return 0;
+            case BLKRAGET:
+                if (!arg) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+                if (err) return (err);
+                put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
+                return (0);
+            case BLKGETSIZE:
+                if (!arg) return -EINVAL;
+                err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+                if (err) return (err);
+                put_user(PF.capacity,(long *) arg);
+                return (0);
+            case BLKFLSBUF:
+                if(!suser())  return -EACCES;
+                if(!(inode->i_rdev)) return -EINVAL;
+                fsync_dev(inode->i_rdev);
+                invalidate_buffers(inode->i_rdev);
+                return 0;
+            RO_IOCTLS(inode->i_rdev,arg);
+            default:
+                return -EINVAL;
+        }
+}
+
+
+static int pf_release (struct inode *inode, struct file *file)
+
+{       kdev_t devp;
+	int	unit;
+
+	struct super_block *sb;
+
+        devp = inode->i_rdev;
+        unit = DEVICE_NR(devp);
+
+        if ((unit >= PF_UNITS) || (PF.access <= 0)) 
+                return -EINVAL;
+
+	PF.access--;
+
+	if (!PF.access) {
+                fsync_dev(devp);
+
+		sb = get_super(devp);
+		if (sb) invalidate_inodes(sb);
+
+                invalidate_buffers(devp);
+		if (PF.removable) pf_lock(unit,0);
+        }
+
+        MOD_DEC_USE_COUNT;
+
+	return 0;
+
+}
+
+static int pf_check_media( kdev_t dev)
+
+{       return 1;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void    cleanup_module(void);
+
+int     init_module(void)
+
+{       int     err;
+        long    flags;
+
+        save_flags(flags);
+        cli();
+
+        err = pf_init();
+
+        restore_flags(flags);
+        return err;
+}
+
+void    cleanup_module(void)
+
+{       long flags;
+	int unit;
+
+        save_flags(flags);
+        cli();
+
+        unregister_blkdev(MAJOR_NR,name);
+
+	for (unit=0;unit<PF_UNITS;unit++)
+	  if (PF.present) pi_release(PI);
+	
+        restore_flags(flags);
+}
+
+#endif
+
+#define	WR(c,r,v)	pi_write_regr(PI,c,r,v)
+#define	RR(c,r)		(pi_read_regr(PI,c,r))
+
+#define LUN             (0x20*PF.lun)
+#define DRIVE           (0xa0+0x10*PF.drive)
+
+static int pf_wait( int unit, int go, int stop, char * fun, char * msg )
+
+{       int j, r, e, s, p;
+
+        j = 0;
+        while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PF_SPIN))
+                udelay(PF_SPIN_DEL);
+
+        if ((r&(STAT_ERR&stop))||(j>=PF_SPIN)) {
+           s = RR(0,7);
+           e = RR(0,1);
+           p = RR(0,2);
+           if (j >= PF_SPIN) e |= 0x100;
+           if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
+                           " loop=%d phase=%d\n",
+                            PF.name,fun,msg,r,s,e,j,p);
+           return (e<<8)+s;
+        }
+        return 0;
+}
+
+static int pf_command( int unit, char * cmd, int dlen, char * fun )
+
+{       pi_connect(PI);
+
+        WR(0,6,DRIVE);
+
+        if (pf_wait(unit,STAT_BUSY|STAT_DRQ,0,fun,"before command")) {
+                pi_disconnect(PI);
+                return -1;
+        }
+
+        WR(0,4,dlen % 256);
+        WR(0,5,dlen / 256);
+        WR(0,7,0xa0);          /* ATAPI packet command */
+
+        if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,fun,"command DRQ")) {
+                pi_disconnect(PI);
+                return -1;
+        }
+
+        if (RR(0,2) != 1) {
+           printk("%s: %s: command phase error\n",PF.name,fun);
+           pi_disconnect(PI);
+           return -1;
+        }
+
+        pi_write_block(PI,cmd,12);
+
+        return 0;
+}
+
+static int pf_completion( int unit, char * buf, char * fun )
+
+{       int r, s, n;
+
+        r = pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR,
+			fun,"completion");
+
+        if ((RR(0,2)&2) && (RR(0,7)&STAT_DRQ)) { 
+                n = (RR(0,4)+256*RR(0,5));
+                pi_read_block(PI,buf,n);
+        }
+
+        s = pf_wait(unit,STAT_BUSY,STAT_READY|STAT_ERR,fun,"data done");
+
+        pi_disconnect(PI); 
+
+        return (r?r:s);
+}
+
+static void pf_req_sense( int unit, int quiet )
+
+{       char    rs_cmd[12] = { ATAPI_REQ_SENSE,LUN,0,0,16,0,0,0,0,0,0,0 };
+        char    buf[16];
+        int     r;
+
+        r = pf_command(unit,rs_cmd,16,"Request sense");
+        udelay(1000);
+        if (!r) pf_completion(unit,buf,"Request sense");
+
+        if ((!r)&&(!quiet)) 
+                printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
+                       PF.name,buf[2]&0xf,buf[12],buf[13]);
+}
+
+static int pf_atapi( int unit, char * cmd, int dlen, char * buf, char * fun )
+
+{       int r;
+
+        r = pf_command(unit,cmd,dlen,fun);
+        udelay(1000);
+        if (!r) r = pf_completion(unit,buf,fun);
+        if (r) pf_req_sense(unit,!fun);
+        
+        return r;
+}
+
+#define DBMSG(msg)      NULL
+
+static void pf_lock(int unit, int func)
+
+{	char	lo_cmd[12] = { ATAPI_LOCK,LUN,0,0,func,0,0,0,0,0,0,0 };
+
+        pf_atapi(unit,lo_cmd,0,pf_scratch,func?"unlock":"lock");
+}
+
+
+static void pf_eject( int unit )
+
+{	char	ej_cmd[12] = { ATAPI_DOOR,LUN,0,0,2,0,0,0,0,0,0,0 };
+
+	pf_lock(unit,0);
+	pf_atapi(unit,ej_cmd,0,pf_scratch,"eject");
+}
+
+#define PF_RESET_TMO   30              /* in tenths of a second */
+
+static void pf_sleep( int cs )
+
+{       current->state = TASK_INTERRUPTIBLE;
+        current->timeout = jiffies + cs;
+        schedule();
+}
+
+
+static int pf_reset( int unit )
+
+/* the ATAPI standard actually specifies the contents of all 7 registers
+   after a reset, but the specification is ambiguous concerning the last
+   two bytes, and different drives interpret the standard differently.
+*/
+
+{	int	i, k, flg;
+	int	expect[5] = {1,1,1,0x14,0xeb};
+	long	flags;
+
+	pi_connect(PI);
+	WR(0,6,DRIVE);
+	WR(0,7,8);
+
+	save_flags(flags);
+	sti();
+
+	pf_sleep(2);
+
+        k = 0;
+        while ((k++ < PF_RESET_TMO) && (RR(1,6)&STAT_BUSY))
+                pf_sleep(10);
+
+	restore_flags(flags);
+
+	flg = 1;
+	for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+	if (verbose) {
+		printk("%s: Reset (%d) signature = ",PF.name,k);
+		for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+		if (!flg) printk(" (incorrect)");
+		printk("\n");
+	}
+	
+	pi_disconnect(PI);
+	return flg-1;	
+}
+
+static void pf_mode_sense( int unit )
+
+{       char    ms_cmd[12] = { ATAPI_MODE_SENSE,LUN,0,0,0,0,0,0,8,0,0,0};
+	char	buf[8];
+
+        pf_atapi(unit,ms_cmd,8,buf,DBMSG("mode sense"));
+	PF.media_status = PF_RW;
+	if (buf[3] & 0x80) PF.media_status = PF_RO;
+}
+
+static void xs( char *buf, char *targ, int offs, int len )
+
+{	int	j,k,l;
+
+	j=0; l=0;
+	for (k=0;k<len;k++) 
+	   if((buf[k+offs]!=0x20)||(buf[k+offs]!=l))
+		l=targ[j++]=buf[k+offs];
+	if (l==0x20) j--; targ[j]=0;
+}
+
+static int xl( char *buf, int offs )
+
+{	int	v,k;
+
+	v=0; 
+	for(k=0;k<4;k++) v=v*256+(buf[k+offs]&0xff);
+	return v;
+}
+
+static void pf_get_capacity( int unit )
+
+{	char    rc_cmd[12] = { ATAPI_CAPACITY,LUN,0,0,0,0,0,0,0,0,0,0};
+	char	buf[8];
+        int 	bs;
+
+	if (pf_atapi(unit,rc_cmd,8,buf,DBMSG("get capacity"))) {
+		PF.media_status = PF_NM;
+		return;
+	}
+	PF.capacity = xl(buf,0) + 1;  
+	bs = xl(buf,4);
+	if (bs != 512) {
+		PF.capacity = 0;
+		if (verbose) printk("%s: Drive %d, LUN %d,"
+		       		    " unsupported block size %d\n",
+			            PF.name,PF.drive,PF.lun,bs);
+		}
+}
+
+static int pf_identify( int unit )
+
+{	int 	dt, s;
+	char	*ms[2] = {"master","slave"};
+	char	mf[10], id[18];
+	char    id_cmd[12] = { ATAPI_IDENTIFY,LUN,0,0,36,0,0,0,0,0,0,0};
+	char	buf[36];
+
+        s = pf_atapi(unit,id_cmd,36,buf,"identify");
+	if (s) return -1;
+
+	dt = buf[0] & 0x1f;
+	if ((dt != 0) && (dt != 7)) {
+	  	if (verbose) 
+		   printk("%s: Drive %d, LUN %d, unsupported type %d\n",
+				PF.name,PF.drive,PF.lun,dt);
+	  	return -1;
+       	}
+
+	xs(buf,mf,8,8);
+	xs(buf,id,16,16);
+
+	PF.removable = (buf[1] & 0x80);
+
+	pf_mode_sense(unit);
+	pf_mode_sense(unit);
+	pf_mode_sense(unit);
+
+	pf_get_capacity(unit);
+
+        printk("%s: %s %s, %s LUN %d, type %d",
+		PF.name,mf,id,ms[PF.drive],PF.lun,dt);
+        if (PF.removable) printk(", removable");
+        if (PF.media_status == PF_NM) 
+                printk(", no media\n");
+        else {  if (PF.media_status == PF_RO) printk(", RO");
+                printk(", %d blocks\n",PF.capacity);
+        }
+
+	return 0;
+}
+
+static int pf_probe( int unit )
+
+/*	returns  0, with id set if drive is detected
+	        -1, if drive detection failed
+*/
+
+{	if (PF.drive == -1) {
+	   for (PF.drive=0;PF.drive<=1;PF.drive++)
+		if (!pf_reset(unit)) {
+		   if (PF.lun != -1) return pf_identify(unit);
+		   else for (PF.lun=0;PF.lun<8;PF.lun++) 
+                           if (!pf_identify(unit)) return 0;
+		}
+	} else {
+	   if (pf_reset(unit)) return -1;
+	   if (PF.lun != -1) return pf_identify(unit);
+	   for (PF.lun=0;PF.lun<8;PF.lun++) 
+	      if (!pf_identify(unit)) return 0;
+	}
+        return -1; 
+}
+
+static int pf_detect( void )
+
+{	int	k, unit;
+
+	printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
+		name,name,PF_VERSION,major,cluster,nice);
+
+	k = 0;
+	if (pf_drive_count == 0) {
+	    unit = 0;
+	    if (pi_init(PI,1,-1,-1,-1,-1,-1,pf_scratch,
+                        PI_PF,verbose,PF.name)) {
+	        if (!pf_probe(unit)) {
+			PF.present = 1;
+			k++;
+	        } else pi_release(PI);
+	    }
+
+	} else for (unit=0;unit<PF_UNITS;unit++) if (DU[D_PRT])
+	    if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+			DU[D_PRO],DU[D_DLY],pf_scratch,PI_PF,verbose,
+			PF.name)) { 
+                if (!pf_probe(unit)) {
+                        PF.present = 1;
+                        k++;
+                } else pi_release(PI);
+            }
+
+	if (k) return 0;
+
+	printk("%s: No ATAPI disk detected\n",name);
+	return -1;
+}
+
+/* The i/o request engine */
+
+static int pf_start( int unit, int cmd, int b, int c )
+
+{	int	i;
+	char	io_cmd[12] = {cmd,LUN,0,0,0,0,0,0,0,0,0,0};
+
+	for(i=0;i<4;i++) { 
+	   io_cmd[5-i] = b & 0xff;
+	   b = b >> 8;
+	}
+	
+	io_cmd[8] = c & 0xff;
+	io_cmd[7] = (c >> 8) & 0xff;
+
+	i = pf_command(unit,io_cmd,c*512,"start i/o");
+
+        udelay(1000);
+
+	return i;	
+}
+
+static int pf_ready( void )
+
+{	int	unit = pf_unit;
+
+	return (((RR(1,6)&(STAT_BUSY|pf_mask)) == pf_mask));
+}
+
+static void do_pf_request (void)
+
+{       struct buffer_head * bh;
+	struct request * req;
+	int unit;
+
+        if (pf_busy) return;
+repeat:
+        if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+        INIT_REQUEST;
+
+        pf_unit = unit = DEVICE_NR(CURRENT->rq_dev);
+        pf_block = CURRENT->sector;
+        pf_count = CURRENT->nr_sectors;
+
+	bh = CURRENT->bh;
+	req = CURRENT;
+	if (bh->b_reqnext)
+		printk("%s: OUCH: b_reqnext != NULL\n",PF.name);
+
+        if ((pf_unit >= PF_UNITS) || (pf_block+pf_count > PF.capacity)) {
+                end_request(0);
+                goto repeat;
+        }
+
+	pf_cmd = CURRENT->cmd;
+	pf_run = pf_count;
+        while ((pf_run <= cluster) &&
+	       (req = req->next) && 
+	       (pf_block+pf_run == req->sector) &&
+	       (pf_cmd == req->cmd) &&
+	       (pf_unit == DEVICE_NR(req->rq_dev)))
+			pf_run += req->nr_sectors;
+
+        pf_buf = CURRENT->buffer;
+        pf_retries = 0;
+
+
+        if (pf_cmd == READ) pi_do_claimed(PI,do_pf_read);
+        else if (pf_cmd == WRITE) pi_do_claimed(PI,do_pf_write);
+        else {  end_request(0);
+                goto repeat;
+        }
+}
+
+static void pf_next_buf( int unit )
+
+{	cli();
+	end_request(1);
+	if (!pf_run) { sti(); return; }
+	
+/* paranoia */
+
+	if ((!CURRENT) ||
+	    (CURRENT->cmd != pf_cmd) ||
+	    (DEVICE_NR(CURRENT->rq_dev) != pf_unit) ||
+	    (CURRENT->rq_status == RQ_INACTIVE) ||
+	    (CURRENT->sector != pf_block)) 
+		printk("%s: OUCH: request list changed unexpectedly\n",
+			PF.name);
+
+	pf_count = CURRENT->nr_sectors;
+	pf_buf = CURRENT->buffer;
+	sti();
+}
+
+static void do_pf_read( void )
+
+{       int	unit = pf_unit;
+
+	pf_busy = 1;
+
+        sti();
+
+	if (pf_start(unit,ATAPI_READ_10,pf_block,pf_run)) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+                        pf_retries++;
+                        pi_do_claimed(PI,do_pf_read);
+			return;
+                }
+                end_request(0);
+                pf_busy = 0;
+                cli();
+                do_pf_request();
+                return;
+        }
+	pf_mask=STAT_DRQ;
+        ps_set_intr(do_pf_read_drq,pf_ready,PF_TMO,nice);
+}
+
+static void do_pf_read_drq( void )
+
+{       int	unit = pf_unit;
+
+	sti();
+	while (1) {
+            if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,
+			"read block","completion") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+			pf_req_sense(unit,0);
+                        pf_retries++;
+                        pi_do_claimed(PI,do_pf_read);
+                        return;
+                }
+                end_request(0);
+                pf_busy = 0;
+                cli();
+                do_pf_request();
+                return;
+            }
+            pi_read_block(PI,pf_buf,512);
+            pf_count--; pf_run--;
+            pf_buf += 512;
+	    pf_block++;
+	    if (!pf_run) break;
+	    if (!pf_count) pf_next_buf(unit);
+        }
+        pi_disconnect(PI);
+        end_request(1);
+        pf_busy = 0;
+        cli();
+        do_pf_request();
+}
+
+static void do_pf_write( void )
+
+{       int	unit = pf_unit;
+
+	pf_busy = 1;
+
+        sti();
+
+	if (pf_start(unit,ATAPI_WRITE_10,pf_block,pf_run)) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+                        pf_retries++;
+                        pi_do_claimed(PI,do_pf_write);
+			return;
+                }
+                end_request(0);
+                pf_busy = 0;
+                cli();
+                do_pf_request();
+                return;
+        }
+
+	while (1) {
+            if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,
+			"write block","data wait") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+                        pf_retries++;
+                        pi_do_claimed(PI,do_pf_write);
+                        return;
+                }
+                end_request(0);
+                pf_busy = 0;
+                cli();
+                do_pf_request();
+                return;
+            }
+            pi_write_block(PI,pf_buf,512);
+	    pf_count--; pf_run--;
+	    pf_buf += 512;
+	    pf_block++;
+	    if (!pf_run) break;
+	    if (!pf_count) pf_next_buf(unit);
+	}
+	pf_mask = 0;
+        ps_set_intr(do_pf_write_done,pf_ready,PF_TMO,nice);
+}
+
+static void do_pf_write_done( void )
+
+{       int	unit = pf_unit;
+
+	sti();
+        if (pf_wait(unit,STAT_BUSY,0,"write block","done") & STAT_ERR) {
+                pi_disconnect(PI);
+                if (pf_retries < PF_MAX_RETRIES) {
+                        pf_retries++;
+			pi_do_claimed(PI,do_pf_write);
+                        return;
+                }
+                end_request(0);
+                pf_busy = 0;
+                cli();
+                do_pf_request();
+                return;
+        }
+        pi_disconnect(PI);
+        end_request(1);
+        pf_busy = 0;
+        cli();
+        do_pf_request();
+}
+
+/* end of pf.c */
+
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/pseudo.h linux/drivers/block/paride/pseudo.h
--- v2.1.76/linux/drivers/block/paride/pseudo.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/pseudo.h	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,138 @@
+/* 
+        pseudo.h    (c) 1997  Grant R. Guenther <grant@torque.net>
+                              Under the terms of the GNU public license.
+
+	This is the "pseudo-interrupt" logic for parallel port drivers.
+
+        This module is #included into each driver.  It makes one
+        function available:
+
+		ps_set_intr( void (*continuation)(void),
+			     int  (*ready)(void),
+			     int timeout,
+			     int nice )
+
+	Which will arrange for ready() to be evaluated frequently and
+	when either it returns true, or timeout jiffies have passed,
+	continuation() will be invoked.
+
+	If nice is true, the test will done approximately once a
+	jiffy.  If nice is 0, the test will also be done whenever
+	the scheduler runs (by adding it to a task queue).
+
+*/
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+
+static void ps_timer_int( unsigned long data);
+static void ps_tq_int( void *data);
+
+static int ps_use_tq = 1;
+static void (* ps_continuation)(void);
+static int (* ps_ready)(void);
+static int ps_then;
+static int ps_timeout;
+static int ps_timer_active = 0;
+static int ps_tq_active = 0;
+
+static struct timer_list ps_timer = {0,0,0,0,ps_timer_int};
+static struct tq_struct ps_tq = {0,0,ps_tq_int,NULL};
+
+static void ps_set_intr( void (*continuation)(void), 
+			 int (*ready)(void),
+			 int timeout, int nice )
+
+{       long	flags;
+
+	save_flags(flags); 
+	cli();
+
+	ps_continuation = continuation;
+	ps_ready = ready;
+        ps_then = jiffies;
+	ps_timeout = jiffies + timeout;
+	ps_use_tq = !nice;
+
+        if (ps_use_tq && !ps_tq_active) {
+#ifdef HAVE_DISABLE_HLT
+                disable_hlt();
+#endif
+		ps_tq_active = 1;
+                queue_task(&ps_tq,&tq_scheduler);
+	}
+
+        if (!ps_timer_active) {
+		ps_timer_active = 1;
+                ps_timer.expires = jiffies;
+                add_timer(&ps_timer);
+        }
+
+	restore_flags(flags);
+}
+
+static void ps_tq_int( void *data )
+
+{       void (*con)(void);
+	long flags;
+
+	save_flags(flags);
+	cli();
+
+        con = ps_continuation;
+
+#ifdef HAVE_DISABLE_HLT
+        enable_hlt();
+#endif
+
+        ps_tq_active = 0;
+
+        if (!con) {
+		restore_flags(flags);
+		return;
+	}
+        if (ps_ready() || (jiffies >= ps_timeout)) {
+                ps_continuation = NULL;
+		restore_flags(flags);
+                con();
+                return;
+                }
+
+#ifdef HAVE_DISABLE_HLT
+        disable_hlt();
+#endif
+
+        ps_tq_active = 1;
+	queue_task(&ps_tq,&tq_scheduler);
+	restore_flags(flags);
+}
+
+static void ps_timer_int( unsigned long data)
+
+{       void (*con)(void);
+	long	flags;
+
+	save_flags(flags);
+	cli();
+
+	con = ps_continuation;
+	ps_timer_active = 0;
+	if (!con) {
+		restore_flags(flags);
+		return;
+	}
+        if (ps_ready() || (jiffies >= ps_timeout)) {
+                ps_continuation = NULL;
+		restore_flags(flags);
+                con();
+		return;
+		}
+	ps_timer_active = 1;
+        ps_timer.expires = jiffies;
+        add_timer(&ps_timer);
+	restore_flags(flags);
+}
+
+/* end of pseudo.h */
+
diff -u --recursive --new-file v2.1.76/linux/drivers/block/paride/setup.h linux/drivers/block/paride/setup.h
--- v2.1.76/linux/drivers/block/paride/setup.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/setup.h	Sun Dec 28 12:05:45 1997
@@ -0,0 +1,56 @@
+/*
+	setup.h	   (c) 1997 Grant R. Guenther <grant@torque.net>
+		            Under the terms of the GNU public license.
+
+        This is a table driven setup function for kernel modules
+        using the module.variable=val,... command line notation.
+
+*/
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+struct setup_tab_t {
+
+	char	*tag;	/* variable name */
+	int	size;	/* number of elements in array */
+	int	*iv;	/* pointer to variable */
+};
+
+typedef struct setup_tab_t STT;
+
+/*  t 	  is a table that describes the variables that can be set
+	  by gen_setup
+    n	  is the number of entries in the table
+    ss	  is a string of the form:
+
+		<tag>=[<val>,...]<val>
+*/
+
+static void generic_setup( STT t[], int n, char *ss )
+
+{	int	j,k;
+
+	k = 0;
+	for (j=0;j<n;j++) {
+		k = strlen(t[j].tag);
+		if (strncmp(ss,t[j].tag,k) == 0) break;
+	}
+	if (j == n) return;
+
+	if (ss[k] == 0) {
+		t[j].iv[0] = 1;
+		return;
+	}
+
+	if (ss[k] != '=') return;
+	ss += (k+1);
+
+	k = 0;
+	while (ss && isdigit(*ss) && (k < t[j].size)) {
+		t[j].iv[k++] = simple_strtoul(ss,NULL,0);
+		if ((ss = strchr(ss,',')) != NULL) ss++;
+	}
+}
+
+/* end of setup.h */
diff -u --recursive --new-file v2.1.76/linux/drivers/block/trm290.c linux/drivers/block/trm290.c
--- v2.1.76/linux/drivers/block/trm290.c	Fri Dec 19 15:52:57 1997
+++ linux/drivers/block/trm290.c	Wed Dec 31 16:45:47 1997
@@ -220,10 +220,10 @@
 	 && !pcibios_read_config_dword(hwif->pci_bus, hwif->pci_fn, 0x20, &cfgbase) && cfgbase)
 	{
 		hwif->config_data = cfgbase & ~1;
-		printk("TRM290: chip config base at 0x%04x\n", hwif->config_data);
+		printk("TRM290: chip config base at 0x%04lx\n", hwif->config_data);
 	} else {
-		hwif->config_data = 0x3df4;
-		printk("TRM290: using default config base at 0x%04x\n", hwif->config_data);
+		hwif->config_data = 0x3df0;
+		printk("TRM290: using default config base at 0x%04lx\n", hwif->config_data);
 	}
 
 	save_flags(flags);
@@ -241,7 +241,7 @@
 		hwif->irq = hwif->channel ? 15 : 14;	/* legacy mode */
 	else if (!hwif->irq && hwif->mate && hwif->mate->irq)
 		hwif->irq = hwif->mate->irq;		/* sharing IRQ with mate */
-	ide_setup_dma(hwif, (hwif->channel ? hwif->config_data ^ 0x0080 : hwif->config_data) + 4, 2);
+	ide_setup_dma(hwif, (hwif->config_data + 4) ^ (hwif->channel ? 0x0080 : 0x0000), 2);
 	hwif->dmaproc = &trm290_dmaproc;
 	hwif->selectproc = &trm290_selectproc;
 	hwif->no_autodma = 1;				/* play it safe for now */
diff -u --recursive --new-file v2.1.76/linux/drivers/cdrom/Config.in linux/drivers/cdrom/Config.in
--- v2.1.76/linux/drivers/cdrom/Config.in	Tue Dec  2 16:45:18 1997
+++ linux/drivers/cdrom/Config.in	Sun Dec 28 12:05:45 1997
@@ -13,7 +13,6 @@
     fi
   fi
 fi
-tristate 'MicroSolutions backpack CDROM support' CONFIG_BPCD
 tristate 'Mitsumi (standard) [no XA/Multisession] CDROM support' CONFIG_MCD
 tristate 'Mitsumi [XA/MultiSession] CDROM support' CONFIG_MCDX
 tristate 'Optics Storage DOLPHIN 8000AT CDROM support' CONFIG_OPTCD
diff -u --recursive --new-file v2.1.76/linux/drivers/cdrom/Makefile linux/drivers/cdrom/Makefile
--- v2.1.76/linux/drivers/cdrom/Makefile	Tue Dec  2 16:45:18 1997
+++ linux/drivers/cdrom/Makefile	Sun Dec 28 12:05:45 1997
@@ -112,14 +112,6 @@
   endif
 endif #CONFIG_SJCD
 
-ifeq ($(CONFIG_BPCD),y)
-L_OBJS += bpcd.o
-else
-  ifeq ($(CONFIG_BPCD),m)
-  M_OBJS += bpcd.o
-  endif
-endif #CONFIG_BPCD
-
 ifeq ($(CONFIG_ISP16_CDI),y)
 L_OBJS += isp16.o
 else
diff -u --recursive --new-file v2.1.76/linux/drivers/cdrom/bpcd.c linux/drivers/cdrom/bpcd.c
--- v2.1.76/linux/drivers/cdrom/bpcd.c	Thu May 29 21:53:05 1997
+++ linux/drivers/cdrom/bpcd.c	Wed Dec 31 16:00:00 1969
@@ -1,793 +0,0 @@
-/* 
-	bpcd.c	(c) 1996  Grant R. Guenther <grant@torque.net>
-		          Under the terms of the GNU public license.
-
-	bpcd.c is a driver for the MicroSolutions "backpack" CDrom,
-	an external parallel port device.  
-
-	There are apparently two versions of the backpack protocol.  This 
-        driver knows about the version 2 protocol - as is used in the 4x 
-	and 6x products.  There is no support for the sound hardware that
-        is included in some models.  It should not be difficult to add 
-        support for the ATAPI audio play functions and the corresponding 
-        ioctls.
-
-	The driver was developed by reverse engineering the protocol
-        and testing it on the backpack model 164550.  This model
-	is actually a stock ATAPI drive packaged with a custom 
-	ASIC that implements the IDE over parallel protocol.
-	I tested with a backpack that happened to contain a Goldstar
-        drive, but I've seen reports of Sony and Mitsumi drives as well.
-
-	bpcd.c can be compiled for version 1.2.13 of the Linux kernel
-        and for the 2.0 series.  (It should also work with most of the
-        later 1.3 kernels.)
-
-        If you have a copy of the driver that has not been integrated into
-        the kernel source tree, you can compile the driver manually, as a 
-        module.  Ensure that /usr/include/linux and /usr/include/asm are 
-	links to the correct include files for the target system.  Compile 
-        the driver with
-
-                cc -D__KERNEL__ -DMODULE -O2 -c bpcd.c
-
-        You must then load it with insmod.  If you are using
-        MODVERSIONS, add the following to the cc command:
-
-		-DMODVERSIONS -I /usr/include/linux/modversions.h
-
-	Before attempting to access the new driver, you will need to
-        create a new device special file.  The following commands will
-	do that for you:
-
-		mknod /dev/bpcd b 41 0
-		chown root:disk /dev/bpcd
-		chmod 660 /dev/bpcd
-
-	Afterward, you can mount a disk in the usual way:
-
-		mount -t iso9660 /dev/bpcd /cdrom
-
-	(assuming you have made a directory /cdrom to use as a
-        mount point).
-
-	The driver will attempt to detect which parallel port your
-        backpack is connected to.  If this fails for any reason, you
-        can override it by specifying a port on the LILO command line
-        (for built in drivers) or the insmod command (for drivers built
-        as modules).   If your drive is on the port at 0x3bc, you would
-        use one of these commands:
-
-		LILO:	   bpcd=0x3bc
-
-		insmod:    insmod bpcd bp_base=0x3bc
-
-	The driver can detect if the parallel port supports 8-bit
-        transfers.  If so, it will use them.  You can force it to use
-        4-bit (nybble) mode by setting the variable bp_nybble to 1 on
-        an insmod command, or using the following LILO parameters:
-
-		bpcd=0x3bc,1
-
-	(you must specify the correct port address if you use this method.)
-
-	There is currently no support for EPP or ECP modes.  Also,
-        as far as I can tell, the MicroSolutions protocol does not
-        support interrupts in the 4-bit and 8-bit modes.
-
-	MicroSolutions' protocol allows for several drives to be
-        chained together off the same parallel port.  Currently, this
-        driver will recognise only one of them.  If you do have more
-        than one drive, it will choose the one with the lowest id number,
-        where the id number is the last two digits of the product's
-        serial number.
-
-	It is not currently possible to connect a printer to the chained
-        port on the BackPack and expect Linux to use both devices at once.
-
-	If you need to use this driver together with a printer on the
-        same port, build both the bpcd and lp drivers are modules.
-
-	Keep an eye on http://www.torque.net/bpcd.html for news and
-        other information about the driver.  If you have any problems
-        with this driver, please send me, grant@torque.net, some mail 
-        directly before posting into the newsgroups or mailing lists.
-
-*/
-
-#define	BP_VERSION	"0.14" 
-
-#define	BP_BASE		0	/* set to 0 for autoprobe */
-#define BP_REP		4
-
-#include <linux/version.h>
-#include <linux/module.h>
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/fs.h>
-#include <linux/major.h>
-#include <linux/kernel.h>
-#include <linux/tqueue.h>
-#include <linux/delay.h>
-#include <linux/cdrom.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-
-#ifndef BPCD_MAJOR
-#define BPCD_MAJOR   41	
-#endif
-
-#define MAJOR_NR BPCD_MAJOR
-
-/* set up defines for blk.h,  why don't all drivers do it this way ? */
-
-#define DEVICE_NAME "BackPack"
-#define DEVICE_REQUEST do_bp_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#include <linux/blk.h>
-
-#define BP_TMO		   300		/* timeout in jiffies */
-#define BP_DELAY            50          /* spin delay in uS */
-
-#define BP_SPIN		(10000/BP_DELAY)*BP_TMO
-
-int bpcd_init(void);
-void bpcd_setup(char * str, int * ints);
-void cleanup_module( void );
-
-static int 	bp_open(struct inode *inode, struct file *file);
-static void 	do_bp_request(void);
-static void 	do_bp_read(void);
-static int 	bp_ioctl(struct inode *inode,struct file *file,
-                         unsigned int cmd, unsigned long arg);
-static int 	bp_release (struct inode *inode, struct file *file);
-
-static int 	bp_detect(void);
-static int      bp_lock(void);
-static void     bp_unlock(void);
-static void     bp_eject(void);
-static void     bp_interrupt(void *data);
-
-static int	bp_base = BP_BASE;
-static int	bp_rep = BP_REP;
-static int	bp_nybble = 0;		/* force 4-bit mode ? */
-
-static int      bp_unit_id;
-
-static int bp_access = 0;		/* count of active opens ... */
-static int bp_mode = 1;			/* 4- or 8-bit mode */
-static int bp_busy = 0;			/* request being processed ? */
-static int bp_timeout;                  /* "interrupt" loop limiter */
-static int bp_sector;			/* address of next requested sector */
-static int bp_count;			/* number of blocks still to do */
-static char * bp_buf;			/* buffer for request in progress */
-static char bp_buffer[2048];		/* raw block buffer */
-static int bp_bufblk = -1;		/* block in buffer, in CD units,
-					   -1 for nothing there */
-static  int	nyb_map[256];		/* decodes a nybble */
-static	int	PortCache = 0;		/* cache of the control port */
-
-static struct tq_struct bp_tq = {0,0,bp_interrupt,NULL}; 
-
-/* kernel glue structures */
-
-static struct file_operations bp_fops = {
-	NULL,			/* lseek - default */
-	block_read,		/* read - general block-dev read */
-	block_write,		/* write - general block-dev write */
-	NULL,			/* readdir - bad */
-	NULL,			/* poll */
-	bp_ioctl,		/* ioctl */
-	NULL,			/* mmap */
-	bp_open,		/* open */
-	bp_release,		/* release */
-	block_fsync,		/* fsync */
-	NULL,			/* fasync */
-	NULL,                   /* media change ? */
-	NULL			/* revalidate new media */
-};
-
-
-/* the MicroSolutions protocol uses bits 3,4,5 & 7 of the status port to
-   deliver a nybble in 4 bit mode.  We use a table lookup to extract the
-   nybble value.  The following function initialises the table.
-*/
-
-__initfunc(static void init_nyb_map( void ))
-
-{	int	i, j;
-
-	for(i=0;i<256;i++) { 
-	   j = (i >> 3) & 0x7;
-	   if (i & 0x80) j |= 8;
-	   nyb_map[i] = j;
-	}
-}
-
-__initfunc(int bpcd_init (void))	/* preliminary initialisation */
-
-{	init_nyb_map();
-
-        if (bp_detect()) return -1;
-
-	if (register_blkdev(MAJOR_NR,"bpcd",&bp_fops)) {
-		printk("bpcd: unable to get major number %d\n",MAJOR_NR);
-		return -1;
-	}
-	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-	read_ahead[MAJOR_NR] = 8;	/* 8 sector (4kB) read ahead */
-
-	return 0;
-}
-
-static int bp_open (struct inode *inode, struct file *file)
-
-{	
-	if (file->f_mode & 2) return -EROFS;  /* wants to write ? */
-
-	MOD_INC_USE_COUNT;
-
-	if (!bp_access)
-	   if (bp_lock()) {
-		MOD_DEC_USE_COUNT;
-		return -ENXIO;
-	   }
-	bp_access++;
-	return 0;
-}
-
-static void do_bp_request (void)
-
-{       if (bp_busy) return;
-        while (1) {
-	    if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
-	    INIT_REQUEST;
-	    if (CURRENT->cmd == READ) {
-	        bp_sector = CURRENT->sector;
-	        bp_count = CURRENT->nr_sectors;
-	        bp_buf = CURRENT->buffer;
-	        do_bp_read();
-		if (bp_busy) return;
-	    } 
-	    else end_request(0);
-	}
-}
-
-static int bp_ioctl(struct inode *inode,struct file *file,
-		    unsigned int cmd, unsigned long arg)
-
-/* we currently support only the EJECT ioctl. */
-
-{	switch (cmd) {
-            case CDROMEJECT: if (bp_access == 1) {
-				bp_eject();
-				return 0;
-			     }
-	    default:
-	        return -EINVAL;
-	}
-}
-
-static int bp_release (struct inode *inode, struct file *file)
-
-{	kdev_t	devp;
-
-	bp_access--;
-	if (!bp_access) { 
-	    devp = inode->i_rdev;
-	    fsync_dev(devp);
-	    invalidate_inodes(devp);
-	    invalidate_buffers(devp);
-	    bp_unlock();
-	}
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-#ifdef MODULE
-
-/* Glue for modules ... */
-
-int	init_module(void)
-
-{	int	err;
-	long    flags;
-
-	save_flags(flags);
-	cli();
-
-	err = bpcd_init();
-
-	restore_flags(flags);
-	return err;
-}
-
-void	cleanup_module(void)
-
-{	long flags;
-
-	save_flags(flags);
-	cli();
-	unregister_blkdev(MAJOR_NR,"bpcd");
-	release_region(bp_base,3);
-	restore_flags(flags);
-}
-
-#else 
-
-/* bpcd_setup:  process lilo command parameters ...
-
-   syntax:	bpcd=base[,nybble[,rep]]
-*/
-
-__initfunc(void bpcd_setup(char *str, int *ints))
-
-{       if (ints[0] > 0) bp_base = ints[1];
-        if (ints[0] > 1) bp_nybble = ints[2];
-        if (ints[0] > 2) bp_rep = ints[3];
-} 
-
-#endif
-
-static	void    out_p( short port, char byte)
-
-{       int i;
-
-	for(i=0;i<bp_rep;i++) outb(byte,bp_base+port);
-}
-
-static	int     in_p( short port)
-
-{       int i;
-	char c;
-
-	for(i=0;i<bp_rep;i++) c=inb(bp_base+port);
-	c=inb(bp_base+port);
-	return c & 0xff;
-}
-
-/* Unlike other PP devices I've worked on, the backpack protocol seems
-   to be driven by *changes* in the values of certain bits on the control
-   port, rather than their absolute value.  Hence the unusual macros ...
-*/
-
-#define w0(byte)  		out_p(0,byte)
-#define r0()      		(in_p(0) & 0xff)
-#define w1(byte)  		out_p(1,byte)
-#define r1()      		(in_p(1) & 0xff)
-#define w2(byte)  		out_p(2,byte) ; PortCache = byte
-#define t2(pat)   		PortCache ^= pat; out_p(2,PortCache)
-#define e2()			PortCache &= 0xfe; out_p(2,PortCache)
-#define o2()			PortCache |= 1; out_p(2,PortCache)
-
-static int read_byte( void )
-
-{	int l, h;
-
-	t2(4); 
-	if (bp_mode == 2) return r0();
-	l = nyb_map[r1()];
-	t2(4);
-	h = nyb_map[r1()];
-	return (h << 4) + l;
-}
-
-static int read_regr( char regr )
-
-{       int r;
-
-       	w0(regr & 0xf); w0(regr); t2(2);
-	if (bp_mode == 2) { e2(); t2(0x20); }
-	r = read_byte();
-	if (bp_mode == 2) { t2(1); t2(0x20); }
-	return r;
-}	
-
-static void write_regr( char regr, char val )
-
-{	w0(regr);
-	t2(2);
-	w0(val);
-	o2(); t2(4); t2(1);
-}
-
-static void write_cmd( char * cmd, int len )
-
-{	int i, f;
-
-	if (bp_mode == 2) f = 0x10; else f = 0;
-	write_regr(4,0x40|f);
-	w0(0x40); t2(2); t2(1);
-	i = 0;
-	while (i < len) {
-	   w0(cmd[i++]); 
-	   t2(4);
-	}
-	write_regr(4,f);
-}
-
-static void read_data( char * buf, int len )
-
-{	int i, f;
-
-	if (bp_mode == 2) f = 0x50; else f = 0x40;
-	write_regr(4,f);
-	w0(0x40); t2(2);
-	if (bp_mode == 2) t2(0x20);
-	for(i=0;i<len;i++) buf[i] = read_byte();
-	if (bp_mode == 2) { t2(1); t2(0x20); }
-}
-
-__initfunc(static int probe( int id ))
-
-{	int l, h, t;
-	int r = -1;
-
-	w2(4); w2(0xe); w2(0xec); w1(0x7f);
-	if ((r1() & 0xf8) != 0x78) return -1;
-	w0(255-id); w2(4); w0(id);
-	t2(8); t2(8); t2(8);
-	t2(2); t = (r1() & 0xf8);
-	if (t != 0x78) {
-		l = nyb_map[t];
-		t2(2); h = nyb_map[r1()];
-		t2(8);
-		r = 0;
-	}
-	w0(0); t2(2); w2(0x4c); w0(0x13);
-	return r;
-}
-	
-static void connect ( void  )
-
-{	int	f;
-
-	w0(0xff-bp_unit_id); w2(4); w0(bp_unit_id);
-	t2(8); t2(8); t2(8); 
-	t2(2); t2(2); t2(8);
-	if (bp_mode == 2) f = 0x10; else f = 0;
-	write_regr(4,f);
-	write_regr(5,8);
-	write_regr(0x46,0x10);
-	write_regr(0x4c,0x38);
-	write_regr(0x4d,0x88);
-	write_regr(0x46,0xa0);
-	write_regr(0x41,0);
-	write_regr(0x4e,8);
-}
-
-static void disconnect ( void )
-
-{	w0(0); t2(2); w2(0x4c);
-	if (bp_mode == 2) w0(0xff); else  w0(0x13);
-} 
-
-static int bp_wait_drq( char * lab, char * fun )
-
-{	int j, r, e;
-
-	j = 0;
-
-	while (1)  {
-        	r = read_regr(0x47);
-		e = read_regr(0x41);
-		if ((r & 9) || (j++ >= BP_SPIN)) break;
-		udelay(BP_DELAY);
-	}
-
-	if ((j >= BP_SPIN) || (r & 1)) {
-	    if (lab && fun) 
-		   printk("bpcd: %s (%s): %s status: %x error: %x\n",
-			  lab,fun,(j >= BP_SPIN)?"timeout, ":"",r,e);
-	    return -1;
-	}
-	return 0;
-}
-
-static int bp_wait( char * lab, char * fun )
-
-{	int j, r, e;
-
-	j = 0;
-	while ((!(read_regr(0xb) & 0x80)) && (j++ < BP_SPIN)) udelay(BP_DELAY);
-        r = read_regr(0x47);
-	e = read_regr(0x41);
-	if ((j >= BP_SPIN) || (r & 1)) {
-	    if (lab && fun) 
-		   printk("bpcd: %s (%s): %s status: %x error: %x\n",
-			  lab,fun,(j >= BP_SPIN)?"timeout, ":"",r,e);
-	    return -1;
-	}
-	return 0;
-}
-
-static int bp_command( char * cmd, int dlen, char * fun )
-
-{	int	r;
-
-	connect();
-	write_regr(0x44,dlen % 256);
-	write_regr(0x45,dlen / 256);
-	write_regr(0x46,0xa0);		/* drive 0 */
-	write_regr(0x47,0xa0);		/* ATAPI packet command */
-	if ((r=bp_wait_drq("bp_command",fun))) {
-	   disconnect();
-	   return r;
-	}
-	write_cmd(cmd,12);
-	return 0;
-}
-
-static int bp_completion( char * fun )
-
-{	int r, n;
-
-	if (!(r=bp_wait("bp_completion",fun))) {
-	    if (read_regr(0x42) == 2) {
-	        n = (read_regr(0x44) + 256*read_regr(0x45));
-	        read_data(bp_buffer,n);
-	        r=bp_wait("transfer done",fun);
-	    }
-	}
-	disconnect(); 
-	return r;
-}
-
-static int bp_atapi( char * cmd, int dlen, char * fun )
-
-{	int r;
-
-	if (!(r=bp_command(cmd,dlen,fun)))
-	  r = bp_completion(fun);
-	return r;
-}
-
-static int bp_req_sense( char * msg )
-
-{	char	rs_cmd[12] = { 0x03,0,0,0,18,0,0,0,0,0,0,0 };
-	int r;
-
-	r = bp_atapi(rs_cmd,18,"request sense");
-	if (msg) printk("bpcd: %s:  sense key: %x, ASC: %x, ASQ: %x\n",msg,
-			 bp_buffer[2]&0xf, bp_buffer[12], bp_buffer[13]);
-	return r;
-}
-
-static int bp_lock(void)
-
-{	char	lo_cmd[12] = { 0x1e,0,0,0,1,0,0,0,0,0,0,0 };
-	char	cl_cmd[12] = { 0x1b,0,0,0,3,0,0,0,0,0,0,0 };
-
-	bp_atapi(cl_cmd,0,"close door");
-        if (bp_req_sense(NULL)) return -1;  /* check for disk */
-	bp_atapi(lo_cmd,0,NULL);
-	bp_req_sense(NULL);  /* in case there was a media change */
-	bp_atapi(lo_cmd,0,"lock door");
-	return 0;
-}
-
-static void bp_unlock( void)
-
-{	char	un_cmd[12] = { 0x1e,0,0,0,0,0,0,0,0,0,0,0 };
-
-	bp_atapi(un_cmd,0,"unlock door");
-}
-
-static void bp_eject( void)
-
-{	char	ej_cmd[12] = { 0x1b,0,0,0,2,0,0,0,0,0,0,0 };
-
-	bp_unlock();
-	bp_atapi(ej_cmd,0,"eject");
-}
-
-__initfunc(static int bp_reset( void ))
-
-/* the ATAPI standard actually specifies the contents of all 7 registers
-   after a reset, but the specification is ambiguous concerning the last
-   two bytes, and different drives interpret the standard differently.
-*/
-
-{	int	i, flg;
-	int	expect[5] = {1,1,1,0x14,0xeb};
-	long	flags;
-
-	connect();
-	write_regr(0x46,0xa0);
-	write_regr(0x47,8);
-
-	save_flags(flags);
-	sti();
-	udelay(500000);  	/* delay 0.5 seconds */
-	restore_flags(flags);
-
-	flg = 1;
-	for(i=0;i<5;i++) flg &= (read_regr(i+0x41) == expect[i]);
-	
-	disconnect();
-	return flg-1;	
-}
-
-__initfunc(static int bp_identify( char * id ))
-
-{	int k;
-	char   id_cmd[12] = {0x12,0,0,0,36,0,0,0,0,0,0,0};
-
-	bp_bufblk = -1;
-        if (bp_atapi(id_cmd,36,"identify")) return -1;
-	for (k=0;k<16;k++) id[k] = bp_buffer[16+k];
-	id[16] = 0;
-	return 0;
-}
-
-__initfunc(static int bp_port_check( void )) 	/* check for 8-bit port */
-
-{	int	r;
-
-        w2(0); 
-	w0(0x55); if (r0() != 0x55) return 0;
-	w0(0xaa); if (r0() != 0xaa) return 0;
-	w2(0x20); w0(0x55); r = r0(); w0(0xaa);
-	if (r0() == r) return 2;
-	if (r0() == 0xaa) return 1;
-	return 0;
-}
-
-__initfunc(static int bp_locate( void ))
-
-{	int	k;
-
-	for(k=0;k<100;k++) 
-	  if (!probe(k)) {
-		bp_unit_id = k;
-		return 0;
-	  }
-	return -1;
-}
-
-__initfunc(static int bp_do_detect( int autop ))
-
-{	char   id[18];
-
-	if (autop) bp_base = autop;
-
-	if (check_region(bp_base,3)) {
-		if (!autop) 
-		  printk("bpcd: Ports at 0x%x are not available\n",bp_base);
-		return -1;
-	}
-
-	bp_mode = bp_port_check();
-
-	if (!bp_mode) {
-		if (!autop)
-		  printk("bpcd: No parallel port at 0x%x\n",bp_base);
-		return -1;
-	}
-
-	if (bp_nybble) bp_mode = 1;
-
-	if (bp_locate()) {
-		if (!autop)
-		  printk("bpcd: Couldn't find a backpack adapter at 0x%x\n",
-			  bp_base);
-		return -1;
-	}
-
-	if (bp_reset()) {
-		if (!autop)
-		  printk("bpcd: Failed to reset CD drive\n");
-		return -1;
-	}
-
-	if (bp_identify(id)) {
-		if (!autop)
-		  printk("bpcd: ATAPI inquiry failed\n");
-		return -1;
-	}
-
-	request_region(bp_base,3,"bpcd");
-
-	printk("bpcd: Found %s, ID %d, using port 0x%x in %d-bit mode\n",
-	       id,bp_unit_id,bp_base,4*bp_mode);
-
-	return 0;
-}
-
-/* If you know about some other weird parallel port base address,
-   add it here ....
-*/
-
-__initfunc(static int bp_detect( void ))
-
-{	if (bp_base) return bp_do_detect(0);
-	if (!bp_do_detect(0x378)) return 0;
-	if (!bp_do_detect(0x278)) return 0;
-	if (!bp_do_detect(0x3bc)) return 0;
-	printk("bpcd: Autoprobe failed\n");
-	return -1;
-}
-
-
-static void bp_transfer( void )
-
-{	int	k, o;
-
-	while (bp_count && (bp_sector/4 == bp_bufblk)) {
-		o = (bp_sector % 4) * 512;
-		for(k=0;k<512;k++) bp_buf[k] = bp_buffer[o+k];
-		bp_count--;
-		bp_buf += 512;
-		bp_sector++;
-	}
-}
-
-static void do_bp_read( void )
-
-{	int	b, i;
-	char	rd_cmd[12] = {0xa8,0,0,0,0,0,0,0,0,1,0,0};
-
-	bp_busy = 1;
-	bp_transfer();
-	if (!bp_count) {
-		end_request(1);
-		bp_busy = 0;
-		return;
-	}
-	sti();
-
-	bp_bufblk = bp_sector / 4;
-        b = bp_bufblk;
-	for(i=0;i<4;i++) { 
-	   rd_cmd[5-i] = b & 0xff;
-	   b = b >> 8;
-	}
-
-	if (bp_command(rd_cmd,2048,"read block")) {
-		bp_bufblk = -1; 
-		bp_busy = 0;
-		cli();
-		bp_req_sense("send read command");
-		end_request(0);
-		return;
-	}
-	bp_timeout = jiffies + BP_TMO;
-	queue_task(&bp_tq,&tq_scheduler); 
-}
-
-static void bp_interrupt( void *data)
-
-{	if (!(read_regr(0xb) & 0x80)) {
-		if (jiffies > bp_timeout) {
-			bp_bufblk = -1;
-			bp_busy = 0;
-			bp_req_sense("interrupt timeout");
-			end_request(0);
-			do_bp_request();
-		}
-		queue_task(&bp_tq,&tq_scheduler); 
-		return;
-	}
-	sti();
-	if (bp_completion("read completion")) {
-		cli();
-		bp_busy = 0;
-		bp_bufblk = -1;
-		bp_req_sense("read completion");
-		end_request(0);
-		do_bp_request();
-	}
-	do_bp_read();
-	do_bp_request();
-}
-	
-/* end of bpcd.c */
diff -u --recursive --new-file v2.1.76/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- v2.1.76/linux/drivers/cdrom/cdrom.c	Thu Dec  4 14:53:55 1997
+++ linux/drivers/cdrom/cdrom.c	Wed Dec 31 11:34:16 1997
@@ -68,8 +68,8 @@
 #include <asm/uaccess.h>
 
 
-#define VERSION "$Id: cdrom.c,v 2.0 1997/11/20 01:58:03 erik Exp $"
-#define REVISION "revision 2.0"
+#define VERSION "$Id: cdrom.c,v 2.1 1997/12/28 15:11:47 david Exp $"
+#define REVISION "$Revision: 2.1 $"
 #define FM_WRITE	0x2                 /* file mode write bit */
 
 /* When VERBOSE_STATUS_INFO is not defined, the debugging printks don't 
@@ -177,7 +177,8 @@
 	ENSURE(reset, CDC_RESET);
 	ENSURE(audio_ioctl, CDC_PLAY_AUDIO);
 	ENSURE(dev_ioctl, CDC_IOCTLS);
-	cdi->options = CDO_AUTO_CLOSE | CDO_USE_FFLAGS | CDO_LOCK | CDO_CHECK_TYPE;
+	cdi->options = CDO_AUTO_CLOSE | CDO_USE_FFLAGS | CDO_LOCK;
+					/* default compatibility mode */
 	cdi->mc_flags = 0;
 	cdo->n_minors = 0;
 	cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name);
@@ -305,15 +306,13 @@
 		ret=-ENOMEDIUM;
 		goto clean_up_and_return;
 	}
-#if 0
-	/* this breaks CD-Players which don't use O_NONBLOCK, workman
-	 * for example, probably others too */
+	/* CD-Players which don't use O_NONBLOCK, workman
+	 * for example, need bit CDO_CHECK_TYPE cleared! */
 	if (cdi->options & CDO_CHECK_TYPE && tracks.data==0) {
 		cdinfo(CD_OPEN, "bummer. wrong media type...\n"); 
 		ret=-EMEDIUMTYPE;
 		goto clean_up_and_return;
 	}
-#endif
 
 	cdinfo(CD_OPEN, "all seems well, opening the device...\n"); 
 
@@ -716,6 +715,10 @@
 	   returns with the above, but this more clearly demonstrates
 	   the problem with the current interface.  Too bad this wasn't 
 	   designed to use bitmasks...         -Erik 
+
+	   Well, now we have the option CDS_MIXED: a mixed-type CD. 
+	   User level programmers might feel the ioctl is not very useful.
+	   					---david
 	*/
 	case CDROM_DISC_STATUS: {
 		tracktype tracks;
@@ -725,11 +728,14 @@
 			return(tracks.error);
 
 		/* Policy mode on */
-		if (tracks.audio>0 && tracks.data==0 && tracks.cdi==0 && tracks.xa==0) 
-			return CDS_AUDIO;
-		if (tracks.cdi>0) return CDS_XA_2_2;
-		if (tracks.xa>0) return CDS_XA_2_1;
-		if (tracks.data>0) return CDS_DATA_1;
+		if (tracks.audio > 0) {
+			if (tracks.data==0 && tracks.cdi==0 && tracks.xa==0) 
+				return CDS_AUDIO;
+			else return CDS_MIXED;
+		}
+		if (tracks.cdi > 0) return CDS_XA_2_2;
+		if (tracks.xa > 0) return CDS_XA_2_1;
+		if (tracks.data > 0) return CDS_DATA_1;
 		/* Policy mode off */
 
 		cdinfo(CD_WARNING,"This disc doesn't have any tracks I recognise!\n");
diff -u --recursive --new-file v2.1.76/linux/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c
--- v2.1.76/linux/drivers/cdrom/cm206.c	Tue Dec  2 16:45:18 1997
+++ linux/drivers/cdrom/cm206.c	Wed Dec 31 11:34:16 1997
@@ -1,5 +1,6 @@
 /* cm206.c. A linux-driver for the cm206 cdrom player with cm260 adapter card.
-   Copyright (c) 1995, 1996 David van Leeuwen.
+   Copyright (c) 1995--1997 David A. van Leeuwen.
+   $Id: cm206.c,v 1.5 1997/12/26 11:02:51 david Exp $
    
      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
@@ -70,40 +71,84 @@
 	      open only for ioctl operation, e.g., for operation of
 	      tray etc.
  4 apr 1996:  0.97 First implementation of layer between VFS and cdrom
-              driver, a Uniform interface. Much of the functionality
+              driver, a generic interface. Much of the functionality
 	      of cm206_open() and cm206_ioctl() is transferred to a
-	      new file cdrom.c and its header cdrom.h. 
+	      new file cdrom.c and its header ucdrom.h. 
 
 	      Upgrade to Linux kernel 1.3.78. 
 
  11 apr 1996  0.98 Upgrade to Linux kernel 1.3.85
               More code moved to cdrom.c
+ 
+ 	      0.99 Some more small changes to decrease number
+ 	      of oopses at module load; 
+ 
+ 27 jul 1996  0.100 Many hours of debugging, kernel change from 1.2.13
+	      to 2.0.7 seems to have introduced some weird behavior
+	      in (interruptible_)sleep_on(&cd->data): the process
+	      seems to be woken without any explicit wake_up in my own
+	      code. Patch to try 100x in case such untriggered wake_up's 
+	      occur. 
 
-	      0.99 Some more small changes to decrease number
-	      of oopses at module load; 
-
-	      Branch from here:
+ 28 jul 1996  0.101 Rewriting of the code that receives the command echo,
+	      using a fifo to store echoed bytes. 
 
-	      0.99.1.0 Update to kernel release 2.0.10 dev_t -> kdev_t
-	      (emoenke) various typos found by others.  extra
-	      module-load oops protection.
-
-	      0.99.1.1 Initialization constant cdrom_dops.speed
-	      changed from float (2.0) to int (2); Cli()-sti() pair
-	      around cm260_reset() in module initialization code.
-
-	      0.99.1.2 Changes literally as proposed by Scott Snyder
-	      <snyder@d0sgif.fnal.gov>, which have to do mainly with
-	      the poor minor support i had. The major new concept is
-	      to change a cdrom driver's operations struct from the
-	      capabilities struct. This reflects the fact that there
-	      is one major for a driver, whilst there can be many
-	      minors whith completely different capabilities.
+ 	      Branch from 0.99:
+ 
+ 	      0.99.1.0 Update to kernel release 2.0.10 dev_t -> kdev_t
+ 	      (emoenke) various typos found by others.  extra
+ 	      module-load oops protection.
+ 
+ 	      0.99.1.1 Initialization constant cdrom_dops.speed
+ 	      changed from float (2.0) to int (2); Cli()-sti() pair
+ 	      around cm260_reset() in module initialization code.
+ 
+ 	      0.99.1.2 Changes literally as proposed by Scott Snyder
+ 	      <snyder@d0sgif.fnal.gov> for the 2.1 kernel line, which
+ 	      have to do mainly with the poor minor support i had. The
+ 	      major new concept is to change a cdrom driver's
+ 	      operations struct from the capabilities struct. This
+ 	      reflects the fact that there is one major for a driver,
+ 	      whilst there can be many minors whith completely
+ 	      different capabilities.
 
 	      0.99.1.3 More changes for operations/info separation.
 
 	      0.99.1.4 Added speed selection (someone had to do this
 	      first).
+
+  23 jan 1997 0.99.1.5 MODULE_PARMS call added.
+
+  23 jan 1997 0.100.1.2--0.100.1.5 following similar lines as 
+  	      0.99.1.1--0.99.1.5. I get too many complaints about the
+	      drive making read errors. What't wrong with the 2.0+
+	      kernel line? Why get i (and othe cm206 owners) weird
+	      results? Why were things good in the good old 1.1--1.2 
+	      era? Why don't i throw away the drive?
+
+ 2 feb 1997   0.102 Added `volatile' to values in cm206_struct. Seems to 
+ 	      reduce many of the problems. Rewrote polling routines
+	      to use fixed delays between polls. 
+	      0.103 Changed printk behavior. 
+	      0.104 Added a 0.100 -> 0.100.1.1 change
+
+11 feb 1997   0.105 Allow auto_probe during module load, disable
+              with module option "auto_probe=0". Moved some debugging
+	      statements to lower priority. Implemented select_speed()
+	      function. 
+
+13 feb 1997   1.0 Final version for 2.0 kernel line. 
+
+	      All following changes will be for the 2.1 kernel line. 
+
+15 feb 1997   1.1 Keep up with kernel 2.1.26, merge in changes from 
+              cdrom.c 0.100.1.1--1.0. Add some more MODULE_PARMS. 
+
+14 sep 1997   1.2 Upgrade to Linux 2.1.55.  Added blksize_size[], patch
+              sent by James Bottomley <James.Bottomley@columbiasc.ncr.com>.
+
+21 dec 1997   1.4 Upgrade to Linux 2.1.72.  
+
  * 
  * Parts of the code are based upon lmscd.c written by Kai Petzke,
  * sbpcd.c written by Eberhard Moenkeberg, and mcd.c by Martin
@@ -123,8 +168,8 @@
  * - Philips/LMS cm206 and cm226 product specification
  * - Philips/LMS cm260 product specification
  *
- *                       David van Leeuwen, david@tm.tno.nl.  */
-#define VERSION "$Id: cm206.c,v 0.99.1.4 1996/12/23 21:46:13 david Exp $"
+ * David van Leeuwen, david@tm.tno.nl.  */
+#define REVISION "$Revision: 1.5 $"
 
 #include <linux/module.h>	
 
@@ -140,6 +185,8 @@
 #include <linux/malloc.h>
 #include <linux/init.h>
 
+/* #include <linux/ucdrom.h> */
+
 #include <asm/io.h>
 
 #define MAJOR_NR CM206_CDROM_MAJOR
@@ -147,7 +194,7 @@
 
 #undef DEBUG
 #define STATISTICS		/* record times and frequencies of events */
-#undef AUTO_PROBE_MODULE
+#define AUTO_PROBE_MODULE
 #define USE_INSW
 
 #include "cm206.h"
@@ -160,15 +207,18 @@
 
 static int cm206_base = CM206_BASE;
 static int cm206_irq = CM206_IRQ; 
-MODULE_PARM(cm206_base, "i");
-MODULE_PARM(cm206_irq, "i");
+MODULE_PARM(cm206_base, "i");	/* base */
+MODULE_PARM(cm206_irq, "i");	/* irq */
+MODULE_PARM(cm206, "1-2i");	/* base,irq or irq,base */
+MODULE_PARM(auto_probe, "i");	/* auto probe base and irq */
 
-#define POLLOOP 10000
+#define POLLOOP 100		/* milliseconds */
 #define READ_AHEAD 1		/* defines private buffer, waste! */
 #define BACK_AHEAD 1		/* defines adapter-read ahead */
 #define DATA_TIMEOUT (3*HZ)	/* measured in jiffies (10 ms) */
 #define UART_TIMEOUT (5*HZ/100)
 #define DSB_TIMEOUT (7*HZ)	/* time for the slowest command to finish */
+#define UR_SIZE 4		/* uart receive buffer fifo size */
 
 #define LINUX_BLOCK_SIZE 512	/* WHERE is this defined? */
 #define RAW_SECTOR_SIZE 2352	/* ok, is also defined in cdrom.h */
@@ -181,13 +231,14 @@
 		     cd->last_stat[st_ ## i] = cd->stat_counter++; \
 		 }
 #else
-#define stats(i) (void) 0
+#define stats(i) (void) 0;
 #endif
 
-#ifdef DEBUG			/* from lmscd.c */
-#define debug(a) printk a
+#define Debug(a) {printk (KERN_DEBUG); printk a;}
+#ifdef DEBUG
+#define debug(a) Debug(a)
 #else
-#define debug(a) (void) 0
+#define debug(a) (void) 0;
 #endif
 
 typedef unsigned char uch;	/* 8-bits */
@@ -197,21 +248,23 @@
   uch track, fsm[3], q0;
 };
 
+static int cm206_blocksizes[1] = { 2048 };
+
 struct cm206_struct {
-  ush intr_ds;	 /* data status read on last interrupt */
-  ush intr_ls;	 /* uart line status read on last interrupt*/
-  uch intr_ur;			/* uart receive buffer */
-  uch dsb, cc;	 /* drive status byte and condition (error) code */
-  uch fool;
+  volatile ush intr_ds;		/* data status read on last interrupt */
+  volatile ush intr_ls;		/* uart line status read on last interrupt*/
+  volatile uch ur[UR_SIZE];	/* uart receive buffer fifo */
+  volatile uch ur_w, ur_r;	/* write/read buffer index */
+  volatile uch dsb, cc;	 /* drive status byte and condition (error) code */
   int command;			/* command to be written to the uart */
   int openfiles;
   ush sector[READ_AHEAD*RAW_SECTOR_SIZE/2]; /* buffered cd-sector */
-  int sector_first, sector_last;	/* range of these sector */
-  struct wait_queue * uart;	/* wait for interrupt */
+  int sector_first, sector_last; /* range of these sectors */
+  struct wait_queue * uart;	/* wait queues for interrupt */
   struct wait_queue * data;
   struct timer_list timer;	/* time-out */
   char timed_out;
-  signed char max_sectors;
+  signed char max_sectors;	/* number of sectors that fit in adapter mem */
   char wait_back;		/* we're waiting for a background-read */
   char background;		/* is a read going on in the background? */
   int adapter_first;		/* if so, that's the starting sector */
@@ -243,15 +296,20 @@
 void send_command_polled(int command)
 {
   int loop=POLLOOP;
-  while (!(inw(r_line_status) & ls_transmitter_buffer_empty) && loop>0) 
+  while (!(inw(r_line_status) & ls_transmitter_buffer_empty) && loop>0) {
+    udelay(1000);		/* one millisec delay */
     --loop;
+  }
   outw(command, r_uart_transmit);
 }
 
 uch receive_echo_polled(void)
 {
   int loop=POLLOOP;
-  while (!(inw(r_line_status) & ls_receive_buffer_full) && loop>0) --loop;
+  while (!(inw(r_line_status) & ls_receive_buffer_full) && loop>0) {
+    udelay(1000);
+    --loop;
+  }
   return ((uch) inw(r_uart_receive));
 }
 
@@ -261,6 +319,15 @@
   return receive_echo_polled();
 }
 
+inline void clear_ur(void) {
+  if (cd->ur_r != cd->ur_w) {
+    debug(("Deleting bytes from fifo:"));
+    for(;cd->ur_r != cd->ur_w; cd->ur_r++, cd->ur_r %= UR_SIZE)
+      debug((" 0x%x", cd->ur[cd->ur_r]));
+    debug(("\n"));
+  }
+}
+
 /* The interrupt handler. When the cm260 generates an interrupt, very
    much care has to be taken in reading out the registers in the right
    order; in case of a receive_buffer_full interrupt, first the
@@ -282,18 +349,27 @@
 				       crc_error, sync_error, toc_ready 
 				       interrupts */
   cd->intr_ls = inw(r_line_status); /* resets overrun bit */
+  debug(("Intr, 0x%x 0x%x, %d\n", cd->intr_ds, cd->intr_ls, cd->background));
   if (cd->intr_ls & ls_attention) stats(attention);
   /* receive buffer full? */
   if (cd->intr_ls & ls_receive_buffer_full) {	
-    cd->intr_ur = inb(r_uart_receive); /* get order right! */
+    cd->ur[cd->ur_w] = inb(r_uart_receive); /* get order right! */
     cd->intr_ls = inw(r_line_status); /* resets rbf interrupt */
-    if (!cd->background && cd->uart) wake_up_interruptible(&cd->uart);
+    debug(("receiving #%d: 0x%x\n", cd->ur_w, cd->ur[cd->ur_w]));
+    cd->ur_w++; cd->ur_w %= UR_SIZE;
+    if (cd->ur_w == cd->ur_r) debug(("cd->ur overflow!\n"));
+    if (cd->uart && cd->background < 2) { 
+      del_timer(&cd->timer);
+      wake_up_interruptible(&cd->uart);
+    }
   }
   /* data ready in fifo? */
   else if (cd->intr_ds & ds_data_ready) { 
     if (cd->background) ++cd->adapter_last;
-    if ((cd->wait_back || !cd->background) && cd->data) 
+    if (cd->data && (cd->wait_back || !cd->background)) {
+      del_timer(&cd->timer);
       wake_up_interruptible(&cd->data);
+    }
     stats(data_ready);
   }
   /* ready to issue a write command? */
@@ -340,6 +416,7 @@
 void cm206_timeout(unsigned long who)
 {
   cd->timed_out = 1;
+  debug(("Timing out\n"));
   wake_up_interruptible((struct wait_queue **) who);
 }
 
@@ -347,9 +424,11 @@
    happened */
 int sleep_or_timeout(struct wait_queue ** wait, int timeout)
 {
+  cd->timed_out=0;
   cd->timer.data=(unsigned long) wait;
   cd->timer.expires = jiffies + timeout;
   add_timer(&cd->timer);
+  debug(("going to sleep\n"));
   interruptible_sleep_on(wait);
   del_timer(&cd->timer);
   if (cd->timed_out) {
@@ -359,14 +438,15 @@
   else return 0;
 }
 
-void cm206_delay(int jiffies) 
+void cm206_delay(int nr_jiffies) 
 {
   struct wait_queue * wait = NULL;
-  sleep_or_timeout(&wait, jiffies);
+  sleep_or_timeout(&wait, nr_jiffies);
 }
 
 void send_command(int command)
 {
+  debug(("Sending 0x%x\n", command));
   if (!(inw(r_line_status) & ls_transmitter_buffer_empty)) {
     cd->command = command;
     cli();			/* don't interrupt before sleep */
@@ -378,19 +458,40 @@
       stats(write_timeout);
       outw(command, r_uart_transmit);
     }
+    debug(("Write commmand delayed\n"));
   }
   else outw(command, r_uart_transmit);
 }
 
-uch receive_echo(void)
+uch receive_byte(int timeout)
 {
-  if (!(inw(r_line_status) & ls_receive_buffer_full) &&
-      sleep_or_timeout(&cd->uart, UART_TIMEOUT)) {
+  uch ret;
+  cli();
+  debug(("cli\n"));
+  ret = cd->ur[cd->ur_r];
+  if (cd->ur_r != cd->ur_w) {
+    sti();
+    debug(("returning #%d: 0x%x\n", cd->ur_r, cd->ur[cd->ur_r]));
+    cd->ur_r++; cd->ur_r %= UR_SIZE;
+    return ret;
+  } 
+  else if (sleep_or_timeout(&cd->uart, timeout)) { /* does sti() */
     debug(("Time out on receive-buffer\n"));
-    stats(receive_timeout);
-    return ((uch) inw(r_uart_receive));
+#ifdef STATISTICS
+    if (timeout==UART_TIMEOUT) stats(receive_timeout) /* no `;'! */
+    else stats(dsb_timeout);
+#endif
+    return 0xda;
   }
-  return cd->intr_ur;
+  ret = cd->ur[cd->ur_r];  
+  debug(("slept; returning #%d: 0x%x\n", cd->ur_r, cd->ur[cd->ur_r]));
+  cd->ur_r++; cd->ur_r %= UR_SIZE;
+  return ret;
+}
+
+inline uch receive_echo(void)
+{
+  return receive_byte(UART_TIMEOUT);
 }
 
 inline uch send_receive(int command)
@@ -399,20 +500,15 @@
   return receive_echo();
 }
 
-uch wait_dsb(void)
+inline uch wait_dsb(void)
 {
-  if (!(inw(r_line_status) & ls_receive_buffer_full) &&
-      sleep_or_timeout(&cd->uart, DSB_TIMEOUT)) {
-    debug(("Time out on Drive Status Byte\n"));
-    stats(dsb_timeout);
-    return ((uch) inw(r_uart_receive));
-  }
-  return cd->intr_ur;
+  return receive_byte(DSB_TIMEOUT);
 }
 
 int type_0_command(int command, int expect_dsb)
 {
   int e;
+  clear_ur();
   if (command != (e=send_receive(command))) {
     debug(("command 0x%x echoed as 0x%x\n", command, e));
     stats(echo);
@@ -433,8 +529,8 @@
   return 0;
 }  
 
-/* This function resets the adapter card. We'd better not do this too */
-/* often, because it tends to generate `lost interrupts.' */
+/* This function resets the adapter card. We'd better not do this too
+ * often, because it tends to generate `lost interrupts.' */
 void reset_cm260(void)
 {
   outw(dc_normal | dc_initialize | READ_AHEAD, r_data_control);
@@ -442,7 +538,7 @@
   outw(dc_normal | READ_AHEAD, r_data_control);
 }
 
-/* fsm: frame-sec-min from linear address */
+/* fsm: frame-sec-min from linear address; one of many */
 void fsm(int lba, uch * fsm) 
 {
   fsm[0] = lba % 75;
@@ -466,20 +562,26 @@
   int i, e;
 
   fsm(start, &read_sector[1]);
+  clear_ur();
   for (i=0; i<4; i++) 
     if (read_sector[i] != (e=send_receive(read_sector[i]))) {
       debug(("read_sector: %x echoes %x\n", read_sector[i], e));
       stats(echo);
-      return -1;
+      if (e==0xff) {		/* this seems to happen often */
+	e = receive_echo();
+	debug(("Second try %x\n", e));
+	if (e!=read_sector[i]) return -1;
+      }
     }
   return 0;
 }
 
 int stop_read(void)
 {
+  int e;
   type_0_command(c_stop,0);
-  if(receive_echo() != 0xff) {
-    debug(("c_stop didn't send 0xff\n"));
+  if((e=receive_echo()) != 0xff) {
+    debug(("c_stop didn't send 0xff, but 0x%x\n", e));
     stats(stop_0xff);
     return -1;
   }
@@ -515,8 +617,11 @@
 }
 #endif
 
+
+#define MAX_TRIES 100
 int read_sector(int start)
 {
+  int tries=0;
   if (cd->background) {
     cd->background=0;
     cd->adapter_last = -1;	/* invalidate adapter memory */
@@ -525,12 +630,18 @@
   cd->fifo_overflowed=0;
   reset_cm260();		/* empty fifo etc. */
   if (start_read(start)) return -1;
-  if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) {
-    debug(("Read timed out sector 0x%x\n", start));
-    stats(read_timeout);
-    stop_read();
-    return -3;		
-  }
+  do {
+    if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) {
+      debug(("Read timed out sector 0x%x\n", start));
+      stats(read_timeout);
+      stop_read();
+      return -3;		
+    } 
+    tries++;
+  } while (cd->intr_ds & ds_fifo_empty && tries < MAX_TRIES);
+  if (tries>1) debug(("Took me some tries\n"))
+  else if (tries == MAX_TRIES) 
+    debug(("MAX_TRIES tries for read sector\n"));
   transport_data(r_fifo_output_buffer, cd->sector, 
 		 READ_AHEAD*RAW_SECTOR_SIZE/2);
   if (read_background(start+READ_AHEAD,1)) stats(read_background);
@@ -569,16 +680,22 @@
     cd->background=3;
     break;
   case 3:
-    if (cd->intr_ur != c_stop) {
-      debug(("cm206_bh: c_stop echoed 0x%x\n", cd->intr_ur));
-      stats(echo);
+    if (cd->ur_r != cd->ur_w) {
+      if (cd->ur[cd->ur_r] != c_stop) {
+	debug(("cm206_bh: c_stop echoed 0x%x\n", cd->ur[cd->ur_r]));
+	stats(echo);
+      }
+      cd->ur_r++; cd->ur_r %= UR_SIZE;
     }
     cd->background++;
     break;
   case 4:
-    if (cd->intr_ur != 0xff) {
-      debug(("cm206_bh: c_stop reacted with 0x%x\n", cd->intr_ur));
-      stats(stop_0xff);
+    if (cd->ur_r != cd->ur_w) {
+      if (cd->ur[cd->ur_r] != 0xff) {
+	debug(("cm206_bh: c_stop reacted with 0x%x\n", cd->ur[cd->ur_r]));
+	stats(stop_0xff);
+      }
+      cd->ur_r++; cd->ur_r %= UR_SIZE;
     }
     cd->background=0;
   }
@@ -699,6 +816,7 @@
     }
     error=0;
     for (i=0; i<CURRENT->nr_sectors; i++) {
+      int e1, e2;
       cd_sec_no = (CURRENT->sector+i)/BLOCKS_ISO; /* 4 times 512 bytes */
       quarter = (CURRENT->sector+i) % BLOCKS_ISO; 
       dest = CURRENT->buffer + i*LINUX_BLOCK_SIZE;
@@ -708,12 +826,14 @@
 	  + (cd_sec_no-cd->sector_first)*RAW_SECTOR_SIZE;
  	memcpy(dest, source, LINUX_BLOCK_SIZE); 
       }
-      else if (!try_adapter(cd_sec_no) || !read_sector(cd_sec_no)) {
+      else if (!(e1=try_adapter(cd_sec_no)) || 
+	       !(e2=read_sector(cd_sec_no))) {
 	source =  ((uch *) cd->sector)+16+quarter*LINUX_BLOCK_SIZE;
 	memcpy(dest, source, LINUX_BLOCK_SIZE); 
       }
       else {
 	error=1;
+	debug(("cm206_request: %d %d\n", e1, e2));
       }
     }
     end_request(!error);
@@ -758,21 +878,22 @@
 
 /* This function does a binary search for track start. It records all
  * tracks seen in the process. Input $track$ must be between 1 and
- * #-of-tracks+1 */
+ * #-of-tracks+1.  Note that the start of the disc must be in toc[1].fsm. 
+ */
 int get_toc_lba(uch track)
 {
-  int max=74*60*75-150, min=0;
+  int max=74*60*75-150, min=fsm2lba(cd->toc[1].fsm);
   int i, lba, l, old_lba=0;
   uch * q = cd->q;
   uch ct;			/* current track */
   int binary=0;
-  const skip = 3*60*75;
+  const skip = 3*60*75;		/* 3 minutes */
 
   for (i=track; i>0; i--) if (cd->toc[i].track) {
     min = fsm2lba(cd->toc[i].fsm);
     break;
   }
-  lba = min + skip;		/* 3 minutes */
+  lba = min + skip;
   do {
     seek(lba); 
     type_1_command(c_read_current_q, 10, q);
@@ -811,11 +932,11 @@
 int read_toc_header(struct cdrom_tochdr * hp)
 {
   if (!FIRST_TRACK) get_disc_status();
-  if (hp && DISC_STATUS & cds_all_audio) { /* all audio */
+  if (hp) { 
     int i;
     hp->cdth_trk0 = FIRST_TRACK;
-    hp->cdth_trk1 = LAST_TRACK;
-    cd->toc[1].track=1;		/* fill in first track position */
+    hp->cdth_trk1 = LAST_TRACK; 
+				/* fill in first track position */
     for (i=0; i<3; i++) cd->toc[1].fsm[i] = cd->disc_status[3+i];
     update_toc_entry(LAST_TRACK+1);		/* find most entries */
     return 0;
@@ -988,7 +1109,7 @@
   else return -EIO;
 }
 
-/* The new Uniform cdrom support. Routines should be concise, most of
+/* The new generic cdrom support. Routines should be concise, most of
    the logic should be in cdrom.c */
 
 /* returns number of times device is in use */
@@ -1019,6 +1140,24 @@
   return CDS_DISC_OK;
 }
  
+/* gives current state of disc in drive */
+int cm206_disc_status(struct cdrom_device_info * cdi)
+{
+  uch xa;
+  get_drive_status();
+  if ((cd->dsb & dsb_not_useful) | !(cd->dsb & dsb_disc_present))
+    return CDS_NO_DISC;
+  get_disc_status();
+  if (DISC_STATUS & cds_all_audio) return CDS_AUDIO;
+  xa = DISC_STATUS >> 4;
+  switch (xa) {
+  case 0: return CDS_DATA_1;	/* can we detect CDS_DATA_2? */
+  case 1: return CDS_XA_2_1;	/* untested */
+  case 2: return CDS_XA_2_2;
+  }
+  return 0;
+}
+
 /* locks or unlocks door lock==1: lock; return 0 upon success */
 int cm206_lock_door(struct cdrom_device_info * cdi, int lock)
 {
@@ -1029,7 +1168,7 @@
 }
   
 /* Although a session start should be in LBA format, we return it in 
-   MSF format because it is slightly easier, and the new Uniform ioctl
+   MSF format because it is slightly easier, and the new generic ioctl
    will take care of the necessary conversion. */
 int cm206_get_last_session(struct cdrom_device_info * cdi, 
 			   struct cdrom_multisession * mssp) 
@@ -1114,7 +1253,9 @@
   cm206_audio_ioctl,		/* audio ioctl */
   cm206_ioctl,			/* device-specific ioctl */
   CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_MULTI_SESSION |
-    CDC_MEDIA_CHANGED | CDC_MCN | CDC_PLAY_AUDIO, /* capability */
+    CDC_MEDIA_CHANGED | CDC_MCN | CDC_PLAY_AUDIO | CDC_SELECT_SPEED |
+    CDC_IOCTLS | CDC_DRIVE_STATUS, 
+    /* capability */
   1,				/* number of minor devices */
 };
 
@@ -1206,7 +1347,7 @@
   uch e=0;
   long int size=sizeof(struct cm206_struct);
 
-  printk(KERN_INFO VERSION);
+  printk(KERN_INFO "cm206 cdrom driver " REVISION);
   cm206_base = probe_base_port(auto_probe ? 0 : cm206_base);
   if (!cm206_base) {
     printk(" can't find adapter!\n");
@@ -1233,14 +1374,14 @@
   /* Now, the problem here is that reset_cm260 can generate an
      interrupt. It seems that this can cause a kernel oops some time
      later. So we wait a while and `service' this interrupt. */
-  udelay(10);
+  udelay(1000);
   outw(dc_normal | READ_AHEAD, r_data_control);
   sti();
   printk(" using IRQ %d\n", cm206_irq);
 #endif
   if (send_receive_polled(c_drive_configuration) != c_drive_configuration) 
     {
-      printk(" drive not there\n");
+      printk(KERN_INFO " drive not there\n");
       cleanup(1);
       return -EIO;
     }
@@ -1257,16 +1398,17 @@
   }
   printk(".\n");
   if (register_blkdev(MAJOR_NR, "cm206", &cdrom_fops) != 0) {
-    printk("Cannot register for major %d!\n", MAJOR_NR);
+    printk(KERN_INFO "Cannot register for major %d!\n", MAJOR_NR);
     cleanup(3);
     return -EIO;
   }
   if (register_cdrom(&cm206_info) != 0) {
-    printk("Cannot register for cdrom %d!\n", MAJOR_NR);
+    printk(KERN_INFO "Cannot register for cdrom %d!\n", MAJOR_NR);
     cleanup(3);
     return -EIO;
   }    
   blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+  blksize_size[MAJOR_NR] = cm206_blocksizes;
   read_ahead[MAJOR_NR] = 16;	/* reads ahead what? */
   init_bh(CM206_BH, cm206_bh);
 
@@ -1336,6 +1478,6 @@
 #endif /* MODULE */
 /*
  * Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -I. -I/usr/src/linux/include/linux -Wall -Wstrict-prototypes -O2 -m486 -c cm206.c -o cm206.o"
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -D__SMP__ -pipe -fno-strength-reduce -m486 -DCPU=486 -D__SMP__ -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h  -c -o cm206.o cm206.c"
  * End:
  */
diff -u --recursive --new-file v2.1.76/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.76/linux/drivers/char/Config.in	Thu Dec  4 14:53:55 1997
+++ linux/drivers/char/Config.in	Mon Dec 29 14:05:13 1997
@@ -110,7 +110,7 @@
 fi
 tristate 'Video For Linux' CONFIG_VIDEO_DEV
 dep_tristate 'BT848 Video For Linux' CONFIG_VIDEO_BT848 $CONFIG_VIDEO_DEV
-#dep_tristate 'Quickcam BW Video For Linux' CONFIG_VIDEO_BWQCAM $CONFIG_VIDEO_DEV
+dep_tristate 'Quickcam BW Video For Linux' CONFIG_VIDEO_BWQCAM $CONFIG_VIDEO_DEV
 dep_tristate 'Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV
 tristate '/dev/nvram support' CONFIG_NVRAM
 tristate 'PC joystick support' CONFIG_JOYSTICK
diff -u --recursive --new-file v2.1.76/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.1.76/linux/drivers/char/bttv.c	Tue Dec 23 16:30:59 1997
+++ linux/drivers/char/bttv.c	Mon Dec 29 10:24:21 1997
@@ -1270,7 +1270,7 @@
 			v.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE);
 			v.flags|=VIDEO_AUDIO_MUTABLE;
 			strcpy(v.name,"TV");
-			if(copy_to_user(&v,arg,sizeof(v)))
+			if(copy_to_user(arg,&v,sizeof(v)))
 				return -EFAULT;
 			return 0;
 		}
diff -u --recursive --new-file v2.1.76/linux/drivers/char/bw-qcam.c linux/drivers/char/bw-qcam.c
--- v2.1.76/linux/drivers/char/bw-qcam.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/bw-qcam.c	Mon Dec 29 14:05:13 1997
@@ -0,0 +1,877 @@
+/*
+ *    QuickCam Driver For Video4Linux.
+ *
+ *	This version only works as a module.
+ *
+ *	Video4Linux conversion work by Alan Cox.
+ */
+
+/* qcam-lib.c -- Library for programming with the Connectix QuickCam.
+ * See the included documentation for usage instructions and details
+ * of the protocol involved. */
+
+
+/* Version 0.5, August 4, 1996 */
+/* Version 0.7, August 27, 1996 */
+/* Version 0.9, November 17, 1996 */
+
+
+/******************************************************************
+
+Copyright (C) 1996 by Scott Laird
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <linux/sched.h>
+#include <linux/videodev.h>
+#include <linux/version.h>
+#include <asm/uaccess.h>
+
+#include "bw-qcam.h"
+
+extern __inline__ int read_lpstatus(struct qcam_device *q)
+{
+	return inb_p(q->port+1);
+}
+
+extern __inline__ int read_lpcontrol(struct qcam_device *q)
+{
+	return inb_p(q->port+2);
+}
+
+extern __inline__ int read_lpdata(struct qcam_device *q)
+{
+	return inb_p(q->port);
+}
+
+extern __inline__ void write_lpdata(struct qcam_device *q, int d)
+{
+	outb_p(d, q->port);
+}
+
+extern __inline__ void write_lpcontrol(struct qcam_device *q,int d)
+{
+	outb(d, q->port+2);
+}
+
+static int qc_waithand(struct qcam_device *q, int val);
+static int qc_command(struct qcam_device *q, int command);
+static int qc_readparam(struct qcam_device *q);
+static int qc_setscanmode(struct qcam_device *q);
+static int qc_readbytes(struct qcam_device *q, char buffer[]);
+
+static struct video_device qcam_template;
+
+static int qc_calibrate(struct qcam_device *q)
+{
+	/*
+	 *	Bugfix by Hanno Mueller hmueller@kabel.de, Mai 21 96
+	 *	The white balance is an individiual value for each
+	 *	quickcam.
+	 */
+
+	int value;
+	int count = 0;
+
+	qc_command(q, 27);	/* AutoAdjustOffset */
+	qc_command(q, 0);	/* Dummy Parameter, ignored by the camera */
+
+	/* GetOffset (33) will read 255 until autocalibration */
+	/* is finished. After that, a value of 1-254 will be */
+	/* returned. */
+
+	do {
+		qc_command(q, 33);
+		value = qc_readparam(q);
+		udelay(1000);
+		schedule();
+		count++;
+	} while (value == 0xff && count<2048);
+
+	q->whitebal = value;
+	return value;
+}
+
+/* Initialize the QuickCam driver control structure.  This is where
+ * defaults are set for people who don't have a config file.*/
+
+static struct qcam_device *qcam_init(int port)
+{
+	struct qcam_device *q;
+	
+	if(check_region(port,3))
+	{
+		printk(KERN_ERR "qcam: I/O port 0x%03X in use.\n", port);
+		return NULL;
+	}
+	
+	q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL);
+	
+	memcpy(&q->vdev, &qcam_template, sizeof(qcam_template));
+
+	q->port = port;		/* Port 0 == Autoprobe */
+	q->port_mode = (QC_ANY | QC_NOTSET);
+	q->width = 320;
+	q->height = 240;
+	q->bpp = 4;
+	q->transfer_scale = 2;
+	q->contrast = 192;
+	q->brightness = 180;
+	q->whitebal = 105;
+	q->top = 1;
+	q->left = 14;
+	q->mode = -1;
+	return q;
+}
+
+
+/* qc_command is probably a bit of a misnomer -- it's used to send
+ * bytes *to* the camera.  Generally, these bytes are either commands
+ * or arguments to commands, so the name fits, but it still bugs me a
+ * bit.  See the documentation for a list of commands. */
+
+static int qc_command(struct qcam_device *q, int command)
+{
+	int n1, n2;
+	int cmd;
+
+	write_lpdata(q, command);
+	write_lpcontrol(q, 6);
+
+	n1 = qc_waithand(q, 1);
+
+	write_lpcontrol(q, 0xe);
+	n2 = qc_waithand(q, 0);
+
+	cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+	return cmd;
+}
+
+static int qc_readparam(struct qcam_device *q)
+{
+	int n1, n2;
+	int cmd;
+
+	write_lpcontrol(q, 6);
+	n1 = qc_waithand(q, 1);
+
+	write_lpcontrol(q, 0xe);
+	n2 = qc_waithand(q, 0);
+
+	cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+	return cmd;
+}
+
+/* qc_waithand busy-waits for a handshake signal from the QuickCam.
+ * Almost all communication with the camera requires handshaking. */
+
+static int qc_waithand(struct qcam_device *q, int val)
+{
+	int status;
+	int runs=0;
+
+	if (val)
+	{
+		while (!((status = read_lpstatus(q)) & 8))
+		{
+			/* 1000 is enough spins on the I/O for all normal
+			   cases, at that point we start to poll slowly 
+			   until the camera wakes up */
+			   
+			if(runs++>1000)
+			{
+				current->state=TASK_INTERRUPTIBLE;
+				current->timeout = jiffies+HZ/10;
+				schedule();
+			}
+			if(runs>1050)
+				return -1;
+		}
+	}
+	else
+	{
+		while (((status = read_lpstatus(q)) & 8))
+		{
+			/* 1000 is enough spins on the I/O for all normal
+			   cases, at that point we start to poll slowly 
+			   until the camera wakes up */
+			   
+			if(runs++>1000)
+			{
+				current->state=TASK_INTERRUPTIBLE;
+				current->timeout = jiffies+HZ/10;
+				schedule();
+			}
+			if(runs++>1050)	/* 5 seconds */
+				return -1;
+		}
+	}
+
+	return status;
+}
+
+/* Waithand2 is used when the qcam is in bidirectional mode, and the
+ * handshaking signal is CamRdy2 (bit 0 of data reg) instead of CamRdy1
+ * (bit 3 of status register).  It also returns the last value read,
+ * since this data is useful. */
+
+static unsigned int qc_waithand2(struct qcam_device *q, int val)
+{
+	unsigned int status;
+	int runs=0;
+	
+	do 
+	{
+		status = read_lpdata(q);
+		/* 1000 is enough spins on the I/O for all normal
+		   cases, at that point we start to poll slowly 
+		   until the camera wakes up */
+		   
+		if(runs++>1000)
+		{
+			current->state=TASK_INTERRUPTIBLE;
+			current->timeout = jiffies+HZ/10;
+			schedule();
+		}
+		if(runs++>1050)	/* 5 seconds */
+			return 0;
+	}
+	while ((status & 1) != val);
+
+	return status;
+}
+
+
+/* Try to detect a QuickCam.  It appears to flash the upper 4 bits of
+   the status register at 5-10 Hz.  This is only used in the autoprobe
+   code.  Be aware that this isn't the way Connectix detects the
+   camera (they send a reset and try to handshake), but this should be
+   almost completely safe, while their method screws up my printer if
+   I plug it in before the camera. */
+
+static int qc_detect(struct qcam_device *q)
+{
+	int reg, lastreg;
+	int count = 0;
+	int i;
+
+	lastreg = reg = read_lpstatus(q) & 0xf0;
+
+	for (i = 0; i < 300; i++) 
+	{
+		reg = read_lpstatus(q) & 0xf0;
+		if (reg != lastreg)
+			count++;
+		lastreg = reg;
+		udelay(1000);
+	}
+
+	/* Be liberal in what you accept...  */
+
+	if (count > 30 && count < 200)
+		return 1;	/* found */
+	else
+		return 0;	/* not found */
+}
+
+
+/* Reset the QuickCam.  This uses the same sequence the Windows
+ * QuickPic program uses.  Someone with a bi-directional port should
+ * check that bi-directional mode is detected right, and then
+ * implement bi-directional mode in qc_readbyte(). */
+
+static void qc_reset(struct qcam_device *q)
+{
+	switch (q->port_mode & QC_FORCE_MASK) 
+	{
+		case QC_FORCE_UNIDIR:
+			q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR;
+			break;
+
+		case QC_FORCE_BIDIR:
+			q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR;
+			break;
+
+		case QC_ANY:
+			write_lpcontrol(q, 0x20);
+			write_lpdata(q, 0x75);
+	
+			if (read_lpdata(q) != 0x75) {
+				q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR;
+			} else {
+				q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR;
+			}
+			break;
+	}
+
+	write_lpcontrol(q, 0xb);
+	udelay(250);
+	write_lpcontrol(q, 0xe);
+	qc_setscanmode(q);		/* in case port_mode changed */
+}
+
+
+/* Decide which scan mode to use.  There's no real requirement that
+ * the scanmode match the resolution in q->height and q-> width -- the
+ * camera takes the picture at the resolution specified in the
+ * "scanmode" and then returns the image at the resolution specified
+ * with the resolution commands.  If the scan is bigger than the
+ * requested resolution, the upper-left hand corner of the scan is
+ * returned.  If the scan is smaller, then the rest of the image
+ * returned contains garbage. */
+
+static int qc_setscanmode(struct qcam_device *q)
+{
+	switch (q->transfer_scale) 
+	{
+		case 1:
+			q->mode = 0;
+			break;
+		case 2:
+			q->mode = 4;
+			break;
+		case 4:
+			q->mode = 8;
+			break;
+	}
+
+	switch (q->bpp) 
+	{
+		case 4:
+			break;
+		case 6:
+			q->mode += 2;
+			break;
+	}
+
+	switch (q->port_mode & QC_MODE_MASK) 
+	{
+		case QC_BIDIR:
+			q->mode += 1;
+			break;
+		case QC_NOTSET:
+		case QC_UNIDIR:
+			break;
+	}
+	return 0;
+}
+
+
+/* Reset the QuickCam and program for brightness, contrast,
+ * white-balance, and resolution. */
+
+void qc_set(struct qcam_device *q)
+{
+	int val;
+	int val2;
+
+	qc_reset(q);
+
+	/* Set the brightness.  Yes, this is repetitive, but it works.
+	 * Shorter versions seem to fail subtly.  Feel free to try :-). */
+	/* I think the problem was in qc_command, not here -- bls */
+	
+	qc_command(q, 0xb);
+	qc_command(q, q->brightness);
+
+	val = q->height / q->transfer_scale;
+	qc_command(q, 0x11);
+	qc_command(q, val);
+	if ((q->port_mode & QC_MODE_MASK) == QC_UNIDIR && q->bpp == 6) {
+		/* The normal "transfers per line" calculation doesn't seem to work
+		   as expected here (and yet it works fine in qc_scan).  No idea
+		   why this case is the odd man out.  Fortunately, Laird's original
+		   working version gives me a good way to guess at working values.
+		   -- bls */
+		val = q->width;
+		val2 = q->transfer_scale * 4;
+	} else {
+		val = q->width * q->bpp;
+		val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) *
+		    q->transfer_scale;
+	}
+	val = (val + val2 - 1) / val2;
+	qc_command(q, 0x13);
+	qc_command(q, val);
+
+	/* Setting top and left -- bls */
+	qc_command(q, 0xd);
+	qc_command(q, q->top);
+	qc_command(q, 0xf);
+	qc_command(q, q->left / 2);
+
+	qc_command(q, 0x19);
+	qc_command(q, q->contrast);
+	qc_command(q, 0x1f);
+	qc_command(q, q->whitebal);
+}
+
+
+/* Qc_readbytes reads some bytes from the QC and puts them in
+   the supplied buffer.  It returns the number of bytes read,
+   or -1 on error. */
+
+extern __inline__ int qc_readbytes(struct qcam_device *q, char buffer[])
+{
+	int ret=1;
+	unsigned int hi, lo;
+	unsigned int hi2, lo2;
+	static int state = 0;
+
+	if (buffer == NULL) 
+	{
+		state = 0;
+		return 0;
+	}
+	
+	switch (q->port_mode & QC_MODE_MASK) 
+	{
+		case QC_BIDIR:		/* Bi-directional Port */
+			write_lpcontrol(q, 0x26);
+			lo = (qc_waithand2(q, 1) >> 1);
+			hi = (read_lpstatus(q) >> 3) & 0x1f;
+			write_lpcontrol(q, 0x2e);
+			lo2 = (qc_waithand2(q, 0) >> 1);
+			hi2 = (read_lpstatus(q) >> 3) & 0x1f;
+			switch (q->bpp) 
+			{
+				case 4:
+					buffer[0] = lo & 0xf;
+					buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3);
+					buffer[2] = (hi & 0x1e) >> 1;
+					buffer[3] = lo2 & 0xf;
+					buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3);
+					buffer[5] = (hi2 & 0x1e) >> 1;
+					ret = 6;
+					break;
+				case 6:
+					buffer[0] = lo & 0x3f;
+					buffer[1] = ((lo & 0x40) >> 6) | (hi << 1);
+					buffer[2] = lo2 & 0x3f;
+					buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1);
+					ret = 4;
+					break;
+			}
+			break;
+
+		case QC_UNIDIR:	/* Unidirectional Port */
+			write_lpcontrol(q, 6);
+			lo = (qc_waithand(q, 1) & 0xf0) >> 4;
+			write_lpcontrol(q, 0xe);
+			hi = (qc_waithand(q, 0) & 0xf0) >> 4;
+
+			switch (q->bpp) 
+			{
+				case 4:
+					buffer[0] = lo;
+					buffer[1] = hi;
+					ret = 2;
+					break;
+				case 6:
+					switch (state) 
+					{
+						case 0:
+							buffer[0] = (lo << 2) | ((hi & 0xc) >> 2);
+							q->saved_bits = (hi & 3) << 4;
+							state = 1;
+							ret = 1;
+							break;
+						case 1:
+							buffer[0] = lo | q->saved_bits;
+							q->saved_bits = hi << 2;
+							state = 2;
+							ret = 1;
+							break;
+						case 2:
+							buffer[0] = ((lo & 0xc) >> 2) | q->saved_bits;
+							buffer[1] = ((lo & 3) << 4) | hi;
+							state = 0;
+							ret = 2;
+							break;
+					}
+					break;
+			}
+			break;
+	}
+	return ret;
+}
+
+/* requests a scan from the camera.  It sends the correct instructions
+ * to the camera and then reads back the correct number of bytes.  In
+ * previous versions of this routine the return structure contained
+ * the raw output from the camera, and there was a 'qc_convertscan'
+ * function that converted that to a useful format.  In version 0.3 I
+ * rolled qc_convertscan into qc_scan and now I only return the
+ * converted scan.  The format is just an one-dimensional array of
+ * characters, one for each pixel, with 0=black up to n=white, where
+ * n=2^(bit depth)-1.  Ask me for more details if you don't understand
+ * this. */
+
+long qc_capture(struct qcam_device * q, char *buf, unsigned long len)
+{
+	int i, j, k;
+	int bytes;
+	int linestotrans, transperline;
+	int divisor;
+	int pixels_per_line;
+	int pixels_read = 0;
+	int got=0;
+	char buffer[6];
+	int  shift=8-q->bpp;
+	char invert;
+
+	if (q->mode == -1) 
+		return -ENXIO;
+
+	qc_command(q, 0x7);
+	qc_command(q, q->mode);
+
+	if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) 
+	{
+		write_lpcontrol(q, 0x2e);	/* turn port around */
+		write_lpcontrol(q, 0x26);
+		(void) qc_waithand(q, 1);
+		write_lpcontrol(q, 0x2e);
+		(void) qc_waithand(q, 0);
+	}
+	
+	/* strange -- should be 15:63 below, but 4bpp is odd */
+	invert = (q->bpp == 4) ? 16 : 63;
+
+	linestotrans = q->height / q->transfer_scale;
+	pixels_per_line = q->width / q->transfer_scale;
+	transperline = q->width * q->bpp;
+	divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) *
+	    q->transfer_scale;
+	transperline = (transperline + divisor - 1) / divisor;
+
+	for (i = 0; i < linestotrans; i++) 
+	{
+		for (pixels_read = j = 0; j < transperline; j++) 
+		{
+			bytes = qc_readbytes(q, buffer);
+			for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++) 
+			{
+				int o;
+				if (buffer[k] == 0 && invert == 16) 
+				{
+					/* 4bpp is odd (again) -- inverter is 16, not 15, but output
+					   must be 0-15 -- bls */
+					buffer[k] = 16;
+				}
+				o=i*pixels_per_line + pixels_read + k;
+				if(o<len)
+				{
+					got++;
+					put_user((invert - buffer[k])<<shift, buf+o);
+				}
+			}
+			pixels_read += bytes;
+		}
+		(void) qc_readbytes(q, 0);	/* reset state machine */
+	}
+
+	if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) 
+	{
+		write_lpcontrol(q, 2);
+		write_lpcontrol(q, 6);
+		udelay(3);
+		write_lpcontrol(q, 0xe);
+	}
+	if(got<len)
+		return got;
+	return len;
+}
+
+/*
+ *	Video4linux interfacing
+ */
+
+static int qcam_open(struct video_device *dev, int flags)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void qcam_close(struct video_device *dev)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static int qcam_init_done(struct video_device *dev)
+{
+	return 0;
+}
+
+static long qcam_write(struct video_device *v, const char *buf, unsigned long count, int noblock)
+{
+	return -EINVAL;
+}
+
+static int qcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+	struct qcam_device *qcam=(struct qcam_device *)dev;
+	
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability b;
+			strcpy(b.name, "Quickcam");
+			b.type = VID_TYPE_CAPTURE|VID_TYPE_SCALES|VID_TYPE_MONOCHROME;
+			b.channels = 1;
+			b.audios = 0;
+			b.maxwidth = 320;
+			b.maxheight = 240;
+			b.minwidth = 80;
+			b.minheight = 60;
+			if(copy_to_user(arg, &b,sizeof(b)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCGCHAN:
+		{
+			struct video_channel v;
+			if(copy_from_user(&v, arg, sizeof(v)))
+				return -EFAULT;
+			if(v.channel!=0)
+				return -EINVAL;
+			v.flags=0;
+			v.tuners=0;
+			/* Good question.. its composite or SVHS so.. */
+			v.type = VIDEO_TYPE_CAMERA;
+			strcpy(v.name, "Camera");
+			if(copy_to_user(arg, &v, sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSCHAN:
+		{
+			int v;
+			if(copy_from_user(&v, arg,sizeof(v)))
+				return -EFAULT;
+			if(v!=0)
+				return -EINVAL;
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner v;
+			if(copy_from_user(&v, arg, sizeof(v))!=0)
+				return -EFAULT;
+			if(v.tuner)
+				return -EINVAL;
+			strcpy(v.name, "Format");
+			v.rangelow=0;
+			v.rangehigh=0;
+			v.flags= 0;
+			v.mode = VIDEO_MODE_AUTO;
+			if(copy_to_user(arg,&v,sizeof(v))!=0)
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner v;
+			if(copy_from_user(&v, arg, sizeof(v))!=0)
+				return -EFAULT;
+			if(v.tuner)
+				return -EINVAL;
+			if(v.mode!=VIDEO_MODE_AUTO)
+				return -EINVAL;
+			return 0;
+		}
+		case VIDIOCGPICT:
+		{
+			struct video_picture p;
+			p.colour=0x8000;
+			p.hue=0x8000;
+			p.brightness=qcam->brightness<<8;
+			p.contrast=qcam->contrast<<8;
+			p.whiteness=qcam->whitebal<<8;
+			p.depth=qcam->bpp;
+			p.palette=VIDEO_PALETTE_GREY;
+			if(copy_to_user(arg, &p, sizeof(p)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSPICT:
+		{
+			struct video_picture p;
+			if(copy_from_user(&p, arg, sizeof(p)))
+				return -EFAULT;
+			if(p.palette!=VIDEO_PALETTE_GREY)
+			    	return -EINVAL;
+			if(p.depth!=4 && p.depth!=6)
+				return -EINVAL;
+			
+			/*
+			 *	Now load the camera.
+			 */
+
+			qcam->brightness = p.brightness>>8;
+			qcam->contrast = p.contrast>>8;
+			qcam->whitebal = p.whiteness>>8;
+			qcam->bpp = p.depth;
+			
+			qc_setscanmode(qcam);
+			return 0;
+		}
+		case VIDIOCSWIN:
+		{
+			struct video_window vw;
+			if(copy_from_user(&vw, arg,sizeof(vw)))
+				return -EFAULT;
+			if(vw.flags)
+				return -EINVAL;
+			if(vw.clipcount)
+				return -EINVAL;
+			if(vw.height<60||vw.height>240)
+				return -EINVAL;
+			if(vw.width<80||vw.width>320)
+				return -EINVAL;
+				
+			qcam->width = 320;
+			qcam->height = 240;
+			qcam->transfer_scale = 4;
+			
+			if(vw.width>=160 && vw.height>=120)
+			{
+				qcam->transfer_scale = 2;
+			}
+			if(vw.width>=320 && vw.height>=240)
+			{
+				qcam->width = 320;
+				qcam->height = 240;
+				qcam->transfer_scale = 1;
+			}
+			qc_setscanmode(qcam);
+			/* Ok we figured out what to use from our wide choice */
+			return 0;
+		}
+		case VIDIOCGWIN:
+		{
+			struct video_window vw;
+			vw.x=0;
+			vw.y=0;
+			vw.width=qcam->width/qcam->transfer_scale;
+			vw.height=qcam->height/qcam->transfer_scale;
+			vw.chromakey=0;
+			vw.flags=0;
+			if(copy_to_user(arg, &vw, sizeof(vw)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCCAPTURE:
+			return -EINVAL;
+		case VIDIOCGFBUF:
+			return -EINVAL;
+		case VIDIOCSFBUF:
+			return -EINVAL;
+		case VIDIOCKEY:
+			return 0;
+		case VIDIOCGFREQ:
+			return -EINVAL;
+		case VIDIOCSFREQ:
+			return -EINVAL;
+		case VIDIOCGAUDIO:
+			return -EINVAL;
+		case VIDIOCSAUDIO:
+			return -EINVAL;
+		default:
+			return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static long qcam_read(struct video_device *v, char *buf, unsigned long count,  int noblock)
+{
+	struct qcam_device *qcam=(struct qcam_device *)v;
+	int len;
+	/* Probably should have a semaphore against multiple users */
+	qc_reset(qcam);
+	len=qc_capture(qcam, buf,count);
+	return len;
+}
+
+ 
+static struct video_device qcam_template=
+{
+	"Connectix Quickcam",
+	VID_TYPE_CAPTURE,
+	VID_HARDWARE_QCAM_BW,
+	qcam_open,
+	qcam_close,
+	qcam_read,
+	qcam_write,
+	qcam_ioctl,
+	NULL,
+	qcam_init_done,
+	NULL,
+	0,
+	0
+};
+
+
+int io=0x378;
+
+MODULE_PARM(io,"i");
+
+static struct qcam_device *qcam;
+
+int init_module(void)
+{
+	qcam=qcam_init(io);
+	if(qcam==NULL)
+		return -ENODEV;
+		
+	qc_reset(qcam);
+	
+	if(qc_detect(qcam)==0)
+	{
+		kfree(qcam);
+		return -ENODEV;
+	}
+	qc_calibrate(qcam);
+	
+	printk(KERN_INFO "Connectix Quickcam at 0x%03X\n", qcam->port);
+	
+	if(video_register_device(&qcam->vdev)==-1)
+		return -ENODEV;
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	video_unregister_device(&qcam->vdev);
+	kfree(qcam);
+}
diff -u --recursive --new-file v2.1.76/linux/drivers/char/bw-qcam.h linux/drivers/char/bw-qcam.h
--- v2.1.76/linux/drivers/char/bw-qcam.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/bw-qcam.h	Mon Dec 29 14:05:13 1997
@@ -0,0 +1,63 @@
+/*
+ *	Video4Linux bw-qcam driver
+ *
+ *	Derived from code..
+ */
+
+/******************************************************************
+
+Copyright (C) 1996 by Scott Laird
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+/* One from column A... */
+#define QC_NOTSET 0
+#define QC_UNIDIR 1
+#define QC_BIDIR  2
+#define QC_SERIAL 3
+
+/* ... and one from column B */
+#define QC_ANY          0x00
+#define QC_FORCE_UNIDIR 0x10
+#define QC_FORCE_BIDIR  0x20
+#define QC_FORCE_SERIAL 0x30
+/* in the port_mode member */
+
+#define QC_MODE_MASK    0x07
+#define QC_FORCE_MASK   0x70
+
+#define MAX_HEIGHT 243
+#define MAX_WIDTH 336
+
+struct qcam_device {
+	struct video_device vdev;
+	int width, height;
+	int bpp;
+	int mode;
+	int contrast, brightness, whitebal;
+	int port;
+	int port_mode;
+	int transfer_scale;
+	int top, left;
+	unsigned int saved_bits;
+};
+
diff -u --recursive --new-file v2.1.76/linux/drivers/char/pms.c linux/drivers/char/pms.c
--- v2.1.76/linux/drivers/char/pms.c	Sun Dec 21 22:36:13 1997
+++ linux/drivers/char/pms.c	Mon Dec 29 10:26:55 1997
@@ -419,8 +419,8 @@
 	}
 	else if(decoder==PHILIPS1)
 	{
-		i2c_write(0x42, 0x08, sense);
-		i2c_write(0x42, 0x09, sense);
+		i2c_write(0x42, 0x0A, sense);
+		i2c_write(0x42, 0x0B, sense);
 	}
 }
 
@@ -428,13 +428,11 @@
 {
 	if(decoder==PHILIPS2)
 	{
-		i2c_write(0x8A, 0x0A, chroma);
-		i2c_write(0x8A, 0x0B, chroma);
+		i2c_write(0x8A, 0x11, chroma);
 	}
 	else if(decoder==PHILIPS1)
 	{
-		i2c_write(0x42, 0x08, chroma);
-		i2c_write(0x42, 0x09, chroma);
+		i2c_write(0x42, 0x11, chroma);
 	}
 }
 
@@ -522,21 +520,21 @@
 		deciden=640;	/* 768 would be ideal */
 	}
 	
-	while(decinum%2==0 && deciden%2==0)
+	while(((decinum|deciden)&1)==0)
 	{
-		decinum/=2;
-		deciden/=2;
+		decinum>>=1;
+		deciden>>=1;
 	}
 	while(deciden>32)
 	{
-		deciden/=2;
-		decinum=(decinum+1)/2;
+		deciden>>=1;
+		decinum=(decinum+1)>>1;
 	}
 	if(deciden==32)
 		deciden--;
 		
 	mvv_write(0x24, 0x80|deciden);
-	mvv_write(0x25, deciden);
+	mvv_write(0x25, decinum);
 }
 
 static void pms_resolution(short width, short height)
@@ -554,7 +552,7 @@
 	{
 		mvv_write(0x1A, 0xFC);
 		mvv_write(0x1B, 0x00);
-		if(height>width)
+		if(height>fg_height)
 			pms_vertdeci(240,240);
 		else
 			pms_vertdeci(fg_height,240);
@@ -576,12 +574,12 @@
 	
 	mvv_write(0x22, width+8);
 	mvv_write(0x23, (width+8)>> 8);
-	
+
 	if(standard==1)
 		pms_horzdeci(width,640);
 	else
 		pms_horzdeci(width+8, 768);
-	
+
 	mvv_write(0x30, mvv_read(0x30)&0xFE);
 	mvv_write(0x08, mvv_read(0x08)|0x01);
 	mvv_write(0x01, mvv_read(0x01)&0xFD);
@@ -610,73 +608,40 @@
 
 static int pms_capture(struct pms_device *dev, char *buf, int rgb555, int count)
 {
-	char dump[16];
 	int y;
-	int ww= dev->width, wh= dev->height;
-	int dinc, zz;
-	int dw=2*ww, loops;
-	unsigned char r8;
+	int dw = 2*dev->width;
+	char *src = (char *)bus_to_virt((void *)mem_base);
+
+	char tmp[dw+16]; /* using a temp buffer is faster than direct  */
+	int cnt = 0;
 	int len=0;
-	
-	short *dst=(short *)buf;
-	short *src=(short *)bus_to_virt((void *)mem_base);
-	
-	if(wh>256)
-	{
-		dinc=2*ww; 
-		zz=wh/2; 
-		loops=2;
-	}
-	else
-	{
-		dinc=ww; 
-		zz=wh; 
-		loops=1;
-	}
-	
-	
-	r8=0x5;
-	if(rgb555)
-		r8|=0x20;
-		
-field_cap:
-	
-	mvv_write(0x08, r8);	/* Capture mode, Enable DRAM, PC enable */
-	
-	for(y=0;y<zz;y++)
-	{
-		int n;
-		
-		len+=16;
-		
-		/*
-		 *	Avoid overrunning the given buffer	
-		 */
-		 
-		n=((char *)dst)-buf;		/* Bytes left in frame */
-		n=count-n;
-		if(n>dw)			/* But only as many as needed */
-			n=dw;
-		memcpy(dump, src, 16);		/* Junk */
-		if(n)
-			copy_to_user(dst, src, n);	/* Data */
-		dst+=dinc;
-		*src=0;				/* Synchronization */
-	}
-	
-	if(--loops>0)
-	{
-		mvv_write(0x14, mvv_read(0x14)|0xC0);	/* Other frame */
-		dst=(short *)buf+ww;
-		goto field_cap;
+	unsigned char r8 = 0x5;  /* value for reg8  */
+
+	if (rgb555)
+		r8 |= 0x20; /* else use untranslated rgb = 565 */
+	mvv_write(0x08,r8); /* capture rgb555/565, init DRAM, PC enable */
+
+/*	printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
+  
+	for (y = 0; y < dev->height; y++ ) 
+	{
+		*src = 0;  /* synchronisiert neue Zeile */
+		memcpy(tmp, src, dw+16); /* discard 8 word   */
+		cnt -= dev->height;
+		while (cnt <= 0) 
+		{ 
+			/*
+			 *	Dont copy too far
+			 */
+			int dt=dw;
+			if(dt+len>count)
+				dt=count-len;
+			cnt += dev->height;
+			copy_to_user(buf, tmp+16, dt);
+			buf += dt;    
+			len += dt;
+		}
 	}
-	
-	/*
-	 *	Set back to capture even frames
-	 */
-	 
-	if(wh>256)
-		mvv_write(0x14, (mvv_read(0x14)|0x80)&~0x40);
 	return len;
 }
 
@@ -738,6 +703,17 @@
 			v.tuners=1;
 			/* Good question.. its composite or SVHS so.. */
 			v.type = VIDEO_TYPE_CAMERA;
+			switch(v.channel)
+			{
+				case 0:
+					strcpy(v.name, "Composite");break;
+				case 1:
+					strcpy(v.name, "SVideo");break;
+				case 2:
+					strcpy(v.name, "Composite(VCR)");break;
+				case 3:
+					strcpy(v.name, "SVideo(VCR)");break;
+			}
 			if(copy_to_user(arg, &v, sizeof(v)))
 				return -EFAULT;
 			return 0;
@@ -747,7 +723,7 @@
 			int v;
 			if(copy_from_user(&v, arg,sizeof(v)))
 				return -EFAULT;
-			if(v<0 && v>3)
+			if(v<0 || v>3)
 				return -EINVAL;
 			pms_videosource(v&1);
 			pms_vcrinput(v>>1);
@@ -974,7 +950,8 @@
 		idec=1;
 	else 
 		idec=0;
-		
+
+	printk(KERN_INFO "PMS type is %d\n", idec);		
 	if(idec==0)
 		return -ENODEV;	
 
@@ -1050,7 +1027,7 @@
 
 int init_module(void)
 {
-	printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.01\n");
+	printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n");
 	
 	data_port = io_port +1;
 	
@@ -1062,6 +1039,7 @@
 	memcpy(&pms_device, &pms_template, sizeof(pms_template));
 	pms_device.height=240;
 	pms_device.width=320;
+	pms_swsense(75);
 	pms_resolution(320,240);
 	return video_register_device((struct video_device *)&pms_device);
 }
diff -u --recursive --new-file v2.1.76/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.76/linux/drivers/char/tty_io.c	Thu Dec  4 14:53:55 1997
+++ linux/drivers/char/tty_io.c	Wed Dec 31 16:40:08 1997
@@ -1548,12 +1548,16 @@
 		switch(cmd) {
 		case TIOCSBRK:
 		case TIOCCBRK:
-			return tty->driver.ioctl(tty, file, cmd, arg);
+			if (tty->driver.ioctl)
+				return tty->driver.ioctl(tty, file, cmd, arg);
+			return -EINVAL;
 			
 		/* These two ioctl's always return success; even if */
 		/* the driver doesn't support them. */
 		case TCSBRK:
-		case TCSBRKP:			
+		case TCSBRKP:
+			if (!tty->driver.ioctl)
+				return 0;
 			retval = tty->driver.ioctl(tty, file, cmd, arg);
 			if (retval == -ENOIOCTLCMD)
 				retval = 0;
diff -u --recursive --new-file v2.1.76/linux/drivers/char/vc_screen.c linux/drivers/char/vc_screen.c
--- v2.1.76/linux/drivers/char/vc_screen.c	Sat Oct 25 02:44:15 1997
+++ linux/drivers/char/vc_screen.c	Mon Dec 29 10:27:23 1997
@@ -56,11 +56,6 @@
 vcs_size(struct inode *inode)
 {
 	int size;
-#ifdef CONFIG_MULTIMON
-   	int currcons = MINOR(inode->i_rdev) & 127;
-	/* Multimon patch	*/
-	if (!vc_cons[currcons].d) return 0;
-#endif
 #ifdef CONFIG_FB_CONSOLE
 	int cons = MINOR(inode->i_rdev) & 127;
 
diff -u --recursive --new-file v2.1.76/linux/drivers/char/wdt.c linux/drivers/char/wdt.c
--- v2.1.76/linux/drivers/char/wdt.c	Sun Dec 21 22:36:13 1997
+++ linux/drivers/char/wdt.c	Mon Dec 29 10:24:21 1997
@@ -368,7 +368,7 @@
 #ifdef CONFIG_WDT_501	
 	misc_deregister(&temp_miscdev);
 #endif	
-	notifier_chain_unregister(&reboot_notifier_list, &wdt_notifier);
+	unregister_reboot_notifier(&wdt_notifier);
 	release_region(io,8);
 	free_irq(irq, NULL);
 }
@@ -388,7 +388,7 @@
 	misc_register(&temp_miscdev);
 #endif	
 	request_region(io, 8, "wdt501p");
-	notifier_chain_register(&reboot_notifier_list, &wdt_notifier);
+	register_reboot_notifier(&wdt_notifier);
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.76/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- v2.1.76/linux/drivers/isdn/isdn_net.c	Thu May 29 21:53:06 1997
+++ linux/drivers/isdn/isdn_net.c	Mon Dec 29 10:22:42 1997
@@ -194,14 +194,19 @@
 #define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/isdn.h>
-#include <linux/if_arp.h>
 #include <net/arp.h>
 #include <net/icmp.h>
+#if (LINUX_VERSION_CODE >= 0x020117)
+#include <linux/poll.h>
+#endif
 #include "isdn_common.h"
 #include "isdn_net.h"
 #ifdef CONFIG_ISDN_PPP
 #include "isdn_ppp.h"
 #endif
+#ifndef DEV_NUMBUFFS
+#include <net/pkt_sched.h>
+#endif
 
 /* Prototypes */
 
@@ -209,7 +214,9 @@
 static int isdn_net_wildmat(char *s, char *p);
 static int isdn_net_start_xmit(struct sk_buff *, struct device *);
 static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *);
+#ifdef DEV_NUMBUFFS
 static void dev_purge_queues(struct device *dev);	/* move this to net/core/dev.c */
+#endif
 
 char *isdn_net_revision = "$Revision: 1.44 $";
 
@@ -253,7 +260,7 @@
 	/* Fill in the MAC-level header. */
 	for (i = 0; i < ETH_ALEN - sizeof(u32); i++)
 		dev->dev_addr[i] = 0xfc;
-	memcpy(&(dev->dev_addr[i]), &dev->pa_addr, sizeof(u32));
+	memset(&(dev->dev_addr[i]), 0, sizeof(u32));
 
 	/* If this interface has slaves, start them also */
 
@@ -303,8 +310,18 @@
 		dev_kfree_skb(lp->sav_skb, FREE_WRITE);
 		lp->sav_skb = NULL;
 	}
+#ifdef DEV_NUMBUFFS
 	if (!lp->master)        /* purge only for master device */
 		dev_purge_queues(&lp->netdev->dev);
+#else
+	if (!lp->master) {	/* reset only master device */
+		/* Moral equivalent of dev_purge_queues():
+		   BEWARE! This chunk of code cannot be called from hardware
+		   interrupt handler. I hope it is true. --ANK
+		 */
+		qdisc_reset(lp->netdev->dev.qdisc);
+	}
+#endif
 	lp->dialstate = 0;
 	dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
 	dev->st_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
@@ -990,7 +1007,6 @@
 		ndev->trans_start = jiffies;
 	}
 	if (skb == NULL) {
-		dev_tint(ndev);
 		return 0;
 	}
 	/* Avoid timer-based retransmission conflicts. */
@@ -1466,20 +1482,17 @@
 #endif
 	ndev->header_cache_update = NULL;
 	ndev->mtu = 1500;
-	ndev->flags = IFF_NOARP;
-	ndev->family = AF_INET;
+	ndev->flags = IFF_NOARP|IFF_POINTOPOINT;
 	ndev->type = ARPHRD_ETHER;
 	ndev->addr_len = ETH_ALEN;
-	ndev->pa_addr = 0;
-	ndev->pa_brdaddr = 0;
-	ndev->pa_mask = 0;
-	ndev->pa_alen = 4;
 
 	for (i = 0; i < ETH_ALEN; i++)
 		ndev->broadcast[i] = 0xff;
 
+#ifdef DEV_NUMBUFFS
 	for (i = 0; i < DEV_NUMBUFFS; i++)
 		skb_queue_head_init(&ndev->buffs[i]);
+#endif
 
 	/* The ISDN-specific entries in the device structure. */
 	ndev->open = &isdn_net_open;
@@ -2247,7 +2260,7 @@
 				p->dev.hard_header_cache = NULL;
 #endif
 				p->dev.header_cache_update = NULL;
-				p->dev.flags = IFF_NOARP;
+				p->dev.flags = IFF_NOARP|IFF_POINTOPOINT;
 			} else {
 				p->dev.hard_header = isdn_net_header;
 				if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) {
@@ -2265,7 +2278,7 @@
 					p->dev.hard_header_cache = NULL;
 #endif
 					p->dev.header_cache_update = NULL;
-					p->dev.flags = IFF_NOARP;
+					p->dev.flags = IFF_NOARP|IFF_POINTOPOINT;
 				}
 			}
 		}
@@ -2585,6 +2598,7 @@
 	return 0;
 }
 
+#ifdef DEV_NUMBUFFS
 /*
  * helper function to flush device queues
  * the better place would be net/core/dev.c
@@ -2600,3 +2614,4 @@
 	}
 
 }
+#endif
diff -u --recursive --new-file v2.1.76/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c
--- v2.1.76/linux/drivers/isdn/isdn_tty.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/isdn/isdn_tty.c	Mon Dec 29 10:22:42 1997
@@ -2445,14 +2445,18 @@
  * Get phone-number from modem-commandbuffer
  */
 static void
-isdn_tty_getdial(char *p, char *q)
+isdn_tty_getdial(char *p, char *q,int cnt)
 {
 	int first = 1;
+	int limit=39;	/* MUST match the size in isdn_tty_parse to avoid
+				buffer overflow */
 
-	while (strchr("0123456789,#.*WPTS-", *p) && *p) {
+	while (strchr("0123456789,#.*WPTS-", *p) && *p && --cnt>0) {
 		if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first))
 			*q++ = *p;
 		p++;
+		if(!--limit)
+			break;
 		first = 0;
 	}
 	*q = 0;
@@ -2589,7 +2593,7 @@
 					m->mdmreg[i], ((i + 1) % 10) ? " " : "\r\n");
 				isdn_tty_at_cout(rb, info);
 			}
-			sprintf(rb, "\r\nEAZ/MSN: %s\r\n",
+			sprintf(rb, "\r\nEAZ/MSN: %.50s\r\n",
 				strlen(m->msn) ? m->msn : "None");
 			isdn_tty_at_cout(rb, info);
 			break;
@@ -3092,7 +3096,7 @@
 				break;
 			case 'D':
 				/* D - Dial */
-				isdn_tty_getdial(++p, ds);
+				isdn_tty_getdial(++p, ds, sizeof ds);
 				p += strlen(p);
 				if (!strlen(m->msn))
 					isdn_tty_modem_result(10, info);
diff -u --recursive --new-file v2.1.76/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.76/linux/drivers/net/Config.in	Tue Dec 23 16:30:59 1997
+++ linux/drivers/net/Config.in	Wed Dec 31 11:50:41 1997
@@ -15,7 +15,7 @@
 tristate 'Dummy net driver support' CONFIG_DUMMY
 tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  if [ "CONFIG_NETLINK_DEV" != "n" ]; then
+  if [ "$CONFIG_NETLINK_DEV" != "n" ]; then
      dep_tristate 'Ethertap network tap' CONFIG_ETHERTAP $CONFIG_NETLINK_DEV
   fi
 fi
diff -u --recursive --new-file v2.1.76/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c
--- v2.1.76/linux/drivers/net/eepro100.c	Mon Dec  1 12:04:13 1997
+++ linux/drivers/net/eepro100.c	Tue Dec 30 14:19:39 1997
@@ -36,7 +36,7 @@
 static int rx_copybreak = 200;
 
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
+static int max_interrupt_work = 200;
 
 #include <linux/config.h>
 #ifdef MODULE
diff -u --recursive --new-file v2.1.76/linux/drivers/net/hamradio/baycom_par.c linux/drivers/net/hamradio/baycom_par.c
--- v2.1.76/linux/drivers/net/hamradio/baycom_par.c	Sun Dec 21 22:36:13 1997
+++ linux/drivers/net/hamradio/baycom_par.c	Wed Dec 31 11:55:54 1997
@@ -392,18 +392,18 @@
 		return -ENXIO;
 	}
 	if (pp->irq < 0) {
-		printk(KERN_ERR "baycom_par: parport at 0x%x has no irq\n", pp->base);
+		printk(KERN_ERR "baycom_par: parport at 0x%lx has no irq\n", pp->base);
 		return -ENXIO;
 	}
 	memset(&bc->modem, 0, sizeof(bc->modem));
 	bc->hdrv.par.bitrate = 9600;
 	if (!(bc->pdev = parport_register_device(pp, dev->name, par96_preempt, par96_wakeup, 
 						 par96_interrupt, PARPORT_DEV_LURK, dev))) {
-		printk(KERN_ERR "baycom_par: cannot register parport at 0x%x\n", pp->base);
+		printk(KERN_ERR "baycom_par: cannot register parport at 0x%lx\n", pp->base);
 		return -ENXIO;
 	}
 	if (parport_claim(bc->pdev)) {
-		printk(KERN_ERR "baycom_par: parport at 0x%x busy\n", pp->base);
+		printk(KERN_ERR "baycom_par: parport at 0x%lx busy\n", pp->base);
 		parport_unregister_device(bc->pdev);
 		return -EBUSY;
 	}
diff -u --recursive --new-file v2.1.76/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.1.76/linux/drivers/scsi/Makefile	Sun Dec 21 22:36:13 1997
+++ linux/drivers/scsi/Makefile	Mon Dec 29 10:11:16 1997
@@ -1,4 +1,3 @@
-
 # Makefile for linux/drivers/scsi
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -34,14 +33,13 @@
 ifeq ($(CONFIG_SCSI),y)
   # We must attach scsi_syms.o to scsi.o, as otherwise there is nothing to
   # pull the object file from the archive.
-  SCSI=scsi.o
+  O_TARGET := scsi_n_syms.o
+  O_OBJS   := scsi.o
   ifeq ($(CONFIG_MODULES),y)
-    O_TARGET := scsi_n_syms.o
-    O_OBJS   := scsi.o scsi_error.o scsi_obsolete.o scsi_queue.o
     OX_OBJS  := scsi_syms.o
-    SCSI     := $(O_TARGET)
   endif
-  L_OBJS += $(SCSI) hosts.o scsi_ioctl.o constants.o scsicam.o
+  L_OBJS += scsi_n_syms.o hosts.o scsi_ioctl.o constants.o scsicam.o
+  L_OBJS += scsi_error.o scsi_obsolete.o scsi_queue.o
   ifeq ($(CONFIG_PROC_FS),y)
     L_OBJS += scsi_proc.o
   endif
diff -u --recursive --new-file v2.1.76/linux/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h
--- v2.1.76/linux/drivers/scsi/hosts.h	Sun Dec 21 22:36:14 1997
+++ linux/drivers/scsi/hosts.h	Fri Jan  2 14:02:17 1998
@@ -403,6 +403,8 @@
 extern struct Scsi_Host * scsi_register(Scsi_Host_Template *, int j);
 extern void scsi_unregister(struct Scsi_Host * i);
 
+extern void scsi_mark_host_reset(struct Scsi_Host *Host);
+
 #define BLANK_HOST {"", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
 
 struct Scsi_Device_Template
diff -u --recursive --new-file v2.1.76/linux/drivers/scsi/scsi_syms.c linux/drivers/scsi/scsi_syms.c
--- v2.1.76/linux/drivers/scsi/scsi_syms.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/scsi/scsi_syms.c	Thu Dec 25 11:42:54 1997
@@ -64,6 +64,7 @@
 EXPORT_SYMBOL(scsi_release_command);
 EXPORT_SYMBOL(print_Scsi_Cmnd);
 EXPORT_SYMBOL(scsi_block_when_processing_errors);
+EXPORT_SYMBOL(scsi_mark_host_reset);
 #if defined(CONFIG_SCSI_LOGGING) /* { */
 EXPORT_SYMBOL(scsi_logging_level);
 #endif
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.1.76/linux/drivers/sound/Config.in	Tue Dec 23 16:31:00 1997
+++ linux/drivers/sound/Config.in	Mon Dec 29 10:24:59 1997
@@ -136,7 +136,7 @@
 fi
 
 dep_tristate 'FM synthesizer (YM3812/OPL-3) support' CONFIG_YM3812 $CONFIG_SOUND
-dep_tristate 'Loopback MIDI device support' CONFIG_VMIDI $CONFIG_VMIDI
+dep_tristate 'Loopback MIDI device support' CONFIG_VMIDI $CONFIG_SOUND
 
 if [ "$CONFIG_UART6850" = "y" ]; then
     hex 'I/O base for UART 6850 MIDI port (Unknown)' U6850_BASE 0
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v2.1.76/linux/drivers/sound/Makefile	Tue Dec 23 16:31:00 1997
+++ linux/drivers/sound/Makefile	Tue Dec 30 11:02:38 1997
@@ -41,7 +41,7 @@
 L_OBJS   :=
 
 ifeq ($(CONFIG_SOUND),y)
-  L_OBJS += soundcard.o dev_table.o sound_switch.o sequencer.o sys_timer.o sound_timer.o lowlevel/lowlevel.o midi_synth.o midibuf.o sound_firmware.o audio.o dmabuf.o
+  L_OBJS += soundcard.o dev_table.o sequencer.o sys_timer.o sound_timer.o lowlevel/lowlevel.o midi_synth.o midibuf.o sound_firmware.o audio.o dmabuf.o
 else
   ifeq ($(CONFIG_SOUND),m)
      M_OBJS += sound.o
@@ -238,8 +238,8 @@
 lowlevel/lowlevel.o:
 	cd lowlevel; make
 
-sound.o: soundcard.o dev_table.o sound_switch.o audio.o dmabuf.o sequencer.o sys_timer.o sound_timer.o lowlevel/lowlevel.o midi_synth.o midibuf.o sound_firmware.o
-	ld -r -o sound.o soundcard.o dev_table.o sound_switch.o audio.o dmabuf.o \
+sound.o: soundcard.o dev_table.o audio.o dmabuf.o sequencer.o sys_timer.o sound_timer.o lowlevel/lowlevel.o midi_synth.o midibuf.o sound_firmware.o
+	ld -r -o sound.o soundcard.o dev_table.o audio.o dmabuf.o \
 	       sequencer.o sys_timer.o sound_timer.o lowlevel/lowlevel.o \
 	       midi_synth.o midibuf.o sound_firmware.o
 	rm sound_syms.o
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v2.1.76/linux/drivers/sound/ad1848.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/sound/ad1848.c	Tue Dec 30 11:02:38 1997
@@ -21,6 +21,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -121,7 +124,6 @@
 
 static int      ad1848_open(int dev, int mode);
 static void     ad1848_close(int dev);
-static int      ad1848_ioctl(int dev, unsigned int cmd, caddr_t arg);
 static void     ad1848_output_block(int dev, unsigned long buf, int count, int intrflag);
 static void     ad1848_start_input(int dev, unsigned long buf, int count, int intrflag);
 static int      ad1848_prepare_for_output(int dev, int bsize, int bcount);
@@ -524,80 +526,74 @@
 		ad_write(devc, 26, ad_read(devc, 26) | 0x40);	/* Mute mono out */
 }
 
-static int
-ad1848_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+static int ad1848_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
-	ad1848_info    *devc = mixer_devs[dev]->devc;
-
-	if (cmd == SOUND_MIXER_PRIVATE1)
-	  {
-		  int             val;
-
-		  val = *(int *) arg;
-
-		  if (val == 0xffff)
-			  return (*(int *) arg = devc->mixer_output_port);
-
-		  val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT);
-
-		  devc->mixer_output_port = val;
-		  val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT;	/* Always on */
-		  devc->mixer_output_port = val;
-
-		  if (val & AUDIO_SPEAKER)
-			  ad_write(devc, 26, ad_read(devc, 26) & ~0x40);	/* Unmute mono out */
-		  else
-			  ad_write(devc, 26, ad_read(devc, 26) | 0x40);		/* Mute mono out */
-
-		  return (*(int *) arg = devc->mixer_output_port);
-	  }
-	if (((cmd >> 8) & 0xff) == 'M')
-	  {
-		  int             val;
-
-		  if (_SIOC_DIR(cmd) & _SIOC_WRITE)
-			  switch (cmd & 0xff)
-			    {
-			    case SOUND_MIXER_RECSRC:
-				    val = *(int *) arg;
-				    return (*(int *) arg = ad1848_set_recmask(devc, val));
-				    break;
-
-			    default:
-				    val = *(int *) arg;
-				    return (*(int *) arg = ad1848_mixer_set(devc, cmd & 0xff, val));
-		  } else
-			  switch (cmd & 0xff)	/*
-						 * Return parameters
-						 */
-			    {
-
-			    case SOUND_MIXER_RECSRC:
-				    return (*(int *) arg = devc->recmask);
-				    break;
-
-			    case SOUND_MIXER_DEVMASK:
-				    return (*(int *) arg = devc->supported_devices);
-				    break;
-
-			    case SOUND_MIXER_STEREODEVS:
-				    if (devc->model == MD_C930)
-					    return (*(int *) arg = devc->supported_devices);
-				    else
-					    return (*(int *) arg = devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
-				    break;
-
-			    case SOUND_MIXER_RECMASK:
-				    return (*(int *) arg = devc->supported_rec_devices);
-				    break;
-
-			    case SOUND_MIXER_CAPS:
-				    return (*(int *) arg = SOUND_CAP_EXCL_INPUT);
-				    break;
+	ad1848_info *devc = mixer_devs[dev]->devc;
+	int val;
 
+	if (cmd == SOUND_MIXER_PRIVATE1) {
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+
+		if (val != 0xffff) {
+			val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT);
+			devc->mixer_output_port = val;
+			val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT;	/* Always on */
+			devc->mixer_output_port = val;
+			if (val & AUDIO_SPEAKER)
+				ad_write(devc, 26, ad_read(devc, 26) & ~0x40);	/* Unmute mono out */
+			else
+				ad_write(devc, 26, ad_read(devc, 26) | 0x40);		/* Mute mono out */
+		}
+		val = devc->mixer_output_port;
+		return __put_user(val, (int *)arg);
+	}
+	if (((cmd >> 8) & 0xff) == 'M') {
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+			switch (cmd & 0xff) {
+			case SOUND_MIXER_RECSRC:
+				if (__get_user(val, (int *)arg))
+					return -EFAULT;
+				val = ad1848_set_recmask(devc, val);
+				return __put_user(val, (int *)arg);
+				
 			    default:
-				    return (*(int *) arg = ad1848_mixer_get(devc, cmd & 0xff));
-			    }
+				if (__get_user(val, (int *)arg))
+					return -EFAULT;
+				val = ad1848_mixer_set(devc, cmd & 0xff, val);
+				return __put_user(val, (int *)arg);
+			} 
+		else
+			switch (cmd & 0xff) {
+				/*
+				 * Return parameters
+				 */
+			    
+			case SOUND_MIXER_RECSRC:
+				val = devc->recmask;
+				return __put_user(val, (int *)arg);
+				
+			case SOUND_MIXER_DEVMASK:
+				val = devc->supported_devices;
+				return __put_user(val, (int *)arg);
+				
+			case SOUND_MIXER_STEREODEVS:
+				val = devc->supported_devices;
+				if (devc->model != MD_C930)
+					val &= ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
+				return __put_user(val, (int *)arg);
+				
+			case SOUND_MIXER_RECMASK:
+				val = devc->supported_rec_devices;
+				return __put_user(val, (int *)arg);
+
+			case SOUND_MIXER_CAPS:
+				return __put_user(SOUND_CAP_EXCL_INPUT, (int *)arg);
+
+			default:
+				val = ad1848_mixer_get(devc, cmd & 0xff);
+				return __put_user(val, (int *)arg);
+			}
 	} else
 		return -EINVAL;
 }
@@ -784,7 +780,7 @@
 	ad1848_close,
 	ad1848_output_block,
 	ad1848_start_input,
-	ad1848_ioctl,
+	NULL,
 	ad1848_prepare_for_input,
 	ad1848_prepare_for_output,
 	ad1848_halt,
@@ -871,12 +867,6 @@
 
 	ad_unmute(devc);
 	restore_flags(flags);
-}
-
-static int
-ad1848_ioctl(int dev, unsigned int cmd, caddr_t arg)
-{
-	return -EINVAL;
 }
 
 static void
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/audio.c linux/drivers/sound/audio.c
--- v2.1.76/linux/drivers/sound/audio.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/sound/audio.c	Tue Dec 30 11:04:27 1997
@@ -11,6 +11,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 
 #include <linux/config.h>
 
@@ -345,197 +348,157 @@
 }
 
 int audio_ioctl(int dev, struct fileinfo *file_must_not_be_used,
-	    unsigned int cmd, caddr_t arg)
+		unsigned int cmd, caddr_t arg)
 {
-	int val;
+	int val, info, count;
+	unsigned long flags;
+	struct dma_buffparms *dmap;
 
 	dev = dev >> 4;
 
-	if (((cmd >> 8) & 0xff) == 'C')
-	{
+	if (((cmd >> 8) & 0xff) == 'C')	{
 		if (audio_devs[dev]->coproc)	/* Coprocessor ioctl */
 			return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0);
 		/* else
-			printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */
+		        printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */
 		return -ENXIO;
-	}
-	else switch (cmd)
-	{
-		case SNDCTL_DSP_SYNC:
-			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-				return 0;
-
-			if (audio_devs[dev]->dmap_out->fragment_size == 0)
-				return 0;
-			sync_output(dev);
-			DMAbuf_sync(dev);
-			DMAbuf_reset(dev);
+	} else switch (cmd) {
+	case SNDCTL_DSP_SYNC:
+		if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
 			return 0;
-
-		case SNDCTL_DSP_POST:
-			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-				return 0;
-			if (audio_devs[dev]->dmap_out->fragment_size == 0)
-				return 0;
-			audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
-			sync_output(dev);
-			dma_ioctl(dev, SNDCTL_DSP_POST, (caddr_t) 0);
+		if (audio_devs[dev]->dmap_out->fragment_size == 0)
 			return 0;
+		sync_output(dev);
+		DMAbuf_sync(dev);
+		DMAbuf_reset(dev);
+		return 0;
 
-		case SNDCTL_DSP_RESET:
-			audio_mode[dev] = AM_NONE;
-			DMAbuf_reset(dev);
+	case SNDCTL_DSP_POST:
+		if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
 			return 0;
+		if (audio_devs[dev]->dmap_out->fragment_size == 0)
+			return 0;
+		audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
+		sync_output(dev);
+		dma_ioctl(dev, SNDCTL_DSP_POST, (caddr_t) 0);
+		return 0;
 
-		case SNDCTL_DSP_GETFMTS:
-			return (*(int *) arg = audio_devs[dev]->format_mask);
-
-		case SNDCTL_DSP_SETFMT:
-			val = *(int *) arg;
-			return (*(int *) arg = set_format(dev, val));
-
-		case SNDCTL_DSP_GETISPACE:
-			if (!(audio_devs[dev]->open_mode & OPEN_READ))
-				return 0;
-			if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
-				return -EBUSY;
-
-			{
-				audio_buf_info  info;
-
-				int err = dma_ioctl(dev, cmd, (caddr_t) & info);
-
-				if (err < 0)
-					return err;
-
-				memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
-				return 0;
-			}
-
-		case SNDCTL_DSP_GETOSPACE:
-			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-				  return -EPERM;
-			if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
-				  return -EBUSY;
-
-			{
-				audio_buf_info  info;
-
-				int err = dma_ioctl(dev, cmd, (caddr_t) & info);
-
-				if (err < 0)
-					return err;
+	case SNDCTL_DSP_RESET:
+		audio_mode[dev] = AM_NONE;
+		DMAbuf_reset(dev);
+		return 0;
 
-				memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
-				return 0;
-			}
+	case SNDCTL_DSP_GETFMTS:
+		val = audio_devs[dev]->format_mask;
+		return __put_user(val, (int *)arg);
+
+	case SNDCTL_DSP_SETFMT:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		val = set_format(dev, val);
+		return __put_user(val, (int *)arg);
 
-		case SNDCTL_DSP_NONBLOCK:
-			dev_nblock[dev] = 1;
+	case SNDCTL_DSP_GETISPACE:
+		if (!(audio_devs[dev]->open_mode & OPEN_READ))
 			return 0;
+		if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+			return -EBUSY;
+		return dma_ioctl(dev, cmd, arg);
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+			return -EPERM;
+		if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+			return -EBUSY;
+		return dma_ioctl(dev, cmd, arg);
+		
+	case SNDCTL_DSP_NONBLOCK:
+		dev_nblock[dev] = 1;
+		return 0;
 
-		case SNDCTL_DSP_GETCAPS:
-		{
-			int info = 1;	/* Revision level of this ioctl() */
-
+	case SNDCTL_DSP_GETCAPS:
+			info = 1 | DSP_CAP_MMAP;	/* Revision level of this ioctl() */
 			if (audio_devs[dev]->flags & DMA_DUPLEX &&
-				audio_devs[dev]->open_mode == OPEN_READWRITE)
-					info |= DSP_CAP_DUPLEX;
-
+			    audio_devs[dev]->open_mode == OPEN_READWRITE)
+				info |= DSP_CAP_DUPLEX;
 			if (audio_devs[dev]->coproc)
 				info |= DSP_CAP_COPROC;
-
 			if (audio_devs[dev]->d->local_qlen)	/* Device has hidden buffers */
 				info |= DSP_CAP_BATCH;
-
 			if (audio_devs[dev]->d->trigger)	/* Supports SETTRIGGER */
 				info |= DSP_CAP_TRIGGER;
-
-			info |= DSP_CAP_MMAP;
-
-			memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
-			return 0;
-		}
-
-		case SOUND_PCM_WRITE_RATE:
-			val = *(int *) arg;
-			return (*(int *) arg = audio_devs[dev]->d->set_speed(dev, val));
-
-		case SOUND_PCM_READ_RATE:
-			return (*(int *) arg = audio_devs[dev]->d->set_speed(dev, 0));
-
-		case SNDCTL_DSP_STEREO:
-		{
-			int n;
-
-			n = *(int *) arg;
-			if (n > 1)
-			{
-/*				printk(KERN_DENUG "sound: SNDCTL_DSP_STEREO called with invalid argument %d\n", n);*/
-				return -EINVAL;
-			}
-			if (n < 0)
-				return -EINVAL;
-
-			return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, n + 1) - 1);
-		}
-
-		case SOUND_PCM_WRITE_CHANNELS:
-			val = *(int *) arg;
-			return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, val));
-
-		case SOUND_PCM_READ_CHANNELS:
-			return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, 0));
-
-		case SOUND_PCM_READ_BITS:
-			return (*(int *) arg = audio_devs[dev]->d->set_bits(dev, 0));
-
-		case SNDCTL_DSP_SETDUPLEX:
-			if (audio_devs[dev]->open_mode != OPEN_READWRITE)
-				return -EPERM;
-			if (audio_devs[dev]->flags & DMA_DUPLEX)
-				return 0;
-			else
-				return -EIO;
-
-		case SNDCTL_DSP_PROFILE:
-			if (audio_devs[dev]->open_mode & OPEN_WRITE)
-				audio_devs[dev]->dmap_out->applic_profile = *(int *) arg;
-			if (audio_devs[dev]->open_mode & OPEN_READ)
-				audio_devs[dev]->dmap_in->applic_profile = *(int *) arg;
-			return 0;
-
-		case SNDCTL_DSP_GETODELAY:
-		{
-			int count;
-			unsigned long   flags;
-			struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
-			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-				return -EINVAL;
-			if (!(dmap->flags & DMA_ALLOC_DONE))
-				return *(int *) arg = 0;
-
-			save_flags (flags);
-			cli ();
-			/* Compute number of bytes that have been played */
-			count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
-			if (count < dmap->fragment_size && dmap->qhead != 0)
-				count += dmap->bytes_in_use;	/* Pointer wrap not handled yet */
-			count += dmap->byte_counter;
-
-			/* Substract current count from the number of bytes written by app */
-			count = dmap->user_counter - count;
-			if (count < 0)
-				count = 0;
-			restore_flags (flags);
-
-			return *(int *) arg = count;
-		}
-		break;
-
-		default:
-			return dma_ioctl(dev, cmd, arg);
+			return __put_user(info, (int *)arg);
+			
+	case SOUND_PCM_WRITE_RATE:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		val = audio_devs[dev]->d->set_speed(dev, val);
+		return __put_user(val, (int *)arg);
+
+	case SOUND_PCM_READ_RATE:
+		val = audio_devs[dev]->d->set_speed(dev, 0);
+		return __put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_STEREO:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val > 1 || val < 0)
+			return -EINVAL;
+		val = audio_devs[dev]->d->set_channels(dev, val + 1) - 1;
+		return __put_user(val, (int *)arg);
+
+	case SOUND_PCM_WRITE_CHANNELS:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		val = audio_devs[dev]->d->set_channels(dev, val);
+		return __put_user(val, (int *)arg);
+
+	case SOUND_PCM_READ_CHANNELS:
+		val = audio_devs[dev]->d->set_channels(dev, 0);
+		return __put_user(val, (int *)arg);
+		
+	case SOUND_PCM_READ_BITS:
+		val = audio_devs[dev]->d->set_bits(dev, 0);
+		return __put_user(val, (int *)arg);
+
+	case SNDCTL_DSP_SETDUPLEX:
+		if (audio_devs[dev]->open_mode != OPEN_READWRITE)
+			return -EPERM;
+		return (audio_devs[dev]->flags & DMA_DUPLEX) ? 0 : -EIO;
+
+	case SNDCTL_DSP_PROFILE:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (audio_devs[dev]->open_mode & OPEN_WRITE)
+			audio_devs[dev]->dmap_out->applic_profile = val;
+		if (audio_devs[dev]->open_mode & OPEN_READ)
+			audio_devs[dev]->dmap_in->applic_profile = val;
+		return 0;
+		
+	case SNDCTL_DSP_GETODELAY:
+		dmap = audio_devs[dev]->dmap_out;
+		if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+			return -EINVAL;
+		if (!(dmap->flags & DMA_ALLOC_DONE))
+			return __put_user(0, (int *)arg);
+		
+		save_flags (flags);
+		cli();
+		/* Compute number of bytes that have been played */
+		count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
+		if (count < dmap->fragment_size && dmap->qhead != 0)
+			count += dmap->bytes_in_use;	/* Pointer wrap not handled yet */
+		count += dmap->byte_counter;
+		
+		/* Substract current count from the number of bytes written by app */
+		count = dmap->user_counter - count;
+		if (count < 0)
+			count = 0;
+		restore_flags (flags);
+		return __put_user(count, (int *)arg);
+		
+	default:
+		return dma_ioctl(dev, cmd, arg);
 	}
 }
 
@@ -676,14 +639,13 @@
 	dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
 }
 
-static int dma_subdivide(int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
+static int dma_subdivide(int dev, struct dma_buffparms *dmap, int fact)
 {
-	if (fact == 0)
-	{
+	if (fact == 0) {
 		fact = dmap->subdivision;
 		if (fact == 0)
 			fact = 1;
-		return (*(int *) arg = fact);
+		return fact;
 	}
 	if (dmap->subdivision != 0 || dmap->fragment_size)	/* Too late to change */
 		return -EINVAL;
@@ -695,10 +657,10 @@
 		return -EINVAL;
 
 	dmap->subdivision = fact;
-	return (*(int *) arg = fact);
+	return fact;
 }
 
-static int dma_set_fragment(int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
+static int dma_set_fragment(int dev, struct dma_buffparms *dmap, int fact)
 {
 	int bytes, count;
 
@@ -747,311 +709,223 @@
 		dmap->fragment_size /= 2;	/* Needs at least 2 buffers */
 
 	dmap->subdivision = 1;	/* Disable SNDCTL_DSP_SUBDIVIDE */
-	if (arg)
-		return (*(int *) arg = bytes | ((count - 1) << 16));
-	else
-		return 0;
+	return bytes | ((count - 1) << 16);
 }
 
 int dma_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
-
 	struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
 	struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
-
-	switch (cmd)
-	{
-
-		case SNDCTL_DSP_SUBDIVIDE:
-		{
-			int             fact;
-			int             ret = 0;
-
-			fact = *(int *) arg;
-
-			if (audio_devs[dev]->open_mode & OPEN_WRITE)
-				ret = dma_subdivide(dev, dmap_out, arg, fact);
-			if (ret < 0)
-				 return ret;
-
-			if (audio_devs[dev]->open_mode != OPEN_WRITE ||
-				(audio_devs[dev]->flags & DMA_DUPLEX &&
-				audio_devs[dev]->open_mode & OPEN_READ))
-					ret = dma_subdivide(dev, dmap_in, arg, fact);
-
+	struct dma_buffparms *dmap;
+	audio_buf_info info;
+	count_info cinfo;
+	int fact, ret, changed, bits, count, err;
+	unsigned long flags;
+
+	switch (cmd) {
+	case SNDCTL_DSP_SUBDIVIDE:
+		ret = 0;
+		if (__get_user(fact, (int *)arg))
+			return -EFAULT;
+		if (audio_devs[dev]->open_mode & OPEN_WRITE)
+			ret = dma_subdivide(dev, dmap_out, fact);
+		if (ret < 0)
 			return ret;
-		}
-		break;
-
-		case SNDCTL_DSP_GETISPACE:
-		case SNDCTL_DSP_GETOSPACE:
-		{
-			struct dma_buffparms *dmap = dmap_out;
-
-			audio_buf_info *info = (audio_buf_info *) arg;
-
-			if (cmd == SNDCTL_DSP_GETISPACE &&
-				!(audio_devs[dev]->open_mode & OPEN_READ))
-					return -EINVAL;
-
-			if (cmd == SNDCTL_DSP_GETOSPACE &&
-				!(audio_devs[dev]->open_mode & OPEN_WRITE))
-					return -EINVAL;
-
-			if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
-				dmap = dmap_in;
-
-			if (dmap->mapping_flags & DMA_MAP_MAPPED)
-				return -EINVAL;
-
-			if (!(dmap->flags & DMA_ALLOC_DONE))
-				reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
-
-			info->fragstotal = dmap->nbufs;
+		if (audio_devs[dev]->open_mode != OPEN_WRITE ||
+		    (audio_devs[dev]->flags & DMA_DUPLEX &&
+		     audio_devs[dev]->open_mode & OPEN_READ))
+			ret = dma_subdivide(dev, dmap_in, fact);
+		if (ret < 0)
+			return ret;
+		return __put_user(ret, (int *)arg);
 
-			if (cmd == SNDCTL_DSP_GETISPACE)
-				info->fragments = dmap->qlen;
-			else
-			{
-				if (!DMAbuf_space_in_queue(dev))
-					info->fragments = 0;
-				else
-				{
-					info->fragments = DMAbuf_space_in_queue(dev);
-					if (audio_devs[dev]->d->local_qlen)
-					{
-						int tmp = audio_devs[dev]->d->local_qlen(dev);
-
-						if (tmp && info->fragments)
-							tmp--;	/*
-								 * This buffer has been counted twice
-								 */
-						info->fragments -= tmp;
-					}
+	case SNDCTL_DSP_GETISPACE:
+	case SNDCTL_DSP_GETOSPACE:
+		dmap = dmap_out;
+		if (cmd == SNDCTL_DSP_GETISPACE && !(audio_devs[dev]->open_mode & OPEN_READ))
+			return -EINVAL;
+		if (cmd == SNDCTL_DSP_GETOSPACE && !(audio_devs[dev]->open_mode & OPEN_WRITE))
+			return -EINVAL;
+		if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
+			dmap = dmap_in;
+		if (dmap->mapping_flags & DMA_MAP_MAPPED)
+			return -EINVAL;
+		if (!(dmap->flags & DMA_ALLOC_DONE))
+			reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
+		info.fragstotal = dmap->nbufs;
+		if (cmd == SNDCTL_DSP_GETISPACE)
+			info.fragments = dmap->qlen;
+		else {
+			if (!DMAbuf_space_in_queue(dev))
+				info.fragments = 0;
+			else {
+				info.fragments = DMAbuf_space_in_queue(dev);
+				if (audio_devs[dev]->d->local_qlen) {
+					int tmp = audio_devs[dev]->d->local_qlen(dev);
+					if (tmp && info.fragments)
+						tmp--;	/*
+							 * This buffer has been counted twice
+							 */
+					info.fragments -= tmp;
 				}
 			}
-
-			if (info->fragments < 0)
-				info->fragments = 0;
-			else if (info->fragments > dmap->nbufs)
-				info->fragments = dmap->nbufs;
-
-			info->fragsize = dmap->fragment_size;
-			info->bytes = info->fragments * dmap->fragment_size;
-
-			if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
-				info->bytes -= dmap->counts[dmap->qhead];
-			else
-			{
-				info->fragments = info->bytes / dmap->fragment_size;
-				info->bytes -= dmap->user_counter % dmap->fragment_size;
-			}
 		}
-		return 0;
-
-		case SNDCTL_DSP_SETTRIGGER:
-		{
-			unsigned long flags;
-
-			int bits;
-			int changed;
-
-			bits = *(int *) arg;
-			bits &= audio_devs[dev]->open_mode;
-
-			if (audio_devs[dev]->d->trigger == NULL)
-				return -EINVAL;
-
-			if (!(audio_devs[dev]->flags & DMA_DUPLEX))
-				if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT))
-				{
-					/* printk(KERN_WARNING "Sound: Device doesn't have full duplex capability\n");*/
-					return -EINVAL;
-				}
-			save_flags(flags);
-			cli();
-			changed = audio_devs[dev]->enable_bits ^ bits;
-
-			if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go)
-			{
-				int err;
-
-				reorganize_buffers(dev, dmap_in, 1);
-
-				if ((err = audio_devs[dev]->d->prepare_for_input(dev,
-					dmap_in->fragment_size, dmap_in->nbufs)) < 0)
-						return -err;
-
-				dmap_in->dma_mode = DMODE_INPUT;
-				audio_devs[dev]->enable_bits = bits;
-				DMAbuf_activate_recording(dev, dmap_in);
-			}
-			
-			if ((changed & bits) & PCM_ENABLE_OUTPUT &&
-				(dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
-				audio_devs[dev]->go)
-			{
-
-				if (!(dmap_out->flags & DMA_ALLOC_DONE))
-				{
-					reorganize_buffers(dev, dmap_out, 0);
-				}
-				dmap_out->dma_mode = DMODE_OUTPUT;
-				audio_devs[dev]->enable_bits = bits;
-				dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
-				DMAbuf_launch_output(dev, dmap_out);
-			}
+		if (info.fragments < 0)
+				info.fragments = 0;
+		else if (info.fragments > dmap->nbufs)
+			info.fragments = dmap->nbufs;
+
+		info.fragsize = dmap->fragment_size;
+		info.bytes = info.fragments * dmap->fragment_size;
+
+		if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
+			info.bytes -= dmap->counts[dmap->qhead];
+		else {
+			info.fragments = info.bytes / dmap->fragment_size;
+			info.bytes -= dmap->user_counter % dmap->fragment_size;
+		}
+		return __copy_to_user(arg, &info, sizeof(info));
+
+	case SNDCTL_DSP_SETTRIGGER:
+		if (__get_user(bits, (int *)arg))
+			return -EFAULT;
+		bits &= audio_devs[dev]->open_mode;
+		if (audio_devs[dev]->d->trigger == NULL)
+			return -EINVAL;
+		if (!(audio_devs[dev]->flags & DMA_DUPLEX) && (bits & PCM_ENABLE_INPUT) &&
+		    (bits & PCM_ENABLE_OUTPUT))
+			return -EINVAL;
+		save_flags(flags);
+		cli();
+		changed = audio_devs[dev]->enable_bits ^ bits;
+		if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go) {
+			reorganize_buffers(dev, dmap_in, 1);
+			if ((err = audio_devs[dev]->d->prepare_for_input(dev,
+					     dmap_in->fragment_size, dmap_in->nbufs)) < 0)
+				return -err;
+			dmap_in->dma_mode = DMODE_INPUT;
 			audio_devs[dev]->enable_bits = bits;
-			
-			if (changed && audio_devs[dev]->d->trigger)
-			{
-				audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go);
-			}
-			restore_flags(flags);
-		}
-		/* Falls through... */
-
-		case SNDCTL_DSP_GETTRIGGER:
-			return (*(int *) arg = audio_devs[dev]->enable_bits);
-
-		case SNDCTL_DSP_SETSYNCRO:
-
-			if (!audio_devs[dev]->d->trigger)
-				return -EINVAL;
-
-			audio_devs[dev]->d->trigger(dev, 0);
-			audio_devs[dev]->go = 0;
-			return 0;
-			break;
-
-		case SNDCTL_DSP_GETIPTR:
-		{
-			count_info      info;
-			unsigned long   flags;
-			struct dma_buffparms *dmap = dmap_in;
-
-			if (!(audio_devs[dev]->open_mode & OPEN_READ))
-				return -EINVAL;
-
-			save_flags(flags);
-			cli();
-			info.bytes = dmap->byte_counter;
-			info.ptr = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_INPUT) & ~3;
-			if (info.ptr < dmap->fragment_size && dmap->qtail != 0)
-				info.bytes += dmap->bytes_in_use;	/* Pointer wrap not handled yet */
-
-			info.blocks = dmap->qlen;
-			info.bytes += info.ptr;
-			memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
-
-			if (dmap->mapping_flags & DMA_MAP_MAPPED)
-				dmap->qlen = 0;	/* Reset interrupt counter */
-			restore_flags(flags);
-			return 0;
-		}
-		break;
-
-		case SNDCTL_DSP_GETOPTR:
-		{
-			count_info      info;
-			unsigned long   flags;
-			struct dma_buffparms *dmap = dmap_out;
-
-			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-				return -EINVAL;
-
-			save_flags(flags);
-			cli();
-			info.bytes = dmap->byte_counter;
-			info.ptr = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT) & ~3;
-			if (info.ptr < dmap->fragment_size && dmap->qhead != 0)
-				info.bytes += dmap->bytes_in_use;	/* Pointer wrap not handled yet */
-			info.blocks = dmap->qlen;
-			info.bytes += info.ptr;
-			memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
-
-			if (dmap->mapping_flags & DMA_MAP_MAPPED)
-				dmap->qlen = 0;	/* Reset interrupt counter */
-
-			restore_flags(flags);
-			return 0;
-		}
-		break;
-
-		case SNDCTL_DSP_GETODELAY:
-		{
-			int count;
-			unsigned long   flags;
-			struct dma_buffparms *dmap = dmap_out;
-
-			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-				return -EINVAL;
-			if (!(dmap->flags & DMA_ALLOC_DONE))
-				return (*(int *) arg = 0);
-
-			save_flags(flags);
-			cli();
-			/* Compute number of bytes that have been played */
-			count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
-			if (count < dmap->fragment_size && dmap->qhead != 0)
-				count += dmap->bytes_in_use;	/* Pointer wrap not handled yet */
-			count += dmap->byte_counter;
-
-			/* Substract current count from the number of bytes written by app */
-			count = dmap->user_counter - count;
-			if (count < 0)
-        			count = 0;
-			restore_flags (flags);
-
-			return (*(int *) arg = count);
+			DMAbuf_activate_recording(dev, dmap_in);
 		}
-
-		case SNDCTL_DSP_POST:
-			if (audio_devs[dev]->dmap_out->qlen > 0)
-				if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
-					DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out);
-			return 0;
-
-		case SNDCTL_DSP_GETBLKSIZE:
-		{
-			int             fragment_size;
-			struct dma_buffparms *dmap = dmap_out;
-
-			if (audio_devs[dev]->open_mode & OPEN_WRITE)
-				reorganize_buffers(dev, dmap_out,
-					(audio_devs[dev]->open_mode == OPEN_READ));
-			if (audio_devs[dev]->open_mode != OPEN_WRITE ||
-				(audio_devs[dev]->flags & DMA_DUPLEX &&
-				audio_devs[dev]->open_mode & OPEN_READ))
-					reorganize_buffers(dev, dmap_in,
-						(audio_devs[dev]->open_mode == OPEN_READ));
-			if (audio_devs[dev]->open_mode == OPEN_READ)
-					dmap = dmap_in;
-			fragment_size = dmap->fragment_size;
-			return (*(int *) arg = fragment_size);
+		if ((changed & bits) & PCM_ENABLE_OUTPUT &&
+		    (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
+		    audio_devs[dev]->go) {
+			if (!(dmap_out->flags & DMA_ALLOC_DONE))
+				reorganize_buffers(dev, dmap_out, 0);
+			dmap_out->dma_mode = DMODE_OUTPUT;
+			audio_devs[dev]->enable_bits = bits;
+			dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
+			DMAbuf_launch_output(dev, dmap_out);
 		}
+		audio_devs[dev]->enable_bits = bits;
+		if (changed && audio_devs[dev]->d->trigger)
+			audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go);
+		restore_flags(flags);
+		/* Falls through... */
 
-		case SNDCTL_DSP_SETFRAGMENT:
-		{
-			int  fact;
-			int  ret = 0;
+	case SNDCTL_DSP_GETTRIGGER:
+		ret = audio_devs[dev]->enable_bits;
+		return __put_user(ret, (int *)arg);
+
+	case SNDCTL_DSP_SETSYNCRO:
+		if (!audio_devs[dev]->d->trigger)
+			return -EINVAL;
+		audio_devs[dev]->d->trigger(dev, 0);
+		audio_devs[dev]->go = 0;
+		return 0;
 
-			fact = *(int *) arg;
-			if (audio_devs[dev]->open_mode & OPEN_WRITE)
-				ret = dma_set_fragment(dev, dmap_out, arg, fact);
-			if (ret < 0)
-				return ret;
-
-			if (audio_devs[dev]->open_mode != OPEN_WRITE ||
-				(audio_devs[dev]->flags & DMA_DUPLEX &&
-				audio_devs[dev]->open_mode & OPEN_READ))
-				ret = dma_set_fragment(dev, dmap_in, arg, fact);
+	case SNDCTL_DSP_GETIPTR:
+		if (!(audio_devs[dev]->open_mode & OPEN_READ))
+			return -EINVAL;
+		save_flags(flags);
+		cli();
+		cinfo.bytes = dmap_in->byte_counter;
+		cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_in, DMODE_INPUT) & ~3;
+		if (cinfo.ptr < dmap_in->fragment_size && dmap_in->qtail != 0)
+			cinfo.bytes += dmap_in->bytes_in_use;	/* Pointer wrap not handled yet */
+		cinfo.blocks = dmap_in->qlen;
+		cinfo.bytes += cinfo.ptr;
+		if (dmap_in->mapping_flags & DMA_MAP_MAPPED)
+			dmap_in->qlen = 0;	/* Reset interrupt counter */
+		restore_flags(flags);
+		return __copy_to_user(arg, &cinfo, sizeof(cinfo));
+
+	case SNDCTL_DSP_GETOPTR:
+		if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+			return -EINVAL;
+
+		save_flags(flags);
+		cli();
+		cinfo.bytes = dmap_out->byte_counter;
+		cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_out, DMODE_OUTPUT) & ~3;
+		if (cinfo.ptr < dmap_out->fragment_size && dmap_out->qhead != 0)
+			cinfo.bytes += dmap_out->bytes_in_use;	/* Pointer wrap not handled yet */
+		cinfo.blocks = dmap_out->qlen;
+		cinfo.bytes += cinfo.ptr;
+		if (dmap_out->mapping_flags & DMA_MAP_MAPPED)
+			dmap_out->qlen = 0;	/* Reset interrupt counter */
+		restore_flags(flags);
+		return __copy_to_user(arg, &cinfo, sizeof(cinfo));
+
+	case SNDCTL_DSP_GETODELAY:
+		if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+			return -EINVAL;
+		if (!(dmap_out->flags & DMA_ALLOC_DONE))
+			return __put_user(0, (int *)arg);
+		save_flags(flags);
+		cli();
+		/* Compute number of bytes that have been played */
+		count = DMAbuf_get_buffer_pointer (dev, dmap_out, DMODE_OUTPUT);
+		if (count < dmap_out->fragment_size && dmap_out->qhead != 0)
+			count += dmap_out->bytes_in_use;	/* Pointer wrap not handled yet */
+		count += dmap_out->byte_counter;
+		/* Substract current count from the number of bytes written by app */
+		count = dmap_out->user_counter - count;
+		if (count < 0)
+			count = 0;
+		restore_flags (flags);
+		return __put_user(count, (int *)arg);
+
+	case SNDCTL_DSP_POST:
+		if (audio_devs[dev]->dmap_out->qlen > 0)
+			if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
+				DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out);
+		return 0;
 
+	case SNDCTL_DSP_GETBLKSIZE:
+		dmap = dmap_out;
+		if (audio_devs[dev]->open_mode & OPEN_WRITE)
+			reorganize_buffers(dev, dmap_out, (audio_devs[dev]->open_mode == OPEN_READ));
+		if (audio_devs[dev]->open_mode == OPEN_READ ||
+		    (audio_devs[dev]->flags & DMA_DUPLEX &&
+		     audio_devs[dev]->open_mode & OPEN_READ))
+			reorganize_buffers(dev, dmap_in, (audio_devs[dev]->open_mode == OPEN_READ));
+		if (audio_devs[dev]->open_mode == OPEN_READ)
+			dmap = dmap_in;
+		ret = dmap->fragment_size;
+		return __put_user(ret, (int *)arg);
+
+	case SNDCTL_DSP_SETFRAGMENT:
+		ret = 0;
+		if (__get_user(fact, (int *)arg))
+			return -EFAULT;
+		if (audio_devs[dev]->open_mode & OPEN_WRITE)
+			ret = dma_set_fragment(dev, dmap_out, fact);
+		if (ret < 0)
 			return ret;
-		}
-		break;
+		if (audio_devs[dev]->open_mode == OPEN_READ ||
+		    (audio_devs[dev]->flags & DMA_DUPLEX &&
+		     audio_devs[dev]->open_mode & OPEN_READ))
+			ret = dma_set_fragment(dev, dmap_in, fact);
+		if (ret < 0)
+			return ret;
+		if (!arg) /* don't know what this is good for, but preserve old semantics */
+			return 0;
+		return __put_user(ret, (int *)arg);
 
-		default:
-			return audio_devs[dev]->d->ioctl(dev, cmd, arg);
+	default:
+		if (!audio_devs[dev]->d->ioctl)
+			return -EINVAL;
+		return audio_devs[dev]->d->ioctl(dev, cmd, arg);
 	}
 }
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
--- v2.1.76/linux/drivers/sound/dmabuf.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/sound/dmabuf.c	Tue Dec 30 10:59:17 1997
@@ -121,24 +121,38 @@
 }
 
 
-static unsigned int
-default_set_bits(int dev, unsigned int bits)
+static unsigned int default_set_bits(int dev, unsigned int bits)
 {
-	return audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SETFMT, (caddr_t) & bits);
+	mm_segment_t fs = get_fs();
+	unsigned int r;
+
+	set_fs(get_ds());
+	r = audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SETFMT, (caddr_t)&bits);
+	set_fs(fs);
+	return r;
 }
 
-static int
-default_set_speed(int dev, int speed)
+static int default_set_speed(int dev, int speed)
 {
-	return audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SPEED, (caddr_t) & speed);
+	mm_segment_t fs = get_fs();
+	int r;
+
+	set_fs(get_ds());
+	r = audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SPEED, (caddr_t)&speed);
+	set_fs(fs);
+	return r;
 }
 
-static short
-default_set_channels(int dev, short channels)
+static short default_set_channels(int dev, short channels)
 {
-	int             c = channels;
+	int c = channels;
+	mm_segment_t fs = get_fs();
+	short r;
 
-	return audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_CHANNELS, (caddr_t) & c);
+	set_fs(get_ds());
+	r = audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_CHANNELS, (caddr_t)&c);
+	set_fs(fs);
+	return r;
 }
 
 static void
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/gus_midi.c linux/drivers/sound/gus_midi.c
--- v2.1.76/linux/drivers/sound/gus_midi.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/gus_midi.c	Tue Dec 30 10:59:17 1997
@@ -175,12 +175,6 @@
 	return 0;
 }
 
-static int
-gus_midi_ioctl(int dev, unsigned cmd, caddr_t arg)
-{
-	return -EINVAL;
-}
-
 static void
 gus_midi_kick(int dev)
 {
@@ -218,7 +212,7 @@
 	{0},
 	gus_midi_open,
 	gus_midi_close,
-	gus_midi_ioctl,
+	NULL, /* ioctl */
 	gus_midi_out,
 	gus_midi_start_read,
 	gus_midi_end_read,
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/gus_wave.c linux/drivers/sound/gus_wave.c
--- v2.1.76/linux/drivers/sound/gus_wave.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/sound/gus_wave.c	Tue Dec 30 11:02:39 1997
@@ -10,6 +10,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 
 
@@ -1107,34 +1110,27 @@
 	return 1;
 }
 
-static int
-guswave_ioctl(int dev,
-	      unsigned int cmd, caddr_t arg)
+static int guswave_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
 
-	switch (cmd)
-	  {
-	  case SNDCTL_SYNTH_INFO:
-		  gus_info.nr_voices = nr_voices;
-		  memcpy((&((char *) arg)[0]), (char *) &gus_info, sizeof(gus_info));
-		  return 0;
-		  break;
+	switch (cmd) {
+	case SNDCTL_SYNTH_INFO:
+		gus_info.nr_voices = nr_voices;
+		return __copy_to_user(arg, &gus_info, sizeof(gus_info));
 
-	  case SNDCTL_SEQ_RESETSAMPLES:
-		  reset_sample_memory();
-		  return 0;
-		  break;
+	case SNDCTL_SEQ_RESETSAMPLES:
+		reset_sample_memory();
+		return 0;
 
-	  case SNDCTL_SEQ_PERCMODE:
-		  return 0;
-		  break;
+	case SNDCTL_SEQ_PERCMODE:
+		return 0;
 
-	  case SNDCTL_SYNTH_MEMAVL:
-		  return (gus_mem_size == 0) ? 0 : gus_mem_size - free_mem_ptr - 32;
+	case SNDCTL_SYNTH_MEMAVL:
+		return (gus_mem_size == 0) ? 0 : gus_mem_size - free_mem_ptr - 32;
 
-	  default:
-		  return -EINVAL;
-	  }
+	default:
+		return -EINVAL;
+	}
 }
 
 static int
@@ -2206,53 +2202,48 @@
 	return bits;
 }
 
-static int
-gus_audio_ioctl(int dev, unsigned int cmd, caddr_t arg)
+static int gus_audio_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
-	int             val;
-
-	switch (cmd)
-	  {
-	  case SOUND_PCM_WRITE_RATE:
-		  val = *(int *) arg;
-		  return (*(int *) arg = gus_audio_set_speed(val));
-		  break;
-
-	  case SOUND_PCM_READ_RATE:
-		  return (*(int *) arg = gus_audio_speed);
-		  break;
-
-	  case SNDCTL_DSP_STEREO:
-		  val = *(int *) arg;
-		  return (*(int *) arg = gus_audio_set_channels(val + 1) - 1);
-		  break;
-
-	  case SOUND_PCM_WRITE_CHANNELS:
-		  val = *(int *) arg;
-		  return (*(int *) arg = gus_audio_set_channels(val));
-		  break;
-
-	  case SOUND_PCM_READ_CHANNELS:
-		  return (*(int *) arg = gus_audio_channels);
-		  break;
-
-	  case SNDCTL_DSP_SETFMT:
-		  val = *(int *) arg;
-		  return (*(int *) arg = gus_audio_set_bits(val));
-		  break;
-
-	  case SOUND_PCM_READ_BITS:
-		  return (*(int *) arg = gus_audio_bits);
-
-	  case SOUND_PCM_WRITE_FILTER:		/* NOT POSSIBLE */
-		  return (*(int *) arg = -EINVAL);
-		  break;
-
-	  case SOUND_PCM_READ_FILTER:
-		  return (*(int *) arg = -EINVAL);
-		  break;
+	int val;
 
-	  }
+	switch (cmd) {
+	case SOUND_PCM_WRITE_RATE:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		val = gus_audio_set_speed(val);
+		return __put_user(val, (int *)arg);
+
+	case SOUND_PCM_READ_RATE:
+		return __put_user(gus_audio_speed, (int *)arg);
+
+	case SNDCTL_DSP_STEREO:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		val = gus_audio_set_channels(val + 1) - 1;
+		return __put_user(val, (int *)arg);
+
+	case SOUND_PCM_WRITE_CHANNELS:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		val = gus_audio_set_channels(val);
+		return __put_user(val, (int *)arg);
+
+	case SOUND_PCM_READ_CHANNELS:
+		return __put_user(gus_audio_channels, (int *)arg);
+		
+	case SNDCTL_DSP_SETFMT:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		val = gus_audio_set_bits(val);
+		return __put_user(val, (int *)arg);
+		
+	case SOUND_PCM_READ_BITS:
+		return __put_user(gus_audio_bits, (int *)arg);
+		
+	case SOUND_PCM_WRITE_FILTER:		/* NOT POSSIBLE */
+	case SOUND_PCM_READ_FILTER:
+		return __put_user(-EINVAL, (int *)arg);
+	}
 	return -EINVAL;
 }
 
@@ -2838,137 +2829,120 @@
 	restore_flags(flags);
 }
 
-int
-gus_default_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
-{
 #define MIX_DEVS	(SOUND_MASK_MIC|SOUND_MASK_LINE| \
 			 SOUND_MASK_SYNTH|SOUND_MASK_PCM)
-	if (((cmd >> 8) & 0xff) == 'M')
-	  {
-		  if (_SIOC_DIR(cmd) & _SIOC_WRITE)
-			  switch (cmd & 0xff)
-			    {
-			    case SOUND_MIXER_RECSRC:
-				    gus_recmask = *(int *) arg;
-				    gus_recmask &= MIX_DEVS;
-				    if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE)))
-					    gus_recmask = SOUND_MASK_MIC;
-				    /* Note! Input volumes are updated during next open for recording */
-				    return (*(int *) arg = gus_recmask);
-				    break;
-
-			    case SOUND_MIXER_MIC:
-				    {
-					    int             vol;
-
-					    vol = *(int *) arg;
-					    vol &= 0xff;
-
-					    if (vol < 0)
-						    vol = 0;
-					    if (vol > 100)
-						    vol = 100;
-					    gus_mic_vol = vol;
-					    set_input_volumes();
-					    return (*(int *) arg = vol | (vol << 8));
-				    }
-				    break;
-
-			    case SOUND_MIXER_LINE:
-				    {
-					    int             vol;
-
-					    vol = *(int *) arg;
-					    vol &= 0xff;
-
-					    if (vol < 0)
-						    vol = 0;
-					    if (vol > 100)
-						    vol = 100;
-					    gus_line_vol = vol;
-					    set_input_volumes();
-					    return (*(int *) arg = vol | (vol << 8));
-				    }
-				    break;
-
-			    case SOUND_MIXER_PCM:
-				    gus_pcm_volume = *(int *) arg;
-				    gus_pcm_volume &= 0xff;
-				    if (gus_pcm_volume < 0)
-					    gus_pcm_volume = 0;
-				    if (gus_pcm_volume > 100)
-					    gus_pcm_volume = 100;
-				    gus_audio_update_volume();
-				    return (*(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8));
-				    break;
-
-			    case SOUND_MIXER_SYNTH:
-				    {
-					    int             voice;
-
-					    gus_wave_volume = *(int *) arg;
-					    gus_wave_volume &= 0xff;
-
-					    if (gus_wave_volume < 0)
-						    gus_wave_volume = 0;
-					    if (gus_wave_volume > 100)
-						    gus_wave_volume = 100;
-
-					    if (active_device == GUS_DEV_WAVE)
-						    for (voice = 0; voice < nr_voices; voice++)
-							    dynamic_volume_change(voice);	/* Apply the new vol */
 
-					    return (*(int *) arg = gus_wave_volume | (gus_wave_volume << 8));
-				    }
-				    break;
-
-			    default:
-				    return -EINVAL;
-		  } else
-			  switch (cmd & 0xff)	/*
-						 * Return parameters
-						 */
-			    {
-
-			    case SOUND_MIXER_RECSRC:
-				    return (*(int *) arg = gus_recmask);
-				    break;
-
-			    case SOUND_MIXER_DEVMASK:
-				    return (*(int *) arg = MIX_DEVS);
-				    break;
-
-			    case SOUND_MIXER_STEREODEVS:
-				    return (*(int *) arg = 0);
-				    break;
-
-			    case SOUND_MIXER_RECMASK:
-				    return (*(int *) arg = SOUND_MASK_MIC | SOUND_MASK_LINE);
-				    break;
-
-			    case SOUND_MIXER_CAPS:
-				    return (*(int *) arg = 0);
-				    break;
-
-			    case SOUND_MIXER_MIC:
-				    return (*(int *) arg = gus_mic_vol | (gus_mic_vol << 8));
-				    break;
-
-			    case SOUND_MIXER_LINE:
-				    return (*(int *) arg = gus_line_vol | (gus_line_vol << 8));
-				    break;
+int gus_default_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	int vol, voice, val;
 
-			    case SOUND_MIXER_PCM:
+	if (((cmd >> 8) & 0xff) == 'M') {
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+			switch (cmd & 0xff) {
+			case SOUND_MIXER_RECSRC:
+				if (__get_user(gus_recmask, (int *)arg))
+					return -EFAULT;
+				gus_recmask &= MIX_DEVS;
+				if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE)))
+					gus_recmask = SOUND_MASK_MIC;
+				/* Note! Input volumes are updated during next open for recording */
+				return __put_user(gus_recmask, (int *)arg);
+
+			case SOUND_MIXER_MIC:
+				if (__get_user(vol, (int *)arg))
+					return -EFAULT;
+				vol &= 0xff;
+				if (vol < 0)
+					vol = 0;
+				if (vol > 100)
+					vol = 100;
+				gus_mic_vol = vol;
+				set_input_volumes();
+				vol |= (vol << 8);
+				return __put_user(vol, (int *)arg);
+				
+			case SOUND_MIXER_LINE:
+				if (__get_user(vol, (int *)arg))
+					return -EFAULT;
+				vol &= 0xff;
+				if (vol < 0)
+					vol = 0;
+				if (vol > 100)
+					vol = 100;
+				gus_line_vol = vol;
+				set_input_volumes();
+				vol |= (vol << 8);
+				return __put_user(vol, (int *)arg);
+
+			case SOUND_MIXER_PCM:
+				if (__get_user(gus_pcm_volume, (int *)arg))
+					return -EFAULT;
+				gus_pcm_volume &= 0xff;
+				if (gus_pcm_volume < 0)
+					gus_pcm_volume = 0;
+				if (gus_pcm_volume > 100)
+					gus_pcm_volume = 100;
+				gus_audio_update_volume();
+				gus_pcm_volume |= (gus_pcm_volume << 8);
+				return __put_user(gus_pcm_volume, (int *)arg);
+
+			case SOUND_MIXER_SYNTH:
+				if (__get_user(gus_wave_volume, (int *)arg))
+					return -EFAULT;
+				gus_wave_volume &= 0xff;
+				if (gus_wave_volume < 0)
+					gus_wave_volume = 0;
+				if (gus_wave_volume > 100)
+					gus_wave_volume = 100;
+				if (active_device == GUS_DEV_WAVE)
+					for (voice = 0; voice < nr_voices; voice++)
+						dynamic_volume_change(voice);	/* Apply the new vol */
+				gus_wave_volume |= (gus_wave_volume << 8);
+				return __put_user(gus_wave_volume, (int *)arg);
+
+			default:
+				return -EINVAL;
+			} else
+				switch (cmd & 0xff) {
+					/*
+					 * Return parameters
+					 */
+				case SOUND_MIXER_RECSRC:
+					return __put_user(gus_recmask, (int *)arg);
+					
+				case SOUND_MIXER_DEVMASK:
+					return __put_user(MIX_DEVS, (int *)arg);
+
+				case SOUND_MIXER_STEREODEVS:
+					return __put_user(0, (int *)arg);
+
+				case SOUND_MIXER_RECMASK:
+					val = SOUND_MASK_MIC | SOUND_MASK_LINE;
+					return __put_user(val, (int *)arg);
+
+				case SOUND_MIXER_CAPS:
+					return __put_user(0, (int *)arg);
+
+				case SOUND_MIXER_MIC:
+					val = gus_mic_vol | (gus_mic_vol << 8);
+					return __put_user(val, (int *)arg);
+
+				case SOUND_MIXER_LINE:
+					val = gus_line_vol | (gus_line_vol << 8);
+					return __put_user(val, (int *)arg);
+
+				case SOUND_MIXER_PCM:
+					val = ;
+					return __put_user(, (int *)arg);
 				    return (*(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8));
 				    break;
 
-			    case SOUND_MIXER_SYNTH:
-				    return (*(int *) arg = gus_wave_volume | (gus_wave_volume << 8));
-				    break;
+				case SOUND_MIXER_SYNTH:
+					return __put_user(gus_wave_volume | (gus_wave_volume << 8), (int *)arg);
 
-			    default:
-				    return -EINVAL;
-			    }
+				default:
+					return -EINVAL;
+				}
 	} else
 		return -EINVAL;
 }
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/ics2101.c linux/drivers/sound/ics2101.c
--- v2.1.76/linux/drivers/sound/ics2101.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/ics2101.c	Tue Dec 30 11:02:39 1997
@@ -10,6 +10,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 
 
@@ -115,79 +118,88 @@
 
 static int ics2101_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
-	if (((cmd >> 8) & 0xff) == 'M')
-	{
-		if (_SIOC_DIR(cmd) & _SIOC_WRITE)
-		{
+	if (((cmd >> 8) & 0xff) == 'M') {
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
 			int val;
+			
+			if (__get_user(val, (int *)arg))
+				return -EFAULT;
+			switch (cmd & 0xff) {
+			case SOUND_MIXER_RECSRC:
+				return gus_default_mixer_ioctl(dev, cmd, arg);
+
+			case SOUND_MIXER_MIC:
+				val = set_volumes(DEV_MIC, val);
+				break;
+				
+			case SOUND_MIXER_CD:
+				val = set_volumes(DEV_CD, val);
+				break;
+
+			case SOUND_MIXER_LINE:
+				val = set_volumes(DEV_LINE, val);
+				break;
+
+			case SOUND_MIXER_SYNTH:
+				val = set_volumes(DEV_GF1, val);
+				break;
+
+			case SOUND_MIXER_VOLUME:
+				val = set_volumes(DEV_VOL, val);
+				break;
 
-			val = *(int *) arg;
-
-			switch (cmd & 0xff)
-			{
-				case SOUND_MIXER_RECSRC:
-					return gus_default_mixer_ioctl(dev, cmd, arg);
-
-				case SOUND_MIXER_MIC:
-					return (*(int *) arg = set_volumes(DEV_MIC, val));
-
-				case SOUND_MIXER_CD:
-					return (*(int *) arg = set_volumes(DEV_CD, val));
-
-				case SOUND_MIXER_LINE:
-					return (*(int *) arg = set_volumes(DEV_LINE, val));
-
-				case SOUND_MIXER_SYNTH:
-					return (*(int *) arg = set_volumes(DEV_GF1, val));
-
-				case SOUND_MIXER_VOLUME:
-					return (*(int *) arg = set_volumes(DEV_VOL, val));
-
-				default:
-					return -EINVAL;
+			default:
+				return -EINVAL;
 			}
-		}
-		else
-		{
-			switch (cmd & 0xff)	/*
-						 * Return parameters
-						 */
-			{
-
-				case SOUND_MIXER_RECSRC:
-					return gus_default_mixer_ioctl(dev, cmd, arg);
-
-				case SOUND_MIXER_DEVMASK:
-					return (*(int *) arg = MIX_DEVS);
-
-				case SOUND_MIXER_STEREODEVS:
-					return (*(int *) arg = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC);
-
-				case SOUND_MIXER_RECMASK:
-					return (*(int *) arg = SOUND_MASK_MIC | SOUND_MASK_LINE);
-
-				case SOUND_MIXER_CAPS:
-				    return (*(int *) arg = 0);
-				    break;
-
-				case SOUND_MIXER_MIC:
-					return (*(int *) arg = volumes[DEV_MIC]);
-
-				case SOUND_MIXER_LINE:
-					return (*(int *) arg = volumes[DEV_LINE]);
-
-				case SOUND_MIXER_CD:
-					return (*(int *) arg = volumes[DEV_CD]);
-
-				case SOUND_MIXER_VOLUME:
-					return (*(int *) arg = volumes[DEV_VOL]);
-
-				case SOUND_MIXER_SYNTH:
-					return (*(int *) arg = volumes[DEV_GF1]);
+			return __put_user(val, (int *)arg);
+		} else {
+			switch (cmd & 0xff) {
+				/*
+				 * Return parameters
+				 */
+			case SOUND_MIXER_RECSRC:
+				return gus_default_mixer_ioctl(dev, cmd, arg);
+
+			case SOUND_MIXER_DEVMASK:
+				val = MIX_DEVS; 
+				break;
+
+			case SOUND_MIXER_STEREODEVS:
+				val = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC; 
+				break;
+
+			case SOUND_MIXER_RECMASK:
+				val = SOUND_MASK_MIC | SOUND_MASK_LINE; 
+				break;
+				
+			case SOUND_MIXER_CAPS:
+				val = 0; 
+				break;
+
+			case SOUND_MIXER_MIC:
+				val = volumes[DEV_MIC];
+				break;
+				
+			case SOUND_MIXER_LINE:
+				val = volumes[DEV_LINE];
+				break;
+
+			case SOUND_MIXER_CD:
+				val = volumes[DEV_CD];
+				break;
+
+			case SOUND_MIXER_VOLUME:
+				val = volumes[DEV_VOL];
+				break;
+
+			case SOUND_MIXER_SYNTH:
+				val = volumes[DEV_GF1]; 
+				break;
 
-				default:
-					return -EINVAL;
+			default:
+				return -EINVAL;
 			}
+			return __put_user(val, (int *)arg);
 		}
 	}
 	return -EINVAL;
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/midi_synth.c linux/drivers/sound/midi_synth.c
--- v2.1.76/linux/drivers/sound/midi_synth.c	Wed Dec 10 11:12:44 1997
+++ linux/drivers/sound/midi_synth.c	Tue Dec 30 10:59:17 1997
@@ -10,6 +10,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 
 #define USE_SEQ_MACROS
@@ -258,30 +261,23 @@
 	 */
 }
 
-int
-midi_synth_ioctl(int dev,
-		 unsigned int cmd, caddr_t arg)
+int midi_synth_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
 	/*
 	 * int orig_dev = synth_devs[dev]->midi_dev;
 	 */
 
-	switch (cmd)
-	  {
-
-	  case SNDCTL_SYNTH_INFO:
-		  memcpy((&((char *) arg)[0]), (char *) synth_devs[dev]->info, sizeof(struct synth_info));
-
-		  return 0;
-		  break;
+	switch (cmd) {
 
-	  case SNDCTL_SYNTH_MEMAVL:
-		  return 0x7fffffff;
-		  break;
-
-	  default:
-		  return -EINVAL;
-	  }
+	case SNDCTL_SYNTH_INFO:
+		return __copy_to_user(arg, synth_devs[dev]->info, sizeof(struct synth_info));
+		
+	case SNDCTL_SYNTH_MEMAVL:
+		return 0x7fffffff;
+
+	default:
+		return -EINVAL;
+	}
 }
 
 int
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/midibuf.c linux/drivers/sound/midibuf.c
--- v2.1.76/linux/drivers/sound/midibuf.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/sound/midibuf.c	Tue Dec 30 11:02:39 1997
@@ -10,6 +10,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 
 #define MIDIBUF_C
@@ -451,39 +454,33 @@
 	return c;
 }
 
-int
-MIDIbuf_ioctl(int dev, struct fileinfo *file,
-	      unsigned int cmd, caddr_t arg)
+int MIDIbuf_ioctl(int dev, struct fileinfo *file,
+		  unsigned int cmd, caddr_t arg)
 {
-	int             val;
+	int val;
 
 	dev = dev >> 4;
-
-	if (((cmd >> 8) & 0xff) == 'C')
-	  {
+	if (((cmd >> 8) & 0xff) == 'C') {
 		  if (midi_devs[dev]->coproc)	/* Coprocessor ioctl */
 			  return midi_devs[dev]->coproc->ioctl(midi_devs[dev]->coproc->devc, cmd, arg, 0);
-		  else
-			  printk("/dev/midi%d: No coprocessor for this device\n", dev);
-
+		  printk("/dev/midi%d: No coprocessor for this device\n", dev);
 		  return -ENXIO;
 	} else
-		switch (cmd)
-		  {
-
-		  case SNDCTL_MIDI_PRETIME:
-			  val = *(int *) arg;
-			  if (val < 0)
-				  val = 0;
-
-			  val = (HZ * val) / 10;
-			  parms[dev].prech_timeout = val;
-			  return (*(int *) arg = val);
-			  break;
-
-		  default:
-			  return midi_devs[dev]->ioctl(dev, cmd, arg);
-		  }
+		switch (cmd) {
+		case SNDCTL_MIDI_PRETIME:
+			if (__get_user(val, (int *)arg))
+				return -EFAULT;
+			if (val < 0)
+				val = 0;
+			val = (HZ * val) / 10;
+			parms[dev].prech_timeout = val;
+			return __put_user(val, (int *)arg);
+			
+		default:
+			if (!midi_devs[dev]->ioctl)
+				return -EINVAL;
+			return midi_devs[dev]->ioctl(dev, cmd, arg);
+		}
 }
 
 int
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/mpu401.c linux/drivers/sound/mpu401.c
--- v2.1.76/linux/drivers/sound/mpu401.c	Wed Dec 10 11:12:44 1997
+++ linux/drivers/sound/mpu401.c	Tue Dec 30 10:59:17 1997
@@ -10,6 +10,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 #include <linux/module.h>
 
@@ -746,45 +749,34 @@
 	return 0;
 }
 
-static int
-mpu401_ioctl(int dev, unsigned cmd, caddr_t arg)
+static int mpu401_ioctl(int dev, unsigned cmd, caddr_t arg)
 {
 	struct mpu_config *devc;
-	int             val;
+	mpu_command_rec rec;
+	int val, ret;
 
 	devc = &dev_conf[dev];
-
-	switch (cmd)
-	  {
-	  case SNDCTL_MIDI_MPUMODE:
-		  if (!(devc->capabilities & MPU_CAP_INTLG))	/* No intelligent mode */
-		    {
-			    printk("MPU-401: Intelligent mode not supported by the HW\n");
-			    return -EINVAL;
-		    }
-		  val = *(int *) arg;
-		  set_uart_mode(dev, devc, !val);
-		  return 0;
-		  break;
-
-	  case SNDCTL_MIDI_MPUCMD:
-		  {
-			  int             ret;
-			  mpu_command_rec rec;
-
-			  memcpy((char *) &rec, (&((char *) arg)[0]), sizeof(rec));
-
-			  if ((ret = mpu401_command(dev, &rec)) < 0)
-				  return ret;
-
-			  memcpy((&((char *) arg)[0]), (char *) &rec, sizeof(rec));
-			  return 0;
-		  }
-		  break;
-
-	  default:
-		  return -EINVAL;
-	  }
+	switch (cmd) {
+	case SNDCTL_MIDI_MPUMODE:
+		if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */
+			printk("MPU-401: Intelligent mode not supported by the HW\n");
+			return -EINVAL;
+		}
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		set_uart_mode(dev, devc, !val);
+		return 0;
+
+	case SNDCTL_MIDI_MPUCMD:
+		if (__copy_from_user(&rec, arg, sizeof(rec)))
+			return -EFAULT;
+		if ((ret = mpu401_command(dev, &rec)) < 0)
+			return ret;
+		return __copy_to_user(arg, &rec, sizeof(rec));
+
+	default:
+		return -EINVAL;
+	}
 }
 
 static void
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/opl3.c linux/drivers/sound/opl3.c
--- v2.1.76/linux/drivers/sound/opl3.c	Tue Dec 23 16:31:00 1997
+++ linux/drivers/sound/opl3.c	Tue Dec 30 10:59:17 1997
@@ -10,6 +10,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 #include <linux/module.h>
 
@@ -102,40 +105,33 @@
 
 static int opl3_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
-	switch (cmd)
-	{
-
-		case SNDCTL_FM_LOAD_INSTR:
-		{
-			struct sbi_instrument ins;
-
-			printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
-			memcpy((char *) &ins, (&((char *) arg)[0]), sizeof(ins));
-
-			if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
-			{
-				printk("FM Error: Invalid instrument number %d\n", ins.channel);
-				return -EINVAL;
-			}
-			return store_instr(ins.channel, &ins);
+	struct sbi_instrument ins;
+	
+	switch (cmd) {
+	case SNDCTL_FM_LOAD_INSTR:
+		printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
+		if (__copy_from_user(&ins, arg, sizeof(ins)))
+			return -EFAULT;
+		if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) {
+			printk("FM Error: Invalid instrument number %d\n", ins.channel);
+			return -EINVAL;
 		}
+		return store_instr(ins.channel, &ins);
 
-		case SNDCTL_SYNTH_INFO:
-			devc->fm_info.nr_voices = 
-				(devc->nr_voice == 12) ? 6 : devc->nr_voice;
-			memcpy((&((char *) arg)[0]), (char *) &devc->fm_info, sizeof(devc->fm_info));
-			return 0;
-
-		case SNDCTL_SYNTH_MEMAVL:
-			return 0x7fffffff;
-
-		case SNDCTL_FM_4OP_ENABLE:
-			if (devc->model == 2)
-				enter_4op_mode();
-			return 0;
+	case SNDCTL_SYNTH_INFO:
+		devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice;
+		return __copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info));
+
+	case SNDCTL_SYNTH_MEMAVL:
+		return 0x7fffffff;
+
+	case SNDCTL_FM_4OP_ENABLE:
+		if (devc->model == 2)
+			enter_4op_mode();
+		return 0;
 
-		default:
-			return -EINVAL;
+	default:
+		return -EINVAL;
 	}
 }
 
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/pas2_midi.c linux/drivers/sound/pas2_midi.c
--- v2.1.76/linux/drivers/sound/pas2_midi.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/pas2_midi.c	Tue Dec 30 10:59:17 1997
@@ -185,12 +185,6 @@
 	return 0;
 }
 
-static int
-pas_midi_ioctl(int dev, unsigned cmd, caddr_t arg)
-{
-	return -EINVAL;
-}
-
 static void
 pas_midi_kick(int dev)
 {
@@ -213,7 +207,7 @@
 	{0},
 	pas_midi_open,
 	pas_midi_close,
-	pas_midi_ioctl,
+	NULL,
 	pas_midi_out,
 	pas_midi_start_read,
 	pas_midi_end_read,
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/pas2_mixer.c linux/drivers/sound/pas2_mixer.c
--- v2.1.76/linux/drivers/sound/pas2_mixer.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/pas2_mixer.c	Tue Dec 30 11:02:39 1997
@@ -12,6 +12,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 
 #include "sound_config.h"
@@ -211,122 +214,97 @@
 	set_mode(0x04 | 0x01);
 }
 
-static int
-pas_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+static int pas_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
-	DEB(printk("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
-
-	if (cmd == SOUND_MIXER_PRIVATE1)	/* Set loudness bit */
-	  {
-		  int             level;
-
-		  level = *(int *) arg;
-
-		  if (level == -1)	/* Return current settings */
-		    {
-			    if (mode_control & 0x04)
-				    return (*(int *) arg = 1);
-			    else
-				    return (*(int *) arg = 0);
-		  } else
-		    {
-			    mode_control &= ~0x04;
-			    if (level)
-				    mode_control |= 0x04;
-			    set_mode(mode_control);
-			    return (*(int *) arg = !!level);	/* 0 or 1 */
-		    }
-	  }
-	if (cmd == SOUND_MIXER_PRIVATE2)	/* Set enhance bit */
-	  {
-		  int             level;
-
-		  level = *(int *) arg;
-
-		  if (level == -1)	/* Return current settings */
-		    {
-			    if (!(mode_control & 0x03))
-				    return (*(int *) arg = 0);
-			    return (*(int *) arg = ((mode_control & 0x03) + 1) * 20);
-		  } else
-		    {
-			    int             i = 0;
-
-			    level &= 0x7f;
-			    if (level)
-				    i = (level / 20) - 1;
-
-			    mode_control &= ~0x03;
-			    mode_control |= i & 0x03;
-			    set_mode(mode_control);
-
-			    if (i)
-				    i = (i + 1) * 20;
-
-			    return i;
-		    }
-	  }
-	if (cmd == SOUND_MIXER_PRIVATE3)	/* Set mute bit */
-	  {
-		  int             level;
-
-		  level = *(int *) arg;
+	int level, v;
 
-		  if (level == -1)	/* Return current settings */
-		    {
-			    return (*(int *) arg = !(pas_read(0x0B8A) & 0x20));
-		  } else
-		    {
-			    if (level)
-				    pas_write(pas_read(0x0B8A) & (~0x20),
-					      0x0B8A);
-			    else
-				    pas_write(pas_read(0x0B8A) | 0x20,
-					      0x0B8A);
-
-			    return !(pas_read(0x0B8A) & 0x20);
-		    }
-	  }
-	if (((cmd >> 8) & 0xff) == 'M')
-	  {
-		  int             v;
-
-		  v = *(int *) arg;
-
-		  if (_SIOC_DIR(cmd) & _SIOC_WRITE)
-			  return (*(int *) arg = pas_mixer_set(cmd & 0xff, v));
-		  else
-		    {
-
-			    switch (cmd & 0xff)
-			      {
-
-			      case SOUND_MIXER_RECSRC:
-				      return (*(int *) arg = rec_devices);
-				      break;
-
-			      case SOUND_MIXER_STEREODEVS:
-				      return (*(int *) arg = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE));
-				      break;
-
-			      case SOUND_MIXER_DEVMASK:
-				      return (*(int *) arg = SUPPORTED_MIXER_DEVICES);
-				      break;
-
-			      case SOUND_MIXER_RECMASK:
-				      return (*(int *) arg = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES);
-				      break;
-
-			      case SOUND_MIXER_CAPS:
-				      return (*(int *) arg = 0);	/* No special capabilities */
-				      break;
-
-
-			      default:
-				      return (*(int *) arg = levels[cmd & 0xff]);
-			      }
-		    }
-	  }
+	DEB(printk("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+	if (cmd == SOUND_MIXER_PRIVATE1) { /* Set loudness bit */
+		if (__get_user(level, (int *)arg))
+			return -EFAULT;
+		if (level == -1)  /* Return current settings */
+			level = (mode_control & 0x04);
+		else {
+			mode_control &= ~0x04;
+			if (level)
+				mode_control |= 0x04;
+			set_mode(mode_control);
+		}
+		level = !!level;
+		return __put_user(level, (int *)arg);
+	}
+	if (cmd == SOUND_MIXER_PRIVATE2) { /* Set enhance bit */
+		if (__get_user(level, (int *)arg))
+			return -EFAULT;
+		if (level == -1) { /* Return current settings */
+			if (!(mode_control & 0x03))
+				level = 0;
+			else
+				level = ((mode_control & 0x03) + 1) * 20;
+		} else {
+			int i = 0;
+			
+			level &= 0x7f;
+			if (level)
+				i = (level / 20) - 1;
+			mode_control &= ~0x03;
+			mode_control |= i & 0x03;
+			set_mode(mode_control);
+			if (i)
+				i = (i + 1) * 20;
+			level = i;
+		}
+		return __put_user(level, (int *)arg);
+	}
+	if (cmd == SOUND_MIXER_PRIVATE3) { /* Set mute bit */
+		if (__get_user(level, (int *)arg))
+			return -EFAULT;
+		if (level == -1)	/* Return current settings */
+			level = = !(pas_read(0x0B8A) & 0x20);
+		else {
+			if (level)
+				pas_write(pas_read(0x0B8A) & (~0x20), 0x0B8A);
+			else
+				pas_write(pas_read(0x0B8A) | 0x20, 0x0B8A);
+
+			level = !(pas_read(0x0B8A) & 0x20);
+		}
+		return __put_user(level, (int *)arg);
+	}
+	if (((cmd >> 8) & 0xff) == 'M') {
+		if (__get_user(v, (int *)arg))
+			return -EFAULT;
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+			v = pas_mixer_set(cmd & 0xff, v);
+		} else {
+			switch (cmd & 0xff) {
+			case SOUND_MIXER_RECSRC:
+				v = rec_devices;
+				break;
+				
+			case SOUND_MIXER_STEREODEVS:
+				v = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE);
+				break;
+				
+			case SOUND_MIXER_DEVMASK:
+				v = SUPPORTED_MIXER_DEVICES;
+				break;
+				
+			case SOUND_MIXER_RECMASK:
+				v = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES;
+				break;
+				
+			case SOUND_MIXER_CAPS:
+				v = 0;	/* No special capabilities */
+				break;
+				
+			default:
+				v = levels[cmd & 0xff];
+				break;
+			}
+		}
+		return __put_user(v, (int *)arg);
+	}
 	return -EINVAL;
 }
 
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/pas2_pcm.c linux/drivers/sound/pas2_pcm.c
--- v2.1.76/linux/drivers/sound/pas2_pcm.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/pas2_pcm.c	Tue Dec 30 11:02:39 1997
@@ -8,6 +8,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 
 #include "sound_config.h"
@@ -144,51 +147,53 @@
 	return pcm_bits;
 }
 
-static int
-pas_audio_ioctl(int dev, unsigned int cmd, caddr_t arg)
+static int pas_audio_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
-	int             val;
+	int val, ret;
 
 	DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
 
-	switch (cmd)
-	  {
-	  case SOUND_PCM_WRITE_RATE:
-		  val = *(int *) arg;
-		  return (*(int *) arg = pcm_set_speed(val));
-		  break;
-
-	  case SOUND_PCM_READ_RATE:
-		  return (*(int *) arg = pcm_speed);
-		  break;
-
-	  case SNDCTL_DSP_STEREO:
-		  val = *(int *) arg;
-		  return (*(int *) arg = pcm_set_channels(val + 1) - 1);
-		  break;
-
-	  case SOUND_PCM_WRITE_CHANNELS:
-		  val = *(int *) arg;
-		  return (*(int *) arg = pcm_set_channels(val));
-		  break;
-
-	  case SOUND_PCM_READ_CHANNELS:
-		  return (*(int *) arg = pcm_channels);
-		  break;
-
-	  case SNDCTL_DSP_SETFMT:
-		  val = *(int *) arg;
-		  return (*(int *) arg = pcm_set_bits(val));
-		  break;
-
-	  case SOUND_PCM_READ_BITS:
-		  return (*(int *) arg = pcm_bits);
-
-	  default:
-		  return -EINVAL;
-	  }
-
-	return -EINVAL;
+	switch (cmd) {
+	case SOUND_PCM_WRITE_RATE:
+		if (__get_user(val, (int *)arg)) 
+			return -EFAULT;
+		ret = pcm_set_speed(val);
+		break;
+
+	case SOUND_PCM_READ_RATE:
+		ret = pcm_speed;
+		break;
+		
+	case SNDCTL_DSP_STEREO:
+		if (__get_user(val, (int *)arg)) 
+			return -EFAULT;
+		ret = pcm_set_channels(val + 1) - 1;
+		break;
+
+	case SOUND_PCM_WRITE_CHANNELS:
+		if (__get_user(val, (int *)arg)) 
+			return -EFAULT;
+		ret = pcm_set_channels(val);
+		break;
+
+	case SOUND_PCM_READ_CHANNELS:
+		ret = pcm_channels;
+		break;
+
+	case SNDCTL_DSP_SETFMT:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		ret = pcm_set_bits(val);
+		break;
+		
+	case SOUND_PCM_READ_BITS:
+		ret = pcm_bits;
+		break;
+  
+	default:
+		return -EINVAL;
+	}
+	return __put_user(ret, (int *)arg);
 }
 
 static void
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/pss.c linux/drivers/sound/pss.c
--- v2.1.76/linux/drivers/sound/pss.c	Tue Dec 23 16:31:00 1997
+++ linux/drivers/sound/pss.c	Tue Dec 30 10:59:17 1997
@@ -10,6 +10,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 #include <linux/module.h>
 
@@ -498,257 +501,177 @@
 	return 0;
 }
 
-static int
-pss_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local)
+static int pss_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local)
 {
+	copr_buffer *buf;
+	copr_msg *mbuf;
+	copr_debug_buf dbuf;
+	unsigned short tmp;
+	unsigned long flags;
+	unsigned short *data;
+	int i, err;
 	/* printk( "PSS coproc ioctl %x %x %d\n",  cmd,  arg,  local); */
+	
+	switch (cmd) {
+	case SNDCTL_COPR_RESET:
+		pss_coproc_reset(dev_info);
+		return 0;
 
-	switch (cmd)
-	  {
-	  case SNDCTL_COPR_RESET:
-		  pss_coproc_reset(dev_info);
-		  return 0;
-		  break;
-
-	  case SNDCTL_COPR_LOAD:
-		  {
-			  copr_buffer    *buf;
-			  int             err;
-
-			  buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
-			  if (buf == NULL)
-				  return -ENOSPC;
-
-			  memcpy((char *) buf, (&((char *) arg)[0]), sizeof(*buf));
-			  err = download_boot_block(dev_info, buf);
-			  vfree(buf);
-			  return err;
-		  }
-		  break;
-
-	  case SNDCTL_COPR_SENDMSG:
-		  {
-			  copr_msg       *buf;
-			  unsigned long   flags;
-			  unsigned short *data;
-			  int             i;
-
-			  buf = (copr_msg *) vmalloc(sizeof(copr_msg));
-			  if (buf == NULL)
-				  return -ENOSPC;
-
-			  memcpy((char *) buf, (&((char *) arg)[0]), sizeof(*buf));
-
-			  data = (unsigned short *) (buf->data);
-
-			  save_flags(flags);
-			  cli();
-
-			  for (i = 0; i < buf->len; i++)
-			    {
-				    if (!pss_put_dspword(devc, *data++))
-				      {
-					      restore_flags(flags);
-					      buf->len = i;	/* feed back number of WORDs sent */
-					      memcpy((&((char *) arg)[0]), (char *) buf, sizeof(*buf));
-					      vfree(buf);
-					      return -EIO;
-				      }
-			    }
-
-			  restore_flags(flags);
-			  vfree(buf);
-
-			  return 0;
-		  }
-		  break;
-
-
-	  case SNDCTL_COPR_RCVMSG:
-		  {
-			  copr_msg       *buf;
-			  unsigned long   flags;
-			  unsigned short *data;
-			  unsigned int    i;
-			  int             err = 0;
-
-			  buf = (copr_msg *) vmalloc(sizeof(copr_msg));
-			  if (buf == NULL)
-				  return -ENOSPC;
-
-
-			  data = (unsigned short *) buf->data;
-
-			  save_flags(flags);
-			  cli();
-
-			  for (i = 0; i < buf->len; i++)
-			    {
-				    buf->len = i;	/* feed back number of WORDs read */
-				    if (!pss_get_dspword(devc, data++))
-				      {
-					      if (i == 0)
-						      err = -EIO;
-					      break;
-				      }
-			    }
-
-			  restore_flags(flags);
-
-			  memcpy((&((char *) arg)[0]), (char *) buf, sizeof(*buf));
-			  vfree(buf);
-
-			  return err;
-		  }
-		  break;
-
-
-	  case SNDCTL_COPR_RDATA:
-		  {
-			  copr_debug_buf  buf;
-			  unsigned long   flags;
-			  unsigned short  tmp;
-
-			  memcpy((char *) &buf, (&((char *) arg)[0]), sizeof(buf));
-
-			  save_flags(flags);
-			  cli();
-			  if (!pss_put_dspword(devc, 0x00d0))
-			    {
-				    restore_flags(flags);
-				    return -EIO;
-			    }
-			  if (!pss_put_dspword(devc, (unsigned short) (buf.parm1 & 0xffff)))
-			    {
-				    restore_flags(flags);
-				    return -EIO;
-			    }
-			  if (!pss_get_dspword(devc, &tmp))
-			    {
-				    restore_flags(flags);
-				    return -EIO;
-			    }
-			  buf.parm1 = tmp;
-			  restore_flags(flags);
-
-			  memcpy((&((char *) arg)[0]), (char *) &buf, sizeof(buf));
-			  return 0;
-		  }
-		  break;
-
-	  case SNDCTL_COPR_WDATA:
-		  {
-			  copr_debug_buf  buf;
-			  unsigned long   flags;
-			  unsigned short  tmp;
-
-			  memcpy((char *) &buf, (&((char *) arg)[0]), sizeof(buf));
-
-			  save_flags(flags);
-			  cli();
-			  if (!pss_put_dspword(devc, 0x00d1))
-			    {
-				    restore_flags(flags);
-				    return -EIO;
-			    }
-			  if (!pss_put_dspword(devc, (unsigned short) (buf.parm1 & 0xffff)))
-			    {
-				    restore_flags(flags);
-				    return -EIO;
-			    }
-			  tmp = (unsigned int) buf.parm2 & 0xffff;
-			  if (!pss_put_dspword(devc, tmp))
-			    {
-				    restore_flags(flags);
-				    return -EIO;
-			    }
-			  restore_flags(flags);
-			  return 0;
-		  }
-		  break;
-
-	  case SNDCTL_COPR_WCODE:
-		  {
-			  copr_debug_buf  buf;
-			  unsigned long   flags;
-			  unsigned short  tmp;
-
-			  memcpy((char *) &buf, (&((char *) arg)[0]), sizeof(buf));
-
-			  save_flags(flags);
-			  cli();
-			  if (!pss_put_dspword(devc, 0x00d3))
-			    {
-				    restore_flags(flags);
-				    return -EIO;
-			    }
-			  if (!pss_put_dspword(devc, (unsigned short) (buf.parm1 & 0xffff)))
-			    {
-				    restore_flags(flags);
-				    return -EIO;
-			    }
-			  tmp = (unsigned int) buf.parm2 & 0x00ff;
-			  if (!pss_put_dspword(devc, tmp))
-			    {
-				    restore_flags(flags);
-				    return -EIO;
-			    }
-			  tmp = ((unsigned int) buf.parm2 >> 8) & 0xffff;
-			  if (!pss_put_dspword(devc, tmp))
-			    {
-				    restore_flags(flags);
-				    return -EIO;
-			    }
-			  restore_flags(flags);
-			  return 0;
-		  }
-		  break;
-
-	  case SNDCTL_COPR_RCODE:
-		  {
-			  copr_debug_buf  buf;
-			  unsigned long   flags;
-			  unsigned short  tmp;
-
-			  memcpy((char *) &buf, (&((char *) arg)[0]), sizeof(buf));
-
-			  save_flags(flags);
-			  cli();
-			  if (!pss_put_dspword(devc, 0x00d2))
-			    {
-				    restore_flags(flags);
-				    return -EIO;
-			    }
-			  if (!pss_put_dspword(devc, (unsigned short) (buf.parm1 & 0xffff)))
-			    {
-				    restore_flags(flags);
-				    return -EIO;
-			    }
-			  if (!pss_get_dspword(devc, &tmp))	/* Read MSB */
-			    {
-				    restore_flags(flags);
-				    return -EIO;
-			    }
-			  buf.parm1 = tmp << 8;
-
-			  if (!pss_get_dspword(devc, &tmp))	/* Read LSB */
-			    {
-				    restore_flags(flags);
-				    return -EIO;
-			    }
-			  buf.parm1 |= tmp & 0x00ff;
-
-			  restore_flags(flags);
-
-			  memcpy((&((char *) arg)[0]), (char *) &buf, sizeof(buf));
-			  return 0;
-		  }
-		  break;
+	case SNDCTL_COPR_LOAD:
+		buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
+		if (buf == NULL)
+			return -ENOSPC;
+		if (__copy_from_user(buf, arg, sizeof(copr_buffer))) {
+			vfree(buf);
+			return -EFAULT;
+		}
+		err = download_boot_block(dev_info, buf);
+		vfree(buf);
+		return err;
+		
+	case SNDCTL_COPR_SENDMSG:
+		mbuf = (copr_msg *)vmalloc(sizeof(copr_msg));
+		if (mbuf == NULL)
+			return -ENOSPC;
+		if (__copy_from_user(mbuf, arg, sizeof(copr_msg))) {
+			vfree(mbuf);
+			return -EFAULT;
+		}
+		data = (unsigned short *)(mbuf->data);
+		save_flags(flags);
+		cli();
+		for (i = 0; i < mbuf->len; i++) {
+			if (!pss_put_dspword(devc, *data++)) {
+				restore_flags(flags);
+				mbuf->len = i;	/* feed back number of WORDs sent */
+				err = __copy_to_user(arg, mbuf, sizeof(copr_msg));
+				vfree(mbuf);
+				return err ? err : -EIO;
+			}
+		}
+		restore_flags(flags);
+		vfree(mbuf);
+		return 0;
 
-	  default:
-		  return -EINVAL;
-	  }
+	case SNDCTL_COPR_RCVMSG:
+		err = 0;
+		mbuf = (copr_msg *)vmalloc(sizeof(copr_msg));
+		if (mbuf == NULL)
+			return -ENOSPC;
+		data = (unsigned short *)mbuf->data;
+		save_flags(flags);
+		cli();
+		for (i = 0; i < mbuf->len; i++) {
+			mbuf->len = i;	/* feed back number of WORDs read */
+			if (!pss_get_dspword(devc, data++)) {
+				if (i == 0)
+					err = -EIO;
+				break;
+			}
+		}
+		restore_flags(flags);
+		if (__copy_to_user(arg, mbuf, sizeof(copr_msg)))
+			err = -EFAULT;
+		vfree(mbuf);
+		return err;
+		
+	case SNDCTL_COPR_RDATA:
+		if (__copy_from_user(&dbuf, arg, sizeof(dbuf)))
+			return -EFAULT;
+		save_flags(flags);
+		cli();
+		if (!pss_put_dspword(devc, 0x00d0)) {
+			restore_flags(flags);
+			return -EIO;
+		}
+		if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
+			restore_flags(flags);
+			return -EIO;
+		}
+		if (!pss_get_dspword(devc, &tmp)) {
+			restore_flags(flags);
+			return -EIO;
+		}
+		dbuf.parm1 = tmp;
+		restore_flags(flags);
+		return __copy_to_user(arg, &dbuf, sizeof(dbuf));
+		
+	case SNDCTL_COPR_WDATA:
+		if (__copy_from_user(&dbuf, arg, sizeof(dbuf)))
+			return -EFAULT;
+		save_flags(flags);
+		cli();
+		if (!pss_put_dspword(devc, 0x00d1)) {
+			restore_flags(flags);
+			return -EIO;
+		}
+		if (!pss_put_dspword(devc, (unsigned short) (dbuf.parm1 & 0xffff))) {
+			restore_flags(flags);
+			return -EIO;
+		}
+		tmp = (unsigned int)dbuf.parm2 & 0xffff;
+		if (!pss_put_dspword(devc, tmp)) {
+			restore_flags(flags);
+			return -EIO;
+		}
+		restore_flags(flags);
+		return 0;
+		
+	case SNDCTL_COPR_WCODE:
+		if (__copy_from_user(&dbuf, arg, sizeof(dbuf)))
+			return -EFAULT;
+		save_flags(flags);
+		cli();
+		if (!pss_put_dspword(devc, 0x00d3)) {
+			restore_flags(flags);
+			return -EIO;
+		}
+		if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
+			restore_flags(flags);
+			return -EIO;
+		}
+		tmp = (unsigned int)dbuf.parm2 & 0x00ff;
+		if (!pss_put_dspword(devc, tmp)) {
+			restore_flags(flags);
+			return -EIO;
+		}
+		tmp = ((unsigned int)dbuf.parm2 >> 8) & 0xffff;
+		if (!pss_put_dspword(devc, tmp)) {
+			restore_flags(flags);
+			return -EIO;
+		}
+		restore_flags(flags);
+		return 0;
+		
+	case SNDCTL_COPR_RCODE:
+		if (__copy_from_user(&dbuf, arg, sizeof(dbuf)))
+			return -EFAULT;
+		save_flags(flags);
+		cli();
+		if (!pss_put_dspword(devc, 0x00d2)) {
+			restore_flags(flags);
+			return -EIO;
+		}
+		if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
+			restore_flags(flags);
+			return -EIO;
+		}
+		if (!pss_get_dspword(devc, &tmp)) { /* Read MSB */
+			restore_flags(flags);
+			return -EIO;
+		}
+		dbuf.parm1 = tmp << 8;
+		if (!pss_get_dspword(devc, &tmp)) { /* Read LSB */
+			restore_flags(flags);
+			return -EIO;
+		}
+		dbuf.parm1 |= tmp & 0x00ff;
+		restore_flags(flags);
+		return __copy_to_user(arg, &dbuf, sizeof(dbuf));
 
+	default:
+		return -EINVAL;
+	}
 	return -EINVAL;
 }
 
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/sb_audio.c linux/drivers/sound/sb_audio.c
--- v2.1.76/linux/drivers/sound/sb_audio.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/sound/sb_audio.c	Tue Dec 30 10:59:17 1997
@@ -956,18 +956,13 @@
 	devc->trigger_bits = bits;
 }
 
-static int sb_audio_ioctl(int dev, unsigned int cmd, caddr_t arg)
-{
-	return -EINVAL;
-}
-
 static struct audio_driver sb1_audio_driver =	/* SB1.x */
 {
 	sb_audio_open,
 	sb_audio_close,
 	sb_set_output_parms,
 	sb_set_input_parms,
-	sb_audio_ioctl,
+	NULL, /* ioctl */
 	sb1_audio_prepare_for_input,
 	sb1_audio_prepare_for_output,
 	sb1_audio_halt_xfer,
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/sb_card.c linux/drivers/sound/sb_card.c
--- v2.1.76/linux/drivers/sound/sb_card.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/sb_card.c	Mon Dec 29 10:22:46 1997
@@ -34,10 +34,10 @@
 probe_sb(struct address_info *hw_config)
 {
 	if (check_region(hw_config->io_base, 16))
-	  {
-		  printk("\n\nsb_dsp.c: I/O port %x already in use\n\n", hw_config->io_base);
-		  return 0;
-	  }
+	{
+		printk("\n\nsb_card.c: I/O port %x is already in use\n\n", hw_config->io_base);
+		return 0;
+	}
 	return sb_dsp_detect(hw_config);
 }
 
@@ -85,37 +85,36 @@
 
 void           *smw_free = NULL;
 
-int
-init_module(void)
+int init_module(void)
 {
-	printk("Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+	printk(KERN_INFO "Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
 
 	if (mad16 == 0 && trix == 0 && pas2 == 0)
-	  {
-		  if (io == -1 || dma == -1 || irq == -1)
-		    {
-			    printk("I/O, IRQ, DMA and type are mandatory\n");
-			    return -EINVAL;
-		    }
-		  config.io_base = io;
-		  config.irq = irq;
-		  config.dma = dma;
-		  config.dma2 = dma16;
-		  config.card_subtype = type;
-
-		  if (!probe_sb(&config))
-			  return -ENODEV;
-		  attach_sb_card(&config);
+	{
+		if (io == -1 || dma == -1 || irq == -1)
+		{
+			printk(KERN_ERR "I/O, IRQ, and DMA are mandatory\n");
+			return -EINVAL;
+		}
+		config.io_base = io;
+		config.irq = irq;
+		config.dma = dma;
+		config.dma2 = dma16;
+		config.card_subtype = type;
+
+		if (!probe_sb(&config))
+			return -ENODEV;
+		attach_sb_card(&config);
 #ifdef CONFIG_MIDI
-		  config_mpu.io_base = mpu_io;
-		  if (mpu_io && probe_sbmpu(&config_mpu))
-			  sbmpu = 1;
+		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);
+		if (sbmpu)
+			attach_sbmpu(&config_mpu);
 #endif
-	  }
+	}
 	SOUND_LOCK;
 	return 0;
 }
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/sb_midi.c linux/drivers/sound/sb_midi.c
--- v2.1.76/linux/drivers/sound/sb_midi.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/sb_midi.c	Tue Dec 30 10:59:17 1997
@@ -128,10 +128,10 @@
 	return 0;
 }
 
-static int
-sb_midi_ioctl(int dev, unsigned cmd, caddr_t arg)
+/* why -EPERM and not -EINVAL?? */
+static int sb_midi_ioctl(int dev, unsigned cmd, caddr_t arg)
 {
-	return -EPERM;
+        return -EPERM;
 }
 
 void
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/sb_mixer.c linux/drivers/sound/sb_mixer.c
--- v2.1.76/linux/drivers/sound/sb_mixer.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/sb_mixer.c	Tue Dec 30 11:02:39 1997
@@ -11,6 +11,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 
 
@@ -281,68 +284,61 @@
 	return devc->recmask;
 }
 
-static int
-sb_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+static int sb_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
-	sb_devc        *devc = mixer_devs[dev]->devc;
-	int             val;
+	sb_devc *devc = mixer_devs[dev]->devc;
+	int val, ret;
 
-/*
- * Use ioctl(fd, SOUND_MIXER_PRIVATE1, &mode) to turn AGC off (0) or on (1).
- */
-	if (cmd == SOUND_MIXER_PRIVATE1 && devc->model == MDL_SB16)
-	  {
-		  int             tmp;
-
-		  tmp = *(int *) arg;
-
-		  sb_setmixer(devc, 0x43, (~tmp) & 0x01);
-		  return 0;
-	  }
-	if (((cmd >> 8) & 0xff) == 'M')
-	  {
-		  if (_SIOC_DIR(cmd) & _SIOC_WRITE)
-			  switch (cmd & 0xff)
-			    {
-			    case SOUND_MIXER_RECSRC:
-				    val = *(int *) arg;
-				    return (*(int *) arg = set_recmask(devc, val));
-				    break;
-
-			    default:
-
-				    val = *(int *) arg;
-				    return (*(int *) arg = sb_mixer_set(devc, cmd & 0xff, val));
+	/*
+	 * Use ioctl(fd, SOUND_MIXER_PRIVATE1, &mode) to turn AGC off (0) or on (1).
+	 */
+	if (cmd == SOUND_MIXER_PRIVATE1 && devc->model == MDL_SB16) {
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		sb_setmixer(devc, 0x43, (~val) & 0x01);
+		return 0;
+	}
+	if (((cmd >> 8) & 0xff) == 'M') {
+		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+			if (__get_user(val, (int *)arg))
+				return -EFAULT;
+			switch (cmd & 0xff) {
+			case SOUND_MIXER_RECSRC:
+				ret = set_recmask(devc, val);
+				break;
+
+			default:
+				ret = sb_mixer_set(devc, cmd & 0xff, val);
+			}
 		  } else
-			  switch (cmd & 0xff)
-			    {
-
-			    case SOUND_MIXER_RECSRC:
-				    return (*(int *) arg = devc->recmask);
-				    break;
-
-			    case SOUND_MIXER_DEVMASK:
-				    return (*(int *) arg = devc->supported_devices);
-				    break;
-
-			    case SOUND_MIXER_STEREODEVS:
-				    if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
-					    return (*(int *) arg = devc->supported_devices);
-				    else
-					    return (*(int *) arg = devc->supported_devices & ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
-				    break;
-
-			    case SOUND_MIXER_RECMASK:
-				    return (*(int *) arg = devc->supported_rec_devices);
-				    break;
-
-			    case SOUND_MIXER_CAPS:
-				    return (*(int *) arg = devc->mixer_caps);
-				    break;
-
-			    default:
-				    return (*(int *) arg = sb_mixer_get(devc, cmd & 0xff));
-			    }
+			  switch (cmd & 0xff) {
+			  case SOUND_MIXER_RECSRC:
+				  ret = devc->recmask;
+				  break;
+				  
+			  case SOUND_MIXER_DEVMASK:
+				  ret = devc->supported_devices;
+				  break;
+				  
+			  case SOUND_MIXER_STEREODEVS:
+				  ret = devc->supported_devices;
+				  if (devc->model != MDL_JAZZ && devc->model != MDL_SMW)
+					  ret &= ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
+				  break;
+				  
+			  case SOUND_MIXER_RECMASK:
+				  ret = devc->supported_rec_devices;
+				  break;
+				  
+			  case SOUND_MIXER_CAPS:
+				  ret = devc->mixer_caps;
+				  break;
+				    
+			  default:
+				  ret = sb_mixer_get(devc, cmd & 0xff);
+				  break;
+			  }
+		return __put_user(ret, (int *)arg); 
 	} else
 		return -EINVAL;
 }
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c
--- v2.1.76/linux/drivers/sound/sequencer.c	Tue Dec  2 09:49:39 1997
+++ linux/drivers/sound/sequencer.c	Tue Dec 30 11:02:39 1997
@@ -10,6 +10,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 
 
@@ -1517,303 +1520,207 @@
 	 */
 }
 
-int
-sequencer_ioctl(int dev, struct fileinfo *file,
-		unsigned int cmd, caddr_t arg)
+int sequencer_ioctl(int dev, struct fileinfo *file,
+		    unsigned int cmd, caddr_t arg)
 {
-	int             midi_dev, orig_dev, val;
-	int             mode = file->mode & O_ACCMODE;
+	int midi_dev, orig_dev, val, err;
+	int mode = file->mode & O_ACCMODE;
+	struct synth_info inf;
+	struct seq_event_rec event_rec;
+	unsigned long flags;
 
 	orig_dev = dev = dev >> 4;
 
-	switch (cmd)
-	  {
-	  case SNDCTL_TMR_TIMEBASE:
-	  case SNDCTL_TMR_TEMPO:
-	  case SNDCTL_TMR_START:
-	  case SNDCTL_TMR_STOP:
-	  case SNDCTL_TMR_CONTINUE:
-	  case SNDCTL_TMR_METRONOME:
-	  case SNDCTL_TMR_SOURCE:
-
-		  if (seq_mode != SEQ_2)
-			  return -EINVAL;
-		  return tmr->ioctl(tmr_no, cmd, arg);
-		  break;
-
-	  case SNDCTL_TMR_SELECT:
-
-		  if (seq_mode != SEQ_2)
-			  return -EINVAL;
-		  pending_timer = *(int *) arg;
-
-		  if (pending_timer < 0 || pending_timer >= num_sound_timers || sound_timer_devs[pending_timer] == NULL)
-		    {
-			    pending_timer = -1;
-			    return -EINVAL;
-		    }
-		  return (*(int *) arg = pending_timer);
-		  break;
-
-	  case SNDCTL_SEQ_PANIC:
-		  seq_panic();
-		  break;
-
-	  case SNDCTL_SEQ_SYNC:
-
-		  if (mode == OPEN_READ)
+	switch (cmd) {
+	case SNDCTL_TMR_TIMEBASE:
+	case SNDCTL_TMR_TEMPO:
+	case SNDCTL_TMR_START:
+	case SNDCTL_TMR_STOP:
+	case SNDCTL_TMR_CONTINUE:
+	case SNDCTL_TMR_METRONOME:
+	case SNDCTL_TMR_SOURCE:
+		if (seq_mode != SEQ_2)
+			return -EINVAL;
+		return tmr->ioctl(tmr_no, cmd, arg);
+		
+	case SNDCTL_TMR_SELECT:
+		if (seq_mode != SEQ_2)
+			return -EINVAL;
+		if (__get_user(pending_timer, (int *)arg))
+			return -EFAULT;
+		if (pending_timer < 0 || pending_timer >= num_sound_timers || sound_timer_devs[pending_timer] == NULL) {
+			pending_timer = -1;
+			return -EINVAL;
+		}
+		return __put_user(pending_timer, (int *)arg);
+
+	case SNDCTL_SEQ_PANIC:
+		seq_panic();
+		break;
+		
+	case SNDCTL_SEQ_SYNC:
+		if (mode == OPEN_READ)
+			return 0;
+		while (qlen > 0 && !signal_pending(current))
+			seq_sync();
+		return qlen ? -EINTR : 0;
+		
+	case SNDCTL_SEQ_RESET:
+		seq_reset();
+		return 0;
+
+	case SNDCTL_SEQ_TESTMIDI:
+		if (__get_user(midi_dev, (int *)arg))
+			return -EFAULT;
+		if (midi_dev < 0 || midi_dev >= max_mididev)
+			return -ENXIO;
+		
+		if (!midi_opened[midi_dev] && 
+		    (err = midi_devs[midi_dev]->open(midi_dev, mode, sequencer_midi_input,
+						     sequencer_midi_output)) < 0)
+			return err;
+		midi_opened[midi_dev] = 1;
+		return 0;
+
+	case SNDCTL_SEQ_GETINCOUNT:
+		if (mode == OPEN_WRITE)
+			return 0;
+		return __put_user(iqlen,  (int *)arg);
+		
+	case SNDCTL_SEQ_GETOUTCOUNT:
+		if (mode == OPEN_READ)
 			  return 0;
-		  while (qlen > 0 && !signal_pending(current))
-			  seq_sync();
-		  if (qlen)
-			  return -EINTR;
-		  else
-			  return 0;
-		  break;
-
-	  case SNDCTL_SEQ_RESET:
-
-		  seq_reset();
-		  return 0;
-		  break;
-
-	  case SNDCTL_SEQ_TESTMIDI:
-		  midi_dev = *(int *) arg;
-		  if (midi_dev < 0 || midi_dev >= max_mididev)
-			  return -ENXIO;
-
-		  if (!midi_opened[midi_dev])
-		    {
-			    int             err, mode;
-
-			    mode = file->mode & O_ACCMODE;
-			    if ((err = midi_devs[midi_dev]->open(midi_dev, mode,
-						    sequencer_midi_input,
-					     sequencer_midi_output)) < 0)
-				    return err;
-		    }
-		  midi_opened[midi_dev] = 1;
-
-		  return 0;
-		  break;
-
-	  case SNDCTL_SEQ_GETINCOUNT:
-		  if (mode == OPEN_WRITE)
-			  return 0;
-		  return (*(int *) arg = iqlen);
-		  break;
-
-	  case SNDCTL_SEQ_GETOUTCOUNT:
-
-		  if (mode == OPEN_READ)
-			  return 0;
-		  return (*(int *) arg = SEQ_MAX_QUEUE - qlen);
-		  break;
-
-	  case SNDCTL_SEQ_GETTIME:
-		  if (seq_mode == SEQ_2)
-			  return tmr->ioctl(tmr_no, cmd, arg);
-
-		  if (softsynthp != NULL)
-			  return (*(int *) arg = softsynthp(SSYN_GETTIME, 0, 0, 0));
-		  else
-			  return (*(int *) arg = jiffies - seq_time);
-		  break;
-
-	  case SNDCTL_SEQ_CTRLRATE:
-		  /*
-		   * If *arg == 0, just return the current rate
-		   */
-		  if (seq_mode == SEQ_2)
-			  return tmr->ioctl(tmr_no, cmd, arg);
-
-		  val = *(int *) arg;
-		  if (val != 0)
-			  return -EINVAL;
-
-		  return (*(int *) arg = HZ);
-		  break;
-
-	  case SNDCTL_SEQ_RESETSAMPLES:
-	  case SNDCTL_SYNTH_REMOVESAMPLE:
-	  case SNDCTL_SYNTH_CONTROL:
-		  {
-			  int             err;
-
-			  dev = *(int *) arg;
-			  if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
-			    {
-				    return -ENXIO;
-			    }
-			  if (!(synth_open_mask & (1 << dev)) && !orig_dev)
-			    {
-				    return -EBUSY;
-			    }
-			  err = synth_devs[dev]->ioctl(dev, cmd, arg);
-			  return err;
-		  }
-		  break;
-
-	  case SNDCTL_SEQ_NRSYNTHS:
-		  return (*(int *) arg = max_synthdev);
-		  break;
-
-	  case SNDCTL_SEQ_NRMIDIS:
-		  return (*(int *) arg = max_mididev);
-		  break;
-
-	  case SNDCTL_SYNTH_MEMAVL:
-		  {
-			  int             dev;
-
-			  dev = *(int *) arg;
-
-			  if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
-				  return -ENXIO;
-
-			  if (!(synth_open_mask & (1 << dev)) && !orig_dev)
-				  return -EBUSY;
-
-			  return (*(int *) arg = synth_devs[dev]->ioctl(dev, cmd, arg));
-		  }
-		  break;
-
-	  case SNDCTL_FM_4OP_ENABLE:
-		  {
-			  int             dev;
-
-			  dev = *(int *) arg;
-
-			  if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
-				  return -ENXIO;
-
-			  if (!(synth_open_mask & (1 << dev)))
-				  return -ENXIO;
-
-			  synth_devs[dev]->ioctl(dev, cmd, arg);
-			  return 0;
-		  }
-		  break;
-
-	  case SNDCTL_SYNTH_INFO:
-		  {
-			  struct synth_info inf;
-			  int             dev;
-
-			  memcpy((char *) &inf, (&((char *) arg)[0]), sizeof(inf));
-			  dev = inf.device;
-
-			  if (dev < 0 || dev >= max_synthdev)
-				  return -ENXIO;
-
-			  if (!(synth_open_mask & (1 << dev)) && !orig_dev)
-				  return -EBUSY;
-
-			  return synth_devs[dev]->ioctl(dev, cmd, arg);
-		  }
-		  break;
-
-
-		  /* Like SYNTH_INFO but returns ID in the name field */
-	  case SNDCTL_SYNTH_ID:
-		  {
-			  struct synth_info inf;
-			  int             dev;
-
-			  memcpy((char *) &inf, (&((char *) arg)[0]), sizeof(inf));
-			  dev = inf.device;
-
-			  if (dev < 0 || dev >= max_synthdev)
-				  return -ENXIO;
-
-			  if (!(synth_open_mask & (1 << dev)) && !orig_dev)
-				  return -EBUSY;
-
-			  memcpy((char *) &inf, (char *) synth_devs[dev]->info, sizeof(inf));
-			  strcpy(inf.name, synth_devs[dev]->id);
-			  inf.device = dev;
-			  memcpy((&((char *) arg)[0]), (char *) &inf, sizeof(inf));
-			  return 0;
-		  }
-		  break;
-
-	  case SNDCTL_SEQ_OUTOFBAND:
-		  {
-			  struct seq_event_rec event_rec;
-			  unsigned long   flags;
-
-			  memcpy((char *) &event_rec, (&((char *) arg)[0]), sizeof(event_rec));
-
-			  save_flags(flags);
-			  cli();
-			  play_event(event_rec.arr);
-			  restore_flags(flags);
-
-			  return 0;
-		  }
-		  break;
-
-	  case SNDCTL_MIDI_INFO:
-		  {
-			  struct midi_info inf;
-			  int             dev;
-			  char           *pp;
-
-			  memcpy((char *) &inf, (&((char *) arg)[0]), sizeof(inf));
-			  dev = inf.device;
-
-			  if (dev < 0 || dev >= max_mididev)
-				  return -ENXIO;
-
-			  midi_devs[dev]->info.device = dev;
-			  pp = (char *) &midi_devs[dev]->info;
-			  memcpy((&((char *) arg)[0]), pp, sizeof(inf));
-			  return 0;
-		  }
-		  break;
-
-	  case SNDCTL_SEQ_THRESHOLD:
-		  {
-			  int             tmp;
-
-			  tmp = *(int *) arg;
-
-			  if (tmp < 1)
-				  tmp = 1;
-			  if (tmp >= SEQ_MAX_QUEUE)
-				  tmp = SEQ_MAX_QUEUE - 1;
-			  output_threshold = tmp;
-			  return 0;
-		  }
-		  break;
-
-	  case SNDCTL_MIDI_PRETIME:
-		  {
-			  int             val;
-
-			  val = *(int *) arg;
-
-			  if (val < 0)
-				  val = 0;
-
-			  val = (HZ * val) / 10;
-			  pre_event_timeout = val;
-			  return (*(int *) arg = val);
-		  }
-		  break;
-
-	  default:
-		  if (mode == OPEN_READ)
-			  return -EIO;
-
-		  if (!synth_devs[0])
-			  return -ENXIO;
-		  if (!(synth_open_mask & (1 << 0)))
-			  return -ENXIO;
-		  return synth_devs[0]->ioctl(0, cmd, arg);
-		  break;
-	  }
-
+		val = SEQ_MAX_QUEUE - qlen;
+		return __put_user(val, (int *)arg);
+		
+	case SNDCTL_SEQ_GETTIME:
+		if (seq_mode == SEQ_2)
+			return tmr->ioctl(tmr_no, cmd, arg);
+		if (softsynthp != NULL)
+			val = softsynthp(SSYN_GETTIME, 0, 0, 0);
+		else
+			val = jiffies - seq_time;
+		return __put_user(val, (int *)arg);
+		
+	case SNDCTL_SEQ_CTRLRATE:
+		/*
+		 * If *arg == 0, just return the current rate
+		 */
+		if (seq_mode == SEQ_2)
+			return tmr->ioctl(tmr_no, cmd, arg);
+		
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0)
+			return -EINVAL;
+		return __put_user(HZ, (int *)arg);
+
+	case SNDCTL_SEQ_RESETSAMPLES:
+	case SNDCTL_SYNTH_REMOVESAMPLE:
+	case SNDCTL_SYNTH_CONTROL:
+		if (__get_user(dev, (int *)arg))
+			return -EFAULT;
+		if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
+			return -ENXIO;
+		if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+			return -EBUSY;
+		return synth_devs[dev]->ioctl(dev, cmd, arg);
+
+	case SNDCTL_SEQ_NRSYNTHS:
+		return __put_user(max_synthdev, (int *)arg);
+
+	case SNDCTL_SEQ_NRMIDIS:
+		return __put_user(max_mididev, (int *)arg);
+
+	case SNDCTL_SYNTH_MEMAVL:
+		if (__get_user(dev, (int *)arg))
+			return -EFAULT;
+		if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
+			return -ENXIO;
+		if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+			return -EBUSY;
+		val = synth_devs[dev]->ioctl(dev, cmd, arg);
+		return __put_user(val, (int *)arg);
+
+	case SNDCTL_FM_4OP_ENABLE:
+		if (__get_user(dev, (int *)arg))
+			return -EFAULT;
+		if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
+			return -ENXIO;
+		if (!(synth_open_mask & (1 << dev)))
+			return -ENXIO;
+		synth_devs[dev]->ioctl(dev, cmd, arg);
+		return 0;
+
+	case SNDCTL_SYNTH_INFO:
+		if (__get_user(dev, (int *)(&(((struct synth_info *)arg)->device))))
+			return -EFAULT;
+		if (dev < 0 || dev >= max_synthdev)
+			return -ENXIO;
+		if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+			return -EBUSY;
+		return synth_devs[dev]->ioctl(dev, cmd, arg);
+		
+		/* Like SYNTH_INFO but returns ID in the name field */
+	case SNDCTL_SYNTH_ID:
+		if (__get_user(dev, (int *)(&(((struct synth_info *)arg)->device))))
+			return -EFAULT;
+		if (dev < 0 || dev >= max_synthdev)
+			return -ENXIO;
+		if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+			return -EBUSY;
+		memcpy(&inf, synth_devs[dev]->info, sizeof(inf));
+		strncpy(inf.name, synth_devs[dev]->id, sizeof(inf.name));
+		inf.device = dev;
+		return __copy_to_user(arg, &inf, sizeof(inf));
+		
+	case SNDCTL_SEQ_OUTOFBAND:
+		if (__copy_from_user(&event_rec, arg, sizeof(event_rec)))
+			return -EFAULT;
+		save_flags(flags);
+		cli();
+		play_event(event_rec.arr);
+		restore_flags(flags);
+		return 0;
+		  
+	case SNDCTL_MIDI_INFO:
+		if (__get_user(dev, (int *)(&(((struct synth_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));
+		
+	case SNDCTL_SEQ_THRESHOLD:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val < 1)
+			val = 1;
+		if (val >= SEQ_MAX_QUEUE)
+			val = SEQ_MAX_QUEUE - 1;
+		output_threshold = val;
+		return 0;
+		
+	case SNDCTL_MIDI_PRETIME:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val < 0)
+			val = 0;
+		val = (HZ * val) / 10;
+		pre_event_timeout = val;
+		return __put_user(val, (int *)arg);
+
+	default:
+		if (mode == OPEN_READ)
+			return -EIO;
+		if (!synth_devs[0])
+			return -ENXIO;
+		if (!(synth_open_mask & (1 << 0)))
+			return -ENXIO;
+		if (!synth_devs[0]->ioctl)
+			return -EINVAL;
+		return synth_devs[0]->ioctl(0, cmd, arg);
+	}
 	return -EINVAL;
 }
 
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/softoss.c linux/drivers/sound/softoss.c
--- v2.1.76/linux/drivers/sound/softoss.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/softoss.c	Tue Dec 30 10:59:17 1997
@@ -10,6 +10,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 #include <linux/module.h>
 
@@ -114,6 +117,7 @@
 static volatile int tmr_running = 0;
 static int      voice_limit = 24;
 
+
 static void
 set_max_voices(int nr)
 {
@@ -662,10 +666,11 @@
 }
 #endif
 
-static void
-start_engine(softsyn_devc * devc)
+static void start_engine(softsyn_devc * devc)
 {
 	struct dma_buffparms *dmap;
+	int trig, n;
+	mm_segment_t fs;
 
 	if (!devc->audio_opened)
 		if (softsyn_open(devc->synthdev, 0) < 0)
@@ -673,46 +678,37 @@
 
 	if (devc->audiodev >= num_audiodevs)
 		return;
-
+	
 	dmap = audio_devs[devc->audiodev]->dmap_out;
-
+	
 	devc->usecs = 0;
 	devc->next_event_usecs = ~0;
 	devc->control_rate = 64;
 	devc->control_counter = 0;
 
-	if (devc->engine_state == ES_STOPPED)
-	  {
-		  int             trig, n = 0;
-
-		  trig = 0;
-		  dma_ioctl(devc->audiodev, SNDCTL_DSP_SETTRIGGER, (caddr_t) & trig);
+	if (devc->engine_state == ES_STOPPED) {
+		n = trig = 0;
+		fs = get_fs();
+		set_fs(get_ds());
+		dma_ioctl(devc->audiodev, SNDCTL_DSP_SETTRIGGER, (caddr_t)&trig);
 #ifdef POLLED_MODE
-		  ;
-
-		  {
-			  poll_timer.expires = (1) + jiffies;
-			  add_timer(&poll_timer);
-		  };		/* Start polling */
+		poll_timer.expires = (1) + jiffies;
+		add_timer(&poll_timer);
+		/* Start polling */
 #else
-		  dmap->audio_callback = softsyn_callback;
-		  dmap->qhead = dmap->qtail = dmap->qlen = 0;
+		dmap->audio_callback = softsyn_callback;
+		dmap->qhead = dmap->qtail = dmap->qlen = 0;
 #endif
-
-		  while (dmap->qlen < devc->max_playahead && n++ < 2)
-			  do_resample(0);
-
-		  devc->engine_state = ES_STARTED;
-		  last_resample_jiffies = jiffies;
-		  resample_counter = 0;
-
-		  trig = PCM_ENABLE_OUTPUT;
-		  if (dma_ioctl(devc->audiodev, SNDCTL_DSP_SETTRIGGER,
-				(caddr_t) & trig) < 0)
-		    {
-			    printk("SoftOSS: Trigger failed\n");
-		    }
-	  }
+		while (dmap->qlen < devc->max_playahead && n++ < 2)
+			do_resample(0);
+		devc->engine_state = ES_STARTED;
+		last_resample_jiffies = jiffies;
+		resample_counter = 0;
+		trig = PCM_ENABLE_OUTPUT;
+		if (dma_ioctl(devc->audiodev, SNDCTL_DSP_SETTRIGGER, (caddr_t)&trig) < 0)
+			printk(KERN_ERR "SoftOSS: Trigger failed\n");
+		set_fs(fs);
+	}
 }
 
 static void
@@ -760,34 +756,25 @@
 	return 0;
 }
 
-static int
-softsyn_ioctl(int dev,
-	      unsigned int cmd, caddr_t arg)
+static int softsyn_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
-	switch (cmd)
-	  {
-
-	  case SNDCTL_SYNTH_INFO:
-		  softsyn_info.nr_voices = devc->maxvoice;
-
-		  memcpy((&((char *) arg)[0]), (char *) &softsyn_info, sizeof(softsyn_info));
-		  return 0;
-		  break;
-
-	  case SNDCTL_SEQ_RESETSAMPLES:
-		  stop_engine(devc);
-		  reset_samples(devc);
-		  return 0;
-		  break;
-
-	  case SNDCTL_SYNTH_MEMAVL:
-		  return devc->ram_size - devc->ram_used;
-		  break;
+	switch (cmd) {
 
-	  default:
-		  return -EINVAL;
-	  }
+	case SNDCTL_SYNTH_INFO:
+		softsyn_info.nr_voices = devc->maxvoice;
+		return __copy_to_user(arg, &softsyn_info, sizeof(softsyn_info));
 
+	case SNDCTL_SEQ_RESETSAMPLES:
+		stop_engine(devc);
+		reset_samples(devc);
+		return 0;
+		  
+	case SNDCTL_SYNTH_MEMAVL:
+		return devc->ram_size - devc->ram_used;
+
+	default:
+		return -EINVAL;
+	}
 }
 
 static int
@@ -994,12 +981,12 @@
 	return 0;
 }
 
-static int
-softsyn_open(int synthdev, int mode)
+static int softsyn_open(int synthdev, int mode)
 {
 	int             err;
 	extern int      softoss_dev;
 	int             frags = 0x7fff0007;	/* fragment size of 128 bytes */
+	mm_segment_t fs;
 
 	if (devc->audio_opened)	/* Already opened */
 		return 0;
@@ -1035,8 +1022,11 @@
 
 	DDB(printk("SoftOSS: Using audio dev %d, speed %d, bits %d, channels %d\n", devc->audiodev, devc->speed, devc->bits, devc->channels));
 
+	fs = get_fs();
+	set_fs(get_ds());
 	dma_ioctl(devc->audiodev, SNDCTL_DSP_SETFRAGMENT, (caddr_t) & frags);
 	dma_ioctl(devc->audiodev, SNDCTL_DSP_GETBLKSIZE, (caddr_t) & devc->fragsize);
+	set_fs(fs);
 
 	if (devc->bits != 16 || devc->channels != 2)
 	  {
@@ -1055,14 +1045,18 @@
 	return 0;
 }
 
-static void
-softsyn_close(int synthdev)
+static void softsyn_close(int synthdev)
 {
+	mm_segment_t fs;
+
 	devc->engine_state = ES_STOPPED;
 #ifdef POLLED_MODE
-	del_timer(&poll_timer);;
+	del_timer(&poll_timer);
 #endif
+	fs = get_fs();
+	set_fs(get_ds());
 	dma_ioctl(devc->audiodev, SNDCTL_DSP_RESET, 0);
+	set_fs(fs);
 	if (devc->audio_opened)
 		audio_release((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo);
 	devc->audio_opened = 0;
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/sound_calls.h linux/drivers/sound/sound_calls.h
--- v2.1.76/linux/drivers/sound/sound_calls.h	Sun Dec 21 22:36:15 1997
+++ linux/drivers/sound/sound_calls.h	Tue Dec 30 11:02:39 1997
@@ -99,14 +99,6 @@
 int ioctl_in(caddr_t arg);
 int ioctl_out(caddr_t arg, int result);
 
-/*	From sound_switch.c	*/
-int sound_read_sw (int dev, struct fileinfo *file, char *buf, int count);
-int sound_write_sw (int dev, struct fileinfo *file, const char *buf, int count);
-int sound_open_sw (int dev, struct fileinfo *file);
-void sound_release_sw (int dev, struct fileinfo *file);
-int sound_ioctl_sw (int dev, struct fileinfo *file,
-	     unsigned int cmd, caddr_t arg);
-
 /*	From opl3.c	*/
 int opl3_detect (int ioaddr, int *osp);
 int opl3_init(int ioaddr, int *osp);
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/sound_switch.c linux/drivers/sound/sound_switch.c
--- v2.1.76/linux/drivers/sound/sound_switch.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/sound_switch.c	Wed Dec 31 16:00:00 1969
@@ -1,734 +0,0 @@
-/*
- * sound/sound_switch.c
- *
- * The system call switch handler
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-#include <linux/config.h>
-
-
-#include "sound_config.h"
-
-static int      in_use = 0;	/* Total # of open devices */
-unsigned long   seq_time = 0;	/* Time for /dev/sequencer */
-
-/*
- * Table for configurable mixer volume handling
- */
-static mixer_vol_table mixer_vols[MAX_MIXER_DEV];
-static int      num_mixer_volumes = 0;
-
-/*
- * /dev/sndstatus -device
- */
-static char    *status_buf = NULL;
-static int      status_len, status_ptr;
-static int      status_busy = 0;
-
-int
-               *
-load_mixer_volumes(char *name, int *levels, int present)
-{
-	int             i, n;
-
-	for (i = 0; i < num_mixer_volumes; i++)
-		if (strcmp(name, mixer_vols[i].name) == 0)
-		  {
-			  if (present)
-				  mixer_vols[i].num = i;
-			  return mixer_vols[i].levels;
-		  }
-	if (num_mixer_volumes >= MAX_MIXER_DEV)
-	  {
-		  printk("Sound: Too many mixers (%s)\n", name);
-		  return levels;
-	  }
-	n = num_mixer_volumes++;
-
-	strcpy(mixer_vols[n].name, name);
-
-	if (present)
-		mixer_vols[n].num = n;
-	else
-		mixer_vols[n].num = -1;
-
-	for (i = 0; i < 32; i++)
-		mixer_vols[n].levels[i] = levels[i];
-	return mixer_vols[n].levels;
-}
-
-static int
-set_mixer_levels(caddr_t arg)
-{
-	mixer_vol_table *buf = NULL;
-	int             err = 0;
-
-	if ((buf = (mixer_vol_table *) vmalloc(sizeof(mixer_vol_table))) == NULL)
-		return -ENOSPC;
-
-	memcpy((char *) buf, (&((char *) arg)[0]), sizeof(*buf));
-
-	load_mixer_volumes(buf->name, buf->levels, 0);
-
-	memcpy((&((char *) arg)[0]), (char *) buf, sizeof(*buf));
-	vfree(buf);
-
-	return err;
-}
-
-static int
-get_mixer_levels(caddr_t arg)
-{
-	mixer_vol_table *buf = NULL;
-	int             n, err = 0;
-
-	if ((buf = (mixer_vol_table *) vmalloc(sizeof(mixer_vol_table))) == NULL)
-		return -ENOSPC;
-
-	memcpy((char *) buf, (&((char *) arg)[0]), sizeof(*buf));
-
-	n = buf->num;
-	if (n < 0 || n >= num_mixer_volumes)
-		err = -EINVAL;
-	else
-	  {
-		  memcpy((char *) buf, (char *) &mixer_vols[n], sizeof(*buf));
-	  }
-
-	memcpy((&((char *) arg)[0]), (char *) buf, sizeof(*buf));
-	vfree(buf);
-
-	return err;
-}
-
-static int
-put_status(char *s)
-{
-	int             l = strlen(s);
-
-	if (status_len + l >= 4000)
-		return 0;
-
-	memcpy(&status_buf[status_len], s, l);
-	status_len += l;
-
-	return 1;
-}
-
-static int
-put_status_int(unsigned int val, int radix)
-{
-	int             l, v;
-
-	static char     hx[] = "0123456789abcdef";
-	char            buf[11];
-
-	if (!val)
-		return put_status("0");
-
-	l = 0;
-	buf[10] = 0;
-
-	while (val)
-	  {
-		  v = val % radix;
-		  val = val / radix;
-
-		  buf[9 - l] = hx[v];
-		  l++;
-	  }
-
-	if (status_len + l >= 4000)
-		return 0;
-
-	memcpy(&status_buf[status_len], &buf[10 - l], l);
-	status_len += l;
-
-	return 1;
-}
-
-static void
-init_status(void)
-{
-	/*
-	 * Write the status information to the status_buf and update status_len.
-	 * There is a limit of 4000 bytes for the data.
-	 */
-
-	int             i;
-
-	status_ptr = 0;
-
-#ifdef SOUND_UNAME_A
-	put_status("OSS/Free" SOUND_VERSION_STRING
-		   " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY ",\n"
-		   SOUND_UNAME_A ")"
-		   "\n");
-#else
-	put_status("OSS/Free:" SOUND_VERSION_STRING
-#if 0
-		   " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@"
-		   SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")"
-#endif
-		   "\n");
-#endif
-
-#ifdef MODULE
-	put_status("Load type: Driver loaded as a module.\n");
-#else
-	put_status("Load type: Driver compiled into kernel\n");
-#endif
-	put_status("Kernel: ");
-	put_status(system_utsname.sysname);
-	put_status(" ");
-	put_status(system_utsname.nodename);
-	put_status(" ");
-	put_status(system_utsname.release);
-	put_status(" ");
-	put_status(system_utsname.version);
-	put_status(" ");
-	put_status(system_utsname.machine);
-	put_status("\n");
-
-
-	if (!put_status("Config options: "))
-		return;
-	if (!put_status_int(SELECTED_SOUND_OPTIONS, 16))
-		return;
-
-	if (!put_status("\n\nInstalled drivers: \n"))
-		return;
-
-	for (i = 0; i < num_sound_drivers; i++)
-		if (sound_drivers[i].card_type != 0)
-		  {
-			  if (!put_status("Type "))
-				  return;
-			  if (!put_status_int(sound_drivers[i].card_type, 10))
-				  return;
-			  if (!put_status(": "))
-				  return;
-			  if (!put_status(sound_drivers[i].name))
-				  return;
-
-			  if (!put_status("\n"))
-				  return;
-		  }
-	if (!put_status("\nCard config: \n"))
-		return;
-
-	for (i = 0; i < num_sound_cards; i++)
-		if (snd_installed_cards[i].card_type != 0)
-		  {
-			  int             drv, tmp;
-
-			  if (!snd_installed_cards[i].enabled)
-				  if (!put_status("("))
-					  return;
-
-			  /*
-			   * if (!put_status_int(snd_installed_cards[i].card_type, 10)) return;
-			   * if (!put_status (": ")) return;
-			   */
-
-			  if ((drv = snd_find_driver(snd_installed_cards[i].card_type)) != -1)
-				  if (!put_status(sound_drivers[drv].name))
-					  return;
-
-			  if (snd_installed_cards[i].config.io_base)
-			    {
-				    if (!put_status(" at 0x"))
-					    return;
-				    if (!put_status_int(snd_installed_cards[i].config.io_base, 16))
-					    return;
-			    }
-			  tmp = snd_installed_cards[i].config.irq;
-			  if (tmp != 0)
-			    {
-				    if (!put_status(" irq "))
-					    return;
-				    if (tmp < 0)
-					    tmp = -tmp;
-				    if (!put_status_int(tmp, 10))
-					    return;
-			    }
-			  if (snd_installed_cards[i].config.dma != -1)
-			    {
-				    if (!put_status(" drq "))
-					    return;
-				    if (!put_status_int(snd_installed_cards[i].config.dma, 10))
-					    return;
-				    if (snd_installed_cards[i].config.dma2 != -1)
-				      {
-					      if (!put_status(","))
-						      return;
-					      if (!put_status_int(snd_installed_cards[i].config.dma2, 10))
-						      return;
-				      }
-			    }
-			  if (!snd_installed_cards[i].enabled)
-				  if (!put_status(")"))
-					  return;
-
-			  if (!put_status("\n"))
-				  return;
-		  }
-	if (!sound_started)
-	  {
-		  put_status("\n\n***** Sound driver not started *****\n\n");
-		  return;
-	  }
-#ifndef CONFIG_AUDIO
-	if (!put_status("\nAudio devices: NOT ENABLED IN CONFIG\n"))
-		return;
-#else
-	if (!put_status("\nAudio devices:\n"))
-		return;
-
-	for (i = 0; i < num_audiodevs; i++)
-	  {
-		  if (audio_devs[i] == NULL)
-			  continue;
-		  if (!put_status_int(i, 10))
-			  return;
-		  if (!put_status(": "))
-			  return;
-		  if (!put_status(audio_devs[i]->name))
-			  return;
-
-		  if (audio_devs[i]->flags & DMA_DUPLEX)
-			  if (!put_status(" (DUPLEX)"))
-				  return;
-
-		  if (!put_status("\n"))
-			  return;
-	  }
-#endif
-
-#ifndef CONFIG_SEQUENCER
-	if (!put_status("\nSynth devices: NOT ENABLED IN CONFIG\n"))
-		return;
-#else
-	if (!put_status("\nSynth devices:\n"))
-		return;
-
-	for (i = 0; i < num_synths; i++)
-	  {
-		  if (synth_devs[i] == NULL)
-			  continue;
-		  if (!put_status_int(i, 10))
-			  return;
-		  if (!put_status(": "))
-			  return;
-		  if (!put_status(synth_devs[i]->info->name))
-			  return;
-		  if (!put_status("\n"))
-			  return;
-	  }
-#endif
-
-#ifndef CONFIG_MIDI
-	if (!put_status("\nMidi devices: NOT ENABLED IN CONFIG\n"))
-		return;
-#else
-	if (!put_status("\nMidi devices:\n"))
-		return;
-
-	for (i = 0; i < num_midis; i++)
-	  {
-		  if (midi_devs[i] == NULL)
-			  continue;
-		  if (!put_status_int(i, 10))
-			  return;
-		  if (!put_status(": "))
-			  return;
-		  if (!put_status(midi_devs[i]->info.name))
-			  return;
-		  if (!put_status("\n"))
-			  return;
-	  }
-#endif
-
-#ifdef CONFIG_SEQUENCER
-	if (!put_status("\nTimers:\n"))
-		return;
-
-	for (i = 0; i < num_sound_timers; i++)
-	  {
-		  if (sound_timer_devs[i] == NULL)
-			  continue;
-		  if (!put_status_int(i, 10))
-			  return;
-		  if (!put_status(": "))
-			  return;
-		  if (!put_status(sound_timer_devs[i]->info.name))
-			  return;
-		  if (!put_status("\n"))
-			  return;
-	  }
-#endif
-
-	if (!put_status("\nMixers:\n"))
-		return;
-
-	for (i = 0; i < num_mixers; i++)
-	  {
-		  if (mixer_devs[i] == NULL)
-			  continue;
-		  if (!put_status_int(i, 10))
-			  return;
-		  if (!put_status(": "))
-			  return;
-		  if (!put_status(mixer_devs[i]->name))
-			  return;
-		  if (!put_status("\n"))
-			  return;
-	  }
-}
-
-static int
-read_status(char *buf, int count)
-{
-	/*
-	 * Return at most 'count' bytes from the status_buf.
-	 */
-	int             l, c;
-
-	l = count;
-	c = status_len - status_ptr;
-
-	if (l > c)
-		l = c;
-	if (l <= 0)
-		return 0;
-
-	{
-		char           *fixit = &status_buf[status_ptr];
-
-		copy_to_user(&(buf)[0], fixit, l);
-	};
-	status_ptr += l;
-
-	return l;
-}
-
-int
-sound_read_sw(int dev, struct fileinfo *file, char *buf, int count)
-{
-	DEB(printk("sound_read_sw(dev=%d, count=%d)\n", dev, count));
-
-	switch (dev & 0x0f)
-	  {
-	  case SND_DEV_STATUS:
-		  return read_status(buf, count);
-		  break;
-
-#ifdef CONFIG_AUDIO
-	  case SND_DEV_DSP:
-	  case SND_DEV_DSP16:
-	  case SND_DEV_AUDIO:
-		  return audio_read(dev, file, buf, count);
-		  break;
-#endif
-
-#ifdef CONFIG_SEQUENCER
-	  case SND_DEV_SEQ:
-	  case SND_DEV_SEQ2:
-		  return sequencer_read(dev, file, buf, count);
-		  break;
-#endif
-
-#ifdef CONFIG_MIDI
-	  case SND_DEV_MIDIN:
-		  return MIDIbuf_read(dev, file, buf, count);
-#endif
-
-	  default:;
-	  }
-
-	return -EINVAL;
-}
-
-int
-sound_write_sw(int dev, struct fileinfo *file, const char *buf, int count)
-{
-
-	DEB(printk("sound_write_sw(dev=%d, count=%d)\n", dev, count));
-
-
-	switch (dev & 0x0f)
-	  {
-
-#ifdef CONFIG_SEQUENCER
-	  case SND_DEV_SEQ:
-	  case SND_DEV_SEQ2:
-		  return sequencer_write(dev, file, buf, count);
-		  break;
-#endif
-
-#ifdef CONFIG_AUDIO
-	  case SND_DEV_DSP:
-	  case SND_DEV_DSP16:
-	  case SND_DEV_AUDIO:
-		  return audio_write(dev, file, buf, count);
-		  break;
-#endif
-
-#ifdef CONFIG_MIDI
-	  case SND_DEV_MIDIN:
-		  return MIDIbuf_write(dev, file, buf, count);
-#endif
-
-	  }
-
-	return -EINVAL;
-}
-
-int
-sound_open_sw(int dev, struct fileinfo *file)
-{
-	int             retval;
-
-	DEB(printk("sound_open_sw(dev=%d)\n", dev));
-
-	if ((dev >= SND_NDEVS) || (dev < 0))
-	  {
-		  printk("Invalid minor device %d\n", dev);
-		  return -ENXIO;
-	  }
-	switch (dev & 0x0f)
-	  {
-	  case SND_DEV_STATUS:
-		  if (status_busy)
-			  return -EBUSY;
-		  status_busy = 1;
-		  if ((status_buf = (char *) vmalloc(4000)) == NULL)
-		    {
-			    status_busy = 0;
-			    return -EIO;
-		    }
-		  status_len = status_ptr = 0;
-		  init_status();
-		  break;
-
-	  case SND_DEV_CTL:
-		  if ((dev & 0xf0) && ((dev & 0xf0) >> 4) >= num_mixers)
-			  return -ENXIO;
-		  return 0;
-		  break;
-
-#ifdef CONFIG_SEQUENCER
-	  case SND_DEV_SEQ:
-	  case SND_DEV_SEQ2:
-		  if ((retval = sequencer_open(dev, file)) < 0)
-			  return retval;
-		  break;
-#endif
-
-#ifdef CONFIG_MIDI
-	  case SND_DEV_MIDIN:
-		  if ((retval = MIDIbuf_open(dev, file)) < 0)
-			  return retval;
-		  break;
-#endif
-
-#ifdef CONFIG_AUDIO
-	  case SND_DEV_DSP:
-	  case SND_DEV_DSP16:
-	  case SND_DEV_AUDIO:
-		  if ((retval = audio_open(dev, file)) < 0)
-			  return retval;
-		  break;
-#endif
-
-	  default:
-		  printk("Invalid minor device %d\n", dev);
-		  return -ENXIO;
-	  }
-
-	in_use++;
-
-	return 0;
-}
-
-void
-sound_release_sw(int dev, struct fileinfo *file)
-{
-
-	DEB(printk("sound_release_sw(dev=%d)\n", dev));
-
-	switch (dev & 0x0f)
-	  {
-	  case SND_DEV_STATUS:
-		  if (status_buf)
-			  vfree(status_buf);
-		  status_buf = NULL;
-		  status_busy = 0;
-		  break;
-
-	  case SND_DEV_CTL:
-		  break;
-
-#ifdef CONFIG_SEQUENCER
-	  case SND_DEV_SEQ:
-	  case SND_DEV_SEQ2:
-		  sequencer_release(dev, file);
-		  break;
-#endif
-
-#ifdef CONFIG_MIDI
-	  case SND_DEV_MIDIN:
-		  MIDIbuf_release(dev, file);
-		  break;
-#endif
-
-#ifdef CONFIG_AUDIO
-	  case SND_DEV_DSP:
-	  case SND_DEV_DSP16:
-	  case SND_DEV_AUDIO:
-		  audio_release(dev, file);
-		  break;
-#endif
-
-	  default:
-		  printk("Sound error: Releasing unknown device 0x%02x\n", dev);
-	  }
-	in_use--;
-}
-
-static int
-get_mixer_info(int dev, caddr_t arg)
-{
-	mixer_info      info;
-	int             i;
-
-	if (dev < 0 || dev >= num_mixers)
-		return -ENXIO;
-
-	strcpy(info.id, mixer_devs[dev]->id);
-	for (i = 0; i < 32 && mixer_devs[dev]->name; i++)
-		info.name[i] = mixer_devs[dev]->name[i];
-	info.name[i] = 0;
-	info.modify_counter = mixer_devs[dev]->modify_counter;
-
-	memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
-	return 0;
-}
-
-static int
-get_old_mixer_info(int dev, caddr_t arg)
-{
-	_old_mixer_info info;
-	int             i;
-
-	if (dev < 0 || dev >= num_mixers)
-		return -ENXIO;
-
-	strcpy(info.id, mixer_devs[dev]->id);
-	for (i = 0; i < 32 && mixer_devs[dev]->name; i++)
-		info.name[i] = mixer_devs[dev]->name[i];
-	info.name[i] = 0;
-
-	memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
-	return 0;
-}
-
-static int
-sound_mixer_ioctl(int mixdev,
-		  unsigned int cmd, caddr_t arg)
-{
-	if (cmd == SOUND_MIXER_INFO)
-		return get_mixer_info(mixdev, arg);
-	if (cmd == SOUND_OLD_MIXER_INFO)
-		return get_old_mixer_info(mixdev, arg);
-
-	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
-		mixer_devs[mixdev]->modify_counter++;
-
-	return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg);
-}
-
-int
-sound_ioctl_sw(int dev, struct fileinfo *file,
-	       unsigned int cmd, caddr_t arg)
-{
-	DEB(printk("sound_ioctl_sw(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
-
-	if (cmd == OSS_GETVERSION)
-		return (*(int *) arg = SOUND_VERSION);
-
-
-	if (((cmd >> 8) & 0xff) == 'M' && num_mixers > 0)	/* Mixer ioctl */
-		if ((dev & 0x0f) != SND_DEV_CTL)
-		  {
-			  int             dtype = dev & 0x0f;
-			  int             mixdev;
-
-			  switch (dtype)
-			    {
-#ifdef CONFIG_AUDIO
-			    case SND_DEV_DSP:
-			    case SND_DEV_DSP16:
-			    case SND_DEV_AUDIO:
-				    mixdev = audio_devs[dev >> 4]->mixer_dev;
-				    if (mixdev < 0 || mixdev >= num_mixers)
-					    return -ENXIO;
-				    return sound_mixer_ioctl(mixdev, cmd, arg);
-				    break;
-#endif
-
-			    default:
-				    return sound_mixer_ioctl(dev, cmd, arg);
-			    }
-		  }
-	switch (dev & 0x0f)
-	  {
-
-	  case SND_DEV_CTL:
-		  if (cmd == SOUND_MIXER_GETLEVELS)
-			  return get_mixer_levels(arg);
-		  if (cmd == SOUND_MIXER_SETLEVELS)
-			  return set_mixer_levels(arg);
-
-		  if (!num_mixers)
-			  return -ENXIO;
-
-		  dev = dev >> 4;
-
-		  if (dev >= num_mixers)
-			  return -ENXIO;
-
-		  return sound_mixer_ioctl(dev, cmd, arg);
-		  break;
-
-#ifdef CONFIG_SEQUENCER
-	  case SND_DEV_SEQ:
-	  case SND_DEV_SEQ2:
-		  return sequencer_ioctl(dev, file, cmd, arg);
-		  break;
-#endif
-
-#ifdef CONFIG_AUDIO
-	  case SND_DEV_DSP:
-	  case SND_DEV_DSP16:
-	  case SND_DEV_AUDIO:
-		  return audio_ioctl(dev, file, cmd, arg);
-		  break;
-#endif
-
-#ifdef CONFIG_MIDI
-	  case SND_DEV_MIDIN:
-		  return MIDIbuf_ioctl(dev, file, cmd, arg);
-		  break;
-#endif
-
-	  }
-
-	return -EINVAL;
-}
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/sound_syms.c linux/drivers/sound/sound_syms.c
--- v2.1.76/linux/drivers/sound/sound_syms.c	Tue Dec 23 16:31:00 1997
+++ linux/drivers/sound/sound_syms.c	Mon Dec 29 10:22:46 1997
@@ -13,6 +13,8 @@
 #include "sound_firmware.h"
 
 extern struct notifier_block *sound_locker;
+extern void sound_notifier_chain_register(struct notifier_block *);
+
 
 EXPORT_SYMBOL(mixer_devs);
 EXPORT_SYMBOL(audio_devs);
@@ -63,6 +65,7 @@
 
 /* Locking */
 EXPORT_SYMBOL(sound_locker);
+EXPORT_SYMBOL(sound_notifier_chain_register);
 
 /* MIDI symbols */
 EXPORT_SYMBOL(midi_devs);
@@ -86,3 +89,4 @@
 EXPORT_SYMBOL(midi_synth_send_sysex);
 EXPORT_SYMBOL(midi_synth_bender);
 EXPORT_SYMBOL(midi_synth_load_patch);
+
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/sound_timer.c linux/drivers/sound/sound_timer.c
--- v2.1.76/linux/drivers/sound/sound_timer.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/sound_timer.c	Tue Dec 30 11:02:39 1997
@@ -1,5 +1,3 @@
-
-
 /*
  * sound/sound_timer.c
  */
@@ -10,6 +8,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 
 
@@ -187,86 +188,72 @@
 	return curr_ticks;
 }
 
-static int
-timer_ioctl(int dev,
-	    unsigned int cmd, caddr_t arg)
+static int timer_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
-	int             val;
-
-	switch (cmd)
-	  {
-	  case SNDCTL_TMR_SOURCE:
-		  return (*(int *) arg = TMR_INTERNAL);
-		  break;
+	int val;
 
-	  case SNDCTL_TMR_START:
-		  tmr_reset();
-		  tmr_running = 1;
-		  return 0;
-		  break;
-
-	  case SNDCTL_TMR_STOP:
-		  tmr_running = 0;
-		  return 0;
-		  break;
-
-	  case SNDCTL_TMR_CONTINUE:
-		  tmr_running = 1;
-		  return 0;
-		  break;
-
-	  case SNDCTL_TMR_TIMEBASE:
-		  val = *(int *) arg;
-
-		  if (val)
-		    {
-			    if (val < 1)
-				    val = 1;
-			    if (val > 1000)
-				    val = 1000;
-			    curr_timebase = val;
-		    }
-		  return (*(int *) arg = curr_timebase);
-		  break;
-
-	  case SNDCTL_TMR_TEMPO:
-		  val = *(int *) arg;
-
-		  if (val)
-		    {
-			    if (val < 8)
-				    val = 8;
-			    if (val > 250)
-				    val = 250;
-			    tmr_offs = tmr_ctr;
-			    ticks_offs += tmr2ticks(tmr_ctr);
-			    tmr_ctr = 0;
-			    curr_tempo = val;
-			    reprogram_timer();
-		    }
-		  return (*(int *) arg = curr_tempo);
-		  break;
-
-	  case SNDCTL_SEQ_CTRLRATE:
-		  val = *(int *) arg;
-
-		  if (val != 0)	/* Can't change */
-			  return -EINVAL;
-
-		  return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
-		  break;
-
-	  case SNDCTL_SEQ_GETTIME:
-		  return (*(int *) arg = curr_ticks);
-		  break;
-
-	  case SNDCTL_TMR_METRONOME:
-		  /* NOP */
-		  break;
+	switch (cmd) {
+	case SNDCTL_TMR_SOURCE:
+		return __put_user(TMR_INTERNAL, (int *)arg);
+
+	case SNDCTL_TMR_START:
+		tmr_reset();
+		tmr_running = 1;
+		return 0;
+		
+	case SNDCTL_TMR_STOP:
+		tmr_running = 0;
+		return 0;
 
-	  default:;
-	  }
+	case SNDCTL_TMR_CONTINUE:
+		tmr_running = 1;
+		return 0;
 
+	case SNDCTL_TMR_TIMEBASE:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val) {
+			if (val < 1)
+				val = 1;
+			if (val > 1000)
+				val = 1000;
+			curr_timebase = val;
+		}
+		return __put_user(curr_timebase, (int *)arg);
+
+	case SNDCTL_TMR_TEMPO:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val) {
+			if (val < 8)
+				val = 8;
+			if (val > 250)
+				val = 250;
+			tmr_offs = tmr_ctr;
+			ticks_offs += tmr2ticks(tmr_ctr);
+			tmr_ctr = 0;
+			curr_tempo = val;
+			reprogram_timer();
+		}
+		return __put_user(curr_tempo, (int *)arg);
+
+	case SNDCTL_SEQ_CTRLRATE:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0)	/* Can't change */
+			return -EINVAL;
+		val = ((curr_tempo * curr_timebase) + 30) / 60;
+		return __put_user(val, (int *)arg);
+		
+	case SNDCTL_SEQ_GETTIME:
+		return __put_user(curr_ticks, (int *)arg);
+		
+	case SNDCTL_TMR_METRONOME:
+		/* NOP */
+		break;
+		
+	default:;
+	}
 	return -EINVAL;
 }
 
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v2.1.76/linux/drivers/sound/soundcard.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/soundcard.c	Tue Dec 30 11:02:39 1997
@@ -1,5 +1,5 @@
 /*
- * linux/kernel/chr_drv/sound/soundcard.c
+ * linux/drivers/sound/soundcard.c
  *
  * Soundcard driver for Linux
  */
@@ -10,6 +10,11 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ *                   integrated sound_switch.c and made /proc/sound (equals to /dev/sndstat,
+ *                   which should disappear in the near future)
+ */
 #include <linux/config.h>
 
 
@@ -27,6 +32,7 @@
 #include <linux/ioport.h>
 #endif				/* __KERNEL__ */
 #include <linux/delay.h>
+#include <linux/proc_fs.h>
 
 #define SOUND_CORE
 
@@ -63,27 +69,342 @@
 #define DMA_MAP_BUSY		2
 
 
+static int in_use = 0;	        /* Total # of open devices */
+unsigned long seq_time = 0;	/* Time for /dev/sequencer */
 
-static ssize_t sound_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+/*
+ * Table for configurable mixer volume handling
+ */
+static mixer_vol_table mixer_vols[MAX_MIXER_DEV];
+static int num_mixer_volumes = 0;
+
+
+int *load_mixer_volumes(char *name, int *levels, int present)
+{
+	int             i, n;
+
+	for (i = 0; i < num_mixer_volumes; i++)
+		if (strcmp(name, mixer_vols[i].name) == 0)
+		  {
+			  if (present)
+				  mixer_vols[i].num = i;
+			  return mixer_vols[i].levels;
+		  }
+	if (num_mixer_volumes >= MAX_MIXER_DEV)
+	  {
+		  printk("Sound: Too many mixers (%s)\n", name);
+		  return levels;
+	  }
+	n = num_mixer_volumes++;
+
+	strcpy(mixer_vols[n].name, name);
+
+	if (present)
+		mixer_vols[n].num = n;
+	else
+		mixer_vols[n].num = -1;
+
+	for (i = 0; i < 32; i++)
+		mixer_vols[n].levels[i] = levels[i];
+	return mixer_vols[n].levels;
+}
+
+static int set_mixer_levels(caddr_t arg)
 {
-	int             dev;
+        /* mixer_vol_table is 174 bytes, so IMHO no reason to not allocate it on the stack */
+	mixer_vol_table buf;   
 
-	dev = MINOR(file->f_dentry->d_inode->i_rdev);
+	if (__copy_from_user(&buf, arg, sizeof(buf)))
+		return -EFAULT;
+	load_mixer_volumes(buf.name, buf.levels, 0);
+	return __copy_to_user(arg, &buf, sizeof(buf));
+}
+
+static int get_mixer_levels(caddr_t arg)
+{
+	int n;
+
+	if (__get_user(n, (int *)(&(((mixer_vol_table *)arg)->num))))
+		return -EFAULT;
+	if (n < 0 || n >= num_mixer_volumes)
+		return -EINVAL;
+	return __copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table));
+}
+
+static int sound_proc_get_info(char *buffer, char **start, off_t offset, int length, int inout)
+{
+	int len, i, drv;
+        off_t pos = 0;
+        off_t begin = 0;
+
+#ifdef MODULE
+#define MODULEPROCSTRING "Driver loaded as a module"
+#else
+#define MODULEPROCSTRING "Driver compiled into kernel"
+#endif
+	
+	len = sprintf(buffer, "OSS/Free:" SOUND_VERSION_STRING "\n"
+		      "Load type: " MODULEPROCSTRING "\n"
+		      "Kernel: %s %s %s %s %s\n"
+		      "Config options: %x\n\nInstalled drivers: \n", 
+		      system_utsname.sysname, system_utsname.nodename, system_utsname.release, 
+		      system_utsname.version, system_utsname.machine, SELECTED_SOUND_OPTIONS);
+	
+	for (i = 0; (i < num_sound_drivers) && (pos <= offset + length); i++) {
+		if (!sound_drivers[i].card_type)
+			continue;
+		len += sprintf(buffer + len, "Type %d: %s\n", 
+			       sound_drivers[i].card_type, sound_drivers[i].name);
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+	}
+	len += sprintf(buffer + len, "\nCard config: \n");
+
+	for (i = 0; (i < num_sound_cards) && (pos <= offset + length); i++) {
+		if (!snd_installed_cards[i].card_type)
+			continue;
+		if (!snd_installed_cards[i].enabled)
+			len += sprintf(buffer + len, "(");
+		if ((drv = snd_find_driver(snd_installed_cards[i].card_type)) != -1)
+			len += sprintf(buffer + len, "%s", sound_drivers[drv].name);
+		if (snd_installed_cards[i].config.io_base)
+			len += sprintf(buffer + len, " at 0x%x", snd_installed_cards[i].config.io_base);
+		if (snd_installed_cards[i].config.irq != 0)
+			len += sprintf(buffer + len, " irq %d", abs(snd_installed_cards[i].config.irq));
+		if (snd_installed_cards[i].config.dma != -1) {
+			len += sprintf(buffer + len, " drq %d", snd_installed_cards[i].config.dma);
+			if (snd_installed_cards[i].config.dma2 != -1)
+				len += sprintf(buffer + len, ",%d", snd_installed_cards[i].config.dma2);
+		}
+		if (!snd_installed_cards[i].enabled)
+			len += sprintf(buffer + len, ")");
+		len += sprintf(buffer + len, "\n");
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+	}
+	if (!sound_started)
+		len += sprintf(buffer + len, "\n\n***** Sound driver not started *****\n\n");
+#ifndef CONFIG_AUDIO
+	len += sprintf(buffer + len, "\nAudio devices: NOT ENABLED IN CONFIG\n");
+#else
+	len += sprintf(buffer + len, "\nAudio devices:\n");
+	for (i = 0; (i < num_audiodevs) && (pos <= offset + length); i++) {
+		if (audio_devs[i] == NULL)
+			continue;
+		len += sprintf(buffer + len, "%d: %s%s\n", i, audio_devs[i]->name, 
+			       audio_devs[i]->flags & DMA_DUPLEX ? " (DUPLEX)" : "");
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+	}
+#endif
+
+#ifndef CONFIG_SEQUENCER
+	len += sprintf(buffer + len, "\nSynth devices: NOT ENABLED IN CONFIG\n");
+#else
+	len += sprintf(buffer + len, "\nSynth devices:\n");
+	for (i = 0; (i < num_synths) && (pos <= offset + length); i++) {
+		if (synth_devs[i] == NULL)
+			continue;
+		len += sprintf(buffer + len, "%d: %s\n", i, synth_devs[i]->info->name);
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+	}
+#endif
+
+#ifndef CONFIG_MIDI
+	len += sprintf(buffer + len, "\nMidi devices: NOT ENABLED IN CONFIG\n");
+#else
+	len += sprintf(buffer + len, "\nMidi devices:\n");
+	for (i = 0; (i < num_midis) && (pos <= offset + length); i++) {
+		if (midi_devs[i] == NULL)
+			continue;
+		len += sprintf(buffer + len, "%d: %s\n", i, midi_devs[i]->info.name);
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+	}
+#endif
+
+#ifdef CONFIG_SEQUENCER
+	len += sprintf(buffer + len, "\nTimers:\n");
+
+	for (i = 0; (i < num_sound_timers) && (pos <= offset + length); i++) {
+		if (sound_timer_devs[i] == NULL)
+			continue;
+		len += sprintf(buffer + len, "%d: %s\n", i, sound_timer_devs[i]->info.name);
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+	}
+#endif
+
+	len += sprintf(buffer + len, "\nMixers:\n");
+	for (i = 0; (i < num_mixers) && (pos <= offset + length); i++) {
+		if (mixer_devs[i] == NULL)
+			continue;
+		len += sprintf(buffer + len, "%d: %s\n", i, mixer_devs[i]->name);
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+	}
+	*start = buffer + (offset - begin);
+	len -= (offset - begin);
+        if (len > length) 
+		len = length;
+        return len;
+}
+
+static struct proc_dir_entry proc_root_sound = {
+        PROC_SOUND, 5, "sound",
+        S_IFREG | S_IRUGO, 1, 0, 0,
+        0, NULL, sound_proc_get_info
+};
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/* 4K page size but our output routines use some slack for overruns */
+#define PROC_BLOCK_SIZE (3*1024)
+
+/*
+ * basically copied from fs/proc/generic.c:proc_file_read 
+ * should be removed sometime in the future together with /dev/sndstat
+ * (a symlink /dev/sndstat -> /proc/sound will do as well)
+ */
+static ssize_t sndstat_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
+{
+        char    *page;
+        ssize_t retval=0;
+        int     eof=0;
+        ssize_t n, count;
+        char    *start;
+
+        if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+                return -ENOMEM;
+
+        while ((nbytes > 0) && !eof)
+        {
+                count = MIN(PROC_BLOCK_SIZE, nbytes);
+
+                start = NULL;
+		n = sound_proc_get_info(page, &start, *ppos, count, 0);
+		if (n < count)
+			eof = 1;
+                        
+                if (!start) {
+                        /*
+                         * For proc files that are less than 4k
+                         */
+                        start = page + *ppos;
+                        n -= *ppos;
+                        if (n <= 0)
+                                break;
+                        if (n > count)
+                                n = count;
+                }
+                if (n == 0)
+                        break;  /* End of file */
+                if (n < 0) {
+                        if (retval == 0)
+                                retval = n;
+                        break;
+                }
+                
+                n -= copy_to_user(buf, start, n);       /* BUG ??? */
+                if (n == 0) {
+                        if (retval == 0)
+                                retval = -EFAULT;
+                        break;
+                }
+                
+                *ppos += n;     /* Move down the file */
+                nbytes -= n;
+                buf += n;
+                retval += n;
+        }
+        free_page((unsigned long) page);
+        return retval;
+}
+
+
+static ssize_t sound_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	int dev = MINOR(file->f_dentry->d_inode->i_rdev);
 
 	files[dev].flags = file->f_flags;
+	DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count));
+	switch (dev & 0x0f) {
+	case SND_DEV_STATUS:
+		return sndstat_file_read(file, buf, count, ppos);
+
+#ifdef CONFIG_AUDIO
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		return audio_read(dev, &files[dev], buf, count);
+#endif
 
-	return sound_read_sw(dev, &files[dev], buf, count);
+#ifdef CONFIG_SEQUENCER
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		return sequencer_read(dev, &files[dev], buf, count);
+#endif
+
+#ifdef CONFIG_MIDI
+	case SND_DEV_MIDIN:
+		return MIDIbuf_read(dev, &files[dev], buf, count);
+#endif
+
+	default:;
+	}
+	return -EINVAL;
 }
 
 static ssize_t sound_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
 {
-	int             dev;
-
-	dev = MINOR(file->f_dentry->d_inode->i_rdev);
+	int dev = MINOR(file->f_dentry->d_inode->i_rdev);
 
 	files[dev].flags = file->f_flags;
+	DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count));
+	switch (dev & 0x0f) {
+#ifdef CONFIG_SEQUENCER
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		return sequencer_write(dev, &files[dev], buf, count);
+#endif
+
+#ifdef CONFIG_AUDIO
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		return audio_write(dev, &files[dev], buf, count);
+#endif
 
-	return sound_write_sw(dev, &files[dev], buf, count);
+#ifdef CONFIG_MIDI
+	case SND_DEV_MIDIN:
+		return MIDIbuf_write(dev, &files[dev], buf, count);
+#endif
+	}
+	return -EINVAL;
 }
 
 static long long sound_lseek(struct file *file, long long offset, int orig)
@@ -93,20 +414,17 @@
 
 static int sound_open(struct inode *inode, struct file *file)
 {
-	int             dev, retval;
+	int dev, retval;
 	struct fileinfo tmp_file;
 
-	if (is_unloading)
-	{
-/*		  printk(KERN_ERR "Sound: Driver partially removed. Can't open device\n");*/
-		  return -EBUSY;
+	if (is_unloading) {
+		/* printk(KERN_ERR "Sound: Driver partially removed. Can't open device\n");*/
+		return -EBUSY;
 	}
 	dev = MINOR(inode->i_rdev);
-
-	if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS)
-	{
-/*		  printk("SoundCard Error: The soundcard system has not been configured\n");*/
-		  return -ENXIO;
+	if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS) {
+		/* printk("SoundCard Error: The soundcard system has not been configured\n");*/
+		return -ENXIO;
 	}
 	tmp_file.mode = 0;
 	tmp_file.flags = file->f_flags;
@@ -117,130 +435,259 @@
 		tmp_file.mode = OPEN_READ;
 	if ((tmp_file.flags & O_ACCMODE) == O_WRONLY)
 		tmp_file.mode = OPEN_WRITE;
+	DEB(printk("sound_open(dev=%d)\n", dev));
+	if ((dev >= SND_NDEVS) || (dev < 0)) {
+		printk(KERN_ERR "Invalid minor device %d\n", dev);
+		return -ENXIO;
+	}
+	switch (dev & 0x0f) {
+	case SND_DEV_STATUS:
+		break;
+
+	case SND_DEV_CTL:
+		if ((dev & 0xf0) && ((dev & 0xf0) >> 4) >= num_mixers)
+			return -ENXIO;
+		break;
+
+#ifdef CONFIG_SEQUENCER
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		if ((retval = sequencer_open(dev, &tmp_file)) < 0)
+			return retval;
+		break;
+#endif
 
-	if ((retval = sound_open_sw(dev, &tmp_file)) < 0)
-		return retval;
+#ifdef CONFIG_MIDI
+	case SND_DEV_MIDIN:
+		if ((retval = MIDIbuf_open(dev, &tmp_file)) < 0)
+			return retval;
+		break;
+#endif
 
+#ifdef CONFIG_AUDIO
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		if ((retval = audio_open(dev, &tmp_file)) < 0)
+			return retval;
+		break;
+#endif
+
+	default:
+		printk(KERN_ERR "Invalid minor device %d\n", dev);
+		return -ENXIO;
+	}
+	in_use++;
 #ifdef MODULE
 	SOUND_INC_USE_COUNT;
 #endif
-
-	memcpy((char *) &files[dev], (char *) &tmp_file, sizeof(tmp_file));
-	return retval;
+	memcpy(&files[dev], &tmp_file, sizeof(tmp_file));
+	return 0;
 }
 
 static int sound_release(struct inode *inode, struct file *file)
 {
-	int             dev;
-
-	dev = MINOR(inode->i_rdev);
+	int dev = MINOR(inode->i_rdev);
 
 	files[dev].flags = file->f_flags;
+	DEB(printk("sound_release(dev=%d)\n", dev));
+	switch (dev & 0x0f) {
+	case SND_DEV_STATUS:
+	case SND_DEV_CTL:
+		break;
+		
+#ifdef CONFIG_SEQUENCER
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		sequencer_release(dev, &files[dev]);
+		break;
+#endif
 
-	sound_release_sw(dev, &files[dev]);
+#ifdef CONFIG_MIDI
+	case SND_DEV_MIDIN:
+		MIDIbuf_release(dev, &files[dev]);
+		break;
+#endif
+
+#ifdef CONFIG_AUDIO
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		audio_release(dev, &files[dev]);
+		break;
+#endif
+
+	default:
+		printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev);
+	}
+	in_use--;
 #ifdef MODULE
 	SOUND_DEC_USE_COUNT;
 #endif
 	return 0;
 }
 
-static int sound_ioctl(struct inode *inode, struct file *file,
-	    unsigned int cmd, unsigned long arg)
+static int get_mixer_info(int dev, caddr_t arg)
 {
-	int             dev, err;
-	int             len = 0;
-	int             alloced = 0;
-	char           *ptr = (char *) arg;
+	mixer_info info;
+	int i;
 
-	dev = MINOR(inode->i_rdev);
+	if (dev < 0 || dev >= num_mixers)
+		return -ENXIO;
+	strcpy(info.id, mixer_devs[dev]->id);
+	for (i = 0; i < 32 && mixer_devs[dev]->name; i++)
+		info.name[i] = mixer_devs[dev]->name[i];
+	info.name[i] = 0;
+	info.modify_counter = mixer_devs[dev]->modify_counter;
+	if (__copy_to_user(arg, &info,  sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
 
-	files[dev].flags = file->f_flags;
+static int get_old_mixer_info(int dev, caddr_t arg)
+{
+	_old_mixer_info info;
+	int             i;
 
-	if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0)
-	{
+	if (dev < 0 || dev >= num_mixers)
+		return -ENXIO;
+	strcpy(info.id, mixer_devs[dev]->id);
+	for (i = 0; i < 32 && mixer_devs[dev]->name; i++)
+		info.name[i] = mixer_devs[dev]->name[i];
+	info.name[i] = 0;
+	if (__copy_to_user(arg, &info,  sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int sound_mixer_ioctl(int mixdev, unsigned int cmd, caddr_t arg)
+{
+	if (cmd == SOUND_MIXER_INFO)
+		return get_mixer_info(mixdev, arg);
+	if (cmd == SOUND_OLD_MIXER_INFO)
+		return get_old_mixer_info(mixdev, arg);
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+		mixer_devs[mixdev]->modify_counter++;
+	if (!mixer_devs[mixdev]->ioctl)
+		return -EINVAL;
+	return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg);
+}
+
+static int sound_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	int err, len = 0, dtype, mixdev;
+	int dev = MINOR(inode->i_rdev);
+
+	files[dev].flags = file->f_flags;
+	if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0) {
 		/*
 		 * Have to validate the address given by the process.
 		 */
-
 		len = _SIOC_SIZE(cmd);
 		if (len < 1 || len > 65536 || arg == 0)
 			return -EFAULT;
-
-		ptr = vmalloc(len);
-		alloced = 1;
-		if (ptr == NULL)
-			return -EFAULT;
-
 		if (_SIOC_DIR(cmd) & _SIOC_WRITE)
-		{
-			if ((err = verify_area(VERIFY_READ, (void *) arg, len)) < 0)
+			if ((err = verify_area(VERIFY_READ, (void *)arg, len)) < 0)
 				return err;
-			copy_from_user(ptr, (char *) arg, len);
-		}
 		if (_SIOC_DIR(cmd) & _SIOC_READ)
-		{
-			if ((err = verify_area(VERIFY_WRITE, (void *) arg, len)) < 0)
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, len)) < 0)
 				return err;
-		}
 	}
-	err = sound_ioctl_sw(dev, &files[dev], cmd, (caddr_t) ptr);
+	DEB(printk("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
+	if (cmd == OSS_GETVERSION)
+		return __put_user(SOUND_VERSION, (int *)arg);
+	
+	if (((cmd >> 8) & 0xff) == 'M' && num_mixers > 0)	/* Mixer ioctl */
+		if ((dev & 0x0f) != SND_DEV_CTL) {
+			dtype = dev & 0x0f;
+			switch (dtype) {
+#ifdef CONFIG_AUDIO
+			case SND_DEV_DSP:
+			case SND_DEV_DSP16:
+			case SND_DEV_AUDIO:
+				mixdev = audio_devs[dev >> 4]->mixer_dev;
+				if (mixdev < 0 || mixdev >= num_mixers)
+					return -ENXIO;
+				return sound_mixer_ioctl(mixdev, cmd, (caddr_t)arg);
+#endif
+				
+			default:
+				return sound_mixer_ioctl(dev, cmd, (caddr_t)arg);
+			}
+		}
+	switch (dev & 0x0f) {
+	case SND_DEV_CTL:
+		if (cmd == SOUND_MIXER_GETLEVELS)
+			return get_mixer_levels((caddr_t)arg);
+		if (cmd == SOUND_MIXER_SETLEVELS)
+			return set_mixer_levels((caddr_t)arg);
+		if (!num_mixers)
+			return -ENXIO;
+		dev = dev >> 4;
+		if (dev >= num_mixers)
+			return -ENXIO;
+		return sound_mixer_ioctl(dev, cmd, (caddr_t)arg);
+		break;
+
+#ifdef CONFIG_SEQUENCER
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		return sequencer_ioctl(dev, &files[dev], cmd, (caddr_t)arg);
+#endif
 
-	if (_SIOC_DIR(cmd) & _SIOC_READ)
-		copy_to_user((char *) arg, ptr, len);
+#ifdef CONFIG_AUDIO
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		return audio_ioctl(dev, &files[dev], cmd, (caddr_t)arg);
+		break;
+#endif
 
-	if (ptr != NULL && alloced)
-		vfree(ptr);
+#ifdef CONFIG_MIDI
+	case SND_DEV_MIDIN:
+		return MIDIbuf_ioctl(dev, &files[dev], cmd, (caddr_t)arg);
+		break;
+#endif
 
-	return ((err < 0) ? err : 0);
+	}
+	return -EINVAL;
 }
 
 static int sound_select(struct inode *inode, struct file *file, int sel_type, poll_table * wait)
 {
-	int             dev;
-
-	dev = MINOR(inode->i_rdev);
+	int dev = MINOR(inode->i_rdev);
 
 	files[dev].flags = file->f_flags;
-
 	DEB(printk("sound_select(dev=%d, type=0x%x)\n", dev, sel_type));
-
-	switch (dev & 0x0f)
-	{
+	switch (dev & 0x0f) {
 #if defined(CONFIG_SEQUENCER) || defined(MODULE)
-		case SND_DEV_SEQ:
-		case SND_DEV_SEQ2:
-			return sequencer_select(dev, &files[dev], sel_type, wait);
-			break;
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		return sequencer_select(dev, &files[dev], sel_type, wait);
 #endif
 
 #if defined(CONFIG_MIDI)
-		case SND_DEV_MIDIN:
-			return MIDIbuf_select(dev, &files[dev], sel_type, wait);
-			break;
+	case SND_DEV_MIDIN:
+		return MIDIbuf_select(dev, &files[dev], sel_type, wait);
 #endif
 
 #if defined(CONFIG_AUDIO) || defined(MODULE)
-		case SND_DEV_DSP:
-		case SND_DEV_DSP16:
-		case SND_DEV_AUDIO:
-			return DMAbuf_select(dev >> 4, &files[dev], sel_type, wait);
-			break;
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		return DMAbuf_select(dev >> 4, &files[dev], sel_type, wait);
 #endif
-
-		default:
-			return 0;
 	}
-
 	return 0;
 }
 
 static unsigned int sound_poll(struct file *file, poll_table * wait)
 {
-	struct inode   *inode;
-	int             ret = 0;
+	struct inode *inode;
+	int ret = 0;
 
 	inode = file->f_dentry->d_inode;
-
 	if (sound_select(inode, file, SEL_IN, wait))
 		ret |= POLLIN;
 	if (sound_select(inode, file, SEL_OUT, wait))
@@ -250,11 +697,10 @@
 
 static int sound_mmap(struct file *file, struct vm_area_struct *vma)
 {
-	int             dev, dev_class;
-	unsigned long   size;
+	int dev_class;
+	unsigned long size;
 	struct dma_buffparms *dmap = NULL;
-
-	dev = MINOR(file->f_dentry->d_inode->i_rdev);
+	int dev = MINOR(file->f_dentry->d_inode->i_rdev);
 
 	files[dev].flags = file->f_flags;
 
@@ -418,7 +864,9 @@
 
 	if (sound_nblocks >= 1024)
 		printk(KERN_ERR "Sound warning: Deallocation table was too small.\n");
-
+	
+	if (proc_register(&proc_root, &proc_root_sound))
+		printk(KERN_ERR "sound: registering /proc/sound failed\n");
 	return 0;
 }
 
@@ -433,6 +881,8 @@
 	{
 		return;
 	}
+        if (proc_unregister(&proc_root, PROC_SOUND))
+		printk(KERN_ERR "sound: unregistering /proc/sound failed\n");
 	if (chrdev_registered)
 		unregister_chrdev(sound_major, "sound");
 
@@ -779,3 +1229,35 @@
 	}
 	printk("\n");
 }
+
+/*
+ *	Module and lock management
+ */
+ 
+struct notifier_block *sound_locker=(struct notifier_block *)0;
+static int lock_depth = 0;
+
+#define SOUND_INC_USE_COUNT	do { notifier_call_chain(&sound_locker, 1, 0); lock_depth++; } while(0);
+#define SOUND_DEC_USE_COUNT	do { notifier_call_chain(&sound_locker, 0, 0); lock_depth--; } while(0);
+
+/*
+ *	When a sound module is registered we need to bring it to the current
+ *	lock level...
+ */
+ 
+void sound_notifier_chain_register(struct notifier_block *bl)
+{
+	int ct=0;
+	
+	notifier_chain_register(&sound_locker, bl);
+	/*
+	 *	Normalise the lock count by calling the entry directly. We
+	 *	have to call the module as it owns its own use counter
+	 */
+	while(ct<lock_depth)
+	{
+		bl->notifier_call(bl, 1, 0);
+		ct++;
+	}
+}
+
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/soundmodule.h linux/drivers/sound/soundmodule.h
--- v2.1.76/linux/drivers/sound/soundmodule.h	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/soundmodule.h	Mon Dec 29 10:22:46 1997
@@ -5,19 +5,21 @@
 
 #include <linux/notifier.h>
 
-#ifdef SOUND_CORE
+extern struct notifier_block *sound_locker;
+extern void sound_notifier_chain_register(struct notifier_block *);
+extern int lock_depth;
 
-struct notifier_block *sound_locker=(struct notifier_block *)0;
+#ifdef SOUND_CORE
 
-#define SOUND_INC_USE_COUNT	notifier_call_chain(&sound_locker, 1, 0)
-#define SOUND_DEC_USE_COUNT	notifier_call_chain(&sound_locker, 0, 0)
+#define SOUND_INC_USE_COUNT	do { notifier_call_chain(&sound_locker, 1, 0); lock_depth++; } while(0);
+#define SOUND_DEC_USE_COUNT	do { notifier_call_chain(&sound_locker, 0, 0); lock_depth--; } while(0);
 
 #else
 
-#define SOUND_LOCK		notifier_chain_register(&sound_locker, &sound_notifier)
+
+#define SOUND_LOCK		sound_notifier_chain_register(&sound_notifier); 
 #define SOUND_LOCK_END		notifier_chain_unregister(&sound_locker, &sound_notifier)
 
-extern struct notifier_block *sound_locker;
 
 
 static int my_notifier_call(struct notifier_block *b, unsigned long foo, void *bar)
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/sscape.c linux/drivers/sound/sscape.c
--- v2.1.76/linux/drivers/sound/sscape.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/sscape.c	Tue Dec 30 10:59:17 1997
@@ -10,6 +10,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 #include <linux/module.h>
 
@@ -570,36 +573,31 @@
 	return 0;
 }
 
-static int
-sscape_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local)
+static int sscape_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local)
 {
+	copr_buffer *buf;
+	int err;
 
-	switch (cmd)
-	  {
-	  case SNDCTL_COPR_RESET:
-		  sscape_coproc_reset(dev_info);
-		  return 0;
-		  break;
-
-	  case SNDCTL_COPR_LOAD:
-		  {
-			  copr_buffer    *buf;
-			  int             err;
-
-			  buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
-			  if (buf == NULL)
-				  return -ENOSPC;
-			  memcpy((char *) buf, (&((char *) arg)[0]), sizeof(*buf));
-			  err = download_boot_block(dev_info, buf);
-			  vfree(buf);
-			  return err;
-		  }
-		  break;
-
-	  default:
-		  return -EINVAL;
-	  }
+	switch (cmd) {
+	case SNDCTL_COPR_RESET:
+		sscape_coproc_reset(dev_info);
+		return 0;
 
+	case SNDCTL_COPR_LOAD:
+		buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
+		if (buf == NULL)
+			return -ENOSPC;
+		if (__copy_from_user(buf, arg, sizeof(copr_buffer))) {
+			vfree(buf);
+			return -EFAULT;
+		}
+		err = download_boot_block(dev_info, buf);
+		vfree(buf);
+		return err;
+		
+	default:
+		return -EINVAL;
+	}
 }
 
 static coproc_operations sscape_coproc_operations =
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/sys_timer.c linux/drivers/sound/sys_timer.c
--- v2.1.76/linux/drivers/sound/sys_timer.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/sys_timer.c	Tue Dec 30 11:02:39 1997
@@ -11,6 +11,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 #include <linux/config.h>
 
 
@@ -184,94 +187,73 @@
 	return curr_ticks;
 }
 
-static int
-def_tmr_ioctl(int dev,
-	      unsigned int cmd, caddr_t arg)
+/* same as sound_timer.c:timer_ioctl!? */
+static int def_tmr_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
-	switch (cmd)
-	  {
-	  case SNDCTL_TMR_SOURCE:
-		  return (*(int *) arg = TMR_INTERNAL);
-		  break;
-
-	  case SNDCTL_TMR_START:
-		  tmr_reset();
-		  tmr_running = 1;
-		  return 0;
-		  break;
-
-	  case SNDCTL_TMR_STOP:
-		  tmr_running = 0;
-		  return 0;
-		  break;
-
-	  case SNDCTL_TMR_CONTINUE:
-		  tmr_running = 1;
-		  return 0;
-		  break;
-
-	  case SNDCTL_TMR_TIMEBASE:
-		  {
-			  int             val;
-
-			  val = *(int *) arg;
-
-			  if (val)
-			    {
-				    if (val < 1)
-					    val = 1;
-				    if (val > 1000)
-					    val = 1000;
-				    curr_timebase = val;
-			    }
-			  return (*(int *) arg = curr_timebase);
-		  }
-		  break;
-
-	  case SNDCTL_TMR_TEMPO:
-		  {
-			  int             val;
-
-			  val = *(int *) arg;
-
-			  if (val)
-			    {
-				    if (val < 8)
-					    val = 8;
-				    if (val > 250)
-					    val = 250;
-				    tmr_offs = tmr_ctr;
-				    ticks_offs += tmr2ticks(tmr_ctr);
-				    tmr_ctr = 0;
-				    curr_tempo = val;
-			    }
-			  return (*(int *) arg = curr_tempo);
-		  }
-		  break;
-
-	  case SNDCTL_SEQ_CTRLRATE:
-		  {
-			  int             val;
-
-			  val = *(int *) arg;
-			  if (val != 0)		/* Can't change */
-				  return -EINVAL;
+	int val;
 
-			  return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
-		  }
-		  break;
-
-	  case SNDCTL_SEQ_GETTIME:
-		  return (*(int *) arg = curr_ticks);
-		  break;
+	switch (cmd) {
+	case SNDCTL_TMR_SOURCE:
+		return __put_user(TMR_INTERNAL, (int *)arg);
+
+	case SNDCTL_TMR_START:
+		tmr_reset();
+		tmr_running = 1;
+		return 0;
 
-	  case SNDCTL_TMR_METRONOME:
-		  /* NOP */
-		  break;
+	case SNDCTL_TMR_STOP:
+		tmr_running = 0;
+		return 0;
 
-	  default:;
-	  }
+	case SNDCTL_TMR_CONTINUE:
+		tmr_running = 1;
+		return 0;
 
+	case SNDCTL_TMR_TIMEBASE:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val) {
+			if (val < 1)
+				val = 1;
+			if (val > 1000)
+				val = 1000;
+			curr_timebase = val;
+		}
+		return __put_user(curr_timebase, (int *)arg);
+
+	case SNDCTL_TMR_TEMPO:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val) {
+			if (val < 8)
+				val = 8;
+			if (val > 250)
+				val = 250;
+			tmr_offs = tmr_ctr;
+			ticks_offs += tmr2ticks(tmr_ctr);
+			tmr_ctr = 0;
+			curr_tempo = val;
+			reprogram_timer();
+		}
+		return __put_user(curr_tempo, (int *)arg);
+
+	case SNDCTL_SEQ_CTRLRATE:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val != 0)	/* Can't change */
+			return -EINVAL;
+		val = ((curr_tempo * curr_timebase) + 30) / 60;
+		return __put_user(val, (int *)arg);
+		
+	case SNDCTL_SEQ_GETTIME:
+		return __put_user(curr_ticks, (int *)arg);
+		
+	case SNDCTL_TMR_METRONOME:
+		/* NOP */
+		break;
+		
+	default:;
+	}
 	return -EINVAL;
 }
 
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/uart401.c linux/drivers/sound/uart401.c
--- v2.1.76/linux/drivers/sound/uart401.c	Tue Dec 23 16:31:00 1997
+++ linux/drivers/sound/uart401.c	Tue Dec 30 10:59:17 1997
@@ -186,12 +186,6 @@
 	return 0;
 }
 
-static int
-uart401_ioctl(int dev, unsigned cmd, caddr_t arg)
-{
-	return -EINVAL;
-}
-
 static void
 uart401_kick(int dev)
 {
@@ -214,7 +208,7 @@
 	{0},
 	uart401_open,
 	uart401_close,
-	uart401_ioctl,
+	NULL, /* ioctl */
 	uart401_out,
 	uart401_start_read,
 	uart401_end_read,
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/uart6850.c linux/drivers/sound/uart6850.c
--- v2.1.76/linux/drivers/sound/uart6850.c	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/uart6850.c	Tue Dec 30 10:59:17 1997
@@ -1,6 +1,3 @@
-
-
-
 /*
  * sound/uart6850.c
  */
@@ -226,12 +223,6 @@
 	return 0;
 }
 
-static int
-uart6850_ioctl(int dev, unsigned cmd, caddr_t arg)
-{
-	return -EINVAL;
-}
-
 static void
 uart6850_kick(int dev)
 {
@@ -256,7 +247,7 @@
 	{0},
 	uart6850_open,
 	uart6850_close,
-	uart6850_ioctl,
+	NULL, /* ioctl */
 	uart6850_out,
 	uart6850_start_read,
 	uart6850_end_read,
diff -u --recursive --new-file v2.1.76/linux/drivers/sound/v_midi.c linux/drivers/sound/v_midi.c
--- v2.1.76/linux/drivers/sound/v_midi.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/sound/v_midi.c	Tue Dec 30 10:59:17 1997
@@ -1,5 +1,5 @@
 /*
- * sound/sb_dsp.c
+ * sound/v_midi.c
  *
  * The low level driver for the Sound Blaster DS chips.
  */
@@ -9,6 +9,7 @@
  * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
+ * ??
  */
 #include <linux/config.h>
 #include <linux/module.h>
@@ -146,10 +147,10 @@
   return 0;
 }
 
-static int
-v_midi_ioctl (int dev, unsigned cmd, caddr_t arg)
+/* why -EPERM and not -EINVAL?? */
+static int v_midi_ioctl (int dev, unsigned cmd, caddr_t arg)
 {
-  return -(EPERM);
+	return -EPERM;
 }
 
 
diff -u --recursive --new-file v2.1.76/linux/fs/exec.c linux/fs/exec.c
--- v2.1.76/linux/fs/exec.c	Tue Dec  2 16:45:20 1997
+++ linux/fs/exec.c	Fri Jan  2 11:01:40 1998
@@ -518,7 +518,7 @@
 	 * Release all of the old mmap stuff
 	 */
 	retval = exec_mmap();
-	if (retval) goto flush_failed;
+	if (retval) goto mmap_failed;
 
 	/* This is the point of no return */
 	release_old_signals(oldsig);
@@ -546,6 +546,9 @@
 
 	return 0;
 
+mmap_failed:
+	if (current->sig != oldsig)
+		kfree(current->sig);
 flush_failed:
 	current->sig = oldsig;
 	return retval;
diff -u --recursive --new-file v2.1.76/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.1.76/linux/fs/nfs/dir.c	Tue Dec 23 16:31:00 1997
+++ linux/fs/nfs/dir.c	Fri Jan  2 11:41:04 1998
@@ -411,7 +411,7 @@
 		int error;
 		
 		dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
-#ifdef NFS_DEBUG
+#ifdef NFS_DEBUG_VERBOSE
 printk("nfs_dentry_delete: unlinking %s/%s\n",
 dentry->d_parent->d_name.name, dentry->d_name.name);
 #endif
@@ -647,34 +647,21 @@
 	if (dentry->d_name.len > NFS_MAXNAMLEN)
 		goto out;
 
-	/* For some reason mode doesn't have the S_IFDIR flag ... */
-	mode |= S_IFDIR;
-	sattr.mode = mode;
+	sattr.mode = mode | S_IFDIR;
 	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
 
 	nfs_invalidate_dircache(dir);
 	error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir),
 				dentry->d_name.name, &sattr, &fhandle, &fattr);
-	if (!error) {
-		/*
-		 * Some AIX servers reportedly fail to fill out the fattr.
-		 * Check for a bad mode value and complain, then drop the
-		 * dentry to force a new lookup.
-		 */
-		if (!S_ISDIR(fattr.mode)) {
-			static int complain = 0;
-			if (!complain++)
-				printk("NFS: buggy server! fattr mode=%x\n",
-					fattr.mode);
-			goto drop;
-		}
-		error = nfs_instantiate(dentry, &fhandle, &fattr);
-	}
-	if (error) {
-	drop:
-		d_drop(dentry);
-	}
+
+	/*
+	 * Always drop the dentry, we can't always depend on
+	 * the fattr returned by the server (AIX seems to be
+	 * broken). We're better off doing another lookup than
+	 * depending on potentially bogus information.
+	 */
+	d_drop(dentry);
 out:
 	return error;
 }
@@ -1109,10 +1096,8 @@
 	 * First check whether the target is busy ... we can't
 	 * safely do _any_ rename if the target is in use.
 	 */
-	if (new_dentry->d_count > 1) {
-		if (new_inode && S_ISDIR(new_inode->i_mode))
-			shrink_dcache_parent(new_dentry);
-	}
+	if (new_dentry->d_count > 1 && !list_empty(&new_dentry->d_subdirs))
+		shrink_dcache_parent(new_dentry);
 	error = -EBUSY;
 	if (new_dentry->d_count > 1) {
 #ifdef NFS_PARANOIA
@@ -1176,28 +1161,33 @@
 
 do_rename:
 	/*
-	 * We must prevent any new references to the target while
-	 * the rename is in progress, so we unhash the dentry.
+	 * To prevent any new references to the target during the rename,
+	 * we unhash the dentry and free the inode in advance.
 	 */
+#ifdef NFS_PARANOIA
+if (new_inode && 
+    new_inode->i_count > (S_ISDIR(new_inode->i_mode) ? 1 : new_inode->i_nlink))
+printk("nfs_rename: %s/%s inode busy?? i_count=%d, i_nlink=%d\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+new_inode->i_count, new_inode->i_nlink);
+#endif
 	if (!list_empty(&new_dentry->d_hash)) {
 		d_drop(new_dentry);
 		rehash = update;
 	}
+	if (new_inode) {
+		d_delete(new_dentry);
+	}
+
 	nfs_invalidate_dircache(new_dir);
 	nfs_invalidate_dircache(old_dir);
 	error = nfs_proc_rename(NFS_SERVER(old_dir),
 				NFS_FH(old_dir), old_dentry->d_name.name,
 				NFS_FH(new_dir), new_dentry->d_name.name);
-	if (rehash) {
-		d_add(new_dentry, new_inode);
-	}
-#ifdef NFS_PARANOIA
-if (new_dentry->d_count > 1)
-printk("nfs_rename: %s/%s busy after rename, d_count=%d\n",
-new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
-#endif
 	if (!error) {
 		/* Update the dcache if needed */
+		if (rehash)
+			d_add(new_dentry, NULL);
 		if (update)
 			d_move(old_dentry, new_dentry);
 	}
diff -u --recursive --new-file v2.1.76/linux/fs/nls/Config.in linux/fs/nls/Config.in
--- v2.1.76/linux/fs/nls/Config.in	Sun Dec 21 16:17:45 1997
+++ linux/fs/nls/Config.in	Mon Dec 29 10:22:46 1997
@@ -2,9 +2,6 @@
 # Native language support configuration
 #
 
-mainmenu_option next_comment
-comment 'Native Language Support'
-
 # msdos and Joliet want NLS
 if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" \
 	-o "$CONFIG_NTFS_FS" != "n" ]; then
@@ -14,6 +11,8 @@
 fi
 
 if [ "$CONFIG_NLS" = "y" ]; then
+  mainmenu_option next_comment
+  comment 'Native Language Support'
   tristate 'Codepage 437'      CONFIG_NLS_CODEPAGE_437
   tristate 'Codepage 737'      CONFIG_NLS_CODEPAGE_737
   tristate 'Codepage 775'      CONFIG_NLS_CODEPAGE_775
@@ -40,6 +39,5 @@
   tristate 'NLS ISO 8859-8'    CONFIG_NLS_ISO8859_8
   tristate 'NLS ISO 8859-9'    CONFIG_NLS_ISO8859_9
   tristate 'NLS KOI8-R'        CONFIG_NLS_KOI8_R
+  endmenu
 fi
-
-endmenu
diff -u --recursive --new-file v2.1.76/linux/fs/ntfs/Makefile linux/fs/ntfs/Makefile
--- v2.1.76/linux/fs/ntfs/Makefile	Sun Dec 21 16:17:45 1997
+++ linux/fs/ntfs/Makefile	Fri Jan  2 01:42:58 1998
@@ -3,7 +3,7 @@
 O_TARGET := ntfs.o
 O_OBJS   := fs.o sysctl.o support.o util.o inode.o dir.o super.o attr.o
 M_OBJS   := $(O_TARGET)
-EXTRA_CFLAGS = -DNTFS_IN_LINUX_KERNEL -DNTFS_VERSION=\"971219\"
+EXTRA_CFLAGS = -DNTFS_IN_LINUX_KERNEL -DNTFS_VERSION=\"980101\"
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.1.76/linux/fs/ntfs/dir.c linux/fs/ntfs/dir.c
--- v2.1.76/linux/fs/ntfs/dir.c	Sun Dec 21 16:17:45 1997
+++ linux/fs/ntfs/dir.c	Fri Jan  2 01:42:58 1998
@@ -731,6 +731,7 @@
 		if(buf[byte] & bit)
 			break;
 	}
+	ntfs_free(buf);
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.76/linux/fs/ntfs/fs.c linux/fs/ntfs/fs.c
--- v2.1.76/linux/fs/ntfs/fs.c	Sun Dec 21 16:17:45 1997
+++ linux/fs/ntfs/fs.c	Fri Jan  2 01:42:59 1998
@@ -468,6 +468,9 @@
 	/* It's not a directory */
 	r->i_op=&ntfs_inode_operations_nobmap;
 	r->i_mode=S_IFREG|S_IRUGO;
+#ifdef CONFIG_NTFS_RW
+	r->i_mode|=S_IWUGO;
+#endif
 	r->i_mode &= ~vol->umask;
 
 	d_instantiate(d,r);
@@ -654,9 +657,12 @@
 	{
 		inode->i_op=can_mmap ? &ntfs_inode_operations : 
 			&ntfs_inode_operations_nobmap;
-		inode->i_mode=S_IFREG|S_IRUGO|S_IMMUTABLE;
 		inode->i_mode=S_IFREG|S_IRUGO;
 	}
+#ifdef CONFIG_NTFS_RW
+	if(!data || !data->compressed)
+		inode->i_mode|=S_IWUGO;
+#endif
 	inode->i_mode &= ~vol->umask;
 }
 
@@ -873,7 +879,9 @@
 	unlock_super(sb);
 	ntfs_debug(DEBUG_OTHER, "unlock_super\n");
 ntfs_read_super_vol:
+	#ifndef NTFS_IN_LINUX_KERNEL
 	ntfs_free(vol);
+	#endif
 ntfs_read_super_dec:
 	ntfs_debug(DEBUG_OTHER, "read_super: done\n");
 	MOD_DEC_USE_COUNT;
diff -u --recursive --new-file v2.1.76/linux/fs/ntfs/inode.c linux/fs/ntfs/inode.c
--- v2.1.76/linux/fs/ntfs/inode.c	Sun Dec 21 16:17:45 1997
+++ linux/fs/ntfs/inode.c	Fri Jan  2 01:42:59 1998
@@ -850,8 +850,7 @@
 	{
 		head = NTFS_GETU16(src) & 0xFFF;
 		/* high bit indicates that compression was performed */
-		comp = NTFS_GETU8(src) & 0x80;
-		comp = (head == 0xFFF);
+		comp = NTFS_GETU16(src) & 0x8000;
 		src += 2;
 		stop = src+head;
 		bits = 0;
diff -u --recursive --new-file v2.1.76/linux/fs/ntfs/support.c linux/fs/ntfs/support.c
--- v2.1.76/linux/fs/ntfs/support.c	Sun Dec 21 16:17:45 1997
+++ linux/fs/ntfs/support.c	Fri Jan  2 01:42:59 1998
@@ -43,6 +43,7 @@
 	}
 }
 
+#ifndef ntfs_malloc
 /* Verbose kmalloc */
 void *ntfs_malloc(int size)
 {
@@ -53,27 +54,34 @@
 
 	return ret;
 }
+#endif
 
+#ifndef ntfs_free
 /* Verbose kfree() */
 void ntfs_free(void *block)
 {
         ntfs_debug(DEBUG_MALLOC, "Freeing memory at %p\n", block);
 	kfree(block);
 }
+#endif
 #else
 void ntfs_debug(int mask, const char *fmt, ...)
 {
 }
 
+#ifndef ntfs_malloc
 void *ntfs_malloc(int size)
 {
 	return kmalloc(size, GFP_KERNEL);
 }
+#endif
 
+#ifndef ntfs_free
 void ntfs_free(void *block)
 {
 	kfree(block);
 }
+#endif
 #endif /* DEBUG */
 
 void ntfs_bzero(void *s, int n)
diff -u --recursive --new-file v2.1.76/linux/fs/ntfs/support.h linux/fs/ntfs/support.h
--- v2.1.76/linux/fs/ntfs/support.h	Sun Dec 21 16:17:45 1997
+++ linux/fs/ntfs/support.h	Fri Jan  2 01:42:59 1998
@@ -10,8 +10,14 @@
 #define DEBUG_MALLOC	2
 
 void ntfs_debug(int mask, const char *fmt, ...);
+#ifdef NTFS_IN_LINUX_KERNEL
+#include <linux/slab.h>
+#define ntfs_malloc(size)  kmalloc(size,GFP_KERNEL)
+#define ntfs_free(ptr)     kfree(ptr)
+#else
 void *ntfs_malloc(int size);
 void ntfs_free(void *block);
+#endif
 void ntfs_bzero(void *s, int n);
 void *ntfs_memcpy(void *dest, const void *src, ntfs_size_t n);
 void *ntfs_memmove(void *dest, const void *src, ntfs_size_t n);
diff -u --recursive --new-file v2.1.76/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.1.76/linux/fs/proc/array.c	Wed Dec 10 11:12:45 1997
+++ linux/fs/proc/array.c	Fri Jan  2 13:58:11 1998
@@ -427,6 +427,14 @@
 	return get_array(p, p->mm->arg_start, p->mm->arg_end, buffer);
 }
 
+/*
+ * These bracket the sleeping functions..
+ */
+extern void scheduling_functions_start_here(void);
+extern void scheduling_functions_end_here(void);
+#define first_sched	((unsigned long) scheduling_functions_start_here)
+#define last_sched	((unsigned long) scheduling_functions_end_here)
+
 static unsigned long get_wchan(struct task_struct *p)
 {
 	if (!p || p == current || p->state == TASK_RUNNING)
@@ -445,8 +453,7 @@
 			if (ebp < stack_page || ebp >= 4092+stack_page)
 				return 0;
 			eip = *(unsigned long *) (ebp+4);
-			if (eip < (unsigned long) interruptible_sleep_on
-			    || eip >= (unsigned long) add_timer)
+			if (eip < first_sched || eip >= last_sched)
 				return eip;
 			ebp = *(unsigned long *) ebp;
 		} while (count++ < 16);
@@ -466,7 +473,7 @@
 	    unsigned long pc;
 
 	    pc = thread_saved_pc(&p->tss);
-	    if (pc >= (unsigned long) interruptible_sleep_on && pc < (unsigned long) add_timer) {
+	    if (pc >= first_sched && pc < last_sched) {
 		schedule_frame = ((unsigned long *)p->tss.ksp)[6];
 		return ((unsigned long *)schedule_frame)[12];
 	    }
@@ -488,10 +495,7 @@
 			    return 0;
 		    pc = ((unsigned long *)fp)[1];
 		/* FIXME: This depends on the order of these functions. */
-		    if ((pc < (unsigned long) __down
-		         || pc >= (unsigned long) add_timer)
-		        && (pc < (unsigned long) schedule
-			    || pc >= (unsigned long) sys_pause))
+		    if (pc < first_sched || pc >= last_sched)
 		      return pc;
 		    fp = *(unsigned long *) fp;
 	    } while (count++ < 16);
diff -u --recursive --new-file v2.1.76/linux/fs/vfat/namei.c linux/fs/vfat/namei.c
--- v2.1.76/linux/fs/vfat/namei.c	Tue Dec 23 16:31:00 1997
+++ linux/fs/vfat/namei.c	Mon Dec 29 08:45:27 1997
@@ -173,6 +173,9 @@
 
 	len = qstr->len;
 	name = qstr->name;
+	while (len && name[len-1] == '.')
+		len--;
+
 	hash = init_name_hash();
 	while (len--) {
 		c = tolower(*name++);
@@ -193,14 +196,12 @@
 	/* A filename cannot end in '.' or we treat it like it has none */
 	alen = a->len;
 	blen = b->len;
-	if (alen != blen) {
-		if (a->name[alen-1] == '.')
-			alen--;
-		if (b->name[blen-1] == '.')
-			blen--;
-		if (alen != blen)
-			return 1;
-	}
+	while (alen && a->name[alen-1] == '.')
+		alen--;
+	while (blen && b->name[blen-1] == '.')
+		blen--;
+	if (alen != blen)
+		return 1;
 
 	return strnicmp(a->name, b->name, alen);
 }
@@ -966,11 +967,8 @@
 			  vf->name, vf->len, name, name_len);
 #endif
 
-	/* Filenames cannot end in '.' or we treat like it has none */
 	if (vf->len != name_len) {
-		if ((vf->len != name_len + 1) || (vf->name[name_len] != '.')) {
-			return 0;
-		}
+		return 0;
 	}
 
 	s1 = name; s2 = vf->name;
diff -u --recursive --new-file v2.1.76/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- v2.1.76/linux/include/asm-i386/processor.h	Sun Dec 21 22:36:16 1997
+++ linux/include/asm-i386/processor.h	Tue Dec 30 07:58:00 1997
@@ -172,7 +172,7 @@
 	_LDT(0),0, \
 	0, 0x8000, \
 	{~0, }, /* ioperm */ \
-	_TSS(0), 0, 0, 0, KERNEL_DS, \
+	_TSS(0), 0, 0, 0, (mm_segment_t) { 0 } /* obsolete */ , \
 	{ { 0, }, },  /* 387 state */ \
 	NULL, 0, 0, 0, 0, 0 /* vm86_info */, \
 }
@@ -180,7 +180,7 @@
 #define start_thread(regs, new_eip, new_esp) do {\
 	unsigned long seg = __USER_DS; \
 	__asm__("mov %w0,%%fs ; mov %w0,%%gs":"=r" (seg) :"0" (seg)); \
-	set_fs(MAKE_MM_SEG(seg)); \
+	set_fs(USER_DS); \
 	regs->xds = seg; \
 	regs->xes = seg; \
 	regs->xss = seg; \
diff -u --recursive --new-file v2.1.76/linux/include/asm-i386/uaccess.h linux/include/asm-i386/uaccess.h
--- v2.1.76/linux/include/asm-i386/uaccess.h	Sun Dec 21 22:36:16 1997
+++ linux/include/asm-i386/uaccess.h	Fri Jan  2 14:01:29 1998
@@ -18,49 +18,42 @@
  */
 
 #define MAKE_MM_SEG(s)	((mm_segment_t) { (s) })
-#define KERNEL_DS	MAKE_MM_SEG(0)
-#define USER_DS		MAKE_MM_SEG(3)
+
+
+#define KERNEL_DS	MAKE_MM_SEG(0xFFFFFFFF)
+#define USER_DS		MAKE_MM_SEG(0xC0000000)
 
 #define get_ds()	(KERNEL_DS)
-#define get_fs()	(current->tss.segment)
-#define set_fs(x)	(current->tss.segment = (x))
+#define get_fs()	(current->addr_limit)
+#define set_fs(x)	(current->addr_limit = (x))
 
 #define segment_eq(a,b)	((a).seg == (b).seg)
 
+extern int __verify_write(const void *, unsigned long);
+
+#define __addr_ok(addr) ((unsigned long)(addr) < (current->addr_limit.seg))
 
 /*
- * Address Ok:
- *
- *				     segment
- *			00 (kernel)		11 (user)
- *
- * high		00	1			1
- * two 		01	1			1
- * bits of	10	1			1
- * address	11	1			0
+ * Uhhuh, this needs 33-bit arithmetic. We have a carry..
  */
-#define __addr_ok(x) \
-	((((unsigned long)(x)>>30)&get_fs().seg) != 3)
+#define __range_ok(addr,size) ({ \
+	unsigned long flag,sum; \
+	asm("addl %3,%1 ; sbbl %0,%0; cmpl %1,%4; sbbl $0,%0" \
+		:"=&r" (flag), "=r" (sum) \
+		:"1" (addr),"g" (size),"g" (current->addr_limit.seg)); \
+	flag; })
 
-#define __user_ok(addr,size) \
-	((size <= 0xC0000000UL) && (addr <= 0xC0000000UL - size))
-#define __kernel_ok \
-	(!get_fs().seg)
+#if CPU > 386
 
-extern int __verify_write(const void *, unsigned long);
+#define access_ok(type,addr,size) (__range_ok(addr,size) == 0)
 
-#if CPU > 386
-#define __access_ok(type,addr,size) \
-	(__kernel_ok || __user_ok(addr,size))
 #else
-#define __access_ok(type,addr,size) \
-	(__kernel_ok || (__user_ok(addr,size) && \
+
+#define access_ok(type,addr,size) ( (__range_ok(addr,size) == 0) && \
 			 ((type) == VERIFY_READ || boot_cpu_data.wp_works_ok || \
-			  __verify_write((void *)(addr),(size)))))
-#endif /* CPU */
+			  __verify_write((void *)(addr),(size))))
 
-#define access_ok(type,addr,size) \
-	__access_ok((type),(unsigned long)(addr),(size))
+#endif /* CPU */
 
 extern inline int verify_area(int type, const void * addr, unsigned long size)
 {
diff -u --recursive --new-file v2.1.76/linux/include/linux/baycom.h linux/include/linux/baycom.h
--- v2.1.76/linux/include/linux/baycom.h	Mon Dec  1 12:04:14 1997
+++ linux/include/linux/baycom.h	Wed Dec 31 11:54:49 1997
@@ -1,14 +1,11 @@
 /*
  * The Linux BAYCOM driver for the Baycom serial 1200 baud modem
  * and the parallel 9600 baud modem
- * (C) 1997 by Thomas Sailer, HB9JNX/AE4WA
+ * (C) 1997-1998 by Thomas Sailer, HB9JNX/AE4WA
  */
 
 #ifndef _BAYCOM_H
 #define _BAYCOM_H
-
-#include <linux/sockios.h>
-#include <linux/if_ether.h>
 
 /* -------------------------------------------------------------------- */
 /*
diff -u --recursive --new-file v2.1.76/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v2.1.76/linux/include/linux/cdrom.h	Tue Dec  2 16:45:20 1997
+++ linux/include/linux/cdrom.h	Wed Dec 31 11:34:16 1997
@@ -352,6 +352,7 @@
 #define CDS_DATA_2		102
 #define CDS_XA_2_1		103
 #define CDS_XA_2_2		104
+#define CDS_MIXED		105
 
 /* User-configurable behavior options for the uniform CD-ROM driver */
 #define CDO_AUTO_CLOSE		0x1     /* close tray on first open() */
diff -u --recursive --new-file v2.1.76/linux/include/linux/genhd.h linux/include/linux/genhd.h
--- v2.1.76/linux/include/linux/genhd.h	Mon Jul  7 16:02:01 1997
+++ linux/include/linux/genhd.h	Fri Jan  2 14:01:29 1998
@@ -21,10 +21,11 @@
 #define CONFIG_SUN_PARTITION 1
 #endif
 
-/* These two have identical behaviour; use the second one if DOS fdisk gets
+/* These three have identical behaviour; use the second one if DOS fdisk gets
    confused about extended/logical partitions starting past cylinder 1023. */
 #define DOS_EXTENDED_PARTITION 5
 #define LINUX_EXTENDED_PARTITION 0x85
+#define WIN98_EXTENDED_PARTITION 0x0f
 
 #define DM6_PARTITION		0x54	/* has DDO: use xlated geom & offset */
 #define EZD_PARTITION		0x55	/* EZ-DRIVE:  same as DM6 (we think) */
diff -u --recursive --new-file v2.1.76/linux/include/linux/hdlcdrv.h linux/include/linux/hdlcdrv.h
--- v2.1.76/linux/include/linux/hdlcdrv.h	Mon Aug 11 14:47:05 1997
+++ linux/include/linux/hdlcdrv.h	Wed Dec 31 11:53:52 1997
@@ -1,20 +1,12 @@
 /*
  * hdlcdrv.h  -- HDLC packet radio network driver.
  * The Linux soundcard driver for 1200 baud and 9600 baud packet radio
- * (C) 1996 by Thomas Sailer, HB9JNX/AE4WA
+ * (C) 1996-1998 by Thomas Sailer, HB9JNX/AE4WA
  */
 
 #ifndef _HDLCDRV_H
 #define _HDLCDRV_H
 
-#include <linux/version.h>
-#include <linux/sockios.h>
-#include <linux/version.h>
-#if LINUX_VERSION_CODE < 0x20119
-#include <linux/if_ether.h>
-#endif
-#include <linux/netdevice.h>
-
 /* -------------------------------------------------------------------- */
 /*
  * structs for the IOCTL commands
@@ -43,9 +35,6 @@
   	int ptt;
   	int dcd;
   	int ptt_keyed;
-#if LINUX_VERSION_CODE < 0x20100
-  	struct enet_statistics stats;
-#endif
 };
 
 struct hdlcdrv_channel_state {
@@ -114,6 +103,9 @@
 /* -------------------------------------------------------------------- */
 
 #ifdef __KERNEL__
+
+#include <linux/netdevice.h>
+#include <linux/if.h>
 
 #define HDLCDRV_MAGIC      0x5ac6e778
 #define HDLCDRV_IFNAMELEN    6
diff -u --recursive --new-file v2.1.76/linux/include/linux/if.h linux/include/linux/if.h
--- v2.1.76/linux/include/linux/if.h	Sat Nov 29 11:25:12 1997
+++ linux/include/linux/if.h	Tue Dec 30 11:40:15 1997
@@ -132,6 +132,7 @@
 #define	ifr_data	ifr_ifru.ifru_data	/* for use by interface	*/
 #define ifr_ifindex	ifr_ifru.ifru_ivalue	/* interface index	*/
 #define ifr_bandwidth	ifr_ifru.ifru_ivalue    /* link bandwidth	*/
+#define ifr_qlen	ifr_ifru.ifru_ivalue	/* Queue length 	*/
 
 /*
  * Structure used in SIOCGIFCONF request.
diff -u --recursive --new-file v2.1.76/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.1.76/linux/include/linux/proc_fs.h	Fri Dec 19 15:53:05 1997
+++ linux/include/linux/proc_fs.h	Fri Jan  2 14:01:30 1998
@@ -52,7 +52,8 @@
 	PROC_SLABINFO,
 	PROC_PARPORT,
 	PROC_OMIRR, /* whether enabled or not */
-	PROC_PPC_HTAB
+	PROC_PPC_HTAB,
+	PROC_SOUND
 };
 
 enum pid_directory_inos {
diff -u --recursive --new-file v2.1.76/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.1.76/linux/include/linux/sched.h	Sun Dec 21 22:36:17 1997
+++ linux/include/linux/sched.h	Fri Jan  2 14:01:29 1998
@@ -182,6 +182,10 @@
 	long counter;
 	long priority;
 	unsigned long flags;	/* per process flags, defined below */
+	mm_segment_t addr_limit;	/* thread address space:
+					 	0-0xBFFFFFFF for user-thead
+						0-0xFFFFFFFF for kernel-thread
+					 */
 	int sigpending;
 	long debugreg[8];  /* Hardware debugging registers */
 	struct exec_domain *exec_domain;
@@ -307,7 +311,7 @@
  * your own risk!. Base=0, limit=0x1fffff (=2MB)
  */
 #define INIT_TASK \
-/* state etc */	{ 0,DEF_PRIORITY,DEF_PRIORITY,0,0, \
+/* state etc */	{ 0,DEF_PRIORITY,DEF_PRIORITY,0,KERNEL_DS,0, \
 /* debugregs */ { 0, },            \
 /* exec domain */&default_exec_domain, \
 /* binfmt */	NULL, \
diff -u --recursive --new-file v2.1.76/linux/include/linux/sockios.h linux/include/linux/sockios.h
--- v2.1.76/linux/include/linux/sockios.h	Mon Dec  1 12:04:15 1997
+++ linux/include/linux/sockios.h	Mon Dec 29 10:22:46 1997
@@ -45,8 +45,8 @@
 #define SIOCSIFMEM	0x8920		/* set memory address (BSD)	*/
 #define SIOCGIFMTU	0x8921		/* get MTU size			*/
 #define SIOCSIFMTU	0x8922		/* set MTU size			*/
-#define	SIOCSIFHWADDR	0x8924		/* set hardware address (NI)	*/
-#define SIOCGIFENCAP	0x8925		/* get/set slip encapsulation   */
+#define	SIOCSIFHWADDR	0x8924		/* set hardware address 	*/
+#define SIOCGIFENCAP	0x8925		/* get/set encapsulations       */
 #define SIOCSIFENCAP	0x8926		
 #define SIOCGIFHWADDR	0x8927		/* Get hardware address		*/
 #define SIOCGIFSLAVE	0x8929		/* Driver slaving support	*/
@@ -63,6 +63,10 @@
 
 #define SIOCGIFBR	0x8940		/* Bridging support		*/
 #define SIOCSIFBR	0x8941		/* Set bridging options 	*/
+
+#define SIOCGIFTXQLEN	0x8942		/* Get the tx queue length	*/
+#define SIOCSIFTXQLEN	0x8943		/* Set the tx queue length 	*/
+
 
 /* ARP cache control calls. */
 		    /*  0x8950 - 0x8952  * obsolete calls, don't re-use */
diff -u --recursive --new-file v2.1.76/linux/include/linux/soundmodem.h linux/include/linux/soundmodem.h
--- v2.1.76/linux/include/linux/soundmodem.h	Wed Dec 18 23:51:48 1996
+++ linux/include/linux/soundmodem.h	Wed Dec 31 11:56:24 1997
@@ -1,13 +1,10 @@
 /*
  * The Linux soundcard driver for 1200 baud and 9600 baud packet radio
- * (C) 1996 by Thomas Sailer, HB9JNX/AE4WA
+ * (C) 1996-1998 by Thomas Sailer, HB9JNX/AE4WA
  */
 
 #ifndef _SOUNDMODEM_H
 #define _SOUNDMODEM_H
-
-#include <linux/sockios.h>
-#include <linux/if_ether.h>
 
 /* -------------------------------------------------------------------- */
 /*
diff -u --recursive --new-file v2.1.76/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.76/linux/include/net/sock.h	Sun Dec 21 22:36:17 1997
+++ linux/include/net/sock.h	Fri Jan  2 14:02:14 1998
@@ -892,7 +892,7 @@
 	if (sk->filter)
 	{
 		if (sk_filter(skb, sk->filter_data, sk->filter))
-			return -1;	/* Toss packet */
+			return -EPERM;	/* Toss packet */
 	}
 #endif /* CONFIG_FILTER */
 
diff -u --recursive --new-file v2.1.76/linux/init/main.c linux/init/main.c
--- v2.1.76/linux/init/main.c	Sun Dec 21 22:36:17 1997
+++ linux/init/main.c	Sun Dec 28 12:05:45 1997
@@ -109,8 +109,14 @@
 #ifdef CONFIG_BLK_DEV_IDE
 extern void ide_setup(char *);
 #endif
-#ifdef CONFIG_BLK_DEV_EZ
-extern void ez_setup(char *str, int *ints);
+#ifdef CONFIG_PARIDE_PD
+extern void pd_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_PARIDE_PF
+extern void pf_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_PARIDE_PCD
+extern void pcd_setup(char *str, int *ints);
 #endif
 extern void floppy_setup(char *str, int *ints);
 extern void st_setup(char *str, int *ints);
@@ -165,9 +171,6 @@
 #ifdef CONFIG_GSCD
 extern void gscd_setup(char *str, int *ints);
 #endif CONFIG_GSCD
-#ifdef CONFIG_BPCD
-extern void bpcd_setup(char *str, int *ints);
-#endif CONFIG_BPCD
 #ifdef CONFIG_CM206
 extern void cm206_setup(char *str, int *ints);
 #endif CONFIG_CM206
@@ -399,10 +402,6 @@
 #endif
 #ifdef CONFIG_BLK_DEV_PS2
 	{ "eda",     0x2400 },
-	{ "eza",     0x2800 },
-#endif
-#ifdef CONFIG_BPCD
-	{ "bpcd",    0x2900 },
 #endif
 #if CONFIG_APBLOCK
 	{ "apblock", APBLOCK_MAJOR << 8},
@@ -585,9 +584,6 @@
 #ifdef CONFIG_BLK_DEV_XD
 	{ "xd=", xd_setup },
 #endif
-#ifdef CONFIG_BLK_DEV_EZ
-	{ "ez=", ez_setup },
-#endif
 #if defined(CONFIG_BLK_DEV_FD) || defined(CONFIG_AMIGA_FLOPPY) || defined(CONFIG_ATARI_FLOPPY)
 	{ "floppy=", floppy_setup },
 #endif
@@ -616,9 +612,6 @@
 #ifdef CONFIG_GSCD
 	{ "gscd=", gscd_setup },
 #endif CONFIG_GSCD
-#ifdef CONFIG_BPCD
-	{ "bpcd=", bpcd_setup },
-#endif CONFIG_BPCD
 #ifdef CONFIG_CM206
 	{ "cm206=", cm206_setup },
 #endif CONFIG_CM206
@@ -723,6 +716,15 @@
 #endif
 #ifdef CONFIG_IP_PNP
 	{ "ip=", ip_auto_config_setup },
+#endif
+#ifdef CONFIG_PARIDE_PD
+	{ "pd.", pd_setup },
+#endif
+#ifdef CONFIG_PARIDE_PCD
+	{ "pcd.", pcd_setup },
+#endif
+#ifdef CONFIG_PARIDE_PF
+	{ "pf.", pf_setup },
 #endif
 	{ 0, 0 }
 };
diff -u --recursive --new-file v2.1.76/linux/kernel/sched.c linux/kernel/sched.c
--- v2.1.76/linux/kernel/sched.c	Wed Dec 10 11:12:46 1997
+++ linux/kernel/sched.c	Fri Jan  2 13:52:42 1998
@@ -104,6 +104,8 @@
 
 struct kernel_stat kstat = { 0 };
 
+void scheduling_functions_start_here(void) { }
+
 static inline void add_to_runqueue(struct task_struct * p)
 {
 	if (p->policy != SCHED_OTHER || p->counter > current->counter + 3)
@@ -677,6 +679,8 @@
 {
 	__sleep_on(p,TASK_UNINTERRUPTIBLE);
 }
+
+void scheduling_functions_end_here(void) { }
 
 static inline void cascade_timers(struct timer_vec *tv)
 {
diff -u --recursive --new-file v2.1.76/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.1.76/linux/mm/page_alloc.c	Mon Jun 16 16:36:01 1997
+++ linux/mm/page_alloc.c	Sun Dec 28 18:59:51 1997
@@ -161,11 +161,13 @@
 	change_bit((index) >> (1+(order)), (area)->map)
 #define CAN_DMA(x) (PageDMA(x))
 #define ADDRESS(x) (PAGE_OFFSET + ((x) << PAGE_SHIFT))
-#define RMQUEUE(order, dma) \
+#define RMQUEUE(order, maxorder, dma) \
 do { struct free_area_struct * area = free_area+order; \
      unsigned long new_order = order; \
-	do { struct page *prev = memory_head(area), *ret; \
-		while (memory_head(area) != (ret = prev->next)) { \
+	do { struct page *prev = memory_head(area), *ret = prev->next; \
+		while (memory_head(area) != ret) { \
+			if (new_order >= maxorder && ret->next == prev) \
+				break; \
 			if (!dma || CAN_DMA(ret)) { \
 				unsigned long map_nr = ret->map_nr; \
 				(prev->next = ret->next)->prev = prev; \
@@ -176,6 +178,7 @@
 				return ADDRESS(map_nr); \
 			} \
 			prev = ret; \
+			ret = ret->next; \
 		} \
 		new_order++; area++; \
 	} while (new_order < NR_MEM_LISTS); \
@@ -196,11 +199,23 @@
 
 unsigned long __get_free_pages(int priority, unsigned long order, int dma)
 {
-	unsigned long flags;
-	int reserved_pages;
+	unsigned long flags, maxorder;
 
 	if (order >= NR_MEM_LISTS)
-		return 0;
+		goto nopage;
+
+	/*
+	 * "maxorder" is the highest order number that we're allowed
+	 * to empty in order to find a free page..
+	 */
+	maxorder = order + NR_MEM_LISTS/3;
+	switch (priority) {
+	case GFP_ATOMIC:
+		maxorder = NR_MEM_LISTS;
+		/* fallthrough - no need to jump around */
+	case GFP_NFS:
+		maxorder += NR_MEM_LISTS/3;
+	}
 
 	if (in_interrupt() && priority != GFP_ATOMIC) {
 		static int count = 0;
@@ -211,19 +226,13 @@
 		}
 	}
 
-	reserved_pages = 5;
-	if (priority != GFP_NFS)
-		reserved_pages = min_free_pages;
 repeat:
 	spin_lock_irqsave(&page_alloc_lock, flags);
-	if ((priority==GFP_ATOMIC) || nr_free_pages > reserved_pages) {
-		RMQUEUE(order, dma);
-		spin_unlock_irqrestore(&page_alloc_lock, flags);
-		return 0;
-	}
+	RMQUEUE(order, maxorder, dma);
 	spin_unlock_irqrestore(&page_alloc_lock, flags);
-	if (priority != GFP_BUFFER && try_to_free_page(priority, dma, 1))
+	if (priority != GFP_BUFFER && priority != GFP_ATOMIC && try_to_free_page(priority, dma, 1))
 		goto repeat;
+nopage:
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.76/linux/net/core/dev.c linux/net/core/dev.c
--- v2.1.76/linux/net/core/dev.c	Tue Dec 23 16:31:00 1997
+++ linux/net/core/dev.c	Mon Dec 29 10:22:46 1997
@@ -1305,6 +1305,16 @@
 			ifr->ifr_ifindex = dev->ifindex;
 			return 0;
 
+		case SIOCGIFTXQLEN:
+			ifr->ifr_qlen = dev->tx_queue_len;
+			return 0;
+
+		case SIOCSIFTXQLEN:
+			if(ifr->ifr_qlen<2 || ifr->ifr_qlen>1024)
+				return -EINVAL;
+			dev->tx_queue_len = ifr->ifr_qlen;
+			return 0;
+
 		/*
 		 *	Unknown or private ioctl
 		 */
@@ -1396,9 +1406,17 @@
 		case SIOCGIFSLAVE:
 		case SIOCGIFMAP:
 		case SIOCGIFINDEX:
+		case SIOCGIFTXQLEN:
 			ret = dev_ifsioc(&ifr, cmd);
-			if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
-				return -EFAULT;
+			if (!ret)
+			{
+#ifdef CONFIG_NET_ALIAS
+				if (colon)
+					*colon = ':';
+#endif
+				if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+					return -EFAULT;
+			}
 			return ret;
 
 		/*
@@ -1414,6 +1432,7 @@
 		case SIOCSIFMAP:
 		case SIOCSIFHWADDR:
 		case SIOCSIFSLAVE:
+		case SIOCSIFTXQLEN:
 		case SIOCADDMULTI:
 		case SIOCDELMULTI:
 		case SIOCSIFHWBROADCAST:
diff -u --recursive --new-file v2.1.76/linux/net/core/filter.c linux/net/core/filter.c
--- v2.1.76/linux/net/core/filter.c	Sun Dec 21 22:36:17 1997
+++ linux/net/core/filter.c	Mon Dec 29 10:22:46 1997
@@ -5,7 +5,7 @@
  *     Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
  *
  * Based on the design of:
- *     - The Berkely Packet Filter
+ *     - The Berkeley Packet Filter
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
diff -u --recursive --new-file v2.1.76/linux/net/ipv4/Config.in linux/net/ipv4/Config.in
--- v2.1.76/linux/net/ipv4/Config.in	Fri Dec 19 15:53:06 1997
+++ linux/net/ipv4/Config.in	Mon Dec 29 10:22:47 1997
@@ -5,6 +5,7 @@
 bool 'IP: advanced router' CONFIG_IP_ADVANCED_ROUTER
 if [ "$CONFIG_IP_ADVANCED_ROUTER" = "y" ]; then
   define_bool CONFIG_RTNETLINK y	
+  define_bool CONFIG_NETLINK y
   bool 'IP: policy routing' CONFIG_IP_MULTIPLE_TABLES
   bool 'IP: equal cost multipath' CONFIG_IP_ROUTE_MULTIPATH
   bool 'IP: use TOS value as routing key' CONFIG_IP_ROUTE_TOS
diff -u --recursive --new-file v2.1.76/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.76/linux/net/ipv4/tcp_ipv4.c	Fri Dec 19 15:53:06 1997
+++ linux/net/ipv4/tcp_ipv4.c	Mon Dec 29 10:22:47 1997
@@ -1467,6 +1467,14 @@
 
 int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
 {
+#ifdef CONFIG_FILTER
+	if (sk->filter)
+	{
+		if (sk_filter(skb, sk->filter_data, sk->filter))
+			return -EPERM;	/* Toss packet */
+	}
+#endif /* CONFIG_FILTER */
+
 	skb_set_owner_r(skb, sk);
 
 	/*
diff -u --recursive --new-file v2.1.76/linux/scripts/Menuconfig linux/scripts/Menuconfig
--- v2.1.76/linux/scripts/Menuconfig	Tue Dec 23 16:31:00 1997
+++ linux/scripts/Menuconfig	Thu Jan  1 10:45:31 1998
@@ -32,6 +32,9 @@
 # handling
 #
 # 101297 Michael Chastain (mec@shout.net) - remove sound driver cruft.
+#
+# 221297 Michael Chastain (mec@shout.net) - make define_bool actually
+# define its arguments so that later tests on them work right.
 #----------------------------------------------------------------------------
 
 
@@ -100,10 +103,10 @@
 }
 
 #
-# Don't need this yet, but we don't want to puke either.
+# Define a boolean to a specific value.
 #
 function define_bool () {
-	:	
+	eval $1=$2
 }
 
 #
@@ -432,7 +435,7 @@
 
 			# Semantics of + and ? in GNU expr changed, so
 			# we avoid them:
-			if expr "$answer" : '0$\|\(-[1-9]\|[0-9]\)[0-9]*$' >/dev/null
+			if expr "$answer" : '0$\|-[1-9][0-9]*$\|[1-9][0-9]*$' >/dev/null
 			then
 				eval $2="$answer"
 			else
@@ -1178,7 +1181,7 @@
 Press <?> for additional information about this option."
 
 inputbox_instructions_int="\
-Please enter a decimal value between 1 and 9999. \
+Please enter a decimal value. \
 Fractions will not be accepted.  \
 Use the <TAB> key to move from the input field to the buttons below it."
 
diff -u --recursive --new-file v2.1.76/linux/scripts/lxdialog/lxdialog.c linux/scripts/lxdialog/lxdialog.c
--- v2.1.76/linux/scripts/lxdialog/lxdialog.c	Sun Feb 25 01:17:58 1996
+++ linux/scripts/lxdialog/lxdialog.c	Tue Dec 23 20:25:10 1997
@@ -158,7 +158,7 @@
 \n  --textbox   <file> <height> <width>\
 \n  --inputbox  <text> <height> <width> [<init>]\
 \n  --yesno     <text> <height> <width>\
-", name, name);
+\n", name, name);
     exit (-1);
 }
 
