diff -u --recursive --new-file v2.1.69/linux/CREDITS linux/CREDITS
--- v2.1.69/linux/CREDITS	Tue Dec  2 09:49:38 1997
+++ linux/CREDITS	Tue Dec  2 11:41:44 1997
@@ -33,6 +33,16 @@
 S: B-2610 Wilrijk-Antwerpen
 S: Belgium
 
+N: Erik Andersen
+E: andersee@debian.org
+W: http://www.inconnect.com/~andersen
+P: 1024/FC4CFFED 78 3C 6A 19 FA 5D 92 5A  FB AC 7B A5 A5 E1 FF 8E
+D: Maintainer of ide-cd and Uniform CD-ROM driver, 
+D: ATAPI CD-Changer support, Major 2.1.x CD-ROM update.
+S: 4538 South Carnegie Tech St.
+S: West Valley City, UT 84120
+S: USA
+
 N: H. Peter Anvin
 E: hpa@zytor.com
 W: http://www.zytor.com/~hpa/
@@ -1753,10 +1763,11 @@
 S: Finland
 
 N: Roger E. Wolff
-E: wolff@dutecai.et.tudelft.nl
+E: R.E.Wolff@BitWizard.nl
 D: Written kmalloc/kfree
-S: Oosterstraat 23
-S: 2611 TT Delft
+D: Written Specialix IO8+ driver
+S: van Bronckhorststraat 12
+S: 2612 XV Delft
 S: The Netherlands
 
 N: David Woodhouse
diff -u --recursive --new-file v2.1.69/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.69/linux/Documentation/Configure.help	Tue Dec  2 09:49:38 1997
+++ linux/Documentation/Configure.help	Tue Dec  2 14:24:04 1997
@@ -3951,7 +3951,7 @@
   This is support for the DIGITAL series of EISA (DEFEA) and PCI
   (DEFPA) controllers which can connect you to a local FDDI network.
 
-Support non-SCSI/IDE/ATAPI drives
+Support CDROM drives that are not SCSI or IDE/ATAPI
 CONFIG_CD_NO_IDESCSI
   If you have a CDROM drive that is neither SCSI nor IDE/ATAPI, say Y
   here, otherwise N. Read the CDROM-HOWTO, available via ftp (user:
@@ -3963,7 +3963,7 @@
   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 include/linux/<driver_name>.h where
+  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
@@ -4004,7 +4004,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/include/linux/mcd.h.  If you say Y here, you should
+  look at linux/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 (
@@ -4048,7 +4048,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/include/linux/sbpcd.h and set "DISTRIBUTION 0" there.
+  into linux/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
@@ -4067,7 +4067,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/include/linux/sbpcd.h before compiling the new kernel. Read
+  linux/drivers/cdrom/sbpcd.h before compiling the new kernel. Read
   the file Documentation/cdrom/sbpcd.
 
 Aztech/Orchid/Okano/Wearnes/TXC/CyDROM  CDROM support
@@ -4099,9 +4099,9 @@
 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 include/linux/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,
+  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
@@ -4827,6 +4827,18 @@
   Documentation/modules.txt. The module will be called smbfs.o. Most
   people say N, however.
 
+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>. 
+
+
 SMB Win95 bug work-around
 CONFIG_SMB_WIN95
   If you want to connect to a share exported by Windows 95, you should
@@ -4972,6 +4984,23 @@
   and read the file Documentation/riscom8.txt. Also it's possible to
   say M here and compile this driver as kernel loadable module; the
   module will be called riscom8.o.
+
+Specialix IO8+ card support
+CONFIG_SPECIALIX
+  This is a driver for the Specialix IO8+ multiport card, that give 
+  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 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.
+
+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. 
 
 Cyclades async mux support
 CONFIG_CYCLADES
diff -u --recursive --new-file v2.1.69/linux/Documentation/cdrom/Makefile linux/Documentation/cdrom/Makefile
--- v2.1.69/linux/Documentation/cdrom/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/cdrom/Makefile	Tue Dec  2 11:41:44 1997
@@ -0,0 +1,21 @@
+LATEXFILE = cdrom-standard
+
+all:
+	make clean
+	latex $(LATEXFILE)
+	latex $(LATEXFILE)
+	@if [ -x `which gv` ]; then \
+		`dvips -q -t letter -o $(LATEXFILE).ps $(LATEXFILE).dvi` ;\
+		`gv -antialias -media letter -nocenter $(LATEXFILE).ps` ;\
+	else \
+		`xdvi $(LATEXFILE).dvi &` ;\
+	fi
+	make sortofclean
+
+clean:
+	rm -f $(LATEXFILE).ps $(LATEXFILE).dvi $(LATEXFILE).aux $(LATEXFILE).log 
+
+sortofclean:
+	rm -f $(LATEXFILE).aux $(LATEXFILE).log 
+
+
diff -u --recursive --new-file v2.1.69/linux/Documentation/cdrom/aztcd linux/Documentation/cdrom/aztcd
--- v2.1.69/linux/Documentation/cdrom/aztcd	Tue Dec  2 09:49:38 1997
+++ linux/Documentation/cdrom/aztcd	Tue Dec  2 11:43:16 1997
@@ -55,8 +55,8 @@
 
 2. INSTALLATION
 The driver consists of a header file 'aztcd.h', which normally should reside 
-in /usr/include/linux and the source code 'aztcd.c', which normally resides in
-/usr/src/linux/drivers/cdrom. It uses /dev/aztcd (/dev/aztcd0 in some distri-
+in /usr/src/linux/drivers/cdrom and the source code 'aztcd.c', which normally 
+resides in the same place. It uses /dev/aztcd (/dev/aztcd0 in some distri-
 butions), which must be a valid block device with major number 29 and reside 
 in directory /dev. To mount a CD-ROM, your kernel needs to have the ISO9660-
 filesystem support included.
@@ -157,7 +157,7 @@
 you create the modules. So rebuild your kernel, if necessary. 
 
 Now edit the base address of your AZTECH interface card in
-/usr/src/linux/include/linux/aztcd.h to the appropriate value. 
+/usr/src/linux/drivers/cdrom/aztcd.h to the appropriate value. 
 aztcd may be configured to use autoprobing the base address by setting
 AZT_BASE_ADDR to '-1'. In that case aztcd probes the addresses listed 
 under AZT_BASE_AUTO. But please remember, that autoprobing always may 
@@ -191,7 +191,7 @@
 soundcard and CDROM, then warm boot (or use loadlin) their PC to start
 Linux.
 Support for the CDROM-interface of SoundWave32-soundcards is directly
-implemented in the AZTECH driver. Please edit /usr/src/linux/include/aztdc.h,
+implemented in the AZTECH driver. Please edit linux/drivers/cdrom/aztdc.h,
 uncomment line '#define AZT_SW32' and set the appropriate value for
 AZT_BASE_ADDR and AZT_SW32_BASE_ADDR. This support was tested with an Orchid
 CDS-3110 connected to a SoundWave32.
@@ -563,7 +563,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <linux/cdrom.h>
-#include <linux/aztcd.h>
+#include <linux/../../drivers/cdrom/aztcd.h>
 
 void help(void)
 { printf("Available Commands:  STOP         s      EJECT/CLOSE  e       QUIT         q\n");
diff -u --recursive --new-file v2.1.69/linux/Documentation/cdrom/cdrom-standard.tex linux/Documentation/cdrom/cdrom-standard.tex
--- v2.1.69/linux/Documentation/cdrom/cdrom-standard.tex	Sun Jan 26 02:07:03 1997
+++ linux/Documentation/cdrom/cdrom-standard.tex	Tue Dec  2 11:41:44 1997
@@ -1,5 +1,6 @@
 \documentclass{article}
-\def\version{$Id: cdrom-standard.tex,v 1.6 1996/12/29 20:45:18 davem Exp $}
+\def\version{$Id: cdrom-standard.tex,v 1.8 1997/11/19 21:58:33 david Exp $}
+\newcommand{\newsection}[1]{\newpage\section{#1}}
 
 \evensidemargin=0pt
 \oddsidemargin=0pt
@@ -7,11 +8,13 @@
 \textwidth=15.99cm \textheight=24.62cm % normal A4, 1'' margin
 
 \def\linux{{\sc Linux}}
-\def\cdrom{{\sc CDrom}}
-\def\cdromc{{\tt cdrom.c}}
-\def\cdromh{{\tt cdrom.h}}
-\def\ucdrom{{\tt ucdrom.h}}
-\def\fo{\sl}
+\def\cdrom{{\sc cd-rom}}
+\def\UCD{{\sc Uniform cd-rom Driver}}
+\def\cdromc{{\tt {cdrom.c}}}
+\def\cdromh{{\tt {cdrom.h}}}
+\def\fo{\sl}                    % foreign words
+\def\ie{{\fo i.e.}}
+\def\eg{{\fo e.g.}}
 
 \everymath{\it} \everydisplay{\it}
 \catcode `\_=\active \def_{\_\penalty100 }
@@ -19,151 +22,144 @@
 
 \begin{document}
 \title{A \linux\ \cdrom\ standard}
-\author{David van Leeuwen\\{\normalsize\tt david@tm.tno.nl}}
+\author{David van Leeuwen\\{\normalsize\tt david@ElseWare.cistron.nl}
+\\{\footnotesize updated by Erik Andersen {\tt(andersee@debian.org)}}}
+\date{19 November 1997}
 
 \maketitle
 
-\section{Introduction}
+\newsection{Introduction}
 
-\linux\ is probably the Unix-like operating system that supports the widest
-variety of hardware devices. The reasons for this are presumably
-\begin{itemize}
-\item The large list of different hardware devices available for the popular
-IBM PC-architecture,
-\item The open design of the operating system, such that everybody can
-write a driver for Linux (source code examples).
+\linux\ is probably the Unix-like operating system that supports
+the widest variety of hardware devices. The reasons for this are
+presumably 
+\begin{itemize} 
+\item 
+  The large list of hardware devices available for the many platforms
+  that \linux\ now supports (\ie, i386-PCs, Sparc Suns, etc.)
+\item 
+  The open design of the operating system, such that anybody can write a
+  driver for \linux.
+\item 
+  There is plenty of source code around as examples of how to write a driver.
 \end{itemize}
-The vast choice and openness has lead not only to a wide support of
-hardware devices, but also to a certain divergence in behavior.
-Especially for \cdrom\ devices, the way a particular drive reacts to a
-`standard' $ioctl()$ call varies a lot from one brand to another;
-however, the \linux\ \cdrom\ driver writers kept away from wilderness
-by the practice of evolving a new driver by copying, understanding and
-changing an existing one.
-
-Since the beginning of the \cdrom, many different interfaces
-developed.  Some of them had their own proprietary design (Sony,
-Mitsumi, Panasonic, Philips), other manufacturers adopted an existing
-electrical interface and changed the functionality
-(CreativeLabs/SoundBlaster, Teac, Funai) or simply adapted their
-drives to one or more of the already existing electrical interfaces
-(Aztech, Sanyo, Funai, Vertos, Longshine, Optics Storage and most of
-the `NoName' manufacturers).  In cases where a new drive really
-brought his own interface or used his own command set and flow control
+The openness of \linux, and the many different types of available
+hardware has allowed \linux\ to support many different hardware devices.
+Unfortunatly, the very openness that has allowed \linux\ to support
+all these different devices has also allowed the behavior of each
+device driver to differ significantly from one device to another.
+This divergence of behavior has been the very significant for \cdrom\
+devices; the way a particular drive reacts to a `standard' $ioctl()$
+call varies greatly from one device driver to another. To avoid making
+their drivers totally inconsistent, the writers of \linux\ \cdrom\
+drivers generally created new device drivers by understanding, copying,
+and then changing an existing one. Unfortunatly, this practice did not
+maintain uniform behavior across all the \linux\ \cdrom\ drivers. 
+
+This document describes an effort to establish Uniform behavior across
+all the different \cdrom\ device drivers for \linux. This document also
+defines the various $ioctl$s, and how the low-level \cdrom\ device
+drivers should implement them. Currently (as of the \linux\ 2.1.$x$
+development kernels) several low-level \cdrom\ device drivers, including
+both IDE/ATAPI and SCSI, now use this Uniform interface.
+
+When the \cdrom\ was developed, the interface between the \cdrom\ drive
+and the computer was not specified in the standards. As a result, many
+different \cdrom\ interfaces were developed. Some of them had their
+own proprietary design (Sony, Mitsumi, Panasonic, Philips), other
+manufacturers adopted an existing electrical interface and changed
+the functionality (CreativeLabs/SoundBlaster, Teac, Funai) or simply
+adapted their drives to one or more of the already existing electrical
+interfaces (Aztech, Sanyo, Funai, Vertos, Longshine, Optics Storage and
+most of the `NoName' manufacturers). In cases where a new drive really
+brought its own interface or used its own command set and flow control
 scheme, either a separate driver had to be written, or an existing
-driver had to get enhanced.
-
-Nowadays, almost all new \cdrom\ types are either ATAPI/IDE or SCSI;
-it is very unlikely that any manufacturer still will create a new
-interface, and new drives for the existing proprietary interfaces are
-getting rare.  But history has delivered us \cdrom\ support for many
-different interfaces.
-
-When (in the 1.3.70's) I looked at the existing interface which is
-expressed through \cdromh\ it appeared to be a rather wild set of
-commands and data formats.\footnote{I cannot recollect what kernel
-  version I looked at, then, presumably 1.2.13 and 1.3.34---the latest
-  kernel that I was indirectly involved in.} It seemed that many
-features of the interface have been added to include certain specific
-capabilities of certain drives, in an {\fo ad hoc\/} manner. More
-importantly, it appeared that actual execution of the commands is
-different for most of the different drivers: e.g., some drivers close
-the tray if an $open()$ call occurs while the tray is unloaded, while
+driver had to be enhanced. History has delivered us \cdrom\ support for
+many of these different interfaces. Nowadays, almost all new \cdrom\
+drives are either IDE/ATAPI or SCSI, and it is very unlikely that any
+manufacturer will create a new interface. Even finding drives for the
+old proprietary interfaces is getting difficult.
+
+When (in the 1.3.70's) I looked at the existing software interface,
+which was expressed through \cdromh, it appeared to be a rather wild
+set of commands and data formats.\footnote{I cannot recollect what
+kernel version I looked at, then, presumably 1.2.13 and 1.3.34---the
+latest kernel that I was indirectly involved in.} It seemed that many
+features of the software interface had been added to accomodate the
+capabilities of a particular drive, in an {\fo ad hoc\/} manner. More
+importantly, it appeared that the behavior of the `standard' commands
+was different for most of the different drivers: \eg, some drivers
+close the tray if an $open()$ call occurs when the tray is open, while
 others do not. Some drivers lock the door upon opening the device, to
 prevent an incoherent file system, but others don't, to allow software
-ejection.  Undoubtedly, the capabilities of the different drives vary,
-but even when two drives have the same capability the driver behavior
-may be different.
-
-I decided to start a discussion on how to improve uniformity,
-addressing all \cdrom-driver developers found in the various driver
-files. The reactions encouraged me to write a uniform (compatible)
-software level \cdromc\ to which this document is the documentation.
-In the mean time, the data structure definitions in \cdromh\ had been
-cleaned up a lot---which was very helpful for the new code.
- 
-\begin{quote}
-\small
-[Apparently, not all \cdrom\ developers support this initiative.
-They---mainly those who used the already existing drivers not only as
-a coding example, but also as a `user interface' reference during
-their own development---have taken care that \cdromh\ reflects a
-software interface to `user programs' which is unique between all
-drivers as much as possible; these driver writers will continue to
-refine the existing \cdromh\ where it seems necessary, and they tend
-to look if any newly requested functionality isn't already there
-before they are ready to define new structures. The {\tt sbpcd} driver
-gives an example that it is possible to let a robot arm play juke
-box---either with audio or with data CDs---and that it is possible to
-let the juke box work on even if a disk has fallen upon the floor and
-the drive door has closed without having a disk inside; without any
-new software layer or any structures which are not already present in
-\cdromh.  This `other' group of \linux\ \cdrom\ driver writers
-explicitly does {\em not\/} support the idea to define an additional
-software layer between driver and user program.]\parfillskip=0pt
-\end{quote}
-
-The effort (\cdromc) of which this is the documentation is {\em not\/}
-meant to drive a wedge between two groups of driver developers, but
-rather to enable sharing of `strategic code' among drivers. The code
-should {\em not\/} be viewed as a new interface to user-level
-programs, but rather as a new interface between driver code and
-kernel. 
-
-Care is taken that 100\,\% compatibility exists with the data
-structures and programmer's interface defined in \cdromh, and in order
-not to interfere with ongoing development in \cdromh, any `new' data
-structures have been put in a separate header file called \ucdrom.
-Because the data structures of \cdromh\ are fully supported, this
-documentation may also be of help to the programmers using the
-interface defined in \cdromh, but this guide is primarily written to
-help \cdrom\ driver developers adapt their code to use the `common
-\cdrom' code in \cdromc.
-
-Personally, I think that the most important hardware interfaces will
-be the IDE/ATAPI drives and of course the SCSI drives, but as prices
-of hardware drop continuously, it is not unlikely that people will
-have more than one \cdrom\ drive, possibly of mixed types. It is
-important that these drives behave in the same way. (In December 1994,
-one of the cheapest \cdrom\ drives was a Philips cm206, a double-speed
-proprietary drive. In the months that I was busy writing a \linux\ 
-driver for it, proprietary drives became old-fashioned and IDE/ATAPI
-drives became standard. At the time of writing (December 1996) the
-cheapest drive is quadruple speed IDE and at less than half the price
-of its predecessor. Twelve speed drives are available now.)
-
-This document defines the various $ioctl$s, and the way the drivers
-should implement this. Currently (in the kernel 2.1.$n$ development
-line) three low-level \cdrom\ drivers use this interface, among
-which are the most important drivers for IDE and SCSI. 
+ejection. Undoubtedly, the capabilities of the different drives vary,
+but even when two drives have the same capability their driver's
+behavior was usually different.
+
+I decided to start a discussion on how to make all the \linux\ \cdrom\
+drivers behave more uniformly. I began by contacting the developers of
+the many \cdrom\ drivers found in the \linux\ kernel. Their reactions
+encouraged me to write the \UCD\ which this document is intended to
+describe. The implementation of the \UCD\ is in the file \cdromc. This
+driver is intended to be an additional software layer that sits on top
+of the low-level device drivers for each \cdrom\ drive. By adding this
+additional layer, it is possible to have all the different \cdrom\
+devices behave {\em exactly\/} the same (insofar as the underlying
+hardware will allow).
+
+The goal of the \UCD\ is {\em not\/} to alienate driver developers who
+have not yet taken steps to support this effort. The goal of \UCD\ is
+simply is give people writing application programs for \cdrom\ drives
+{\em one\/} \linux\ \cdrom\ interface with consistent behavior for all
+\cdrom\ devices. In addition, this also provides a consistent interface
+between the low-level device driver code and the \linux\ kernel. Care
+is taken that 100\,\% compatibility exists with the data structures and
+programmer's interface defined in \cdromh. This guide was written to
+help \cdrom\ driver developers adapt their code to use the \UCD\ code
+defined in \cdromc.
+
+Personally, I think that the most important hardware interfaces are
+the IDE/ATAPI drives and, of course, the SCSI drives, but as prices
+of hardware drop continuously, it is also likely that people may have
+more than one \cdrom\ drive, possibly of mixed types. It is important
+that these drives behave in the same way. In December 1994, one of the
+cheapest \cdrom\ drives was a Philips cm206, a double-speed proprietary
+drive. In the months that I was busy writing a \linux\ driver for it,
+proprietary drives became obsolete and IDE/ATAPI drives became the
+standard. At the time of the last update to this document (November
+1997) it is becoming difficult to even {\em find} anything less than a
+16 speed \cdrom\ drive, and 24 speed drives are common.
 
-\section{Standardizing through another software level}
+\newsection{Standardizing through another software level}
 \label{cdrom.c}
 
-At the time this document was conceived, all drivers directly implement
-the $ioctl()$ calls through their own routines, with the danger of
-forgetting calls to $verify_area()$ and the risk of divergence in
-implementation. 
-
-For this reason, we\footnote{The writing style is such that `we' is
-used when (at least part of) the \cdrom-device driver authors support
-the idea, an `I' is used for personal opinions} propose to define
-another software-level, that separates the $ioctl()$ and $open()$
-implementation from the actual hardware implementation. Note that we
-do not wish to alter the existing application interface defined in
-\cdromh, but rather want to re-root the hardware-implementation through
-some common code. 
-
-We believe that \cdrom\ drives are specific enough (i.e., different
-from other block-devices such as floppy or hard disc drives), to
-define a set of {\em \cdrom\ device operations},
-$<cdrom-device>_dops$. These are of a different nature than the
-classical block-device file operations $<block-device>_fops$.
-
-The extra interfacing level routines are implemented in a file
-\cdromc, and a low-level \cdrom\ driver hands over the interfacing to
-the kernel by registering the following general $struct\ 
-file_operations$:
+At the time this document was conceived, all drivers directly
+implemented the \cdrom\ $ioctl()$ calls through their own routines. This
+led to the danger of different drivers forgetting to do important things
+like checking that the user was giving the driver valid data. More
+importantly, this led to the divergence of behavior, which has already
+been discussed.
+
+For this reason, the \UCD\ was created to enforce consistent \cdrom\
+drive behavior, and to provide a common set of services to the various
+low-level \cdrom\ device drivers. The \UCD\ now provides another
+software-level, that separates the $ioctl()$ and $open()$ implementation
+from the actual hardware implementation. Note that this effort has
+made few changes which will effect a user's application programs. The
+greatest change involved moving the contents of the various low-level
+\cdrom\ driver's header files to the kernel's cdrom directory. This was
+done to help ensure that the user is only presented with only one cdrom
+interface, the interface defined in \cdromh.
+
+\cdrom\ drives are specific enough (\ie, different from other
+block-devices such as floppy or hard disc drives), to define a set
+of common {\em \cdrom\ device operations}, $<cdrom-device>_dops$.
+These operations are different than the classical block-device file
+operations, $<block-device>_fops$.
+
+The routines for the \UCD\ interface level are implemented in the file
+\cdromc. In this file, the \UCD\ interfaces with the kernel as a block
+device by registering the following general $struct\ file_operations$:
 $$
 \halign{$#$\ \hfil&$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr
 struct& file_operations\ cdrom_fops = \{\hidewidth\cr
@@ -182,49 +178,54 @@
         &NULL                   & revalidate \cr
 \};\cr
 }
-$$
-Every active \cdrom\ device shares this $struct$. The routines declared
-above are all implemented in \cdromc, and this is the place where the
-{\em behavior\/} of all \cdrom-devices is defined, and hence
-standardized. The implementation of the interfacing to the various
-types of hardware still is done by the various \cdrom-device drivers,
-but these routines only implement certain {\em capabilities\/} that
-are typical to \cdrom\ (removable-media) devices.
-
-Registration of the \cdrom\ device driver should now be to the general
-routines in \cdromc, not to the VFS any more. The interfacing with
-\cdromc\ is implemented trough two general structures, that contain
-information about the capabilities of the driver, and the specific
-drives on which the driver operates. The structures are separated to
-contain information about
+$$ 
+
+Every active \cdrom\ device shares this $struct$. The routines
+declared above are all implemented in \cdromc, since this file is the
+place where the behavior of all \cdrom-devices is defined and
+standardized. The actual interface to the various types of \cdrom\ 
+hardware is still performed by various low-level \cdrom-device
+drivers. These routines simply implement certain {\em capabilities\/}
+that are common to all \cdrom\ (and really, all removable-media
+devices).
+
+Registration of a low-level \cdrom\ device driver is now done through
+the general routines in \cdromc, not through the Virtual File System
+(VFS) any more. The interface implemented in \cdromc\ is carried out
+through two general structures that contain information about the
+capabilities of the driver, and the specific drives on which the
+driver operates. The structures are:
 \begin{description}
-\item[the low-level driver] It lists the routines that actually
-  implement \cdrom\ operations, and hence the structure is called
-  $cdrom_device_ops$. The structure is conceptually connected to the
-  major number of the device (although some drivers may have have
-  different major numbers, as is the case for the IDE driver).
-\item[the specific drive] It lists the variables informative of the
-  drive that is driven, and hence the structure is called
-  $cdrom_device_info$. The structure is conceptually connected to the
-  minor number of the device.
+\item[$cdrom_device_ops$] 
+  This structure contains information about the low-level driver for a
+  \cdrom\ device. This structure is conceptually connected to the major
+  number of the device (although some drivers may have different
+  major numbers, as is the case for the IDE driver).
+\item[$cdrom_device_info$] 
+  This structure contains information about a particular \cdrom\ drive,
+  such as its device name, speed, etc. This structure is conceptually
+  connected to the minor number of the device.
 \end{description}
 
-The registration is done for each drive found by the driver (and hence
-for each minor number) though the call
-$$register_cdrom(struct\ cdrom_device_info * <device>_info, char * name)  
-$$
-This device information structure $<device>_info$ (described
-shortly) contains all information needed for the kernel to interface
-with the low-level \cdrom\ device driver. One of the main entries of
-this structure is a pointer to the $cdrom_device_ops$ structure of the
-driver.
-
-This device operations structure $cdrom_device_ops$ lists the
-implemented routines for interfacing to the hardware. [It is
-impossible to come up with a complete list of all capabilities of
-(future) \cdrom\ drives, as the developments in technology follow-up
-at an incredible rate. Maybe write-operation (WORM devices) will
-become very popular in the future.]  The list now is:
+Registering a particular \cdrom\ drive with the \UCD\ is done by the
+low-level device driver though a call to:
+$$register_cdrom(struct\ cdrom_device_info * <device>_info)  
+$$
+The device information structure, $<device>_info$, contains all the
+information needed for the kernel to interface with the low-level
+\cdrom\ device driver. One of the most important entries in this
+structure is a pointer to the $cdrom_device_ops$ structure of the
+low-level driver.
+
+The device operations structure, $cdrom_device_ops$, contains a list
+of pointers to the functions which are implemented in the low-level
+device driver. When \cdromc\ accesses a \cdrom\ device, it does it
+through the functions in this structure. It is impossible to know all
+the capabilities of future \cdrom\ drives, so it is expected that this
+list may need to be expanded from time to time as new technologies are
+developed. For example, CD-R and CD-R/W drives are beginning to become
+popular, and support will soon need to be added for them. For now, the
+current $struct$ is:
 $$
 \halign{$#$\ \hfil&$#$\ \hfil&\hbox to 10em{$#$\hss}&
   $/*$ \rm# $*/$\hfil\cr
@@ -232,7 +233,6 @@
   &int& (* open)(struct\ cdrom_device_info *, int)\cr
   &void& (* release)(struct\ cdrom_device_info *);\cr 
   &int& (* drive_status)(struct\ cdrom_device_info *);\cr     
-  &int& (* disc_status)(struct\ cdrom_device_info *);\cr      
   &int& (* media_changed)(struct\ cdrom_device_info *, int);\cr 
   &int& (* tray_move)(struct\ cdrom_device_info *, int);\cr
   &int& (* lock_door)(struct\ cdrom_device_info *, int);\cr
@@ -247,32 +247,35 @@
   &int& (* dev_ioctl)(struct\ cdrom_device_info *, unsigned\ int, 
         unsigned\ long);\cr
 \noalign{\medskip}
-  &\llap{const\ }int& capability;&  capability flags \cr
+  &const\ int& capability;& capability flags \cr
   &int& n_minors;& number of active minor devices \cr
+\};\cr
 }
 $$
-The \cdrom-driver should simply implement (some of) these
-functions, and register the functions to the global \cdrom\ driver,
-which performs interfacing with the Virtual File System and system
-$ioctl$s. The flags $capability$ specify the hardware-capabilities on
-registration of the device. The value $n_minors$ should be a positive
-value indicating the number of minor devices that are supported by the
-driver, normally~1. Although these two variables are `informative'
-rather than `operational,' they are included in $cdrom_device_ops$
-because they describe the capability of the {\em driver\/} rather than
-the {\em drive}. Nomenclature has always been difficult in computer
-programming. 
+When a low-level device driver implements one of these capabilities,
+it should add a function pointer to this $struct$. When a particular
+function is not implemented, however, this $struct$ should contain a
+NULL instead. The $capability$ flags specify the capabilities of the
+\cdrom\ hardware and/or low-level \cdrom\ driver when a \cdrom\ drive
+is registered with the \UCD. The value $n_minors$ should be a positive
+value indicating the number of minor devices that are supported by
+the low-level device driver, normally~1. Although these two variables
+are `informative' rather than `operational,' they are included in
+$cdrom_device_ops$ because they describe the capability of the {\em
+driver\/} rather than the {\em drive}. Nomenclature has always been
+difficult in computer programming.
 
 Note that most functions have fewer parameters than their
 $blkdev_fops$ counterparts. This is because very little of the
-information in the structures $inode$ and $file$ are used, the main
-parameter is the first, from which the major and minor number can be
-extracted. (Most low-level \cdrom\ drivers don't even look at that value
-as only one device is supported.) This will be available through $dev$
-in $cdrom_device_info$ described below. 
+information in the structures $inode$ and $file$ are used. For most
+drivers, the main parameter is the $struct$ $cdrom_device_info$, from
+which the major and minor number can be extracted. (Most low-level
+\cdrom\ drivers don't even look at the major and minor number though,
+since many of them only support one device.) This will be available
+through $dev$ in $cdrom_device_info$ described below.
 
-The drive-specific, minor-like information that is registered to
-\cdromc, contains the following fields:
+The drive-specific, minor-like information that is registered with
+\cdromc, currently contains the following fields:
 $$
 \halign{$#$\ \hfil&$#$\ \hfil&\hbox to 10em{$#$\hss}&
   $/*$ \rm# $*/$\hfil\cr
@@ -281,72 +284,71 @@
   & struct\ cdrom_device_info *& next;& next device_info for this major\cr
   & void *&  handle;& driver-dependent data\cr
 \noalign{\medskip}
-  & kdev_t&  dev;& device number (incorporates minor)/\cr
+  & kdev_t&  dev;& device number (incorporates minor)\cr
   & int& mask;& mask of capability: disables them \cr
-  &\llap{$const\ $}int& speed;&  maximum speed for reading data \cr
-  &\llap{$const\ $}int& n_discs;& number of discs in jukebox \cr
+  &const\ int& speed;& maximum speed for reading data \cr
+  &const\ int& capacity;& number of discs in a jukebox \cr
 \noalign{\medskip}
   &int& options : 30;& options flags \cr
   &long& mc_flags : 2;& media-change buffer flags \cr
   & int& use_count;& number of times devices is opened\cr
 \}\cr
 }$$
-
-With this $struct$, a linked list of minor devices registered with
-the same low-level driver is built, though the field $next$.  The
-device number, the device operations struct and specifications of
-properties of the drive are stored in this structure.
-
-The flags $mask$ can be used to mask out some of the capabilities
-listed in $ops\to capability$, if a specific drive doesn't support a
-feature of the driver. The value $speed$ specifies the maximum
-head-rate of the drive, measured in units of normal audio speed
-(176\,kB/sec raw data or 150\,kB/sec file system data).  The value
-$n_discs$ should reflect the number of discs the drive can hold
-simultaneously, if it is designed as a juke-box, or otherwise~1.
-The parameters are declared $const$ because they describe properties
-of the drive, which don't change after registration. 
+Using this $struct$, a linked list of the registered minor devices is
+built, using the $next$ field. The device number, the device operations
+struct and specifications of properties of the drive are stored in this
+structure.
+
+The $mask$ flags can be used to mask out some of the capabilities listed
+in $ops\to capability$, if a specific drive doesn't support a feature
+of the driver. The value $speed$ specifies the maximum head-rate of the
+drive, measured in units of normal audio speed (176\,kB/sec raw data or
+150\,kB/sec file system data). The value $n_discs$ should reflect the
+number of discs the drive can hold simultaneously, if it is designed
+as a juke-box, or otherwise~1. The parameters are declared $const$
+because they describe properties of the drive, which don't change after
+registration.
 
 A few registers contain variables local to the \cdrom\ drive. The
 flags $options$ are used to specify how the general \cdrom\ routines
 should behave. These various flags registers should provide enough
-flexibility to adapt to the different user's wishes (and {\em not\/}
-the `arbitrary' wishes of the author of the low-level device driver,
-as is the case in the old scheme). The register $mc_flags$ is used to
-buffer the information from $media_changed()$ to two separate queues.
-Other data that is specific to minor drive, can be accessed through
-$handle$, which can point to a data structure specific to the
-low-level driver. The fields $use_count$, $next$, $options$ and
-$mc_flags$ need not be initialized.
+flexibility to adapt to the different user's wishes (and {\em not\/} the
+`arbitrary' wishes of the author of the low-level device driver, as is
+the case in the old scheme). The register $mc_flags$ is used to buffer
+the information from $media_changed()$ to two separate queues. Other
+data that is specific to minor drive, can be accessed through $handle$,
+which can point to a data structure specific to the low-level driver.
+The fields $use_count$, $next$, $options$ and $mc_flags$ need not be
+initialized.
 
 The intermediate software layer that \cdromc\ forms will performs some
 additional bookkeeping. The use count of the device (the number of
-processes that have the device opened) is registered in $use_count$.
-The function $cdrom_ioctl()$ will verify the appropriate user-memory
-regions for read and write, and in case a location on the CD is
-transferred, it will `sanitize' the format by making requests to the
-low-level drivers in a standard format, and translating all formats
-between the user-software and low level drivers. This relieves much of
-the drivers memory checking and format checking and translation. Also,
-the necessary structures will be declared on the program stack.
+processes that have the device opened) is registered in $use_count$. The
+function $cdrom_ioctl()$ will verify the appropriate user-memory regions
+for read and write, and in case a location on the CD is transferred,
+it will `sanitize' the format by making requests to the low-level
+drivers in a standard format, and translating all formats between the
+user-software and low level drivers. This relieves much of the drivers
+memory checking and format checking and translation. Also, the necessary
+structures will be declared on the program stack.
 
 The implementation of the functions should be as defined in the
 following sections. Two functions {\em must\/} be implemented, namely
 $open()$ and $release()$. Other functions may be omitted, their
 corresponding capability flags will be cleared upon registration.
 Generally, a function returns zero on success and negative on error. A
-function call should return only after the command has completed, but
-of course waiting for the device should not use processor time.
+function call should return only after the command has completed, but of
+course waiting for the device should not use processor time.
 
-\subsection{$Open(struct\ cdrom_device_info * cdi, int\ purpose)$}
+\subsection{$Int\ open(struct\ cdrom_device_info * cdi, int\ purpose)$}
 
 $Open()$ should try to open the device for a specific $purpose$, which
 can be either:
 \begin{itemize}
-\item[0] Open for data read, as is used by {\tt mount()} (2), or the
-user commands {\tt dd} or {\tt cat}.  
-\item[1] Open for $ioctl$ commanding, as is used for audio-CD playing
-programs mostly. 
+\item[0] Open for reading data, as done by {\tt {mount()}} (2), or the
+user commands {\tt {dd}} or {\tt {cat}}.  
+\item[1] Open for $ioctl$ commands, as done by audio-CD playing
+programs.
 \end{itemize}
 In case the driver supports modules, the call $MOD_INC_USE_COUNT$
 should be performed exactly once, if the $open()$ was successful. The
@@ -355,10 +357,11 @@
 
 Notice that any strategic code (closing tray upon $open()$, etc.)\ is
 done by the calling routine in \cdromc, so the low-level routine
-should only be concerned with proper initialization and device-use
-count.
+should only be concerned with proper initialization, such as spinning
+up the disc, etc. % and device-use count
 
-\subsection{$Release(struct\ cdrom_device_info * cdi)$}
+
+\subsection{$Void\ release(struct\ cdrom_device_info * cdi)$}
 
 In case of module support, a single call $MOD_DEC_USE_COUNT$ should be
 coded here.  Possibly other device-specific actions should be taken
@@ -366,14 +369,14 @@
 ejection of the tray, or unlocking the door, should be left over to
 the general routine $cdrom_release()$. Also, the invalidation of the
 allocated buffers in the VFS is taken care of by the routine in
-\cdromc.
+\cdromc.  This is the only function returning type $void$. 
 
-\subsection{$Drive_status(struct\ cdrom_device_info * cdi)$}
+\subsection{$Int\ drive_status(struct\ cdrom_device_info * cdi)$}
 \label{drive status}
 
 The function $drive_status$, if implemented, should provide
-information of the status of the drive (not the status of the disc,
-which may or may not be in the drive). In \ucdrom\ the possibilities
+information on the status of the drive (not the status of the disc,
+which may or may not be in the drive). In \cdromh\ the possibilities
 are listed: 
 $$
 \halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr
@@ -384,19 +387,32 @@
 CDS_DISC_OK& a disc is loaded and everything is fine\cr
 }
 $$
-%For a juke-box, the second argument $drive_nr$ specifies information
-%is requested for another than the default disc ($drive_nr=0$),
-%possibly only a subset of the return values can be returned.
 
-\subsection{$Disc_status(struct\ cdrom_device_info * cdi)$}
+\subsection{$Disc_status$}
 \label{disc status}
 
-As a complement to $drive_status()$, this function can provide the
-general \cdrom-routines with information about the current disc that
-is inserted in the drive represented by $cdi\to dev$. The history of
-development of the CD's use as a carrier medium for various digital
-information has lead to many different disc types, hence this function
-can return:
+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
@@ -408,11 +424,11 @@
 CDS_XA_2_2& mixed data (XA), mode 2, form 1 (2324  user bytes)\cr
 }
 $$
-As far as I know, \cdrom s are always of type $CDS_DATA_1$. For
+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 {\tt cdrom.h}. 
+a recent version of \cdromh. 
 
-\subsection{$Media_changed(struct\ cdrom_device_info * cdi, int disc_nr)$}
+\subsection{$Int\ media_changed(struct\ cdrom_device_info * cdi, int\ disc_nr)$}
 
 This function is very similar to the original function in $struct\ 
 file_operations$. It returns 1 if the medium of the device $cdi\to
@@ -421,9 +437,9 @@
 ignored for single-disc drives.  Note that by `re-routing' this
 function through $cdrom_media_changed()$, we can implement separate
 queues for the VFS and a new $ioctl()$ function that can report device
-changes to software (e.g., an auto-mounting daemon).
+changes to software (\eg, an auto-mounting daemon).
 
-\subsection{$Tray_move(struct\ cdrom_device_info * cdi, int\ position)$}
+\subsection{$Int\ tray_move(struct\ cdrom_device_info * cdi, int\ position)$}
 
 This function, if implemented, should control the tray movement. (No
 other function should control this.) The parameter $position$ controls
@@ -436,7 +452,7 @@
 error. Note that if the tray is already in the desired position, no
 action need be taken, and the return value should be 0. 
 
-\subsection{$Lock_door(struct\ cdrom_device_info * cdi, int\ lock)$}
+\subsection{$Int\ lock_door(struct\ cdrom_device_info * cdi, int\ lock)$}
 
 This function (and no other code) controls locking of the door, if the
 drive allows this. The value of $lock$ controls the desired locking
@@ -445,42 +461,47 @@
 \item[0] Unlock door, manual opening is allowed
 \item[1] Lock door, tray cannot be ejected manually
 \end{itemize}
-Return values are as for $tray_move()$.
-
-\subsection{$Select_speed(struct\ cdrom_device_info * cdi, int\ speed)$}
+This function returns 0 upon success, and a non-zero value upon
+error. Note that if the door is already in the requested state, no
+action need be taken, and the return value should be 0. 
 
-Although none of the drivers has implemented this function so far,
-some drives are capable of head-speed selection, and hence this is a
-capability that should be standardized through a function in the
-device-operations structure. This function should select the speed at
-which data is read or audio is played back. The special value `0'
-means `auto-selection', i.e., maximum data-rate or real-time audio
-rate. If the drive doesn't have this `auto-selection' capability, the
-decision should be made on the current disc loaded and the return
-value should be positive. A negative return value indicates an
-error. 
+\subsection{$Int\ select_speed(struct\ cdrom_device_info * cdi, int\ speed)$}
 
-There are a few reasons for having the speed to be selectable. Badly
+Some \cdrom\ drives are capable of changing their head-speed. There
+are several reasons for changing the speed of a \cdrom\ drive. Badly
 pressed \cdrom s may benefit from less-than-maximum head rate. Modern
-\cdrom\ drives can obtain very high head rates (up to twelve times
-audio speed is common), but these drives tend to make an annoyingly
-loud noise. A lower speed may reduce this. Finally, although the
-audio-low-pass filters probably aren't designed for it, more than
-real-time playback of audio might be used for high-speed copying of
-audio tracks.
+\cdrom\ drives can obtain very high head rates (up to $24\times$ is
+common).  It has been reported that these drives can make reading
+errors at these high speeds, reducing the speed can prevent data loss
+in these circumstances.  Finally, some of these drives can
+make an annoyingly loud noise, which a lower speed may reduce. %Finally,
+%although the audio-low-pass filters probably aren't designed for it,
+%more than real-time playback of audio might be used for high-speed
+%copying of audio tracks.
+
+This function specifies the speed at which data is read or audio is
+played back. The value of $speed$ specifies the head-speed of the
+drive, measured in units of standard cdrom speed (176\,kB/sec raw data
+or 150\,kB/sec file system data). So to request that a \cdrom\ drive
+operate at 300\,kB/sec you would call the CDROM_SELECT_SPEED $ioctl$
+with $speed=2$. The special value `0' means `auto-selection', \ie,
+maximum data-rate or real-time audio rate. If the drive doesn't have
+this `auto-selection' capability, the decision should be made on the
+current disc loaded and the return value should be positive. A negative
+return value indicates an error.
 
-\subsection{$Select_disc(struct\ cdrom_device_info * cdi, int\ number)$}
+\subsection{$Int\ select_disc(struct\ cdrom_device_info * cdi, int\ number)$}
 
 If the drive can store multiple discs (a juke-box) this function
-should perform disc selection. It should return the number of the
+will perform disc selection. It should return the number of the
 selected disc on success, a negative value on error. Currently, only
-the IDE-cd driver supports such functionality.
+the ide-cd driver supports this functionality.
 
-\subsection{$Get_last_session(struct\ cdrom_device_info * cdi, struct\
+\subsection{$Int\ get_last_session(struct\ cdrom_device_info * cdi, struct\
   cdrom_multisession * ms_info)$}
 
 This function should implement the old corresponding $ioctl()$. For
-device $cdi->dev$, the start of the last session of the current disc
+device $cdi\to dev$, the start of the last session of the current disc
 should be returned in the pointer argument $ms_info$. Note that
 routines in \cdromc\ have sanitized this argument: its requested
 format will {\em always\/} be of the type $CDROM_LBA$ (linear block
@@ -491,7 +512,7 @@
 course) and the routines in \cdromc\ will make the transform if
 necessary. The return value is 0 upon success.
 
-\subsection{$Get_mcn(struct\ cdrom_device_info * cdi, struct\
+\subsection{$Int\ get_mcn(struct\ cdrom_device_info * cdi, struct\
   cdrom_mcn * mcn)$}
 
 Some discs carry a `Media Catalog Number' (MCN), also called
@@ -502,17 +523,19 @@
 pre-declared memory region of type $struct\ cdrom_mcn$. The MCN is
 expected as a 13-character string, terminated by a null-character.
 
-\subsection{$Reset(struct\ cdrom_device_info * cdi)$}
+\subsection{$Int\ reset(struct\ cdrom_device_info * cdi)$}
 
-This call should implement hard-resetting the drive (although in
-circumstances that a hard-reset is necessary, a drive may very well
-not listen to commands anymore). Preferably, control is returned to the
-caller only after the drive has finished resetting.
+This call should perform a hard-reset on the drive (although in
+circumstances that a hard-reset is necessary, a drive may very well not
+listen to commands anymore). Preferably, control is returned to the
+caller only after the drive has finished resetting. If the drive is no
+longer listening, it may be wise for the underlying low-level cdrom
+driver to time out.
 
-\subsection{$Audio_ioctl(struct\ cdrom_device_info * cdi, unsigned\
+\subsection{$Int\ audio_ioctl(struct\ cdrom_device_info * cdi, unsigned\
   int\ cmd, void * arg)$}
 
-Some of the \cdrom-$ioctl$s defined in {\tt cdrom.h} can be
+Some of the \cdrom-$ioctl$s defined in \cdromh\ can be
 implemented by the routines described above, and hence the function
 $cdrom_ioctl$ will use those. However, most $ioctl$s deal with
 audio-control. We have decided to leave these accessed through a
@@ -523,17 +546,19 @@
 Seconds, Frames) for all audio calls. It also verifies the memory
 location of $arg$, and reserves stack-memory for the argument. This
 makes implementation of the $audio_ioctl()$ much simpler than in the
-old driver scheme. For an example you may look up the function
-$cm206_audio_ioctl()$ in {\tt cm206.c} that should be updated with
+old driver scheme. For example, you may look up the function
+$cm206_audio_ioctl()$ in {\tt {cm206.c}} that should be updated with
 this documentation. 
 
-An unimplemented ioctl should return $-EINVAL$, but a harmless request
-(e.g., $CDROMSTART$) may be ignored by returning 0 (success). Other
-errors should be according to the standards, whatever they are. (We
-may decide to sanitize the return value in $cdrom_ioctl()$, in order
-to guarantee a uniform interface to the audio-player software.)
+An unimplemented ioctl should return $-ENOSYS$, but a harmless request
+(\eg, $CDROMSTART$) may be ignored by returning 0 (success). Other
+errors should be according to the standards, whatever they are. When
+an error is returned by the low-level driver, the \UCD\ tries whenever
+possible to return the error code to the calling program. (We may decide
+to sanitize the return value in $cdrom_ioctl()$ though, in order to
+guarantee a uniform interface to the audio-player software.)
 
-\subsection{$Dev_ioctl(struct\ cdrom_device_info * cdi, unsigned\ int\
+\subsection{$Int\ dev_ioctl(struct\ cdrom_device_info * cdi, unsigned\ int\
   cmd, unsigned\ long\ arg)$}
 
 Some $ioctl$s seem to be specific to certain \cdrom\ drives. That is,
@@ -547,7 +572,9 @@
 so either the audio-file-system should ask for 75264 bytes at once
 (the least common multiple of 512 and 2352), or the drivers should
 bend their backs to cope with this incoherence (to which I would be
-opposed). Once this question is resolved, this code should be
+opposed).  Furthermore, it it very difficult for the hardware to find
+the exact frame boundaries, since there are no synchronization headers
+in audio frames.  Once these issues are resolved, this code should be
 standardized in \cdromc.
 
 Because there are so many $ioctl$s that seem to be introduced to
@@ -555,17 +582,17 @@
   actually uses these? I'd be interested!} any `non-standard' $ioctl$s
 are routed through the call $dev_ioctl()$. In principle, `private'
 $ioctl$s should be numbered after the device's major number, and not
-the general \cdrom\ $ioctl$ number, {\tt 0x53}. Currently the
+the general \cdrom\ $ioctl$ number, {\tt {0x53}}. Currently the
 non-supported $ioctl$s are: {\it CDROMREADMODE1, CDROMREADMODE2,
   CDROMREADAUDIO, CDROMREADRAW, CDROMREADCOOKED, CDROMSEEK,
-  CDROMPLAY\-BLK and CDROMREADALL}.
+  CDROMPLAY\-BLK and CDROM\-READALL}.
 
 \subsection{\cdrom\ capabilities}
 
 Instead of just implementing some $ioctl$ calls, the interface in
 \cdromc\ supplies the possibility to indicate the {\em capabilities\/}
 of a \cdrom\ drive. This can be done by ORing any number of
-capability-constants that are defined in \ucdrom\ at the registration
+capability-constants that are defined in \cdromh\ at the registration
 phase. Currently, the capabilities are any of:
 $$
 \halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr
@@ -578,11 +605,14 @@
 CDC_MCN& can read Medium Catalog Number\cr
 CDC_MEDIA_CHANGED& can report if disc has changed\cr
 CDC_PLAY_AUDIO& can perform audio-functions (play, pause, etc)\cr
+CDC_RESET& hard reset device\cr
+CDC_IOCTLS& driver has non-standard ioctls\cr
+CDC_DRIVE_STATUS& driver implements drive status\cr
 }
 $$
 The capability flag is declared $const$, to prevent drivers from
 accidentally tampering with the contents. The capability fags actually
-inform \cdromc\ on what the driver is capable of. If the drive found
+inform \cdromc\ of what the driver can do. If the drive found
 by the driver does not have the capability, is can be masked out by
 the $cdrom_device_info$ variable $mask$. For instance, the SCSI \cdrom\
 driver has implemented the code for loading and ejecting \cdrom's, and
@@ -630,9 +660,12 @@
 }
 $$
 One option needs some more explanation: $CDO_USE_FFLAGS$. In the next
-section we explain what the need for this option is.
+newsection we explain what the need for this option is.
 
-\section{The need to know the purpose of opening}
+A software package {\tt setcd}, available from the Debian distribution
+and {\tt sunsite.unc.edu}, allows user level control of these flags. 
+
+\newsection{The need to know the purpose of opening the \cdrom\ device}
 
 Traditionally, Unix devices can be used in two different `modes',
 either by reading/writing to the device file, or by issuing
@@ -668,7 +701,7 @@
 $ioctl$ commands, while data use wants to open for correct and
 reliable data transfer. The only way user programs can indicate what
 their {\em purpose\/} of opening the device is, is through the $flags$
-parameter (see {\tt open(2)}). For \cdrom\ devices, these flags aren't
+parameter (see {\tt {open(2)}}). For \cdrom\ devices, these flags aren't
 implemented (some drivers implement checking for write-related flags,
 but this is not strictly necessary if the device file has correct
 permission flags). Most option flags simply don't make sense to
@@ -704,9 +737,9 @@
 configurations.\footnote{Incidentally, I think that SUN's approach to
 mounting \cdrom s is very good in origin: under Solaris a
 volume-daemon automatically mounts a newly inserted \cdrom\ under {\tt
-/cdrom/$<volume-name>$/}. In my opinion they should have pushed this
+{/cdrom/$<volume-name>$/}}. In my opinion they should have pushed this
 further and have {\em every\/} \cdrom\ on the local area network be
-mounted at the similar location, i.e., no matter in which particular
+mounted at the similar location, \ie, no matter in which particular
 machine you insert a \cdrom, it will always appear at the same
 position in the directory tree, on every system. When I wanted to
 implement such a user-program for \linux, I came across the
@@ -724,64 +757,60 @@
 
 \subsection{The preferred strategy of $open()$}
 
-The routines in \cdromc\ are designed in such a way that a run-time
+The routines in \cdromc\ are designed in such a way that run-time
 configuration of the behavior of \cdrom\ devices (of {\em any\/} type)
 can be carried out, by the $CDROM_SET/CLEAR_OPTIONS$ $ioctls$. Thus, various
 modes of operation can be set:
 \begin{description}
-\item[$CDO_AUTO_CLOSE \mathrel| CDO_USE_FFLAGS \mathrel| CDO_LOCK$]
-This is the default setting. (With $CDO_CHECK_TYPE$ it will be better,
-in the future.) If the device is not yet opened by any other process,
-and it is opened for data ($O_NONBLOCK$ is not set) and the tray is
-found open, an attempt to close the tray is made. Then, it is verified
-that a disc is in the drive and, if $CDO_CHECK_TYPE$ is set, that its
-type is `data mode 1.' Only if all tests are passed, the return value
-is zero. The door is locked to prevent file system corruption. If
-opened for audio ($O_NONBLOCK$ is set), no actions are taken and a
-value of 0 will be returned.
-\item[0] $Open()$ will always be successful, the option flags are
-ignored. Neither actions are undertaken, nor any integrity checks are
-made. 
-\item[$CDO_AUTO_CLOSE \mathrel| CDO_AUTO_EJECT \mathrel| CDO_LOCK$]
-This mimics the behavior of the current sbpcd-driver. The option flags
-are ignored, the tray is closed on the first open, if
-necessary. Similarly, the tray is opened on the last release, i.e., if
-a \cdrom\ is unmounted, it is automatically ejected, such that the
-user can replace it. 
-\end{description}
+\item[$CDO_AUTO_CLOSE \mathrel| CDO_USE_FFLAGS \mathrel| CDO_LOCK$] This
+is the default setting. (With $CDO_CHECK_TYPE$ it will be better, in the
+future.) If the device is not yet opened by any other process, and it
+the device is being opened for data ($O_NONBLOCK$ is not set) and the
+tray is found to be open, an attempt to close the tray is made. Then,
+it is verified that a disc is in the drive and, if $CDO_CHECK_TYPE$ is
+set, that it contains tracks of type `data mode 1.' Only if all tests
+are passed, the return value is zero. The door is locked to prevent file
+system corruption. If the drive is opened for audio ($O_NONBLOCK$ is
+set), no actions are taken and a value of 0 will be returned. 
+\item[$CDO_AUTO_CLOSE \mathrel| CDO_AUTO_EJECT \mathrel| CDO_LOCK$] This
+mimics the behavior of the current sbpcd-driver. The option flags are
+ignored, the tray is closed on the first open, if necessary. Similarly,
+the tray is opened on the last release, \ie, if a \cdrom\ is unmounted,
+it is automatically ejected, such that the user can replace it.
+\end{description} 
 We hope that these option can convince everybody (both driver
-maintainers and user program developers) to adapt to the new \cdrom\
-driver scheme and option flag interpretation. 
+maintainers and user program developers) to adopt the new \cdrom\
+driver scheme and option flag interpretation.
 
-\section{Description of routines in \cdromc}
+\newsection{Description of routines in \cdromc}
 
 Only a few routines in \cdromc\ are exported to the drivers. In this
-section we will treat these, as well as the functioning of the routines
-that `take over' the interface to the kernel. The header file
-belonging to \cdromc\ is called \ucdrom, but may be included in {\tt
-cdrom.h} in the future.
+newsection we will discuss these, as well as the functions that `take
+over' the \cdrom\ interface to the kernel. The header file belonging
+to \cdromc\ is called \cdromh. Formerly, some of the contents of this
+file were placed in the file {\tt {ucdrom.h}}, but this file has now been
+merged back into \cdromh.
 
 \subsection{$Struct\ file_operations\ cdrom_fops$}
 
-The contents of this structure has been described in
-section~\ref{cdrom.c}, and this structure should be used in
-registering the block device to the kernel:
+The contents of this structure were described in section~\ref{cdrom.c}.
+As already stated, this structure should be used to register block
+devices with the kernel:
 $$
 register_blkdev(major, <name>, \&cdrom_fops);
 $$
 
-\subsection{$Int\ register_cdrom( struct\ cdrom_device_info\ * cdi, 
-  char * name,)$}
+\subsection{$Int\ register_cdrom( struct\ cdrom_device_info\ * cdi)$}
 
-Similar to registering $cdrom_fops$ to the kernel, the device
-operations and information structures, as described in
-section~\ref{cdrom.c}, should be registered to the general \cdrom\ 
-interface:
+This function is used in about the same way one registers $cdrom_fops$
+with the kernel, the device operations and information structures,
+as described in section~\ref{cdrom.c}, should be registered with the
+\UCD:
 $$
-register_cdrom(\&<device>_info), <name>);
+register_cdrom(\&<device>_info));
 $$
 This function returns zero upon success, and non-zero upon
-failure. The structure $<device>_info$ should have a pointer the
+failure. The structure $<device>_info$ should have a pointer to the
 driver's $<device>_dops$, as in 
 $$
 \vbox{\halign{&$#$\hfil\cr
@@ -790,46 +819,46 @@
 &\ldots\cr
 \}\cr
 }}$$
-Note that a drivers has one static structure, $<device>_dops$, while
-it has as many structures $<device>_info$ as there are minor devices
+Note that a driver must have one static structure, $<device>_dops$, while
+it may have as many structures $<device>_info$ as there are minor devices
 active. $Register_cdrom()$ builds a linked list from these. 
 
 \subsection{$Int\ unregister_cdrom(struct\ cdrom_device_info * cdi)$}
 
-Unregistering device $cdi$ with minor number $MINOR(cdi\to dev)$
-removes the minor device from the list. If it was the last minor for
-the driver, this disconnects the registered device-operation routines
-from the \cdrom\ interface. This function returns zero upon success,
-and non-zero upon failure.
+Unregistering device $cdi$ with minor number $MINOR(cdi\to dev)$ removes
+the minor device from the list. If it was the last registered minor for
+the low-level driver, this disconnects the registered device-operation
+routines from the \cdrom\ interface. This function returns zero upon
+success, and non-zero upon failure.
 
 \subsection{$Int\ cdrom_open(struct\ inode * ip, struct\ file * fp)$}
 
 This function is not called directly by the low-level drivers, it is
 listed in the standard $cdrom_fops$. If the VFS opens a file, this
-function becomes active. A strategy logic is implemented in this
-routine, taking care of all capabilities and options that are set in
-the $cdrom_device_ops$ connected to the device. Then, the program flow is
-transferred to the device_dependent $open()$ call. 
+function becomes active. A strategy is implemented in this routine,
+taking care of all capabilities and options that are set in the
+$cdrom_device_ops$ connected to the device. Then, the program flow is
+transferred to the device_dependent $open()$ call.
 
 \subsection{$Void\ cdrom_release(struct\ inode *ip, struct\ file
 *fp)$}
 
 This function implements the reverse-logic of $cdrom_open()$, and then
-calls the device-dependent $release()$ routine.  When the use-count
-has reached 0, the allocated buffers in the are flushed by calls to
-$sync_dev(dev)$ and $invalidate_buffers(dev)$.
+calls the device-dependent $release()$ routine. When the use-count has
+reached 0, the allocated buffers are flushed by calls to $sync_dev(dev)$
+and $invalidate_buffers(dev)$.
 
 
 \subsection{$Int\ cdrom_ioctl(struct\ inode *ip, struct\ file *fp,
                        unsigned\ int\ cmd, unsigned\ long\ arg)$}
 \label{cdrom-ioctl}
 
-This function handles all $ioctl$ requests for \cdrom\ devices in a
-uniform way. The different calls fall into three categories: $ioctl$s
-that can be directly implemented by device operations, ones that are
-routed through the call $audio_ioctl()$, and the remaining ones, that
-are presumable device-dependent. Generally, a negative return value
-indicates an error. 
+This function handles all the standard $ioctl$ requests for \cdrom\
+devices in a uniform way. The different calls fall into three
+categories: $ioctl$s that can be directly implemented by device
+operations, ones that are routed through the call $audio_ioctl()$, and
+the remaining ones, that are presumable device-dependent. Generally, a
+negative return value indicates an error.
 
 \subsubsection{Directly implemented $ioctl$s}
 \label{ioctl-direct}
@@ -847,7 +876,7 @@
 \item[CDROM_GET_MCN or CDROM_GET_UPC] Get the Medium Catalog Number from a CD.
 \end{description}
 
-\subsubsection{$Ioctl$s rooted through $audio_ioctl()$}
+\subsubsection{$Ioctl$s routed through $audio_ioctl()$}
 \label{ioctl-audio}
 
 The following set of $ioctl$s are all implemented through a call to
@@ -887,10 +916,11 @@
 \item[CDROM_CLEAR_OPTIONS] Clear options specified by $arg$. Returns
   the option flag register after modification.
 \item[CDROM_SELECT_SPEED] Select head-rate speed of disc specified as
-  by $arg$. The value 0 means `auto-select', i.e., play audio discs at
-  real time and data disc at maximum speed. The value $arg$ is
-  checked against the maximum head rate of the drive found in
-  the $cdrom_dops$.
+  by $arg$ in units of standard cdrom speed (176\,kB/sec raw data or
+  150\,kB/sec file system data). The value 0 means `auto-select', \ie,
+  play audio discs at real time and data disc at maximum speed. The value
+  $arg$ is checked against the maximum head rate of the drive found in the
+  $cdrom_dops$.
 \item[CDROM_SELECT_DISC] Select disc numbered $arg$ from a juke-box.
   First disc is numbered 0. The number $arg$ is checked against the
   maximum number of discs in the juke-box found in the $cdrom_dops$.
@@ -902,8 +932,8 @@
   value $CDSL_CURRENT$ requests that information about the currently
   selected slot is returned.
 \item[CDROM_DRIVE_STATUS] Returns the status of the drive by a call to
-  $drive_status()$. Return values are as defined in section~\ref{drive
-    status}. Note that this call doesn't return information on the
+  $drive_status()$. Return values are defined in section~\ref{drive
+   status}. Note that this call doesn't return information on the
   current playing activity of the drive; this can be polled through an
   $ioctl$ call to $CDROMSUBCHNL$. For Juke-boxes, an extra argument
   $arg$ specifies the slot for which (possibly limited) information is
@@ -916,40 +946,38 @@
   juke-box. 
 \end{description}
 
-\subsubsection{Device dependent $ioct$s}
+\subsubsection{Device dependent $ioctl$s}
 
 Finally, all other $ioctl$s are passed to the function $dev_ioctl()$,
 if implemented. No memory allocation or verification is carried out. 
 
-\subsection{How to update your driver}
+\newsection{How to update your driver}
 
 \begin{enumerate}
 \item Make a backup of your current driver. 
-\item Get hold of the files \cdromc\ and \ucdrom, they should be in
+\item Get hold of the files \cdromc\ and \cdromh, they should be in
   the directory tree that came with this documentation.
-\item Include {\tt \char`\<linux/ucdrom.h>} just after {\tt cdrom.h}.
+\item Make sure you include \cdromh.
 \item Change the 3rd argument of $register_blkdev$ from
 $\&<your-drive>_fops$ to $\&cdrom_fops$. 
-\item Just after that line, add a line to register to the \cdrom\ 
-  routines:
-  $$register_cdrom(\&<your-drive>_info, <name>);$$
-  Similarly, add a
-  call to $unregister_cdrom()$.
+\item Just after that line, add the following to register with the \UCD:
+  $$register_cdrom(\&<your-drive>_info);$$
+  Similarly, add a call to $unregister_cdrom()$ at the appropriate place.
 \item Copy an example of the device-operations $struct$ to your
-  source, e.g., from {\tt cm206.c} $cm206_dops$, and change all
+  source, \eg, from {\tt {cm206.c}} $cm206_dops$, and change all
   entries to names corresponding to your driver, or names you just
   happen to like. If your driver doesn't support a certain function,
   make the entry $NULL$. At the entry $capability$ you should list all
-  capabilities your drive could support, in principle. If your drive
+  capabilities your driver currently supports. If your driver
   has a capability that is not listed, please send me a message.
 \item Copy the $cdrom_device_info$ declaration from the same example
   driver, and modify the entries according to your needs. If your
   driver dynamically determines the capabilities of the hardware, this
   structure should also be declared dynamically. 
 \item Implement all functions in your $<device>_dops$ structure,
-  according to prototypes listed in \ucdrom, and specifications given
+  according to prototypes listed in \cdromh, and specifications given
   in section~\ref{cdrom.c}. Most likely you have already implemented
-  the code in a large part, and you may just have to adapt the
+  the code in a large part, and you will almost certainly need to adapt the
   prototype and return values.
 \item Rename your $<device>_ioctl()$ function to $audio_ioctl$ and
   change the prototype a little. Remove entries listed in the first
@@ -968,111 +996,29 @@
   function, $<device>_ioctl$, the device-dependent $ioctl$s. Note that
   memory checking and allocation must be kept in this code!
 \item Change the prototypes of $<device>_open()$ and
-  $<device>_release()$, and remove any strategic code (i.e., tray
+  $<device>_release()$, and remove any strategic code (\ie, tray
   movement, door locking, etc.).
 \item Try to recompile the drivers. We advice you to use modules, both
-  for {\tt cdrom.o} and your driver, as debugging is much easier this
+  for {\tt {cdrom.o}} and your driver, as debugging is much easier this
   way.
 \end{enumerate} 
 
-\section{Thanks}
+\newsection{Thanks}
 
-Thanks to all the people involved. Thanks to Scott Snyder and Gerd
-Knorr, who were the first to implement this interface for SCSI and
-IDE-CD drivers and added many ideas for extension of the data
-structures relative to kernel~2.0. Further thanks to Thomas Quinot,
-Jon Tombs, Ken Pizzini, Eberhard M\"onkeberg and Andrew Kroll, the
-\linux\ \cdrom\ device driver developers who were kind enough to give
-suggestions and criticisms during the writing. Finally of course, I
-want to thank Linus Torvalds for making this possible in the first
-place.
+Thanks to all the people involved.  First, Erik Andersen, who has
+taken over the torch in maintaining \cdromc\ and integrating many
+\cdrom-related code in the 2.1-kernel.  Thanks to Scott Snyder and
+Gerd Knorr, who were the first to implement this interface for SCSI
+and IDE-CD drivers and added many ideas for extension of the data
+structures relative to kernel~2.0.  Further thanks to Heiko Eissfeldt,
+Thomas Quinot, Jon Tombs, Ken Pizzini, Eberhard M\"onkeberg and Andrew
+Kroll, the \linux\ \cdrom\ device driver developers who were kind
+enough to give suggestions and criticisms during the writing. Finally
+of course, I want to thank Linus Torvalds for making this possible in
+the first place.
 
 \vfill
 $ \version\ $
 \eject
 \end{document}
 
-\def\versionlog{
-$Log: cdrom-standard.tex,v $
-Revision 1.6  1996/12/29 20:45:18  davem
-Merge to 2.1.18, versioned module symbols are
-disabled until new modutils is released.
-
-Revision 1.6  1996/12/23 21:17:44  david
-Added reasons for speed selection.
-
-Revision 1.5  1996/12/22 21:54:25  david
-Repared version definition.
-
-Revision 1.4  1996/12/22 21:40:26  david
-Added older version log at end of text.
-
-Revision 1.3  1996/12/22 21:31:58  david
-Adapted text to fit kernel changes up to 2.1.15.
-
-Sun Dec 22 21:31:58 1996  David A. van Leeuwen  <david@ElseWare>
-
-        * cdrom-standard.tex: Adapted text to fit kernel changes up to 2.1.15.
-
-Sun Sep 22 20:18:00 1996  David
-
-        * cdrom-standard.tex:
-        Documentation adapted to align with developments started by Scott
-        Snyder, Gerd Knorr and myself.
-
-        Split _ops in _info (minor stuff) and _ops (major stuff).
-        Moved capability back to _ops, added n_minors.
-        Implemented use_count in _info.
-        Removed open_files().
-
-Sat Aug 10 10:57:16 1996  David 
-
-        * cdrom-standard.tex:
-        Weakened Eberhard's annoying comments by making it a quotation and
-        having it appear smaal-type. He doesn't react to my email-messages.
-
-        Changed float speed to int speed.
-
-Tue May 21 15:27:10 1996  David 
-
-        * cdrom-standard.tex:
-        Typographic errors introduced by Eberhard Moenkeberg were corrected,
-        as well one of my own typos. I deepfully respect the person that
-        corrects all typos i make in code and documentation!
-
-        Linux version 1.99.6.
-
-        * cdrom-standard.tex:
-        Changes made by Eberhard Moenkeberg, much to my annoyance. The
-        contents of the introduction were altered completely so that it now
-        says the opposite of what I wrote before: that there is no need for an
-        additional unform cdrom layer. This makes the whole document and the
-        project for that matter worthless.
-
-        ---david
-
-        * cdrom-standard.tex:
-        Version as it appeared first in the official kernel documentation
-        tree, Linux 1.99.2, Documentation/cdrom/cdrom-standard.tex.
-
-        Some improvements in use of English language have been made by people
-        that know better English than me.
-
-Wed Apr 17 20:46:34 1996  David 
-
-        * cdrom-standard.tex: changed #minors from 4 to 16.
-
-Sun Apr 14 20:53:17 1996  David 
-
-        * cdrom-standard.tex:
-        Update to go with cdrom.c version 0.3. More ioctl stuff.
-
-Wed Apr 10 18:00:28 1996  David 
-
-        * cdrom-standard.tex:
-        Version as first distributed among CDrom device driver authors.
-
-Mon Apr  8 18:25:21 1996  David A. van Leeuwen  <david@ElseWare>
-
-        * cdrom-standard.tex: *** empty log message ***
-}
diff -u --recursive --new-file v2.1.69/linux/Documentation/cdrom/cm206 linux/Documentation/cdrom/cm206
--- v2.1.69/linux/Documentation/cdrom/cm206	Sun May 12 11:36:00 1996
+++ linux/Documentation/cdrom/cm206	Tue Dec  2 11:41:44 1997
@@ -116,7 +116,7 @@
 -------------------------------------------------------
 If autoprobing does not work, you can hard-wire the default values
 of the base port address (CM206_BASE) and interrupt request line
-(CM206_IRQ) into the file ./include/linux/cm206.h. Change
+(CM206_IRQ) into the file /usr/src/linux/drivers/cdrom/cm206.h. Change
 the defines of CM206_IRQ and CM206_BASE.
 
 
diff -u --recursive --new-file v2.1.69/linux/Documentation/cdrom/gscd linux/Documentation/cdrom/gscd
--- v2.1.69/linux/Documentation/cdrom/gscd	Sat Jul  1 09:05:58 1995
+++ linux/Documentation/cdrom/gscd	Tue Dec  2 11:41:44 1997
@@ -23,7 +23,7 @@
 Installation
 ------------
 
-Change to '/usr/src/linux/include/linux' and edit the file 'gscd.h'. Insert
+Change to '/usr/src/linux/drivers/cdrom' and edit the file 'gscd.h'. Insert
 the i/o address of your interface card.
 
 The default base address is 0x340. This will work for most applications. 
diff -u --recursive --new-file v2.1.69/linux/Documentation/cdrom/ide-cd linux/Documentation/cdrom/ide-cd
--- v2.1.69/linux/Documentation/cdrom/ide-cd	Tue Nov 12 00:32:40 1996
+++ linux/Documentation/cdrom/ide-cd	Tue Dec  2 11:41:44 1997
@@ -379,12 +379,14 @@
  * interface by Erik Andersen <andersee@et.byu.edu>.
  */
 
+#include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
-#include <stdio.h>
-#include <linux/ucdrom.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/cdrom.h>
 
 
 int
@@ -395,7 +397,7 @@
 	int fd;           /* file descriptor for CD-ROM device */
 	int status;       /* return status for system calls */
 	int verbose = 0;
-	int x_slot = -1;
+	int slot=-1, x_slot;
 	int total_slots_available;
 
 	program = argv[0];
@@ -419,10 +421,10 @@
 	device = argv[0];
  
 	if (argc == 2)
-		x_slot = atoi (argv[1]) - 1;
+		slot = atoi (argv[1]) - 1;
 
 	/* open device */ 
-	fd = open (device, 0);
+	fd = open(device, O_RDONLY | O_NONBLOCK);
 	if (fd < 0) {
 		fprintf (stderr, "%s: open failed for `%s': %s\n",
 			 program, device, strerror (errno));
@@ -437,8 +439,8 @@
 		exit (1);
 	}
 
-	if (x_slot >= 0) {
-		if (x_slot >= total_slots_available) {
+	if (slot >= 0) {
+		if (slot >= total_slots_available) {
 			fprintf (stderr, "Bad slot number.  "
 				 "Should be 1 -- %d.\n",
 				 total_slots_available);
@@ -446,19 +448,33 @@
 		}
 
 		/* load */ 
-		status = ioctl (fd, CDROM_SELECT_DISC, x_slot);
+		slot=ioctl (fd, CDROM_SELECT_DISC, slot);
+		if (slot<0) {
+			fflush(stdout);
+				perror ("CDROM_SELECT_DISC ");
+			exit(1);
+		}
 	}
 
-	if (x_slot < 0 || verbose) {
+	if (slot < 0 || verbose) {
 
-		status = ioctl (fd, CDROM_SELECT_DISC, CDSL_CURRENT);
+		status=ioctl (fd, CDROM_SELECT_DISC, CDSL_CURRENT);
+		if (status<0) {
+			fflush(stdout);
+			perror (" CDROM_SELECT_DISC");
+			exit(1);
+		}
+		slot=status;
 
-		printf ("Current slot: %d\n", status+1);
+		printf ("Current slot: %d\n", slot+1);
 		printf ("Total slots available: %d\n",
 			total_slots_available);
 
 		printf ("Drive status: ");
-		switch (ioctl (fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) {
+                status = ioctl (fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
+                if (status<0) {
+                  perror(" CDROM_DRIVE_STATUS");
+                } else switch(status) {
 		case CDS_DISC_OK:
 			printf ("Ready.\n");
 			break;
@@ -475,23 +491,58 @@
 
 		for (x_slot=0; x_slot<total_slots_available; x_slot++) {
 			printf ("Slot %2d: ", x_slot+1);
-			switch (ioctl (fd, CDROM_DRIVE_STATUS, x_slot)) {
+             		status = ioctl (fd, CDROM_DRIVE_STATUS, x_slot);
+             		if (status<0) {
+             		     perror(" CDROM_DRIVE_STATUS");
+             		} else switch(status) {
 			case CDS_DISC_OK:
 				printf ("Disc present.");
 				break;
 			case CDS_NO_DISC: 
 				printf ("Empty slot.");
 				break;
+			case CDS_TRAY_OPEN:
+				printf ("CD-ROM tray open.\n");
+				break;
+			case CDS_DRIVE_NOT_READY:
+				printf ("CD-ROM drive not ready.\n");
+				break;
 			case CDS_NO_INFO:
-				printf ("No Information.");
+				printf ("No Information available.");
 				break;
 			default:
 				printf ("This Should not happen!\n");
 				break;
 			}
-			switch (ioctl (fd, CDROM_MEDIA_CHANGED, x_slot)) {
+		  if (slot == x_slot) {
+                  status = ioctl (fd, CDROM_DISC_STATUS);
+                  if (status<0) {
+			perror(" CDROM_DISC_STATUS");
+                  }
+		  switch (status) {
+			case CDS_AUDIO:
+				printf ("\tAudio disc.\t");
+				break;
+			case CDS_DATA_1:
+			case CDS_DATA_2:
+				printf ("\tData disc type %d.\t", status-CDS_DATA_1+1);
+				break;
+			case CDS_XA_2_1:
+			case CDS_XA_2_2:
+				printf ("\tXA data disc type %d.\t", status-CDS_XA_2_1+1);
+				break;
+			default:
+				printf ("\tUnknown disc type 0x%x!\t", status);
+				break;
+			}
+			}
+                  	status = ioctl (fd, CDROM_MEDIA_CHANGED, x_slot);
+                  	if (status<0) {
+				perror(" CDROM_MEDIA_CHANGED");
+                  	}
+		  	switch (status) {
 			case 1:
-				printf ("\t\tChanged.\n");
+				printf ("Changed.\n");
 				break;
 			default:
 				printf ("\n");
diff -u --recursive --new-file v2.1.69/linux/Documentation/cdrom/isp16 linux/Documentation/cdrom/isp16
--- v2.1.69/linux/Documentation/cdrom/isp16	Thu Apr 11 23:49:29 1996
+++ linux/Documentation/cdrom/isp16	Tue Dec  2 11:41:44 1997
@@ -82,7 +82,7 @@
   dma=0
   drive_type=Sanyo
 reflecting my own configuration. The defaults can be changed in
-the file ~/include/linux/ips16.h.
+the file linux/drivers/cdrom/ips16.h.
 
 The cdrom interface can be configured at run time by loading the
 initialisation driver as a module. In that case, the interface
diff -u --recursive --new-file v2.1.69/linux/Documentation/cdrom/sbpcd linux/Documentation/cdrom/sbpcd
--- v2.1.69/linux/Documentation/cdrom/sbpcd	Tue Feb  6 22:17:49 1996
+++ linux/Documentation/cdrom/sbpcd	Tue Dec  2 11:41:44 1997
@@ -88,7 +88,7 @@
 to configure the CDROM interface for type "Panasonic" (LaserMate) and address
 0x340.
 
-The interface type has to get configured in /usr/include/linux/sbpcd.h, 
+The interface type has to get configured in linux/drivers/cdrom/sbpcd.h, 
 because the register layout is different between the "SoundBlaster" and the
 "LaserMate" type.
 
@@ -176,7 +176,7 @@
 - this README file
 - the driver file linux/drivers/cdrom/sbpcd.c
 - the stub files linux/drivers/cdrom/sbpcd[234].c
-- the header file linux/include/linux/sbpcd.h.
+- the header file linux/drivers/cdrom/sbpcd.h.
 
 
 To install:
@@ -189,7 +189,7 @@
    correct if you can mount a data CD. There is no choice for you with the
    type; only one is the right, the others are deadly wrong.
 
-   a. Go into /usr/src/linux/include/linux/sbpcd.h and configure it for your
+   a. Go into /usr/src/linux/drivers/cdrom/sbpcd.h and configure it for your
       hardware (near the beginning):
       a1. Set it up for the appropriate type of interface board.
           "Original" CreativeLabs sound cards need "SBPRO 1".
@@ -568,10 +568,10 @@
 #include <linux/cdrom.h>
 
 #ifdef AZT_PRIVATE_IOCTLS
-#include <linux/aztcd.h>
+#include <linux/../../drivers/cdrom/aztcd.h>
 #endif AZT_PRIVATE_IOCTLS
 #ifdef SBP_PRIVATE_IOCTLS
-#include <linux/sbpcd.h>
+#include <linux/../../drivers/cdrom/sbpcd.h>
 #include <linux/fs.h>
 #endif SBP_PRIVATE_IOCTLS
 
diff -u --recursive --new-file v2.1.69/linux/Documentation/cdrom/sjcd linux/Documentation/cdrom/sjcd
--- v2.1.69/linux/Documentation/cdrom/sjcd	Thu Apr 11 23:49:29 1996
+++ linux/Documentation/cdrom/sjcd	Tue Dec  2 11:41:44 1997
@@ -39,7 +39,7 @@
 file /etc/lilo.conf). You could also use 'append="sjcd=<configuration_info>"'
 in the appropriate section of /etc/lilo.conf
 If you're building a kernel yourself you can set your default base
-i/o address with SJCD_BASE_ADDR in include/linux/sjcd.h.
+i/o address with SJCD_BASE_ADDR in /usr/src/linux/drivers/cdrom/sjcd.h.
 
 The sjcd driver supports being loaded as a module. The following
 command will set the base i/o address on the fly (assuming you
diff -u --recursive --new-file v2.1.69/linux/Documentation/cdrom/sonycd535 linux/Documentation/cdrom/sonycd535
--- v2.1.69/linux/Documentation/cdrom/sonycd535	Sat Jul  1 09:05:58 1995
+++ linux/Documentation/cdrom/sonycd535	Tue Dec  2 11:41:44 1997
@@ -36,7 +36,7 @@
 	  recognized
 
 	- you must enter your interface address into 
-          /usr/src/linux/include/linux/sonycd535.h and build the
+          /usr/src/linux/drivers/cdrom/sonycd535.h and build the
           appropriate kernel or use the "kernel command line" parameter
                 sonycd535=0x320
           with the correct interface address.
diff -u --recursive --new-file v2.1.69/linux/Documentation/ioctl-number.txt linux/Documentation/ioctl-number.txt
--- v2.1.69/linux/Documentation/ioctl-number.txt	Sat Nov 29 11:25:08 1997
+++ linux/Documentation/ioctl-number.txt	Tue Dec  2 11:41:44 1997
@@ -74,8 +74,7 @@
 'P'	all	linux/soundcard.h
 'Q'	all	linux/soundcard.h
 'R'	all	linux/random.h
-'S'	00-1F	linux/cdrom.h
-'S'	20-7F	linux/ucdrom.h
+'S'	00-7F	linux/cdrom.h
 'S'	80-81	scsi/scsi_ioctl.h
 'S'	82-FF	scsi/scsi.h
 'T'	all	linux/soundcard.h	conflict!
diff -u --recursive --new-file v2.1.69/linux/Documentation/specialix.txt linux/Documentation/specialix.txt
--- v2.1.69/linux/Documentation/specialix.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/specialix.txt	Tue Dec  2 09:19:03 1997
@@ -0,0 +1,334 @@
+
+      specialix.txt  -- specialix IO8+ multiport serial driver readme.
+
+
+
+      Copyright (C) 1997  Roger Wolff (R.E.Wolff@BitWizard.nl)
+
+      Specialix pays for the development and support of this driver.
+      Please DO contact io8-linux@specialix.co.uk if you require
+      support.
+
+      This driver was developed in the BitWizard linux device
+      driver service. If you require a linux device driver for your
+      product, please contact devices@BitWizard.nl for a quote.
+
+      This code is firmly based on the riscom/8 serial driver,
+      written by Dmitry Gorodchanin. The specialix IO8+ card
+      programming information was obtained from the CL-CD1865 Data
+      Book, and Specialix document number 6200059: IO8+ Hardware
+      Functional Specification.
+
+      This program is free software; you can redistribute it and/or
+      modify it under the terms of the GNU General Public License as
+      published by the Free Software Foundation; either version 2 of
+      the License, or (at your option) any later version.
+
+      This program is distributed in the hope that it will be
+      useful, but WITHOUT ANY WARRANTY; without even the implied
+      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+      PURPOSE.  See the GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public
+      License along with this program; if not, write to the Free
+      Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+      USA.
+
+
+Intro
+=====
+
+ 
+This file contains some random information, that I like to have online
+instead of in a manual that can get lost. Ever misplace your Linux
+kernel sources?  And the manual of one of the boards in your computer?
+
+
+Adresses and interrupts
+=======================
+
+Addres dip switch settings:
+The dip switch sets bits 2-9 of the IO address. 
+
+       9 8 7 6 5 4 3 2 
+     +-----------------+
+   0 | X   X X X X X X |
+     |                 |    =   IoBase = 0x100 
+   1 |   X             |
+     +-----------------+          ------ RS232 connectors ---->
+         
+         |    |    |
+       edge connector
+         |    |    |
+         V    V    V
+
+Base address 0x100 caused a conflict in one of my computers once.  I
+haven't the foggiest why. My Specialix card is now at 0x180.  My
+other computer runs just fine with the Specialix card at 0x100....
+The card occupies 4 addresses, but actually only two are really used.
+
+The driver now still autoprobes at 0x100, 0x180, 0x250 and 0x260.  If
+that causes trouble for you, please report that. I'll remove
+autoprobing then.
+
+The driver will tell the card what IRQ to use, so you don't have to
+change any jumpers to change the IRQ. Just use a command line
+argument (irq=xx) to the insmod program to set the interrupt.
+
+If your specialix cards are not at the default locations, you can use
+the kernel command line argument "specialix=io0,irq0,io1,irq1...".
+Here "io0" is the io address for the first card, and "irq0" is the
+irq line that the first card should use. And so on. 
+
+Examples. 
+
+You use the driver as a module and have three cards at 0x100, 0x250
+and 0x180. And some way or another you want them detected in that
+order. Moreover irq 12 is taken (e.g. by your PS/2 mouse).
+
+  insmod specialix.o iobase=0x100,0x250,0x180 irq=9,11,15
+
+The same three cards, but now in the kernel would require you to
+add 
+
+   specialix=0x100,9,0x250,11,0x180,15
+
+to the command line. This would become 
+
+   append="specialix=0x100,9,0x250,11,0x180,15" 
+
+in your /etc/lilo.conf file if you use lilo. 
+
+
+Baud rates
+==========
+
+The rev 1.2 and below boards use a CL-CD1864. These chips can only 
+do 64kbit. The rev 1.3 and newer boards use a CL-CD1865. These chips
+are officially capable of 115k2.
+
+The Specialix card uses a 25MHz crystal (in times two mode, which in
+fact is a divided by two mode). This is not enough to reach the rated
+115k2 on all ports at the same time. With this clock rate you can only
+do 37% of this rate. This means that at 115k2 on all ports you are
+going to loose characters (The chip cannot handle that many incoming
+bits at this clock rate.) (Yes, you read that correctly: there is a
+limit to the number of -=bits=- per second that the chip can handle.)
+
+If you near the "limit" you will first start to see a graceful
+degradation in that the chip cannot keep the transmitter busy at all
+times. However with a central clock this slow, you can also get it to
+miss incoming characters.
+
+The specialix card cannot reliably do 115k2. If you use it, you have
+to do "extensive testing" (*) to verify if it actually works.
+
+When "mgetty" communicates with my modem at 115k2 it reports:
+got: +++[0d]ATQ0V1H0[0d][0d][8a]O[cb][0d][8a]
+                            ^^^^ ^^^^ ^^^^ 
+
+The three characters that have the "^^^" under them have suffered a
+bit error in the highest bit. In conclusion: I've tested it, and found
+that it simply DOESN"T work for me. I also suspect that this is also
+caused by the baud rate being just a little bit out of tune. 
+
+
+(*) Cirrus logic CD1864 databook, page 40.
+
+
+Cables for the Specialix IO8+
+=============================
+
+The pinout of the connectors on the IO8+ is:
+
+     pin    short    direction    long name
+            name
+    Pin 1   DCD      input        Data Carrier Detect
+    Pin 2   RXD      input        Receive
+    Pin 3   DTR/RTS  output       Data Terminal Ready/Ready To Send
+    Pin 4   GND      -            Ground
+    Pin 5   TXD      output       Transmit
+    Pin 6   CTS      input        Clear To Send
+        
+    
+             -- 6  5  4  3  2  1 --
+             |                    |
+             |                    |
+             |                    |
+             |                    |
+             +-----          -----+
+                  |__________|
+                      clip
+    
+    Front view of an RJ12 connector. Cable moves "into" the paper.
+    (the plug is ready to plug into your mouth this way...)
+
+    
+    NULL cable. I don't know who is going to use these except for
+    testing purposes, but I tested the cards with this cable. (It 
+    took quite a while to figure out, so I'm not going to delete
+    it. So there! :-)
+    
+    
+    This end goes               This end needs
+    straight into the           some twists in
+    RJ12 plug.                  the wiring.
+       IO8+ RJ12                   IO8+ RJ12
+        1  DCD       white          -
+        -             -             1 DCD
+        2  RXD       black          5 TXD
+        3  DTR/RTS   red            6 CTS
+        4  GND       green          4 GND
+        5  TXD       yellow         2 RXD
+        6  CTS       blue           3 DTR/RTS
+    
+
+    Same NULL cable, but now sorted on the second column.
+ 
+        1  DCD       white          -
+        -             -             1 DCD
+        5  TXD       yellow         2 RXD
+        6  CTS       blue           3 DTR/RTS
+        4  GND       green          4 GND
+        2  RXD       black          5 TXD
+        3  DTR/RTS   red            6 CTS
+    
+    
+    
+    This is a modem cable usable for hardware handshaking:
+        RJ12                        DB25           DB9
+        1   DCD      white          8 DCD          1 DCD
+        2   RXD      black          3 RXD          2 RXD
+        3   DTR/RTS  red            4 RTS          7 RTS
+        4   GND      green          7 GND          5 GND
+        5   TXD      yellow         2 TXD          3 TXD
+        6   CTS      blue           5 CTS          8 CTS
+                            +----   6 DSR          6 DSR
+                            +----  20 DTR          4 DTR
+
+    This is a modem cable usable for software handshaking:
+    It allows you to reset the modem using the DTR ioctls.
+    I (REW) have never tested this, "but xxxxxxxxxxxxx
+    says that it works." If you test this, please
+    tell me and I'll fill in your name on the xxx's.
+
+        RJ12                        DB25           DB9
+        1   DCD      white          8 DCD          1 DCD
+        2   RXD      black          3 RXD          2 RXD
+        3   DTR/RTS  red           20 DTR          4 DTR
+        4   GND      green          7 GND          5 GND
+        5   TXD      yellow         2 TXD          3 TXD
+        6   CTS      blue           5 CTS          8 CTS
+                            +----   6 DSR          6 DSR
+                            +----   4 RTS          7 RTS
+
+   I bought a 6 wire flat cable. It was colored as indicated.
+   Check that yours is the same before you trust me on this.
+   
+ 
+Hardware handshaking issues.
+============================
+
+The driver can be compiled in two different ways. The default
+("Specialix DTR/RTS pin is RTS" is off) the pin behaves as DTR when
+hardware handshaking is off. It behaves as the RTS hardware
+handshaking signal when hardware handshaking is selected.
+
+When you use this, you have to use the appropriate cable. The
+cable will either be compatible with hardware handshaking or with
+software handshaking. So switching on the fly is not really an
+option.
+
+I actually prefer to use the "Specialix DTR/RTS pin is RTS" option.
+This makes the DTR/RTS pin always an RTS pin, and ioctls to
+change DTR are always ignored. I have a cable that is configured
+for this. 
+
+
+Ports and devices
+=================
+
+Port 0 is the one furthest from the ISA connector.
+
+Devices:
+
+You should make the devices as follows:
+
+bash
+cd /dev
+for i in  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 \
+         16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
+do
+  echo -n "$i "
+  mknod /dev/ttyW$i c 75 $i
+  mknod /dev/cuw$i c 76 $i
+done
+echo ""
+
+
+You cannot have more than 4 boards in one computer. The card only
+supports 4 different interrupts. If you really want this, contact me
+about this and I'll give you a few tips (requires soldering iron)....
+
+
+------------------------------------------------------------------------
+
+
+  Fixed bugs and restrictions:
+       - During intialization, interrupts are blindly turned on.
+            Having a shadow variable would cause an extra memory
+            access on every IO instruction. 
+       - The interrupt (on the card) should be disabled when we
+         don't allocate the Linux end of the interrupt. This allows 
+         a different driver/card to use it while all ports are not in
+         use..... (a la standard serial port)
+       == An extra _off variant of the sx_in and sx_out macros are
+          now available. They don't set the interrupt enable bit.
+          These are used during initialization. Normal operation uses
+          the old variant which enables the interrupt line.
+       - RTS/DTR issue needs to be implemented according to 
+         specialix' spec.
+            I kind of like the "determinism" of the current 
+            implementation. Compile time flag?
+       == Ok. Compile time flag! Default is how Specialix likes it.
+       == Now a config time flag! Gets saved in your config file. Neat!
+       - Can you set the IO address from the lilo command line?
+            If you need this, bug me about it, I'll make it. 
+       == Hah! No bugging needed. Fixed! :-)
+       - Cirrus logic hasn't gotten back to me yet why the CD1865 can
+            and the CD1864 can't do 115k2. I suspect that this is
+            because the CD1864 is not rated for 33MHz operation.
+            Therefore the CD1864 versions of the card can't do 115k2 on 
+            all ports just like the CD1865 versions. The driver does
+            not block 115k2 on CD1864 cards. 
+        == I called the Cirrus Logic representative here in Holland.
+           The CD1864 databook is identical to the CD1865 databook, 
+           except for an extra warning at the end. Similar Bit errors
+           have been observed in testing at 115k2 on both an 1865 and
+           a 1864 chip. I see no reason why I would prohibit 115k2 on
+           1864 chips and not do it on 1865 chips. Actually there is
+           reason to prohibit it on BOTH chips. I print a warning.
+           If you use 115k2, you're on your own. 
+       - A spiky CD may send spurious HUPs. Also in CLOCAL???
+         -- A fix for this turned out to be counter productive. 
+            Different fix? Current behaviour is acceptable?
+         -- Maybe the current implementation is correct. If anybody
+            gets bitten by this, please report, and it will get fixed.
+
+         -- Testing revealed that when in CLOCAL, the problem doesn't
+            occur. As warned for in the CD1865 manual, the chip may
+            send modem intr's on a spike. We could filter those out,
+            but that would be a cludge anyway (You'd still risk getting
+            a spurious HUP when two spikes occur.).....
+ 
+
+
+  Bugs & restrictions:
+       - This is a difficult card to autoprobe.
+            You have to WRITE to the address register to even 
+            read-probe a CD186x register. Disable autodetection?
+         -- Specialix: any suggestions?
+       - Arbitrary baud rates are not implemented yet. 
+            If you need this, bug me about it. 
+
+
diff -u --recursive --new-file v2.1.69/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.69/linux/MAINTAINERS	Sat Nov 29 11:25:09 1997
+++ linux/MAINTAINERS	Tue Dec  2 11:41:44 1997
@@ -414,6 +414,13 @@
 W:	http://www.nyx.net/~arobinso
 S:	Maintainted
 
+SPECIALIX IO8+ MULTIPORT SERIAL CARD DRIVER
+P:	Roger Wolff
+M:	R.E.Wolff@BitWizard.nl
+M:	io8-linux@specialix.co.uk
+L:	linux-kernel@vger.rutgers.edu ?
+S:	Supported
+
 MOUSE AND MISC DEVICES [GENERAL]
 P:	Alessandro Rubini
 M:	rubini@ipvvis.unipv.it
@@ -463,6 +470,12 @@
 NON-IDE/NON-SCSI CDROM DRIVERS [GENERAL] (come on, crew - mark your responsibility)
 P:	Eberhard Moenkeberg
 M:	emoenke@gwdg.de
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
+UNIFORM CDROM DRIVER 
+P:	Erik Andersen
+M:	andersee@debian.org
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
diff -u --recursive --new-file v2.1.69/linux/Makefile linux/Makefile
--- v2.1.69/linux/Makefile	Tue Dec  2 09:49:38 1997
+++ linux/Makefile	Tue Dec  2 11:41:44 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 69
+SUBLEVEL = 70
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
@@ -133,7 +133,7 @@
 DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
 endif
 
-ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR),)
+ifeq ($(CONFIG_CDROM),y)
 DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a
 endif
 
diff -u --recursive --new-file v2.1.69/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v2.1.69/linux/arch/alpha/config.in	Sat Oct 25 02:44:14 1997
+++ linux/arch/alpha/config.in	Tue Dec  2 11:41:44 1997
@@ -152,6 +152,17 @@
 fi
 endmenu
 
+# Conditionally compile in the Uniform CD-ROM driver
+if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then
+  define_bool CONFIG_CDROM y
+else
+  if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then
+    define_bool CONFIG_CDROM m
+  else
+    define_bool CONFIG_CDROM n
+  fi
+fi
+
 source fs/Config.in
 
 source fs/nls/Config.in
diff -u --recursive --new-file v2.1.69/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S
--- v2.1.69/linux/arch/alpha/kernel/entry.S	Mon Dec  1 12:04:11 1997
+++ linux/arch/alpha/kernel/entry.S	Tue Dec  2 09:15:11 1997
@@ -33,11 +33,11 @@
 #define TASK_COUNTER	8
 #define TASK_PRIORITY	16
 #define TASK_FLAGS	24
+#define TASK_SIGPENDING	32	
 
 /*
  * task flags (must match include/linux/sched.h):
  */
-#define PF_SIGPENDING	0x00000008
 #define	PF_PTRACED	0x00000010
 
 /*
@@ -543,9 +543,8 @@
 	lda	$4,init_task_union
 	bne	$2,reschedule
 	xor	$4,$8,$4
-	ldq	$5,TASK_FLAGS($8)
+	ldl	$5,TASK_SIGPENDING($8)
 	beq	$4,restore_all
-	and	$5,PF_SIGPENDING,$5
 	bne	$5,signal_return
 restore_all:
 	RESTORE_ALL
diff -u --recursive --new-file v2.1.69/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.1.69/linux/arch/i386/config.in	Sat Oct 25 02:44:14 1997
+++ linux/arch/i386/config.in	Tue Dec  2 11:41:44 1997
@@ -102,6 +102,17 @@
 fi
 endmenu
 
+# Conditionally compile in the Uniform CD-ROM driver
+if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then
+  define_bool CONFIG_CDROM y
+else
+  if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then
+    define_bool CONFIG_CDROM m
+  else
+    define_bool CONFIG_CDROM n
+  fi
+fi
+
 source fs/Config.in
 
 source fs/nls/Config.in
diff -u --recursive --new-file v2.1.69/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.69/linux/arch/i386/defconfig	Tue Dec  2 09:49:38 1997
+++ linux/arch/i386/defconfig	Tue Dec  2 14:31:19 1997
@@ -200,6 +200,7 @@
 # CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
 #
 # CONFIG_CD_NO_IDESCSI is not set
+CONFIG_CDROM=y
 
 #
 # Filesystems
@@ -219,6 +220,7 @@
 CONFIG_NFSD=y
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
 # CONFIG_SMB_FS is not set
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
diff -u --recursive --new-file v2.1.69/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.1.69/linux/arch/i386/kernel/entry.S	Tue Dec  2 09:49:39 1997
+++ linux/arch/i386/kernel/entry.S	Tue Dec  2 09:15:11 1997
@@ -74,6 +74,7 @@
 counter		=  4
 priority	=  8
 flags		= 12
+sigpending	= 16
 dbgreg6		= 44
 dbgreg7		= 48
 exec_domain	= 52
@@ -169,7 +170,7 @@
 ret_with_reschedule:
 	cmpl $0,SYMBOL_NAME(need_resched)
 	jne reschedule
-	testb $0x8,flags(%ebx)		# PF_SIGPENDING
+	cmpl $0,sigpending(%ebx)
 	jne signal_return
 	RESTORE_ALL
 	ALIGN
diff -u --recursive --new-file v2.1.69/linux/arch/m68k/config.in linux/arch/m68k/config.in
--- v2.1.69/linux/arch/m68k/config.in	Sat Oct 25 02:44:14 1997
+++ linux/arch/m68k/config.in	Tue Dec  2 11:41:44 1997
@@ -207,6 +207,17 @@
 
 fi
 
+# Conditionally compile in the Uniform CD-ROM driver
+if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" ]; then
+  define_bool CONFIG_CDROM y
+else
+  if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" ]; then
+    define_bool CONFIG_CDROM m
+  else
+    define_bool CONFIG_CDROM n
+  fi
+fi
+
 source fs/Config.in
 
 source fs/nls/Config.in
diff -u --recursive --new-file v2.1.69/linux/arch/mips/config.in linux/arch/mips/config.in
--- v2.1.69/linux/arch/mips/config.in	Sat Oct 25 02:44:14 1997
+++ linux/arch/mips/config.in	Tue Dec  2 11:41:44 1997
@@ -179,6 +179,17 @@
 	endmenu
 fi
 
+# Conditionally compile in the Uniform CD-ROM driver
+if [ "$CONFIG_BLK_DEV_SR" = "y" ]; then
+  define_bool CONFIG_CDROM y
+else
+  if [ "$CONFIG_BLK_DEV_SR" = "m" ]; then
+    define_bool CONFIG_CDROM m
+  else
+    define_bool CONFIG_CDROM n
+  fi
+fi
+
 source fs/Config.in
 
 source fs/nls/Config.in
diff -u --recursive --new-file v2.1.69/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.1.69/linux/arch/ppc/config.in	Sat Oct 25 02:44:14 1997
+++ linux/arch/ppc/config.in	Tue Dec  2 11:41:44 1997
@@ -106,6 +106,17 @@
 fi
 endmenu
 
+# Conditionally compile in the Uniform CD-ROM driver
+if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then
+  define_bool CONFIG_CDROM y
+else
+  if [ "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then
+    define_bool CONFIG_CDROM m
+  else
+    define_bool CONFIG_CDROM n
+  fi
+fi
+
 source fs/Config.in
 
 source fs/nls/Config.in
diff -u --recursive --new-file v2.1.69/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.1.69/linux/arch/sparc/config.in	Wed Nov 12 13:34:25 1997
+++ linux/arch/sparc/config.in	Tue Dec  2 11:41:44 1997
@@ -144,6 +144,17 @@
 	endmenu
 fi
 
+# Conditionally compile in the Uniform CD-ROM driver
+if [ "$CONFIG_BLK_DEV_SR" = "y" ]; then
+  define_bool CONFIG_CDROM y
+else
+  if [ "$CONFIG_BLK_DEV_SR" = "m" ]; then
+    define_bool CONFIG_CDROM m
+  else
+    define_bool CONFIG_CDROM n
+  fi
+fi
+
 source fs/Config.in
 
 source fs/nls/Config.in
diff -u --recursive --new-file v2.1.69/linux/arch/sparc64/config.in linux/arch/sparc64/config.in
--- v2.1.69/linux/arch/sparc64/config.in	Sat Oct 25 02:44:15 1997
+++ linux/arch/sparc64/config.in	Tue Dec  2 11:41:44 1997
@@ -181,6 +181,17 @@
 	endmenu
 fi
 
+# Conditionally compile in the Uniform CD-ROM driver
+if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" ]; then
+  define_bool CONFIG_CDROM y
+else
+  if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" ]; then
+    define_bool CONFIG_CDROM m
+  else
+    define_bool CONFIG_CDROM n
+  fi
+fi
+
 source fs/Config.in
 
 source fs/nls/Config.in
diff -u --recursive --new-file v2.1.69/linux/drivers/ap1000/ddv.c linux/drivers/ap1000/ddv.c
--- v2.1.69/linux/drivers/ap1000/ddv.c	Sun Jan 26 02:07:10 1997
+++ linux/drivers/ap1000/ddv.c	Tue Dec  2 12:18:30 1997
@@ -382,7 +382,9 @@
 		save_flags(flags); cli();
 
 		while (!rem_queue) {
-			current->signal = 0;
+			spin_lock_irq(&current->sigmask_lock);
+			flush_signals(current);
+			spin_unlock_irq(&current->sigmask_lock);
 			interruptible_sleep_on(&ddv_daemon_wait);
 		}
 
diff -u --recursive --new-file v2.1.69/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.1.69/linux/drivers/block/ide-cd.c	Thu Mar 27 14:40:02 1997
+++ linux/drivers/block/ide-cd.c	Tue Dec  2 11:41:44 1997
@@ -1,15 +1,46 @@
-/* #define VERBOSE_IDE_CD_ERRORS 1 */
+#define VERBOSE_IDE_CD_ERRORS 1
 /*
  * linux/drivers/block/ide-cd.c
- * ATAPI cd-rom driver.  To be used with ide.c.
+ * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
+ * Copyright (C) 1996, 1997  Erik Andersen <andersee@debian.org>
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * ATAPI CD-ROM driver.  To be used with ide.c.
  * See Documentation/cdrom/ide-cd for usage information.
  *
- * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
- * Copyright (C) 1996  Erik Andersen <andersee@et.byu.edu>
+ * Suggestions are welcome. Patches that work are more welcome though.
+ * For those wishing to work on this driver, please be sure you download
+ * and comply with the latest ATAPI standard. This document can be
+ * obtained by anonymous ftp from fission.dt.wdc.com in directory:
+ * /pub/standards/SFF_atapi/spec/SFF8020-r2.6/PDF/8020r26.pdf
  *
- * May be copied or modified under the terms of the GNU General Public License
- * see linux/COPYING for more information.
+ * Drives that deviate from the ATAPI standard will be accomodated as much
+ * as possible via compile options.  Since I only have a few drives, you you
+ * generally need to send me patches...
  *
+ * ----------------------------------
+ * TO DO LIST:
+ * -Avoid printing error messages for expected errors from the drive.
+ *    (If you are using a cd changer, you may get errors in the kernel
+ *     logs that are completly expected.  Don't complain to me about this,
+ *     unless you have a patch to fix it.  I am working on it...)
+ * -Implement ide_cdrom_select_speed using the generic cdrom interface
+ * -Fix ide_cdrom_reset so that it works (it does nothing right now)
+ * -When trying to mount a cdrom with the tray open, you get an billion
+ *     "tray open or drive not ready" messages until the tray gets closed.
+ *     This is because ide-cd does not properly return drive_status immediatly,
+ *     but instead waits for the drive to close first (bad, bad, bad)
+ *     and keeps on trying, and failing...  This bug was revealed by the 
+ *     recent changes to the Uniform CD-ROm driver, and I havn't had a 
+ *     chance to fix it yet.
+ *
+ * MOSTLY DONE LIST:
+ *  Query the drive to find what features are available
+ *   before trying to use them.
+ *
+ *
+ * ----------------------------------
  * 1.00  Oct 31, 1994 -- Initial version.
  * 1.01  Nov  2, 1994 -- Fixed problem with starting request in
  *                       cdrom_check_status.
@@ -121,7 +152,7 @@
  *                       
  *                       
  * 4.00  Nov 5, 1996   -- New ide-cd maintainer,
- *                                 Erik B. Andersen <andersee@et.byu.edu>
+ *                                 Erik B. Andersen <andersee@debian.org>
  *                     -- Newer Creative drives don't always set the error
  *                          register correctly.  Make sure we see media changes
  *                          regardless.
@@ -147,32 +178,16 @@
  * 4.04  Dec 29, 1996  -- Added CDROMREADRAW ioclt based on patch 
  *                          by Ales Makarov (xmakarov@sun.felk.cvut.cz)
  *
+ * 4.05  Nov 20, 1997  -- Modified to print more drive info on init
+ *                        Minor other changes
+ *                        Fix errors on CDROMSTOP (If you have a "Dolphin",
+ *                          you must define IHAVEADOLPHIN)
+ *                        Added identifier so new Sanyo CD-changer works
+ *                        Better detection if door locking isn't supported
  *
- * MOSTLY DONE LIST:
- *  Query the drive to find what features are available
- *   before trying to use them.
- *
- * TO DO LIST:
- *  Avoid printing error messages for expected errors from the drive.
- *    (If you are using a cd changer, you may get errors in the kernel
- *     logs that are completly expected.  Don't complain to me about this,
- *     unless you have a patch to fix it.  I am working on it...)
- *  Reset unlocks drive?
- *  Implement ide_cdrom_disc_status using the generic cdrom interface
- *  Implement ide_cdrom_select_speed using the generic cdrom interface
- *  Fix ide_cdrom_reset so that it works (it does nothing right now)
- *
- *  -- Suggestions are welcome.  Patches that work are more welcome though.
- *       For those wishing to work on this driver, please be sure you download
- *       and comply with the latest ATAPI standard.  This document can be
- *       obtained by anonymous ftp from fission.dt.wdc.com in directory:
- *       /pub/standards/atapi/spec/SFF8020-r2.6/PDF/8020r26.pdf
- *
- */
-
-
-/***************************************************************************/
+ *************************************************************************/
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -185,7 +200,6 @@
 #include <linux/errno.h>
 #include <linux/hdreg.h>
 #include <linux/cdrom.h>
-#include <linux/ucdrom.h>
 #include <asm/irq.h>
 #include <asm/io.h>
 #include <asm/byteorder.h>
@@ -463,13 +477,16 @@
 				   because workman constantly polls the drive
 				   with this command, and we don't want
 				   to uselessly fill up the syslog. */
-				if (pc->c[0] != SCMD_READ_SUBCHANNEL)
+				if (pc->c[0] != SCMD_READ_SUBCHANNEL) {
 					printk ("%s: tray open or drive not ready\n",
 						drive->name);
+					return 1;
+				}
 			} else if (sense_key == UNIT_ATTENTION) {
 				/* Check for media change. */
 				cdrom_saw_media_change (drive);
-				printk ("%s: media changed\n", drive->name);
+				printk (" %s: media changed\n", drive->name);
+				return 0;
 			} else {
 				/* Otherwise, print an error. */
 				ide_dump_status (drive, "packet command error",
@@ -1094,8 +1111,13 @@
 		if (pc->buflen == 0)
 			cdrom_end_request (1, drive);
 		else {
+			/* Comment this out, because this always happins 
+			   right after a reset occurs, and it is annoying to 
+			   always print expected stuff.  */
+			/*
 			printk ("%s: cdrom_pc_intr: data underrun %d\n",
 				drive->name, pc->buflen);
+			*/
 			pc->stat = 1;
 			cdrom_end_request (1, drive);
 		}
@@ -1355,7 +1377,7 @@
 static inline
 void lba_to_msf (int lba, byte *m, byte *s, byte *f)
 {
-	lba += CD_BLOCK_OFFSET;
+	lba += CD_MSF_OFFSET;
 	lba &= 0xffffff;  /* negative lbas use only 24 bits */
 	*m = lba / (CD_SECS * CD_FRAMES);
 	lba %= (CD_SECS * CD_FRAMES);
@@ -1367,7 +1389,7 @@
 static inline
 int msf_to_lba (byte m, byte s, byte f)
 {
-	return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_BLOCK_OFFSET;
+	return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET;
 }
 
 
@@ -1421,7 +1443,7 @@
 	   probably cannot lock the door. */
 	if (stat != 0 &&
 	    reqbuf->sense_key == ILLEGAL_REQUEST &&
-	    reqbuf->asc == 0x24) {
+	    (reqbuf->asc == 0x24 || reqbuf->asc == 0x20)) {
 		printk ("%s: door locking not supported\n",
 			drive->name);
 		CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
@@ -2325,12 +2347,17 @@
 		return cdrom_startstop (drive, 1, NULL);
 
 	case CDROMSTOP: {
-		int stat;
-
-		stat = cdrom_startstop (drive, 0, NULL);
-		if (stat) return stat;
-		/* pit says the Dolphin needs this. */
-		return cdrom_eject (drive, 1, NULL);
+#ifdef IHAVEADOLPHIN
+               /*  Certain Drives require this.  Most don't
+                   and will produce errors upon CDROMSTOP
+                   pit says the Dolphin needs this.  If you
+                   own a dolphin, just define IHAVEADOLPHIN somewhere */
+                int stat;
+                stat = cdrom_startstop (drive, 0, NULL);
+                if (stat) return stat;
+                return cdrom_eject (drive, 1, NULL);
+#endif /* end of IHAVEADOLPHIN  */
+               return cdrom_startstop (drive, 0, NULL);
 	}
 
 	case CDROMPAUSE:
@@ -2360,7 +2387,7 @@
 	return ide_do_drive_cmd (drive, &req, ide_wait);
 #endif
 
-/* For now, just return 0, as if things worked...	*/
+/* For now, just return 0, as if things had worked...	*/
 	return 0;
 
 
@@ -2399,10 +2426,8 @@
 	int stat;
 	int nslots, curslot;
 
-	if ( ! CDROM_CONFIG_FLAGS (drive)->is_changer) {
-		printk ("%s: Not a changer.", drive->name);
-		return -EINVAL;
-	}
+	if ( ! CDROM_CONFIG_FLAGS (drive)->is_changer) 
+		return -EDRIVE_CANT_DO_THIS;
 
 #if ! STANDARD_ATAPI
 	if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
@@ -2436,7 +2461,7 @@
 
 	stat = cdrom_check_status (drive, &my_reqbuf);
 	if (stat && my_reqbuf.sense_key == NOT_READY)
-		return (-ENOENT);
+		return -ENOENT;
 
 	if (slot == CDSL_NONE) {
 		(void) cdrom_load_unload (drive, -1, NULL);
@@ -2449,11 +2474,8 @@
 #if ! STANDARD_ATAPI
 		    CDROM_STATE_FLAGS (drive)->sanyo_slot == 0 &&
 #endif
-		    info->changer_info->slots[slot].disc_present
-		    == 0) {
-			printk ("%s: Requested slot does not contain a CD.\n",
-				drive->name);
-			return (-ENOENT);
+		    info->changer_info->slots[slot].disc_present == 0) {
+			return -ENOMEDIUM;
 		}
 
 		stat = cdrom_load_unload (drive, slot, NULL);
@@ -2519,7 +2541,6 @@
 	}
 }
 
-
 static
 int ide_cdrom_get_last_session (struct cdrom_device_info *cdi,
 				struct cdrom_multisession *ms_info)
@@ -2628,16 +2649,16 @@
 /****************************************************************************
  * Device initialization.
  */
+
 static
 struct cdrom_device_ops ide_cdrom_dops = {
 	ide_cdrom_open_real,    /* open */
 	ide_cdrom_release_real, /* release */
 	ide_cdrom_drive_status, /* drive_status */
-	0, /* disc_status */
 	ide_cdrom_check_media_change_real, /* media_changed */
 	ide_cdrom_tray_move,    /* tray_move */
 	ide_cdrom_lock_door,    /* lock_door */
-	0, /* select_speed */
+	NULL, /* select_speed */
 	ide_cdrom_select_disc, /* select_disc */
 	ide_cdrom_get_last_session, /* get_last_session */
 	ide_cdrom_get_mcn, /* get_mcn */
@@ -2646,11 +2667,11 @@
 	ide_cdrom_dev_ioctl,   /* dev_ioctl */
 	CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK
 	| CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN
-	| CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO,  /* capability */
+	| CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET 
+	| CDC_IOCTLS | CDC_DRIVE_STATUS,  /* capability */
 	0 /* n_minors */
 };
 
-
 static int ide_cdrom_register (ide_drive_t *drive, int nslots)
 {
 	struct cdrom_info *info = drive->driver_data;
@@ -2660,11 +2681,11 @@
 	devinfo->dev = MKDEV (HWIF(drive)->major, minor);
 	devinfo->ops = &ide_cdrom_dops;
 	devinfo->mask = 0;
-	*(int *)&devinfo->speed = 0;
+	*(int *)&devinfo->speed = CDROM_CONFIG_FLAGS (drive)->max_speed;
 	*(int *)&devinfo->capacity = nslots;
 	devinfo->handle = (void *) drive;
-
-	return register_cdrom (devinfo, drive->name);
+	strcpy(devinfo->name, drive->name);
+	return register_cdrom (devinfo);
 }
 
 
@@ -2690,6 +2711,11 @@
 	if (buf.cap.lock == 0)
 		CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
 
+	if (buf.cap.cd_r_write)
+		CDROM_CONFIG_FLAGS (drive)->cd_r = 1;
+	if (buf.cap.cd_rw_write)
+		CDROM_CONFIG_FLAGS (drive)->cd_rw = 1;
+
 #if ! STANDARD_ATAPI
 	if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
 		CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
@@ -2711,9 +2737,23 @@
 		}
 	}
 
-	if (CDROM_CONFIG_FLAGS (drive)->is_changer)
-		printk (" %s: ATAPI CDROM changer with %d slots\n", 
-			drive->name, nslots);
+	CDROM_STATE_FLAGS (drive)->curent_speed  = ntohs(buf.cap.curspeed)/176;
+	CDROM_CONFIG_FLAGS (drive)->max_speed = ntohs(buf.cap.maxspeed)/176;
+
+        stat=0;
+        printk ("%s: ATAPI %dx CDROM", 
+        	drive->name, CDROM_CONFIG_FLAGS (drive)->max_speed);
+        if (CDROM_CONFIG_FLAGS (drive)->cd_r|CDROM_CONFIG_FLAGS (drive)->cd_rw) 
+        	printk (" CD%s%s", 
+        	(CDROM_CONFIG_FLAGS (drive)->cd_r)? "-R" : "", 
+        	(CDROM_CONFIG_FLAGS (drive)->cd_rw)? "/RW" : "");
+        if (CDROM_CONFIG_FLAGS (drive)->is_changer) 
+        	printk (" changer w/%d slots", nslots);
+        else 	
+        	printk (" drive");
+	printk (" %s/%dkB Cache\n", 
+		(CDROM_CONFIG_FLAGS (drive)->is_changer)? "&" : "w",  
+        	ntohs(buf.cap.buffer_size) );
 
 	return nslots;
 }
@@ -2751,6 +2791,8 @@
 		CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
 
 	CDROM_CONFIG_FLAGS (drive)->is_changer = 0;
+	CDROM_CONFIG_FLAGS (drive)->cd_r = 0;
+	CDROM_CONFIG_FLAGS (drive)->cd_rw = 0;
 	CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 0;
 
 #if ! STANDARD_ATAPI
@@ -2802,13 +2844,15 @@
 			CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
 		}
 
-		/* Sanyo 3 CD changer uses a non-standard command 
-                   for CD changing. */
-                else if ((strcmp(drive->id->model, "CD-ROM CDR-C3 G") == 0) ||
-                         (strcmp(drive->id->model, "CD-ROM CDR-C3G") == 0)) {
-			/* uses CD in slot 0 when value is set to 3 */
-			CDROM_STATE_FLAGS (drive)->sanyo_slot = 3;
-		}
+                /* Sanyo 3 CD changer uses a non-standard command
+                    for CD changing */
+                 else if ((strcmp(drive->id->model, "CD-ROM CDR-C3 G") == 0) ||
+                         (strcmp(drive->id->model, "CD-ROM CDR-C3G") == 0) ||
+                         (strcmp(drive->id->model, "CD-ROM CDR_C36") == 0)) {
+                        /* uses CD in slot 0 when value is set to 3 */
+                        CDROM_STATE_FLAGS (drive)->sanyo_slot = 3;
+                }
+
 
 	}
 #endif /* not STANDARD_ATAPI */
diff -u --recursive --new-file v2.1.69/linux/drivers/block/ide-cd.h linux/drivers/block/ide-cd.h
--- v2.1.69/linux/drivers/block/ide-cd.h	Mon Apr  7 11:35:29 1997
+++ linux/drivers/block/ide-cd.h	Tue Dec  2 11:41:44 1997
@@ -115,8 +115,11 @@
 	__u8 toctracks_as_bcd : 1; /* TOC track numbers are in BCD. */
 	__u8 subchan_as_bcd   : 1; /* Subchannel info is in BCD. */
 	__u8 is_changer       : 1; /* Drive is a changer. */
+	__u8 cd_r             : 1; /* Drive can write to CD-R media . */
+	__u8 cd_rw            : 1; /* Drive can write to CD-R/W media . */
 	__u8 supp_disc_present: 1; /* Changer can report exact contents
 				      of slots. */
+	__u8 max_speed        : 4; /* Max speed of the drive */
 	__u8 seeking          : 1; /* Seeking in progress */
 	__u8 reserved         : 6;
 };
@@ -130,6 +133,7 @@
 	__u8 toc_valid     : 1; /* Saved TOC information is current. */
 	__u8 door_locked   : 1; /* We think that the drive door is locked. */
 	__u8 sanyo_slot    : 2; /* Sanyo 3 CD changer support */
+	__u8 curent_speed  : 4; /* Current speed of the drive */
 	__u8 reserved      : 3;
 };
 #define CDROM_STATE_FLAGS(drive)  ((struct ide_cd_state_flags *)&((drive)->bios_head))
@@ -239,16 +243,16 @@
 
 	/* Drive supports read from CD-R discs (orange book, part II) */
         unsigned cd_r_read           : 1; /* reserved in 1.2 */
-	/* Drive supports read from CD-E discs (orange book, part III) */
-        unsigned cd_e_read           : 1; /* reserved in 1.2 */
+	/* Drive supports read from CD-R/W (CD-E) discs (orange book, part III) */
+        unsigned cd_rw_read           : 1; /* reserved in 1.2 */
 	/* Drive supports reading CD-R discs with addressing method 2 */
 	unsigned method2             : 1; /* reserved in 1.2 */
 	unsigned reserved2           : 5;
 
 	/* Drive supports write to CD-R discs (orange book, part II) */
         unsigned cd_r_write          : 1; /* reserved in 1.2 */
-	/* Drive supports write to CD-E discs (orange book, part III) */
-        unsigned cd_e_write          : 1; /* reserved in 1.2 */
+	/* Drive supports write to CD-R/W (CD-E) discs (orange book, part III) */
+        unsigned cd_rw_write          : 1; /* reserved in 1.2 */
 	unsigned reserved3           : 6;
 
 	/* Drive supports audio play operations. */
diff -u --recursive --new-file v2.1.69/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.1.69/linux/drivers/block/ll_rw_blk.c	Wed Nov 12 13:34:25 1997
+++ linux/drivers/block/ll_rw_blk.c	Tue Dec  2 11:41:44 1997
@@ -654,7 +654,6 @@
 		}
 	}
 }
-
 #ifdef CONFIG_BLK_DEV_EZ
 extern void ez_init( void );
 #endif
@@ -694,9 +693,12 @@
 #ifdef CONFIG_BLK_DEV_LOOP
 	loop_init();
 #endif
-#ifdef CONFIG_CDI_INIT
-	cdi_init();		/* this MUST precede ide_init */
-#endif CONFIG_CDI_INIT
+#ifdef CONFIG_CDROM		/* this must precede all CD-ROM drivers */
+	cdrom_init();
+#endif CONFIG_CDROM
+#ifdef CONFIG_ISP16_CDI
+	isp16_init();
+#endif CONFIG_ISP16_CDI
 #ifdef CONFIG_BLK_DEV_IDE
 	ide_init();		/* this MUST precede hd_init */
 #endif
diff -u --recursive --new-file v2.1.69/linux/drivers/block/md.c linux/drivers/block/md.c
--- v2.1.69/linux/drivers/block/md.c	Wed Nov 26 16:24:01 1997
+++ linux/drivers/block/md.c	Mon Dec  1 14:47:07 1997
@@ -987,9 +987,11 @@
 		cli();
 		if (!test_bit(THREAD_WAKEUP, &thread->flags)) {
 			do {
-				current->signal = 0;
-				interruptible_sleep_on(&thread->wqueue);
-			} while (current->signal);
+			         spin_lock_irq(&current->sigmask_lock);
+				 flush_signals(current);
+				 spin_unlock_irq(&current->sigmask_lock);
+				 interruptible_sleep_on(&thread->wqueue);
+			} while (signal_pending(current));
 		}
 	}
 }
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/Config.in linux/drivers/cdrom/Config.in
--- v2.1.69/linux/drivers/cdrom/Config.in	Wed Dec 18 05:57:28 1996
+++ linux/drivers/cdrom/Config.in	Tue Dec  2 11:41:44 1997
@@ -19,9 +19,6 @@
 tristate 'Optics Storage DOLPHIN 8000AT CDROM support' CONFIG_OPTCD
 tristate 'Philips/LMS CM206 CDROM support' CONFIG_CM206
 tristate 'Sanyo CDR-H94A CDROM support' CONFIG_SJCD
-bool 'Soft configurable cdrom interface card support' CONFIG_CDI_INIT
-if [ "$CONFIG_CDI_INIT" = "y" ]; then
-  tristate 'ISP16/MAD16/Mozart soft configurable cdrom interface support' CONFIG_ISP16_CDI
-fi
+tristate 'ISP16/MAD16/Mozart soft configurable cdrom interface support' CONFIG_ISP16_CDI
 tristate 'Sony CDU31A/CDU33A CDROM support' CONFIG_CDU31A
 tristate 'Sony CDU535 CDROM support' CONFIG_CDU535
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/Makefile linux/drivers/cdrom/Makefile
--- v2.1.69/linux/drivers/cdrom/Makefile	Mon Apr 14 16:28:11 1997
+++ linux/drivers/cdrom/Makefile	Tue Dec  2 11:41:44 1997
@@ -90,11 +90,9 @@
 
 ifeq ($(CONFIG_CM206),y)
 L_OBJS += cm206.o
-USE_GENERIC_CD=1
 else
   ifeq ($(CONFIG_CM206),m)
   M_OBJS += cm206.o
-    USE_MODULAR_GENERIC_CD=1
   endif
 endif #CONFIG_CM206
 
@@ -122,42 +120,20 @@
   endif
 endif #CONFIG_BPCD
 
-ifeq ($(CONFIG_CDI_INIT),y)
-L_OBJS += cdi.o
-endif #CONFIG_CDI_INIT
 ifeq ($(CONFIG_ISP16_CDI),y)
 L_OBJS += isp16.o
 else
-#  ifeq ($(CONFIG_CDI_INIT),m)
-#  M_OBJS += cdi.o
-#  endif
   ifeq ($(CONFIG_ISP16_CDI),m)
   M_OBJS += isp16.o
   endif
 endif #CONFIG_ISP16_CDI
 
-ifeq ($(CONFIG_BLK_DEV_SR),y)
-USE_GENERIC_CD=1
-else
-  ifeq ($(CONFIG_BLK_DEV_SR),m)
-    USE_MODULAR_GENERIC_CD=1
-  endif
-endif #SCSI CDROM DRIVER
-
-ifeq ($(CONFIG_BLK_DEV_IDECD),y)
-USE_GENERIC_CD=1
-else
-  ifeq ($(CONFIG_BLK_DEV_IDECD),m)
-    USE_MODULAR_GENERIC_CD=1
-  endif
-endif #CONFIG_BLK_DEV_IDECD
-
-ifdef USE_GENERIC_CD
+ifeq ($(CONFIG_CDROM),y)
 LX_OBJS += cdrom.o
 else
-  ifdef USE_MODULAR_GENERIC_CD
+  ifeq ($(CONFIG_CDROM),m)
   MX_OBJS += cdrom.o
   endif
-endif #The Makefile configuration for the generic cdrom interface
-  
+endif #CONFIG_CDROM for the Uniform CD-ROM driver
+
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/aztcd.c linux/drivers/cdrom/aztcd.c
--- v2.1.69/linux/drivers/cdrom/aztcd.c	Tue Dec  2 09:49:39 1997
+++ linux/drivers/cdrom/aztcd.c	Tue Dec  2 11:41:44 1997
@@ -163,7 +163,7 @@
 #define MAJOR_NR AZTECH_CDROM_MAJOR 
 
 #include <linux/blk.h>
-#include <linux/aztcd.h>
+#include "aztcd.h"
 
 #include <linux/module.h>
 #include <linux/errno.h>
@@ -2266,7 +2266,7 @@
 
 static long azt_msf2hsg(struct msf *mp)
 { return azt_bcd2bin(mp -> frame) + azt_bcd2bin(mp -> sec) * 75
-		                  + azt_bcd2bin(mp -> min) * 4500 - CD_BLOCK_OFFSET;
+		                  + azt_bcd2bin(mp -> min) * 4500 - CD_MSF_OFFSET;
 }
 
 static void azt_bin2bcd(unsigned char *p)
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/aztcd.h linux/drivers/cdrom/aztcd.h
--- v2.1.69/linux/drivers/cdrom/aztcd.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/cdrom/aztcd.h	Tue Dec  2 11:41:44 1997
@@ -0,0 +1,154 @@
+/* $Id: aztcd.h,v 2.50 1996/05/17 16:15:43 root Exp root $
+ *
+ * Definitions for a AztechCD268 CD-ROM interface
+ *	Copyright (C) 1994, 1995  Werner Zimmermann
+ *
+ *	based on Mitsumi CDROM driver by Martin Harriss
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  History:	W.Zimmermann adaption to Aztech CD268-01A Version 1.3
+ *		October 1994 Email: zimmerma@rz.fht-esslingen.de
+ */
+
+/* *** change this to set the I/O port address of your CD-ROM drive*/
+#define AZT_BASE_ADDR		0x320
+
+/* Uncomment this, if your CDROM is connected to a Soundwave32-soundcard
+   and configure AZT_BASE_ADDR and AZT_SW32_BASE_ADDR */
+/*#define AZT_SW32 1
+*/
+
+#ifdef AZT_SW32 
+#define AZT_SW32_BASE_ADDR      0x220  /*I/O port base address of your soundcard*/
+#endif
+
+/* Set this to 1, if you want your tray to be locked, set to 0 to prevent tray 
+   from locking */
+#define AZT_ALLOW_TRAY_LOCK	1
+
+/*Set this to 1 to allow auto-eject when unmounting a disk, set to 0, if you 
+  don't want the auto-eject feature*/
+#define AZT_AUTO_EJECT          0
+
+/*Set this to 1, if you want to use incompatible ioctls for reading in raw and
+  cooked mode */
+#define AZT_PRIVATE_IOCTLS      1
+
+/*Set this to 1, if you want multisession support by the ISO fs. Even if you set 
+  this value to '0' you can use multisession CDs. In that case the drive's firm-
+  ware will do the appropriate redirection automatically. The CD will then look
+  like a single session CD (but nevertheless all data may be read). Please read 
+  chapter '5.1 Multisession support' in README.aztcd for details. Normally it's 
+  uncritical to leave this setting untouched */
+#define AZT_MULTISESSION        1
+
+/*---------------------------------------------------------------------------*/
+/*-----nothing to be configured for normal applications below this line------*/
+
+
+/* Increase this if you get lots of timeouts; if you get kernel panic, replace
+   STEN_LOW_WAIT by STEN_LOW in the source code */
+#define AZT_STATUS_DELAY	400       /*for timer wait, STEN_LOW_WAIT*/
+#define AZT_TIMEOUT		8000000   /*for busy wait STEN_LOW, DTEN_LOW*/
+#define AZT_FAST_TIMEOUT	10000     /*for reading the version string*/
+
+/* number of times to retry a command before giving up */
+#define AZT_RETRY_ATTEMPTS	3
+
+/* port access macros */
+#define CMD_PORT		azt_port
+#define DATA_PORT		azt_port
+#define STATUS_PORT		azt_port+1
+#define MODE_PORT		azt_port+2
+#ifdef  AZT_SW32                
+ #define AZT_SW32_INIT          (unsigned int) (0xFF00 & (AZT_BASE_ADDR*16))
+ #define AZT_SW32_CONFIG_REG    AZT_SW32_BASE_ADDR+0x16  /*Soundwave32 Config. Register*/
+ #define AZT_SW32_ID_REG        AZT_SW32_BASE_ADDR+0x04  /*Soundwave32 ID Version Register*/
+#endif
+
+/* status bits */
+#define AST_CMD_CHECK		0x80		/* 1 = command error */
+#define AST_DOOR_OPEN		0x40		/* 1 = door is open */
+#define AST_NOT_READY		0x20		/* 1 = no disk in the drive */
+#define AST_DSK_CHG		0x02		/* 1 = disk removed or changed */
+#define AST_MODE                0x01            /* 0=MODE1, 1=MODE2 */
+#define AST_MODE_BITS		0x1C		/* Mode Bits */
+#define AST_INITIAL		0x0C		/* initial, only valid ... */
+#define AST_BUSY		0x04		/* now playing, only valid
+						   in combination with mode
+						   bits */
+/* flag bits */
+#define AFL_DATA		0x02		/* data available if low */
+#define AFL_STATUS		0x04		/* status available if low */
+#define AFL_OP_OK		0x01		/* OP_OK command correct*/
+#define AFL_PA_OK		0x02		/* PA_OK parameter correct*/
+#define AFL_OP_ERR		0x05		/* error in command*/
+#define AFL_PA_ERR		0x06		/* error in parameters*/
+#define POLLED			0x04		/* polled mode */
+
+/* commands */
+#define ACMD_SOFT_RESET		0x10		/* reset drive */
+#define ACMD_PLAY_READ		0x20		/* read data track in cooked mode */
+#define ACMD_PLAY_READ_RAW      0x21		/* reading in raw mode*/
+#define ACMD_SEEK               0x30            /* seek msf address*/
+#define ACMD_SEEK_TO_LEADIN     0x31		/* seek to leadin track*/
+#define ACMD_GET_ERROR		0x40		/* get error code */
+#define ACMD_GET_STATUS		0x41		/* get status */
+#define ACMD_GET_Q_CHANNEL      0x50		/* read info from q channel */
+#define ACMD_EJECT		0x60		/* eject/open tray */
+#define ACMD_CLOSE              0x61            /* close tray */
+#define ACMD_LOCK		0x71		/* lock tray closed */
+#define ACMD_UNLOCK		0x72		/* unlock tray */
+#define ACMD_PAUSE		0x80		/* pause */
+#define ACMD_STOP		0x81		/* stop play */
+#define ACMD_PLAY_AUDIO		0x90		/* play audio track */
+#define ACMD_SET_VOLUME		0x93		/* set audio level */
+#define ACMD_GET_VERSION	0xA0		/* get firmware version */
+#define ACMD_SET_DISK_TYPE	0xA1		/* set disk data mode */
+
+#define MAX_TRACKS		104
+
+struct msf {
+	unsigned char	min;
+	unsigned char	sec;
+	unsigned char	frame;
+};
+
+struct azt_Play_msf {
+	struct msf	start;
+	struct msf	end;
+};
+
+struct azt_DiskInfo {
+	unsigned char	first;
+        unsigned char   next;
+	unsigned char	last;
+	struct msf	diskLength;
+	struct msf	firstTrack;
+        unsigned char   multi;
+        struct msf      nextSession;
+        struct msf      lastSession;
+        unsigned char   xa;
+        unsigned char   audio;
+};
+
+struct azt_Toc {
+	unsigned char	ctrl_addr;
+	unsigned char	track;
+	unsigned char	pointIndex;
+	struct msf	trackTime;
+	struct msf	diskTime;
+};
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/cdi.c linux/drivers/cdrom/cdi.c
--- v2.1.69/linux/drivers/cdrom/cdi.c	Tue May 13 22:41:06 1997
+++ linux/drivers/cdrom/cdi.c	Wed Dec 31 16:00:00 1969
@@ -1,50 +0,0 @@
-/* -- cdi.c
- *
- *    Initialisation of software configurable cdrom interface 
- *    cards goes here.
- *
- *    Copyright (c) 1996 Eric van der Maarel <H.T.M.v.d.Maarel@marin.nl>
- *
- *    Version 0.1
- *
- *    History:
- *    0.1 First release. Only support for ISP16/MAD16/Mozart.
- *
- *    This program is free software; you can redistribute it and/or modify
- *    it under the terms of the GNU General Public License as published by
- *    the Free Software Foundation; either version 2 of the License, or
- *    (at your option) any later version.
- *
- *    This program is distributed in the hope that it will be useful,
- *    but WITHOUT ANY WARRANTY; without even the implied warranty of
- *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *    GNU General Public License for more details.
- *
- *    You should have received a copy of the GNU General Public License
- *    along with this program; if not, write to the Free Software
- *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <linux/config.h>
-#include <linux/blk.h>  /* where the proto type of cdi_init() is */
-#include <linux/init.h>
-#ifdef CONFIG_ISP16_CDI
-#include <linux/isp16.h>
-#endif CONFIG_ISP16_CDI
-
-/*
- *  Cdrom interface configuration.
- */
-__initfunc(int
-cdi_init(void))
-{
-  int ret_val = -1;
-
-#ifdef CONFIG_ISP16_CDI
-  ret_val &= isp16_init();
-#endif CONFIG_ISP16_CDI
-
-  return(ret_val);
-}
-
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- v2.1.69/linux/drivers/cdrom/cdrom.c	Mon Apr 14 16:28:11 1997
+++ linux/drivers/cdrom/cdrom.c	Tue Dec  2 11:56:07 1997
@@ -1,12 +1,58 @@
-/* cdrom.c. Common ioctl and open routines for various Linux cdrom drivers. -*- linux-c -*-
+/* linux/drivers/cdrom/cdrom.c. 
    Copyright (c) 1996, 1997 David A. van Leeuwen.
+   Copyright (c) 1997 Erik Andersen (andersee@debian.org)
 
-   The routines in the file should provide an interface between
-   software accessing cdroms and the various drivers that implement
-   specific hardware devices.
+   May be copied or modified under the terms of the GNU General Public
+   License.  See linux/COPYING for more information.
+
+   Uniform CD-ROM driver for Linux.
+   See Documentation/cdrom/cdrom-standard.tex for usage information.
+
+   The routines in the file provide a uniform interface between the
+   software that uses CD-ROMs and the various low-level drivers that
+   actually talk to actual hardware devices. Suggestions are welcome.
+   Patches that work are more welcome though.  ;-)
+
+
+  Recent Changes:
+  ----------------------------------
+
+  New maintainer! As David A. van Leeuwen has been too busy to activly
+  maintain and improve this driver, I am now carrying on the torch. If
+  you have a problem with this driver, please feel free to contact me.
+
+  Added (rudimentary) sysctl interface. I realize this is really weak
+  right now, and is _very_ badly implemented. It will be improved... I
+  plan on having one directory per drive, with entries for outputing
+  general drive information, and sysctl based tunable parameters such
+  as whether the tray should auto-close for that drive. Suggestions (or
+  patches) for improvements are very welcome.
+
+  Modified CDROM_DISC_STATUS so that it is now incorporated into
+  the Uniform CD-ROM driver via the cdrom_count_tracks function.
+  The cdrom_count_tracks function helps resolve some of the false
+  assumptions of the CDROM_DISC_STATUS ioctl, and is also used to check
+  for the correct media type when mounting or playing audio from a CD.
+
+  Remove the calls to verify_area and only use the copy_from_user and
+  copy_to_user stuff, since these calls now provide their own memory
+  checking with the 2.1.x kernels.
+
+  Major update to return codes so that errors from low-level drivers
+  are passed on through (thanks to Gerd Knorr for pointing out this
+  problem).
+
+  Made it so if a function isn't implemented in a low-level driver,
+  ENOSYS is now returned instead of EINVAL.
+
+  Simplified some complex logic so that the source code is easier to read.
+
+  Other stuff I probably forgot to mention (lots of changes).
 
  */
 
+
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/major.h>
@@ -14,36 +60,50 @@
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
+#include <linux/malloc.h> 
+#include <linux/cdrom.h>
+#include <linux/sysctl.h>
 #include <asm/fcntl.h>
 #include <asm/segment.h>
 #include <asm/uaccess.h>
 
-#include <linux/cdrom.h>
-#include <linux/ucdrom.h>
-
-/* define CHECKTYPE if you want to check for audio/data cdrom. You
-   must use cd player programs that respect the O_NONBLOCK option for
-   opening the cdrom-device for ioctl-commanding only. (See
-   Documentation/cdrom/cdrom-standard.tex). Patches for a number of cd
-   players (CDplayer-2.0, cd-console-1.1, workbone-2.3, cdtool-1.0,
-   cdp-0.33, cdplayer-0.4, cdthing-1.4, lyn0.8a, playcd-1.0) can be
-   found in directory
-
-	ftp://ftp.gwdg.de/pub/linux/cdrom/drivers/cm206/
-
-   A runtime configuration program "checktype.c" to allow for cdrom
-   medium type checking can be found at the same location.
-
-   In order to be able to get neat system errors like "No medium
-   found" or "Wrong medium type" upon attempting to mount/play an
-   empty slot, mount an audio disc or play a data disc, you need to
-   have a new libc.5.so.  */
-#undef CHECKTYPE
 
+#define VERSION "$Id: cdrom.c,v 2.0 1997/11/20 01:58:03 erik Exp $"
+#define REVISION "revision 2.0"
 #define FM_WRITE	0x2                 /* file mode write bit */
 
-#define VERSION "$Id: cdrom.c,v 1.7 1997/02/27 22:14:26 david Exp $"
-#define REVISION "$Revision: 1.7 $"
+/* When VERBOSE_STATUS_INFO is not defined, the debugging printks don't 
+   get compiled in */
+#define VERBOSE_STATUS_INFO
+
+/* I use an error-log mask to give fine grain control over the type of
+   error messages dumped to the system logs.  The available masks include: */
+#define CD_WARNING	0x1
+#define CD_REG_UNREG	0x2
+#define CD_DO_IOCTL	0x4
+#define CD_OPEN		0x8
+#define CD_CLOSE	0x10
+
+#define ERRLOGMASK (CD_WARNING) 
+/* #define ERRLOGMASK (CD_WARNING|CD_OPEN|CD_CLOSE) */
+/* #define ERRLOGMASK (CD_WARNING|CD_REG_UNREG|CD_DO_IOCTL|CD_OPEN|CD_CLOSE) */
+
+#ifdef VERBOSE_STATUS_INFO
+#define cdinfo(type, fmt, args...) \
+	if (ERRLOGMASK & type) printk(KERN_INFO "cdrom: " fmt, ## args)
+#else
+#define cdinfo(type, fmt, args...) 
+#endif
+
+
+/* These are used to simplify getting data in from and back to user land */
+#define IOCTL_IN(arg, type, in) { \
+            int ret=copy_from_user(&in, (type *) arg, sizeof in); \
+            if (ret) return ret; }
+
+#define IOCTL_OUT(arg, type, out) { \
+            int ret=copy_to_user((type *) arg, &out, sizeof out); \
+            if (ret) return ret; }
 
 /* Not-exported routines. */
 static int cdrom_open(struct inode *ip, struct file *fp);
@@ -51,6 +111,23 @@
 static int cdrom_ioctl(struct inode *ip, struct file *fp,
 				unsigned int cmd, unsigned long arg);
 static int cdrom_media_changed(kdev_t dev);
+static int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
+                           void *buffer, size_t *lenp);
+static int open_for_data(struct cdrom_device_info * cdi);
+static int check_for_audio_disc(struct cdrom_device_info * cdi,
+			 struct cdrom_device_ops * cdo);
+static void sanitize_format(union cdrom_addr *addr, 
+		u_char * curr, u_char requested);
+typedef struct {
+	int data;
+	int audio;
+	int cdi;
+	int xa;
+	long error;
+} tracktype;
+
+static void cdrom_count_tracks(struct cdrom_device_info *cdi,tracktype* tracks);
+static struct cdrom_device_info *topCdromPtr = NULL;
 
 struct file_operations cdrom_fops =
 {
@@ -69,47 +146,45 @@
 	NULL                            /* revalidate */
 };
 
-static struct cdrom_device_info *cdromdevs[MAX_BLKDEV] = {
-	NULL,
-};
+void cdrom_init(void) {
+	if (!topCdromPtr)
+	    printk(KERN_INFO "Uniform CD-ROM driver " REVISION "\n");
+}
 
 /* This macro makes sure we don't have to check on cdrom_device_ops
  * existence in the run-time routines below. Change_capability is a
  * hack to have the capability flags defined const, while we can still
  * change it here without gcc complaining at every line.
  */
-
 #define ENSURE(call, bits) if (cdo->call == NULL) *change_capability &= ~(bits)
 
-/* We don't use $name$ yet, but it could be used for the /proc
- * filesystem in the future, or for other purposes.
- */
-int register_cdrom(struct cdrom_device_info *cdi, char *name)
+int register_cdrom(struct cdrom_device_info *cdi)
 {
 	int major = MAJOR (cdi->dev);
-	struct cdrom_device_ops *cdo = cdi->ops;
-	int *change_capability = (int *)&cdo->capability; /* hack */
+        struct cdrom_device_ops *cdo = cdi->ops;
+        int *change_capability = (int *)&cdo->capability; /* hack */
 
 	if (major < 0 || major >= MAX_BLKDEV)
 		return -1;
 	if (cdo->open == NULL || cdo->release == NULL)
 		return -2;
+	ENSURE(drive_status, CDC_DRIVE_STATUS );
+	ENSURE(media_changed, CDC_MEDIA_CHANGED);
 	ENSURE(tray_move, CDC_CLOSE_TRAY | CDC_OPEN_TRAY);
 	ENSURE(lock_door, CDC_LOCK);
 	ENSURE(select_speed, CDC_SELECT_SPEED);
 	ENSURE(select_disc, CDC_SELECT_DISC);
 	ENSURE(get_last_session, CDC_MULTI_SESSION);
+	ENSURE(get_mcn, CDC_MCN);
+	ENSURE(reset, CDC_RESET);
 	ENSURE(audio_ioctl, CDC_PLAY_AUDIO);
-	ENSURE(media_changed, CDC_MEDIA_CHANGED);
-	if (cdromdevs[major]==NULL) cdo->n_minors = 0;
-	else cdo->n_minors++;
-	cdi->next = cdromdevs[major];
-	cdromdevs[major] = cdi;
-	cdi->options = CDO_AUTO_CLOSE | CDO_USE_FFLAGS | CDO_LOCK;
-#ifdef CHECKTYPE
-	cdi->options |= CDO_CHECK_TYPE;
-#endif
+	ENSURE(dev_ioctl, CDC_IOCTLS);
+	cdi->options = CDO_AUTO_CLOSE | CDO_USE_FFLAGS | CDO_LOCK | CDO_CHECK_TYPE;
 	cdi->mc_flags = 0;
+	cdo->n_minors = 0;
+	cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name);
+	cdi->next = topCdromPtr; 	
+	topCdromPtr = cdi;
 	return 0;
 }
 #undef ENSURE
@@ -123,7 +198,7 @@
 		return -1;
 
 	prev = NULL;
-	cdi = cdromdevs[major];
+	cdi = topCdromPtr;
 	while (cdi != NULL && cdi->dev != unreg->dev) {
 		prev = cdi;
 		cdi = cdi->next;
@@ -134,16 +209,18 @@
 	if (prev)
 		prev->next = cdi->next;
 	else
-		cdromdevs[major] = cdi->next;
+		topCdromPtr = cdi->next;
 	cdi->ops->n_minors--;
+	cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" unregistered\n", cdi->name);
 	return 0;
 }
 
 static
 struct cdrom_device_info *cdrom_find_device (kdev_t dev)
 {
-	struct cdrom_device_info *cdi = cdromdevs[MAJOR (dev)];
+	struct cdrom_device_info *cdi;
 
+	cdi = topCdromPtr;
 	while (cdi != NULL && cdi->dev != dev)
 		cdi = cdi->next;
 	return cdi;
@@ -158,9 +235,6 @@
  * this way.
  */
 static
-int open_for_data(struct cdrom_device_info * cdi);
-
-static
 int cdrom_open(struct inode *ip, struct file *fp)
 {
 	kdev_t dev = ip->i_rdev;
@@ -168,6 +242,7 @@
 	int purpose = !!(fp->f_flags & O_NONBLOCK);
 	int ret=0;
 
+	cdinfo(CD_OPEN, "entering cdrom_open\n"); 
 	if (cdi == NULL)
 		return -ENODEV;
 	if (fp->f_mode & FM_WRITE)
@@ -178,6 +253,7 @@
 	else
 		ret = open_for_data(cdi);
 	if (!ret) cdi->use_count++;
+	cdinfo(CD_OPEN, "Use count for \"/dev/%s\" now %d\n", cdi->name, cdi->use_count);
 	return ret;
 }
 
@@ -186,58 +262,169 @@
 {
 	int ret;
 	struct cdrom_device_ops *cdo = cdi->ops;
+	tracktype tracks;
+	cdinfo(CD_OPEN, "entering open_for_data\n");
+	/* Check if the driver can report drive status.  If it can we
+	   can do clever things.  If it can't, well, we at least tried! */
 	if (cdo->drive_status != NULL) {
-		int ds = cdo->drive_status(cdi, CDSL_CURRENT);
-		if (ds == CDS_TRAY_OPEN) {
+		ret = cdo->drive_status(cdi, CDSL_CURRENT);
+		cdinfo(CD_OPEN, "drive_status=%d\n", ret); 
+		if (ret == CDS_TRAY_OPEN) {
+			cdinfo(CD_WARNING, "tray is open...\n"); 
 			/* can/may i close it? */
 			if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY &&
 			    cdi->options & CDO_AUTO_CLOSE) {
-				if (cdo->tray_move(cdi,0))
-					return -EIO;
-			} else
-				return -ENOMEDIUM; /* can't close: too bad */
-			ds = cdo->drive_status(cdi, CDSL_CURRENT);
-			if (ds == CDS_NO_DISC)
-				return -ENOMEDIUM;
+				ret=cdo->tray_move(cdi,0);
+				if (ret) {
+					cdinfo(CD_WARNING, "bummer. tried to close tray but failed.\n"); 
+					/* Ignore the error from the low
+					level driver.  We don't care why it
+					couldn't close the tray.  We only care 
+					that there is no disc in the drive, 
+					since that is the _REAL_ problem here.*/
+					ret=-ENOMEDIUM;
+					goto clean_up_and_return;
+				}
+			} else {
+				cdinfo(CD_WARNING, "this driver can't close the tray.\n"); 
+				ret=-ENOMEDIUM;
+				goto clean_up_and_return;
+			}
+			/* Ok, the door should be closed now.. Check again */
+			ret = cdo->drive_status(cdi, CDSL_CURRENT);
+			cdinfo(CD_OPEN, "tried again. drive_status=%d\n", ret); 
+			if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN)) {
+				ret=-ENOMEDIUM;
+				goto clean_up_and_return;
+			}
 		}
+		if (ret!=CDS_DISC_OK)
+			goto clean_up_and_return;
+	}
+	cdrom_count_tracks(cdi, &tracks);
+	if (tracks.error == CDS_NO_DISC) {
+		cdinfo(CD_OPEN, "bummer. no disc...\n");
+		ret=-ENOMEDIUM;
+		goto clean_up_and_return;
 	}
-	if (cdo->disc_status != NULL) {
-		int ds = cdo->disc_status(cdi);
-		if (ds == CDS_NO_DISC)
-			return -ENOMEDIUM;
-		if (cdi->options & CDO_CHECK_TYPE &&
-		    ds != CDS_DATA_1)
-			return -EMEDIUMTYPE;
+	if (cdi->options & CDO_CHECK_TYPE && tracks.data==0) {
+		cdinfo(CD_OPEN, "bummer. wrong media type...\n"); 
+		ret=-EMEDIUMTYPE;
+		goto clean_up_and_return;
 	}
-	/* all is well, we can open the device */
+
+	cdinfo(CD_OPEN, "all seems well, opening the device...\n"); 
+
+	/* all seems well, we can open the device */
 	ret = cdo->open(cdi, 0); /* open for data */
-	if (cdo->capability & ~cdi->mask & CDC_LOCK &&
-	    cdi->options & CDO_LOCK)
-		cdo->lock_door(cdi, 1);
+	cdinfo(CD_OPEN, "opening the device gave me %d.\n", ret); 
+	/* After all this careful checking, we shouldn't have problems
+	   opening the device, but we don't want the device locked if 
+	   this somehow fails... */
+	if (ret) {
+		cdinfo(CD_OPEN, "open device failed...\n"); 
+		goto clean_up_and_return;
+	}
+	if (cdo->capability & ~cdi->mask & CDC_LOCK && 
+		cdi->options & CDO_LOCK) {
+			cdo->lock_door(cdi, 1);
+			cdinfo(CD_OPEN, "door locked.\n");
+	}	
+	cdinfo(CD_OPEN, "device opened sucessfully.\n"); 
+	return ret;
+
+	/* Something failed.  Try to unlock the drive, because some drivers
+	(notably ide-cd) lock the drive after every command.  This produced
+	a nasty bug where after mount failed, the drive would remain locked!  
+	This ensures that the drive gets unlocked after a mount fails.  This 
+	is a goto to avoid adding bloating the driver with redundant code. */ 
+clean_up_and_return:
+	cdinfo(CD_WARNING, "failed to open the device.\n"); 
+	if (cdo->capability & ~cdi->mask & CDC_LOCK && 
+		cdi->options & CDO_LOCK) {
+			cdo->lock_door(cdi, 0);
+			cdinfo(CD_WARNING, "door unlocked.\n");
+	}
 	return ret;
 }
 
+/* This code is similar to that in open_for_data. The routine is called
+   in case a audio play operation is requested. It doesn't make much sense
+   to do this on a data disc.
+*/
+int check_for_audio_disc(struct cdrom_device_info * cdi,
+			 struct cdrom_device_ops * cdo)
+{
+        int ret;
+	tracktype tracks;
+	cdinfo(CD_OPEN, "entering check_for_audio_disc\n");
+	if (!(cdi->options & CDO_CHECK_TYPE))
+		return 0;
+	if (cdo->drive_status != NULL) {
+		ret = cdo->drive_status(cdi, CDSL_CURRENT);
+		cdinfo(CD_OPEN, "drive_status=%d\n", ret); 
+		if (ret == CDS_TRAY_OPEN) {
+			cdinfo(CD_WARNING, "tray is open...\n"); 
+			/* can/may i close it? */
+			if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY &&
+			    cdi->options & CDO_AUTO_CLOSE) {
+				ret=cdo->tray_move(cdi,0);
+				if (ret) {
+					cdinfo(CD_WARNING, "bummer. tried to close tray but failed.\n"); 
+					/* Ignore the error from the low
+					level driver.  We don't care why it
+					couldn't close the tray.  We only care 
+					that there is no disc in the drive, 
+					since that is the _REAL_ problem here.*/
+					return -ENOMEDIUM;
+				}
+			} else {
+				cdinfo(CD_WARNING, "this driver can't close the tray.\n"); 
+				return -ENOMEDIUM;
+			}
+			/* Ok, the door should be closed now.. Check again */
+			ret = cdo->drive_status(cdi, CDSL_CURRENT);
+			cdinfo(CD_OPEN, "tried again. drive_status=%d\n", ret); 
+			if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN))
+				return -ENOMEDIUM;
+			if (ret!=CDS_DISC_OK)
+				return -EIO;
+		}	
+	}
+	cdrom_count_tracks(cdi, &tracks);
+	if (tracks.error) 
+		return(tracks.error);
+
+	if (tracks.audio==0)
+		return -EMEDIUMTYPE;
+
+	return 0;
+}
+
+
 /* Admittedly, the logic below could be performed in a nicer way. */
 static
 int cdrom_release(struct inode *ip, struct file *fp)
 {
 	kdev_t dev = ip->i_rdev;
 	struct cdrom_device_info *cdi = cdrom_find_device (dev);
-	struct cdrom_device_ops *cdo;
+	struct cdrom_device_ops *cdo = cdi->ops;
 	int opened_for_data;
 
+	cdinfo(CD_CLOSE, "entering cdrom_release\n"); 
 	if (cdi == NULL)
 		return 0;
+	if (cdi->use_count > 0) cdi->use_count--;
+	if (cdi->use_count == 0)
+		cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name);
+	if (cdi->use_count == 0 &&      /* last process that closes dev*/
+	    cdo->capability & CDC_LOCK) {
+		cdinfo(CD_CLOSE, "Unlocking door!\n");
+		cdo->lock_door(cdi, 0);
+		}
 	opened_for_data = !(cdi->options & CDO_USE_FFLAGS) ||
 		!(fp && fp->f_flags & O_NONBLOCK);
-	cdo = cdi->ops;
-	if (cdi->use_count == 1 &&      /* last process that closes dev*/
-	    opened_for_data &&
-	    cdi->options & CDO_LOCK &&
-	    cdo->capability & ~cdi->mask & CDC_LOCK)
-		cdo->lock_door(cdi, 0);
 	cdo->release(cdi);
-	if (cdi->use_count > 0) cdi->use_count--;
 	if (cdi->use_count == 0) {      /* last process that closes dev*/
 		sync_dev(dev);
 		invalidate_buffers(dev);
@@ -277,10 +464,57 @@
 	if (cdi == NULL)
 		return -ENODEV;
 	if (cdi->ops->media_changed == NULL)
-		return -EINVAL;
+		return -ENOSYS;
 	return media_changed(cdi, 0);
 }
 
+static
+void cdrom_count_tracks(struct cdrom_device_info *cdi, tracktype* tracks)
+{
+	struct cdrom_tochdr header;
+	struct cdrom_tocentry entry;
+	int ret, i;
+	tracks->data=0;
+	tracks->audio=0;
+	tracks->cdi=0;
+	tracks->xa=0;
+	tracks->error=0;
+	cdinfo(CD_OPEN, "entering cdrom_count_tracks\n"); 
+        if (!(cdi->ops->capability & CDC_PLAY_AUDIO)) { 
+                tracks->error=CDS_NO_INFO;
+                return;
+        }        
+	/* Grab the TOC header so we can see how many tracks there are */
+	ret=cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header);
+	if (ret) {
+		tracks->error=(ret == -ENOMEDIUM) ? CDS_NO_DISC : CDS_NO_INFO;
+		return;
+	}	
+	/* check what type of tracks are on this disc */
+	entry.cdte_format = CDROM_MSF;
+	for (i = header.cdth_trk0; i <= header.cdth_trk1; i++) {
+		entry.cdte_track  = i;
+		if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry)) {
+			tracks->error=CDS_NO_INFO;
+			return;
+		}	
+		if (entry.cdte_ctrl & CDROM_DATA_TRACK) {
+		    if (entry.cdte_format == 0x10)
+			tracks->cdi++;
+		    else if (entry.cdte_format == 0x20) 
+			tracks->xa++;
+		    else
+			tracks->data++;
+		} else
+		    tracks->audio++;
+		cdinfo(CD_OPEN, "track %d: format=%d, ctrl=%d\n",
+		       i, entry.cdte_format, entry.cdte_ctrl);
+	}	
+	cdinfo(CD_OPEN, "disc has %d tracks: %d=audio %d=data %d=Cd-I %d=XA\n", 
+		header.cdth_trk1, tracks->audio, tracks->data, 
+		tracks->cdi, tracks->xa);
+}	
+
 /* Requests to the low-level drivers will /always/ be done in the
    following format convention:
 
@@ -317,21 +551,6 @@
 	*curr = requested;
 }
 
-/* All checking and format change makes this code really hard to read!
- * So let's make some check and memory move macros.  These macros are
- * a little inefficient when both used in the same piece of code, as
- * verify_area is used twice, but who cares, as ioctl() calls
- * shouldn't be in inner loops.
- */
-#define GETARG(type, x) { \
-	int ret=verify_area(VERIFY_READ, (void *) arg, sizeof x); \
-	    if (ret) return ret; \
-	    copy_from_user(&x, (type *) arg, sizeof x); }
-#define PUTARG(type, x) { \
-	    int ret=verify_area(VERIFY_WRITE, (void *) arg, sizeof x); \
-	    if (ret) return ret; \
-	    copy_to_user((type *) arg, &x, sizeof x); }
-
 /* Some of the cdrom ioctls are not implemented here, because these
  * appear to be either too device-specific, or it is not clear to me
  * what use they are. These are (number of drivers that support them
@@ -346,10 +565,6 @@
  * memory-verification is performed for these ioctls.
  */
 static
-int check_for_audio_disc(struct cdrom_device_info * cdi,
-			 struct cdrom_device_ops * cdo);
-
-static
 int cdrom_ioctl(struct inode *ip, struct file *fp,
 		unsigned int cmd, unsigned long arg)
 {
@@ -360,85 +575,103 @@
 	if (cdi == NULL)
 		return -ENODEV;
 	cdo = cdi->ops;
-	/* the first few commands do not deal with audio capabilities, but
+
+	/* the first few commands do not deal with audio drive_info, but
 	   only with routines in cdrom device operations. */
 	switch (cmd) {
 		/* maybe we should order cases after statistics of use? */
 
-	case CDROMMULTISESSION:
-	{
+	case CDROMMULTISESSION: {
+		int ret;
 		struct cdrom_multisession ms_info;
 		u_char requested_format;
-		if (!(cdo->capability & CDC_MULTI_SESSION))
-			return -EINVAL;
-		GETARG(struct cdrom_multisession, ms_info);
+		cdinfo(CD_DO_IOCTL, "entering CDROMMULTISESSION\n"); 
+                if (!(cdo->capability & CDC_MULTI_SESSION))
+                        return -ENOSYS;
+		IOCTL_IN(arg, struct cdrom_multisession, ms_info);
 		requested_format = ms_info.addr_format;
+		if (!((requested_format == CDROM_MSF) ||
+			(requested_format == CDROM_LBA)))
+				return -EINVAL;
 		ms_info.addr_format = CDROM_LBA;
-		cdo->get_last_session(cdi, &ms_info);
+		if ((ret=cdo->get_last_session(cdi, &ms_info)))
+			return ret;
 		sanitize_format(&ms_info.addr, &ms_info.addr_format,
 				requested_format);
-		PUTARG(struct cdrom_multisession, ms_info);
+		IOCTL_OUT(arg, struct cdrom_multisession, ms_info);
+		cdinfo(CD_DO_IOCTL, "CDROMMULTISESSION sucessful\n"); 
 		return 0;
-	}
+		}
 
-	case CDROMEJECT:
-		if (cdo->capability & ~cdi->mask & CDC_OPEN_TRAY) {
-			if (cdi->use_count == 1) {
-				if (cdo->capability & ~cdi->mask & CDC_LOCK)
-					cdo->lock_door(cdi, 0);
-				return cdo->tray_move(cdi, 1);
-			} else
-				return -EBUSY;
-		} else
-			return -EINVAL;
+	case CDROMEJECT: {
+		int ret;
+		cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n"); 
+		if (!(cdo->capability & ~cdi->mask & CDC_OPEN_TRAY))
+			return -ENOSYS;
+		if (cdi->use_count != 1) 
+			return -EBUSY;
+		if (cdo->capability & ~cdi->mask & CDC_LOCK) {
+			if ((ret=cdo->lock_door(cdi, 0)))
+				return ret;
+			}
+		return cdo->tray_move(cdi, 1);
+		}
 
 	case CDROMCLOSETRAY:
-		if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY)
-			return cdo->tray_move(cdi, 0);
-		else
-			return -EINVAL;
+		cdinfo(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n"); 
+		if (!(cdo->capability & ~cdi->mask & CDC_OPEN_TRAY))
+			return -ENOSYS;
+		return cdo->tray_move(cdi, 0);
 
 	case CDROMEJECT_SW:
+		cdinfo(CD_DO_IOCTL, "entering CDROMEJECT_SW\n"); 
 		cdi->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT);
 		if (arg)
 			cdi->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT;
 		return 0;
 
-	case CDROM_MEDIA_CHANGED:
-		if (cdo->capability & ~cdi->mask & CDC_MEDIA_CHANGED) {
-			if (!(cdo->capability & ~cdi->mask & CDC_SELECT_DISC)
-			    || arg == CDSL_CURRENT)
-				/* cannot select disc or select current disc */
-				return media_changed(cdi, 1);
-			if ((unsigned int)arg < cdi->capacity)
-				return cdo->media_changed (cdi, arg);
-			return -EINVAL;
-		} else
+	case CDROM_MEDIA_CHANGED: {
+		cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n"); 
+		if (!(cdo->capability & ~cdi->mask & CDC_MEDIA_CHANGED))
+			return -ENOSYS;
+		if (!(cdo->capability & ~cdi->mask & CDC_SELECT_DISC)
+		    || arg == CDSL_CURRENT)
+			/* cannot select disc or select current disc */
+			return media_changed(cdi, 1);
+		if ((unsigned int)arg >= cdi->capacity)
 			return -EINVAL;
+		return cdo->media_changed (cdi, arg);
+		}
 
 	case CDROM_SET_OPTIONS:
+		cdinfo(CD_DO_IOCTL, "entering CDROM_SET_OPTIONS\n"); 
 		cdi->options |= (int) arg;
 		return cdi->options;
 
 	case CDROM_CLEAR_OPTIONS:
+		cdinfo(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n"); 
 		cdi->options &= ~(int) arg;
 		return cdi->options;
 
-	case CDROM_SELECT_SPEED:
-		if ((int)arg <= cdi->speed &&
-		    cdo->capability & ~cdi->mask & CDC_SELECT_SPEED)
-			return cdo->select_speed(cdi, arg);
-		else
-			return -EINVAL;
+	case CDROM_SELECT_SPEED: {
+		cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_SPEED\n"); 
+		if (!(cdo->capability & ~cdi->mask & CDC_SELECT_SPEED))
+			return -ENOSYS;
+		if ((int)arg > cdi->speed )
+			return -EINVAL;
+		return cdo->select_speed(cdi, arg);
+		}
 
-	case CDROM_SELECT_DISC:
-		if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE))
-			return cdo->select_disc(cdi, arg);
-		if ((int)arg < cdi->capacity &&
-		    cdo->capability & ~cdi->mask & CDC_SELECT_DISC)
+	case CDROM_SELECT_DISC: {
+		cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\n"); 
+		if (!(cdo->capability & ~cdi->mask & CDC_SELECT_DISC))
+			return -ENOSYS;
+                if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE)) 
 			return cdo->select_disc(cdi, arg);
-		else
-			return -EINVAL;
+		if ((int)arg >= cdi->capacity)
+			return -EDRIVE_CANT_DO_THIS;
+		return cdo->select_disc(cdi, arg);
+		}
 
 /* The following function is implemented, although very few audio
  * discs give Universal Product Code information, which should just be
@@ -446,51 +679,82 @@
  * is written on the CD is /not/ uniform across all discs!
  */
 	case CDROM_GET_MCN: {
+		int ret;
 		struct cdrom_mcn mcn;
+		cdinfo(CD_DO_IOCTL, "entering CDROM_GET_MCN\n"); 
 		if (!(cdo->capability & CDC_MCN))
-			return -EINVAL;
-		if (!cdo->get_mcn(cdi, &mcn)) {
-			PUTARG(struct cdrom_mcn, mcn);
-			return 0;
+			return -ENOSYS;
+		if ((ret=cdo->get_mcn(cdi, &mcn)))
+			return ret;
+		IOCTL_OUT(arg, struct cdrom_mcn, mcn);
+		cdinfo(CD_DO_IOCTL, "CDROM_GET_MCN sucessful\n"); 
+		return 0;
 		}
-		return -EINVAL;
-	}
 
-	case CDROM_DRIVE_STATUS:
-		if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE))
+	case CDROM_DRIVE_STATUS: {
+		cdinfo(CD_DO_IOCTL, "entering CDROM_DRIVE_STATUS\n"); 
+		if (!(cdo->capability & CDC_DRIVE_STATUS))
+			return -ENOSYS;
+                if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE)) 
 			return cdo->drive_status(cdi, arg);
-		if (cdo->drive_status == NULL ||
-			    ((cdo->capability & ~cdi->mask & CDC_SELECT_DISC)
-			    && (int)arg >= cdi->capacity))
+                if (((int)arg > cdi->capacity))
 			return -EINVAL;
-		else
-			return cdo->drive_status(cdi, arg);
+		return cdo->drive_status(cdi, arg);
+		}
 
-	case CDROM_DISC_STATUS:
-		if (cdo->disc_status == NULL)
-			return -EINVAL;
-		else
-			return cdo->disc_status(cdi);
+	/* Ok, this is where problems start.  The current interface for the
+	   CDROM_DISC_STATUS ioctl is flawed.  It makes the false assumption
+	   that CDs are all CDS_DATA_1 or all CDS_AUDIO, etc.  Unfortunatly,
+	   while this is often the case, it is also very common for CDs to
+	   have some tracks with data, and some tracks with audio.  Just 
+	   because I feel like it, I declare the following to be the best
+	   way to cope.  If the CD has ANY data tracks on it, it will be
+	   returned as a data CD.  If it has any XA tracks, I will return
+	   it as that.  Now I could simplify this interface by combining these 
+	   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 
+	*/
+	case CDROM_DISC_STATUS: {
+		tracktype tracks;
+		cdinfo(CD_DO_IOCTL, "entering CDROM_DISC_STATUS\n"); 
+		cdrom_count_tracks(cdi, &tracks);
+		if (tracks.error) 
+			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;
+		/* Policy mode off */
+
+		cdinfo(CD_WARNING,"This disc doesn't have any tracks I recognise!\n");
+		return CDS_NO_INFO;
+		}
 
 	case CDROM_CHANGER_NSLOTS:
-		return cdi->capacity;
+		cdinfo(CD_DO_IOCTL, "entering CDROM_CHANGER_NSLOTS\n"); 
+	return cdi->capacity;
 
 /* The following is not implemented, because there are too many
- * different data type. We could support /1/ raw mode, that is large
+ * different data types. We could support /1/ raw mode, that is large
  * enough to hold everything.
  */
 
 #if 0
 	case CDROMREADMODE1: {
+		int ret;
 		struct cdrom_msf msf;
 		char buf[CD_FRAMESIZE];
-		GETARG(struct cdrom_msf, msf);
-		if (!cdo->read_audio(dev, cmd, &msf, &buf, cdi)) {
-			PUTARG(char *, buf);
-			return 0;
+		cdinfo(CD_DO_IOCTL, "entering CDROMREADMODE1\n"); 
+		IOCTL_IN(arg, struct cdrom_msf, msf);
+		if (ret=cdo->read_audio(dev, cmd, &msf, &buf, cdi))
+			return ret;
+		IOCTL_OUT(arg, __typeof__(buf), buf);
+		return 0;
 		}
-		return -EINVAL;
-	}
 #endif
 	} /* switch */
 
@@ -499,144 +763,231 @@
 
 #define CHECKAUDIO if ((ret=check_for_audio_disc(cdi, cdo))) return ret
 
-	if (cdo->capability & CDC_PLAY_AUDIO) {
-		int ret;
+	if (!(cdo->capability & CDC_PLAY_AUDIO))
+		return -ENOSYS;
+	else {
 		switch (cmd) {
-		case CDROMSUBCHNL:
-		{
+		case CDROMSUBCHNL: {
+			int ret;
 			struct cdrom_subchnl q;
 			u_char requested, back;
-			GETARG(struct cdrom_subchnl, q);
+			/* comment out the cdinfo calls here because they
+			   fill up the sys logs when CD players poll the drive*/
+			/* cdinfo(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/ 
+			IOCTL_IN(arg, struct cdrom_subchnl, q);
 			requested = q.cdsc_format;
+                        if (!((requested == CDROM_MSF) ||
+                                (requested == CDROM_LBA)))
+                                        return -EINVAL;
 			q.cdsc_format = CDROM_MSF;
-			if (!cdo->audio_ioctl(cdi, cmd, &q)) {
-				back = q.cdsc_format; /* local copy */
-				sanitize_format(&q.cdsc_absaddr, &back,
-				requested);
-				sanitize_format(&q.cdsc_reladdr,
-				&q.cdsc_format, requested);
-				PUTARG(struct cdrom_subchnl, q);
-				return 0;
-			} else
-				return -EINVAL;
-		}
+			if ((ret=cdo->audio_ioctl(cdi, cmd, &q)))
+				return ret;
+			back = q.cdsc_format; /* local copy */
+			sanitize_format(&q.cdsc_absaddr, &back, requested);
+			sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
+			IOCTL_OUT(arg, struct cdrom_subchnl, q);
+			/* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL sucessful\n"); */ 
+			return 0;
+			}
 		case CDROMREADTOCHDR: {
+			int ret;
 			struct cdrom_tochdr header;
-			GETARG(struct cdrom_tochdr, header);
-			CHECKAUDIO;
-			if (!cdo->audio_ioctl(cdi, cmd, &header)) {
-				PUTARG(struct cdrom_tochdr, header);
-				return 0;
-			} else
-				return -EINVAL;
-		}
+			/* comment out the cdinfo calls here because they
+			   fill up the sys logs when CD players poll the drive*/
+			/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCHDR\n"); */ 
+			IOCTL_IN(arg, struct cdrom_tochdr, header);
+			if ((ret=cdo->audio_ioctl(cdi, cmd, &header)))
+				return ret;
+			IOCTL_OUT(arg, struct cdrom_tochdr, header);
+			/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCHDR sucessful\n"); */ 
+			return 0;
+			}
 		case CDROMREADTOCENTRY: {
+			int ret;
 			struct cdrom_tocentry entry;
 			u_char requested_format;
-			GETARG(struct cdrom_tocentry, entry);
+			/* comment out the cdinfo calls here because they
+			   fill up the sys logs when CD players poll the drive*/
+			/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */ 
+			IOCTL_IN(arg, struct cdrom_tocentry, entry);
 			requested_format = entry.cdte_format;
+			if (!((requested_format == CDROM_MSF) || 
+				(requested_format == CDROM_LBA)))
+					return -EINVAL;
 			/* make interface to low-level uniform */
 			entry.cdte_format = CDROM_MSF;
-			if (!(cdo->audio_ioctl(cdi, cmd, &entry))) {
-				sanitize_format(&entry.cdte_addr,
-				&entry.cdte_format, requested_format);
-				PUTARG(struct cdrom_tocentry, entry);
-				return 0;
-			} else
-				return -EINVAL;
-		}
+			if ((ret=cdo->audio_ioctl(cdi, cmd, &entry)))
+				return ret;
+			sanitize_format(&entry.cdte_addr,
+			&entry.cdte_format, requested_format);
+			IOCTL_OUT(arg, struct cdrom_tocentry, entry);
+			/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCENTRY sucessful\n"); */ 
+			return 0;
+			}
 		case CDROMPLAYMSF: {
+			int ret;
 			struct cdrom_msf msf;
-			GETARG(struct cdrom_msf, msf);
+			cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); 
+			IOCTL_IN(arg, struct cdrom_msf, msf);
 			CHECKAUDIO;
 			return cdo->audio_ioctl(cdi, cmd, &msf);
-		}
+			}
 		case CDROMPLAYTRKIND: {
-			struct cdrom_ti track_index;
-			GETARG(struct cdrom_ti, track_index);
+			int ret;
+			struct cdrom_ti ti;
+			cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n"); 
+			IOCTL_IN(arg, struct cdrom_ti, ti);
 			CHECKAUDIO;
-			return cdo->audio_ioctl(cdi, cmd, &track_index);
-		}
+			return cdo->audio_ioctl(cdi, cmd, &ti);
+			}
 		case CDROMVOLCTRL: {
 			struct cdrom_volctrl volume;
-			GETARG(struct cdrom_volctrl, volume);
+			cdinfo(CD_DO_IOCTL, "entering CDROMVOLCTRL\n"); 
+			IOCTL_IN(arg, struct cdrom_volctrl, volume);
 			return cdo->audio_ioctl(cdi, cmd, &volume);
-		}
+			}
 		case CDROMVOLREAD: {
+			int ret;
 			struct cdrom_volctrl volume;
-			if (!cdo->audio_ioctl(cdi, cmd, &volume)) {
-				PUTARG(struct cdrom_volctrl, volume);
-				return 0;
+			cdinfo(CD_DO_IOCTL, "entering CDROMVOLREAD\n"); 
+			if ((ret=cdo->audio_ioctl(cdi, cmd, &volume)))
+				return ret;
+			IOCTL_OUT(arg, struct cdrom_volctrl, volume);
+			return 0;
 			}
-			return -EINVAL;
-		}
 		case CDROMSTART:
-			CHECKAUDIO;
 		case CDROMSTOP:
 		case CDROMPAUSE:
-		case CDROMRESUME:
+		case CDROMRESUME: {
+			int ret;
+			cdinfo(CD_DO_IOCTL, "doing audio ioctl (start/stop/pause/resume)\n"); 
+			CHECKAUDIO;
 			return cdo->audio_ioctl(cdi, cmd, NULL);
+			}
 		} /* switch */
 	}
 
-	if (cdo->dev_ioctl != NULL)     /* device specific ioctls? */
+	/* device specific ioctls? */
+	if (!(cdo->capability & CDC_IOCTLS))
+		return -ENOSYS;
+	else 
 		return cdo->dev_ioctl(cdi, cmd, arg);
-	return -EINVAL;
-}
-
-/* This code is similar to that in open_for_data. The routine is called
-   in case a audio play operation is requested. It doesn't make much sense
-   do do this on a data disc.
- */
-int check_for_audio_disc(struct cdrom_device_info * cdi,
-			 struct cdrom_device_ops * cdo)
-{
-	if (!(cdi->options & CDO_CHECK_TYPE))
-		return 0;
-	if (cdo->drive_status != NULL) {
-		int ds = cdo->drive_status(cdi, CDSL_CURRENT);
-		if (ds == CDS_TRAY_OPEN) {
-			/* can/may i close it? */
-			if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY &&
-			    cdi->options & CDO_AUTO_CLOSE) {
-				if (cdo->tray_move(cdi,0))
-					return -EIO;
-			} else
-				return -ENOMEDIUM; /* can't close: too bad */
-			ds = cdo->drive_status(cdi, CDSL_CURRENT);
-			if (ds == CDS_NO_DISC)
-				return -ENOMEDIUM;
-		}
-		if (cdo->disc_status != NULL) {
-			ds = cdo->disc_status(cdi);
-			if (ds == CDS_NO_DISC)
-				return -ENOMEDIUM;
-			if (ds != CDS_AUDIO)
-				return -EMEDIUMTYPE;
-		}
-	}
-	return 0;
 }
 
 EXPORT_SYMBOL(register_cdrom);
 EXPORT_SYMBOL(unregister_cdrom);
 EXPORT_SYMBOL(cdrom_fops);
 
+#ifdef CONFIG_SYSCTL
+#define CDROM_STR_SIZE 1000
+static char cdrom_drive_info[CDROM_STR_SIZE]="info\n";
+
+#ifdef MODULE
+
+static struct ctl_table_header *cdrom_sysctl_header;
+
+static void cdrom_sysctl_register(void)
+{
+	cdrom_sysctl_header = register_sysctl_table(cdrom_root_table, 0);
+}
+
+static void cdrom_sysctl_unregister(void)
+{
+	unregister_sysctl_table(cdrom_sysctl_header);
+}
+
+#endif
+
+/* Place files in /proc/sys/dev/cdrom */
+ctl_table cdrom_table[] = {
+	{DEV_CDROM_INFO, "info", &cdrom_drive_info, 
+		CDROM_STR_SIZE*sizeof(char), 0444, NULL, &cdrom_sysctl_info},
+	{0}
+	};
+
+ctl_table cdrom_cdrom_table[] = {
+	{DEV_CDROM, "cdrom", NULL, 0, 0555, cdrom_table},
+	{0}
+	};
+
+/* Make sure that /proc/sys/dev is there */
+ctl_table cdrom_root_table[] = {
+	{CTL_DEV, "dev", NULL, 0, 0555, cdrom_cdrom_table},
+	{0}
+	};
+
+
+int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
+                           void *buffer, size_t *lenp)
+{
+        int retv;
+	char buf[40];
+	struct cdrom_device_info *cdi;
+
+        sprintf(cdrom_drive_info, "CD-ROM information\n\n");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next){
+           sprintf(buf, "drive name:\t\t%s\n", cdi->name);
+           strcat(cdrom_drive_info, buf);
+           sprintf(buf, "drive speed:\t\t%d\n", cdi->speed);
+           strcat(cdrom_drive_info, buf);
+           sprintf(buf, "drive # of slots:\t%d\n", cdi->capacity);
+           strcat(cdrom_drive_info, buf);
+           sprintf(buf, "Can close tray:\t\t%d\n", ((cdi->ops->capability & CDC_CLOSE_TRAY)!=0));
+           strcat(cdrom_drive_info, buf);
+           sprintf(buf, "Can open tray:\t\t%d\n", ((cdi->ops->capability & CDC_OPEN_TRAY)!=0));
+           strcat(cdrom_drive_info, buf);
+           sprintf(buf, "Can lock tray:\t\t%d\n", ((cdi->ops->capability & CDC_LOCK)!=0));
+           strcat(cdrom_drive_info, buf);
+           sprintf(buf, "Can change speed:\t%d\n", ((cdi->ops->capability & CDC_SELECT_SPEED)!=0));
+           strcat(cdrom_drive_info, buf);
+           sprintf(buf, "Can select disk:\t%d\n", ((cdi->ops->capability & CDC_SELECT_DISC)!=0));
+           strcat(cdrom_drive_info, buf);
+           sprintf(buf, "Can read multisession:\t%d\n", ((cdi->ops->capability & CDC_MULTI_SESSION)!=0));
+           strcat(cdrom_drive_info, buf);
+           sprintf(buf, "Can read MCN:\t\t%d\n", ((cdi->ops->capability & CDC_MCN)!=0));
+           strcat(cdrom_drive_info, buf);
+           sprintf(buf, "Reports media changed:\t%d\n", ((cdi->ops->capability & CDC_MEDIA_CHANGED)!=0));
+           strcat(cdrom_drive_info, buf);
+           sprintf(buf, "Can play audio:\t\t%d\n", ((cdi->ops->capability & CDC_PLAY_AUDIO)!=0));
+           strcat(cdrom_drive_info, buf);
+           strcat(cdrom_drive_info, "\n\n");
+	}
+	*lenp=sizeof(cdrom_drive_info);
+
+        if (!write) {
+        	retv = proc_dostring(ctl, write, filp, buffer, lenp);
+        }
+        else
+        	retv = proc_dostring(ctl, write, filp, buffer, lenp);
+        return retv;
+}
+
+
+#endif /* endif CONFIG_SYSCTL */
+
+
 #ifdef MODULE
 int init_module(void)
 {
-	printk(KERN_INFO "Module cdrom: Generic CDROM driver " REVISION "\n");
+	cdrom_init();
+#ifdef CONFIG_SYSCTL
+	cdrom_sysctl_register();
+#endif /* CONFIG_SYSCTL */ 
 	return 0;
 }
 
 void cleanup_module(void)
 {
-	/*
-	printk(KERN_INFO "Module cdrom removed\n");
-	*/
+	printk(KERN_INFO "Uniform CD-ROM driver unloaded\n");
+#ifdef CONFIG_SYSCTL
+	cdrom_sysctl_unregister();
+#endif /* CONFIG_SYSCTL */ 
 }
 
-#endif
+#endif /* endif MODULE */
+
+
+
 /*
  * Local variables:
  * comment-column: 40
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/cdu31a.c linux/drivers/cdrom/cdu31a.c
--- v2.1.69/linux/drivers/cdrom/cdu31a.c	Wed Sep 24 20:05:46 1997
+++ linux/drivers/cdrom/cdu31a.c	Tue Dec  2 11:41:44 1997
@@ -111,6 +111,13 @@
  *    Heiko Eissfeldt <heiko@colossus.escape.de>
  *         For finding abug in the return of the track numbers.
  */
+/* conversion to Uniform cdrom layer.
+   TOC processing redone for proper multisession support.
+
+   TODO: 
+         CDs with form1 and form2 sectors cause problems
+         with current read-ahead strategy.
+   Heiko Eissfeldt Sep 97 */
 
 /*
  *
@@ -173,7 +180,13 @@
  *             status and be reset.  It recovers, though.
  *
  *  03/07/97 - Fixed a problem with timers.
- */
+ *
+ *
+ *  18 Spetember 1997 -- Ported to Uniform CD-ROM driver by 
+ *                 Heiko Eissfeldt <heiko@colossus.escape.de> with additional
+ *                 changes by Erik Andersen <andersee@debian.org>
+ *
+*/
 
 #include <linux/major.h>
 
@@ -198,16 +211,16 @@
 #include <asm/dma.h>
 
 #include <linux/cdrom.h>
-#include <linux/cdu31a.h>
+#include "cdu31a.h"
 
 #define MAJOR_NR CDU31A_CDROM_MAJOR
 #include <linux/blk.h>
 
-#define DEBUG 0
-
-#define CDU31A_READAHEAD 128  /* 128 sector, 64kB, 32 reads read-ahead */
+#define CDU31A_READAHEAD 4  /* 128 sector, 64kB, 32 reads read-ahead */
 #define CDU31A_MAX_CONSECUTIVE_ATTENTIONS 10
 
+#define DEBUG 0
+
 /* Define the following if you have data corruption problems. */
 #undef SONY_POLL_EACH_BYTE
 
@@ -240,7 +253,8 @@
 static int handle_sony_cd_attention(void);
 static int read_subcode(void);
 static void sony_get_toc(void);
-static int scd_open(struct inode *inode, struct file *filp);
+/*static int scd_open(struct inode *inode, struct file *filp);*/
+static int scd_open(struct cdrom_device_info *, int);
 static void do_sony_cd_cmd(unsigned char cmd,
                            unsigned char *params,
                            unsigned int num_params,
@@ -276,6 +290,8 @@
 
 static int sony_spun_up = 0;               /* Has the drive been spun up? */
 
+static int sony_speed = 0;                 /* Last wanted speed */
+
 static int sony_xa_mode = 0;               /* Is an XA disk in the drive
 					      and the drive a CDU31A? */
 
@@ -288,9 +304,12 @@
 static int sony_pas_init = 0;		   /* Initialize the Pro-Audio
 					      Spectrum card? */
 
-static struct s_sony_session_toc sony_toc;  /* Holds the
-					       table of
-					       contents. */
+static struct s_sony_session_toc single_toc;  /* Holds the
+					         table of
+					         contents. */
+
+static struct s_all_sessions_toc sony_toc; /* entries gathered from all
+                                                sessions */
 
 static int sony_toc_read = 0;		   /* Has the TOC been read for
 					      the drive? */
@@ -307,7 +326,8 @@
 						   using the CDROM drive, or
 						   NULL if none. */
 
-static int is_double_speed = 0; /* Is the drive a CDU33A? */
+static int is_double_speed = 0; /* does the drive support double speed ? */
+static int is_a_cdu31a = 1;     /* Is the drive a CDU31A? */
 
 static int is_auto_eject = 1;   /* Door has been locked? 1=No/0=Yes */
 
@@ -375,6 +395,30 @@
    return retval;
 }
 
+/*
+ * Uniform cdrom interface function
+ * report back, if disc has changed from time of last request.
+ */
+static int
+scd_media_changed(struct cdrom_device_info *cdi, int disc_nr)
+{
+  return scd_disk_change(cdi->dev);
+}
+
+/*
+ * Uniform cdrom interface function
+ * report back, if drive is ready
+ */
+static int scd_drive_status(struct cdrom_device_info *cdi, int slot_nr)
+{
+  if (CDSL_CURRENT != slot_nr) {
+     /* we have no changer support */
+     return -EINVAL;
+  }
+
+  return sony_spun_up ? CDS_DISC_OK : CDS_DRIVE_NOT_READY;
+}
+
 static inline void
 enable_interrupts(void)
 {
@@ -474,6 +518,25 @@
    outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg);
 }
 
+/*
+ * Uniform cdrom interface function
+ * reset drive and return when it is ready
+ */
+static int scd_reset(struct cdrom_device_info * cdi)
+{
+  int retry_count;
+
+  reset_drive();
+
+  retry_count = jiffies + SONY_RESET_TIMEOUT;
+  while ((retry_count > jiffies) && (!is_attention()))
+  {
+     sony_sleep();
+  }
+
+  return 0;
+}
+
 static inline void
 clear_attention(void)
 {
@@ -574,11 +637,65 @@
 }
 
 /*
+ * give more verbose error messages
+ */
+static unsigned char *translate_error( unsigned char err_code )
+{
+   static unsigned char errbuf[80];
+
+   switch (err_code) {
+     case 0x10: return "illegal command ";
+     case 0x11: return "illegal parameter ";
+
+     case 0x20: return "not loaded ";
+     case 0x21: return "no disc ";
+     case 0x22: return "not spinning ";
+     case 0x23: return "spinning ";
+     case 0x25: return "spindle servo ";
+     case 0x26: return "focus servo ";
+     case 0x29: return "eject mechanism ";
+     case 0x2a: return "audio playing ";
+     case 0x2c: return "emergency eject ";
+
+     case 0x30: return "focus ";
+     case 0x31: return "frame sync ";
+     case 0x32: return "subcode address ";
+     case 0x33: return "block sync ";
+     case 0x34: return "header address ";
+
+     case 0x40: return "illegal track read ";
+     case 0x41: return "mode 0 read ";
+     case 0x42: return "illegal mode read ";
+     case 0x43: return "illegal block size read ";
+     case 0x44: return "mode read ";
+     case 0x45: return "form read ";
+     case 0x46: return "leadout read ";
+     case 0x47: return "buffer overrun ";
+
+     case 0x53: return "unrecoverable CIRC ";
+     case 0x57: return "unrecoverable LECC ";
+
+     case 0x60: return "no TOC ";
+     case 0x61: return "invalid subcode data ";
+     case 0x63: return "focus on TOC read ";
+     case 0x64: return "frame sync on TOC read ";
+     case 0x65: return "TOC data ";
+
+     case 0x70: return "hardware failure ";
+     case 0x91: return "leadin ";
+     case 0x92: return "leadout ";
+     case 0x93: return "data track ";
+   }
+   sprintf(errbuf, "unknown 0x%02x ", err_code);
+   return errbuf;
+}
+
+/*
  * Set the drive parameters so the drive will auto-spin-up when a
  * disk is inserted.
  */
 static void
-set_drive_params(void)
+set_drive_params(int want_doublespeed)
 {
    unsigned char res_reg[12];
    unsigned int res_size;
@@ -602,7 +719,7 @@
 
    if (is_auto_eject) params[1] |= SONY_AUTO_EJECT_BIT;
    
-   if (is_double_speed)
+   if (is_double_speed && want_doublespeed)
    {
       params[1] |= SONY_DOUBLE_SPEED_BIT; /* Set the drive to double speed if 
                                              possible */
@@ -619,6 +736,38 @@
 }
 
 /*
+ * Uniform cdrom interface function
+ * select reading speed for data access
+ */
+static int scd_select_speed(struct cdrom_device_info *cdi, int speed)
+{
+  if (speed == 0)
+    sony_speed = 1;
+  else 
+    sony_speed = speed - 1;
+
+  set_drive_params(sony_speed);
+  return 0;
+}
+
+/*
+ * Uniform cdrom interface function
+ * lock or unlock eject button
+ */
+static int scd_lock_door(struct cdrom_device_info *cdi, int lock)
+{
+   if (lock == 0 && sony_usage == 1)
+   {
+      /* Unlock the door, only if nobody is using the drive */
+      is_auto_eject = 1;
+   } else {
+      is_auto_eject = 0;
+   }
+   set_drive_params(sony_speed);
+   return 0;
+}
+
+/*
  * This code will reset the drive and attempt to restore sane parameters.
  */
 static void
@@ -636,7 +785,7 @@
    {
       sony_sleep();
    }
-   set_drive_params();
+   set_drive_params(sony_speed);
    do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
    if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
    {
@@ -1069,9 +1218,9 @@
    unsigned int log;
 
 
-   log = bcd_to_int(msf[2]);
-   log += bcd_to_int(msf[1]) * 75;
-   log += bcd_to_int(msf[0]) * 4500;
+   log = msf[2];
+   log += msf[1] * 75;
+   log += msf[0] * 4500;
    log = log - LOG_START_OFFSET;
 
    return log;
@@ -1092,10 +1241,37 @@
    buf[2] = size % 256;
 }
 
+/* Uniform cdrom interface function.
+   Return the status of the current disc:
+   If it is recognized as CD-I -> return XA Mode 2 Form 2
+   If it is recognized as XA   -> return XA Mode 2 Form 1
+   If there is at least one data track return Mode 1
+   else return type AUDIO
+ */
+static int scd_disc_status(struct cdrom_device_info *cdi)
+{
+  if (sony_spun_up)
+  {
+    int i;
+
+    sony_get_toc();
+    /* look into the TOC */
+    if (sony_toc.disk_type == 0x10)   /* this is a CD-I disc */
+      return CDS_XA_2_2;
+    if (sony_toc.disk_type == 0x20)   /* this is a XA disc */
+      return CDS_XA_2_1;
+    for (i = 0; i < sony_toc.track_entries; i++)
+      if (sony_toc.tracks[i].control & CDROM_DATA_TRACK)
+        return CDS_DATA_1;
+    return CDS_AUDIO;
+  } else 
+    return CDS_NO_INFO;
+}
+
 /* Starts a read operation. Returns 0 on success and 1 on failure. 
    The read operation used here allows multiple sequential sectors 
    to be read and status returned for each sector.  The driver will
-   read the out one at a time as the requests come and abort the
+   read the output one at a time as the requests come and abort the
    operation if the requested sector is not the next one from the
    drive. */
 static int
@@ -1128,7 +1304,7 @@
    /* Read the full readahead amount. */
    else
    {
-      read_size = CDU31A_READAHEAD;
+      read_size = CDU31A_READAHEAD / 4;
    }
    size_to_buf(read_size, &params[3]);
 
@@ -1192,8 +1368,9 @@
    do_sony_cd_cmd(SONY_ABORT_CMD, NULL, 0, result_reg, &result_size);
    if ((result_reg[0] & 0xf0) == 0x20)
    {
-      printk("CDU31A: Error aborting read, error = 0x%2.2x\n",
-             result_reg[1]);
+      printk("CDU31A: Error aborting read, %s error\n",
+             translate_error(
+             result_reg[1]));
    }
 
    while (is_result_reg_not_empty())
@@ -1525,6 +1702,8 @@
 #endif
 }
 
+static int scd_spinup(void);
+
 /*
  * The OS calls this to perform a read or write operation to the drive.
  * Write obviously fail.  Reads to a read ahead of sony_buffer_size
@@ -1599,13 +1778,7 @@
 
       if (!sony_spun_up)
       {
-         struct inode in;
-
-         /* This is a kludge to get a valid dev in an inode that
-            scd_open can take.  That's the only thing scd_open()
-            uses the inode for. */
-         in.i_rdev = CURRENT->rq_dev;
-         scd_open(&in,NULL);
+         scd_spinup();
       }
 
       /* I don't use INIT_REQUEST because it calls return, which would
@@ -1633,16 +1806,6 @@
 	 goto cdu31a_request_startover;
       }
 
-      /* Check for base read of multi-session disk.  This will still work
-         for single session disks, so just do it.  Blocks less than 80
-         are for the volume info, so offset them by the start track (which
-         should be zero for a single-session disk). */
-      if (block < 80)
-      {
-	 /* Offset the request into the session. */
-	 block += (sony_toc.start_track_lba * 4);
-      }
-
       switch(CURRENT->cmd)
       {
       case READ:
@@ -1735,7 +1898,7 @@
             }
             else
             {
-               printk("CDU31A: Read error: 0x%2.2x\n", res_reg[1]);
+               printk("CDU31A: %s error for block %d, nblock %d\n", translate_error(res_reg[1]), block, nblock);
             }
             goto try_read_again;
          }
@@ -1775,23 +1938,6 @@
 #endif
 }
 
-/* Copy overlapping buffers. */
-static void
-mcovlp(char *dst,
-       char *src,
-       int  size)
-{
-   src += (size - 1);
-   dst += (size - 1);
-   while (size > 0)
-   {
-      *dst = *src;
-      size--;
-      dst--;
-      src--;
-   }
-}
-
 
 /*
  * Read the table of contents from the drive and set up TOC if
@@ -1805,7 +1951,9 @@
    unsigned char parms[1];
    int session;
    int num_spin_ups;
-
+   int totaltracks = 0;
+   int mint = 99;
+   int maxt = 0;
 
 #if DEBUG
    printk("Entering sony_get_toc\n");
@@ -1839,9 +1987,8 @@
             goto respinup_on_gettoc;
          }
 
-         printk("cdu31a: Error reading TOC: %x %x\n",
-                sony_toc.exec_status[0],
-                sony_toc.exec_status[1]);
+         printk("cdu31a: Error reading TOC: %x %s\n",
+                res_reg[0], translate_error(res_reg[1]));
 	 return;
       }
 
@@ -1851,9 +1998,12 @@
 	 fails.  Then we know what the last valid session on the disk is.
 	 No need to check session 0, since session 0 is the same as session
          1; the command returns different information if you give it 0. 
-         Don't check session 1 because that is the first session, it must
-         be there. */
-      session = 2;
+        */
+#if DEBUG
+      memset(&sony_toc, 0x0e, sizeof(sony_toc));
+      memset(&single_toc, 0x0f, sizeof(single_toc));
+#endif
+      session = 1;
       while (1)
       {
 #if DEBUG
@@ -1875,95 +2025,265 @@
 	    /* An error reading the TOC, this must be past the last session. */
 	    break;
          }
-
-         session++;
-
-         /* Let's not get carried away... */
-         if (session > 20)
-         {
-            printk("cdu31a: too many sessions: %d\n", session);
-            return;
-         }
-      }
-
-      session--;
-
 #if DEBUG
-      printk("Reading session %d\n", session);
+         printk("Reading session %d\n", session);
 #endif
 
-      parms[0] = session;
-      do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD,
+         parms[0] = session;
+         do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD,
 		     parms,
 		     1, 
-		     (unsigned char *) &sony_toc, 
+		     (unsigned char *) &single_toc, 
 		     &res_size);
-      if ((res_size < 2) || ((sony_toc.exec_status[0] & 0xf0) == 0x20))
-      {
-         printk("cdu31a: Error reading session %d: %x %x\n",
+         if ((res_size < 2) || ((single_toc.exec_status[0] & 0xf0) == 0x20))
+         {
+            printk("cdu31a: Error reading session %d: %x %s\n",
                 session,
-                sony_toc.exec_status[0],
-                sony_toc.exec_status[1]);
-	 /* An error reading the TOC.  Return without sony_toc_read
-	    set. */
-	 return;
-      }
-      
-      sony_toc_read = 1;
+                single_toc.exec_status[0],
+                translate_error(single_toc.exec_status[1]));
+	    /* An error reading the TOC.  Return without sony_toc_read
+	       set. */
+	    return;
+         }
+#if DEBUG
+      printk("add0 %01x, con0 %01x, poi0 %02x, 1st trk %d, dsktyp %x, dum0 %x\n",
+              single_toc.address0, single_toc.control0, single_toc.point0,
+              bcd_to_int(single_toc.first_track_num), single_toc.disk_type, single_toc.dummy0);
+      printk("add1 %01x, con1 %01x, poi1 %02x, lst trk %d, dummy1 %x, dum2 %x\n",
+              single_toc.address1, single_toc.control1, single_toc.point1,
+              bcd_to_int(single_toc.last_track_num), single_toc.dummy1, single_toc.dummy2);
+      printk("add2 %01x, con2 %01x, poi2 %02x leadout start min %d, sec %d, frame %d\n",
+              single_toc.address2, single_toc.control2, single_toc.point2,
+              bcd_to_int(single_toc.lead_out_start_msf[0]),
+              bcd_to_int(single_toc.lead_out_start_msf[1]),
+              bcd_to_int(single_toc.lead_out_start_msf[2]));
+      if (res_size > 18 && single_toc.pointb0 > 0xaf)
+      printk("addb0 %01x, conb0 %01x, poib0 %02x, nextsession min %d, sec %d, frame %d\n"
+             "#mode5_ptrs %02d, max_start_outer_leadout_msf min %d, sec %d, frame %d\n",
+              single_toc.addressb0, single_toc.controlb0, single_toc.pointb0,
+              bcd_to_int(single_toc.next_poss_prog_area_msf[0]),
+              bcd_to_int(single_toc.next_poss_prog_area_msf[1]),
+              bcd_to_int(single_toc.next_poss_prog_area_msf[2]),
+              single_toc.num_mode_5_pointers,
+              bcd_to_int(single_toc.max_start_outer_leadout_msf[0]),
+              bcd_to_int(single_toc.max_start_outer_leadout_msf[1]),
+              bcd_to_int(single_toc.max_start_outer_leadout_msf[2]));
+      if (res_size > 27 && single_toc.pointb1 > 0xaf)
+      printk("addb1 %01x, conb1 %01x, poib1 %02x, %x %x %x %x #skipint_ptrs %d, #skiptrkassign %d %x\n",
+              single_toc.addressb1, single_toc.controlb1, single_toc.pointb1,
+              single_toc.dummyb0_1[0],
+              single_toc.dummyb0_1[1],
+              single_toc.dummyb0_1[2],
+              single_toc.dummyb0_1[3],
+              single_toc.num_skip_interval_pointers,
+              single_toc.num_skip_track_assignments,
+              single_toc.dummyb0_2);
+      if (res_size > 36 && single_toc.pointb2 > 0xaf)
+      printk("addb2 %01x, conb2 %01x, poib2 %02x, %02x %02x %02x %02x %02x %02x %02x\n",
+              single_toc.addressb2, single_toc.controlb2, single_toc.pointb2,
+              single_toc.tracksb2[0],
+              single_toc.tracksb2[1],
+              single_toc.tracksb2[2],
+              single_toc.tracksb2[3],
+              single_toc.tracksb2[4],
+              single_toc.tracksb2[5],
+              single_toc.tracksb2[6]);
+      if (res_size > 45 && single_toc.pointb3 > 0xaf)
+      printk("addb3 %01x, conb3 %01x, poib3 %02x, %02x %02x %02x %02x %02x %02x %02x\n",
+              single_toc.addressb3, single_toc.controlb3, single_toc.pointb3,
+              single_toc.tracksb3[0],
+              single_toc.tracksb3[1],
+              single_toc.tracksb3[2],
+              single_toc.tracksb3[3],
+              single_toc.tracksb3[4],
+              single_toc.tracksb3[5],
+              single_toc.tracksb3[6]);
+      if (res_size > 54 && single_toc.pointb4 > 0xaf)
+      printk("addb4 %01x, conb4 %01x, poib4 %02x, %02x %02x %02x %02x %02x %02x %02x\n",
+              single_toc.addressb4, single_toc.controlb4, single_toc.pointb4,
+              single_toc.tracksb4[0],
+              single_toc.tracksb4[1],
+              single_toc.tracksb4[2],
+              single_toc.tracksb4[3],
+              single_toc.tracksb4[4],
+              single_toc.tracksb4[5],
+              single_toc.tracksb4[6]);
+      if (res_size > 63 && single_toc.pointc0 > 0xaf)
+      printk("addc0 %01x, conc0 %01x, poic0 %02x, %02x %02x %02x %02x %02x %02x %02x\n",
+              single_toc.addressc0, single_toc.controlc0, single_toc.pointc0,
+              single_toc.dummyc0[0],
+              single_toc.dummyc0[1],
+              single_toc.dummyc0[2],
+              single_toc.dummyc0[3],
+              single_toc.dummyc0[4],
+              single_toc.dummyc0[5],
+              single_toc.dummyc0[6]);
+#endif
+#undef DEBUG
+#define DEBUG 0
+
+      sony_toc.lead_out_start_msf[0] = bcd_to_int(single_toc.lead_out_start_msf[0]);
+      sony_toc.lead_out_start_msf[1] = bcd_to_int(single_toc.lead_out_start_msf[1]);
+      sony_toc.lead_out_start_msf[2] = bcd_to_int(single_toc.lead_out_start_msf[2]);
+      sony_toc.lead_out_start_lba = single_toc.lead_out_start_lba =
+                    msf_to_log(sony_toc.lead_out_start_msf);
 
       /* For points that do not exist, move the data over them
 	 to the right location. */
-      if (sony_toc.pointb0 != 0xb0)
+      if (single_toc.pointb0 != 0xb0)
       {
-	 mcovlp(((char *) &sony_toc) + 27,
-		((char *) &sony_toc) + 18,
-		res_size - 18);
+	 memmove(((char *) &single_toc) + 27,
+		 ((char *) &single_toc) + 18,
+		 res_size - 18);
 	 res_size += 9;
       }
-      if (sony_toc.pointb1 != 0xb1)
+      else if (res_size > 18) {
+        sony_toc.lead_out_start_msf[0] = bcd_to_int(single_toc.max_start_outer_leadout_msf[0]);
+        sony_toc.lead_out_start_msf[1] = bcd_to_int(single_toc.max_start_outer_leadout_msf[1]);
+        sony_toc.lead_out_start_msf[2] = bcd_to_int(single_toc.max_start_outer_leadout_msf[2]);
+        sony_toc.lead_out_start_lba = msf_to_log(sony_toc.lead_out_start_msf);
+      }
+      if (single_toc.pointb1 != 0xb1)
       {
-	 mcovlp(((char *) &sony_toc) + 36,
-		((char *) &sony_toc) + 27,
+	 memmove(((char *) &single_toc) + 36,
+		((char *) &single_toc) + 27,
 		res_size - 27);
 	 res_size += 9;
       }
-      if (sony_toc.pointb2 != 0xb2)
+      if (single_toc.pointb2 != 0xb2)
       {
-	 mcovlp(((char *) &sony_toc) + 45,
-		((char *) &sony_toc) + 36,
+	 memmove(((char *) &single_toc) + 45,
+		((char *) &single_toc) + 36,
 		res_size - 36);
 	 res_size += 9;
       }
-      if (sony_toc.pointb3 != 0xb3)
+      if (single_toc.pointb3 != 0xb3)
       {
-	 mcovlp(((char *) &sony_toc) + 54,
-		((char *) &sony_toc) + 45,
+	 memmove(((char *) &single_toc) + 54,
+		((char *) &single_toc) + 45,
 		res_size - 45);
 	 res_size += 9;
       }
-      if (sony_toc.pointb4 != 0xb4)
+      if (single_toc.pointb4 != 0xb4)
       {
-	 mcovlp(((char *) &sony_toc) + 63,
-		((char *) &sony_toc) + 54,
+	 memmove(((char *) &single_toc) + 63,
+		((char *) &single_toc) + 54,
 		res_size - 54);
 	 res_size += 9;
       }
-      if (sony_toc.pointc0 != 0xc0)
+      if (single_toc.pointc0 != 0xc0)
       {
-	 mcovlp(((char *) &sony_toc) + 72,
-		((char *) &sony_toc) + 63,
+	 memmove(((char *) &single_toc) + 72,
+		((char *) &single_toc) + 63,
 		res_size - 63);
 	 res_size += 9;
       }
 
-      sony_toc.start_track_lba = msf_to_log(sony_toc.tracks[0].track_start_msf);
-      sony_toc.lead_out_start_lba = msf_to_log(sony_toc.lead_out_start_msf);
+#if DEBUG
+      printk("start track lba %u,  leadout start lba %u\n",
+              single_toc.start_track_lba, single_toc.lead_out_start_lba);
+      { int i;
+        for (i = 0; i < 1 + bcd_to_int(single_toc.last_track_num)
+                          - bcd_to_int(single_toc.first_track_num); i++) {
+          printk("trk %02d: add 0x%01x, con 0x%01x,  track %02d, start min %02d, sec %02d, frame %02d\n",
+                 i, single_toc.tracks[i].address, single_toc.tracks[i].control,
+                 bcd_to_int(single_toc.tracks[i].track),
+                 bcd_to_int(single_toc.tracks[i].track_start_msf[0]),
+                 bcd_to_int(single_toc.tracks[i].track_start_msf[1]),
+                 bcd_to_int(single_toc.tracks[i].track_start_msf[2]));
+          if (mint > bcd_to_int(single_toc.tracks[i].track))
+            mint = bcd_to_int(single_toc.tracks[i].track);
+          if (maxt < bcd_to_int(single_toc.tracks[i].track))
+            maxt = bcd_to_int(single_toc.tracks[i].track);
+        }
+        printk("min track number %d,   max track number %d\n", mint, maxt);
+      }
+#endif
+
+         /* prepare a special table of contents for a CD-I disc. They don't have one. */
+         if (single_toc.disk_type == 0x10 &&
+             single_toc.first_track_num == 2 &&
+             single_toc.last_track_num == 2 /* CD-I */)
+         {
+            sony_toc.tracks[totaltracks].address = 1;
+	    sony_toc.tracks[totaltracks].control = 4; /* force data tracks */
+            sony_toc.tracks[totaltracks].track = 1;
+            sony_toc.tracks[totaltracks].track_start_msf[0] = 0;
+            sony_toc.tracks[totaltracks].track_start_msf[1] = 2;
+            sony_toc.tracks[totaltracks].track_start_msf[2] = 0;
+            mint = maxt = 1;
+            totaltracks++;
+         } else
+         /* gather track entries from this session */
+         { int i;
+	   for (i = 0; i < 1 + bcd_to_int(single_toc.last_track_num)
+                          - bcd_to_int(single_toc.first_track_num); i++, totaltracks++) {
+             sony_toc.tracks[totaltracks].address = single_toc.tracks[i].address;
+             sony_toc.tracks[totaltracks].control = single_toc.tracks[i].control;
+             sony_toc.tracks[totaltracks].track = bcd_to_int(single_toc.tracks[i].track);
+             sony_toc.tracks[totaltracks].track_start_msf[0] = 
+                  bcd_to_int(single_toc.tracks[i].track_start_msf[0]);
+             sony_toc.tracks[totaltracks].track_start_msf[1] = 
+                  bcd_to_int(single_toc.tracks[i].track_start_msf[1]);
+             sony_toc.tracks[totaltracks].track_start_msf[2] = 
+                  bcd_to_int(single_toc.tracks[i].track_start_msf[2]);
+             if (i == 0)
+               single_toc.start_track_lba = msf_to_log(sony_toc.tracks[totaltracks].track_start_msf);
+             if (mint > sony_toc.tracks[totaltracks].track)
+               mint = sony_toc.tracks[totaltracks].track;
+             if (maxt < sony_toc.tracks[totaltracks].track)
+               maxt = sony_toc.tracks[totaltracks].track;
+           }
+         }
+         sony_toc.first_track_num = mint;
+         sony_toc.last_track_num = maxt;
+         /* Disk type of last session wins. For example:
+            CD-Extra has disk type 0 for the first session, so
+            a dumb HiFi CD player thinks it is a plain audio CD.
+	    We are interested in the disk type of the last session,
+            which is 0x20 (XA) for CD-Extra, so we can access the
+            data track ... */
+         sony_toc.disk_type = single_toc.disk_type;
+         sony_toc.sessions = session;
+
+         /* don't believe everything :-) */
+         if (session == 1)
+            single_toc.start_track_lba = 0;
+         sony_toc.start_track_lba = single_toc.start_track_lba;
 
+         if (session > 1 && single_toc.pointb0 == 0xb0 &&
+             sony_toc.lead_out_start_lba == single_toc.lead_out_start_lba)
+	 {
+            break;
+         }
+
+         /* Let's not get carried away... */
+         if (session > 40)
+         {
+            printk("cdu31a: too many sessions: %d\n", session);
+            break;
+         }
+         session++;
+      }
+      sony_toc.track_entries = totaltracks;
+      /* add one entry for the LAST track with track number CDROM_LEADOUT */ 
+      sony_toc.tracks[totaltracks].address = single_toc.address2;
+      sony_toc.tracks[totaltracks].control = single_toc.control2;
+      sony_toc.tracks[totaltracks].track = CDROM_LEADOUT;
+      sony_toc.tracks[totaltracks].track_start_msf[0] = 
+             sony_toc.lead_out_start_msf[0];
+      sony_toc.tracks[totaltracks].track_start_msf[1] = 
+             sony_toc.lead_out_start_msf[1];
+      sony_toc.tracks[totaltracks].track_start_msf[2] = 
+             sony_toc.lead_out_start_msf[2];
+
+      sony_toc_read = 1;
+#undef DEBUG
 #if DEBUG
    printk("Disk session %d, start track: %d, stop track: %d\n",
 	  session,
-	  sony_toc.start_track_lba,
-	  sony_toc.lead_out_start_lba);
+	  single_toc.start_track_lba,
+	  single_toc.lead_out_start_lba);
 #endif
    }
 #if DEBUG
@@ -1973,19 +2293,35 @@
 
 
 /*
+ * Uniform cdrom interface function
+ * return multisession offset and sector information
+ */
+static int scd_get_last_session(struct cdrom_device_info *cdi, 
+                                struct cdrom_multisession *ms_info)
+{
+   if (ms_info == NULL)
+      return 1;
+
+   if (!sony_toc_read)
+      sony_get_toc();
+
+   ms_info->addr_format = CDROM_LBA;
+   ms_info->addr.lba    = sony_toc.start_track_lba;
+   ms_info->xa_flag     = sony_toc.disk_type == SONY_XA_DISK_TYPE ||
+                          sony_toc.disk_type == 0x10 /* CDI */;
+
+   return 0;
+}
+
+/*
  * Search for a specific track in the table of contents.
  */
 static int
 find_track(int track)
 {
    int i;
-   int num_tracks;
-
 
-   num_tracks = (  bcd_to_int(sony_toc.last_track_num)
-                 - bcd_to_int(sony_toc.first_track_num)
-		 + 1);
-   for (i = 0; i < num_tracks; i++)
+   for (i = 0; i <= sony_toc.track_entries; i++)
    {
       if (sony_toc.tracks[i].track == track)
       {
@@ -1998,7 +2334,7 @@
 
 
 /*
- * Read the subcode and put it int last_sony_subcode for future use.
+ * Read the subcode and put it in last_sony_subcode for future use.
  */
 static int
 read_subcode(void)
@@ -2013,11 +2349,60 @@
                   &res_size);
    if ((res_size < 2) || ((last_sony_subcode.exec_status[0] & 0xf0) == 0x20))
    {
-      printk("Sony CDROM error 0x%2.2x (read_subcode)\n",
-             last_sony_subcode.exec_status[1]);
+      printk("Sony CDROM error %s (read_subcode)\n",
+             translate_error(last_sony_subcode.exec_status[1]));
       return -EIO;
    }
 
+   last_sony_subcode.track_num = bcd_to_int(last_sony_subcode.track_num);
+   last_sony_subcode.index_num = bcd_to_int(last_sony_subcode.index_num);
+   last_sony_subcode.abs_msf[0] = bcd_to_int(last_sony_subcode.abs_msf[0]);
+   last_sony_subcode.abs_msf[1] = bcd_to_int(last_sony_subcode.abs_msf[1]);
+   last_sony_subcode.abs_msf[2] = bcd_to_int(last_sony_subcode.abs_msf[2]);
+
+   last_sony_subcode.rel_msf[0] = bcd_to_int(last_sony_subcode.rel_msf[0]);
+   last_sony_subcode.rel_msf[1] = bcd_to_int(last_sony_subcode.rel_msf[1]);
+   last_sony_subcode.rel_msf[2] = bcd_to_int(last_sony_subcode.rel_msf[2]);
+   return 0;
+}
+
+/*
+ * Uniform cdrom interface function
+ * return the media catalog number found on some older audio cds
+ */
+static int
+scd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn)
+{
+   unsigned char resbuffer[2 + 14];
+   unsigned char *mcnp = mcn->medium_catalog_number;
+   unsigned char *resp = resbuffer + 3;
+   unsigned int res_size;
+
+   memset(mcn->medium_catalog_number, 0, 14);
+   do_sony_cd_cmd(SONY_REQ_UPC_EAN_CMD,
+                  NULL,
+                  0, 
+                  resbuffer,
+                  &res_size);
+   if ((res_size < 2) || ((resbuffer[0] & 0xf0) == 0x20))
+     ;
+   else {
+     /* packed bcd to single ASCII digits */
+     *mcnp++ = (*resp >> 4)     + '0'; 
+     *mcnp++ = (*resp++ & 0x0f) + '0'; 
+     *mcnp++ = (*resp >> 4)     + '0'; 
+     *mcnp++ = (*resp++ & 0x0f) + '0'; 
+     *mcnp++ = (*resp >> 4)     + '0'; 
+     *mcnp++ = (*resp++ & 0x0f) + '0'; 
+     *mcnp++ = (*resp >> 4)     + '0'; 
+     *mcnp++ = (*resp++ & 0x0f) + '0'; 
+     *mcnp++ = (*resp >> 4)     + '0'; 
+     *mcnp++ = (*resp++ & 0x0f) + '0'; 
+     *mcnp++ = (*resp >> 4)     + '0'; 
+     *mcnp++ = (*resp++ & 0x0f) + '0'; 
+     *mcnp++ = (*resp >> 4)     + '0'; 
+   }
+   *mcnp = '\0';
    return 0;
 }
 
@@ -2030,12 +2415,8 @@
  * (not BCD), so all the conversions are done.
  */
 static int
-sony_get_subchnl_info(long arg)
+sony_get_subchnl_info(struct cdrom_subchnl *schi)
 {
-   int err;
-   struct cdrom_subchnl schi;
-
-
    /* Get attention stuff */
    while (handle_sony_cd_attention())
       ;
@@ -2046,13 +2427,9 @@
       return -EIO;
    }
 
-   err = verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));
-   if (err) return err;
-
-   copy_from_user(&schi, (char *) arg, sizeof(schi));
-   
    switch (sony_audio_status)
    {
+   case CDROM_AUDIO_NO_STATUS:
    case CDROM_AUDIO_PLAY:
       if (read_subcode() < 0)
       {
@@ -2064,40 +2441,39 @@
    case CDROM_AUDIO_COMPLETED:
       break;
 
+#if 0
    case CDROM_AUDIO_NO_STATUS:
-      schi.cdsc_audiostatus = sony_audio_status;
-      copy_to_user((char *) arg, &schi, sizeof(schi));
+      schi->cdsc_audiostatus = sony_audio_status;
       return 0;
       break;
-
+#endif
    case CDROM_AUDIO_INVALID:
    case CDROM_AUDIO_ERROR:
    default:
       return -EIO;
    }
 
-   schi.cdsc_audiostatus = sony_audio_status;
-   schi.cdsc_adr = last_sony_subcode.address;
-   schi.cdsc_ctrl = last_sony_subcode.control;
-   schi.cdsc_trk = bcd_to_int(last_sony_subcode.track_num);
-   schi.cdsc_ind = bcd_to_int(last_sony_subcode.index_num);
-   if (schi.cdsc_format == CDROM_MSF)
-   {
-      schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode.abs_msf[0]);
-      schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode.abs_msf[1]);
-      schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode.abs_msf[2]);
-
-      schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode.rel_msf[0]);
-      schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode.rel_msf[1]);
-      schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode.rel_msf[2]);
+   schi->cdsc_audiostatus = sony_audio_status;
+   schi->cdsc_adr = last_sony_subcode.address;
+   schi->cdsc_ctrl = last_sony_subcode.control;
+   schi->cdsc_trk = last_sony_subcode.track_num;
+   schi->cdsc_ind = last_sony_subcode.index_num;
+   if (schi->cdsc_format == CDROM_MSF)
+   {
+      schi->cdsc_absaddr.msf.minute = last_sony_subcode.abs_msf[0];
+      schi->cdsc_absaddr.msf.second = last_sony_subcode.abs_msf[1];
+      schi->cdsc_absaddr.msf.frame = last_sony_subcode.abs_msf[2];
+
+      schi->cdsc_reladdr.msf.minute = last_sony_subcode.rel_msf[0];
+      schi->cdsc_reladdr.msf.second = last_sony_subcode.rel_msf[1];
+      schi->cdsc_reladdr.msf.frame = last_sony_subcode.rel_msf[2];
    }
-   else if (schi.cdsc_format == CDROM_LBA)
+   else if (schi->cdsc_format == CDROM_LBA)
    {
-      schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode.abs_msf);
-      schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode.rel_msf);
+      schi->cdsc_absaddr.lba = msf_to_log(last_sony_subcode.abs_msf);
+      schi->cdsc_reladdr.lba = msf_to_log(last_sony_subcode.rel_msf);
    }
    
-   copy_to_user((char *) arg, &schi, sizeof(schi));
    return 0;
 }
 
@@ -2172,7 +2548,7 @@
       /* If data block, then get 2340 bytes offset by 12. */
       if (sony_raw_data_mode)
       {
-         insb(sony_cd_read_reg, buffer + CD_XA_HEAD, CD_FRAMESIZE_XA);
+         insb(sony_cd_read_reg, buffer + CD_XA_HEAD, CD_FRAMESIZE_RAW1);
       }
       else
       {
@@ -2243,8 +2619,7 @@
 /* Perform a raw data read.  This will automatically detect the
    track type and read the proper data (audio or data). */
 static int
-read_audio(struct cdrom_read_audio *ra,
-           struct inode            *inode)
+read_audio(struct cdrom_read_audio *ra)
 {
    int retval;
    unsigned char params[2];
@@ -2274,7 +2649,7 @@
 
    if (!sony_spun_up)
    {
-      scd_open (inode, NULL);
+     scd_spinup();
    }
 
    /* Set the drive to do raw operations. */
@@ -2355,9 +2730,9 @@
                }
                else
                {
-                  printk("CDU31A: Error reading audio data on sector %d: 0x%x\n",
+                  printk("CDU31A: Error reading audio data on sector %d: %s\n",
                          ra->addr.lba + cframe,
-                         res_reg[1]);
+                         translate_error(res_reg[1]));
                   retval = -EIO;
                   goto exit_read_audio;
                }
@@ -2371,9 +2746,9 @@
          }
          else
          {
-            printk("CDU31A: Error reading audio data on sector %d: 0x%x\n",
+            printk("CDU31A: Error reading audio data on sector %d: %s\n",
                    ra->addr.lba + cframe,
-                   res_reg[1]);
+                   translate_error(res_reg[1]));
             retval = -EIO;
             goto exit_read_audio;
          }
@@ -2391,8 +2766,8 @@
    get_result(res_reg, &res_size);
    if ((res_reg[0] & 0xf0) == 0x20)
    {
-      printk("CDU31A: Error return from audio read: 0x%x\n",
-             res_reg[1]);
+      printk("CDU31A: Error return from audio read: %s\n",
+             translate_error(res_reg[1]));
       retval = -EIO;
       goto exit_read_audio;
    }
@@ -2438,19 +2813,38 @@
       do_sony_cd_cmd(cmd, params, num_params, result_buffer, result_size);
       if ((*result_size < 2) || ((result_buffer[0] & 0xf0) == 0x20))
       {
-         printk("Sony CDROM error 0x%2.2x (CDROM%s)\n", result_buffer[1], name);
+         printk("Sony CDROM error %s (CDROM%s)\n", translate_error(result_buffer[1]), name);
          return -EIO;
       }
       return 0;
 }
  
 /*
+ * Uniform cdrom interface function
+ * open the tray
+ */
+static int scd_tray_move(struct cdrom_device_info *cdi, int position)
+{
+  if (position == 1 /* open tray */)
+  {
+    unsigned char res_reg[12];
+    unsigned int res_size;
+
+    do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
+    do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+
+    sony_audio_status = CDROM_AUDIO_INVALID;
+    return do_sony_cd_cmd_chk("EJECT",SONY_EJECT_CMD, NULL, 0, res_reg, &res_size);
+  } else
+  return 0;
+}
+
+/*
  * The big ugly ioctl handler.
  */
-static int scd_ioctl(struct inode *inode,
-          struct file  *file,
+static int scd_audio_ioctl(struct cdrom_device_info *cdi,
           unsigned int  cmd,
-          unsigned long arg)
+          void * arg)
 {
    unsigned char res_reg[12];
    unsigned int res_size;
@@ -2458,16 +2852,10 @@
    int i;
 
 
-   if (!inode)
-   {
-      return -EINVAL;
-   }
-
    switch (cmd)
    {
    case CDROMSTART:     /* Spin up the drive */
       return do_sony_cd_cmd_chk("START",SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
-      return 0;
       break;
       
    case CDROMSTOP:      /* Spin down the drive */
@@ -2504,12 +2892,12 @@
       do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
       
       /* Start the drive at the saved position. */
-      params[1] = cur_pos_msf[0];
-      params[2] = cur_pos_msf[1];
-      params[3] = cur_pos_msf[2];
-      params[4] = final_pos_msf[0];
-      params[5] = final_pos_msf[1];
-      params[6] = final_pos_msf[2];
+      params[1] = int_to_bcd(cur_pos_msf[0]);
+      params[2] = int_to_bcd(cur_pos_msf[1]);
+      params[3] = int_to_bcd(cur_pos_msf[2]);
+      params[4] = int_to_bcd(final_pos_msf[0]);
+      params[5] = int_to_bcd(final_pos_msf[1]);
+      params[6] = int_to_bcd(final_pos_msf[2]);
       params[0] = 0x03;
       if(do_sony_cd_cmd_chk("RESUME",SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size)<0)
       	return -EIO;
@@ -2517,32 +2905,27 @@
       return 0;
 
    case CDROMPLAYMSF:   /* Play starting at the given MSF address. */
-      i=verify_area(VERIFY_READ, (char *) arg, 6);
-      if(i)
-      	return i;
       do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
-      copy_from_user(&(params[1]), (void *) arg, 6);
       
       /* The parameters are given in int, must be converted */
       for (i=1; i<7; i++)
       {
-         params[i] = int_to_bcd(params[i]);
+         params[i] = int_to_bcd(((unsigned char *)arg)[i-1]);
       }
       params[0] = 0x03;
       if(do_sony_cd_cmd_chk("PLAYMSF",SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size)<0)
       	return -EIO;
       
       /* Save the final position for pauses and resumes */
-      final_pos_msf[0] = params[4];
-      final_pos_msf[1] = params[5];
-      final_pos_msf[2] = params[6];
+      final_pos_msf[0] = bcd_to_int(params[4]);
+      final_pos_msf[1] = bcd_to_int(params[5]);
+      final_pos_msf[2] = bcd_to_int(params[6]);
       sony_audio_status = CDROM_AUDIO_PLAY;
       return 0;
 
    case CDROMREADTOCHDR:        /* Read the table of contents header */
       {
          struct cdrom_tochdr *hdr;
-         struct cdrom_tochdr loc_hdr;
          
          sony_get_toc();
          if (!sony_toc_read)
@@ -2551,19 +2934,14 @@
          }
          
          hdr = (struct cdrom_tochdr *) arg;
-         i=verify_area(VERIFY_WRITE, hdr, sizeof(*hdr));
-         if(i<0)
-         	return i;
-         loc_hdr.cdth_trk0 = bcd_to_int(sony_toc.first_track_num);
-         loc_hdr.cdth_trk1 = bcd_to_int(sony_toc.last_track_num);
-         copy_to_user(hdr, &loc_hdr, sizeof(*hdr));
+         hdr->cdth_trk0 = sony_toc.first_track_num;
+         hdr->cdth_trk1 = sony_toc.last_track_num;
       }
       return 0;
 
    case CDROMREADTOCENTRY:      /* Read a given table of contents entry */
       {
          struct cdrom_tocentry *entry;
-         struct cdrom_tocentry loc_entry;
          int track_idx;
          unsigned char *msf_val = NULL;
          
@@ -2574,54 +2952,35 @@
          }
          
          entry = (struct cdrom_tocentry *) arg;
-         i=verify_area(VERIFY_READ, entry, sizeof(*entry));
-         if(i<0)
-         	return i;
-         i=verify_area(VERIFY_WRITE, entry, sizeof(*entry));
-         if(i<0)
-         	return i;
          
-         copy_from_user(&loc_entry, entry, sizeof(loc_entry));
-         
-         /* Lead out is handled separately since it is special. */
-         if (loc_entry.cdte_track == CDROM_LEADOUT)
+         track_idx = find_track(entry->cdte_track);
+         if (track_idx < 0)
          {
-            loc_entry.cdte_adr = sony_toc.address2;
-            loc_entry.cdte_ctrl = sony_toc.control2;
-            msf_val = sony_toc.lead_out_start_msf;
+            return -EINVAL;
          }
-         else
-         {
-            track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
-            if (track_idx < 0)
-            {
-               return -EINVAL;
-            }
             
-            loc_entry.cdte_adr = sony_toc.tracks[track_idx].address;
-            loc_entry.cdte_ctrl = sony_toc.tracks[track_idx].control;
-            msf_val = sony_toc.tracks[track_idx].track_start_msf;
-         }
+         entry->cdte_adr = sony_toc.tracks[track_idx].address;
+         entry->cdte_ctrl = sony_toc.tracks[track_idx].control;
+         msf_val = sony_toc.tracks[track_idx].track_start_msf;
          
          /* Logical buffer address or MSF format requested? */
-         if (loc_entry.cdte_format == CDROM_LBA)
+         if (entry->cdte_format == CDROM_LBA)
          {
-            loc_entry.cdte_addr.lba = msf_to_log(msf_val);
+            entry->cdte_addr.lba = msf_to_log(msf_val);
          }
-         else if (loc_entry.cdte_format == CDROM_MSF)
+         else if (entry->cdte_format == CDROM_MSF)
          {
-            loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val);
-            loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val+1));
-            loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val+2));
+            entry->cdte_addr.msf.minute = *msf_val;
+            entry->cdte_addr.msf.second = *(msf_val+1);
+            entry->cdte_addr.msf.frame = *(msf_val+2);
          }
-         copy_to_user(entry, &loc_entry, sizeof(*entry));
       }
       return 0;
       break;
 
    case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
       {
-         struct cdrom_ti ti;
+         struct cdrom_ti *ti = (struct cdrom_ti *) arg;
          int track_idx;
          
          sony_get_toc();
@@ -2630,46 +2989,41 @@
             return -EIO;
          }
          
-         i=verify_area(VERIFY_READ, (char *) arg, sizeof(ti));
-         if(i<0)
-         	return i;
-         
-         copy_from_user(&ti, (char *) arg, sizeof(ti));
-         if (   (ti.cdti_trk0 < bcd_to_int(sony_toc.first_track_num))
-             || (ti.cdti_trk0 > bcd_to_int(sony_toc.last_track_num))
-             || (ti.cdti_trk1 < ti.cdti_trk0))
+         if (   (ti->cdti_trk0 < sony_toc.first_track_num)
+             || (ti->cdti_trk0 > sony_toc.last_track_num)
+             || (ti->cdti_trk1 < ti->cdti_trk0))
          {
             return -EINVAL;
          }
          
-         track_idx = find_track(int_to_bcd(ti.cdti_trk0));
+         track_idx = find_track(ti->cdti_trk0);
          if (track_idx < 0)
          {
             return -EINVAL;
          }
-         params[1] = sony_toc.tracks[track_idx].track_start_msf[0];
-         params[2] = sony_toc.tracks[track_idx].track_start_msf[1];
-         params[3] = sony_toc.tracks[track_idx].track_start_msf[2];
+         params[1] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[0]);
+         params[2] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[1]);
+         params[3] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[2]);
          
          /*
           * If we want to stop after the last track, use the lead-out
           * MSF to do that.
           */
-         if (ti.cdti_trk1 >= bcd_to_int(sony_toc.last_track_num))
+         if (ti->cdti_trk1 >= sony_toc.last_track_num)
          {
-            log_to_msf(msf_to_log(sony_toc.lead_out_start_msf)-1,
-                       &(params[4]));
+            track_idx = find_track(CDROM_LEADOUT);
          }
          else
          {
-            track_idx = find_track(int_to_bcd(ti.cdti_trk1+1));
-            if (track_idx < 0)
-            {
-               return -EINVAL;
-            }
-            log_to_msf(msf_to_log(sony_toc.tracks[track_idx].track_start_msf)-1,
-                       &(params[4]));
+            track_idx = find_track(ti->cdti_trk1+1);
+         }
+         if (track_idx < 0)
+         {
+            return -EINVAL;
          }
+         params[4] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[0]);
+         params[5] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[1]);
+         params[6] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[2]);
          params[0] = 0x03;
          
          do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
@@ -2680,43 +3034,44 @@
          {
             printk("Params: %x %x %x %x %x %x %x\n", params[0], params[1],
                    params[2], params[3], params[4], params[5], params[6]);
-            printk("Sony CDROM error 0x%2.2x (CDROMPLAYTRKIND\n", res_reg[1]);
+            printk("Sony CDROM error %s (CDROMPLAYTRKIND)\n", translate_error(res_reg[1]));
             return -EIO;
          }
          
          /* Save the final position for pauses and resumes */
-         final_pos_msf[0] = params[4];
-         final_pos_msf[1] = params[5];
-         final_pos_msf[2] = params[6];
+         final_pos_msf[0] = bcd_to_int(params[4]);
+         final_pos_msf[1] = bcd_to_int(params[5]);
+         final_pos_msf[2] = bcd_to_int(params[6]);
          sony_audio_status = CDROM_AUDIO_PLAY;
          return 0;
       }
      
-   case CDROMSUBCHNL:   /* Get subchannel info */
-      return sony_get_subchnl_info(arg);
-
    case CDROMVOLCTRL:   /* Volume control.  What volume does this change, anyway? */
       {
-         struct cdrom_volctrl volctrl;
-         
-         i=verify_area(VERIFY_READ, (char *) arg, sizeof(volctrl));
-         if(i<0)
-         	return i;
+         struct cdrom_volctrl *volctrl = (struct cdrom_volctrl *) arg;
          
-         copy_from_user(&volctrl, (char *) arg, sizeof(volctrl));
          params[0] = SONY_SD_AUDIO_VOLUME;
-         params[1] = volctrl.channel0;
-         params[2] = volctrl.channel1;
+         params[1] = volctrl->channel0;
+         params[2] = volctrl->channel1;
          return do_sony_cd_cmd_chk("VOLCTRL",SONY_SET_DRIVE_PARAM_CMD, params, 3, res_reg, &res_size);
       }
-   case CDROMEJECT:     /* Eject the drive */
-      do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
-      do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+   case CDROMSUBCHNL:   /* Get subchannel info */
+      return sony_get_subchnl_info((struct cdrom_subchnl *)arg);
 
-      sony_audio_status = CDROM_AUDIO_INVALID;
-      return do_sony_cd_cmd_chk("EJECT",SONY_EJECT_CMD, NULL, 0, res_reg, &res_size);
-     
-    case CDROMREADAUDIO:      /* Read 2352 byte audio tracks and 2340 byte
+   default:
+      return -EINVAL;
+   }
+}
+
+static int scd_dev_ioctl(struct cdrom_device_info *cdi,
+          unsigned int  cmd,
+          unsigned long arg)
+{
+   int i;
+
+   switch (cmd)
+   {
+   case CDROMREADAUDIO:      /* Read 2352 byte audio tracks and 2340 byte
 				 raw data tracks. */
       {
          struct cdrom_read_audio ra;
@@ -2728,14 +3083,15 @@
             return -EIO;
          }
          
-         i=verify_area(VERIFY_READ, (char *) arg, sizeof(ra));
-         if(i<0)
-         	return i;
-         copy_from_user(&ra, (char *) arg, sizeof(ra));
+         if (ra.nframes == 0)
+         {
+            return 0;
+         }
 
          i=verify_area(VERIFY_WRITE, ra.buf, CD_FRAMESIZE_RAW * ra.nframes);
          if(i<0)
          	return i;
+         copy_from_user(&ra, (char *) arg, sizeof(ra));
 
          if (ra.addr_format == CDROM_LBA)
          {
@@ -2773,92 +3129,93 @@
             return -EINVAL;
          }
 
-         return(read_audio(&ra, inode));
+         return(read_audio(&ra));
       }
       return 0;
       break;
 
-   case CDROMEJECT_SW:
-      is_auto_eject = arg;
-      set_drive_params();
-      return 0;
-      break;
-
    default:
       return -EINVAL;
    }
 }
 
-
-/*
- * Open the drive for operations.  Spin the drive up and read the table of
- * contents if these have not already been done.
- */
-static int
-scd_open(struct inode *inode,
-         struct file *filp)
+static int scd_spinup(void)
 {
    unsigned char res_reg[12];
    unsigned int res_size;
    int num_spin_ups;
-   unsigned char params[2];
 
+   num_spin_ups = 0;
 
-   if ((filp) && filp->f_mode & 2)
-      return -EROFS;
+respinup_on_open:
+   do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
 
-   if (!sony_spun_up)
+   /* The drive sometimes returns error 0.  I don't know why, but ignore
+      it.  It seems to mean the drive has already done the operation. */
+   if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
    {
-      num_spin_ups = 0;
-
-respinup_on_open:
-      do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+      printk("Sony CDROM %s error (scd_open, spin up)\n", translate_error(res_reg[1]));
+      return 1;
+   }
+      
+   do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
 
-      /* The drive sometimes returns error 0.  I don't know why, but ignore
-         it.  It seems to mean the drive has already done the operation. */
-      if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
+   /* The drive sometimes returns error 0.  I don't know why, but ignore
+      it.  It seems to mean the drive has already done the operation. */
+   if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
+   {
+      /* If the drive is already playing, it's ok.  */
+      if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0))
       {
-         printk("Sony CDROM error 0x%2.2x (scd_open, spin up)\n", res_reg[1]);
-         return -EIO;
+         return 0;
       }
-      
-      do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
 
-      /* The drive sometimes returns error 0.  I don't know why, but ignore
-         it.  It seems to mean the drive has already done the operation. */
-      if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
+      /* If the drive says it is not spun up (even though we just did it!)
+         then retry the operation at least a few times. */
+      if (   (res_reg[1] == SONY_NOT_SPIN_ERR)
+          && (num_spin_ups < MAX_CDU31A_RETRIES))
       {
-         /* If the drive is already playing, it's ok.  */
-         if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0))
-         {
-            goto drive_spinning;
-         }
+         num_spin_ups++;
+         goto respinup_on_open;
+      }
 
-         /* If the drive says it is not spun up (even though we just did it!)
-            then retry the operation at least a few times. */
-         if (   (res_reg[1] == SONY_NOT_SPIN_ERR)
-             && (num_spin_ups < MAX_CDU31A_RETRIES))
-         {
-            num_spin_ups++;
-            goto respinup_on_open;
-         }
+      printk("Sony CDROM error %s (scd_open, read toc)\n", translate_error(res_reg[1]));
+      do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+      return 1;  
+   }
+   return 0;
+}
 
-         printk("Sony CDROM error 0x%2.2x (scd_open, read toc)\n", res_reg[1]);
-         do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
-         
+/*
+ * Open the drive for operations.  Spin the drive up and read the table of
+ * contents if these have not already been done.
+ */
+static int
+scd_open(struct cdrom_device_info *cdi, int openmode)
+{
+   unsigned char res_reg[12];
+   unsigned int res_size;
+   unsigned char params[2];
+
+   MOD_INC_USE_COUNT;
+   if (sony_usage == 0)
+   {
+      if (scd_spinup() != 0) {
+         MOD_DEC_USE_COUNT;
          return -EIO;
       }
-
       sony_get_toc();
       if (!sony_toc_read)
       {
          do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+         MOD_DEC_USE_COUNT;
          return -EIO;
       }
 
       /* For XA on the CDU31A only, we have to do special reads.
          The CDU33A handles XA automagically. */
-      if (   (sony_toc.disk_type == SONY_XA_DISK_TYPE)
+      /* if (   (sony_toc.disk_type == SONY_XA_DISK_TYPE) */
+      if (   (sony_toc.disk_type != 0x00)
           && (!is_double_speed))
       {
          params[0] = SONY_SD_DECODE_PARAM;
@@ -2873,6 +3230,7 @@
             printk("CDU31A: Unable to set XA params: 0x%2.2x\n", res_reg[1]);
          }
          sony_xa_mode = 1;
+printk("sony_xa_mode is set\n");
       }
       /* A non-XA disk.  Set the parms back if necessary. */
       else if (sony_xa_mode)
@@ -2889,25 +3247,13 @@
             printk("CDU31A: Unable to reset XA params: 0x%2.2x\n", res_reg[1]);
          }
          sony_xa_mode = 0;
+printk("sony_xa_mode is reset\n");
       }
 
       sony_spun_up = 1;
    }
 
-drive_spinning:
-
-   /* If filp is not NULL (standard open), try a disk change. */
-   if (filp)
-   {
-      check_disk_change(inode->i_rdev);
-   }
-
    sony_usage++;
-   MOD_INC_USE_COUNT;
-
-   /* If all is OK (until now...), then lock the door */
-   is_auto_eject = 0;
-   set_drive_params();
 
    return 0;
 }
@@ -2917,51 +3263,54 @@
  * Close the drive.  Spin it down if no task is using it.  The spin
  * down will fail if playing audio, so audio play is OK.
  */
-static int
-scd_release(struct inode *inode,
-         struct file *filp)
+static void
+scd_release(struct cdrom_device_info *cdi)
 {
-   unsigned char res_reg[12];
-   unsigned int  res_size;
-
-
-   if (sony_usage > 0)
+   if (sony_usage == 1)
    {
-      sony_usage--;
-      MOD_DEC_USE_COUNT;
-   }
-   if (sony_usage == 0)
-   {
-      sync_dev(inode->i_rdev);
-
-      /* Unlock the door, only if nobody is using the drive */
-      is_auto_eject = 1;
-      set_drive_params();
+      unsigned char res_reg[12];
+      unsigned int  res_size;
 
       do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
 
       sony_spun_up = 0;
    }
-   return 0;
+   sony_usage--;
+   MOD_DEC_USE_COUNT;
 }
 
-
-static struct file_operations scd_fops = {
-   NULL,                   /* lseek - default */
-   block_read,             /* read - general block-dev read */
-   block_write,            /* write - general block-dev write */
-   NULL,                   /* readdir - bad */
-   NULL,                   /* poll */
-   scd_ioctl,              /* ioctl */
-   NULL,                   /* mmap */
-   scd_open,               /* open */
-   scd_release,            /* release */
-   NULL,                   /* fsync */
-   NULL,                   /* fasync */
-   scd_disk_change,        /* media_change */
-   NULL                    /* revalidate */
+static struct cdrom_device_ops scd_dops = {
+  scd_open,                   /* open */
+  scd_release,                /* release */
+  scd_drive_status,           /* drive status */
+  scd_media_changed,          /* media changed */
+  scd_tray_move,              /* tray move */
+  scd_lock_door,              /* lock door */
+  scd_select_speed,           /* select speed */
+  NULL,                       /* select disc */
+  scd_get_last_session,       /* get last session */
+  scd_get_mcn,                /* get universal product code */
+  scd_reset,                  /* hard reset */
+  scd_audio_ioctl,            /* audio ioctl */
+  scd_dev_ioctl,              /* device-specific ioctl */
+  CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_MULTI_SESSION | 
+  CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO |
+  CDC_RESET | CDC_IOCTLS | CDC_DRIVE_STATUS, /* capability */
+  1,                            /* number of minor devices */
 };
 
+static struct cdrom_device_info scd_info = {
+  &scd_dops,                  /* device operations */
+  NULL,                       /* link */
+  NULL,                       /* handle */
+  MKDEV(MAJOR_NR,0),          /* dev */
+  0,                          /* mask */
+  2,                          /* maximum speed */
+  1,                          /* number of discs */
+  0,                          /* options, not owned */
+  0,                          /* mc_flags, not owned */
+  0                           /* use count, not owned */
+};
 
 /* The different types of disc loading mechanisms supported */
 static const char *load_mech[] __initdata = { "caddy", "tray", "pop-up", "unknown" };
@@ -3069,6 +3418,8 @@
 {
    struct s_sony_drive_config drive_config;
    unsigned int res_size;
+   char msg[255];
+   char buf[40];
    int i;
    int drive_found;
    int tmp_irq;
@@ -3136,12 +3487,14 @@
 
    if (drive_found)
    {
+      int deficiency=0;
+
       request_region(cdu31a_port, 4,"cdu31a");
       
-      if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops))
+      if (register_blkdev(MAJOR_NR,"cdu31a",&cdrom_fops))
       {
 	 printk("Unable to get major %d for CDU-31a\n", MAJOR_NR);
-	 return -EIO;
+         goto errout2;
       }
 
       if (SONY_HWC_DOUBLE_SPEED(drive_config))
@@ -3152,7 +3505,7 @@
       tmp_irq = cdu31a_irq; /* Need IRQ 0 because we can't sleep here. */
       cdu31a_irq = 0;
 
-      set_drive_params();
+      set_drive_params(sony_speed);
 
       cdu31a_irq = tmp_irq;
       
@@ -3165,41 +3518,48 @@
 	 }
       }
       
-      printk(KERN_INFO "Sony I/F CDROM : %8.8s %16.16s %8.8s\n",
+      sprintf(msg, "Sony I/F CDROM : %8.8s %16.16s %8.8s\n",
 	     drive_config.vendor_id,
 	     drive_config.product_id,
 	     drive_config.product_rev_level);
-      printk(KERN_INFO "  Capabilities: %s",
+      sprintf(buf, "  Capabilities: %s",
 	     load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]);
+      strcat(msg, buf);
       if (SONY_HWC_AUDIO_PLAYBACK(drive_config))
       {
-	 printk(", audio");
-      }
+         strcat(msg, ", audio");
+      } else
+	 deficiency |= CDC_PLAY_AUDIO;
       if (SONY_HWC_EJECT(drive_config))
       {
-	 printk(", eject");
-      }
+         strcat(msg, ", eject");
+      } else
+	 deficiency |= CDC_OPEN_TRAY;
       if (SONY_HWC_LED_SUPPORT(drive_config))
       {
-	 printk(", LED");
+         strcat(msg, ", LED");
       }
       if (SONY_HWC_ELECTRIC_VOLUME(drive_config))
       {
-	 printk(", elec. Vol");
+         strcat(msg, ", elec. Vol");
       }
       if (SONY_HWC_ELECTRIC_VOLUME_CTL(drive_config))
       {
-	 printk(", sep. Vol");
+         strcat(msg, ", sep. Vol");
       }
       if (is_double_speed)
       {
-	 printk(", double speed");
-      }
+         strcat(msg, ", double speed");
+      } else
+	 deficiency |= CDC_SELECT_SPEED;
       if (cdu31a_irq > 0)
       {
-	 printk(", irq %d", cdu31a_irq);
+	 sprintf(buf, ", irq %d", cdu31a_irq);
+         strcat(msg, buf);
       }
-      printk("\n");
+      strcat(msg, "\n");
+
+      is_a_cdu31a = strcmp("CD-ROM CDU31A", drive_config.product_id) == 0;
 
       blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
       read_ahead[MAJOR_NR] = CDU31A_READAHEAD;
@@ -3209,6 +3569,13 @@
       
       init_timer(&cdu31a_abort_timer);
       cdu31a_abort_timer.function = handle_abort_timeout;
+
+      scd_info.mask = deficiency;
+
+      if (register_cdrom(&scd_info))
+      {
+         goto errout0;
+      }
    }
 
 
@@ -3220,8 +3587,18 @@
    }
    else
    {
-      return -EIO;
+      goto errout3;
    }
+errout0:
+   printk("Unable to register CDU-31a with Uniform cdrom driver\n");
+   if (unregister_blkdev(MAJOR_NR, "cdu31a"))    
+   {
+      printk("Can't unregister block device for cdu31a\n");
+   }
+errout2:
+   release_region(cdu31a_port,4);
+errout3:
+   return -EIO;
 }
 
 #ifdef MODULE
@@ -3235,6 +3612,11 @@
 void
 cleanup_module(void)
 {
+   if (unregister_cdrom(&scd_info))    
+   {
+      printk("Can't unregister cdu31a from Uniform cdrom driver\n");
+      return;
+   }
    if ((unregister_blkdev(MAJOR_NR, "cdu31a") == -EINVAL))    
    {
       printk("Can't unregister cdu31a\n");
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/cdu31a.h linux/drivers/cdrom/cdu31a.h
--- v2.1.69/linux/drivers/cdrom/cdu31a.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/cdrom/cdu31a.h	Tue Dec  2 11:41:44 1997
@@ -0,0 +1,411 @@
+/*
+ * Definitions for a Sony interface CDROM drive.
+ *
+ * Corey Minyard (minyard@wf-rch.cirr.com)
+ *
+ *  Copyright (C) 1993  Corey Minyard
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * General defines.
+ */
+#define SONY_XA_DISK_TYPE 0x20
+
+/*
+ * Offsets (from the base address) and bits for the various write registers
+ * of the drive.
+ */
+#define SONY_CMD_REG_OFFSET     0
+#define SONY_PARAM_REG_OFFSET   1
+#define SONY_WRITE_REG_OFFSET   2
+#define SONY_CONTROL_REG_OFFSET 3
+#       define SONY_ATTN_CLR_BIT        0x01
+#       define SONY_RES_RDY_CLR_BIT     0x02
+#       define SONY_DATA_RDY_CLR_BIT    0x04
+#       define SONY_ATTN_INT_EN_BIT     0x08
+#       define SONY_RES_RDY_INT_EN_BIT  0x10
+#       define SONY_DATA_RDY_INT_EN_BIT 0x20
+#       define SONY_PARAM_CLR_BIT       0x40
+#       define SONY_DRIVE_RESET_BIT     0x80
+
+/*
+ * Offsets (from the base address) and bits for the various read registers
+ * of the drive.
+ */
+#define SONY_STATUS_REG_OFFSET  0
+#       define SONY_ATTN_BIT            0x01
+#       define SONY_RES_RDY_BIT         0x02
+#       define SONY_DATA_RDY_BIT        0x04
+#       define SONY_ATTN_INT_ST_BIT     0x08
+#       define SONY_RES_RDY_INT_ST_BIT  0x10
+#       define SONY_DATA_RDY_INT_ST_BIT 0x20
+#       define SONY_DATA_REQUEST_BIT    0x40
+#       define SONY_BUSY_BIT            0x80
+#define SONY_RESULT_REG_OFFSET  1
+#define SONY_READ_REG_OFFSET    2
+#define SONY_FIFOST_REG_OFFSET  3
+#       define SONY_PARAM_WRITE_RDY_BIT 0x01
+#       define SONY_PARAM_REG_EMPTY_BIT 0x02
+#       define SONY_RES_REG_NOT_EMP_BIT 0x04
+#       define SONY_RES_REG_FULL_BIT    0x08
+
+#define LOG_START_OFFSET        150     /* Offset of first logical sector */
+
+#define SONY_DETECT_TIMEOUT	(8*HZ/10) /* Maximum amount of time
+                                           that drive detection code
+                                           will wait for response
+                                           from drive (in 1/100th's
+                                           of seconds). */
+ 
+#define SONY_JIFFIES_TIMEOUT    1000	/* Maximum number of times the
+                                           drive will wait/try for an
+                                           operation */
+#define SONY_RESET_TIMEOUT      100	/* Maximum number of times the
+                                           drive will wait/try a reset
+                                           operation */
+#define SONY_READY_RETRIES      20000   /* How many times to retry a
+                                           spin waiting for a register
+                                           to come ready */
+
+#define MAX_CDU31A_RETRIES      3       /* How many times to retry an
+                                           operation */
+
+/* Commands to request or set drive control parameters and disc information */
+#define SONY_REQ_DRIVE_CONFIG_CMD       0x00    /* Returns s_sony_drive_config */
+#define SONY_REQ_DRIVE_MODE_CMD         0x01
+#define SONY_REQ_DRIVE_PARAM_CMD        0x02
+#define SONY_REQ_MECH_STATUS_CMD        0x03
+#define SONY_REQ_AUDIO_STATUS_CMD       0x04
+#define SONY_SET_DRIVE_PARAM_CMD        0x10
+#define SONY_REQ_TOC_DATA_CMD           0x20    /* Returns s_sony_toc */
+#define SONY_REQ_SUBCODE_ADDRESS_CMD    0x21    /* Returns s_sony_subcode */
+#define SONY_REQ_UPC_EAN_CMD            0x22
+#define SONY_REQ_ISRC_CMD               0x23
+#define SONY_REQ_TOC_DATA_SPEC_CMD      0x24    /* Returns s_sony_session_toc */
+
+/* Commands to request information from the drive */
+#define SONY_READ_TOC_CMD               0x30    /* let the drive firmware grab the TOC */
+#define SONY_SEEK_CMD                   0x31
+#define SONY_READ_CMD                   0x32
+#define SONY_READ_BLKERR_STAT_CMD       0x34
+#define SONY_ABORT_CMD                  0x35
+#define SONY_READ_TOC_SPEC_CMD          0x36
+
+/* Commands to control audio */
+#define SONY_AUDIO_PLAYBACK_CMD         0x40
+#define SONY_AUDIO_STOP_CMD             0x41
+#define SONY_AUDIO_SCAN_CMD             0x42
+
+/* Miscellaneous control commands */
+#define SONY_EJECT_CMD                  0x50
+#define SONY_SPIN_UP_CMD                0x51
+#define SONY_SPIN_DOWN_CMD              0x52
+
+/* Diagnostic commands */
+#define SONY_WRITE_BUFFER_CMD           0x60
+#define SONY_READ_BUFFER_CMD            0x61
+#define SONY_DIAGNOSTICS_CMD            0x62
+
+
+/*
+ * The following are command parameters for the set drive parameter command
+ */
+#define SONY_SD_DECODE_PARAM            0x00
+#define SONY_SD_INTERFACE_PARAM         0x01
+#define SONY_SD_BUFFERING_PARAM         0x02
+#define SONY_SD_AUDIO_PARAM             0x03
+#define SONY_SD_AUDIO_VOLUME            0x04
+#define SONY_SD_MECH_CONTROL            0x05
+#define SONY_SD_AUTO_SPIN_DOWN_TIME     0x06
+
+/*
+ * The following are parameter bits for the mechanical control command
+ */
+#define SONY_AUTO_SPIN_UP_BIT           0x01
+#define SONY_AUTO_EJECT_BIT             0x02
+#define SONY_DOUBLE_SPEED_BIT           0x04
+
+/*
+ * The following extract information from the drive configuration about
+ * the drive itself.
+ */
+#define SONY_HWC_GET_LOAD_MECH(c)       (c.hw_config[0] & 0x03)
+#define SONY_HWC_EJECT(c)               (c.hw_config[0] & 0x04)
+#define SONY_HWC_LED_SUPPORT(c)         (c.hw_config[0] & 0x08)
+#define SONY_HWC_DOUBLE_SPEED(c)        (c.hw_config[0] & 0x10)
+#define SONY_HWC_GET_BUF_MEM_SIZE(c)    ((c.hw_config[0] & 0xc0) >> 6)
+#define SONY_HWC_AUDIO_PLAYBACK(c)      (c.hw_config[1] & 0x01)
+#define SONY_HWC_ELECTRIC_VOLUME(c)     (c.hw_config[1] & 0x02)
+#define SONY_HWC_ELECTRIC_VOLUME_CTL(c) (c.hw_config[1] & 0x04)
+
+#define SONY_HWC_CADDY_LOAD_MECH        0x00
+#define SONY_HWC_TRAY_LOAD_MECH         0x01
+#define SONY_HWC_POPUP_LOAD_MECH        0x02
+#define SONY_HWC_UNKWN_LOAD_MECH        0x03
+
+#define SONY_HWC_8KB_BUFFER             0x00
+#define SONY_HWC_32KB_BUFFER            0x01
+#define SONY_HWC_64KB_BUFFER            0x02
+#define SONY_HWC_UNKWN_BUFFER           0x03
+
+/*
+ * This is the complete status returned from the drive configuration request
+ * command.
+ */
+struct s_sony_drive_config
+{
+   unsigned char exec_status[2];
+   char vendor_id[8];
+   char product_id[16];
+   char product_rev_level[8];
+   unsigned char hw_config[2];
+};
+
+/* The following is returned from the request subcode address command */
+struct s_sony_subcode
+{
+   unsigned char exec_status[2];
+   unsigned char address        :4;
+   unsigned char control        :4;
+   unsigned char track_num;
+   unsigned char index_num;
+   unsigned char rel_msf[3];
+   unsigned char reserved1;
+   unsigned char abs_msf[3];
+};
+
+#define MAX_TRACKS 100	/* The maximum tracks a disk may have. */
+/*
+ * The following is returned from the request TOC (Table Of Contents) command.
+ * (last_track_num-first_track_num+1) values are valid in tracks.
+ */
+struct s_sony_toc
+{
+   unsigned char exec_status[2];
+   unsigned char address0       :4;
+   unsigned char control0       :4;
+   unsigned char point0;
+   unsigned char first_track_num;
+   unsigned char disk_type;
+   unsigned char dummy0;
+   unsigned char address1       :4;
+   unsigned char control1       :4;
+   unsigned char point1;
+   unsigned char last_track_num;
+   unsigned char dummy1;
+   unsigned char dummy2;
+   unsigned char address2       :4;
+   unsigned char control2       :4;
+   unsigned char point2;
+   unsigned char lead_out_start_msf[3];
+   struct
+   {
+      unsigned char address     :4;
+      unsigned char control     :4;
+      unsigned char track;
+      unsigned char track_start_msf[3];
+   } tracks[MAX_TRACKS];
+
+   unsigned int lead_out_start_lba;
+};
+
+struct s_sony_session_toc
+{
+   unsigned char exec_status[2];
+   unsigned char session_number;
+   unsigned char address0       :4;
+   unsigned char control0       :4;
+   unsigned char point0;
+   unsigned char first_track_num;
+   unsigned char disk_type;
+   unsigned char dummy0;
+   unsigned char address1       :4;
+   unsigned char control1       :4;
+   unsigned char point1;
+   unsigned char last_track_num;
+   unsigned char dummy1;
+   unsigned char dummy2;
+   unsigned char address2       :4;
+   unsigned char control2       :4;
+   unsigned char point2;
+   unsigned char lead_out_start_msf[3];
+   unsigned char addressb0      :4;
+   unsigned char controlb0      :4;
+   unsigned char pointb0;
+   unsigned char next_poss_prog_area_msf[3];
+   unsigned char num_mode_5_pointers;
+   unsigned char max_start_outer_leadout_msf[3];
+   unsigned char addressb1      :4;
+   unsigned char controlb1      :4;
+   unsigned char pointb1;
+   unsigned char dummyb0_1[4];
+   unsigned char num_skip_interval_pointers;
+   unsigned char num_skip_track_assignments;
+   unsigned char dummyb0_2;
+   unsigned char addressb2      :4;
+   unsigned char controlb2      :4;
+   unsigned char pointb2;
+   unsigned char tracksb2[7];
+   unsigned char addressb3      :4;
+   unsigned char controlb3      :4;
+   unsigned char pointb3;
+   unsigned char tracksb3[7];
+   unsigned char addressb4      :4;
+   unsigned char controlb4      :4;
+   unsigned char pointb4;
+   unsigned char tracksb4[7];
+   unsigned char addressc0      :4;
+   unsigned char controlc0      :4;
+   unsigned char pointc0;
+   unsigned char dummyc0[7];
+   struct
+   {
+      unsigned char address     :4;
+      unsigned char control     :4;
+      unsigned char track;
+      unsigned char track_start_msf[3];
+   } tracks[MAX_TRACKS];
+
+   unsigned int start_track_lba;
+   unsigned int lead_out_start_lba;
+   unsigned int mint;
+   unsigned int maxt;
+};
+
+struct s_all_sessions_toc
+{
+   unsigned char sessions;
+   unsigned int track_entries;
+   unsigned char first_track_num;
+   unsigned char last_track_num;
+   unsigned char disk_type;
+   unsigned char lead_out_start_msf[3];
+   struct
+   {
+      unsigned char address     :4;
+      unsigned char control     :4;
+      unsigned char track;
+      unsigned char track_start_msf[3];
+   } tracks[MAX_TRACKS];
+
+   unsigned int start_track_lba;
+   unsigned int lead_out_start_lba;
+};
+
+
+/*
+ * The following are errors returned from the drive.
+ */
+
+/* Command error group */
+#define SONY_ILL_CMD_ERR                0x10
+#define SONY_ILL_PARAM_ERR              0x11
+
+/* Mechanism group */
+#define SONY_NOT_LOAD_ERR               0x20
+#define SONY_NO_DISK_ERR                0x21
+#define SONY_NOT_SPIN_ERR               0x22
+#define SONY_SPIN_ERR                   0x23
+#define SONY_SPINDLE_SERVO_ERR          0x25
+#define SONY_FOCUS_SERVO_ERR            0x26
+#define SONY_EJECT_MECH_ERR             0x29
+#define SONY_AUDIO_PLAYING_ERR          0x2a
+#define SONY_EMERGENCY_EJECT_ERR        0x2c
+
+/* Seek error group */
+#define SONY_FOCUS_ERR                  0x30
+#define SONY_FRAME_SYNC_ERR             0x31
+#define SONY_SUBCODE_ADDR_ERR           0x32
+#define SONY_BLOCK_SYNC_ERR             0x33
+#define SONY_HEADER_ADDR_ERR            0x34
+
+/* Read error group */
+#define SONY_ILL_TRACK_R_ERR            0x40
+#define SONY_MODE_0_R_ERR               0x41
+#define SONY_ILL_MODE_R_ERR             0x42
+#define SONY_ILL_BLOCK_SIZE_R_ERR       0x43
+#define SONY_MODE_R_ERR                 0x44
+#define SONY_FORM_R_ERR                 0x45
+#define SONY_LEAD_OUT_R_ERR             0x46
+#define SONY_BUFFER_OVERRUN_R_ERR       0x47
+
+/* Data error group */
+#define SONY_UNREC_CIRC_ERR             0x53
+#define SONY_UNREC_LECC_ERR             0x57
+
+/* Subcode error group */
+#define SONY_NO_TOC_ERR                 0x60
+#define SONY_SUBCODE_DATA_NVAL_ERR      0x61
+#define SONY_FOCUS_ON_TOC_READ_ERR      0x63
+#define SONY_FRAME_SYNC_ON_TOC_READ_ERR 0x64
+#define SONY_TOC_DATA_ERR               0x65
+
+/* Hardware failure group */
+#define SONY_HW_FAILURE_ERR             0x70
+#define SONY_LEAD_IN_A_ERR              0x91
+#define SONY_LEAD_OUT_A_ERR             0x92
+#define SONY_DATA_TRACK_A_ERR           0x93
+
+/*
+ * The following are returned from the Read With Block Error Status command.
+ * They are not errors but information (Errors from the 0x5x group above may
+ * also be returned
+ */
+#define SONY_NO_CIRC_ERR_BLK_STAT       0x50
+#define SONY_NO_LECC_ERR_BLK_STAT       0x54
+#define SONY_RECOV_LECC_ERR_BLK_STAT    0x55
+#define SONY_NO_ERR_DETECTION_STAT      0x59
+
+/* 
+ * The following is not an error returned by the drive, but by the code
+ * that talks to the drive.  It is returned because of a timeout.
+ */
+#define SONY_TIMEOUT_OP_ERR             0x01
+#define SONY_SIGNAL_OP_ERR              0x02
+#define SONY_BAD_DATA_ERR               0x03
+
+
+/*
+ * The following are attention code for asynchronous events from the drive.
+ */
+
+/* Standard attention group */
+#define SONY_EMER_EJECT_ATTN            0x2c
+#define SONY_HW_FAILURE_ATTN            0x70
+#define SONY_MECH_LOADED_ATTN           0x80
+#define SONY_EJECT_PUSHED_ATTN          0x81
+
+/* Audio attention group */
+#define SONY_AUDIO_PLAY_DONE_ATTN       0x90
+#define SONY_LEAD_IN_ERR_ATTN           0x91
+#define SONY_LEAD_OUT_ERR_ATTN          0x92
+#define SONY_DATA_TRACK_ERR_ATTN        0x93
+#define SONY_AUDIO_PLAYBACK_ERR_ATTN    0x94
+
+/* Auto spin up group */
+#define SONY_SPIN_UP_COMPLETE_ATTN      0x24
+#define SONY_SPINDLE_SERVO_ERR_ATTN     0x25
+#define SONY_FOCUS_SERVO_ERR_ATTN       0x26
+#define SONY_TOC_READ_DONE_ATTN         0x62
+#define SONY_FOCUS_ON_TOC_READ_ERR_ATTN 0x63
+#define SONY_SYNC_ON_TOC_READ_ERR_ATTN  0x65
+
+/* Auto eject group */
+#define SONY_SPIN_DOWN_COMPLETE_ATTN    0x27
+#define SONY_EJECT_COMPLETE_ATTN        0x28
+#define SONY_EJECT_MECH_ERR_ATTN        0x29
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c
--- v2.1.69/linux/drivers/cdrom/cm206.c	Tue May 13 22:41:06 1997
+++ linux/drivers/cdrom/cm206.c	Tue Dec  2 11:41:44 1997
@@ -70,9 +70,9 @@
 	      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 generic interface. Much of the functionality
+              driver, a Uniform interface. Much of the functionality
 	      of cm206_open() and cm206_ioctl() is transferred to a
-	      new file cdrom.c and its header ucdrom.h. 
+	      new file cdrom.c and its header cdrom.h. 
 
 	      Upgrade to Linux kernel 1.3.78. 
 
@@ -140,8 +140,6 @@
 #include <linux/malloc.h>
 #include <linux/init.h>
 
-#include <linux/ucdrom.h>
-
 #include <asm/io.h>
 
 #define MAJOR_NR CM206_CDROM_MAJOR
@@ -152,7 +150,7 @@
 #undef AUTO_PROBE_MODULE
 #define USE_INSW
 
-#include <linux/cm206.h>
+#include "cm206.h"
 
 /* This variable defines whether or not to probe for adapter base port 
    address and interrupt request. It can be overridden by the boot 
@@ -990,7 +988,7 @@
   else return -EIO;
 }
 
-/* The new generic cdrom support. Routines should be concise, most of
+/* The new Uniform cdrom support. Routines should be concise, most of
    the logic should be in cdrom.c */
 
 /* returns number of times device is in use */
@@ -1021,24 +1019,6 @@
   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)
 {
@@ -1049,7 +1029,7 @@
 }
   
 /* Although a session start should be in LBA format, we return it in 
-   MSF format because it is slightly easier, and the new generic ioctl
+   MSF format because it is slightly easier, and the new Uniform ioctl
    will take care of the necessary conversion. */
 int cm206_get_last_session(struct cdrom_device_info * cdi, 
 			   struct cdrom_multisession * mssp) 
@@ -1123,7 +1103,6 @@
   cm206_open,			/* open */
   cm206_release,		/* release */
   cm206_drive_status,		/* drive status */
-  cm206_disc_status,		/* disc status */
   cm206_media_changed,		/* media changed */
   cm206_tray_move,		/* tray move */
   cm206_lock_door,		/* lock door */
@@ -1150,7 +1129,8 @@
   1,				/* number of discs */
   0,				/* options, not owned */
   0,				/* mc_flags, not owned */
-  0				/* use count, not owned */
+  0,				/* use count, not owned */
+  "cm206"			/* name of the device type */
 };
 
 /* This routine gets called during initialization if thing go wrong,
@@ -1281,7 +1261,7 @@
     cleanup(3);
     return -EIO;
   }
-  if (register_cdrom(&cm206_info, "cm206") != 0) {
+  if (register_cdrom(&cm206_info) != 0) {
     printk("Cannot register for cdrom %d!\n", MAJOR_NR);
     cleanup(3);
     return -EIO;
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/cm206.h linux/drivers/cdrom/cm206.h
--- v2.1.69/linux/drivers/cdrom/cm206.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/cdrom/cm206.h	Tue Dec  2 11:41:44 1997
@@ -0,0 +1,171 @@
+/* cm206.h Header file for cm206.c.
+   Copyright (c) 1995 David van Leeuwen 
+*/
+
+#ifndef LINUX_CM206_H
+#define LINUX_CM206_H
+
+#include <linux/ioctl.h>
+
+/* First, the cm260 stuff */
+/* The ports and irq used. Although CM206_BASE and CM206_IRQ are defined
+   below, the values are not used unless autoprobing is turned off and 
+   no LILO boot options or module command line options are given. Change
+   these values to your own as last resort if autoprobing and options
+   don't work. */
+
+#define CM206_BASE 0x340
+#define CM206_IRQ 11
+
+#define r_data_status (cm206_base)
+#define r_uart_receive (cm206_base+0x2)
+#define r_fifo_output_buffer (cm206_base+0x4)
+#define r_line_status (cm206_base+0x6)
+#define r_data_control (cm206_base+0x8)
+#define r_uart_transmit (cm206_base+0xa)
+#define r_test_clock (cm206_base+0xc)
+#define r_test_control (cm206_base+0xe)
+
+/* the data_status flags */
+#define ds_ram_size 0x4000
+#define ds_toc_ready 0x2000
+#define ds_fifo_empty 0x1000
+#define ds_sync_error 0x800
+#define ds_crc_error 0x400
+#define ds_data_error 0x200
+#define ds_fifo_overflow 0x100
+#define ds_data_ready 0x80
+
+/* the line_status flags */
+#define ls_attention 0x10
+#define ls_parity_error 0x8
+#define ls_overrun 0x4
+#define ls_receive_buffer_full 0x2
+#define ls_transmitter_buffer_empty 0x1
+
+/* the data control register flags */
+#define dc_read_q_channel 0x4000
+#define dc_mask_sync_error 0x2000
+#define dc_toc_enable 0x1000
+#define dc_no_stop_on_error 0x800
+#define dc_break 0x400
+#define dc_initialize 0x200
+#define dc_mask_transmit_ready 0x100
+#define dc_flag_enable 0x80
+
+/* Define the default data control register flags here */
+#define dc_normal (dc_mask_sync_error | dc_no_stop_on_error | \
+		   dc_mask_transmit_ready)
+
+/* now some constants related to the cm206 */
+/* another drive status byte, echoed by the cm206 on most commands */
+
+#define dsb_error_condition 0x1
+#define dsb_play_in_progress 0x4
+#define dsb_possible_media_change 0x8
+#define dsb_disc_present 0x10
+#define dsb_drive_not_ready 0x20
+#define dsb_tray_locked 0x40
+#define dsb_tray_not_closed 0x80
+
+#define dsb_not_useful (dsb_drive_not_ready | dsb_tray_not_closed)
+
+/* the cm206 command set */
+
+#define c_close_tray 0
+#define c_lock_tray 0x01
+#define c_unlock_tray 0x04
+#define c_open_tray 0x05
+#define c_seek 0x10
+#define c_read_data 0x20
+#define c_force_1x 0x21
+#define c_force_2x 0x22
+#define c_auto_mode 0x23
+#define c_play 0x30
+#define c_set_audio_mode 0x31
+#define c_read_current_q 0x41
+#define c_stream_q 0x42
+#define c_drive_status 0x50
+#define c_disc_status 0x51
+#define c_audio_status 0x52
+#define c_drive_configuration 0x53
+#define c_read_upc 0x60
+#define c_stop 0x70
+#define c_calc_checksum 0xe5
+
+#define c_gimme 0xf8
+
+/* finally, the (error) condition that the drive can be in      *
+ * OK, this is not always an error, but let's prefix it with e_ */
+
+#define e_none 0
+#define e_illegal_command 0x01
+#define e_sync 0x02
+#define e_seek 0x03
+#define e_parity 0x04
+#define e_focus 0x05
+#define e_header_sync 0x06
+#define e_code_incompatibility 0x07
+#define e_reset_done 0x08
+#define e_bad_parameter 0x09
+#define e_radial 0x0a
+#define e_sub_code 0x0b
+#define e_no_data_track 0x0c
+#define e_scan 0x0d
+#define e_tray_open 0x0f
+#define e_no_disc 0x10
+#define e_tray stalled 0x11
+
+/* drive configuration masks */
+
+#define dcf_revision_code 0x7
+#define dcf_transfer_rate 0x60
+#define dcf_motorized_tray 0x80
+
+/* disc status byte */
+
+#define cds_multi_session 0x2
+#define cds_all_audio 0x8
+#define cds_xa_mode 0xf0
+
+/* finally some ioctls for the driver */
+
+#define CM206CTL_GET_STAT _IO( 0x20, 0 )
+#define CM206CTL_GET_LAST_STAT _IO( 0x20, 1 )
+
+#ifdef STATISTICS
+
+/* This is an ugly way to guarantee that the names of the statistics
+ * are the same in the code and in the diagnostics program.  */
+
+#ifdef __KERNEL__
+#define x(a) st_ ## a
+#define y enum
+#else
+#define x(a) #a
+#define y char * stats_name[] = 
+#endif
+
+y {x(interrupt), x(data_ready), x(fifo_overflow), x(data_error),
+     x(crc_error), x(sync_error), x(lost_intr), x(echo),
+     x(write_timeout), x(receive_timeout), x(read_timeout),
+     x(dsb_timeout), x(stop_0xff), x(back_read_timeout),
+     x(sector_transferred), x(read_restarted), x(read_background),
+     x(bh), x(open), x(ioctl_multisession), x(attention)
+#ifdef __KERNEL__
+     , x(last_entry)
+#endif
+ };
+
+#ifdef __KERNEL__
+#define NR_STATS st_last_entry
+#else
+#define NR_STATS (sizeof(stats_name)/sizeof(char*))
+#endif
+
+#undef y
+#undef x
+
+#endif STATISTICS
+
+#endif LINUX_CM206_H
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/gscd.c linux/drivers/cdrom/gscd.c
--- v2.1.69/linux/drivers/cdrom/gscd.c	Tue May 13 22:41:06 1997
+++ linux/drivers/cdrom/gscd.c	Tue Dec  2 11:41:44 1997
@@ -64,7 +64,7 @@
 #define MAJOR_NR GOLDSTAR_CDROM_MAJOR
 #include <linux/blk.h>
 #define gscd_port gscd /* for compatible parameter passing with "insmod" */
-#include <linux/gscd.h>
+#include "gscd.h"
 
 
 static int gscdPresent            = 0;
@@ -1072,7 +1072,7 @@
 
 static void gscd_hsg2msf (long hsg, struct msf *msf)
 {
-	hsg += CD_BLOCK_OFFSET;
+	hsg += CD_MSF_OFFSET;
 	msf -> min = hsg / (CD_FRAMES*CD_SECS);
 	hsg %= CD_FRAMES*CD_SECS;
 	msf -> sec = hsg / CD_FRAMES;
@@ -1100,7 +1100,7 @@
 	return gscd_bcd2bin(mp -> frame)
 		+ gscd_bcd2bin(mp -> sec) * CD_FRAMES
 		+ gscd_bcd2bin(mp -> min) * CD_FRAMES * CD_SECS
-		- CD_BLOCK_OFFSET;
+		- CD_MSF_OFFSET;
 }
 
 static int gscd_bcd2bin (unsigned char bcd)
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/gscd.h linux/drivers/cdrom/gscd.h
--- v2.1.69/linux/drivers/cdrom/gscd.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/cdrom/gscd.h	Tue Dec  2 11:41:44 1997
@@ -0,0 +1,110 @@
+/*
+ * Definitions for a GoldStar R420 CD-ROM interface
+ *
+ *   Copyright (C) 1995  Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>
+ *                       Eberhard Moenkeberg <emoenke@gwdg.de>
+ *
+ *  Published under the GPL.
+ *
+ */
+
+
+/* The Interface Card default address is 0x340. This will work for most
+   applications. Address selection is accomplished by jumpers PN801-1 to
+   PN801-4 on the GoldStar Interface Card.
+   Appropriate settings are: 0x300, 0x310, 0x320, 0x330, 0x340, 0x350, 0x360
+   0x370, 0x380, 0x390, 0x3A0, 0x3B0, 0x3C0, 0x3D0, 0x3E0, 0x3F0             */
+
+/* insert here the I/O port address */
+#define GSCD_BASE_ADDR	        0x340
+
+/* change this to set the dma-channel */
+#define GSCD_DMA_CHANNEL        3                  /* not used */ 
+
+/************** nothing to set up below here *********************/
+
+/* port access macro */
+#define GSCDPORT(x)		(gscd_port + (x))
+
+/*
+ * commands
+ * the lower nibble holds the command length
+ */
+#define CMD_STATUS     0x01
+#define CMD_READSUBQ   0x02 /* 1: ?, 2: UPC, 5: ? */
+#define CMD_SEEK       0x05 /* read_mode M-S-F */
+#define CMD_READ       0x07 /* read_mode M-S-F nsec_h nsec_l */
+#define CMD_RESET      0x11
+#define CMD_SETMODE    0x15
+#define CMD_PLAY       0x17 /* M-S-F M-S-F */
+#define CMD_LOCK_CTL   0x22 /* 0: unlock, 1: lock */
+#define CMD_IDENT      0x31
+#define CMD_SETSPEED   0x32 /* 0: auto */ /* ??? */
+#define CMD_GETMODE    0x41
+#define CMD_PAUSE      0x51
+#define CMD_READTOC    0x61
+#define CMD_DISKINFO   0x71
+#define CMD_TRAY_CTL   0x81
+
+/*
+ * disk_state:
+ */
+#define ST_PLAYING	0x80
+#define ST_UNLOCKED	0x40
+#define ST_NO_DISK	0x20
+#define ST_DOOR_OPEN	0x10
+#define ST_x08  0x08
+#define ST_x04	0x04
+#define ST_INVALID	0x02
+#define ST_x01	0x01
+
+/*
+ * cmd_type:
+ */
+#define TYPE_INFO	0x01
+#define TYPE_DATA	0x02
+
+/*
+ * read_mode:
+ */
+#define MOD_POLLED	0x80
+#define MOD_x08	0x08
+#define MOD_RAW	0x04
+
+#define READ_DATA(port, buf, nr) insb(port, buf, nr)
+
+#define SET_TIMER(func, jifs) \
+	((timer_table[GSCD_TIMER].expires = jiffies + jifs), \
+	(timer_table[GSCD_TIMER].fn = func), \
+	(timer_active |= 1<<GSCD_TIMER))
+
+#define CLEAR_TIMER		timer_active &= ~(1<<GSCD_TIMER)
+
+#define MAX_TRACKS		104
+
+struct msf {
+	unsigned char	min;
+	unsigned char	sec;
+	unsigned char	frame;
+};
+
+struct gscd_Play_msf {
+	struct msf	start;
+	struct msf	end;
+};
+
+struct gscd_DiskInfo {
+	unsigned char	first;
+	unsigned char	last;
+	struct msf	diskLength;
+	struct msf	firstTrack;
+};
+
+struct gscd_Toc {
+	unsigned char	ctrl_addr;
+	unsigned char	track;
+	unsigned char	pointIndex;
+	struct msf	trackTime;
+	struct msf	diskTime;
+};
+
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/isp16.c linux/drivers/cdrom/isp16.c
--- v2.1.69/linux/drivers/cdrom/isp16.c	Tue May 13 22:41:06 1997
+++ linux/drivers/cdrom/isp16.c	Tue Dec  2 11:41:44 1997
@@ -47,9 +47,9 @@
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/ioport.h>
-#include <linux/isp16.h>
 #include <linux/init.h>
 #include <asm/io.h>
+#include "isp16.h"
 
 static short isp16_detect(void);
 static short isp16_c928__detect(void);
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/isp16.h linux/drivers/cdrom/isp16.h
--- v2.1.69/linux/drivers/cdrom/isp16.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/cdrom/isp16.h	Tue Dec  2 11:41:44 1997
@@ -0,0 +1,75 @@
+/* -- isp16.h
+ *
+ *  Header for detection and initialisation of cdrom interface (only) on
+ *  ISP16 (MAD16, Mozart) sound card.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* These are the default values */
+#define ISP16_CDROM_TYPE "Sanyo"
+#define ISP16_CDROM_IO_BASE 0x340
+#define ISP16_CDROM_IRQ 0
+#define ISP16_CDROM_DMA 0
+
+/* Some (Media)Magic */
+/* define types of drive the interface on an ISP16 card may be looking at */
+#define ISP16_DRIVE_X 0x00
+#define ISP16_SONY  0x02
+#define ISP16_PANASONIC0 0x02
+#define ISP16_SANYO0 0x02
+#define ISP16_MITSUMI  0x04
+#define ISP16_PANASONIC1 0x06
+#define ISP16_SANYO1 0x06
+#define ISP16_DRIVE_NOT_USED 0x08  /* not used */
+#define ISP16_DRIVE_SET_MASK 0xF1  /* don't change 0-bit or 4-7-bits*/
+/* ...for port */
+#define ISP16_DRIVE_SET_PORT 0xF8D
+/* set io parameters */
+#define ISP16_BASE_340  0x00
+#define ISP16_BASE_330  0x40
+#define ISP16_BASE_360  0x80
+#define ISP16_BASE_320  0xC0
+#define ISP16_IRQ_X  0x00
+#define ISP16_IRQ_5  0x04  /* shouldn't be used to avoid sound card conflicts */
+#define ISP16_IRQ_7  0x08  /* shouldn't be used to avoid sound card conflicts */
+#define ISP16_IRQ_3  0x0C
+#define ISP16_IRQ_9  0x10
+#define ISP16_IRQ_10  0x14
+#define ISP16_IRQ_11  0x18
+#define ISP16_DMA_X  0x03
+#define ISP16_DMA_3  0x00
+#define ISP16_DMA_5  0x00
+#define ISP16_DMA_6  0x01
+#define ISP16_DMA_7  0x02
+#define ISP16_IO_SET_MASK  0x20  /* don't change 5-bit */
+/* ...for port */
+#define ISP16_IO_SET_PORT  0xF8E
+/* enable the card */
+#define ISP16_C928__ENABLE_PORT  0xF90  /* ISP16 with OPTi 82C928 chip */
+#define ISP16_C929__ENABLE_PORT  0xF91  /* ISP16 with OPTi 82C929 chip */
+#define ISP16_ENABLE_CDROM  0x80  /* seven bit */
+
+/* the magic stuff */
+#define ISP16_CTRL_PORT  0xF8F
+#define ISP16_C928__CTRL  0xE2  /* ISP16 with OPTi 82C928 chip */
+#define ISP16_C929__CTRL  0xE3  /* ISP16 with OPTi 82C929 chip */
+
+#define ISP16_IO_BASE 0xF8D
+#define ISP16_IO_SIZE 5  /* ports used from 0xF8D up to 0xF91 */
+
+void isp16_setup(char *str, int *ints);
+int isp16_init(void);
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/mcd.c linux/drivers/cdrom/mcd.c
--- v2.1.69/linux/drivers/cdrom/mcd.c	Tue May 13 22:41:06 1997
+++ linux/drivers/cdrom/mcd.c	Tue Dec  2 11:41:44 1997
@@ -65,6 +65,8 @@
 	Michael K. Johnson <johnsonm@redhat.com> added retries on open
 	for slow drives which take a while to recognize that they contain
 	a CD.
+
+	November 1997 -- ported to the Uniform CD-ROM driver by Erik Andersen.
 */
 
 #include <linux/module.h>
@@ -91,23 +93,10 @@
 #include <linux/blk.h>
 
 #define mcd_port mcd    /* for compatible parameter passing with "insmod" */
-#include <linux/mcd.h>
+#include "mcd.h"
 
-#if 0
-static int mcd_sizes[] = { 0 };
-#endif
 static int mcd_blocksizes[1] = { 0, };
 
-/* I know putting defines in this file is probably stupid, but it should be */
-/* the only place that they are really needed... I HOPE! :) */
-
-/* How many sectors to read at 1x when an error at 2x speed occurs. */
-/* You can change this to anything from 2 to 32767, but 30 seems to */
-/* work best for me.  I have found that when the drive has problems */
-/* reading one sector, it will have troubles reading the next few.  */
-#define SINGLE_HOLD_SECTORS 30	
-
-#define MCMD_2X_READ 0xC1	/* Double Speed Read DON'T TOUCH! */
 
 /* I added A flag to drop to 1x speed if too many errors 0 = 1X ; 1 = 2X */
 static int mcdDouble = 0; 
@@ -195,6 +184,48 @@
 static int GetDiskInfo(void);
 static int GetToc(void);
 static int getValue(unsigned char *result);
+static int mcd_open(struct cdrom_device_info * cdi, int purpose);
+static void mcd_release(struct cdrom_device_info * cdi);
+static int mcd_media_changed(struct cdrom_device_info * cdi, int disc_nr);
+static int mcd_tray_move(struct cdrom_device_info * cdi, int position);
+int mcd_audio_ioctl(struct cdrom_device_info * cdi, unsigned int cmd,
+                      void * arg);
+int mcd_drive_status(struct cdrom_device_info * cdi, int slot_nr);
+
+static struct cdrom_device_ops mcd_dops = {
+  mcd_open,                   /* open */
+  mcd_release,                /* release */
+  mcd_drive_status,           /* drive status */
+  //NULL,           /* drive status */
+  mcd_media_changed,          /* media changed */
+  mcd_tray_move,              /* tray move */
+  NULL,                       /* lock door */
+  NULL,                       /* select speed */
+  NULL,                       /* select disc */
+  NULL,                       /* get last session */
+  NULL,                       /* get universal product code */
+  NULL,                       /* hard reset */
+  mcd_audio_ioctl,            /* audio ioctl */
+  NULL,                  /* device-specific ioctl */
+  CDC_OPEN_TRAY | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO
+  | CDC_DRIVE_STATUS, /* capability */
+  0,                            /* number of minor devices */
+};
+
+static struct cdrom_device_info mcd_info = {
+  &mcd_dops,                    /* device operations */
+  NULL,                         /* link */
+  NULL,                         /* handle */
+  MKDEV(MAJOR_NR,0),            /* dev */
+  0,                            /* mask */
+  2,                            /* maximum speed */
+  1,                            /* number of discs */
+  0,                            /* options, not owned */
+  0,                            /* mc_flags, not owned */
+  0,                            /* use count, not owned */
+  "mcd",                         /* name of the device type */
+};
+
 
 
 __initfunc(void mcd_setup(char *str, int *ints))
@@ -209,9 +240,8 @@
 #endif /* WORK_AROUND_MITSUMI_BUG_93 */
 }
 
- 
-static int
-check_mcd_change(kdev_t full_dev)
+
+static int mcd_media_changed(struct cdrom_device_info * cdi, int disc_nr)
 {
    int retval, target;
 
@@ -219,7 +249,7 @@
 #if 1	 /* the below is not reliable */
    return 0;
 #endif  
-   target = MINOR(full_dev);
+   target = cdi->dev;
 
    if (target > 0) {
       printk("mcd: Mitsumi CD-ROM request error: invalid device.\n");
@@ -245,11 +275,11 @@
 
 	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
 	{
+		outb(MCMD_GET_STATUS, MCDPORT(0));    /* send get-status cmd */
 
-		outb(MCMD_GET_STATUS, MCDPORT(0));	/* send get-status cmd */
 		st = getMcdStatus(MCD_STATUS_DELAY);
-		if (st != -1)
-			break;
+		if (st != -1) 
+        		break;
 	}
 
 	return st;
@@ -277,6 +307,33 @@
 }
 
 
+static int 
+mcd_tray_move(struct cdrom_device_info * cdi, int position)
+{
+	int i;
+	if (position) {
+	/*  Eject */
+       		/* all drives can at least stop! */
+		if (audioStatus == CDROM_AUDIO_PLAY) {
+	  	outb(MCMD_STOP, MCDPORT(0));
+	  	i = getMcdStatus(MCD_STATUS_DELAY);
+		}
+
+                audioStatus = CDROM_AUDIO_NO_STATUS;
+
+                outb(MCMD_EJECT, MCDPORT(0));
+                /*
+                 * the status (i) shows failure on all but the FX drives.
+                 * But nothing we can do about that in software!
+                 * So just read the status and forget it. - Jon.
+                 */
+                i = getMcdStatus(MCD_STATUS_DELAY);
+                return 0;
+	}
+	else 
+		return -EINVAL;
+}
+
 long
 msf2hsg(struct msf *mp)
 {
@@ -287,22 +344,18 @@
 }
 
 
-static int
-mcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
-						unsigned long arg)
+int mcd_audio_ioctl(struct cdrom_device_info * cdi, unsigned int cmd,
+                      void * arg)
 {
 	int i, st;
 	struct mcd_Toc qInfo;
-	struct cdrom_ti ti;
-	struct cdrom_tochdr tocHdr;
-	struct cdrom_msf msf;
-	struct cdrom_tocentry entry;
+	struct cdrom_ti *ti;
+	struct cdrom_tochdr *tocHdr;
+	struct cdrom_msf *msf;
+	struct cdrom_subchnl *subchnl;
+	struct cdrom_tocentry *entry;
 	struct mcd_Toc *tocPtr;
-	struct cdrom_subchnl subchnl;
-	struct cdrom_volctrl volctrl;
-
-	if (!ip)
-		return -EINVAL;
+	struct cdrom_volctrl *volctrl;
 
 	st = statusCmd();
 	if (st < 0)
@@ -311,7 +364,7 @@
 	if (!tocUpToDate)
 	{
 		i = updateToc();
-		if (i < 0)
+		if (i < 0) 
 			return i;	/* error reading TOC */
 	}
 
@@ -372,24 +425,20 @@
 
 	case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
 
-		st = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
-		if (st)
-			return st;
-
-		copy_from_user(&ti, (void *) arg, sizeof ti);
-
-		if (ti.cdti_trk0 < DiskInfo.first
-			|| ti.cdti_trk0 > DiskInfo.last
-			|| ti.cdti_trk1 < ti.cdti_trk0)
+		ti=(struct cdrom_ti *) arg;
+
+		if (ti->cdti_trk0 < DiskInfo.first
+			|| ti->cdti_trk0 > DiskInfo.last
+			|| ti->cdti_trk1 < ti->cdti_trk0)
 		{
 			return -EINVAL;
 		}
 
-		if (ti.cdti_trk1 > DiskInfo.last)
-			ti. cdti_trk1 = DiskInfo.last;
+		if (ti->cdti_trk1 > DiskInfo.last)
+			ti->cdti_trk1 = DiskInfo.last;
 
-		mcd_Play.start = Toc[ti.cdti_trk0].diskTime;
-		mcd_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
+		mcd_Play.start = Toc[ti->cdti_trk0].diskTime;
+		mcd_Play.end = Toc[ti->cdti_trk1 + 1].diskTime;
 
 #ifdef MCD_DEBUG
 printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n",
@@ -415,27 +464,23 @@
 		  audioStatus = CDROM_AUDIO_NO_STATUS;
 		}
 
-		st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
-		if (st)
-			return st;
-
-		copy_from_user(&msf, (void *) arg, sizeof msf);
+		msf=(struct cdrom_msf *) arg;
 
 		/* convert to bcd */
 
-		bin2bcd(&msf.cdmsf_min0);
-		bin2bcd(&msf.cdmsf_sec0);
-		bin2bcd(&msf.cdmsf_frame0);
-		bin2bcd(&msf.cdmsf_min1);
-		bin2bcd(&msf.cdmsf_sec1);
-		bin2bcd(&msf.cdmsf_frame1);
-
-		mcd_Play.start.min = msf.cdmsf_min0;
-		mcd_Play.start.sec = msf.cdmsf_sec0;
-		mcd_Play.start.frame = msf.cdmsf_frame0;
-		mcd_Play.end.min = msf.cdmsf_min1;
-		mcd_Play.end.sec = msf.cdmsf_sec1;
-		mcd_Play.end.frame = msf.cdmsf_frame1;
+		bin2bcd(&msf->cdmsf_min0);
+		bin2bcd(&msf->cdmsf_sec0);
+		bin2bcd(&msf->cdmsf_frame0);
+		bin2bcd(&msf->cdmsf_min1);
+		bin2bcd(&msf->cdmsf_sec1);
+		bin2bcd(&msf->cdmsf_frame1);
+
+		mcd_Play.start.min = msf->cdmsf_min0;
+		mcd_Play.start.sec = msf->cdmsf_sec0;
+		mcd_Play.start.frame = msf->cdmsf_frame0;
+		mcd_Play.end.min = msf->cdmsf_min1;
+		mcd_Play.end.sec = msf->cdmsf_sec1;
+		mcd_Play.end.frame = msf->cdmsf_frame1;
 
 #ifdef MCD_DEBUG
 printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n",
@@ -454,102 +499,66 @@
 		return 0;
 
 	case CDROMREADTOCHDR:        /* Read the table of contents header */
-		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr);
-		if (st)
-			return st;
-
-		tocHdr.cdth_trk0 = DiskInfo.first;
-		tocHdr.cdth_trk1 = DiskInfo.last;
-		copy_to_user((void *) arg, &tocHdr, sizeof tocHdr);
+	        tocHdr=(struct cdrom_tochdr *) arg; 
+		tocHdr->cdth_trk0 = DiskInfo.first;
+		tocHdr->cdth_trk1 = DiskInfo.last;
 		return 0;
 
 	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
+		entry=(struct cdrom_tocentry *) arg;
+		if (entry->cdte_track == CDROM_LEADOUT)
+			tocPtr = &Toc[DiskInfo.last - DiskInfo.first + 1];
 
-		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
-		if (st)
-			return st;
-
-		copy_from_user(&entry, (void *) arg, sizeof entry);
-		if (entry.cdte_track == CDROM_LEADOUT)
-			/* XXX */
-			tocPtr = &Toc[DiskInfo.last + 1];
-
-		else if (entry.cdte_track > DiskInfo.last
-				|| entry.cdte_track < DiskInfo.first)
+		else if (entry->cdte_track > DiskInfo.last
+				|| entry->cdte_track < DiskInfo.first)
 			return -EINVAL;
 
 		else
-			tocPtr = &Toc[entry.cdte_track];
+			tocPtr = &Toc[entry->cdte_track];
 
-		entry.cdte_adr = tocPtr -> ctrl_addr;
-		entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
+		entry->cdte_adr = tocPtr -> ctrl_addr;
+		entry->cdte_ctrl = tocPtr -> ctrl_addr >> 4;
 
-		if (entry.cdte_format == CDROM_LBA)
-			entry.cdte_addr.lba = msf2hsg(&tocPtr -> diskTime);
+		if (entry->cdte_format == CDROM_LBA)
+			entry->cdte_addr.lba = msf2hsg(&tocPtr -> diskTime);
 
-		else if (entry.cdte_format == CDROM_MSF)
+		else if (entry->cdte_format == CDROM_MSF)
 		{
-			entry.cdte_addr.msf.minute = bcd2bin(tocPtr -> diskTime.min);
-			entry.cdte_addr.msf.second = bcd2bin(tocPtr -> diskTime.sec);
-			entry.cdte_addr.msf.frame = bcd2bin(tocPtr -> diskTime.frame);
+			entry->cdte_addr.msf.minute = bcd2bin(tocPtr -> diskTime.min);
+			entry->cdte_addr.msf.second = bcd2bin(tocPtr -> diskTime.sec);
+			entry->cdte_addr.msf.frame = bcd2bin(tocPtr -> diskTime.frame);
 		}
 
 		else
 			return -EINVAL;
 
-		copy_to_user((void *) arg, &entry, sizeof entry);
 		return 0;
 
 	case CDROMSUBCHNL:   /* Get subchannel info */
 
-		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof subchnl);
-		if (st)
-			return st;
-
-		copy_from_user(&subchnl, (void *) arg, sizeof subchnl);
-
+                subchnl=(struct cdrom_subchnl *) arg;
 		if (GetQChannelInfo(&qInfo) < 0)
 			return -EIO;
 
-		subchnl.cdsc_audiostatus = audioStatus;
-		subchnl.cdsc_adr = qInfo.ctrl_addr;
-		subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4;
-		subchnl.cdsc_trk = bcd2bin(qInfo.track);
-		subchnl.cdsc_ind = bcd2bin(qInfo.pointIndex);
-
-		if (subchnl.cdsc_format == CDROM_LBA)
-		{
-			subchnl.cdsc_absaddr.lba = msf2hsg(&qInfo.diskTime);
-			subchnl.cdsc_reladdr.lba = msf2hsg(&qInfo.trackTime);
-		}
-
-		else if (subchnl.cdsc_format == CDROM_MSF)
-		{
-			subchnl.cdsc_absaddr.msf.minute = bcd2bin(qInfo.diskTime.min);
-			subchnl.cdsc_absaddr.msf.second = bcd2bin(qInfo.diskTime.sec);
-			subchnl.cdsc_absaddr.msf.frame = bcd2bin(qInfo.diskTime.frame);
-
-			subchnl.cdsc_reladdr.msf.minute = bcd2bin(qInfo.trackTime.min);
-			subchnl.cdsc_reladdr.msf.second = bcd2bin(qInfo.trackTime.sec);
-			subchnl.cdsc_reladdr.msf.frame = bcd2bin(qInfo.trackTime.frame);
-		}
-
-		else
-			return -EINVAL;
-
-		copy_to_user((void *) arg, &subchnl, sizeof subchnl);
-		return 0;
+		subchnl->cdsc_audiostatus = audioStatus;
+		subchnl->cdsc_adr = qInfo.ctrl_addr;
+		subchnl->cdsc_ctrl = qInfo.ctrl_addr >> 4;
+		subchnl->cdsc_trk = bcd2bin(qInfo.track);
+		subchnl->cdsc_ind = bcd2bin(qInfo.pointIndex);
+		subchnl->cdsc_absaddr.msf.minute = bcd2bin(qInfo.diskTime.min);
+		subchnl->cdsc_absaddr.msf.second = bcd2bin(qInfo.diskTime.sec);
+		subchnl->cdsc_absaddr.msf.frame = bcd2bin(qInfo.diskTime.frame);
+		subchnl->cdsc_reladdr.msf.minute = bcd2bin(qInfo.trackTime.min);
+		subchnl->cdsc_reladdr.msf.second = bcd2bin(qInfo.trackTime.sec);
+		subchnl->cdsc_reladdr.msf.frame = bcd2bin(qInfo.trackTime.frame);
+		return(0);
 
 	case CDROMVOLCTRL:   /* Volume control */
-		st = verify_area(VERIFY_READ, (void *) arg, sizeof(volctrl));
-		if (st)
-			return st;
-
-		copy_from_user(&volctrl, (char *) arg, sizeof(volctrl));
+		volctrl=(struct cdrom_volctrl *) arg;
 		outb(MCMD_SET_VOLUME, MCDPORT(0));
-		outb(volctrl.channel0, MCDPORT(0));
+		outb(volctrl->channel0, MCDPORT(0));
 		outb(255, MCDPORT(0));
-		outb(volctrl.channel1, MCDPORT(0));
+		outb(volctrl->channel1, MCDPORT(0));
 		outb(255, MCDPORT(0));
 
 		i = getMcdStatus(MCD_STATUS_DELAY);
@@ -567,29 +576,11 @@
 
 		return 0;
 
-	case CDROMEJECT:
- 	       /* all drives can at least stop! */
- 		if (audioStatus == CDROM_AUDIO_PLAY) {
- 		  outb(MCMD_STOP, MCDPORT(0));
- 		  i = getMcdStatus(MCD_STATUS_DELAY);
- 		}
- 
- 		audioStatus = CDROM_AUDIO_NO_STATUS;
- 
-		outb(MCMD_EJECT, MCDPORT(0));
-		/*
-		 * the status (i) shows failure on all but the FX drives.
-		 * But nothing we can do about that in software!
-		 * So just read the status and forget it. - Jon.
-		 */
- 		i = getMcdStatus(MCD_STATUS_DELAY);
-		return 0;
 	default:
 		return -EINVAL;
 	}
 }
 
-
 /*
  * Take care of the different block sizes between cdrom and Linux.
  * When Linux gets variable block sizes this will probably go away.
@@ -1095,44 +1086,28 @@
 /*
  * Open the device special file.  Check that a disk is in.
  */
-
-int
-mcd_open(struct inode *ip, struct file *fp)
+static int mcd_open(struct cdrom_device_info * cdi, int purpose)
 {
-	int st;
-	int count = 0;
-
+	int st, count=0;
 	if (mcdPresent == 0)
 		return -ENXIO;			/* no hardware */
-	
-	if (fp->f_mode & 2)			/* write access? */
-		return -EROFS;
-
-	if (!mcd_open_count && mcd_state == MCD_S_IDLE) {
 
-	mcd_invalidate_buffers();
+        if (!mcd_open_count && mcd_state == MCD_S_IDLE) {
+        mcd_invalidate_buffers();
+        do {
+                st = statusCmd();               /* check drive status */
+                if (st == -1)
+                        return -EIO;            /* drive doesn't respond */
+                if ((st & MST_READY) == 0) {    /* no disk? wait a sec... */
+                        current->state = TASK_INTERRUPTIBLE;
+                        current->timeout = jiffies + HZ;
+                        schedule();
+                }
+        } while (((st & MST_READY) == 0) && count++ < MCD_RETRY_ATTEMPTS);
 
-	do {
-		st = statusCmd();		/* check drive status */
-		if (st == -1)
-			return -EIO;		/* drive doesn't respond */
-		if ((st & MST_READY) == 0) {	/* no disk? wait a sec... */
-			current->state = TASK_INTERRUPTIBLE;
-			current->timeout = jiffies + HZ;
-			schedule();
-		}
-	} while (((st & MST_READY) == 0) && count++ < MCD_RETRY_ATTEMPTS);
-
-	if ((st & MST_READY) == 0)		/* no disk in drive */
-	{
-		printk("mcd: no disk in drive\n");
-		return -EIO;
-	}
-
-	if (updateToc() < 0)
-		return -EIO;
-
-	}
+        if (updateToc() < 0)
+                       return -EIO;
+        }
 	++mcd_open_count;
         MOD_INC_USE_COUNT;
 	return 0;
@@ -1142,34 +1117,37 @@
 /*
  * On close, we flush all mcd blocks from the buffer cache.
  */
-
-static int
-mcd_release(struct inode * inode, struct file * file)
+static void mcd_release(struct cdrom_device_info * cdi)
 { MOD_DEC_USE_COUNT;
   if (!--mcd_open_count) {
 	mcd_invalidate_buffers();
-	sync_dev(inode->i_rdev);
-	invalidate_buffers(inode -> i_rdev);
   }
-  return 0;
 }
 
 
-static struct file_operations mcd_fops = {
-	NULL,			/* lseek - default */
-	block_read,		/* read - general block-dev read */
-	block_write,		/* write - general block-dev write */
-	NULL,			/* readdir - bad */
-	NULL,			/* poll */
-	mcd_ioctl,		/* ioctl */
-	NULL,			/* mmap */
-	mcd_open,		/* open */
-	mcd_release,		/* release */
-	NULL,			/* fsync */
-	NULL,			/* fasync */
-	check_mcd_change,	/* media change */
-	NULL			/* revalidate */
-};
+
+/* This routine gets called during initialization if thing go wrong,
+ * and is used in cleanup_module as well. */
+void cleanup(int level)
+{
+  switch (level) {
+  case 3:
+    if (unregister_cdrom(&mcd_info)) {
+      printk(KERN_WARNING "Can't unregister cdrom mcd\n");
+      return;
+    }
+    free_irq(mcd_irq, NULL);
+  case 2:
+    release_region(mcd_port,4);
+  case 1:
+    if (unregister_blkdev(MAJOR_NR, "mcd")) {
+      printk(KERN_WARNING "Can't unregister major mcd\n");
+      return;
+    }
+  default:
+  }
+}
+
 
 
 /*
@@ -1180,23 +1158,21 @@
 {
 	int count;
 	unsigned char result[3];
+	char msg[80];
 
 	if (mcd_port <= 0 || mcd_irq <= 0) {
 	  printk("skip mcd_init\n");
           return -EIO;
 	}
 
-	printk(KERN_INFO "mcd=0x%x,%d: ", mcd_port, mcd_irq);
-
-	if (register_blkdev(MAJOR_NR, "mcd", &mcd_fops) != 0)
+	if (register_blkdev(MAJOR_NR, "mcd", &cdrom_fops) != 0)
 	{
 		printk("Unable to get major %d for Mitsumi CD-ROM\n",
 		       MAJOR_NR);
                 return -EIO;
 	}
-
         if (check_region(mcd_port, 4)) {
-	  unregister_blkdev(MAJOR_NR, "mcd");
+	  cleanup(1);
 	  printk("Init failed, I/O port (%X) already in use\n",
 		 mcd_port);
           return -EIO;
@@ -1212,7 +1188,7 @@
 	for (count = 0; count < 2000000; count++)
 		(void) inb(MCDPORT(1));		/* delay a bit */
 
-	outb(0x40, MCDPORT(0));			/* send get-stat cmd */
+	outb(0x40, MCDPORT(0));	                /* send get-stat cmd */
 	for (count = 0; count < 2000000; count++)
 		if (!(inb(MCDPORT(1)) & MFL_STATUS))
 			break;
@@ -1220,7 +1196,7 @@
 	if (count >= 2000000) {
 		printk("Init failed. No mcd device at 0x%x irq %d\n",
 		     mcd_port, mcd_irq);
-		unregister_blkdev(MAJOR_NR, "mcd");
+		cleanup(1);
                 return -EIO;
 	}
 	count = inb(MCDPORT(0));		/* pick up the status */
@@ -1228,26 +1204,16 @@
 	outb(MCMD_GET_VERSION,MCDPORT(0));
 	for(count=0;count<3;count++)
 		if(getValue(result+count)) {
-			unregister_blkdev(MAJOR_NR, "mcd");
 			printk("mitsumi get version failed at 0x%d\n",
 			       mcd_port);
+                        cleanup(1);
                         return -EIO;
 		}	
 
 	if (result[0] == result[1] && result[1] == result[2]) {
-		unregister_blkdev(MAJOR_NR, "mcd");
+		cleanup(1);
                 return -EIO;
 	}
-	printk("Mitsumi status, type and version : %02X %c %x ",
-	       result[0],result[1],result[2]);
-
-     if (result[1] == 'D') 
-	{
-	printk("Double Speed CD ROM\n");
-	MCMD_DATA_READ = MCMD_2X_READ;
-        mcdDouble = 1; /* Added flag to drop to 1x speed if too many errors */
-        }
-       else printk("Single Speed CD ROM\n");
 
 	mcdVersion=result[2];
 
@@ -1259,9 +1225,22 @@
 	if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD", NULL))
 	{
 		printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
-		unregister_blkdev(MAJOR_NR, "mcd");
+		cleanup(1);
                 return -EIO;
 	}
+
+        if (result[1] == 'D') 
+	{
+	sprintf(msg, " mcd: Mitsumi Double Speed CD-ROM at port=0x%x, irq=%d\n", mcd_port, mcd_irq);
+	MCMD_DATA_READ = MCMD_2X_READ;
+	mcd_info.speed = 2;
+        mcdDouble = 1; /* Added flag to drop to 1x speed if too many errors */
+        }
+        else {
+        sprintf(msg, " mcd: Mitsumi Single Speed CD-ROM at port=0x%x, irq=%d\n", mcd_port, mcd_irq);
+	mcd_info.speed = 2;
+	}
+
 	request_region(mcd_port, 4,"mcd");
 
 	outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
@@ -1276,6 +1255,14 @@
 
 	mcd_invalidate_buffers();
 	mcdPresent = 1;
+
+        if (register_cdrom(&mcd_info) != 0) {
+              printk("Cannot register Mitsumi CD-ROM!\n");
+              cleanup(3);
+              return -EIO;
+        }
+        printk(msg);
+
 	return 0;
 }
 
@@ -1412,15 +1399,31 @@
 }
 
 
+/* gives current state of the drive This function is quite unreliable, 
+   and should probably be rewritten by someone, eventually... */
+int mcd_drive_status(struct cdrom_device_info * cdi, int slot_nr)
+{
+  int st;
+
+  st = statusCmd();               /* check drive status */
+  if (st == -1)
+      return -EIO;            /* drive doesn't respond */
+  if ((st & MST_READY)) return CDS_DISC_OK;
+  if ((st & MST_DOOR_OPEN)) return CDS_TRAY_OPEN;
+  if ((st & MST_DSK_CHG)) return CDS_NO_DISC;
+  if ((st & MST_BUSY)) return CDS_DRIVE_NOT_READY;
+  return -EIO;
+}
+
+
 /*
- * Read a value from the drive.  Should return quickly, so a busy wait
- * is used to avoid excessive rescheduling.
+ * Read a value from the drive.
  */
 
 static int
 getValue(unsigned char *result)
 {
-	int count;
+        int count;
 	int s;
 
 	for (count = 0; count < 2000; count++)
@@ -1521,13 +1524,6 @@
 	DiskInfo.first = bcd2bin(DiskInfo.first);
 	DiskInfo.last = bcd2bin(DiskInfo.last);
 
-	if (getValue(&DiskInfo.diskLength.min) < 0) return -1;
-	if (getValue(&DiskInfo.diskLength.sec) < 0) return -1;
-	if (getValue(&DiskInfo.diskLength.frame) < 0) return -1;
-	if (getValue(&DiskInfo.firstTrack.min) < 0) return -1;
-	if (getValue(&DiskInfo.firstTrack.sec) < 0) return -1;
-	if (getValue(&DiskInfo.firstTrack.frame) < 0) return -1;
-
 #ifdef MCD_DEBUG
 printk("Disk Info: first %d last %d length %02x:%02x.%02x first %02x:%02x.%02x\n",
 	DiskInfo.first,
@@ -1540,6 +1536,13 @@
 	DiskInfo.firstTrack.frame);
 #endif
 
+	if (getValue(&DiskInfo.diskLength.min) < 0) return -1;
+	if (getValue(&DiskInfo.diskLength.sec) < 0) return -1;
+	if (getValue(&DiskInfo.diskLength.frame) < 0) return -1;
+	if (getValue(&DiskInfo.firstTrack.min) < 0) return -1;
+	if (getValue(&DiskInfo.firstTrack.sec) < 0) return -1;
+	if (getValue(&DiskInfo.firstTrack.frame) < 0) return -1;
+
 	return 0;
 }
 
@@ -1635,12 +1638,6 @@
 
 void cleanup_module(void)
 {
-  if ((unregister_blkdev(MAJOR_NR, "mcd") == -EINVAL))
-     { printk("What's that: can't unregister mcd\n");
-       return;    
-     }
-  release_region(mcd_port,4);
-  free_irq(mcd_irq, NULL);
-  printk(KERN_INFO "mcd module released.\n");
+  cleanup(3);
 }
 #endif MODULE
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/mcd.h linux/drivers/cdrom/mcd.h
--- v2.1.69/linux/drivers/cdrom/mcd.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/cdrom/mcd.h	Tue Dec  2 11:41:44 1997
@@ -0,0 +1,128 @@
+/*
+ * Definitions for a Mitsumi CD-ROM interface
+ *
+ *	Copyright (C) 1992  Martin Harriss
+ *
+ *	martin@bdsi.com
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* *** change this to set the I/O port address */
+#define MCD_BASE_ADDR	        0x300
+
+/* *** change this to set the interrupt number */
+#define MCD_INTR_NR     11
+
+/* *** make the following line uncommented, if you're sure,
+ * *** all configuration is done */
+
+/* #define I_WAS_HERE */
+
+
+
+
+/* Increase this if you get lots of timeouts */
+#define MCD_STATUS_DELAY	1000
+
+/* number of times to retry a command before giving up */
+#define MCD_RETRY_ATTEMPTS      10
+
+/* port access macro */
+#define MCDPORT(x)		(mcd_port + (x))
+
+/* How many sectors to read at 1x when an error at 2x speed occurs. */
+/* You can change this to anything from 2 to 32767, but 30 seems to */
+/* work best for me.  I have found that when the drive has problems */
+/* reading one sector, it will have troubles reading the next few.  */
+#define SINGLE_HOLD_SECTORS 30	
+
+#define MCMD_2X_READ 0xC1	/* Double Speed Read DON'T TOUCH! */
+
+/* status bits */
+
+#define MST_CMD_CHECK		0x01		/* command error */
+#define MST_BUSY		0x02		/* now playing */
+#define MST_READ_ERR		0x04		/* read error */
+#define MST_DSK_TYPE		0x08
+#define MST_SERVO_CHECK		0x10
+#define MST_DSK_CHG		0x20		/* disk removed or changed */
+#define MST_READY		0x40		/* disk in the drive */
+#define MST_DOOR_OPEN		0x80		/* door is open */
+
+/* flag bits */
+
+#define MFL_DATA		0x02		/* data available */
+#define MFL_STATUS		0x04		/* status available */
+
+/* commands */
+
+#define MCMD_GET_DISK_INFO	0x10		/* read info from disk */
+#define MCMD_GET_Q_CHANNEL	0x20		/* read info from q channel */
+#define MCMD_GET_STATUS		0x40
+#define MCMD_SET_MODE		0x50
+#define MCMD_SOFT_RESET		0x60
+#define MCMD_STOP		0x70		/* stop play */
+#define MCMD_CONFIG_DRIVE	0x90
+#define MCMD_SET_VOLUME		0xAE		/* set audio level */
+#define MCMD_PLAY_READ		0xC0		/* play or read data */
+#define MCMD_GET_VERSION  	0xDC
+#define MCMD_EJECT		0xF6            /* eject (FX drive) */
+
+/* borrowed from hd.c */
+
+#define READ_DATA(port, buf, nr) \
+insb(port, buf, nr)
+
+#define SET_TIMER(func, jifs) \
+	((timer_table[MCD_TIMER].expires = jiffies + jifs), \
+	(timer_table[MCD_TIMER].fn = func), \
+	(timer_active |= 1<<MCD_TIMER))
+
+#define CLEAR_TIMER		timer_active &= ~(1<<MCD_TIMER)
+
+#define MAX_TRACKS		104
+
+struct msf {
+	unsigned char	min;
+	unsigned char	sec;
+	unsigned char	frame;
+};
+
+struct mcd_Play_msf {
+	struct msf	start;
+	struct msf	end;
+};
+
+struct mcd_DiskInfo {
+	unsigned char	first;
+	unsigned char	last;
+	struct msf	diskLength;
+	struct msf	firstTrack;
+};
+
+struct mcd_Toc {
+	unsigned char	ctrl_addr;
+	unsigned char	track;
+	unsigned char	pointIndex;
+	struct msf	trackTime;
+	struct msf	diskTime;
+};
+
+#ifndef I_WAS_HERE
+#warning You have not edited mcd.h
+#warning Perhaps irq and i/o settings are wrong.
+#endif
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/mcdx.c linux/drivers/cdrom/mcdx.c
--- v2.1.69/linux/drivers/cdrom/mcdx.c	Wed Sep 24 20:05:46 1997
+++ linux/drivers/cdrom/mcdx.c	Tue Dec  2 11:41:44 1997
@@ -73,7 +73,10 @@
 
 /* for compatible parameter passing with "insmod" */
 #define	mcdx_drive_map mcdx
-#include <linux/mcdx.h>
+#include "mcdx.h"
+
+#define MCDX_QUIET 0
+
 
 #ifndef HZ
 #error HZ not defined
@@ -107,8 +110,6 @@
 const int REQUEST_SIZE = 800;	/* should be less then 255 * 4 */
 const int DIRECT_SIZE = 400;	/* should be less then REQUEST_SIZE */
 
-const unsigned long ACLOSE_INHIBIT = 800;  /* 1/100 s of autoclose inhibit */
-
 enum drivemodes { TOC, DATA, RAW, COOKED };
 enum datamodes { MODE0, MODE1, MODE2 };
 enum resetmodes { SOFT, HARD };
@@ -160,8 +161,6 @@
     volatile int introk;	/* status of last irq operation */
     volatile int busy;		/* drive performs an operation */
     volatile int lock;		/* exclusive usage */
-    int eject_sw;           /* 1 - eject on last close (default 0) */
-    int autoclose;          /* 1 - close the door on open (default 1) */
 
 	/* cd infos */
 	struct s_diskinfo di;
@@ -197,7 +196,6 @@
     unsigned char playcmd;  /* play should always be single speed */
     unsigned int xxx;      /* set if changed, reset while open */
     unsigned int yyy;      /* set if changed, reset by media_changed */
-    unsigned long ejected;  /* time we called the eject function */
     int users;				/* keeps track of open/close */
     int lastsector;			/* last block accessible */
     int status;				/* last operation's error / status */
@@ -214,22 +212,25 @@
 /* declared in blk.h */
 int mcdx_init(void);
 void do_mcdx_request(void);
-int check_mcdx_media_change(kdev_t);
 
 /* already declared in init/main */
 void mcdx_setup(char *, int *);
 
 /*	Indirect exported functions. These functions are exported by their
 	addresses, such as mcdx_open and mcdx_close in the
-	structure fops. */
+	structure mcdx_dops. */
 
 /* ???  exported by the mcdx_sigaction struct */
 static void mcdx_intr(int, void *, struct pt_regs*);
 
 /* exported by file_ops */
-static int mcdx_open(struct inode*, struct file*);
-static int mcdx_close(struct inode*, struct file*);
-static int mcdx_ioctl(struct inode*, struct file*, unsigned int, unsigned long);
+static int mcdx_open(struct cdrom_device_info * cdi, int purpose);
+static void mcdx_close(struct cdrom_device_info * cdi);
+static int mcdx_media_changed(struct cdrom_device_info * cdi, int disc_nr);
+static int mcdx_tray_move(struct cdrom_device_info * cdi, int position);
+static int mcdx_lockdoor(struct cdrom_device_info * cdi, int lock);
+static int mcdx_audio_ioctl(struct cdrom_device_info * cdi, unsigned int cmd,
+                      void * arg);
 
 /* misc internal support functions */
 static void log2msf(unsigned int, struct cdrom_msf0*);
@@ -243,13 +244,10 @@
 static int mcdx_xfer(struct s_drive_stuff*, char* buf, int sector, int nr_sectors);
 
 static int mcdx_config(struct s_drive_stuff*, int);
-static int mcdx_closedoor(struct s_drive_stuff*, int);
 static int mcdx_requestversion(struct s_drive_stuff*, struct s_version*, int);
-static int mcdx_lockdoor(struct s_drive_stuff*, int, int);
 static int mcdx_stop(struct s_drive_stuff*, int);
 static int mcdx_hold(struct s_drive_stuff*, int);
 static int mcdx_reset(struct s_drive_stuff*, enum resetmodes, int);
-static int mcdx_eject(struct s_drive_stuff*, int);
 static int mcdx_setdrivemode(struct s_drive_stuff*, enum drivemodes, int);
 static int mcdx_setdatamode(struct s_drive_stuff*, enum datamodes, int);
 static int mcdx_requestsubqcode(struct s_drive_stuff*, struct s_subqcode*, int);
@@ -276,37 +274,79 @@
 		0, 0, 0, 0, 0, 0, 0, 0};
 MODULE_PARM(mcdx, "1-4i");
 
-static struct file_operations mcdx_fops = {
-	NULL,			/* lseek - use kernel default */
-	block_read,		/* read - general block-dev read */
-	block_write,	/* write - general block-dev write */
-	NULL,			/* no readdir */
-	NULL,			/* no poll */
-	mcdx_ioctl,		/* ioctl() */
-	NULL,			/* no mmap */
-	mcdx_open,		/* open() */
-	mcdx_close,		/* close() */
-	NULL,			/* fsync */
-	NULL,                     /* fasync */
-	check_mcdx_media_change,  /* media_change */
-	NULL                      /* revalidate */
+static struct cdrom_device_ops mcdx_dops = {
+  mcdx_open,                   /* open */
+  mcdx_close,                  /* release */
+  NULL,           /* drive status */
+  mcdx_media_changed,         /* media changed */
+  mcdx_tray_move,             /* tray move */
+  mcdx_lockdoor,              /* lock door */
+  NULL,                       /* select speed */
+  NULL,                       /* select disc */
+  NULL,                       /* get last session */
+  NULL,                       /* get universal product code */
+  NULL,                       /* hard reset */
+  mcdx_audio_ioctl,            /* audio ioctl */
+  NULL,                  /* device-specific ioctl */
+  CDC_OPEN_TRAY | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO
+  | CDC_DRIVE_STATUS, /* capability */
+  0,                            /* number of minor devices */
+};
+
+static struct cdrom_device_info mcdx_info = {
+  &mcdx_dops,                    /* device operations */
+  NULL,                         /* link */
+  NULL,                         /* handle */
+  MKDEV(MAJOR_NR,0),            /* dev */
+  0,                            /* mask */
+  2,                            /* maximum speed */
+  1,                            /* number of discs */
+  0,                            /* options, not owned */
+  0,                            /* mc_flags, not owned */
+  0,                            /* use count, not owned */
+  "mcdx",                         /* name of the device type */
 };
 
+
+
 /* KERNEL INTERFACE FUNCTIONS **************************************/
 
-static int
-mcdx_ioctl(
-	struct inode* ip, struct file* fp,
-	unsigned int cmd, unsigned long arg)
+
+static int mcdx_audio_ioctl(struct cdrom_device_info * cdi, unsigned int cmd,
+                      void * arg)
 {
-	struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
+	struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(cdi->dev)];
 
 	if (!stuffp->present) return -ENXIO;
-	if (!ip) return -EINVAL;
+
+	if (stuffp->xxx)
+	{
+		if(-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1))
+		{
+			stuffp->lastsector = -1;
+		}
+		else
+		{
+			stuffp->lastsector = (CD_FRAMESIZE / 512)
+					* msf2log(&stuffp->di.msf_leadout) - 1;
+		}
+
+		if (stuffp->toc)
+		{
+			kfree(stuffp->toc);
+			stuffp->toc = NULL;
+			if (-1 == mcdx_readtoc(stuffp)) return -1;
+		}
+
+		stuffp->xxx=0;
+	}
 
 	switch (cmd) {
-		case CDROMSTART: {
-			xtrace(IOCTL, "ioctl() START\n");
+		case CDROMSTART: {       
+			     xtrace(IOCTL, "ioctl() START\n");
+		        /* Spin up the drive.  Don't think we can do this.
+                 * For now, ignore it.
+                 */
 			return 0;
 		}
 
@@ -319,51 +359,40 @@
 		}
 
 		case CDROMPLAYTRKIND: {
-			int ans;
-			struct cdrom_ti ti;
+			struct cdrom_ti *ti=(struct cdrom_ti *) arg;
 
 			xtrace(IOCTL, "ioctl() PLAYTRKIND\n");
-			if ((ans = verify_area(VERIFY_READ, (void*) arg, sizeof(ti))))
-				return ans;
-			copy_from_user(&ti, (void*) arg, sizeof(ti));
-			if ((ti.cdti_trk0 < stuffp->di.n_first)
-					|| (ti.cdti_trk0 > stuffp->di.n_last)
-					|| (ti.cdti_trk1 < stuffp->di.n_first))
-				return -EINVAL;
-			if (ti.cdti_trk1 > stuffp->di.n_last) ti.cdti_trk1 = stuffp->di.n_last;
-            xtrace(PLAYTRK, "ioctl() track %d to %d\n", ti.cdti_trk0, ti.cdti_trk1);
-
-            return mcdx_playtrk(stuffp, &ti);
+			if ((ti->cdti_trk0 < stuffp->di.n_first)
+					|| (ti->cdti_trk0 > stuffp->di.n_last)
+					|| (ti->cdti_trk1 < stuffp->di.n_first))
+			return -EINVAL;
+			if (ti->cdti_trk1 > stuffp->di.n_last)
+					ti->cdti_trk1 = stuffp->di.n_last;
+            xtrace(PLAYTRK, "ioctl() track %d to %d\n", ti->cdti_trk0, ti->cdti_trk1);
+            return mcdx_playtrk(stuffp, ti);
         }
 
         case CDROMPLAYMSF: {
-            int ans;
-            struct cdrom_msf msf;
+            struct cdrom_msf *msf=(struct cdrom_msf *) arg;
 
             xtrace(IOCTL, "ioctl() PLAYMSF\n");
 
             if ((stuffp->audiostatus == CDROM_AUDIO_PLAY)
                 && (-1 == mcdx_hold(stuffp, 1))) return -EIO;
 
-            if ((ans = verify_area(
-                    VERIFY_READ, (void*) arg, sizeof(struct cdrom_msf))))
-                return ans;
-
-            copy_from_user(&msf, (void*) arg, sizeof msf);
-
-            msf.cdmsf_min0 = uint2bcd(msf.cdmsf_min0);
-            msf.cdmsf_sec0 = uint2bcd(msf.cdmsf_sec0);
-            msf.cdmsf_frame0 = uint2bcd(msf.cdmsf_frame0);
+            msf->cdmsf_min0 = uint2bcd(msf->cdmsf_min0);
+            msf->cdmsf_sec0 = uint2bcd(msf->cdmsf_sec0);
+            msf->cdmsf_frame0 = uint2bcd(msf->cdmsf_frame0);
+
+            msf->cdmsf_min1 = uint2bcd(msf->cdmsf_min1);
+            msf->cdmsf_sec1 = uint2bcd(msf->cdmsf_sec1);
+            msf->cdmsf_frame1 = uint2bcd(msf->cdmsf_frame1);
+
+            stuffp->stop.dt.minute = msf->cdmsf_min1;
+            stuffp->stop.dt.second = msf->cdmsf_sec1;
+            stuffp->stop.dt.frame = msf->cdmsf_frame1;
 
-            msf.cdmsf_min1 = uint2bcd(msf.cdmsf_min1);
-            msf.cdmsf_sec1 = uint2bcd(msf.cdmsf_sec1);
-            msf.cdmsf_frame1 = uint2bcd(msf.cdmsf_frame1);
-
-            stuffp->stop.dt.minute = msf.cdmsf_min1;
-            stuffp->stop.dt.second = msf.cdmsf_sec1;
-            stuffp->stop.dt.frame = msf.cdmsf_frame1;
-
-            return mcdx_playmsf(stuffp, &msf);
+            return mcdx_playmsf(stuffp, msf);
         }
 
         case CDROMRESUME: {
@@ -372,103 +401,70 @@
         }
 
 		case CDROMREADTOCENTRY: {
-			struct cdrom_tocentry entry;
+			struct cdrom_tocentry *entry=(struct cdrom_tocentry *) arg;
 			struct s_subqcode *tp = NULL;
-			int ans;
-
 			xtrace(IOCTL, "ioctl() READTOCENTRY\n");
 
             if (-1 == mcdx_readtoc(stuffp)) return -1;
-
-			if ((ans = verify_area(VERIFY_WRITE, (void *) arg, sizeof(entry)))) return ans;
-			copy_from_user(&entry, (void *) arg, sizeof(entry));
-
-			if (entry.cdte_track == CDROM_LEADOUT)
+			if (entry->cdte_track == CDROM_LEADOUT)
 				tp = &stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1];
-			else if (entry.cdte_track > stuffp->di.n_last
-					|| entry.cdte_track < stuffp->di.n_first) return -EINVAL;
-			else tp = &stuffp->toc[entry.cdte_track - stuffp->di.n_first];
-
-			if (NULL == tp) xwarn("FATAL.\n");
-
-			entry.cdte_adr = tp->control;
-			entry.cdte_ctrl = tp->control >> 4;
-
-			if (entry.cdte_format == CDROM_MSF) {
-				entry.cdte_addr.msf.minute = bcd2uint(tp->dt.minute);
-				entry.cdte_addr.msf.second = bcd2uint(tp->dt.second);
-				entry.cdte_addr.msf.frame = bcd2uint(tp->dt.frame);
-			} else if (entry.cdte_format == CDROM_LBA)
-				entry.cdte_addr.lba = msf2log(&tp->dt);
-			else return -EINVAL;
-
-			copy_to_user((void*) arg, &entry, sizeof(entry));
+			else if (entry->cdte_track > stuffp->di.n_last
+					|| entry->cdte_track < stuffp->di.n_first) return -EINVAL;
+			else tp = &stuffp->toc[entry->cdte_track - stuffp->di.n_first];
 
+			if (NULL == tp) 
+				return -EIO;
+			entry->cdte_adr = tp->control;
+			entry->cdte_ctrl = tp->control >> 4;
+            /* Always return stuff in MSF, and let the Uniform cdrom driver
+                worry about what the user actually wants */
+			entry->cdte_addr.msf.minute = bcd2uint(tp->dt.minute);
+			entry->cdte_addr.msf.second = bcd2uint(tp->dt.second);
+			entry->cdte_addr.msf.frame = bcd2uint(tp->dt.frame);
 			return 0;
 		}
 
 		case CDROMSUBCHNL: {
-			int ans;
-			struct cdrom_subchnl sub;
+			struct cdrom_subchnl *sub= (struct cdrom_subchnl *)arg;
 			struct s_subqcode q;
 
 			xtrace(IOCTL, "ioctl() SUBCHNL\n");
 
-			if ((ans = verify_area(VERIFY_WRITE,
-                    (void*) arg, sizeof(sub)))) return ans;
-
-			copy_from_user(&sub, (void*) arg, sizeof(sub));
-
-			if (-1 == mcdx_requestsubqcode(stuffp, &q, 2)) return -EIO;
+			if (-1 == mcdx_requestsubqcode(stuffp, &q, 2)) 
+				return -EIO;
 
             xtrace(SUBCHNL, "audiostatus: %x\n", stuffp->audiostatus);
-			sub.cdsc_audiostatus = stuffp->audiostatus;
-			sub.cdsc_adr = q.control;
-			sub.cdsc_ctrl = q.control >> 4;
-			sub.cdsc_trk = bcd2uint(q.tno);
-			sub.cdsc_ind = bcd2uint(q.index);
+			sub->cdsc_audiostatus = stuffp->audiostatus;
+			sub->cdsc_adr = q.control;
+			sub->cdsc_ctrl = q.control >> 4;
+			sub->cdsc_trk = bcd2uint(q.tno);
+			sub->cdsc_ind = bcd2uint(q.index);
 
             xtrace(SUBCHNL, "trk %d, ind %d\n",
-                    sub.cdsc_trk, sub.cdsc_ind);
-
-			if (sub.cdsc_format == CDROM_LBA) {
-				sub.cdsc_absaddr.lba = msf2log(&q.dt);
-				sub.cdsc_reladdr.lba = msf2log(&q.tt);
-                xtrace(SUBCHNL, "lba: abs %d, rel %d\n",
-                    sub.cdsc_absaddr.lba,
-                    sub.cdsc_reladdr.lba);
-			} else if (sub.cdsc_format == CDROM_MSF) {
-				sub.cdsc_absaddr.msf.minute = bcd2uint(q.dt.minute);
-				sub.cdsc_absaddr.msf.second = bcd2uint(q.dt.second);
-				sub.cdsc_absaddr.msf.frame = bcd2uint(q.dt.frame);
-				sub.cdsc_reladdr.msf.minute = bcd2uint(q.tt.minute);
-				sub.cdsc_reladdr.msf.second = bcd2uint(q.tt.second);
-				sub.cdsc_reladdr.msf.frame = bcd2uint(q.tt.frame);
-                xtrace(SUBCHNL,
-                        "msf: abs %02d:%02d:%02d, rel %02d:%02d:%02d\n",
-                        sub.cdsc_absaddr.msf.minute,
-                        sub.cdsc_absaddr.msf.second,
-                        sub.cdsc_absaddr.msf.frame,
-                        sub.cdsc_reladdr.msf.minute,
-                        sub.cdsc_reladdr.msf.second,
-                        sub.cdsc_reladdr.msf.frame);
-			} else return -EINVAL;
-
-			copy_to_user((void*) arg, &sub, sizeof(sub));
+                    sub->cdsc_trk, sub->cdsc_ind);
+            /* Always return stuff in MSF, and let the Uniform cdrom driver
+                worry about what the user actually wants */
+			sub->cdsc_absaddr.msf.minute = bcd2uint(q.dt.minute);
+			sub->cdsc_absaddr.msf.second = bcd2uint(q.dt.second);
+			sub->cdsc_absaddr.msf.frame = bcd2uint(q.dt.frame);
+			sub->cdsc_reladdr.msf.minute = bcd2uint(q.tt.minute);
+			sub->cdsc_reladdr.msf.second = bcd2uint(q.tt.second);
+			sub->cdsc_reladdr.msf.frame = bcd2uint(q.tt.frame);
+			xtrace(SUBCHNL,
+					"msf: abs %02d:%02d:%02d, rel %02d:%02d:%02d\n",
+					sub->cdsc_absaddr.msf.minute, sub->cdsc_absaddr.msf.second,
+					sub->cdsc_absaddr.msf.frame, sub->cdsc_reladdr.msf.minute,
+					sub->cdsc_reladdr.msf.second, sub->cdsc_reladdr.msf.frame);
 
 			return 0;
 		}
 
 		case CDROMREADTOCHDR: {
-			struct cdrom_tochdr toc;
-			int ans;
+			struct cdrom_tochdr *toc=(struct cdrom_tochdr *) arg;
 
 			xtrace(IOCTL, "ioctl() READTOCHDR\n");
-			if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof toc)))
-				return ans;
-			toc.cdth_trk0 = stuffp->di.n_first;
-			toc.cdth_trk1 = stuffp->di.n_last;
-			copy_to_user((void*) arg, &toc, sizeof toc);
+			toc->cdth_trk0 = stuffp->di.n_first;
+			toc->cdth_trk1 = stuffp->di.n_last;
 			xtrace(TOCHDR, "ioctl() track0 = %d, track1 = %d\n",
 					stuffp->di.n_first, stuffp->di.n_last);
 			return 0;
@@ -485,78 +481,41 @@
 		}
 
 		case CDROMMULTISESSION: {
-			int ans;
-			struct cdrom_multisession ms;
+			struct cdrom_multisession *ms=(struct cdrom_multisession *) arg;
 			xtrace(IOCTL, "ioctl() MULTISESSION\n");
-			if (0 != (ans = verify_area(VERIFY_WRITE, (void*) arg,
-					sizeof(struct cdrom_multisession))))
-				return ans;
-
-			copy_from_user(&ms, (void*) arg, sizeof(struct cdrom_multisession));
-			if (ms.addr_format == CDROM_MSF) {
-				ms.addr.msf.minute = bcd2uint(stuffp->multi.msf_last.minute);
-				ms.addr.msf.second = bcd2uint(stuffp->multi.msf_last.second);
-				ms.addr.msf.frame = bcd2uint(stuffp->multi.msf_last.frame);
-			} else if (ms.addr_format == CDROM_LBA)
-				ms.addr.lba = msf2log(&stuffp->multi.msf_last);
-			else
-				return -EINVAL;
-			ms.xa_flag = !!stuffp->multi.multi;
-
-			copy_to_user((void*) arg, &ms, sizeof(struct cdrom_multisession));
-			if (ms.addr_format == CDROM_MSF) {
-				xtrace(MS,
-						"ioctl() (%d, %02x:%02x.%02x [%02x:%02x.%02x])\n",
-						ms.xa_flag,
-						ms.addr.msf.minute,
-						ms.addr.msf.second,
-						ms.addr.msf.frame,
-						stuffp->multi.msf_last.minute,
-						stuffp->multi.msf_last.second,
-						stuffp->multi.msf_last.frame);
-			} else {
-			    xtrace(MS,
-					"ioctl() (%d, 0x%08x [%02x:%02x.%02x])\n",
-					ms.xa_flag,
-					ms.addr.lba,
-					stuffp->multi.msf_last.minute,
-					stuffp->multi.msf_last.second,
-					stuffp->multi.msf_last.frame);
-			  }
+			/* Always return stuff in LBA, and let the Uniform cdrom driver
+				worry about what the user actually wants */
+			ms->addr.lba = msf2log(&stuffp->multi.msf_last);
+			ms->xa_flag = !!stuffp->multi.multi;
+			xtrace(MS, "ioctl() (%d, 0x%08x [%02x:%02x.%02x])\n",
+				ms->xa_flag, ms->addr.lba, stuffp->multi.msf_last.minute,
+                    stuffp->multi.msf_last.second,stuffp->multi.msf_last.frame);
+ 
 			return 0;
 		}
 
 		case CDROMEJECT: {
 			xtrace(IOCTL, "ioctl() EJECT\n");
 			if (stuffp->users > 1) return -EBUSY;
-			if (-1 == mcdx_eject(stuffp, 1)) return -EIO;
-			return 0;
+			return(mcdx_tray_move(cdi, 1));
 		}
 
-        case CDROMEJECT_SW: {
-            stuffp->eject_sw = arg;
-            return 0;
+        case CDROMCLOSETRAY: {
+            xtrace(IOCTL, "ioctl() CDROMCLOSETRAY\n");
+            return(mcdx_tray_move(cdi, 0));
         }
 
         case CDROMVOLCTRL: {
-            int ans;
-            struct cdrom_volctrl volctrl;
-
+			struct cdrom_volctrl *volctrl=(struct cdrom_volctrl *)arg;
             xtrace(IOCTL, "ioctl() VOLCTRL\n");
-            if ((ans = verify_area(
-                    VERIFY_READ,
-                    (void*) arg,
-                    sizeof(volctrl))))
-                return ans;
 
-            copy_from_user(&volctrl, (char *) arg, sizeof(volctrl));
 #if 0		/* not tested! */
 			/* adjust for the weirdness of workman (md) */
 			/* can't test it (hs) */
 			volctrl.channel2 = volctrl.channel1;
 			volctrl.channel1 = volctrl.channel3 = 0x00;
 #endif
-            return mcdx_setattentuator(stuffp, &volctrl, 2);
+            return mcdx_setattentuator(stuffp, volctrl, 2);
         }
 
 		default:
@@ -645,19 +604,12 @@
     goto again;
 }
 
-static int
-mcdx_open(struct inode *ip, struct file *fp)
-/*  actions done on open:
- *  1)  get the drives status
- *  2)  set the stuffp.readcmd if a CD is in.
- *  (return no error if no CD is found, since ioctl()
- *  needs an opened device */
+static int 
+mcdx_open(struct cdrom_device_info * cdi, int purpose)
 {
     struct s_drive_stuff *stuffp;
-
 	xtrace(OPENCLOSE, "open()\n");
-
-    stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
+    stuffp = mcdx_stuffp[MINOR(cdi->dev)];
     if (!stuffp->present) return -ENXIO;
 
 	/* Make the modules looking used ... (thanx bjorn).
@@ -665,248 +617,154 @@
 	 * on error return */
 	MOD_INC_USE_COUNT;
 
-#if 0
-	/* We don't allow multiple users of a drive.  In case of data CDs
-     * they'll be used by mounting, which ensures anyway exclusive
-	 * usage. In case of audio CDs it's meaningless to try playing to
-	 * different tracks at once! (md)
-	 * - Hey, what about cat /dev/cdrom? Why shouldn't it called by
-	 * more then one process at any time?  (hs) */
-	if (stuffp->users) {
-		MOD_DEC_USE_COUNT;
-		return -EBUSY;
-	}
-#endif
-
     /* this is only done to test if the drive talks with us */
     if (-1 == mcdx_getstatus(stuffp, 1)) {
 		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
 
-	/* close the door,
-	 * This should be explained ...
-	 * - If the door is open and its last close is too recent the
-	 *   autoclose wouldn't probably be what we want.
-	 * - If we didn't try to close the door yet, close it and go on.
-	 * - If we autoclosed the door and couldn't succeed in find a valid
-	 *   CD we shouldn't try autoclose any longer (until a valid CD is
-	 *   in.) */
-
-	if (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_DOOR) {
-        if (jiffies - stuffp->ejected < ACLOSE_INHIBIT) {
-			MOD_DEC_USE_COUNT;
-			return -EIO;
-		}
-        if (stuffp->autoclose) mcdx_closedoor(stuffp, 1);
-		else {
-			MOD_DEC_USE_COUNT;
-			return -EIO;
-		}
-    }
-
-	/* if the media changed we will have to do a little more */
-	if (stuffp->xxx) {
-
-		xtrace(OPENCLOSE, "open() media changed\n");
-        /* but wait - the time of media change will be set at the
-         * very last of this block - it seems, some of the following
-         * talk() will detect a media change ... (I think, config()
-         * is the reason. */
+    if (stuffp->xxx) {
 
+        xtrace(OPENCLOSE, "open() media changed\n");
         stuffp->audiostatus = CDROM_AUDIO_INVALID;
-		stuffp->readcmd = 0;
-
-		/* get the multisession information */
-		xtrace(OPENCLOSE, "open() Request multisession info\n");
-		if (-1 == mcdx_requestmultidiskinfo(
-				stuffp, &stuffp->multi, 6)) {
-			xinfo("No multidiskinfo\n");
-			stuffp->autoclose = 0;
-			/*
-			MOD_DEC_USE_COUNT;
-			stuffp->xxx = 0;
-			return -EIO;
-			*/
-		} else {
-			/* we succeeded, so on next open(2) we could try auto close
-			 * again */
-			stuffp->autoclose = 1;
-
-#if !MCDX_QUIET
-			if (stuffp->multi.multi > 2)
-				xinfo("open() unknown multisession value (%d)\n",
-						stuffp->multi.multi);
-#endif
-
-			/* multisession ? */
-			if (!stuffp->multi.multi)
-				stuffp->multi.msf_last.second = 2;
-
-			xtrace(OPENCLOSE, "open() MS: %d, last @ %02x:%02x.%02x\n",
-					stuffp->multi.multi,
-					stuffp->multi.msf_last.minute,
-					stuffp->multi.msf_last.second,
-					stuffp->multi.msf_last.frame);
-
-		{ ; } /* got multisession information */
-
-		/* request the disks table of contents (aka diskinfo) */
-		if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) {
-
-			stuffp->lastsector = -1;
-
+        stuffp->readcmd = 0;
+        xtrace(OPENCLOSE, "open() Request multisession info\n");
+        if (-1 == mcdx_requestmultidiskinfo( stuffp, &stuffp->multi, 6))
+            	xinfo("No multidiskinfo\n");
 		} else {
+            /* multisession ? */
+            if (!stuffp->multi.multi)
+                stuffp->multi.msf_last.second = 2;
+
+            xtrace(OPENCLOSE, "open() MS: %d, last @ %02x:%02x.%02x\n",
+                    stuffp->multi.multi,
+                    stuffp->multi.msf_last.minute,
+                    stuffp->multi.msf_last.second,
+                    stuffp->multi.msf_last.frame);
+
+        { ; } /* got multisession information */
+        /* request the disks table of contents (aka diskinfo) */
+        if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) {
+
+            stuffp->lastsector = -1;
+
+        } else {
+
+            stuffp->lastsector = (CD_FRAMESIZE / 512)
+                    * msf2log(&stuffp->di.msf_leadout) - 1;
+
+            xtrace(OPENCLOSE, "open() start %d (%02x:%02x.%02x) %d\n",
+                    stuffp->di.n_first,
+                    stuffp->di.msf_first.minute,
+                    stuffp->di.msf_first.second,
+                    stuffp->di.msf_first.frame,
+                    msf2log(&stuffp->di.msf_first));
+            xtrace(OPENCLOSE, "open() last %d (%02x:%02x.%02x) %d\n",
+                    stuffp->di.n_last,
+                    stuffp->di.msf_leadout.minute,
+                    stuffp->di.msf_leadout.second,
+                    stuffp->di.msf_leadout.frame,
+                    msf2log(&stuffp->di.msf_leadout));
+        }
+      
+        if (stuffp->toc) {
+            xtrace(MALLOC, "open() free old toc @ %p\n", stuffp->toc);
+            kfree(stuffp->toc);
 
-			stuffp->lastsector = (CD_FRAMESIZE / 512)
-					* msf2log(&stuffp->di.msf_leadout) - 1;
-
-			xtrace(OPENCLOSE, "open() start %d (%02x:%02x.%02x) %d\n",
-					stuffp->di.n_first,
-					stuffp->di.msf_first.minute,
-					stuffp->di.msf_first.second,
-					stuffp->di.msf_first.frame,
-					msf2log(&stuffp->di.msf_first));
-			xtrace(OPENCLOSE, "open() last %d (%02x:%02x.%02x) %d\n",
-					stuffp->di.n_last,
-					stuffp->di.msf_leadout.minute,
-					stuffp->di.msf_leadout.second,
-					stuffp->di.msf_leadout.frame,
-					msf2log(&stuffp->di.msf_leadout));
-		}
-
-		if (stuffp->toc) {
-			xtrace(MALLOC, "open() free old toc @ %p\n", stuffp->toc);
-			kfree(stuffp->toc);
-
-			stuffp->toc = NULL;
-		}
+            stuffp->toc = NULL;
+        }
 
-		xtrace(OPENCLOSE, "open() init irq generation\n");
-		if (-1 == mcdx_config(stuffp, 1)) {
-			MOD_DEC_USE_COUNT;
-			return -EIO;
-		}
+        xtrace(OPENCLOSE, "open() init irq generation\n");
+        if (-1 == mcdx_config(stuffp, 1)) {
+            MOD_DEC_USE_COUNT;
+            return -EIO;
+        }
 
 #if FALLBACK
-		/* Set the read speed */
-		xwarn("AAA %x AAA\n", stuffp->readcmd);
-		if (stuffp->readerrs) stuffp->readcmd = READ1X;
-		else stuffp->readcmd =
-				stuffp->present | SINGLE ? READ1X : READ2X;
-		xwarn("XXX %x XXX\n", stuffp->readcmd);
+        /* Set the read speed */
+        xwarn("AAA %x AAA\n", stuffp->readcmd);
+        if (stuffp->readerrs) stuffp->readcmd = READ1X;
+        else stuffp->readcmd =
+                stuffp->present | SINGLE ? READ1X : READ2X;
+        xwarn("XXX %x XXX\n", stuffp->readcmd);
 #else
-		stuffp->readcmd = stuffp->present | SINGLE ? READ1X : READ2X;
+        stuffp->readcmd = stuffp->present | SINGLE ? READ1X : READ2X;
 #endif
 
-		/* try to get the first sector, iff any ... */
-		if (stuffp->lastsector >= 0) {
-			char buf[512];
-			int ans;
-			int tries;
-
-			stuffp->xa = 0;
-			stuffp->audio = 0;
-
-			for (tries = 6; tries; tries--) {
-
-				stuffp->introk = 1;
-
-				xtrace(OPENCLOSE, "open() try as %s\n",
-					stuffp->xa ? "XA" : "normal");
-
-				/* set data mode */
-				if (-1 == (ans = mcdx_setdatamode(stuffp,
-						stuffp->xa ? MODE2 : MODE1, 1))) {
-					/* MOD_DEC_USE_COUNT, return -EIO; */
-					stuffp->xa = 0;
-					break;
-				}
+        /* try to get the first sector, iff any ... */
+        if (stuffp->lastsector >= 0) {
+            char buf[512];
+            int ans;
+            int tries;
 
-				if ((stuffp->audio = e_audio(ans))) break;
+            stuffp->xa = 0;
+            stuffp->audio = 0;
 
-				while (0 == (ans = mcdx_transfer(stuffp, buf, 0, 1)))
-					;
+            for (tries = 6; tries; tries--) {
 
-				if (ans == 1) break;
-				stuffp->xa = !stuffp->xa;
-			}
-			/* if (!tries) MOD_DEC_USE_COUNT, return -EIO; */
-		}
+                stuffp->introk = 1;
 
-		/* xa disks will be read in raw mode, others not */
-		if (-1 == mcdx_setdrivemode(stuffp,
-				stuffp->xa ? RAW : COOKED, 1)) {
-			MOD_DEC_USE_COUNT;
-			return -EIO;
-		}
+                xtrace(OPENCLOSE, "open() try as %s\n",
+                    stuffp->xa ? "XA" : "normal");
+                /* set data mode */
+                if (-1 == (ans = mcdx_setdatamode(stuffp,
+                        stuffp->xa ? MODE2 : MODE1, 1))) {
+                    /* MOD_DEC_USE_COUNT, return -EIO; */
+                    stuffp->xa = 0;
+                    break;
+                }
 
-		if (stuffp->audio) {
-			xinfo("open() audio disk found\n");
-		} else if (stuffp->lastsector >= 0) {
-			xinfo("open() %s%s disk found\n",
-					stuffp->xa ? "XA / " : "",
-					stuffp->multi.multi ? "Multi Session" : "Single Session");
-		}
+                if ((stuffp->audio = e_audio(ans))) break;
 
-        /* stuffp->xxx = 0; */
-	}
-
-    /* lock the door if not already done */
-    if (0 == stuffp->users && (-1 == mcdx_lockdoor(stuffp, 1, 1)))  {
-		MOD_DEC_USE_COUNT;
-        return -EIO;
-	}
-	}
+                while (0 == (ans = mcdx_transfer(stuffp, buf, 0, 1)))
+                    ;
 
+                if (ans == 1) break;
+                stuffp->xa = !stuffp->xa;
+            }
+        }
+        /* xa disks will be read in raw mode, others not */
+        if (-1 == mcdx_setdrivemode(stuffp,
+                stuffp->xa ? RAW : COOKED, 1)) {
+            MOD_DEC_USE_COUNT;
+            return -EIO;
+        }
+        if (stuffp->audio) {
+            xinfo("open() audio disk found\n");
+        } else if (stuffp->lastsector >= 0) {
+            xinfo("open() %s%s disk found\n",
+                    stuffp->xa ? "XA / " : "",
+                    stuffp->multi.multi ? "Multi Session" : "Single Session");
+        }
+    }
 	stuffp->xxx = 0;
     stuffp->users++;
     return 0;
-
 }
 
-static int
-mcdx_close(struct inode *ip, struct file *fp)
+static void mcdx_close(struct cdrom_device_info * cdi)
 {
     struct s_drive_stuff *stuffp;
 
     xtrace(OPENCLOSE, "close()\n");
 
-    stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
-
-    if (0 == --stuffp->users) {
-		sync_dev(ip->i_rdev);	/* needed for r/o device? */
-
-		/* invalidate_inodes(ip->i_rdev); */
-		invalidate_buffers(ip->i_rdev);
-
-
-#if !MCDX_QUIET
-		if (-1 == mcdx_lockdoor(stuffp, 0, 3))
-				xinfo("close() Cannot unlock the door\n");
-#else
-		mcdx_lockdoor(stuffp, 0, 3);
-#endif
-
-        /* eject if wished */
-        if (stuffp->eject_sw) mcdx_eject(stuffp, 1);
+    stuffp = mcdx_stuffp[MINOR(cdi->dev)];
 
-    }
+    --stuffp->users;
 
     MOD_DEC_USE_COUNT;
-    return 0;
 }
 
-int check_mcdx_media_change(kdev_t full_dev)
+static int mcdx_media_changed(struct cdrom_device_info * cdi, int disc_nr) 
 /*	Return: 1 if media changed since last call to this function
 			0 otherwise */
 {
     struct s_drive_stuff *stuffp;
 
-    xinfo("check_mcdx_media_change called for device %s\n",
-	  kdevname(full_dev));
+    xinfo("mcdx_media_changed called for device %s\n",
+	  kdevname(cdi->dev));
 
-	stuffp = mcdx_stuffp[MINOR(full_dev)];
+	stuffp = mcdx_stuffp[MINOR(cdi->dev)];
 	mcdx_getstatus(stuffp, 1);
 
 	if (stuffp->yyy == 0) return 0;
@@ -1140,6 +998,10 @@
 
     for (i = 0; i < MCDX_NDRIVES; i++) {
 		struct s_drive_stuff *stuffp;
+        if (unregister_cdrom(&mcdx_info)) {
+			printk(KERN_WARNING "Can't unregister cdrom mcdx\n");
+			return;
+		}
 		stuffp = mcdx_stuffp[i];
 		if (!stuffp) continue;
 		release_region((unsigned long) stuffp->wreg_data, MCDX_IO_SIZE);
@@ -1153,7 +1015,7 @@
 		kfree(stuffp);
     }
 
-    if (unregister_blkdev(MAJOR_NR, DEVICE_NAME) != 0) {
+    if (unregister_blkdev(MAJOR_NR, "mcdx") != 0) {
         xwarn("cleanup() unregister_blkdev() failed\n");
     }
 #if !MCDX_QUIET
@@ -1168,7 +1030,7 @@
 __initfunc(int mcdx_init(void))
 {
 	int drive;
-
+	char msg[80];
 #ifdef MODULE
 	xwarn("Version 2.14(hs) for " UTS_RELEASE "\n");
 #else
@@ -1204,7 +1066,6 @@
 
 		/* set default values */
 		memset(stuffp, 0, sizeof(*stuffp));
-        stuffp->autoclose = 1;      /* close the door on open(2) */
 
 		stuffp->present = 0;		/* this should be 0 already */
 		stuffp->toc = NULL;			/* this should be NULL already */
@@ -1274,7 +1135,7 @@
 		}
 
 		xtrace(INIT, "init() register blkdev\n");
-		if (register_blkdev(MAJOR_NR, DEVICE_NAME, &mcdx_fops) != 0) {
+		if (register_blkdev(MAJOR_NR, "mcdx", &cdrom_fops) != 0) {
             xwarn("%s=0x%3p,%d: Init failed. Can't get major %d.\n",
                     MCDX,
                     stuffp->wreg_data, stuffp->irq, MAJOR_NR);
@@ -1289,7 +1150,7 @@
 
 		xtrace(INIT, "init() subscribe irq and i/o\n");
 		mcdx_irq_map[stuffp->irq] = stuffp;
-		if (request_irq(stuffp->irq, mcdx_intr, SA_INTERRUPT, DEVICE_NAME, NULL)) {
+		if (request_irq(stuffp->irq, mcdx_intr, SA_INTERRUPT, "mcdx", NULL)) {
             xwarn("%s=0x%3p,%d: Init failed. Can't get irq (%d).\n",
                     MCDX,
                     stuffp->wreg_data, stuffp->irq, stuffp->irq);
@@ -1299,7 +1160,7 @@
 		}
 		request_region((unsigned int) stuffp->wreg_data,
                 MCDX_IO_SIZE,
-                DEVICE_NAME);
+                "mcdx");
 
 		xtrace(INIT, "init() get garbage\n");
 		{
@@ -1318,15 +1179,23 @@
 
 		stuffp->minor = drive;
 
-		xwarn("(%s) installed at 0x%3p, irq %d."
-			   " (Firmware version %c %x)\n",
-			   DEVICE_NAME,
+		sprintf(msg, " mcdx: Mitsumi CD-ROM installed at 0x%3p, irq %d."
+			   " (Firmware version %c %x)\n", 
 			   stuffp->wreg_data, stuffp->irq, version.code,
                version.ver);
 		mcdx_stuffp[drive] = stuffp;
 		xtrace(INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp);
+        if (register_cdrom(&mcdx_info) != 0) {
+              printk("Cannot register Mitsumi CD-ROM!\n");
+        	  release_region((unsigned long) stuffp->wreg_data, MCDX_IO_SIZE);
+        	  free_irq(stuffp->irq, NULL);
+              kfree(stuffp);
+              if (unregister_blkdev(MAJOR_NR, "mcdx") != 0)
+        		xwarn("cleanup() unregister_blkdev() failed\n");
+			  return -EIO;
+        }
+        printk(msg);
 	}
-
 	return 0;
 }
 
@@ -1539,7 +1408,7 @@
 
 static void log2msf(unsigned int l, struct cdrom_msf0* pmsf)
 {
-    l += CD_BLOCK_OFFSET;
+    l += CD_MSF_OFFSET;
     pmsf->minute = uint2bcd(l / 4500), l %= 4500;
     pmsf->second = uint2bcd(l / 75);
     pmsf->frame = uint2bcd(l % 75);
@@ -1550,7 +1419,7 @@
     return bcd2uint(pmsf->frame)
     + bcd2uint(pmsf->second) * 75
     + bcd2uint(pmsf->minute) * 4500
-    - CD_BLOCK_OFFSET;
+    - CD_MSF_OFFSET;
 }
 
 int mcdx_readtoc(struct s_drive_stuff* stuffp)
@@ -1712,12 +1581,20 @@
 /* Drive functions ************************************************/
 
 static int
-mcdx_closedoor(struct s_drive_stuff *stuffp, int tries)
+mcdx_tray_move(struct cdrom_device_info * cdi, int position)
 {
-	if (stuffp->present & DOOR)
-		return mcdx_talk(stuffp, "\xf8", 1, NULL, 1, 5 * HZ, tries);
-	else
-		return 0;
+    struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(cdi->dev)];
+
+    if (!stuffp->present) 
+		return -ENXIO;
+	if (!(stuffp->present & DOOR))
+		return -ENOSYS; 
+
+ 	if (position)                /* 1: eject */
+        	return mcdx_talk(stuffp, "\xf6", 1, NULL, 1, 5 * HZ, 3);
+	else /* 0: close */
+			return mcdx_talk(stuffp, "\xf8", 1, NULL, 1, 5 * HZ, 3);
+    return 1;
 }
 
 static int
@@ -1729,15 +1606,6 @@
 { return mcdx_talk(stuffp, "\x70", 1, NULL, 1, 2 * HZ, tries); }
 
 static int
-mcdx_eject(struct s_drive_stuff *stuffp, int tries)
-{
-	if (stuffp->present & DOOR) {
-        stuffp->ejected = jiffies;
-		return mcdx_talk(stuffp, "\xf6", 1, NULL, 1, 5 * HZ, tries);
-    } else return 0;
-}
-
-static int
 mcdx_requestsubqcode(struct s_drive_stuff *stuffp,
         struct s_subqcode *sub,
         int tries)
@@ -1886,13 +1754,16 @@
 	} else return mcdx_talk(stuffp, "\x60", 1, NULL, 1, 5 * HZ, tries);
 }
 
-static int
-mcdx_lockdoor(struct s_drive_stuff *stuffp, int lock, int tries)
+static int mcdx_lockdoor(struct cdrom_device_info * cdi, int lock)
 {
+    struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(cdi->dev)];
 	char cmd[2] = { 0xfe };
+
+    if (!(stuffp->present & DOOR))
+		return -ENOSYS;
     if (stuffp->present & DOOR) {
         cmd[1] = lock ? 0x01 : 0x00;
-        return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 1, 5 * HZ, tries);
+        return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 1, 5 * HZ, 3);
     } else return 0;
 }
 
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/mcdx.h linux/drivers/cdrom/mcdx.h
--- v2.1.69/linux/drivers/cdrom/mcdx.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/cdrom/mcdx.h	Tue Dec  2 11:41:44 1997
@@ -0,0 +1,184 @@
+/*
+ * Definitions for the Mitsumi CDROM interface
+ * Copyright (C) 1995 1996 Heiko Schlittermann <heiko@lotte.sax.de>
+ * VERSION: @VERSION@
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Thanks to
+ *  The Linux Community at all and ...
+ *  Martin Harris (he wrote the first Mitsumi Driver)
+ *  Eberhard Moenkeberg (he gave me much support and the initial kick)
+ *  Bernd Huebner, Ruediger Helsch (Unifix-Software Gmbh, they
+ *      improved the original driver)
+ *  Jon Tombs, Bjorn Ekwall (module support)
+ *  Daniel v. Mosnenck (he sent me the Technical and Programming Reference)
+ *  Gerd Knorr (he lent me his PhotoCD)
+ *  Nils Faerber and Roger E. Wolff (extensively tested the LU portion)
+ *  Andreas Kies (testing the mysterious hang up's)
+ *  ... somebody forgotten?
+ *  Marcin Dalecki
+ *  
+ */
+
+/*
+ *	The following lines are for user configuration
+ *	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *	{0|1} -- 1 if you want the driver detect your drive, may crash and
+ *	needs a long time to seek.  The higher the address the longer the
+ *	seek.
+ *
+ *  WARNING: AUTOPROBE doesn't work.
+ */
+#define MCDX_AUTOPROBE 0
+
+/*
+ *	Drive specific settings according to the jumpers on the controller
+ *	board(s).
+ *	o	MCDX_NDRIVES  :  number of used entries of the following table
+ *	o	MCDX_DRIVEMAP :  table of {i/o base, irq} per controller
+ *
+ *	NOTE: I didn't get a drive at irq 9(2) working.  Not even alone.
+ */
+#if MCDX_AUTOPROBE == 0
+	#define MCDX_NDRIVES 1
+	#define MCDX_DRIVEMAP {		\
+			{0x300, 11},	\
+			{0x304, 05},  	\
+			{0x000, 00},  	\
+			{0x000, 00},  	\
+			{0x000, 00},  	\
+	  	}
+#else
+	#error Autoprobing is not implemented yet.
+#endif
+
+#ifndef MCDX_QUIET
+#define MCDX_QUIET   1
+#endif
+
+#ifndef MCDX_DEBUG
+#define MCDX_DEBUG   0
+#endif
+
+/* *** make the following line uncommented, if you're sure,
+ * *** all configuration is done */
+/* #define I_WAS_HERE */
+#define I_WAS_HERE   /* delete this line, it's for heiko only */
+
+/*	The name of the device */
+#define MCDX "mcdx"	
+
+/* Flags for DEBUGGING */
+#define INIT 		0
+#define MALLOC 		0
+#define IOCTL 		0
+#define PLAYTRK     0
+#define SUBCHNL     0
+#define TOCHDR      0
+#define MS          0
+#define PLAYMSF     0
+#define READTOC     0
+#define OPENCLOSE 	0
+#define HW		    0
+#define TALK		0
+#define IRQ 		0
+#define XFER 		0
+#define REQUEST	 	0
+#define SLEEP		0
+
+/*	The following addresses are taken from the Mitsumi Reference 
+ *  and describe the possible i/o range for the controller.
+ */
+#define MCDX_IO_BEGIN	((char*) 0x300)	/* first base of i/o addr */
+#define MCDX_IO_END		((char*) 0x3fc)	/* last base of i/o addr */
+
+/*	Per controller 4 bytes i/o are needed. */
+#define MCDX_IO_SIZE		4
+
+/*
+ *	Bits
+ */
+
+/* The status byte, returned from every command, set if
+ * the description is true */
+#define MCDX_RBIT_OPEN       0x80	/* door is open */
+#define MCDX_RBIT_DISKSET    0x40	/* disk set (recognised) */
+#define MCDX_RBIT_CHANGED    0x20	/* disk was changed */
+#define MCDX_RBIT_CHECK      0x10	/* disk rotates, servo is on */
+#define MCDX_RBIT_AUDIOTR    0x08   /* current track is audio */
+#define MCDX_RBIT_RDERR      0x04	/* read error, refer SENSE KEY */
+#define MCDX_RBIT_AUDIOBS    0x02	/* currently playing audio */
+#define MCDX_RBIT_CMDERR     0x01	/* command, param or format error */
+
+/* The I/O Register holding the h/w status of the drive,
+ * can be read at i/o base + 1 */
+#define MCDX_RBIT_DOOR       0x10	/* door is open */
+#define MCDX_RBIT_STEN       0x04	/* if 0, i/o base contains drive status */
+#define MCDX_RBIT_DTEN       0x02	/* if 0, i/o base contains data */
+
+/*
+ *	The commands.
+ */
+
+#define OPCODE	1		/* offset of opcode */
+#define MCDX_CMD_REQUEST_TOC		1, 0x10
+#define MCDX_CMD_REQUEST_STATUS		1, 0x40 
+#define MCDX_CMD_RESET				1, 0x60
+#define MCDX_CMD_REQUEST_DRIVE_MODE	1, 0xc2
+#define MCDX_CMD_SET_INTERLEAVE		2, 0xc8, 0
+#define MCDX_CMD_DATAMODE_SET		2, 0xa0, 0
+	#define MCDX_DATAMODE1		0x01
+	#define MCDX_DATAMODE2		0x02
+#define MCDX_CMD_LOCK_DOOR		2, 0xfe, 0
+
+#define READ_AHEAD			4	/* 8 Sectors (4K) */
+
+/*	Useful macros */
+#define e_door(x)		((x) & MCDX_RBIT_OPEN)
+#define e_check(x)		(~(x) & MCDX_RBIT_CHECK)
+#define e_notset(x)		(~(x) & MCDX_RBIT_DISKSET)
+#define e_changed(x)	((x) & MCDX_RBIT_CHANGED)
+#define e_audio(x)		((x) & MCDX_RBIT_AUDIOTR)
+#define e_audiobusy(x)	((x) & MCDX_RBIT_AUDIOBS)
+#define e_cmderr(x)		((x) & MCDX_RBIT_CMDERR)
+#define e_readerr(x)	((x) & MCDX_RBIT_RDERR)
+
+/**	no drive specific */
+#define MCDX_CDBLK	2048	/* 2048 cooked data each blk */
+
+#define MCDX_DATA_TIMEOUT	(HZ/10)	/* 0.1 second */
+
+/*
+ * Access to the msf array
+ */
+#define MSF_MIN		0			/* minute */
+#define MSF_SEC		1			/* second */
+#define MSF_FRM		2			/* frame  */
+
+/*
+ * Errors
+ */
+#define MCDX_E		1			/* unspec error */
+#define MCDX_ST_EOM 0x0100		/* end of media */
+#define MCDX_ST_DRV 0x00ff		/* mask to query the drive status */
+
+#ifndef I_WAS_HERE
+#warning You have not edited mcdx.h
+#warning Perhaps irq and i/o settings are wrong.
+#endif
+
+/* ex:set ts=4 sw=4: */
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/optcd.c linux/drivers/cdrom/optcd.c
--- v2.1.69/linux/drivers/cdrom/optcd.c	Tue May 13 22:41:06 1997
+++ linux/drivers/cdrom/optcd.c	Tue Dec  2 11:41:44 1997
@@ -72,7 +72,7 @@
 #include <linux/blk.h>
 
 #include <linux/cdrom.h>
-#include <linux/optcd.h>
+#include "optcd.h"
 
 #include <asm/uaccess.h>
 
@@ -1838,7 +1838,7 @@
 	case CDROMMULTISESSION:	retval = cdrommultisession(arg); break;
 #endif
 
-	case CDROM_GET_UPC:	retval = -EINVAL; break; /* not implemented */
+	case CDROM_GET_MCN:	retval = -EINVAL; break; /* not implemented */
 	case CDROMVOLREAD:	retval = -EINVAL; break; /* not implemented */
 
 	case CDROMREADRAW:
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/optcd.h linux/drivers/cdrom/optcd.h
--- v2.1.69/linux/drivers/cdrom/optcd.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/cdrom/optcd.h	Tue Dec  2 11:41:44 1997
@@ -0,0 +1,52 @@
+/*	linux/include/linux/optcd.h - Optics Storage 8000 AT CDROM driver
+	$Id: optcd.h,v 1.2 1996/01/15 18:43:44 root Exp root $
+
+	Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl)
+
+
+	Configuration file for linux/drivers/cdrom/optcd.c
+*/
+
+#ifndef _LINUX_OPTCD_H
+#define _LINUX_OPTCD_H
+
+
+/* I/O base of drive. Drive uses base to base+2.
+   This setting can be overridden with the kernel or insmod command
+   line option 'optcd=<portbase>'. Use address of 0 to disable driver. */
+#define OPTCD_PORTBASE	0x340
+
+
+/* enable / disable parts of driver by define / undef */
+#define	MULTISESSION		/* multisession support (ALPHA) */
+
+
+/* Change 0 to 1 to debug various parts of the driver */
+#define	DEBUG_DRIVE_IF	0	/* Low level drive interface */
+#define	DEBUG_CONV	0	/* Address conversions */
+#define	DEBUG_BUFFERS	0	/* Buffering and block size conversion */
+#define	DEBUG_REQUEST	0	/* Request mechanism */
+#define	DEBUG_STATE	0	/* State machine */
+#define	DEBUG_TOC	0	/* Q-channel and Table of Contents */
+#define	DEBUG_MULTIS	0	/* Multisession code */
+#define	DEBUG_VFS	0	/* VFS interface */
+
+
+/* Don't touch these unless you know what you're doing. */
+
+/* Various timeout loop repetition counts. */
+#define BUSY_TIMEOUT		10000000	/* for busy wait */
+#define FAST_TIMEOUT		100000		/* ibid. for probing */
+#define SLEEP_TIMEOUT		6000		/* for timer wait */
+#define MULTI_SEEK_TIMEOUT	1000		/* for timer wait */
+#define READ_TIMEOUT		6000		/* for poll wait */
+#define STOP_TIMEOUT		2000		/* for poll wait */
+#define RESET_WAIT		5000		/* busy wait at drive reset */
+
+/* # of buffers for block size conversion. 6 is optimal for my setup (P75),
+   giving 280 kb/s, with 0.4% CPU usage. Experiment to find your optimal
+   setting */
+#define N_BUFS		6
+
+
+#endif _LINUX_OPTCD_H
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/sbpcd.c linux/drivers/cdrom/sbpcd.c
--- v2.1.69/linux/drivers/cdrom/sbpcd.c	Tue May 13 22:41:06 1997
+++ linux/drivers/cdrom/sbpcd.c	Tue Dec  2 11:41:44 1997
@@ -13,7 +13,7 @@
  *             labelled E2550UA or MK4015 or 2800F).
  */
 
-#define VERSION "v4.6 Eberhard Moenkeberg <emoenke@gwdg.de>"
+#define VERSION "v4.61 Eberhard Moenkeberg <emoenke@gwdg.de>"
 
 /*   Copyright (C) 1993, 1994, 1995  Eberhard Moenkeberg <emoenke@gwdg.de>
  *
@@ -300,6 +300,10 @@
  *       Experiments to speed up the CD-55A; again with help of Rob Riggs
  *       (to be true, he gave both, idea & code. ;-)
  *
+ *  4.61 Ported to Uniform CD-ROM driver by 
+ *       Heiko Eissfeldt <heiko@colossus.escape.de> with additional
+ *       changes by Erik Andersen <andersee@debian.org>
+ *
  *
  *  TODO
  *     implement "read all subchannel data" (96 bytes per frame)
@@ -334,8 +338,8 @@
 #include <asm/io.h>
 #include <asm/uaccess.h>
 #include <stdarg.h>
-#include <linux/sbpcd.h>
 #include <linux/config.h>
+#include "sbpcd.h"
 
 #if !(SBPCD_ISSUE-1)
 #define MAJOR_NR MATSUSHITA_CDROM_MAJOR
@@ -404,6 +408,7 @@
 	CDROM_PORT, SBPRO, /* probe with user's setup first */
 #if DISTRIBUTION
 	0x230, 1, /* Soundblaster Pro and 16 (default) */
+#if 0
 	0x300, 0, /* CI-101P (default), WDH-7001C (default),
 		     Galaxy (default), Reveal (one default) */
 	0x250, 1, /* OmniCD default, Soundblaster Pro and 16 */
@@ -438,6 +443,7 @@
 	0x290, 1, /* Soundblaster 16 */
 	0x310, 0, /* Lasermate, CI-101P, WDH-7001C */
 #endif MODULE
+#endif
 #endif DISTRIBUTION
 };
 #else
@@ -523,7 +529,7 @@
 #if DISTRIBUTION
 static int sbpcd_debug = (1<<DBG_INF);
 #else
-static int sbpcd_debug = ((1<<DBG_INF) |
+static int sbpcd_debug = 0 & ((1<<DBG_INF) |
 			  (1<<DBG_TOC) |
 			  (1<<DBG_MUL) |
 			  (1<<DBG_UPC));
@@ -558,7 +564,7 @@
 static const char *str_ss_l = "soundscape";
 static const char *str_t16 = "Teac16bit";
 static const char *str_t16_l = "teac16bit";
-const char *type;
+static const char *type;
 
 #if !(SBPCD_ISSUE-1)
 static const char *major_name="sbpcd";
@@ -736,7 +742,8 @@
 	u_char mode_yb_7;
 	u_char mode_xb_8;
 	u_char delay;
-	
+	struct cdrom_device_info *sbpcd_infop;
+
 } D_S[NR_SBPCD];
 
 /*
@@ -1990,6 +1997,7 @@
 		if (D_S[d].error_byte!=aud_12) return -501;
 	return (0);
 }
+
 /*==========================================================================*/
 static int SetSpeed(void)
 {
@@ -2005,6 +2013,19 @@
 	i=cc_SetSpeed(speed,0,0);
 	return (i);
 }
+
+static void switch_drive(int i);
+
+static int sbpcd_select_speed(struct cdrom_device_info *cdi, int speed)
+{
+  int i = MINOR(cdi->dev);
+
+  if (i != d)
+    switch_drive(i);
+
+  return cc_SetSpeed(speed == 2 ? speed_300 : speed_150, 0, 0);
+}
+
 /*==========================================================================*/
 static int DriveReset(void)
 {
@@ -2030,6 +2051,17 @@
 	}
 	return (0);
 }
+
+static int sbpcd_reset(struct cdrom_device_info *cdi)
+{
+  int i = MINOR(cdi->dev);
+
+  if (i != d)
+    switch_drive(i);
+
+  return DriveReset();
+}
+
 /*==========================================================================*/
 static int cc_PlayAudio(int pos_audio_start,int pos_audio_end)
 {
@@ -2220,6 +2252,12 @@
 	}
 	return (i);
 }
+
+static int sbpcd_lock_door(struct cdrom_device_info *cdi, int lock)
+{
+  return lock ? LockDoor() : UnLockDoor();
+}
+
 /*==========================================================================*/
 static int cc_CloseTray(void)
 {
@@ -2261,6 +2299,12 @@
 	msg(DBG_LCS,"p_door_closed bit %d after\n", st_door_closed);
 	return (i);
 }
+
+static int sbpcd_tray_move(struct cdrom_device_info *cdi, int position)
+{
+  return position ? cc_CloseTray() : 0; 
+}
+
 /*==========================================================================*/
 static int cc_ReadSubQ(void)
 {
@@ -2956,6 +3000,79 @@
 	D_S[d].diskstate_flags |= upc_bit;
 	return (0);
 }
+
+static int sbpcd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn)
+{
+	int i;
+	unsigned char *mcnp = mcn->medium_catalog_number;
+	unsigned char *resp;
+
+	D_S[d].diskstate_flags &= ~upc_bit;
+	clr_cmdbuf();
+	if (fam1_drive)
+	{
+		drvcmd[0]=CMD1_READ_UPC;
+		response_count=8;
+		flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+	}
+	else if (fam0L_drive)
+	{
+		drvcmd[0]=CMD0_READ_UPC;
+		response_count=0;
+		flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+	}
+	else if (fam2_drive)
+	{
+		return (-1);
+	}
+	else if (famT_drive)
+	{
+		return (-1);
+	}
+	i=cmd_out();
+	if (i<0)
+	{
+		msg(DBG_000,"cc_ReadUPC cmd_out: err %d\n", i);
+		return (i);
+	}
+	if (fam0L_drive)
+	{
+		response_count=16;
+		if (famL_drive) flags_cmd_out=f_putcmd;
+		i=cc_ReadPacket();
+		if (i<0)
+		{
+			msg(DBG_000,"cc_ReadUPC ReadPacket: err %d\n", i);
+			return (i);
+		}
+	}
+	D_S[d].UPC_ctl_adr=0;
+	if (fam1_drive) i=0;
+	else i=2;
+
+	resp = infobuf + i;
+	if (*resp++ == 0x80) {
+		/* packed bcd to single ASCII digits */
+		*mcnp++ = (*resp >> 4)     + '0';
+		*mcnp++ = (*resp++ & 0x0f) + '0';
+		*mcnp++ = (*resp >> 4)     + '0';
+		*mcnp++ = (*resp++ & 0x0f) + '0';
+		*mcnp++ = (*resp >> 4)     + '0';
+		*mcnp++ = (*resp++ & 0x0f) + '0';
+		*mcnp++ = (*resp >> 4)     + '0';
+		*mcnp++ = (*resp++ & 0x0f) + '0';
+		*mcnp++ = (*resp >> 4)     + '0';
+		*mcnp++ = (*resp++ & 0x0f) + '0';
+		*mcnp++ = (*resp >> 4)     + '0';
+		*mcnp++ = (*resp++ & 0x0f) + '0';
+		*mcnp++ = (*resp >> 4)     + '0';
+	}
+	*mcnp = '\0';
+
+	D_S[d].diskstate_flags |= upc_bit;
+	return (0);
+}
+
 /*==========================================================================*/
 static int cc_CheckMultiSession(void)
 {
@@ -3716,6 +3833,21 @@
 	int i, j;
 	D_S[d].diskstate_flags &= ~toc_bit;
 	D_S[d].ored_ctl_adr=0;
+	/* special handling of CD-I HE */
+	if ((D_S[d].n_first_track == 2 && D_S[d].n_last_track == 2) ||
+             D_S[d].xa_byte == 0x10)
+        {
+		D_S[d].TocBuffer[1].nixbyte=0;
+		D_S[d].TocBuffer[1].ctl_adr=0x40;
+		D_S[d].TocBuffer[1].number=1;
+		D_S[d].TocBuffer[1].format=0;
+		D_S[d].TocBuffer[1].address=blk2msf(0);
+		D_S[d].ored_ctl_adr |= 0x40;
+		D_S[d].n_first_track = 1;
+		D_S[d].n_last_track = 1;
+		D_S[d].xa_byte = 0x10;
+                j = 2;
+        } else
 	for (j=D_S[d].n_first_track;j<=D_S[d].n_last_track;j++)
 	{
 		i=cc_ReadTocEntry(j);
@@ -3801,7 +3933,7 @@
 	}
 	i=cc_ReadUPC();
 	if (i<0) msg(DBG_INF,"DiskInfo: cc_ReadUPC returns %d\n", i);
-	if ((fam0L_drive) && (D_S[d].xa_byte==0x20))
+	if ((fam0L_drive) && (D_S[d].xa_byte==0x20 || D_S[d].xa_byte == 0x10))
 	{
 		/* XA disk with old drive */
 		cc_ModeSelect(CD_FRAMESIZE_RAW1);
@@ -3811,6 +3943,18 @@
 	msg(DBG_000,"DiskInfo done.\n");
 	return (0);
 }
+
+static int sbpcd_drive_status(struct cdrom_device_info *cdi, int slot_nr)
+{
+  if (CDSL_CURRENT != slot_nr) {
+     /* we have no changer support */
+     return -EINVAL;
+  }
+
+  return D_S[d].status_bits & p1_disk_ok ? CDS_DISC_OK : CDS_DRIVE_NOT_READY;
+}
+
+
 /*==========================================================================*/
 #if FUTURE
 /*
@@ -3934,40 +4078,40 @@
 	return (1);
 }
 /*==========================================================================*/
+		
+static int sbpcd_get_last_session(struct cdrom_device_info *cdi, struct cdrom_multisession *ms_infp)
+{
+	ms_infp->addr_format = CDROM_LBA;
+	ms_infp->addr.lba    = D_S[d].lba_multi;
+	if (D_S[d].f_multisession)
+		ms_infp->xa_flag=1; /* valid redirection address */
+	else
+		ms_infp->xa_flag=0; /* invalid redirection address */
+
+	return  1;
+}
 
 /*==========================================================================*/
 /*==========================================================================*/
 /*
  * ioctl support
  */
-static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd,
-		       u_long arg)
+static int sbpcd_dev_ioctl(struct cdrom_device_info *cdi, u_int cmd,
+		      u_long arg)
 {
-	int i, st;
+	int i;
 	
 	msg(DBG_IO2,"ioctl(%d, 0x%08lX, 0x%08lX)\n",
-	    MINOR(inode->i_rdev), cmd, arg);
-	if (!inode) return (-EINVAL);
-	i=MINOR(inode->i_rdev);
+	    MINOR(cdi->dev), cmd, arg);
+	i=MINOR(cdi->dev);
 	if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
 	{
-		msg(DBG_INF, "ioctl: bad device: %04X\n", inode->i_rdev);
+		msg(DBG_INF, "ioctl: bad device: %04X\n", cdi->dev);
 		return (-ENXIO);             /* no such drive */
 	}
 	down(&ioctl_read_sem);
 	if (d!=i) switch_drive(i);
 	
-#if 0
-	st=GetStatus();
-	if (st<0) RETURN_UP(-EIO);
-	
-	if (!toc_valid)
-	{
-		i=DiskInfo();
-		if (i<0) RETURN_UP(-EIO);	/* error reading TOC */
-	}
-#endif
-	
 	msg(DBG_IO2,"ioctl: device %d, request %04X\n",i,cmd);
 	switch (cmd) 		/* Sun-compatible */
 	{
@@ -3975,305 +4119,47 @@
 		if (!suser()) RETURN_UP(-EPERM);
 		i=sbpcd_dbg_ioctl(arg,1);
 		RETURN_UP(i);
+	case CDROMRESET:      /* hard reset the drive */
+		msg(DBG_IOC,"ioctl: CDROMRESET entered.\n");
+		i=DriveReset();
+		D_S[d].audio_state=0;
+		RETURN_UP(i);
 		
-	case CDROMPAUSE:     /* Pause the drive */
-		msg(DBG_IOC,"ioctl: CDROMPAUSE entered.\n");
-		/* pause the drive unit when it is currently in PLAY mode,         */
-		/* or reset the starting and ending locations when in PAUSED mode. */
-		/* If applicable, at the next stopping point it reaches            */
-		/* the drive will discontinue playing.                             */
-		switch (D_S[d].audio_state)
-		{
-		case audio_playing:
-			if (famL_drive) i=cc_ReadSubQ();
-			else i=cc_Pause_Resume(1);
-			if (i<0) RETURN_UP(-EIO);
-			if (famL_drive) i=cc_Pause_Resume(1);
-			else i=cc_ReadSubQ();
-			if (i<0) RETURN_UP(-EIO);
-			D_S[d].pos_audio_start=D_S[d].SubQ_run_tot;
-			D_S[d].audio_state=audio_pausing;
-			RETURN_UP(0);
-		case audio_pausing:
-			i=cc_Seek(D_S[d].pos_audio_start,1);
-			if (i<0) RETURN_UP(-EIO);
-			RETURN_UP(0);
-		default:
-			RETURN_UP(-EINVAL);
-		}
-		
-	case CDROMRESUME: /* resume paused audio play */
-		msg(DBG_IOC,"ioctl: CDROMRESUME entered.\n");
-		/* resume playing audio tracks when a previous PLAY AUDIO call has  */
-		/* been paused with a PAUSE command.                                */
-		/* It will resume playing from the location saved in SubQ_run_tot.  */
-		if (D_S[d].audio_state!=audio_pausing) return -EINVAL;
-		if (famL_drive)
-			i=cc_PlayAudio(D_S[d].pos_audio_start,
-				       D_S[d].pos_audio_end);
-		else i=cc_Pause_Resume(3);
-		if (i<0) RETURN_UP(-EIO);
-		D_S[d].audio_state=audio_playing;
-		RETURN_UP(0);
-		
-	case CDROMPLAYMSF:
-		msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n");
+	case CDROMREADMODE1:
+		msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n");
 #if SAFE_MIXED
 		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
 #endif SAFE_MIXED
-		if (D_S[d].audio_state==audio_playing)
-		{
-			i=cc_Pause_Resume(1);
-			if (i<0) RETURN_UP(-EIO);
-			i=cc_ReadSubQ();
-			if (i<0) RETURN_UP(-EIO);
-			D_S[d].pos_audio_start=D_S[d].SubQ_run_tot;
-			i=cc_Seek(D_S[d].pos_audio_start,1);
-		}
-		st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_msf));
-		if (st) RETURN_UP(st);
-		copy_from_user(&msf, (void *) arg, sizeof(struct cdrom_msf));
-		/* values come as msf-bin */
-		D_S[d].pos_audio_start = (msf.cdmsf_min0<<16) |
-                        (msf.cdmsf_sec0<<8) |
-				msf.cdmsf_frame0;
-		D_S[d].pos_audio_end = (msf.cdmsf_min1<<16) |
-			(msf.cdmsf_sec1<<8) |
-				msf.cdmsf_frame1;
-		msg(DBG_IOX,"ioctl: CDROMPLAYMSF %08X %08X\n",
-		    D_S[d].pos_audio_start,D_S[d].pos_audio_end);
-		i=cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end);
-		if (i<0)
-		{
-			msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i);
-			DriveReset();
-			D_S[d].audio_state=0;
-			RETURN_UP(-EIO);
-		}
-		D_S[d].audio_state=audio_playing;
+		cc_ModeSelect(CD_FRAMESIZE);
+		cc_ModeSense();
+		D_S[d].mode=READ_M1;
 		RETURN_UP(0);
 		
-	case CDROMPLAYTRKIND: /* Play a track.  This currently ignores index. */
-		msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n");
+	case CDROMREADMODE2: /* not usable at the moment */
+		msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n");
 #if SAFE_MIXED
 		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
 #endif SAFE_MIXED
-		if (D_S[d].audio_state==audio_playing)
-		{
-			msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n");
-#if 1
-			RETURN_UP(0); /* just let us play on */
-#else
-			RETURN_UP(-EINVAL); /* play on, but say "error" */
-#endif
-		}
-		st=verify_area(VERIFY_READ,(void *) arg,sizeof(struct cdrom_ti));
-		if (st<0)
-		{
-			msg(DBG_IOX,"CDROMPLAYTRKIND: verify_area error.\n");
-			RETURN_UP(st);
-		}
-		copy_from_user(&ti,(void *) arg,sizeof(struct cdrom_ti));
-		msg(DBG_IOX,"ioctl: trk0: %d, ind0: %d, trk1:%d, ind1:%d\n",
-		    ti.cdti_trk0,ti.cdti_ind0,ti.cdti_trk1,ti.cdti_ind1);
-		if (ti.cdti_trk0<D_S[d].n_first_track) RETURN_UP(-EINVAL);
-		if (ti.cdti_trk0>D_S[d].n_last_track) RETURN_UP(-EINVAL);
-		if (ti.cdti_trk1<ti.cdti_trk0) ti.cdti_trk1=ti.cdti_trk0;
-		if (ti.cdti_trk1>D_S[d].n_last_track) ti.cdti_trk1=D_S[d].n_last_track;
-		D_S[d].pos_audio_start=D_S[d].TocBuffer[ti.cdti_trk0].address;
-		D_S[d].pos_audio_end=D_S[d].TocBuffer[ti.cdti_trk1+1].address;
-		i=cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end);
-		if (i<0)
-		{
-			msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i);
-			DriveReset();
-			D_S[d].audio_state=0;
-			RETURN_UP(-EIO);
-		}
-		D_S[d].audio_state=audio_playing;
+		cc_ModeSelect(CD_FRAMESIZE_RAW1);
+		cc_ModeSense();
+		D_S[d].mode=READ_M2;
 		RETURN_UP(0);
 		
-	case CDROMREADTOCHDR:        /* Read the table of contents header */
-		msg(DBG_IOC,"ioctl: CDROMREADTOCHDR entered.\n");
-		tochdr.cdth_trk0=D_S[d].n_first_track;
-		tochdr.cdth_trk1=D_S[d].n_last_track;
-		st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_tochdr));
-		if (st) RETURN_UP(st);
-		copy_to_user((void *) arg, &tochdr, sizeof(struct cdrom_tochdr));
-		RETURN_UP(0);
+	case CDROMAUDIOBUFSIZ: /* configure the audio buffer size */
+		msg(DBG_IOC,"ioctl: CDROMAUDIOBUFSIZ entered.\n");
+		if (D_S[d].sbp_audsiz>0) vfree(D_S[d].aud_buf);
+		D_S[d].aud_buf=NULL;
+		D_S[d].sbp_audsiz=arg;
 		
-	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
-		msg(DBG_IOC,"ioctl: CDROMREADTOCENTRY entered.\n");
-		st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_tocentry));
-		if (st) RETURN_UP(st);
-		copy_from_user(&tocentry, (void *) arg, sizeof(struct cdrom_tocentry));
-		i=tocentry.cdte_track;
-		if (i==CDROM_LEADOUT) i=D_S[d].n_last_track+1;
-		else if (i<D_S[d].n_first_track||i>D_S[d].n_last_track)
-                  RETURN_UP(-EINVAL);
-		tocentry.cdte_adr=D_S[d].TocBuffer[i].ctl_adr&0x0F;
-		tocentry.cdte_ctrl=(D_S[d].TocBuffer[i].ctl_adr>>4)&0x0F;
-		tocentry.cdte_datamode=D_S[d].TocBuffer[i].format;
-		if (tocentry.cdte_format==CDROM_MSF) /* MSF-bin required */
+		if (D_S[d].sbp_audsiz>0)
 		{
-			tocentry.cdte_addr.msf.minute=(D_S[d].TocBuffer[i].address>>16)&0x00FF;
-			tocentry.cdte_addr.msf.second=(D_S[d].TocBuffer[i].address>>8)&0x00FF;
-			tocentry.cdte_addr.msf.frame=D_S[d].TocBuffer[i].address&0x00FF;
-		}
-		else if (tocentry.cdte_format==CDROM_LBA) /* blk required */
-			tocentry.cdte_addr.lba=msf2blk(D_S[d].TocBuffer[i].address);
-		else RETURN_UP(-EINVAL);
-		copy_to_user((void *) arg, &tocentry, sizeof(struct cdrom_tocentry));
-		RETURN_UP(0);
-		
-	case CDROMRESET:      /* hard reset the drive */
-		msg(DBG_IOC,"ioctl: CDROMRESET entered.\n");
-		i=DriveReset();
-		D_S[d].audio_state=0;
-		RETURN_UP(i);
-		
-	case CDROMSTOP:      /* Spin down the drive */
-		msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n");
-#if SAFE_MIXED
-		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
-#endif SAFE_MIXED
-		i=cc_Pause_Resume(1);
-		D_S[d].audio_state=0;
-		RETURN_UP(i);
-		
-	case CDROMSTART:  /* Spin up the drive */
-		msg(DBG_IOC,"ioctl: CDROMSTART entered.\n");
-		cc_SpinUp();
-		D_S[d].audio_state=0;
-		RETURN_UP(0);
-		
-	case CDROMEJECT:
-		msg(DBG_IOC,"ioctl: CDROMEJECT entered.\n");
-		if (fam0_drive) return (0);
-		if (D_S[d].open_count>1) RETURN_UP(-EBUSY);
-		i=UnLockDoor();
-		D_S[d].open_count=-9; /* to get it locked next time again */
-		i=cc_SpinDown();
-		msg(DBG_IOX,"ioctl: cc_SpinDown returned %d.\n", i);
-		msg(DBG_TEA,"ioctl: cc_SpinDown returned %d.\n", i);
-		if (i<0) RETURN_UP(-EIO);
-		D_S[d].CD_changed=0xFF;
-		D_S[d].diskstate_flags=0;
-		D_S[d].audio_state=0;
-		RETURN_UP(0);
-		
-	case CDROMEJECT_SW:
-		msg(DBG_IOC,"ioctl: CDROMEJECT_SW entered.\n");
-		if (fam0_drive) RETURN_UP(0);
-		D_S[d].f_eject=arg;
-		RETURN_UP(0);
-		
-	case CDROMVOLCTRL:   /* Volume control */
-		msg(DBG_IOC,"ioctl: CDROMVOLCTRL entered.\n");
-		st=verify_area(VERIFY_READ,(void *) arg,sizeof(volctrl));
-		if (st) RETURN_UP(st);
-		copy_from_user(&volctrl,(char *) arg,sizeof(volctrl));
-		D_S[d].vol_chan0=0;
-		D_S[d].vol_ctrl0=volctrl.channel0;
-		D_S[d].vol_chan1=1;
-		D_S[d].vol_ctrl1=volctrl.channel1;
-		i=cc_SetVolume();
-		RETURN_UP(0);
-		
-	case CDROMVOLREAD:   /* read Volume settings from drive */
-		msg(DBG_IOC,"ioctl: CDROMVOLREAD entered.\n");
-		st=verify_area(VERIFY_WRITE,(void *)arg,sizeof(volctrl));
-		if (st) RETURN_UP(st);
-		st=cc_GetVolume();
-		if (st<0) return (st);
-		volctrl.channel0=D_S[d].vol_ctrl0;
-		volctrl.channel1=D_S[d].vol_ctrl1;
-		volctrl.channel2=0;
-		volctrl.channel2=0;
-		copy_to_user((void *)arg,&volctrl,sizeof(volctrl));
-		RETURN_UP(0);
-
-	case CDROMSUBCHNL:   /* Get subchannel info */
-		msg(DBG_IOS,"ioctl: CDROMSUBCHNL entered.\n");
-		if ((st_spinning)||(!subq_valid)) {
-			i=cc_ReadSubQ();
-			if (i<0) RETURN_UP(-EIO);
-		}
-		st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl));
-		if (st)	RETURN_UP(st);
-		copy_from_user(&SC, (void *) arg, sizeof(struct cdrom_subchnl));
-		switch (D_S[d].audio_state)
-		{
-		case audio_playing:
-			SC.cdsc_audiostatus=CDROM_AUDIO_PLAY;
-			break;
-		case audio_pausing:
-			SC.cdsc_audiostatus=CDROM_AUDIO_PAUSED;
-			break;
-		default:
-			SC.cdsc_audiostatus=CDROM_AUDIO_NO_STATUS;
-			break;
-		}
-		SC.cdsc_adr=D_S[d].SubQ_ctl_adr;
-		SC.cdsc_ctrl=D_S[d].SubQ_ctl_adr>>4;
-		SC.cdsc_trk=bcd2bin(D_S[d].SubQ_trk);
-		SC.cdsc_ind=bcd2bin(D_S[d].SubQ_pnt_idx);
-		if (SC.cdsc_format==CDROM_LBA)
-		{
-			SC.cdsc_absaddr.lba=msf2blk(D_S[d].SubQ_run_tot);
-			SC.cdsc_reladdr.lba=msf2blk(D_S[d].SubQ_run_trk);
-		}
-		else /* not only if (SC.cdsc_format==CDROM_MSF) */
-		{
-			SC.cdsc_absaddr.msf.minute=(D_S[d].SubQ_run_tot>>16)&0x00FF;
-			SC.cdsc_absaddr.msf.second=(D_S[d].SubQ_run_tot>>8)&0x00FF;
-			SC.cdsc_absaddr.msf.frame=D_S[d].SubQ_run_tot&0x00FF;
-			SC.cdsc_reladdr.msf.minute=(D_S[d].SubQ_run_trk>>16)&0x00FF;
-			SC.cdsc_reladdr.msf.second=(D_S[d].SubQ_run_trk>>8)&0x00FF;
-			SC.cdsc_reladdr.msf.frame=D_S[d].SubQ_run_trk&0x00FF;
-		}
-		copy_to_user((void *) arg, &SC, sizeof(struct cdrom_subchnl));
-		msg(DBG_IOS,"CDROMSUBCHNL: %1X %02X %08X %08X %02X %02X %06X %06X\n",
-		    SC.cdsc_format,SC.cdsc_audiostatus,
-		    SC.cdsc_adr,SC.cdsc_ctrl,
-		    SC.cdsc_trk,SC.cdsc_ind,
-		    SC.cdsc_absaddr,SC.cdsc_reladdr);
-		RETURN_UP(0);
-		
-	case CDROMREADMODE1:
-		msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n");
-#if SAFE_MIXED
-		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
-#endif SAFE_MIXED
-		cc_ModeSelect(CD_FRAMESIZE);
-		cc_ModeSense();
-		D_S[d].mode=READ_M1;
-		RETURN_UP(0);
-		
-	case CDROMREADMODE2: /* not usable at the moment */
-		msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n");
-#if SAFE_MIXED
-		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
-#endif SAFE_MIXED
-		cc_ModeSelect(CD_FRAMESIZE_RAW1);
-		cc_ModeSense();
-		D_S[d].mode=READ_M2;
-		RETURN_UP(0);
-		
-	case CDROMAUDIOBUFSIZ: /* configure the audio buffer size */
-		msg(DBG_IOC,"ioctl: CDROMAUDIOBUFSIZ entered.\n");
-		if (D_S[d].sbp_audsiz>0) vfree(D_S[d].aud_buf);
-		D_S[d].aud_buf=NULL;
-		D_S[d].sbp_audsiz=arg;
-		if (D_S[d].sbp_audsiz>0)
-		{
-			D_S[d].aud_buf=(u_char *) vmalloc(D_S[d].sbp_audsiz*CD_FRAMESIZE_RAW);
-			if (D_S[d].aud_buf==NULL)
-			{
-				msg(DBG_INF,"audio buffer (%d frames) not available.\n",D_S[d].sbp_audsiz);
-				D_S[d].sbp_audsiz=0;
-			}
-			else msg(DBG_INF,"audio buffer size: %d frames.\n",D_S[d].sbp_audsiz);
+			D_S[d].aud_buf=(u_char *) vmalloc(D_S[d].sbp_audsiz*CD_FRAMESIZE_RAW);
+			if (D_S[d].aud_buf==NULL)
+			{
+				msg(DBG_INF,"audio buffer (%d frames) not available.\n",D_S[d].sbp_audsiz);
+				D_S[d].sbp_audsiz=0;
+			}
+			else msg(DBG_INF,"audio buffer size: %d frames.\n",D_S[d].sbp_audsiz);
 		}
 		RETURN_UP(D_S[d].sbp_audsiz);
 
@@ -4496,28 +4382,277 @@
 		RETURN_UP(0);
 	} /* end of CDROMREADAUDIO */
 		
-	case CDROMMULTISESSION: /* tell start-of-last-session */
-		msg(DBG_IOC,"ioctl: CDROMMULTISESSION entered.\n");
-		st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_multisession));
-		if (st) RETURN_UP(st);
-		copy_from_user(&ms_info, (void *) arg, sizeof(struct cdrom_multisession));
-		if (ms_info.addr_format==CDROM_MSF) /* MSF-bin requested */
-			lba2msf(D_S[d].lba_multi,&ms_info.addr.msf.minute);
-		else if (ms_info.addr_format==CDROM_LBA) /* lba requested */
-			ms_info.addr.lba=D_S[d].lba_multi;
-		else RETURN_UP(-EINVAL);
-		if (D_S[d].f_multisession) ms_info.xa_flag=1; /* valid redirection address */
-		else ms_info.xa_flag=0; /* invalid redirection address */
-		copy_to_user((void *) arg, &ms_info, sizeof(struct cdrom_multisession));
-		msg(DBG_MUL,"ioctl: CDROMMULTISESSION done (%d, %08X).\n",
-		    ms_info.xa_flag, ms_info.addr.lba);
-		RETURN_UP(0);
-		
 	case BLKRASET:
 		if(!suser()) RETURN_UP(-EACCES);
-		if(!(inode->i_rdev)) RETURN_UP(-EINVAL);
+		if(!(cdi->dev)) RETURN_UP(-EINVAL);
 		if(arg > 0xff) RETURN_UP(-EINVAL);
-		read_ahead[MAJOR(inode->i_rdev)] = arg;
+		read_ahead[MAJOR(cdi->dev)] = arg;
+		RETURN_UP(0);
+#if 0
+	case CDROMEJECT:
+		msg(DBG_IOC,"ioctl: CDROMEJECT entered.\n");
+		if (fam0_drive) return (0);
+		if (D_S[d].open_count>1) RETURN_UP(-EBUSY);
+		i=UnLockDoor();
+		D_S[d].open_count=-9; /* to get it locked next time again */
+		i=cc_SpinDown();
+		msg(DBG_IOX,"ioctl: cc_SpinDown returned %d.\n", i);
+		msg(DBG_TEA,"ioctl: cc_SpinDown returned %d.\n", i);
+		if (i<0) RETURN_UP(-EIO);
+		D_S[d].CD_changed=0xFF;
+		D_S[d].diskstate_flags=0;
+		D_S[d].audio_state=0;
+		RETURN_UP(0);
+
+	case CDROMEJECT_SW:
+		msg(DBG_IOC,"ioctl: CDROMEJECT_SW entered.\n");
+		if (fam0_drive) RETURN_UP(0);
+		D_S[d].f_eject=arg;
+		RETURN_UP(0);
+#endif		
+	default:
+		msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd);
+		RETURN_UP(-EINVAL);
+	} /* end switch(cmd) */
+}
+
+static int sbpcd_audio_ioctl(struct cdrom_device_info *cdi, u_int cmd,
+		       void * arg)
+{
+	int i, st;
+	
+	msg(DBG_IO2,"ioctl(%d, 0x%08lX, 0x%08p)\n",
+	    MINOR(cdi->dev), cmd, arg);
+	i=MINOR(cdi->dev);
+	if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
+	{
+		msg(DBG_INF, "ioctl: bad device: %04X\n", cdi->dev);
+		return (-ENXIO);             /* no such drive */
+	}
+	down(&ioctl_read_sem);
+	if (d!=i) switch_drive(i);
+	
+	msg(DBG_IO2,"ioctl: device %d, request %04X\n",i,cmd);
+	switch (cmd) 		/* Sun-compatible */
+	{
+		
+	case CDROMPAUSE:     /* Pause the drive */
+		msg(DBG_IOC,"ioctl: CDROMPAUSE entered.\n");
+		/* pause the drive unit when it is currently in PLAY mode,         */
+		/* or reset the starting and ending locations when in PAUSED mode. */
+		/* If applicable, at the next stopping point it reaches            */
+		/* the drive will discontinue playing.                             */
+		switch (D_S[d].audio_state)
+		{
+		case audio_playing:
+			if (famL_drive) i=cc_ReadSubQ();
+			else i=cc_Pause_Resume(1);
+			if (i<0) RETURN_UP(-EIO);
+			if (famL_drive) i=cc_Pause_Resume(1);
+			else i=cc_ReadSubQ();
+			if (i<0) RETURN_UP(-EIO);
+			D_S[d].pos_audio_start=D_S[d].SubQ_run_tot;
+			D_S[d].audio_state=audio_pausing;
+			RETURN_UP(0);
+		case audio_pausing:
+			i=cc_Seek(D_S[d].pos_audio_start,1);
+			if (i<0) RETURN_UP(-EIO);
+			RETURN_UP(0);
+		default:
+			RETURN_UP(-EINVAL);
+		}
+		
+	case CDROMRESUME: /* resume paused audio play */
+		msg(DBG_IOC,"ioctl: CDROMRESUME entered.\n");
+		/* resume playing audio tracks when a previous PLAY AUDIO call has  */
+		/* been paused with a PAUSE command.                                */
+		/* It will resume playing from the location saved in SubQ_run_tot.  */
+		if (D_S[d].audio_state!=audio_pausing) return -EINVAL;
+		if (famL_drive)
+			i=cc_PlayAudio(D_S[d].pos_audio_start,
+				       D_S[d].pos_audio_end);
+		else i=cc_Pause_Resume(3);
+		if (i<0) RETURN_UP(-EIO);
+		D_S[d].audio_state=audio_playing;
+		RETURN_UP(0);
+		
+	case CDROMPLAYMSF:
+		msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n");
+#if SAFE_MIXED
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
+#endif SAFE_MIXED
+		if (D_S[d].audio_state==audio_playing)
+		{
+			i=cc_Pause_Resume(1);
+			if (i<0) RETURN_UP(-EIO);
+			i=cc_ReadSubQ();
+			if (i<0) RETURN_UP(-EIO);
+			D_S[d].pos_audio_start=D_S[d].SubQ_run_tot;
+			i=cc_Seek(D_S[d].pos_audio_start,1);
+		}
+		memcpy(&msf, (void *) arg, sizeof(struct cdrom_msf));
+		/* values come as msf-bin */
+		D_S[d].pos_audio_start = (msf.cdmsf_min0<<16) |
+                        (msf.cdmsf_sec0<<8) |
+				msf.cdmsf_frame0;
+		D_S[d].pos_audio_end = (msf.cdmsf_min1<<16) |
+			(msf.cdmsf_sec1<<8) |
+				msf.cdmsf_frame1;
+		msg(DBG_IOX,"ioctl: CDROMPLAYMSF %08X %08X\n",
+		    D_S[d].pos_audio_start,D_S[d].pos_audio_end);
+		i=cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end);
+		if (i<0)
+		{
+			msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i);
+			DriveReset();
+			D_S[d].audio_state=0;
+			RETURN_UP(-EIO);
+		}
+		D_S[d].audio_state=audio_playing;
+		RETURN_UP(0);
+		
+	case CDROMPLAYTRKIND: /* Play a track.  This currently ignores index. */
+		msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n");
+#if SAFE_MIXED
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
+#endif SAFE_MIXED
+		if (D_S[d].audio_state==audio_playing)
+		{
+			msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n");
+#if 1
+			RETURN_UP(0); /* just let us play on */
+#else
+			RETURN_UP(-EINVAL); /* play on, but say "error" */
+#endif
+		}
+		memcpy(&ti,(void *) arg,sizeof(struct cdrom_ti));
+		msg(DBG_IOX,"ioctl: trk0: %d, ind0: %d, trk1:%d, ind1:%d\n",
+		    ti.cdti_trk0,ti.cdti_ind0,ti.cdti_trk1,ti.cdti_ind1);
+		if (ti.cdti_trk0<D_S[d].n_first_track) RETURN_UP(-EINVAL);
+		if (ti.cdti_trk0>D_S[d].n_last_track) RETURN_UP(-EINVAL);
+		if (ti.cdti_trk1<ti.cdti_trk0) ti.cdti_trk1=ti.cdti_trk0;
+		if (ti.cdti_trk1>D_S[d].n_last_track) ti.cdti_trk1=D_S[d].n_last_track;
+		D_S[d].pos_audio_start=D_S[d].TocBuffer[ti.cdti_trk0].address;
+		D_S[d].pos_audio_end=D_S[d].TocBuffer[ti.cdti_trk1+1].address;
+		i=cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end);
+		if (i<0)
+		{
+			msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i);
+			DriveReset();
+			D_S[d].audio_state=0;
+			RETURN_UP(-EIO);
+		}
+		D_S[d].audio_state=audio_playing;
+		RETURN_UP(0);
+		
+	case CDROMREADTOCHDR:        /* Read the table of contents header */
+		msg(DBG_IOC,"ioctl: CDROMREADTOCHDR entered.\n");
+		tochdr.cdth_trk0=D_S[d].n_first_track;
+		tochdr.cdth_trk1=D_S[d].n_last_track;
+		memcpy((void *) arg, &tochdr, sizeof(struct cdrom_tochdr));
+		RETURN_UP(0);
+		
+	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
+		msg(DBG_IOC,"ioctl: CDROMREADTOCENTRY entered.\n");
+		memcpy(&tocentry, (void *) arg, sizeof(struct cdrom_tocentry));
+		i=tocentry.cdte_track;
+		if (i==CDROM_LEADOUT) i=D_S[d].n_last_track+1;
+		else if (i<D_S[d].n_first_track||i>D_S[d].n_last_track)
+                  RETURN_UP(-EINVAL);
+		tocentry.cdte_adr=D_S[d].TocBuffer[i].ctl_adr&0x0F;
+		tocentry.cdte_ctrl=(D_S[d].TocBuffer[i].ctl_adr>>4)&0x0F;
+		tocentry.cdte_datamode=D_S[d].TocBuffer[i].format;
+		if (tocentry.cdte_format==CDROM_MSF) /* MSF-bin required */
+		{
+			tocentry.cdte_addr.msf.minute=(D_S[d].TocBuffer[i].address>>16)&0x00FF;
+			tocentry.cdte_addr.msf.second=(D_S[d].TocBuffer[i].address>>8)&0x00FF;
+			tocentry.cdte_addr.msf.frame=D_S[d].TocBuffer[i].address&0x00FF;
+		}
+		else if (tocentry.cdte_format==CDROM_LBA) /* blk required */
+			tocentry.cdte_addr.lba=msf2blk(D_S[d].TocBuffer[i].address);
+		else RETURN_UP(-EINVAL);
+		memcpy((void *) arg, &tocentry, sizeof(struct cdrom_tocentry));
+		RETURN_UP(0);
+		
+	case CDROMSTOP:      /* Spin down the drive */
+		msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n");
+#if SAFE_MIXED
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
+#endif SAFE_MIXED
+		i=cc_Pause_Resume(1);
+		D_S[d].audio_state=0;
+		RETURN_UP(i);
+		
+	case CDROMSTART:  /* Spin up the drive */
+		msg(DBG_IOC,"ioctl: CDROMSTART entered.\n");
+		cc_SpinUp();
+		D_S[d].audio_state=0;
+		RETURN_UP(0);
+		
+	case CDROMVOLCTRL:   /* Volume control */
+		msg(DBG_IOC,"ioctl: CDROMVOLCTRL entered.\n");
+		memcpy(&volctrl,(char *) arg,sizeof(volctrl));
+		D_S[d].vol_chan0=0;
+		D_S[d].vol_ctrl0=volctrl.channel0;
+		D_S[d].vol_chan1=1;
+		D_S[d].vol_ctrl1=volctrl.channel1;
+		i=cc_SetVolume();
+		RETURN_UP(0);
+		
+	case CDROMVOLREAD:   /* read Volume settings from drive */
+		msg(DBG_IOC,"ioctl: CDROMVOLREAD entered.\n");
+		st=cc_GetVolume();
+		if (st<0) return (st);
+		volctrl.channel0=D_S[d].vol_ctrl0;
+		volctrl.channel1=D_S[d].vol_ctrl1;
+		volctrl.channel2=0;
+		volctrl.channel2=0;
+		memcpy((void *)arg,&volctrl,sizeof(volctrl));
+		RETURN_UP(0);
+
+	case CDROMSUBCHNL:   /* Get subchannel info */
+		msg(DBG_IOS,"ioctl: CDROMSUBCHNL entered.\n");
+		if ((st_spinning)||(!subq_valid)) {
+			i=cc_ReadSubQ();
+			if (i<0) RETURN_UP(-EIO);
+		}
+		memcpy(&SC, (void *) arg, sizeof(struct cdrom_subchnl));
+		switch (D_S[d].audio_state)
+		{
+		case audio_playing:
+			SC.cdsc_audiostatus=CDROM_AUDIO_PLAY;
+			break;
+		case audio_pausing:
+			SC.cdsc_audiostatus=CDROM_AUDIO_PAUSED;
+			break;
+		default:
+			SC.cdsc_audiostatus=CDROM_AUDIO_NO_STATUS;
+			break;
+		}
+		SC.cdsc_adr=D_S[d].SubQ_ctl_adr;
+		SC.cdsc_ctrl=D_S[d].SubQ_ctl_adr>>4;
+		SC.cdsc_trk=bcd2bin(D_S[d].SubQ_trk);
+		SC.cdsc_ind=bcd2bin(D_S[d].SubQ_pnt_idx);
+		if (SC.cdsc_format==CDROM_LBA)
+		{
+			SC.cdsc_absaddr.lba=msf2blk(D_S[d].SubQ_run_tot);
+			SC.cdsc_reladdr.lba=msf2blk(D_S[d].SubQ_run_trk);
+		}
+		else /* not only if (SC.cdsc_format==CDROM_MSF) */
+		{
+			SC.cdsc_absaddr.msf.minute=(D_S[d].SubQ_run_tot>>16)&0x00FF;
+			SC.cdsc_absaddr.msf.second=(D_S[d].SubQ_run_tot>>8)&0x00FF;
+			SC.cdsc_absaddr.msf.frame=D_S[d].SubQ_run_tot&0x00FF;
+			SC.cdsc_reladdr.msf.minute=(D_S[d].SubQ_run_trk>>16)&0x00FF;
+			SC.cdsc_reladdr.msf.second=(D_S[d].SubQ_run_trk>>8)&0x00FF;
+			SC.cdsc_reladdr.msf.frame=D_S[d].SubQ_run_trk&0x00FF;
+		}
+		memcpy((void *) arg, &SC, sizeof(struct cdrom_subchnl));
+		msg(DBG_IOS,"CDROMSUBCHNL: %1X %02X %08X %08X %02X %02X %06X %06X\n",
+		    SC.cdsc_format,SC.cdsc_audiostatus,
+		    SC.cdsc_adr,SC.cdsc_ctrl,
+		    SC.cdsc_trk,SC.cdsc_ind,
+		    SC.cdsc_absaddr,SC.cdsc_reladdr);
 		RETURN_UP(0);
 		
 	default:
@@ -5104,19 +5239,18 @@
 /*
  *  Open the device special file.  Check that a disk is in. Read TOC.
  */
-static int sbpcd_open(struct inode *ip, struct file *fp)
+static int sbpcd_open(struct cdrom_device_info *cdi, int purpose)
 {
 	int i;
 	
-	i = MINOR(ip->i_rdev);
+	i = MINOR(cdi->dev);
 	if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
 	{
-		msg(DBG_INF, "open: bad device: %04X\n", ip->i_rdev);
+		msg(DBG_INF, "open: bad device: %04X\n", cdi->dev);
 		return (-ENXIO);             /* no such drive */
 	}
-	if (fp->f_mode & 2)
-		return -EROFS;
 	
+	MOD_INC_USE_COUNT;
 	down(&ioctl_read_sem);
 	switch_drive(i);
 	
@@ -5136,21 +5270,13 @@
 	if (i<0)
 	{
 		msg(DBG_INF,"sbpcd_open: ResponseStatus timed out (%d).\n",i);
+		MOD_DEC_USE_COUNT;
 		RETURN_UP(-EIO);                  /* drive doesn't respond */
 	}
 	if (famT_drive)	msg(DBG_TEA,"sbpcd_open: ResponseStatus=%02X\n", i);
-	if (!st_door_closed)
-	{
-		if (famT_drive)	msg(DBG_TEA,"sbpcd_open: !st_door_closed.\n");
-		cc_CloseTray();
-		flags_cmd_out |= f_respo2;
-		cc_ReadStatus();
-		i=ResponseStatus();
-	}
 	if (!(famT_drive))
 		if (!st_spinning)
 		{
-			if (famT_drive)	msg(DBG_TEA,"sbpcd_open: !st_spinning.\n");
 			cc_SpinUp();
 			flags_cmd_out |= f_respo2;
 			cc_ReadStatus();
@@ -5161,19 +5287,12 @@
 	{
 		msg(DBG_INF, "sbpcd_open: no disk in drive.\n");
 		D_S[d].open_count=0;
-#if JUKEBOX
-		if (!fam0_drive)
-		{
-			i=UnLockDoor();
-			cc_SpinDown(); /* eject tray */
-		}
-#endif
+		MOD_DEC_USE_COUNT;
 		RETURN_UP(-ENXIO);
 	}
 	/*
 	 * try to keep an "open" counter here and lock the door if 0->1.
 	 */
-	MOD_INC_USE_COUNT;
 	msg(DBG_LCK,"open_count: %d -> %d\n",
 	    D_S[d].open_count,D_S[d].open_count+1);
 	if (++D_S[d].open_count<=1)
@@ -5201,22 +5320,21 @@
 /*
  *  On close, we flush all sbp blocks from the buffer cache.
  */
-static int sbpcd_release(struct inode * ip, struct file * file)
+static void sbpcd_release(struct cdrom_device_info * cdi)
 {
 	int i;
 	
-	i = MINOR(ip->i_rdev);
+	i = MINOR(cdi->dev);
 	if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
 	{
-		msg(DBG_INF, "release: bad device: %04X\n", ip->i_rdev);
-		return 0;
+		msg(DBG_INF, "release: bad device: %04X\n", cdi->dev);
+		return ;
 	}
 	down(&ioctl_read_sem);
 	switch_drive(i);
 	/*
 	 * try to keep an "open" counter here and unlock the door if 1->0.
 	 */
-	MOD_DEC_USE_COUNT;
 	msg(DBG_LCK,"open_count: %d -> %d\n",
 	    D_S[d].open_count,D_S[d].open_count-1);
 	if (D_S[d].open_count>-2) /* CDROMEJECT may have been done */
@@ -5224,9 +5342,7 @@
 		if (--D_S[d].open_count<=0) 
 		{
 			D_S[d].sbp_first_frame=D_S[d].sbp_last_frame=-1;
-			sync_dev(ip->i_rdev); /* nonsense if read only device? */
-			invalidate_buffers(ip->i_rdev);
-			i=UnLockDoor();
+			invalidate_buffers(cdi->dev);
 			if (D_S[d].audio_state!=audio_playing)
 				if (D_S[d].f_eject) cc_SpinDown();
 			D_S[d].diskstate_flags &= ~cd_size_bit;
@@ -5237,12 +5353,14 @@
 		}
 	}
 	up(&ioctl_read_sem);
-	return 0;
+	MOD_DEC_USE_COUNT;
+	return ;
 }
 /*==========================================================================*/
 /*
  *
  */
+#if 0
 static struct file_operations sbpcd_fops =
 {
 	NULL,                   /* lseek - default */
@@ -5259,6 +5377,39 @@
 	sbpcd_chk_disk_change,  /* media_change */
 	NULL                    /* revalidate */
 };
+#endif
+static int sbpcd_media_changed( struct cdrom_device_info *cdi, int disc_nr);
+static struct cdrom_device_ops sbpcd_dops = {
+  sbpcd_open,                   /* open */
+  sbpcd_release,                /* release */
+  sbpcd_drive_status,           /* drive status */
+  sbpcd_media_changed,          /* media changed */
+  sbpcd_tray_move,              /* tray move */
+  sbpcd_lock_door,              /* lock door */
+  sbpcd_select_speed,           /* select speed */
+  NULL,                         /* select disc */
+  sbpcd_get_last_session,       /* get last session */
+  sbpcd_get_mcn,                /* get universal product code */
+  sbpcd_reset,                  /* hard reset */
+  sbpcd_audio_ioctl,            /* audio ioctl */
+  sbpcd_dev_ioctl,              /* device-specific ioctl */
+  CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_MULTI_SESSION |
+    CDC_MEDIA_CHANGED | CDC_MCN | CDC_PLAY_AUDIO, /* capability */
+  1,                            /* number of minor devices */
+};
+
+static struct cdrom_device_info sbpcd_info = {
+  &sbpcd_dops,                /* device operations */
+  NULL,                       /* link */
+  NULL,                       /* handle */
+  MKDEV(MAJOR_NR,0),          /* dev */
+  0,                          /* mask */
+  2,                          /* maximum speed */
+  1,                          /* number of discs */
+  0,                          /* options, not owned */
+  0,                          /* mc_flags, not owned */
+  0                           /* use count, not owned */
+};
 /*==========================================================================*/
 /*
  * accept "kernel command line" parameters 
@@ -5495,15 +5646,15 @@
 		if (!famL_drive) cc_DriveReset();
 #endif 0
 		if (!st_spinning) cc_SpinUp();
-		D_S[d].sbp_first_frame = -1;  /* First frame in buffer */
-		D_S[d].sbp_last_frame = -1;   /* Last frame in buffer  */
-		D_S[d].sbp_read_frames = 0;   /* Number of frames being read to buffer */
-		D_S[d].sbp_current = 0;       /* Frame being currently read */
-		D_S[d].CD_changed=1;
-		D_S[d].frame_size=CD_FRAMESIZE;
-		D_S[d].f_eject=0;
+		D_S[j].sbp_first_frame = -1;  /* First frame in buffer */
+		D_S[j].sbp_last_frame = -1;   /* Last frame in buffer  */
+		D_S[j].sbp_read_frames = 0;   /* Number of frames being read to buffer */
+		D_S[j].sbp_current = 0;       /* Frame being currently read */
+		D_S[j].CD_changed=1;
+		D_S[j].frame_size=CD_FRAMESIZE;
+		D_S[j].f_eject=0;
 #if EJECT
-		if (!fam0_drive) D_S[d].f_eject=1;
+		if (!fam0_drive) D_S[j].f_eject=1;
 #endif EJECT
 		cc_ReadStatus();
 		i=ResponseStatus();  /* returns orig. status or p_busy_new */
@@ -5521,8 +5672,8 @@
 		}
 		msg(DBG_INI,"init: first GetStatus: %d\n",i);
 		msg(DBG_LCS,"init: first GetStatus: error_byte=%d\n",
-		    D_S[d].error_byte);
-		if (D_S[d].error_byte==aud_12)
+		    D_S[j].error_byte);
+		if (D_S[j].error_byte==aud_12)
 		{
 			timeout=jiffies+2*HZ;
 			do
@@ -5531,14 +5682,14 @@
 				msg(DBG_INI,"init: second GetStatus: %02X\n",i);
 				msg(DBG_LCS,
 				    "init: second GetStatus: error_byte=%d\n",
-				    D_S[d].error_byte);
+				    D_S[j].error_byte);
 				if (i<0) break;
 				if (!st_caddy_in) break;
 				}
 			while ((!st_diskok)||(timeout<jiffies));
 		}
 		i=SetSpeed();
-		if (i>=0) D_S[d].CD_changed=1;
+		if (i>=0) D_S[j].CD_changed=1;
 	}
 	
 	/*
@@ -5550,7 +5701,7 @@
 	OUT(MIXER_data,0xCC); /* one nibble per channel, max. value: 0xFF */
 #endif SOUND_BASE
 	
-	if (register_blkdev(MAJOR_NR, major_name, &sbpcd_fops) != 0)
+	if (register_blkdev(MAJOR_NR, major_name, &cdrom_fops) != 0)
 	{
 		msg(DBG_INF, "Can't get MAJOR %d for Matsushita CDROM\n", MAJOR_NR);
 #ifdef MODULE
@@ -5566,6 +5717,8 @@
 	
 	for (j=0;j<NR_SBPCD;j++)
 	{
+		struct cdrom_device_info * sbpcd_infop;
+
 		if (D_S[j].drv_id==-1) continue;
 		switch_drive(j);
 #if SAFE_MIXED
@@ -5583,6 +5736,11 @@
 		if (D_S[j].sbp_buf==NULL)
 		{
 			msg(DBG_INF,"data buffer (%d frames) not available.\n",D_S[j].sbp_bufsiz);
+			if ((unregister_blkdev(MAJOR_NR, major_name) == -EINVAL))
+			{
+				printk("Can't unregister %s\n", major_name);
+			}
+			release_region(CDo_command,4);
 			return -EIO;
 		}
 #ifdef MODULE
@@ -5594,13 +5752,29 @@
 			if (D_S[j].aud_buf==NULL) msg(DBG_INF,"audio buffer (%d frames) not available.\n",D_S[j].sbp_audsiz);
 			else msg(DBG_INF,"audio buffer size: %d frames.\n",D_S[j].sbp_audsiz);
 		}
+                sbpcd_infop = vmalloc(sizeof (struct cdrom_device_info));
+		if (sbpcd_infop == NULL)
+		{
+                        release_region(CDo_command,4);
+                        return -ENOMEM;
+		}
+		D_S[j].sbpcd_infop = sbpcd_infop;
+		memcpy (sbpcd_infop, &sbpcd_info, sizeof(struct cdrom_device_info));
+		sbpcd_infop->dev = MKDEV(MAJOR_NR, j);
+		strncpy(sbpcd_infop->name,major_name, sizeof(sbpcd_infop->name)); 
+
+		if (register_cdrom(sbpcd_infop))
+		{
+                	printk(" sbpcd: Unable to register with Uniform CD-ROm driver\n");
+		}
+
 		/*
 		 * set the block size
 		 */
 		sbpcd_blocksizes[j]=CD_FRAMESIZE;
 	}
 	blksize_size[MAJOR_NR]=sbpcd_blocksizes;
-	
+
 #ifndef MODULE
  init_done:
 #if !(SBPCD_ISSUE-1)
@@ -5635,6 +5809,12 @@
 		if (D_S[j].drv_id==-1) continue;
 		vfree(D_S[j].sbp_buf);
 		if (D_S[j].sbp_audsiz>0) vfree(D_S[j].aud_buf);
+		if ((unregister_cdrom(D_S[j].sbpcd_infop) == -EINVAL))
+		{
+			msg(DBG_INF, "What's that: can't unregister info %s.\n", major_name);
+			return;
+		}
+		vfree(D_S[j].sbpcd_infop);
 	}
 	msg(DBG_INF, "%s module released.\n", major_name);
 }
@@ -5664,6 +5844,11 @@
         }
         else
                 return (0);
+}
+
+static int sbpcd_media_changed( struct cdrom_device_info *cdi, int disc_nr)
+{
+   return sbpcd_chk_disk_change(cdi->dev);
 }
 /*==========================================================================*/
 /*
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/sbpcd.h linux/drivers/cdrom/sbpcd.h
--- v2.1.69/linux/drivers/cdrom/sbpcd.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/cdrom/sbpcd.h	Tue Dec  2 11:41:45 1997
@@ -0,0 +1,858 @@
+/*
+ * sbpcd.h   Specify interface address and interface type here.
+ */
+
+/*
+ * Attention! This file contains user-serviceable parts!
+ * I recommend to make use of it...
+ * If you feel helpless, look into linux/Documentation/cdrom/sbpcd
+ * (good idea anyway, at least before mailing me).
+ *
+ * The definitions for the first controller can get overridden by
+ * the kernel command line ("lilo boot option").
+ * Examples:
+ *                                 sbpcd=0x300,LaserMate
+ *                             or
+ *                                 sbpcd=0x230,SoundBlaster
+ *                             or
+ *                                 sbpcd=0x338,SoundScape
+ *                             or
+ *                                 sbpcd=0x2C0,Teac16bit
+ *
+ * If sbpcd gets used as a module, you can load it with
+ *     insmod sbpcd.o sbpcd=0x300,0
+ * or
+ *     insmod sbpcd.o sbpcd=0x230,1
+ * or
+ *     insmod sbpcd.o sbpcd=0x338,2
+ * or
+ *     insmod sbpcd.o sbpcd=0x2C0,3
+ * respective to override the configured address and type.
+ */
+
+/*
+ * define your CDROM port base address as CDROM_PORT
+ * and specify the type of your interface card as SBPRO.
+ *
+ * address:
+ * ========
+ * SBPRO type addresses typically are 0x0230 (=0x220+0x10), 0x0250, ...
+ * LASERMATE type (CI-101P, WDH-7001C) addresses typically are 0x0300, ...
+ * SOUNDSCAPE addresses are from the LASERMATE type and range. You have to
+ * specify the REAL address here, not the configuration port address. Look
+ * at the CDROM driver's invoking line within your DOS CONFIG.SYS, or let
+ * sbpcd auto-probe, if you are not firm with the address.
+ * There are some soundcards on the market with 0x0630, 0x0650, ...; their
+ * type is not obvious (both types are possible).
+ *
+ * example: if your SBPRO audio address is 0x220, specify 0x230 and SBPRO 1.
+ *          if your soundcard has its CDROM port above 0x300, specify
+ *          that address and try SBPRO 0 first.
+ *          if your SoundScape configuration port is at 0x330, specify
+ *          0x338 and SBPRO 2.
+ *
+ * interface type:
+ * ===============
+ * set SBPRO to 1 for "true" SoundBlaster card
+ * set SBPRO to 0 for "compatible" soundcards and
+ *                for "poor" (no sound) interface cards.
+ * set SBPRO to 2 for Ensonic SoundScape or SPEA Media FX cards
+ * set SBPRO to 3 for Teac 16bit interface cards
+ *
+ * Almost all "compatible" sound boards need to set SBPRO to 0.
+ * If SBPRO is set wrong, the drives will get found - but any
+ * data access will give errors (audio access will work).
+ * The "OmniCD" no-sound interface card from CreativeLabs and most Teac
+ * interface cards need SBPRO 1.
+ *
+ * sound base:
+ * ===========
+ * The SOUND_BASE definition tells if we should try to turn the CD sound
+ * channels on. It will only be of use regarding soundcards with a SbPro
+ * compatible mixer.
+ *
+ * Example: #define SOUND_BASE 0x220 enables the sound card's CD channels
+ *          #define SOUND_BASE 0     leaves the soundcard untouched
+ */
+#if !(SBPCD_ISSUE-1)     /* first (or if you have only one) interface board: */
+#define CDROM_PORT 0x340 /* <-----------<< port address                      */
+#define SBPRO      0     /* <-----------<< interface type                    */
+#define MAX_DRIVES 4     /* set to 1 if the card does not use "drive select" */
+#define SOUND_BASE 0x220 /* <-----------<< sound address of this card or 0   */
+#endif
+#if !(SBPCD_ISSUE-2)     /* ==================== second interface board: === */
+#define CDROM_PORT 0x344 /* <-----------<< port address                      */
+#define SBPRO      0     /* <-----------<< interface type                    */
+#define MAX_DRIVES 4     /* set to 1 if the card does not use "drive select" */
+#define SOUND_BASE 0x000 /* <-----------<< sound address of this card or 0   */
+#endif
+#if !(SBPCD_ISSUE-3)     /* ===================== third interface board: === */
+#define CDROM_PORT 0x630 /* <-----------<< port address                      */
+#define SBPRO      1     /* <-----------<< interface type                    */
+#define MAX_DRIVES 4     /* set to 1 if the card does not use "drive select" */
+#define SOUND_BASE 0x240 /* <-----------<< sound address of this card or 0   */
+#endif
+#if !(SBPCD_ISSUE-4)     /* ==================== fourth interface board: === */
+#define CDROM_PORT 0x634 /* <-----------<< port address                      */
+#define SBPRO      0     /* <-----------<< interface type                    */
+#define MAX_DRIVES 4     /* set to 1 if the card does not use "drive select" */
+#define SOUND_BASE 0x000 /* <-----------<< sound address of this card or 0   */
+#endif
+
+/*
+ * some more or less user dependent definitions - service them!
+ */
+
+/* Set this to 0 once you have configured your interface definitions right. */
+#define DISTRIBUTION 1
+
+/*
+ * Time to wait after giving a message.
+ * This gets important if you enable non-standard DBG_xxx flags.
+ * You will see what happens if you omit the pause or make it
+ * too short. Be warned!
+ */
+#define KLOGD_PAUSE 1
+
+/* tray control: eject tray if no disk is in */
+#if DISTRIBUTION
+#define JUKEBOX 0
+#else
+#define JUKEBOX 1
+#endif DISTRIBUTION
+
+/* tray control: eject tray after last use */
+#if DISTRIBUTION
+#define EJECT 0
+#else
+#define EJECT 1
+#endif DISTRIBUTION
+
+/* max. number of audio frames to read with one     */
+/* request (allocates n* 2352 bytes kernel memory!) */
+/* may be freely adjusted, f.e. 75 (= 1 sec.), at   */
+/* runtime by use of the CDROMAUDIOBUFSIZ ioctl.    */
+#define READ_AUDIO 0
+
+/* Optimizations for the Teac CD-55A drive read performance.
+ * SBP_TEAC_SPEED can be changed here, or one can set the 
+ * variable "teac" when loading as a module.
+ * Valid settings are:
+ *   0 - very slow - the recommended "DISTRIBUTION 1" setup.
+ *   1 - 2x performance with little overhead. No busy waiting.
+ *   2 - 4x performance with 5ms overhead per read. Busy wait.
+ *
+ * Setting SBP_TEAC_SPEED or the variable 'teac' to anything
+ * other than 0 may cause problems. If you run into them, first
+ * change SBP_TEAC_SPEED back to 0 and see if your drive responds
+ * normally. If yes, you are "allowed" to report your case - to help
+ * me with the driver, not to solve your hassle. Don´t mail if you
+ * simply are stuck into your own "tuning" experiments, you know?
+ */
+#define SBP_TEAC_SPEED 1
+
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ * nothing to change below here if you are not fully aware what you're doing
+ */
+#ifndef _LINUX_SBPCD_H
+
+#define _LINUX_SBPCD_H
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ * driver's own read_ahead, data mode
+ */
+#define SBP_BUFFER_FRAMES 8 
+
+#define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */
+#undef  FUTURE
+#undef SAFE_MIXED
+
+#define TEST_UPC 0
+#define SPEA_TEST 0
+#define TEST_STI 0
+#define OLD_BUSY 0
+#undef PATH_CHECK
+#ifndef SOUND_BASE
+#define SOUND_BASE 0
+#endif
+#if DISTRIBUTION
+#undef SBP_TEAC_SPEED
+#define SBP_TEAC_SPEED 0
+#endif
+/*==========================================================================*/
+/*
+ * DDI interface definitions
+ * "invented" by Fred N. van Kempen..
+ */
+#define DDIOCSDBG	0x9000
+
+/*==========================================================================*/
+/*
+ * "private" IOCTL functions
+ */
+#define CDROMAUDIOBUFSIZ	0x5382 /* set the audio buffer size */
+
+/*==========================================================================*/
+/*
+ * Debug output levels
+ */
+#define DBG_INF	1	/* necessary information */
+#define DBG_BSZ	2	/* BLOCK_SIZE trace */
+#define DBG_REA	3	/* READ status trace */
+#define DBG_CHK	4	/* MEDIA CHECK trace */
+#define DBG_TIM	5	/* datarate timer test */
+#define DBG_INI	6	/* initialization trace */
+#define DBG_TOC	7	/* tell TocEntry values */
+#define DBG_IOC	8	/* ioctl trace */
+#define DBG_STA	9	/* ResponseStatus() trace */
+#define DBG_ERR	10	/* cc_ReadError() trace */
+#define DBG_CMD	11	/* cmd_out() trace */
+#define DBG_WRN	12	/* give explanation before auto-probing */
+#define DBG_MUL	13	/* multi session code test */
+#define DBG_IDX	14	/* test code for drive_id !=0 */
+#define DBG_IOX	15	/* some special information */
+#define DBG_DID	16	/* drive ID test */
+#define DBG_RES	17	/* drive reset info */
+#define DBG_SPI	18	/* SpinUp test */
+#define DBG_IOS	19	/* ioctl trace: subchannel functions */
+#define DBG_IO2	20	/* ioctl trace: general */
+#define DBG_UPC	21	/* show UPC information */
+#define DBG_XA1	22	/* XA mode debugging */
+#define DBG_LCK	23	/* door (un)lock info */
+#define DBG_SQ1	24	/* dump SubQ frame */
+#define DBG_AUD	25	/* READ AUDIO debugging */
+#define DBG_SEQ	26	/* Sequoia interface configuration trace */
+#define DBG_LCS	27	/* Longshine LCS-7260 debugging trace */
+#define DBG_CD2	28	/* MKE/Funai CD200 debugging trace */
+#define DBG_TEA	29	/* TEAC CD-55A debugging trace */
+#define DBG_ECS	30	/* ECS-AT (Vertos 100) debugging trace */
+#define DBG_000	31	/* unnecessary information */
+
+/*==========================================================================*/
+/*==========================================================================*/
+
+/*
+ * bits of flags_cmd_out:
+ */
+#define f_respo3		0x100
+#define f_putcmd		0x80
+#define f_respo2		0x40
+#define f_lopsta		0x20
+#define f_getsta		0x10
+#define f_ResponseStatus	0x08
+#define f_obey_p_check		0x04
+#define f_bit1			0x02
+#define f_wait_if_busy		0x01
+
+/*
+ * diskstate_flags:
+ */
+#define x80_bit			0x80
+#define upc_bit			0x40
+#define volume_bit		0x20
+#define toc_bit			0x10
+#define multisession_bit	0x08
+#define cd_size_bit		0x04
+#define subq_bit		0x02
+#define frame_size_bit		0x01
+
+/*
+ * disk states (bits of diskstate_flags):
+ */
+#define upc_valid		(D_S[d].diskstate_flags&upc_bit)
+#define volume_valid		(D_S[d].diskstate_flags&volume_bit)
+#define toc_valid		(D_S[d].diskstate_flags&toc_bit)
+#define cd_size_valid		(D_S[d].diskstate_flags&cd_size_bit)
+#define subq_valid		(D_S[d].diskstate_flags&subq_bit)
+#define frame_size_valid	(D_S[d].diskstate_flags&frame_size_bit)
+
+/*
+ * the status_bits variable
+ */
+#define p_success	0x100
+#define p_door_closed	0x80
+#define p_caddy_in	0x40
+#define p_spinning	0x20
+#define p_check		0x10
+#define p_busy_new	0x08
+#define p_door_locked	0x04
+#define p_disk_ok	0x01
+
+/*
+ * LCS-7260 special status result bits:
+ */
+#define p_lcs_door_locked	0x02
+#define p_lcs_door_closed	0x01 /* probably disk_in */
+
+/*
+ * CR-52x special status result bits:
+ */
+#define p_caddin_old	0x40
+#define p_success_old	0x08
+#define p_busy_old	0x04
+#define p_bit_1		0x02	/* hopefully unused now */
+
+/*
+ * "generation specific" defs of the status result bits:
+ */
+#define p0_door_closed	0x80
+#define p0_caddy_in	0x40
+#define p0_spinning	0x20
+#define p0_check	0x10
+#define p0_success	0x08 /* unused */
+#define p0_busy		0x04
+#define p0_bit_1	0x02 /* unused */
+#define p0_disk_ok	0x01
+
+#define pL_disk_in	0x40
+#define pL_spinning	0x20
+#define pL_check	0x10
+#define pL_success	0x08 /* unused ?? */
+#define pL_busy		0x04
+#define pL_door_locked	0x02
+#define pL_door_closed	0x01
+
+#define pV_door_closed	0x40
+#define pV_spinning	0x20
+#define pV_check	0x10
+#define pV_success	0x08
+#define pV_busy		0x04
+#define pV_door_locked	0x02
+#define pV_disk_ok	0x01
+
+#define p1_door_closed	0x80
+#define p1_disk_in	0x40
+#define p1_spinning	0x20
+#define p1_check	0x10
+#define p1_busy		0x08
+#define p1_door_locked	0x04
+#define p1_bit_1	0x02 /* unused */
+#define p1_disk_ok	0x01
+
+#define p2_disk_ok	0x80
+#define p2_door_locked	0x40
+#define p2_spinning	0x20
+#define p2_busy2	0x10
+#define p2_busy1	0x08
+#define p2_door_closed	0x04
+#define p2_disk_in	0x02
+#define p2_check	0x01
+
+/*
+ * used drive states:
+ */
+#define st_door_closed	(D_S[d].status_bits&p_door_closed)
+#define st_caddy_in	(D_S[d].status_bits&p_caddy_in)
+#define st_spinning	(D_S[d].status_bits&p_spinning)
+#define st_check	(D_S[d].status_bits&p_check)
+#define st_busy		(D_S[d].status_bits&p_busy_new)
+#define st_door_locked	(D_S[d].status_bits&p_door_locked)
+#define st_diskok	(D_S[d].status_bits&p_disk_ok)
+
+/*
+ * bits of the CDi_status register:
+ */
+#define s_not_result_ready	0x04 /* 0: "result ready" */
+#define s_not_data_ready	0x02 /* 0: "data ready"   */
+#define s_attention		0x01 /* 1: "attention required" */
+/*
+ * usable as:
+ */
+#define DRV_ATTN	((inb(CDi_status)&s_attention)!=0)
+#define DATA_READY	((inb(CDi_status)&s_not_data_ready)==0)
+#define RESULT_READY	((inb(CDi_status)&s_not_result_ready)==0)
+
+/*
+ * drive families and types (firmware versions):
+ */
+#define drv_fam0	0x0100		/* CR-52x family */
+#define drv_199		(drv_fam0+0x01)	/* <200 */
+#define drv_200		(drv_fam0+0x02)	/* <201 */
+#define drv_201		(drv_fam0+0x03)	/* <210 */
+#define drv_210		(drv_fam0+0x04)	/* <211 */
+#define drv_211		(drv_fam0+0x05)	/* <300 */
+#define drv_300		(drv_fam0+0x06)	/* >=300 */
+
+#define drv_fam1	0x0200		/* CR-56x family */
+#define drv_099		(drv_fam1+0x01)	/* <100 */
+#define drv_100		(drv_fam1+0x02)	/* >=100, only 1.02 and 5.00 known */
+
+#define drv_fam2	0x0400		/* CD200 family */
+
+#define drv_famT	0x0800		/* TEAC CD-55A */
+
+#define drv_famL	0x1000		/* Longshine family */
+#define drv_260		(drv_famL+0x01)	/* LCS-7260 */
+#define drv_e1		(drv_famL+0x01)	/* LCS-7260, firmware "A E1" */
+#define drv_f4		(drv_famL+0x02)	/* LCS-7260, firmware "A4F4" */
+
+#define drv_famV	0x2000		/* ECS-AT (vertos-100) family */
+#define drv_at		(drv_famV+0x01)	/* ECS-AT, firmware "1.00" */
+
+#define fam0_drive	(D_S[d].drv_type&drv_fam0)
+#define famL_drive	(D_S[d].drv_type&drv_famL)
+#define famV_drive	(D_S[d].drv_type&drv_famV)
+#define fam1_drive	(D_S[d].drv_type&drv_fam1)
+#define fam2_drive	(D_S[d].drv_type&drv_fam2)
+#define famT_drive	(D_S[d].drv_type&drv_famT)
+#define fam0L_drive	(D_S[d].drv_type&(drv_fam0|drv_famL))
+#define fam0V_drive	(D_S[d].drv_type&(drv_fam0|drv_famV))
+#define famLV_drive	(D_S[d].drv_type&(drv_famL|drv_famV))
+#define fam0LV_drive	(D_S[d].drv_type&(drv_fam0|drv_famL|drv_famV))
+#define fam1L_drive	(D_S[d].drv_type&(drv_fam1|drv_famL))
+#define fam1V_drive	(D_S[d].drv_type&(drv_fam1|drv_famV))
+#define fam1LV_drive	(D_S[d].drv_type&(drv_fam1|drv_famL|drv_famV))
+#define fam01_drive	(D_S[d].drv_type&(drv_fam0|drv_fam1))
+#define fam12_drive	(D_S[d].drv_type&(drv_fam1|drv_fam2))
+#define fam2T_drive	(D_S[d].drv_type&(drv_fam2|drv_famT))
+
+/*
+ * audio states:
+ */
+#define audio_playing	2
+#define audio_pausing	1
+
+/*
+ * drv_pattern, drv_options:
+ */
+#define speed_auto	0x80
+#define speed_300	0x40
+#define speed_150	0x20
+#define audio_mono	0x04
+
+/*
+ * values of cmd_type (0 else):
+ */
+#define READ_M1	0x01	/* "data mode 1": 2048 bytes per frame */
+#define READ_M2	0x02	/* "data mode 2": 12+2048+280 bytes per frame */
+#define READ_SC	0x04	/* "subchannel info": 96 bytes per frame */
+#define READ_AU	0x08	/* "audio frame": 2352 bytes per frame */
+
+/*
+ * sense_byte:
+ *
+ *          values: 00
+ *                  01
+ *                  81
+ *                  82 "raw audio" mode
+ *                  xx from infobuf[0] after 85 00 00 00 00 00 00
+ */
+
+/* audio status (bin) */
+#define aud_00 0x00 /* Audio status byte not supported or not valid */
+#define audx11 0x0b /* Audio play operation in progress             */
+#define audx12 0x0c /* Audio play operation paused                  */
+#define audx13 0x0d /* Audio play operation successfully completed  */
+#define audx14 0x0e /* Audio play operation stopped due to error    */
+#define audx15 0x0f /* No current audio status to return            */
+/* audio status (bcd) */
+#define aud_11 0x11 /* Audio play operation in progress             */
+#define aud_12 0x12 /* Audio play operation paused                  */
+#define aud_13 0x13 /* Audio play operation successfully completed  */
+#define aud_14 0x14 /* Audio play operation stopped due to error    */
+#define aud_15 0x15 /* No current audio status to return            */
+
+/*
+ * highest allowed drive number (MINOR+1)
+ */
+#define NR_SBPCD	4
+
+/*
+ * we try to never disable interrupts - seems to work
+ */
+#define SBPCD_DIS_IRQ	0
+
+/*
+ * "write byte to port"
+ */
+#define OUT(x,y)	outb(y,x)
+
+/*==========================================================================*/
+
+#define MIXER_addr SOUND_BASE+4 /* sound card's address register */
+#define MIXER_data SOUND_BASE+5 /* sound card's data register */
+#define MIXER_CD_Volume	0x28	/* internal SB Pro register address */
+
+/*==========================================================================*/
+
+#define MAX_TRACKS	99
+
+#define ERR_DISKCHANGE 615
+
+/*==========================================================================*/
+/*
+ * To make conversions easier (machine dependent!)
+ */
+typedef union _msf
+{
+	u_int n;
+	u_char c[4];
+} MSF;
+
+typedef union _blk
+{
+	u_int n;
+	u_char c[4];
+} BLK;
+
+/*==========================================================================*/
+
+/*============================================================================
+==============================================================================
+
+COMMAND SET of "old" drives like CR-521, CR-522
+               (the CR-562 family is different):
+
+No.	Command			       Code
+--------------------------------------------
+
+Drive Commands:
+ 1	Seek				01	
+ 2	Read Data			02
+ 3	Read XA-Data			03
+ 4	Read Header			04
+ 5	Spin Up				05
+ 6	Spin Down			06
+ 7	Diagnostic			07
+ 8	Read UPC			08
+ 9	Read ISRC			09
+10	Play Audio			0A
+11	Play Audio MSF			0B
+12	Play Audio Track/Index		0C
+
+Status Commands:
+13	Read Status			81	
+14	Read Error			82
+15	Read Drive Version		83
+16	Mode Select			84
+17	Mode Sense			85
+18	Set XA Parameter		86
+19	Read XA Parameter		87
+20	Read Capacity			88
+21	Read SUB_Q			89
+22	Read Disc Code			8A
+23	Read Disc Information		8B
+24	Read TOC			8C
+25	Pause/Resume			8D
+26	Read Packet			8E
+27	Read Path Check			00
+ 
+ 
+all numbers (lba, msf-bin, msf-bcd, counts) to transfer high byte first
+
+mnemo     7-byte command        #bytes response (r0...rn)
+________ ____________________  ____ 
+
+Read Status:
+status:  81.                    (1)  one-byte command, gives the main
+                                                          status byte
+Read Error:
+check1:  82 00 00 00 00 00 00.  (6)  r1: audio status
+
+Read Packet:
+check2:  8e xx 00 00 00 00 00. (xx)  gets xx bytes response, relating
+                                        to commands 01 04 05 07 08 09
+
+Play Audio:
+play:    0a ll-bb-aa nn-nn-nn.  (0)  play audio, ll-bb-aa: starting block (lba),
+                                                 nn-nn-nn: #blocks
+Play Audio MSF:
+         0b mm-ss-ff mm-ss-ff   (0)  play audio from/to
+
+Play Audio Track/Index:
+         0c ...
+
+Pause/Resume:
+pause:   8d pr 00 00 00 00 00.  (0)  pause (pr=00) 
+                                     resume (pr=80) audio playing
+
+Mode Select:
+         84 00 nn-nn ??-?? 00   (0)  nn-nn: 2048 or 2340
+                                     possibly defines transfer size
+
+set_vol: 84 83 00 00 sw le 00.  (0)  sw(itch): lrxxxxxx (off=1)
+                                     le(vel): min=0, max=FF, else half
+				     (firmware 2.11)
+
+Mode Sense:
+get_vol: 85 03 00 00 00 00 00.  (2)  tell current audio volume setting
+
+Read Disc Information:
+tocdesc: 8b 00 00 00 00 00 00.  (6)  read the toc descriptor ("msf-bin"-format)
+
+Read TOC:
+tocent:  8c fl nn 00 00 00 00.  (8)  read toc entry #nn
+                                       (fl=0:"lba"-, =2:"msf-bin"-format)
+
+Read Capacity:
+capacit: 88 00 00 00 00 00 00.  (5)  "read CD-ROM capacity"
+
+
+Read Path Check:
+ping:    00 00 00 00 00 00 00.  (2)  r0=AA, r1=55
+                                     ("ping" if the drive is connected)
+
+Read Drive Version:
+ident:   83 00 00 00 00 00 00. (12)  gives "MATSHITAn.nn" 
+                                     (n.nn = 2.01, 2.11., 3.00, ...)
+
+Seek:
+seek:    01 00 ll-bb-aa 00 00.  (0)  
+seek:    01 02 mm-ss-ff 00 00.  (0)  
+
+Read Data:
+read:    02 xx-xx-xx nn-nn fl. (??)  read nn-nn blocks of 2048 bytes,
+                                     starting at block xx-xx-xx  
+                                     fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx
+
+Read XA-Data:
+read:    03 xx-xx-xx nn-nn fl. (??)  read nn-nn blocks of 2340 bytes, 
+                                     starting at block xx-xx-xx
+                                     fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx
+
+Read SUB_Q:
+         89 fl 00 00 00 00 00. (13)  r0: audio status, r4-r7: lba/msf, 
+                                       fl=0: "lba", fl=2: "msf"
+
+Read Disc Code:
+         8a 00 00 00 00 00 00. (14)  possibly extended "check condition"-info
+
+Read Header:
+         04 00 ll-bb-aa 00 00.  (0)   4 bytes response with "check2"
+         04 02 mm-ss-ff 00 00.  (0)   4 bytes response with "check2"
+
+Spin Up:
+         05 00 ll-bb-aa 00 00.  (0)  possibly implies a "seek"
+
+Spin Down:
+         06 ...
+
+Diagnostic:
+         07 00 ll-bb-aa 00 00.  (2)   2 bytes response with "check2"
+         07 02 mm-ss-ff 00 00.  (2)   2 bytes response with "check2"
+
+Read UPC:
+         08 00 ll-bb-aa 00 00. (16)  
+         08 02 mm-ss-ff 00 00. (16)  
+
+Read ISRC:
+         09 00 ll-bb-aa 00 00. (15)  15 bytes response with "check2"
+         09 02 mm-ss-ff 00 00. (15)  15 bytes response with "check2"
+
+Set XA Parameter:
+         86 ...
+
+Read XA Parameter:
+         87 ...
+
+==============================================================================
+============================================================================*/
+
+/*
+ * commands
+ *
+ * CR-52x:      CMD0_
+ * CR-56x:      CMD1_
+ * CD200:       CMD2_
+ * LCS-7260:    CMDL_
+ * TEAC CD-55A: CMDT_
+ * ECS-AT:      CMDV_
+ */
+#define CMD1_RESET	0x0a
+#define CMD2_RESET	0x01
+#define CMDT_RESET	0xc0
+
+#define CMD1_LOCK_CTL	0x0c
+#define CMD2_LOCK_CTL	0x1e
+#define CMDT_LOCK_CTL	CMD2_LOCK_CTL
+#define CMDL_LOCK_CTL	0x0e
+#define CMDV_LOCK_CTL	CMDL_LOCK_CTL
+
+#define CMD1_TRAY_CTL	0x07
+#define CMD2_TRAY_CTL	0x1b
+#define CMDT_TRAY_CTL	CMD2_TRAY_CTL
+#define CMDL_TRAY_CTL	0x0d
+#define CMDV_TRAY_CTL	CMDL_TRAY_CTL
+
+#define CMD1_MULTISESS	0x8d
+#define CMDL_MULTISESS	0x8c
+#define CMDV_MULTISESS	CMDL_MULTISESS
+
+#define CMD1_SUBCHANINF	0x11
+#define CMD2_SUBCHANINF	0x??
+
+#define CMD1_ABORT	0x08
+#define CMD2_ABORT	0x08
+#define CMDT_ABORT	0x08
+
+#define CMD2_x02	0x02
+
+#define CMD2_SETSPEED	0xda
+
+#define CMD0_PATH_CHECK	0x00
+#define CMD1_PATH_CHECK	0x???
+#define CMD2_PATH_CHECK	0x???
+#define CMDT_PATH_CHECK	0x???
+#define CMDL_PATH_CHECK	CMD0_PATH_CHECK
+#define CMDV_PATH_CHECK	CMD0_PATH_CHECK
+
+#define CMD0_SEEK	0x01
+#define CMD1_SEEK	CMD0_SEEK
+#define CMD2_SEEK	0x2b
+#define CMDT_SEEK	CMD2_SEEK
+#define CMDL_SEEK	CMD0_SEEK
+#define CMDV_SEEK	CMD0_SEEK
+
+#define CMD0_READ	0x02
+#define CMD1_READ	0x10
+#define CMD2_READ	0x28
+#define CMDT_READ	CMD2_READ
+#define CMDL_READ	CMD0_READ
+#define CMDV_READ	CMD0_READ
+
+#define CMD0_READ_XA	0x03
+#define CMD2_READ_XA	0xd4
+#define CMD2_READ_XA2	0xd5
+#define CMDL_READ_XA	CMD0_READ_XA /* really ?? */
+#define CMDV_READ_XA	CMD0_READ_XA
+
+#define CMD0_READ_HEAD	0x04
+
+#define CMD0_SPINUP	0x05
+#define CMD1_SPINUP	0x02
+#define CMD2_SPINUP	CMD2_TRAY_CTL
+#define CMDL_SPINUP	CMD0_SPINUP
+#define CMDV_SPINUP	CMD0_SPINUP
+
+#define CMD0_SPINDOWN	0x06 /* really??? */
+#define CMD1_SPINDOWN	0x06
+#define CMD2_SPINDOWN	CMD2_TRAY_CTL
+#define CMDL_SPINDOWN	0x0d
+#define CMDV_SPINDOWN	CMD0_SPINDOWN
+
+#define CMD0_DIAG	0x07
+
+#define CMD0_READ_UPC	0x08
+#define CMD1_READ_UPC	0x88
+#define CMD2_READ_UPC	0x???
+#define CMDL_READ_UPC	CMD0_READ_UPC
+#define CMDV_READ_UPC	0x8f
+
+#define CMD0_READ_ISRC	0x09
+
+#define CMD0_PLAY	0x0a
+#define CMD1_PLAY	0x???
+#define CMD2_PLAY	0x???
+#define CMDL_PLAY	CMD0_PLAY
+#define CMDV_PLAY	CMD0_PLAY
+
+#define CMD0_PLAY_MSF	0x0b
+#define CMD1_PLAY_MSF	0x0e
+#define CMD2_PLAY_MSF	0x47
+#define CMDT_PLAY_MSF	CMD2_PLAY_MSF
+#define CMDL_PLAY_MSF	0x???
+
+#define CMD0_PLAY_TI	0x0c
+#define CMD1_PLAY_TI	0x0f
+
+#define CMD0_STATUS	0x81
+#define CMD1_STATUS	0x05
+#define CMD2_STATUS	0x00
+#define CMDT_STATUS	CMD2_STATUS
+#define CMDL_STATUS	CMD0_STATUS
+#define CMDV_STATUS	CMD0_STATUS
+#define CMD2_SEEK_LEADIN 0x00
+
+#define CMD0_READ_ERR	0x82
+#define CMD1_READ_ERR	CMD0_READ_ERR
+#define CMD2_READ_ERR	0x03
+#define CMDT_READ_ERR	CMD2_READ_ERR /* get audio status */
+#define CMDL_READ_ERR	CMD0_READ_ERR
+#define CMDV_READ_ERR	CMD0_READ_ERR
+
+#define CMD0_READ_VER	0x83
+#define CMD1_READ_VER	CMD0_READ_VER
+#define CMD2_READ_VER	0x12
+#define CMDT_READ_VER	CMD2_READ_VER /* really ?? */
+#define CMDL_READ_VER	CMD0_READ_VER
+#define CMDV_READ_VER	CMD0_READ_VER
+
+#define CMD0_SETMODE	0x84
+#define CMD1_SETMODE	0x09
+#define CMD2_SETMODE	0x55
+#define CMDT_SETMODE	CMD2_SETMODE
+#define CMDL_SETMODE	CMD0_SETMODE
+
+#define CMD0_GETMODE	0x85
+#define CMD1_GETMODE	0x84
+#define CMD2_GETMODE	0x5a
+#define CMDT_GETMODE	CMD2_GETMODE
+#define CMDL_GETMODE	CMD0_GETMODE
+
+#define CMD0_SET_XA	0x86
+
+#define CMD0_GET_XA	0x87
+
+#define CMD0_CAPACITY	0x88
+#define CMD1_CAPACITY	0x85
+#define CMD2_CAPACITY	0x25
+#define CMDL_CAPACITY	CMD0_CAPACITY /* missing in some firmware versions */
+
+#define CMD0_READSUBQ	0x89
+#define CMD1_READSUBQ	0x87
+#define CMD2_READSUBQ	0x42
+#define CMDT_READSUBQ	CMD2_READSUBQ
+#define CMDL_READSUBQ	CMD0_READSUBQ
+#define CMDV_READSUBQ	CMD0_READSUBQ
+
+#define CMD0_DISKCODE	0x8a
+
+#define CMD0_DISKINFO	0x8b
+#define CMD1_DISKINFO	CMD0_DISKINFO
+#define CMD2_DISKINFO	0x43
+#define CMDT_DISKINFO	CMD2_DISKINFO
+#define CMDL_DISKINFO	CMD0_DISKINFO
+#define CMDV_DISKINFO	CMD0_DISKINFO
+
+#define CMD0_READTOC	0x8c
+#define CMD1_READTOC	CMD0_READTOC
+#define CMD2_READTOC	0x???
+#define CMDL_READTOC	CMD0_READTOC
+#define CMDV_READTOC	CMD0_READTOC
+
+#define CMD0_PAU_RES	0x8d
+#define CMD1_PAU_RES	0x0d
+#define CMD2_PAU_RES	0x4b
+#define CMDT_PAUSE	CMD2_PAU_RES
+#define CMDL_PAU_RES	CMD0_PAU_RES
+#define CMDV_PAUSE	CMD0_PAU_RES
+
+#define CMD0_PACKET	0x8e
+#define CMD1_PACKET	CMD0_PACKET
+#define CMD2_PACKET	0x???
+#define CMDL_PACKET	CMD0_PACKET
+#define CMDV_PACKET	0x???
+
+/*==========================================================================*/
+/*==========================================================================*/
+#endif _LINUX_SBPCD_H
+/*==========================================================================*/
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file. 
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/sjcd.c linux/drivers/cdrom/sjcd.c
--- v2.1.69/linux/drivers/cdrom/sjcd.c	Tue May 13 22:41:06 1997
+++ linux/drivers/cdrom/sjcd.c	Tue Dec  2 11:41:45 1997
@@ -73,7 +73,7 @@
 
 #define MAJOR_NR SANYO_CDROM_MAJOR
 #include <linux/blk.h>
-#include <linux/sjcd.h>
+#include "sjcd.h"
 
 static int sjcd_present = 0;
 
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/sjcd.h linux/drivers/cdrom/sjcd.h
--- v2.1.69/linux/drivers/cdrom/sjcd.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/cdrom/sjcd.h	Tue Dec  2 11:41:45 1997
@@ -0,0 +1,181 @@
+/*
+ * Definitions for a Sanyo CD-ROM interface.
+ *
+ *   Copyright (C) 1995  Vadim V. Model
+ *                                       model@cecmow.enet.dec.com
+ *                                       vadim@rbrf.msk.su
+ *                                       vadim@ipsun.ras.ru
+ *                       Eric van der Maarel
+ *                                       H.T.M.v.d.Maarel@marin.nl
+ *
+ *  This information is based on mcd.c from M. Harriss and sjcd102.lst from
+ *  E. Moenkeberg.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SJCD_H__
+#define __SJCD_H__
+
+/*
+ * Change this to set the I/O port address as default. More flexibility
+ * come with setup implementation.
+ */
+#define SJCD_BASE_ADDR      0x340
+
+/*
+ * Change this to set the irq as default. Really SANYO do not use interrupts
+ * at all.
+ */
+#define SJCD_INTR_NR        0
+
+/*
+ * Change this to set the dma as default value. really SANYO does not use
+ * direct memory access at all.
+ */
+#define SJCD_DMA_NR         0
+
+/*
+ * Macros which allow us to find out the status of the drive.
+ */
+#define SJCD_STATUS_AVAILABLE( x ) (((x)&0x02)==0)
+#define SJCD_DATA_AVAILABLE( x )   (((x)&0x01)==0)
+
+/*
+ * Port access macro. Three ports are available: S-data port (command port),
+ * status port (read only) and D-data port (read only).
+ */
+#define SJCDPORT( x )       ( sjcd_base + ( x ) )
+#define SJCD_STATUS_PORT    SJCDPORT( 1 )
+#define SJCD_S_DATA_PORT    SJCDPORT( 0 )
+#define SJCD_COMMAND_PORT   SJCDPORT( 0 )
+#define SJCD_D_DATA_PORT    SJCDPORT( 2 )
+
+/*
+ * Drive info bits. Drive info available as first (mandatory) byte of
+ * command completion status.
+ */
+#define SST_NOT_READY       0x10        /* no disk in the drive (???) */
+#define SST_MEDIA_CHANGED   0x20        /* disk is changed */
+#define SST_DOOR_OPENED     0x40        /* door is open */
+
+/* commands */
+
+#define SCMD_EJECT_TRAY     0xD0        /* eject tray if not locked */
+#define SCMD_LOCK_TRAY      0xD2        /* lock tray when in */
+#define SCMD_UNLOCK_TRAY    0xD4        /* unlock tray when in */
+#define SCMD_CLOSE_TRAY     0xD6        /* load tray in */
+
+#define SCMD_RESET          0xFA        /* soft reset */
+#define SCMD_GET_STATUS     0x80
+#define SCMD_GET_VERSION    0xCC
+
+#define SCMD_DATA_READ      0xA0        /* are the same, depend on mode&args */
+#define SCMD_SEEK           0xA0
+#define SCMD_PLAY           0xA0
+
+#define SCMD_GET_QINFO      0xA8
+
+#define SCMD_SET_MODE       0xC4
+#define SCMD_MODE_PLAY      0xE0
+#define SCMD_MODE_COOKED    (0xF8 & ~0x20)
+#define SCMD_MODE_RAW       0xF9
+#define SCMD_MODE_x20_BIT   0x20        /* What is it for ? */
+
+#define SCMD_SET_VOLUME     0xAE
+#define SCMD_PAUSE          0xE0
+#define SCMD_STOP           0xE0
+
+#define SCMD_GET_DISK_INFO  0xAA
+
+/*
+ * Some standard arguments for SCMD_GET_DISK_INFO.
+ */
+#define SCMD_GET_1_TRACK    0xA0    /* get the first track information */
+#define SCMD_GET_L_TRACK    0xA1    /* get the last track information */
+#define SCMD_GET_D_SIZE     0xA2    /* get the whole disk information */
+
+/*
+ * Borrowed from hd.c. Allows to optimize multiple port read commands.
+ */
+#define S_READ_DATA( port, buf, nr )      insb( port, buf, nr )
+
+/*
+ * We assume that there are no audio disks with TOC length more than this
+ * number (I personally have never seen disks with more than 20 fragments).
+ */
+#define SJCD_MAX_TRACKS		100
+
+struct msf {
+  unsigned char   min;
+  unsigned char   sec;
+  unsigned char   frame;
+};
+
+struct sjcd_hw_disk_info {
+  unsigned char track_control;
+  unsigned char track_no;
+  unsigned char x, y, z;
+  union {
+    unsigned char track_no;
+    struct msf track_msf;
+  } un;
+};
+
+struct sjcd_hw_qinfo {
+  unsigned char track_control;
+  unsigned char track_no;
+  unsigned char x;
+  struct msf rel;
+  struct msf abs;
+};
+
+struct sjcd_play_msf {
+  struct msf  start;
+  struct msf  end;
+};
+
+struct sjcd_disk_info {
+  unsigned char   first;
+  unsigned char   last;
+  struct msf      disk_length;
+  struct msf      first_track;
+};
+
+struct sjcd_toc {
+  unsigned char   ctrl_addr;
+  unsigned char   track;
+  unsigned char   point_index;
+  struct msf      track_time;
+  struct msf      disk_time;
+};
+
+#if defined( SJCD_GATHER_STAT )
+
+struct sjcd_stat {
+  int ticks;
+  int tticks[ 8 ];
+  int idle_ticks;
+  int start_ticks;
+  int mode_ticks;
+  int read_ticks;
+  int data_ticks;
+  int stop_ticks;
+  int stopping_ticks;
+};
+
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/sonycd535.c linux/drivers/cdrom/sonycd535.c
--- v2.1.69/linux/drivers/cdrom/sonycd535.c	Tue May 13 22:41:06 1997
+++ linux/drivers/cdrom/sonycd535.c	Tue Dec  2 11:41:45 1997
@@ -132,7 +132,7 @@
 #define MAJOR_NR CDU535_CDROM_MAJOR
 # include <linux/blk.h>
 #define sony535_cd_base_io sonycd535 /* for compatible parameter passing with "insmod" */
-#include <linux/sonycd535.h>
+#include "sonycd535.h"
 
 /*
  * this is the base address of the interface card for the Sony CDU-535
diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/sonycd535.h linux/drivers/cdrom/sonycd535.h
--- v2.1.69/linux/drivers/cdrom/sonycd535.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/cdrom/sonycd535.h	Tue Dec  2 11:41:45 1997
@@ -0,0 +1,183 @@
+#ifndef SONYCD535_H
+#define SONYCD535_H
+
+/*
+ * define all the commands recognized by the CDU-531/5
+ */
+#define SONY535_REQUEST_DRIVE_STATUS_1		(0x80)
+#define SONY535_REQUEST_SENSE			(0x82)
+#define SONY535_REQUEST_DRIVE_STATUS_2		(0x84)
+#define SONY535_REQUEST_ERROR_STATUS		(0x86)
+#define SONY535_REQUEST_AUDIO_STATUS		(0x88)
+#define SONY535_INQUIRY				(0x8a)
+
+#define SONY535_SET_INACTIVITY_TIME		(0x90)
+
+#define SONY535_SEEK_AND_READ_N_BLOCKS_1	(0xa0)
+#define SONY535_SEEK_AND_READ_N_BLOCKS_2	(0xa4)
+#define SONY535_PLAY_AUDIO			(0xa6)
+
+#define SONY535_REQUEST_DISC_CAPACITY		(0xb0)
+#define SONY535_REQUEST_TOC_DATA		(0xb2)
+#define SONY535_REQUEST_SUB_Q_DATA		(0xb4)
+#define SONY535_REQUEST_ISRC			(0xb6)
+#define SONY535_REQUEST_UPC_EAN			(0xb8)
+
+#define SONY535_SET_DRIVE_MODE			(0xc0)
+#define SONY535_REQUEST_DRIVE_MODE		(0xc2)
+#define SONY535_SET_RETRY_COUNT			(0xc4)
+
+#define SONY535_DIAGNOSTIC_1			(0xc6)
+#define SONY535_DIAGNOSTIC_4			(0xcc)
+#define SONY535_DIAGNOSTIC_5			(0xce)
+
+#define SONY535_EJECT_CADDY			(0xd0)
+#define SONY535_DISABLE_EJECT_BUTTON		(0xd2)
+#define SONY535_ENABLE_EJECT_BUTTON		(0xd4)
+
+#define SONY535_HOLD				(0xe0)
+#define SONY535_AUDIO_PAUSE_ON_OFF		(0xe2)
+#define SONY535_SET_VOLUME			(0xe8)
+
+#define SONY535_STOP				(0xf0)
+#define SONY535_SPIN_UP				(0xf2)
+#define SONY535_SPIN_DOWN			(0xf4)
+
+#define SONY535_CLEAR_PARAMETERS		(0xf6)
+#define SONY535_CLEAR_ENDING_ADDRESS		(0xf8)
+
+/*
+ * define some masks
+ */
+#define SONY535_DATA_NOT_READY_BIT		(0x1)
+#define SONY535_RESULT_NOT_READY_BIT		(0x2)
+
+/*
+ *  drive status 1
+ */
+#define SONY535_STATUS1_COMMAND_ERROR		(0x1)
+#define SONY535_STATUS1_DATA_ERROR		(0x2)
+#define SONY535_STATUS1_SEEK_ERROR		(0x4)
+#define SONY535_STATUS1_DISC_TYPE_ERROR		(0x8)
+#define SONY535_STATUS1_NOT_SPINNING		(0x10)
+#define SONY535_STATUS1_EJECT_BUTTON_PRESSED	(0x20)
+#define SONY535_STATUS1_CADDY_NOT_INSERTED	(0x40)
+#define SONY535_STATUS1_BYTE_TWO_FOLLOWS	(0x80)
+
+/*
+ * drive status 2
+ */
+#define SONY535_CDD_LOADING_ERROR		(0x7)
+#define SONY535_CDD_NO_DISC			(0x8)
+#define SONY535_CDD_UNLOADING_ERROR		(0x9)
+#define SONY535_CDD_CADDY_NOT_INSERTED		(0xd)
+#define SONY535_ATN_RESET_OCCURRED		(0x2)
+#define SONY535_ATN_DISC_CHANGED		(0x4)
+#define SONY535_ATN_RESET_AND_DISC_CHANGED	(0x6)
+#define SONY535_ATN_EJECT_IN_PROGRESS		(0xe)
+#define SONY535_ATN_BUSY			(0xf)
+
+/*
+ * define some parameters
+ */
+#define SONY535_AUDIO_DRIVE_MODE		(0)
+#define SONY535_CDROM_DRIVE_MODE		(0xe0)
+
+#define SONY535_PLAY_OP_PLAYBACK		(0)
+#define SONY535_PLAY_OP_ENTER_HOLD		(1)
+#define SONY535_PLAY_OP_SET_AUDIO_ENDING_ADDR	(2)
+#define SONY535_PLAY_OP_SCAN_FORWARD		(3)
+#define SONY535_PLAY_OP_SCAN_BACKWARD		(4)
+
+/*
+ *  convert from msf format to block number 
+ */
+#define SONY_BLOCK_NUMBER(m,s,f) (((m)*60L+(s))*75L+(f))
+#define SONY_BLOCK_NUMBER_MSF(x) (((x)[0]*60L+(x)[1])*75L+(x)[2])
+
+/*
+ *  error return values from the doSonyCmd() routines
+ */
+#define TIME_OUT			(-1)
+#define NO_CDROM			(-2)
+#define BAD_STATUS			(-3)
+#define CD_BUSY				(-4)
+#define NOT_DATA_CD			(-5)
+#define NO_ROOM				(-6)
+
+#define LOG_START_OFFSET        150     /* Offset of first logical sector */
+
+#define SONY_JIFFIES_TIMEOUT	(5*HZ)	/* Maximum time
+					   the drive will wait/try for an
+					   operation */
+#define SONY_READY_RETRIES      (50000)  /* How many times to retry a
+                                                  spin waiting for a register
+                                                  to come ready */
+#define SONY535_FAST_POLLS	(10000)   /* how many times recheck 
+                                                  status waiting for a data
+                                                  to become ready */
+
+typedef unsigned char Byte;
+
+/*
+ * This is the complete status returned from the drive configuration request
+ * command.
+ */
+struct s535_sony_drive_config
+{
+   char vendor_id[8];
+   char product_id[16];
+   char product_rev_level[4];
+};
+
+/* The following is returned from the request sub-q data command */
+struct s535_sony_subcode
+{
+   unsigned char address        :4;
+   unsigned char control        :4;
+   unsigned char track_num;
+   unsigned char index_num;
+   unsigned char rel_msf[3];
+   unsigned char abs_msf[3];
+};
+
+struct s535_sony_disc_capacity
+{
+   Byte mFirstTrack, sFirstTrack, fFirstTrack;
+   Byte mLeadOut, sLeadOut, fLeadOut;
+};
+
+/*
+ * The following is returned from the request TOC (Table Of Contents) command.
+ * (last_track_num-first_track_num+1) values are valid in tracks.
+ */
+struct s535_sony_toc
+{
+   unsigned char reserved0      :4;
+   unsigned char control0       :4;
+   unsigned char point0;
+   unsigned char first_track_num;
+   unsigned char reserved0a;
+   unsigned char reserved0b;
+   unsigned char reserved1      :4;
+   unsigned char control1       :4;
+   unsigned char point1;
+   unsigned char last_track_num;
+   unsigned char dummy1;
+   unsigned char dummy2;
+   unsigned char reserved2      :4;
+   unsigned char control2       :4;
+   unsigned char point2;
+   unsigned char lead_out_start_msf[3];
+   struct
+   {
+      unsigned char reserved    :4;
+      unsigned char control     :4;
+      unsigned char track;
+      unsigned char track_start_msf[3];
+   } tracks[100];
+
+   unsigned int lead_out_start_lba;
+};
+
+#endif /* SONYCD535_H */
diff -u --recursive --new-file v2.1.69/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.69/linux/drivers/char/Config.in	Sat Nov 29 11:25:09 1997
+++ linux/drivers/char/Config.in	Tue Dec  2 09:19:03 1997
@@ -31,6 +31,10 @@
      tristate '  Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION
    fi
    tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8
+   tristate 'Specialix IO8+ card support' CONFIG_SPECIALIX
+   if [ "$CONFIG_SPECIALIX" = "y" -o  "$CONFIG_SPECIALIX" = "m" ]; then
+     bool 'Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS
+   fi 
    tristate 'Hayes ESP serial port support' CONFIG_ESPSERIAL
    if [ "$CONFIG_ESPSERIAL" = "y" -o "$CONFIG_ESPSERIAL" = "m" ]; then
      int '  DMA channel' CONFIG_ESPSERIAL_DMA_CHANNEL 1
diff -u --recursive --new-file v2.1.69/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.1.69/linux/drivers/char/Makefile	Sat Nov 29 11:25:09 1997
+++ linux/drivers/char/Makefile	Tue Dec  2 09:19:03 1997
@@ -123,6 +123,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SPECIALIX),y)
+L_OBJS += specialix.o
+else
+  ifeq ($(CONFIG_SPECIALIX),m)
+  M_OBJS += specialix.o
+  endif
+endif
+
 ifeq ($(CONFIG_ATIXL_BUSMOUSE),y)
 M = y
 L_OBJS += atixlmouse.o
diff -u --recursive --new-file v2.1.69/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.1.69/linux/drivers/char/bttv.c	Sat Nov 29 11:25:09 1997
+++ linux/drivers/char/bttv.c	Tue Dec  2 09:37:34 1997
@@ -54,6 +54,7 @@
 static unsigned int remap=0;
 static unsigned int vidmem=0;
 static unsigned int tuner=0;	/* Default tuner */
+MODULE_PARM(tuner,"i");
 
 static int find_vga(void);
 static void bt848_set_risc_jmps(struct bttv *btv);
@@ -603,7 +604,7 @@
 			}
 			interruptible_sleep_on(&btv->vbiq);
 			sti();
-			if(current->signal & ~current->blocked)
+			if(signal_pending(current))
 			{
 				if(todo==count)
 					return -EINTR;
diff -u --recursive --new-file v2.1.69/linux/drivers/char/cd1865.h linux/drivers/char/cd1865.h
--- v2.1.69/linux/drivers/char/cd1865.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/cd1865.h	Tue Dec  2 09:19:03 1997
@@ -0,0 +1,263 @@
+/*
+ *      linux/drivers/char/cd1865.h -- Definitions relating to the CD1865
+ *                          for the Specialix IO8+ multiport serial driver.
+ *
+ *      Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *
+ *      Specialix pays for the development and support of this driver.
+ *      Please DO contact io8-linux@specialix.co.uk if you require
+ *      support.
+ *
+ *      This driver was developped in the BitWizard linux device
+ *      driver service. If you require a linux device driver for your
+ *      product, please contact devices@BitWizard.nl for a quote.
+ *
+ */
+
+/*
+ * Definitions for Driving CD180/CD1864/CD1865 based eightport serial cards.
+ */
+
+
+/* Values of choice for Interrupt ACKs */
+/* These values are "obligatory" if you use the register based
+ * interrupt acknowledgements. See page 99-101 of V2.0 of the CD1865
+ * databook */
+#define SX_ACK_MINT     0x75    /* goes to PILR1                           */
+#define SX_ACK_TINT     0x76    /* goes to PILR2                           */
+#define SX_ACK_RINT     0x77    /* goes to PILR3                           */
+
+/* Chip ID (is used when chips ar daisy chained.) */
+#define SX_ID           0x10
+
+/* Definitions for Cirrus Logic CL-CD186x 8-port async mux chip */
+ 
+#define CD186x_NCH       8       /* Total number of channels                */
+#define CD186x_TPC       16      /* Ticks per character                     */
+#define CD186x_NFIFO	 8	 /* TX FIFO size                            */
+
+
+/* Global registers */
+
+#define CD186x_GIVR      0x40    /* Global Interrupt Vector Register        */
+#define CD186x_GICR      0x41    /* Global Interrupting Channel Register    */
+#define CD186x_PILR1     0x61    /* Priority Interrupt Level Register 1     */
+#define CD186x_PILR2     0x62    /* Priority Interrupt Level Register 2     */
+#define CD186x_PILR3     0x63    /* Priority Interrupt Level Register 3     */
+#define CD186x_CAR       0x64    /* Channel Access Register                 */
+#define CD186x_SRSR      0x65    /* Channel Access Register                 */
+#define CD186x_GFRCR     0x6b    /* Global Firmware Revision Code Register  */
+#define CD186x_PPRH      0x70    /* Prescaler Period Register High          */
+#define CD186x_PPRL      0x71    /* Prescaler Period Register Low           */
+#define CD186x_RDR       0x78    /* Receiver Data Register                  */
+#define CD186x_RCSR      0x7a    /* Receiver Character Status Register      */
+#define CD186x_TDR       0x7b    /* Transmit Data Register                  */
+#define CD186x_EOIR      0x7f    /* End of Interrupt Register               */
+#define CD186x_MRAR      0x75    /* Modem Request Acknowlege register       */
+#define CD186x_TRAR      0x76    /* Transmit Request Acknowlege register    */
+#define CD186x_RRAR      0x77    /* Recieve Request Acknowlege register     */
+#define CD186x_SRCR      0x66    /* Service Request Configuration register  */
+
+/* Channel Registers */
+
+#define CD186x_CCR       0x01    /* Channel Command Register                */
+#define CD186x_IER       0x02    /* Interrupt Enable Register               */
+#define CD186x_COR1      0x03    /* Channel Option Register 1               */
+#define CD186x_COR2      0x04    /* Channel Option Register 2               */
+#define CD186x_COR3      0x05    /* Channel Option Register 3               */
+#define CD186x_CCSR      0x06    /* Channel Control Status Register         */
+#define CD186x_RDCR      0x07    /* Receive Data Count Register             */
+#define CD186x_SCHR1     0x09    /* Special Character Register 1            */
+#define CD186x_SCHR2     0x0a    /* Special Character Register 2            */
+#define CD186x_SCHR3     0x0b    /* Special Character Register 3            */
+#define CD186x_SCHR4     0x0c    /* Special Character Register 4            */
+#define CD186x_MCOR1     0x10    /* Modem Change Option 1 Register          */
+#define CD186x_MCOR2     0x11    /* Modem Change Option 2 Register          */
+#define CD186x_MCR       0x12    /* Modem Change Register                   */
+#define CD186x_RTPR      0x18    /* Receive Timeout Period Register         */
+#define CD186x_MSVR      0x28    /* Modem Signal Value Register             */
+#define CD186x_MSVRTS    0x29    /* Modem Signal Value Register             */
+#define CD186x_MSVDTR    0x2a    /* Modem Signal Value Register             */
+#define CD186x_RBPRH     0x31    /* Receive Baud Rate Period Register High  */
+#define CD186x_RBPRL     0x32    /* Receive Baud Rate Period Register Low   */
+#define CD186x_TBPRH     0x39    /* Transmit Baud Rate Period Register High */
+#define CD186x_TBPRL     0x3a    /* Transmit Baud Rate Period Register Low  */
+
+
+/* Global Interrupt Vector Register (R/W) */
+
+#define GIVR_ITMASK     0x07     /* Interrupt type mask                     */
+#define  GIVR_IT_MODEM   0x01    /* Modem Signal Change Interrupt           */
+#define  GIVR_IT_TX      0x02    /* Transmit Data Interrupt                 */
+#define  GIVR_IT_RCV     0x03    /* Receive Good Data Interrupt             */
+#define  GIVR_IT_REXC    0x07    /* Receive Exception Interrupt             */
+
+
+/* Global Interrupt Channel Register (R/W) */
+ 
+#define GICR_CHAN       0x1c    /* Channel Number Mask                     */
+#define GICR_CHAN_OFF   2       /* Channel Number shift                    */
+
+
+/* Channel Address Register (R/W) */
+
+#define CAR_CHAN        0x07    /* Channel Number Mask                     */
+#define CAR_A7          0x08    /* A7 Address Extension (unused)           */
+
+
+/* Receive Character Status Register (R/O) */
+
+#define RCSR_TOUT       0x80    /* Rx Timeout                              */
+#define RCSR_SCDET      0x70    /* Special Character Detected Mask         */
+#define  RCSR_NO_SC      0x00   /* No Special Characters Detected          */
+#define  RCSR_SC_1       0x10   /* Special Char 1 (or 1 & 3) Detected      */
+#define  RCSR_SC_2       0x20   /* Special Char 2 (or 2 & 4) Detected      */
+#define  RCSR_SC_3       0x30   /* Special Char 3 Detected                 */
+#define  RCSR_SC_4       0x40   /* Special Char 4 Detected                 */
+#define RCSR_BREAK      0x08    /* Break has been detected                 */
+#define RCSR_PE         0x04    /* Parity Error                            */
+#define RCSR_FE         0x02    /* Frame Error                             */
+#define RCSR_OE         0x01    /* Overrun Error                           */
+
+
+/* Channel Command Register (R/W) (commands in groups can be OR-ed) */
+
+#define CCR_HARDRESET   0x81    /* Reset the chip                          */
+
+#define CCR_SOFTRESET   0x80    /* Soft Channel Reset                      */
+
+#define CCR_CORCHG1     0x42    /* Channel Option Register 1 Changed       */
+#define CCR_CORCHG2     0x44    /* Channel Option Register 2 Changed       */
+#define CCR_CORCHG3     0x48    /* Channel Option Register 3 Changed       */
+
+#define CCR_SSCH1       0x21    /* Send Special Character 1                */
+
+#define CCR_SSCH2       0x22    /* Send Special Character 2                */
+
+#define CCR_SSCH3       0x23    /* Send Special Character 3                */
+
+#define CCR_SSCH4       0x24    /* Send Special Character 4                */
+
+#define CCR_TXEN        0x18    /* Enable Transmitter                      */
+#define CCR_RXEN        0x12    /* Enable Receiver                         */
+
+#define CCR_TXDIS       0x14    /* Disable Transmitter                     */
+#define CCR_RXDIS       0x11    /* Disable Receiver                        */
+
+
+/* Interrupt Enable Register (R/W) */
+
+#define IER_DSR         0x80    /* Enable interrupt on DSR change          */
+#define IER_CD          0x40    /* Enable interrupt on CD change           */
+#define IER_CTS         0x20    /* Enable interrupt on CTS change          */
+#define IER_RXD         0x10    /* Enable interrupt on Receive Data        */
+#define IER_RXSC        0x08    /* Enable interrupt on Receive Spec. Char  */
+#define IER_TXRDY       0x04    /* Enable interrupt on TX FIFO empty       */
+#define IER_TXEMPTY     0x02    /* Enable interrupt on TX completely empty */
+#define IER_RET         0x01    /* Enable interrupt on RX Exc. Timeout     */
+
+
+/* Channel Option Register 1 (R/W) */
+
+#define COR1_ODDP       0x80    /* Odd Parity                              */
+#define COR1_PARMODE    0x60    /* Parity Mode mask                        */
+#define  COR1_NOPAR      0x00   /* No Parity                               */
+#define  COR1_FORCEPAR   0x20   /* Force Parity                            */
+#define  COR1_NORMPAR    0x40   /* Normal Parity                           */
+#define COR1_IGNORE     0x10    /* Ignore Parity on RX                     */
+#define COR1_STOPBITS   0x0c    /* Number of Stop Bits                     */
+#define  COR1_1SB        0x00   /* 1 Stop Bit                              */
+#define  COR1_15SB       0x04   /* 1.5 Stop Bits                           */
+#define  COR1_2SB        0x08   /* 2 Stop Bits                             */
+#define COR1_CHARLEN    0x03    /* Character Length                        */
+#define  COR1_5BITS      0x00   /* 5 bits                                  */
+#define  COR1_6BITS      0x01   /* 6 bits                                  */
+#define  COR1_7BITS      0x02   /* 7 bits                                  */
+#define  COR1_8BITS      0x03   /* 8 bits                                  */
+
+
+/* Channel Option Register 2 (R/W) */
+
+#define COR2_IXM        0x80    /* Implied XON mode                        */
+#define COR2_TXIBE      0x40    /* Enable In-Band (XON/XOFF) Flow Control  */
+#define COR2_ETC        0x20    /* Embedded Tx Commands Enable             */
+#define COR2_LLM        0x10    /* Local Loopback Mode                     */
+#define COR2_RLM        0x08    /* Remote Loopback Mode                    */
+#define COR2_RTSAO      0x04    /* RTS Automatic Output Enable             */
+#define COR2_CTSAE      0x02    /* CTS Automatic Enable                    */
+#define COR2_DSRAE      0x01    /* DSR Automatic Enable                    */
+
+
+/* Channel Option Register 3 (R/W) */
+
+#define COR3_XONCH      0x80    /* XON is a pair of characters (1 & 3)     */
+#define COR3_XOFFCH     0x40    /* XOFF is a pair of characters (2 & 4)    */
+#define COR3_FCT        0x20    /* Flow-Control Transparency Mode          */
+#define COR3_SCDE       0x10    /* Special Character Detection Enable      */
+#define COR3_RXTH       0x0f    /* RX FIFO Threshold value (1-8)           */
+
+
+/* Channel Control Status Register (R/O) */
+
+#define CCSR_RXEN       0x80    /* Receiver Enabled                        */
+#define CCSR_RXFLOFF    0x40    /* Receive Flow Off (XOFF was sent)        */
+#define CCSR_RXFLON     0x20    /* Receive Flow On (XON was sent)          */
+#define CCSR_TXEN       0x08    /* Transmitter Enabled                     */
+#define CCSR_TXFLOFF    0x04    /* Transmit Flow Off (got XOFF)            */
+#define CCSR_TXFLON     0x02    /* Transmit Flow On (got XON)              */
+
+
+/* Modem Change Option Register 1 (R/W) */
+
+#define MCOR1_DSRZD     0x80    /* Detect 0->1 transition of DSR           */
+#define MCOR1_CDZD      0x40    /* Detect 0->1 transition of CD            */
+#define MCOR1_CTSZD     0x20    /* Detect 0->1 transition of CTS           */
+#define MCOR1_DTRTH     0x0f    /* Auto DTR flow control Threshold (1-8)   */
+#define  MCOR1_NODTRFC   0x0     /* Automatic DTR flow control disabled     */
+
+
+/* Modem Change Option Register 2 (R/W) */
+
+#define MCOR2_DSROD     0x80    /* Detect 1->0 transition of DSR           */
+#define MCOR2_CDOD      0x40    /* Detect 1->0 transition of CD            */
+#define MCOR2_CTSOD     0x20    /* Detect 1->0 transition of CTS           */
+
+/* Modem Change Register (R/W) */
+
+#define MCR_DSRCHG      0x80    /* DSR Changed                             */
+#define MCR_CDCHG       0x40    /* CD Changed                              */
+#define MCR_CTSCHG      0x20    /* CTS Changed                             */
+
+
+/* Modem Signal Value Register (R/W) */
+
+#define MSVR_DSR        0x80    /* Current state of DSR input              */
+#define MSVR_CD         0x40    /* Current state of CD input               */
+#define MSVR_CTS        0x20    /* Current state of CTS input              */
+#define MSVR_DTR        0x02    /* Current state of DTR output             */
+#define MSVR_RTS        0x01    /* Current state of RTS output             */
+
+
+/* Escape characters */
+
+#define CD186x_C_ESC     0x00    /* Escape character                        */
+#define CD186x_C_SBRK    0x81    /* Start sending BREAK                     */
+#define CD186x_C_DELAY   0x82    /* Delay output                            */
+#define CD186x_C_EBRK    0x83    /* Stop sending BREAK                      */
+
+#define SRSR_RREQint     0x10    /* This chip wants "rec" serviced          */
+#define SRSR_TREQint     0x04    /* This chip wants "transmit" serviced     */
+#define SRSR_MREQint     0x01    /* This chip wants "mdm change" serviced   */
+
+
+
+#define SRCR_PKGTYPE    0x80
+#define SRCR_REGACKEN   0x40
+#define SRCR_DAISYEN    0x20
+#define SRCR_GLOBPRI    0x10
+#define SRCR_UNFAIR     0x08
+#define SRCR_AUTOPRI    0x02
+#define SRCR_PRISEL     0x01
+
+
diff -u --recursive --new-file v2.1.69/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- v2.1.69/linux/drivers/char/cyclades.c	Wed Nov 12 13:34:26 1997
+++ linux/drivers/char/cyclades.c	Tue Dec  2 09:33:16 1997
@@ -2339,7 +2339,7 @@
 			break;
 		}
 	    restore_flags(flags);
-	    if (current->signal & ~current->blocked) {
+	    if (signal_pending(current))  {
 		retval = -ERESTARTSYS;
 		break;
 	    }
@@ -2397,7 +2397,7 @@
 	      || (cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD))) {
 		break;
 	    }
-	    if (current->signal & ~current->blocked) {
+	    if (signal_pending(current)) {
 		retval = -ERESTARTSYS;
 		break;
 	    }
diff -u --recursive --new-file v2.1.69/linux/drivers/char/ftape/Config.in linux/drivers/char/ftape/Config.in
--- v2.1.69/linux/drivers/char/ftape/Config.in	Wed Nov 26 16:24:01 1997
+++ linux/drivers/char/ftape/Config.in	Tue Dec  2 13:47:33 1997
@@ -17,7 +17,7 @@
 	"Normal		CONFIG_FT_NORMAL_DEBUG	\
 	 Excessive	CONFIG_FT_FULL_DEBUG	\
 	 Reduced	CONFIG_FT_NO_TRACE	\
-	 None		CONIFG_FT_NO_TRACE_AT_ALL" Normal
+	 None		CONFIG_FT_NO_TRACE_AT_ALL" Normal
 comment 'Hardware configuration'
 choice 'Floppy tape controllers'				\
 	"Standard			CONFIG_FT_STD_FDC	\
diff -u --recursive --new-file v2.1.69/linux/drivers/char/ftape/lowlevel/fdc-io.c linux/drivers/char/ftape/lowlevel/fdc-io.c
--- v2.1.69/linux/drivers/char/ftape/lowlevel/fdc-io.c	Wed Nov 26 16:24:02 1997
+++ linux/drivers/char/ftape/lowlevel/fdc-io.c	Tue Dec  2 09:33:16 1997
@@ -445,7 +445,7 @@
 		fdc_reset();
 		resetting = 0;
 	}
-	TRACE_EXIT (current->signal & ~current->blocked) ? -EINTR : -ETIME;
+	TRACE_EXIT (signal_pending(current)) ? -EINTR : -ETIME;
 }
 
 /*      Start/stop drive motor. Enable DMA mode.
diff -u --recursive --new-file v2.1.69/linux/drivers/char/ftape/lowlevel/ftape-init.c linux/drivers/char/ftape/lowlevel/ftape-init.c
--- v2.1.69/linux/drivers/char/ftape/lowlevel/ftape-init.c	Wed Nov 26 16:24:02 1997
+++ linux/drivers/char/ftape/lowlevel/ftape-init.c	Tue Dec  2 09:33:16 1997
@@ -94,7 +94,7 @@
 #ifdef MODULE
 	while (ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS) < 0) {
 		ftape_sleep(FT_SECOND/20);
-		if (current->signal & ~current->blocked) {
+		if (signal_pending(current)) {
 			(void)ftape_set_nr_buffers(0);
 			TRACE(ft_t_bug,
 			      "Killed by signal while allocating buffers.");
diff -u --recursive --new-file v2.1.69/linux/drivers/char/ftape/lowlevel/ftape-io.c linux/drivers/char/ftape/lowlevel/ftape-io.c
--- v2.1.69/linux/drivers/char/ftape/lowlevel/ftape-io.c	Wed Nov 26 16:24:02 1997
+++ linux/drivers/char/ftape/lowlevel/ftape-io.c	Tue Dec  2 09:33:16 1997
@@ -103,7 +103,7 @@
 			}
 			/*  Mmm. Isn't current->blocked == 0xffffffff ?
 			 */
-			if (current->signal & ~current->blocked) {
+			if (signal_pending(current)) {
 				TRACE(ft_t_err,
 				      "awoken by non-blocked signal :-(");
 				break;	/* exit on signal */
diff -u --recursive --new-file v2.1.69/linux/drivers/char/specialix.c linux/drivers/char/specialix.c
--- v2.1.69/linux/drivers/char/specialix.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/specialix.c	Tue Dec  2 09:19:03 1997
@@ -0,0 +1,2332 @@
+/*
+ *      specialix.c  -- specialix IO8+ multiport serial driver.
+ *
+ *      Copyright (C) 1997  Roger Wolff (R.E.Wolff@BitWizard.nl)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *
+ *      Specialix pays for the development and support of this driver.
+ *      Please DO contact io8-linux@specialix.co.uk if you require
+ *      support. But please read the documentation (specialix.txt)
+ *      first.
+ *
+ *      This driver was developped in the BitWizard linux device
+ *      driver service. If you require a linux device driver for your
+ *      product, please contact devices@BitWizard.nl for a quote.
+ *
+ *      This code is firmly based on the riscom/8 serial driver,
+ *      written by Dmitry Gorodchanin. The specialix IO8+ card
+ *      programming information was obtained from the CL-CD1865 Data
+ *      Book, and Specialix document number 6200059: IO8+ Hardware
+ *      Functional Specification.
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License as
+ *      published by the Free Software Foundation; either version 2 of
+ *      the License, or (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be
+ *      useful, but WITHOUT ANY WARRANTY; without even the implied
+ *      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ *      PURPOSE.  See the GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public
+ *      License along with this program; if not, write to the Free
+ *      Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ *      USA.
+ *
+ * Revision history:
+ *
+ * Revision 1.0:  April 1st 1997.
+ *                Initial release for alpha testing.
+ * Revision 1.1:  April 14th 1997. 
+ *                Incorporated Richard Hudsons suggestions, 
+ *                removed some debugging printk's.
+ * Revision 1.2:  April 15th 1997.
+ *                Ported to 2.1.x kernels.
+ * Revision 1.3:  April 17th 1997 
+ *                Backported to 2.0. (Compatibility macros). 
+ * Revision 1.4:  April 18th 1997
+ *                Fixed DTR/RTS bug that caused the card to indicate 
+ *                "don't send data" to a modem after the password prompt.  
+ *                Fixed bug for premature (fake) interrupts.
+ * Revision 1.5:  April 19th 1997
+ *                fixed a minor typo in the header file, cleanup a little. 
+ *                performance warnings are now MAXed at once per minute.
+ * Revision 1.6:  May 23 1997
+ *                Changed the specialix=... format to include interrupt.
+ * Revision 1.7:  May 27 1997
+ *                Made many more debug printk's a compile time option.
+ * Revision 1.8:  Jul 1  1997
+ *                port to linux-2.1.43 kernel.
+ * 
+ */
+
+#define VERSION "1.8"
+
+
+/*
+ * There is a bunch of documentation about the card, jumpers, config
+ * settings, restrictions, cables, device names and numbers in
+ * ../../Documentation/specialix.txt 
+ */
+
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/mm.h>
+#include <linux/serial.h>
+#include <linux/fcntl.h>
+#include <linux/major.h>
+#include <linux/delay.h>
+#include <linux/tqueue.h>
+#include <linux/version.h>
+
+
+/* ************************************************************** */
+/* * This section can be removed when 2.0 becomes outdated....  * */
+/* ************************************************************** */
+
+#if LINUX_VERSION_CODE < 131328    /* Less than 2.1.0 */
+#define TWO_ZERO
+#else
+#if LINUX_VERSION_CODE < 131371   /* less than 2.1.43 */
+/* This has not been extensively tested yet. Sorry. */
+#warning "You're on your own between 2.1.0 and 2.1.43.... "
+#warning "Please use a recent kernel."
+#endif
+#endif
+
+
+#ifdef TWO_ZERO
+#define Get_user(a,b)         a = get_user(b)
+#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
+#define copy_to_user(a,b,c)   memcpy_tofs(a,b,c)
+#define queue_task            queue_task_irq_off
+#else
+#define Get_user(a,b)         get_user(a,b)
+#endif
+
+/* ************************************************************** */
+/* *                End of compatibility section..              * */
+/* ************************************************************** */
+
+
+#ifndef TWO_ZERO
+#include <asm/uaccess.h>
+#endif
+
+#include "specialix_io8.h"
+#include "cd1865.h"
+
+
+
+/* Configurable options: */
+
+/* Am I paranoid or not ? ;-) */
+#define SPECIALIX_PARANOIA_CHECK
+
+/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help)
+   When the IRQ routine leaves the chip in a state that is keeps on
+   requiring attention, the timer doesn't help either. */
+#undef SPECIALIX_TIMER
+
+/* 
+ * The following defines are mostly for testing purposes. But if you need
+ * some nice reporting in your syslog, you can define them also.
+ */
+#undef SX_REPORT_FIFO
+#undef SX_REPORT_OVERRUN
+
+
+
+#ifdef CONFIG_SPECIALIX_RTSCTS
+#define SX_CRTSCTS(bla) 1
+#else
+#define SX_CRTSCTS(tty) C_CRTSCTS(tty)
+#endif
+
+
+/* Used to be outb (0xff, 0x80); */
+#define short_pause() udelay (1)
+
+
+#define SPECIALIX_LEGAL_FLAGS \
+	(ASYNC_HUP_NOTIFY   | ASYNC_SAK          | ASYNC_SPLIT_TERMIOS   | \
+	 ASYNC_SPD_HI       | ASYNC_SPEED_VHI    | ASYNC_SESSION_LOCKOUT | \
+	 ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+DECLARE_TASK_QUEUE(tq_specialix);
+
+
+
+#define SPECIALIX_TYPE_NORMAL	1
+#define SPECIALIX_TYPE_CALLOUT	2
+
+static struct specialix_board * IRQ_to_board[16] = { NULL, } ;
+static struct tty_driver specialix_driver, specialix_callout_driver;
+static int    specialix_refcount = 0;
+static struct tty_struct * specialix_table[SX_NBOARD * SX_NPORT] = { NULL, };
+static struct termios * specialix_termios[SX_NBOARD * SX_NPORT] = { NULL, };
+static struct termios * specialix_termios_locked[SX_NBOARD * SX_NPORT] = { NULL, };
+static unsigned char * tmp_buf = NULL;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static unsigned long baud_table[] =  {
+	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+	9600, 19200, 38400, 57600, 115200, 0, 
+};
+
+static struct specialix_board sx_board[SX_NBOARD] =  {
+	{ 0, SX_IOBASE1,  9, },
+	{ 0, SX_IOBASE2, 11, },
+	{ 0, SX_IOBASE3, 12, },
+	{ 0, SX_IOBASE4, 15, },
+};
+
+static struct specialix_port sx_port[SX_NBOARD * SX_NPORT] =  {
+	{ 0, },
+};
+		
+
+#ifdef SPECIALIX_TIMER
+static struct timer_list missed_irq_timer;
+static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs);
+#endif
+
+
+
+static inline int sx_paranoia_check(struct specialix_port const * port,
+				    kdev_t device, const char *routine)
+{
+#ifdef SPECIALIX_PARANOIA_CHECK
+	static const char *badmagic =
+		KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n";
+	static const char *badinfo =
+		KERN_ERR "sx: Warning: null specialix port for device %s in %s\n";
+ 
+	if (!port) {
+		printk(badinfo, kdevname(device), routine);
+		return 1;
+	}
+	if (port->magic != SPECIALIX_MAGIC) {
+		printk(badmagic, kdevname(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+
+/*
+ * 
+ *  Service functions for specialix IO8+ driver.
+ * 
+ */
+
+/* Get board number from pointer */
+extern inline int board_No (struct specialix_board * bp)
+{
+	return bp - sx_board;
+}
+
+
+/* Get port number from pointer */
+extern inline int port_No (struct specialix_port const * port)
+{
+	return SX_PORT(port - sx_port); 
+}
+
+
+/* Get pointer to board from pointer to port */
+extern inline struct specialix_board * port_Board(struct specialix_port const * port)
+{
+	return &sx_board[SX_BOARD(port - sx_port)];
+}
+
+
+/* Input Byte from CL CD186x register */
+extern inline unsigned char sx_in(struct specialix_board  * bp, unsigned short reg)
+{
+	bp->reg = reg | 0x80;
+	outb (reg | 0x80, bp->base + SX_ADDR_REG);
+	return inb  (bp->base + SX_DATA_REG);
+}
+
+
+/* Output Byte to CL CD186x register */
+extern inline void sx_out(struct specialix_board  * bp, unsigned short reg,
+			  unsigned char val)
+{
+	bp->reg = reg | 0x80;
+	outb (reg | 0x80, bp->base + SX_ADDR_REG);
+	outb (val, bp->base + SX_DATA_REG);
+}
+
+
+/* Input Byte from CL CD186x register */
+extern inline unsigned char sx_in_off(struct specialix_board  * bp, unsigned short reg)
+{
+	bp->reg = reg;
+	outb (reg, bp->base + SX_ADDR_REG);
+	return inb  (bp->base + SX_DATA_REG);
+}
+
+
+/* Output Byte to CL CD186x register */
+extern inline void sx_out_off(struct specialix_board  * bp, unsigned short reg,
+			  unsigned char val)
+{
+	bp->reg = reg;
+	outb (reg, bp->base + SX_ADDR_REG);
+	outb (val, bp->base + SX_DATA_REG);
+}
+
+
+/* Wait for Channel Command Register ready */
+extern inline void sx_wait_CCR(struct specialix_board  * bp)
+{
+	unsigned long delay;
+
+	for (delay = SX_CCR_TIMEOUT; delay; delay--) 
+		if (!sx_in(bp, CD186x_CCR))
+			return;
+	
+	printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
+}
+
+
+/* Wait for Channel Command Register ready */
+extern inline void sx_wait_CCR_off(struct specialix_board  * bp)
+{
+	unsigned long delay;
+
+	for (delay = SX_CCR_TIMEOUT; delay; delay--) 
+		if (!sx_in_off(bp, CD186x_CCR))
+			return;
+	
+	printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
+}
+
+
+/*
+ *  specialix IO8+ IO range functions.
+ */
+
+extern inline int sx_check_io_range(struct specialix_board * bp)
+{
+	return check_region (bp->base, SX_IO_SPACE);
+}
+
+
+extern inline void sx_request_io_range(struct specialix_board * bp)
+{
+	request_region(bp->base, SX_IO_SPACE, "specialix IO8+" );
+}
+
+
+extern inline void sx_release_io_range(struct specialix_board * bp)
+{
+	release_region(bp->base, SX_IO_SPACE);
+}
+
+	
+/* Must be called with enabled interrupts */
+extern inline void sx_long_delay(unsigned long delay)
+{
+	unsigned long i;
+	
+	for (i = jiffies + delay; i > jiffies; ) ;
+}
+
+
+
+/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
+int sx_set_irq ( struct specialix_board *bp)
+{
+	int virq;
+	int i;
+
+	switch (bp->irq) {
+	/* In the same order as in the docs... */
+	case 15: virq = 0;break;
+	case 12: virq = 1;break;
+	case 11: virq = 2;break;
+	case 9:  virq = 3;break;
+	default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq);
+	         return 0;
+	}
+
+	for (i=0;i<2;i++) {
+		sx_out(bp, CD186x_CAR, i);
+		sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
+	}
+	return 1;
+}
+
+
+/* Reset and setup CD186x chip */
+static int sx_init_CD186x(struct specialix_board  * bp)
+{
+	unsigned long flags;
+	int scaler;
+	int rv = 1;
+	
+	save_flags(flags); cli();
+
+	sx_wait_CCR_off(bp);			   /* Wait for CCR ready        */
+	sx_out_off(bp, CD186x_CCR, CCR_HARDRESET);      /* Reset CD186x chip          */
+	sti();
+	sx_long_delay(HZ/20);                      /* Delay 0.05 sec            */
+	cli();
+	sx_out_off(bp, CD186x_GIVR, SX_ID);             /* Set ID for this chip      */
+	sx_out_off(bp, CD186x_GICR, 0);                 /* Clear all bits            */
+	sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT);      /* Prio for modem intr       */
+	sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT);      /* Prio for transmitter intr */
+	sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT);      /* Prio for receiver intr    */
+	/* Set RegAckEn */
+	sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN);
+	
+	/* Setting up prescaler. We need 4 ticks per 1 ms */
+	scaler =  SX_OSCFREQ/SPECIALIX_TPS;
+
+	sx_out_off(bp, CD186x_PPRH, scaler >> 8);
+	sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
+
+	if (!sx_set_irq (bp)) {
+		/* Figure out how to pass this along... */
+		printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq);
+		rv = 0;
+	}
+
+	restore_flags(flags);
+	return rv;
+}
+
+
+int read_cross_byte (struct specialix_board *bp, int reg, int bit)
+{
+	int i;
+	int t;
+
+	for (i=0, t=0;i<8;i++) {
+		sx_out_off (bp, CD186x_CAR, i);
+		if (sx_in_off (bp, reg) & bit) 
+			t |= 1 << i;
+	}
+	return t;
+}
+
+
+#ifdef SPECIALIX_TIMER
+void missed_irq (unsigned long data)
+{
+	if (sx_in ((struct specialix_board *)data, CD186x_SRSR) &  
+	                                            (SRSR_RREQint |
+	                                             SRSR_TREQint |
+	                                             SRSR_MREQint)) {
+		printk (KERN_INFO "Missed interrupt... Calling int from timer. \n");
+		sx_interrupt (((struct specialix_board *)data)->irq, 
+		              NULL, NULL);
+	}
+	missed_irq_timer.expires = jiffies + HZ;
+	add_timer (&missed_irq_timer);
+}
+#endif
+
+
+
+/* Main probing routine, also sets irq. */
+static int sx_probe(struct specialix_board *bp)
+{
+	unsigned char val1, val2;
+#if 0
+	int irqs = 0;
+	int retries;
+#endif
+	int rev;
+	int chip;
+
+	if (sx_check_io_range(bp)) 
+		return 1;
+
+	/* Are the I/O ports here ? */
+	sx_out_off(bp, CD186x_PPRL, 0x5a);
+	short_pause ();
+	val1 = sx_in_off(bp, CD186x_PPRL);
+
+	sx_out_off(bp, CD186x_PPRL, 0xa5);
+	short_pause ();
+	val2 = sx_in_off(bp, CD186x_PPRL);
+
+	
+	if ((val1 != 0x5a) || (val2 != 0xa5)) {
+		printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
+		       board_No(bp), bp->base);
+		return 1;
+	}
+
+	/* Check the DSR lines that Specialix uses as board 
+	   identification */
+	val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR);
+	val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS);
+#ifdef SPECIALIX_DEBUG
+	printk (KERN_DEBUG "sx%d: DSR lines are: %02x, rts lines are: %02x\n", 
+	        board_No(bp),  val1, val2);
+#endif
+	if (val1 != 0xb2) {
+		printk(KERN_INFO "sx%d: specialix IO8+ ID at 0x%03x not found.\n",
+		       board_No(bp), bp->base);
+		return 1;
+	}
+
+
+#if 0
+	/* It's time to find IRQ for this board */
+	for (retries = 0; retries < 5 && irqs <= 0; retries++) {
+		irqs = probe_irq_on();
+		sx_init_CD186x(bp);	       		/* Reset CD186x chip       */
+		sx_out(bp, CD186x_CAR, 2);               /* Select port 2          */
+		sx_wait_CCR(bp);
+		sx_out(bp, CD186x_CCR, CCR_TXEN);        /* Enable transmitter     */
+		sx_out(bp, CD186x_IER, IER_TXRDY);       /* Enable tx empty intr   */
+		sx_long_delay(HZ/20);	       		
+		irqs = probe_irq_off(irqs);
+
+#if SPECIALIX_DEBUG > 2
+		printk (KERN_DEBUG "SRSR = %02x, ",  sx_in(bp, CD186x_SRSR));
+		printk (           "TRAR = %02x, ",  sx_in(bp, CD186x_TRAR));
+		printk (           "GIVR = %02x, ",  sx_in(bp, CD186x_GIVR));
+		printk (           "GICR = %02x, ",  sx_in(bp, CD186x_GICR));
+		printk (           "\n");
+#endif
+		/* Reset CD186x again      */
+		if (!sx_init_CD186x(bp)) {
+			/* Hmmm. This is dead code anyway. */
+		}
+#if SPECIALIX_DEBUG > 2
+		printk (KERN_DEBUG "val1 = %02x, val2 = %02x, val3 = %02x.\n", 
+		        val1, val2, val3); 
+#endif
+	
+	}
+	
+#if 0
+	if (irqs <= 0) {
+		printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n",
+		       board_No(bp), bp->base);
+		return 1;
+	}
+#endif
+	printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs);
+	if (irqs > 0)
+		bp->irq = irqs;
+#endif
+	/* Reset CD186x again  */
+	if (!sx_init_CD186x(bp)) {
+		return -EIO;
+	}
+
+	sx_request_io_range(bp);
+	bp->flags |= SX_BOARD_PRESENT;
+	
+	/* Chip           revcode   pkgtype
+	                  GFRCR     SRCR bit 7
+	   CD180 rev B    0x81      0
+	   CD180 rev C    0x82      0
+	   CD1864 rev A   0x82      1
+	   CD1865 rev A   0x83      1  -- Do not use!!! Does not work. 
+	   CD1865 rev B   0x84      1
+	 -- Thanks to Gwen Wang, Cirrus Logic.
+	 */
+
+	switch (sx_in_off(bp, CD186x_GFRCR)) {
+	case 0x82:chip = 1864;rev='A';break;
+	case 0x83:chip = 1865;rev='A';break;
+	case 0x84:chip = 1865;rev='B';break;
+	case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */
+	default:chip=-1;rev='x';
+	}
+
+#if SPECIALIX_DEBUG > 2
+	printk (KERN_DEBUG " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );
+#endif
+
+#ifdef SPECIALIX_TIMER
+	init_timer (&missed_irq_timer);
+	missed_irq_timer.function = missed_irq;
+	missed_irq_timer.data = (unsigned long) bp;
+	missed_irq_timer.expires = jiffies + HZ;
+	add_timer (&missed_irq_timer);
+#endif
+
+	printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
+	       board_No(bp),
+	       bp->base, bp->irq,
+	       chip, rev);
+
+	return 0;
+}
+
+/* 
+ * 
+ *  Interrupt processing routines.
+ * */
+
+extern inline void sx_mark_event(struct specialix_port * port, int event)
+{
+	/* 
+	 * I'm not quite happy with current scheme all serial
+	 * drivers use their own BH routine.
+	 * It seems this easily can be done with one BH routine
+	 * serving for all serial drivers.
+	 * For now I must introduce another one - SPECIALIX_BH.
+	 * Still hope this will be changed in near future.
+	 * -- Dmitry.
+	 */
+	set_bit(event, &port->event);
+	queue_task(&port->tqueue, &tq_specialix);
+	mark_bh(SPECIALIX_BH);
+}
+
+
+extern inline struct specialix_port * sx_get_port(struct specialix_board * bp,
+					       unsigned char const * what)
+{
+	unsigned char channel;
+	struct specialix_port * port;
+	
+	channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
+	if (channel < CD186x_NCH) {
+		port = &sx_port[board_No(bp) * SX_NPORT + channel];
+		if (port->flags & ASYNC_INITIALIZED) {
+			return port;
+		}
+	}
+	printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n", 
+	       board_No(bp), what, channel);
+	return NULL;
+}
+
+
+extern inline void sx_receive_exc(struct specialix_board * bp)
+{
+	struct specialix_port *port;
+	struct tty_struct *tty;
+	unsigned char status;
+	unsigned char ch;
+
+	if (!(port = sx_get_port(bp, "Receive")))
+		return;
+
+	tty = port->tty;
+	if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+		printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n",
+		       board_No(bp), port_No(port));
+		return;
+	}
+	
+#ifdef SX_REPORT_OVERRUN	
+	status = sx_in(bp, CD186x_RCSR);
+	if (status & RCSR_OE) {
+		port->overrun++;
+#if SPECIALIX_DEBUG 
+		printk(KERN_DEBUG "sx%d: port %d: Overrun. Total %ld overruns.\n", 
+		       board_No(bp), port_No(port), port->overrun);
+#endif		
+	}
+	status &= port->mark_mask;
+#else	
+	status = sx_in(bp, CD186x_RCSR) & port->mark_mask;
+#endif	
+	ch = sx_in(bp, CD186x_RDR);
+	if (!status) {
+		return;
+	}
+	if (status & RCSR_TOUT) {
+		printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n", 
+		       board_No(bp), port_No(port));
+		return;
+		
+	} else if (status & RCSR_BREAK) {
+#ifdef SPECIALIX_DEBUG
+		printk(KERN_DEBUG "sx%d: port %d: Handling break...\n",
+		       board_No(bp), port_No(port));
+#endif
+		*tty->flip.flag_buf_ptr++ = TTY_BREAK;
+		if (port->flags & ASYNC_SAK)
+			do_SAK(tty);
+		
+	} else if (status & RCSR_PE) 
+		*tty->flip.flag_buf_ptr++ = TTY_PARITY;
+	
+	else if (status & RCSR_FE) 
+		*tty->flip.flag_buf_ptr++ = TTY_FRAME;
+	
+	else if (status & RCSR_OE)
+		*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+	
+	else
+		*tty->flip.flag_buf_ptr++ = 0;
+	
+	*tty->flip.char_buf_ptr++ = ch;
+	tty->flip.count++;
+	queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+
+extern inline void sx_receive(struct specialix_board * bp)
+{
+	struct specialix_port *port;
+	struct tty_struct *tty;
+	unsigned char count;
+	
+	if (!(port = sx_get_port(bp, "Receive")))
+		return;
+	
+	tty = port->tty;
+	
+	count = sx_in(bp, CD186x_RDCR);
+	
+#ifdef SX_REPORT_FIFO
+	port->hits[count > 8 ? 9 : count]++;
+#endif	
+	
+	while (count--) {
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+			printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n",
+			       board_No(bp), port_No(port));
+			break;
+		}
+		*tty->flip.char_buf_ptr++ = sx_in(bp, CD186x_RDR);
+		*tty->flip.flag_buf_ptr++ = 0;
+		tty->flip.count++;
+	}
+	queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+
+extern inline void sx_transmit(struct specialix_board * bp)
+{
+	struct specialix_port *port;
+	struct tty_struct *tty;
+	unsigned char count;
+	
+	
+	if (!(port = sx_get_port(bp, "Transmit")))
+		return;
+	
+	tty = port->tty;
+	
+	if (port->IER & IER_TXEMPTY) {
+		/* FIFO drained */
+		sx_out(bp, CD186x_CAR, port_No(port));
+		port->IER &= ~IER_TXEMPTY;
+		sx_out(bp, CD186x_IER, port->IER);
+		return;
+	}
+	
+	if ((port->xmit_cnt <= 0 && !port->break_length)
+	    || tty->stopped || tty->hw_stopped) {
+		sx_out(bp, CD186x_CAR, port_No(port));
+		port->IER &= ~IER_TXRDY;
+		sx_out(bp, CD186x_IER, port->IER);
+		return;
+	}
+	
+	if (port->break_length) {
+		if (port->break_length > 0) {
+			if (port->COR2 & COR2_ETC) {
+				sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+				sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
+				port->COR2 &= ~COR2_ETC;
+			}
+			count = MIN(port->break_length, 0xff);
+			sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+			sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
+			sx_out(bp, CD186x_TDR, count);
+			if (!(port->break_length -= count))
+				port->break_length--;
+		} else {
+			sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+			sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
+			sx_out(bp, CD186x_COR2, port->COR2);
+			sx_wait_CCR(bp);
+			sx_out(bp, CD186x_CCR, CCR_CORCHG2);
+			port->break_length = 0;
+		}
+		return;
+	}
+	
+	count = CD186x_NFIFO;
+	do {
+		sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
+		port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
+		if (--port->xmit_cnt <= 0)
+			break;
+	} while (--count > 0);
+	
+	if (port->xmit_cnt <= 0) {
+		sx_out(bp, CD186x_CAR, port_No(port));
+		port->IER &= ~IER_TXRDY;
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+	if (port->xmit_cnt <= port->wakeup_chars)
+		sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+}
+
+
+extern inline void sx_check_modem(struct specialix_board * bp)
+{
+	struct specialix_port *port;
+	struct tty_struct *tty;
+	unsigned char mcr;
+
+#ifdef SPECIALIX_DEBUG
+	printk (KERN_DEBUG "Modem intr. ");
+#endif
+	if (!(port = sx_get_port(bp, "Modem")))
+		return;
+	
+	tty = port->tty;
+	
+	mcr = sx_in(bp, CD186x_MCR);
+	printk ("mcr = %02x.\n", mcr);
+
+	if ((mcr & MCR_CDCHG)) {
+#ifdef SPECIALIX_DEBUG 
+		printk (KERN_DEBUG "CD just changed... ");
+#endif
+		if (sx_in(bp, CD186x_MSVR) & MSVR_CD) {
+#ifdef SPECIALIX_DEBUG
+			printk ( "Waking up guys in open.\n");
+#endif
+			wake_up_interruptible(&port->open_wait);
+		}
+		else if (!((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+		           (port->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SPECIALIX_DEBUG
+			printk ( "Sending HUP.\n");
+#endif
+			queue_task(&port->tqueue_hangup,  
+			           &tq_scheduler);      
+		} else {
+#ifdef SPECIALIX_DEBUG
+			printk ( "Don't need to send HUP.\n");
+#endif
+		}
+	}
+	
+#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
+	if (mcr & MCR_CTSCHG) {
+		if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
+			tty->hw_stopped = 0;
+			port->IER |= IER_TXRDY;
+			if (port->xmit_cnt <= port->wakeup_chars)
+				sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+		} else {
+			tty->hw_stopped = 1;
+			port->IER &= ~IER_TXRDY;
+		}
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+	if (mcr & MCR_DSSXHG) {
+		if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
+			tty->hw_stopped = 0;
+			port->IER |= IER_TXRDY;
+			if (port->xmit_cnt <= port->wakeup_chars)
+				sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+		} else {
+			tty->hw_stopped = 1;
+			port->IER &= ~IER_TXRDY;
+		}
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
+	
+	/* Clear change bits */
+	sx_out(bp, CD186x_MCR, 0);
+}
+
+
+/* The main interrupt processing routine */
+static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs)
+{
+	unsigned char status;
+	unsigned char ack;
+	struct specialix_board *bp;
+	unsigned long loop = 0;
+	int saved_reg;
+
+	bp = IRQ_to_board[irq];
+	
+	if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) {
+#ifdef SPECIALIX_DEBUG 
+		printk (KERN_DEBUG "sx: False interrupt. irq %d.\n", irq);
+#endif
+		return;
+	}
+
+	saved_reg = bp->reg;
+
+	while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &
+	                                   (SRSR_RREQint |
+		                            SRSR_TREQint |
+	                                    SRSR_MREQint)))) {	
+		if (status & SRSR_RREQint) {
+			ack = sx_in(bp, CD186x_RRAR);
+
+			if (ack == (SX_ID | GIVR_IT_RCV))
+				sx_receive(bp);
+			else if (ack == (SX_ID | GIVR_IT_REXC))
+				sx_receive_exc(bp);
+			else
+				printk(KERN_ERR "sx%d: Bad receive ack 0x%02x.\n",
+				       board_No(bp), ack);
+		
+		} else if (status & SRSR_TREQint) {
+			ack = sx_in(bp, CD186x_TRAR);
+
+			if (ack == (SX_ID | GIVR_IT_TX))
+				sx_transmit(bp);
+			else
+				printk(KERN_ERR "sx%d: Bad transmit ack 0x%02x.\n",
+				       board_No(bp), ack);
+		} else if (status & SRSR_MREQint) {
+			ack = sx_in(bp, CD186x_MRAR);
+
+			if (ack == (SX_ID | GIVR_IT_MODEM)) 
+				sx_check_modem(bp);
+			else
+				printk(KERN_ERR "sx%d: Bad modem ack 0x%02x.\n",
+				       board_No(bp), ack);
+		
+		} 
+
+		sx_out(bp, CD186x_EOIR, 0);   /* Mark end of interrupt */
+	}
+	bp->reg = saved_reg;
+	outb (bp->reg, bp->base + SX_ADDR_REG);
+}
+
+
+/*
+ *  Routines for open & close processing.
+ */
+
+
+/* Called with disabled interrupts */
+extern inline int sx_setup_board(struct specialix_board * bp)
+{
+	int error;
+
+	if (bp->flags & SX_BOARD_ACTIVE) 
+		return 0;
+
+	error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", NULL);
+
+	if (error) 
+		return error;
+
+	IRQ_to_board[bp->irq] = bp;
+	(void) sx_in (bp, 0); /* Turn ON interrupts. */
+
+	bp->flags |= SX_BOARD_ACTIVE;
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+
+/* Called with disabled interrupts */
+extern inline void sx_shutdown_board(struct specialix_board *bp)
+{
+	if (!(bp->flags & SX_BOARD_ACTIVE))
+		return;
+	
+	bp->flags &= ~SX_BOARD_ACTIVE;
+	
+	free_irq(bp->irq, NULL);
+	(void) sx_in_off (bp, 0); /* Turn off interrupts. */
+
+	IRQ_to_board[bp->irq] = NULL;
+	
+	MOD_DEC_USE_COUNT;
+}
+
+
+/*
+ * Setting up port characteristics. 
+ * Must be called with disabled interrupts
+ */
+static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port)
+{
+	struct tty_struct *tty;
+	unsigned long baud;
+	long tmp;
+	unsigned char cor1 = 0, cor3 = 0;
+	unsigned char mcor1 = 0, mcor2 = 0;
+	static int again=0;
+	
+	if (!(tty = port->tty) || !tty->termios)
+		return;
+
+	port->IER  = 0;
+	port->COR2 = 0;
+	/* Select port on the board */
+	sx_out(bp, CD186x_CAR, port_No(port));
+
+	/* The Specialix board doens't implement the RTS lines.
+	   They are used to set the IRQ level. Don't touch them. */
+	if (SX_CRTSCTS(tty))
+		port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
+	else
+		port->MSVR =  (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
+#ifdef DEBUG_SPECIALIX
+	printk (KERN_DEBUG "sx: got MSVR=%02x.\n", port->MSVR);
+#endif
+	baud = C_BAUD(tty);
+	
+	if (baud & CBAUDEX) {
+		baud &= ~CBAUDEX;
+		if (baud < 1 || baud > 2) 
+			port->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			baud += 15;
+	}
+	if (baud == 15) {
+		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+			baud ++;
+		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+			baud += 2;
+	}
+	
+	
+	if (!baud_table[baud]) {
+		/* Drop DTR & exit */
+#ifdef SPECIALIX_DEBUG
+		printk (KERN_DEBUG "Dropping DTR...  Hmm....\n");
+#endif
+		if (!SX_CRTSCTS (tty)) {
+			port -> MSVR &= ~ MSVR_DTR;
+			sx_out(bp, CD186x_MSVR, port->MSVR );
+		} 
+#ifdef DEBUG_SPECIALIX
+		else
+			printk (KERN_DEBUG "Can't drop DTR: no DTR.\n");
+#endif
+		return;
+	} else {
+		/* Set DTR on */
+		if (!SX_CRTSCTS (tty)) {
+			port ->MSVR |= MSVR_DTR;
+		}
+	}
+	
+	/*
+	 * Now we must calculate some speed depended things 
+	 */
+	
+	/* Set baud rate for port */
+	tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] +
+		CD186x_TPC/2) / CD186x_TPC);
+	if ((tmp < 0x10) && (again < jiffies)) { 
+		again = jiffies + HZ * 60;
+		/* Page 48 of version 2.0 of the CL-CD1865 databook */
+		if (tmp >= 12) {
+			printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
+			        "Performance degradation is possible.\n",
+			        port_No (port), tmp);
+		} else {
+			printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
+			        "Warning: overstressing Cirrus chip. "
+                                "This might not work.\n", 
+			        port_No (port), tmp);
+		}
+	}
+
+	sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff); 
+	sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff); 
+	sx_out(bp, CD186x_RBPRL, tmp & 0xff); 
+	sx_out(bp, CD186x_TBPRL, tmp & 0xff);
+	
+	baud = (baud_table[baud] + 5) / 10;   /* Estimated CPS */
+	
+	/* Two timer ticks seems enough to wakeup something like SLIP driver */
+	tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;		
+	port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
+					      SERIAL_XMIT_SIZE - 1 : tmp);
+	
+	/* Receiver timeout will be transmission time for 1.5 chars */
+	tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
+	tmp = (tmp > 0xff) ? 0xff : tmp;
+	sx_out(bp, CD186x_RTPR, tmp);
+	
+	switch (C_CSIZE(tty)) {
+	 case CS5:
+		cor1 |= COR1_5BITS;
+		break;
+	 case CS6:
+		cor1 |= COR1_6BITS;
+		break;
+	 case CS7:
+		cor1 |= COR1_7BITS;
+		break;
+	 case CS8:
+		cor1 |= COR1_8BITS;
+		break;
+	}
+	
+	if (C_CSTOPB(tty)) 
+		cor1 |= COR1_2SB;
+	
+	cor1 |= COR1_IGNORE;
+	if (C_PARENB(tty)) {
+		cor1 |= COR1_NORMPAR;
+		if (C_PARODD(tty)) 
+			cor1 |= COR1_ODDP;
+		if (I_INPCK(tty)) 
+			cor1 &= ~COR1_IGNORE;
+	}
+	/* Set marking of some errors */
+	port->mark_mask = RCSR_OE | RCSR_TOUT;
+	if (I_INPCK(tty)) 
+		port->mark_mask |= RCSR_FE | RCSR_PE;
+	if (I_BRKINT(tty) || I_PARMRK(tty)) 
+		port->mark_mask |= RCSR_BREAK;
+	if (I_IGNPAR(tty)) 
+		port->mark_mask &= ~(RCSR_FE | RCSR_PE);
+	if (I_IGNBRK(tty)) {
+		port->mark_mask &= ~RCSR_BREAK;
+		if (I_IGNPAR(tty)) 
+			/* Real raw mode. Ignore all */
+			port->mark_mask &= ~RCSR_OE;
+	}
+	/* Enable Hardware Flow Control */
+	if (C_CRTSCTS(tty)) {
+#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
+		port->IER |= IER_DSR | IER_CTS;
+		mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
+		mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
+		tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
+#else
+		port->COR2 |= COR2_CTSAE; 
+#endif
+	}
+	/* Enable Software Flow Control. FIXME: I'm not sure about this */
+	/* Some people reported that it works, but I still doubt it */
+	if (I_IXON(tty)) {
+		port->COR2 |= COR2_TXIBE;
+		cor3 |= (COR3_FCT | COR3_SCDE);
+		if (I_IXANY(tty))
+			port->COR2 |= COR2_IXM;
+		sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
+		sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
+		sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
+		sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
+	}
+	if (!C_CLOCAL(tty)) {
+		/* Enable CD check */
+		port->IER |= IER_CD;
+		mcor1 |= MCOR1_CDZD;
+		mcor2 |= MCOR2_CDOD;
+	}
+	
+	if (C_CREAD(tty)) 
+		/* Enable receiver */
+		port->IER |= IER_RXD;
+	
+	/* Set input FIFO size (1-8 bytes) */
+	cor3 |= SPECIALIX_RXFIFO; 
+	/* Setting up CD186x channel registers */
+	sx_out(bp, CD186x_COR1, cor1);
+	sx_out(bp, CD186x_COR2, port->COR2);
+	sx_out(bp, CD186x_COR3, cor3);
+	/* Make CD186x know about registers change */
+	sx_wait_CCR(bp);
+	sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
+	/* Setting up modem option registers */
+#ifdef DEBUG_SPECIALIX
+	printk ("Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
+#endif
+	sx_out(bp, CD186x_MCOR1, mcor1);
+	sx_out(bp, CD186x_MCOR2, mcor2);
+	/* Enable CD186x transmitter & receiver */
+	sx_wait_CCR(bp);
+	sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
+	/* Enable interrupts */
+	sx_out(bp, CD186x_IER, port->IER);
+	/* And finally set the modem lines... */
+	sx_out(bp, CD186x_MSVR, port->MSVR);
+}
+
+
+/* Must be called with interrupts enabled */
+static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
+{
+	unsigned long flags;
+	
+	if (port->flags & ASYNC_INITIALIZED)
+		return 0;
+	
+	if (!port->xmit_buf) {
+		/* We may sleep in get_free_page() */
+		unsigned long tmp;
+		
+		if (!(tmp = get_free_page(GFP_KERNEL)))
+			return -ENOMEM;
+
+		if (port->xmit_buf) {
+			free_page(tmp);
+			return -ERESTARTSYS;
+		}
+		port->xmit_buf = (unsigned char *) tmp;
+	}
+		
+	save_flags(flags); cli();
+		
+	if (port->tty) 
+		clear_bit(TTY_IO_ERROR, &port->tty->flags);
+		
+	if (port->count == 1) 
+		bp->count++;
+		
+	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+	sx_change_speed(bp, port);
+	port->flags |= ASYNC_INITIALIZED;
+		
+	restore_flags(flags);
+	return 0;
+}
+
+
+/* Must be called with interrupts disabled */
+static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
+{
+	struct tty_struct *tty;
+	
+	if (!(port->flags & ASYNC_INITIALIZED)) 
+		return;
+	
+#ifdef SX_REPORT_OVERRUN
+	printk(KERN_INFO "sx%d: port %d: Total %ld overruns were detected.\n",
+	       board_No(bp), port_No(port), port->overrun);
+#endif	
+#ifdef SX_REPORT_FIFO
+	{
+		int i;
+		
+		printk(KERN_INFO "sx%d: port %d: FIFO hits [ ",
+		       board_No(bp), port_No(port));
+		for (i = 0; i < 10; i++) {
+			printk("%ld ", port->hits[i]);
+		}
+		printk("].\n");
+	}
+#endif	
+	if (port->xmit_buf) {
+		free_page((unsigned long) port->xmit_buf);
+		port->xmit_buf = NULL;
+	}
+
+	/* Select port */
+	sx_out(bp, CD186x_CAR, port_No(port));
+
+	if (!(tty = port->tty) || C_HUPCL(tty)) {
+		/* Drop DTR */
+		sx_out(bp, CD186x_MSVDTR, 0);
+	}
+	
+	/* Reset port */
+	sx_wait_CCR(bp);
+	sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
+	/* Disable all interrupts from this port */
+	port->IER = 0;
+	sx_out(bp, CD186x_IER, port->IER);
+	
+	if (tty)
+		set_bit(TTY_IO_ERROR, &tty->flags);
+	port->flags &= ~ASYNC_INITIALIZED;
+	
+	if (--bp->count < 0) {
+		printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d\n",
+		       board_No(bp), bp->count);
+		bp->count = 0;
+	}
+	
+	/*
+	 * If this is the last opened port on the board
+	 * shutdown whole board
+	 */
+	if (!bp->count) 
+		sx_shutdown_board(bp);
+}
+
+	
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+                           struct specialix_port *port)
+{
+	struct wait_queue wait = { current, NULL };
+	struct specialix_board *bp = port_Board(port);
+	int    retval;
+	int    do_clocal = 0;
+	int    CD;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
+		interruptible_sleep_on(&port->close_wait);
+		if (port->flags & ASYNC_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -ERESTARTSYS;
+	}
+
+	/*
+	 * If this is a callout device, then just make sure the normal
+	 * device isn't being used.
+	 */
+	if (tty->driver.subtype == SPECIALIX_TYPE_CALLOUT) {
+		if (port->flags & ASYNC_NORMAL_ACTIVE)
+			return -EBUSY;
+		if ((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (port->flags & ASYNC_SESSION_LOCKOUT) &&
+		    (port->session != current->session))
+			return -EBUSY;
+		if ((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (port->flags & ASYNC_PGRP_LOCKOUT) &&
+		    (port->pgrp != current->pgrp))
+			return -EBUSY;
+		port->flags |= ASYNC_CALLOUT_ACTIVE;
+		return 0;
+	}
+	
+	/*
+	 * If non-blocking mode is set, or the port is not enabled,
+	 * then make the check up front and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		if (port->flags & ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		port->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (port->flags & ASYNC_CALLOUT_ACTIVE) {
+		if (port->normal_termios.c_cflag & CLOCAL) 
+			do_clocal = 1;
+	} else {
+		if (C_CLOCAL(tty))
+			do_clocal = 1;
+	}
+	
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * rs_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&port->open_wait, &wait);
+	cli();
+	if (!tty_hung_up_p(filp))
+		port->count--;
+	sti();
+	port->blocked_open++;
+	while (1) {
+		cli();
+		sx_out(bp, CD186x_CAR, port_No(port));
+		CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
+		if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) {
+			if (SX_CRTSCTS (tty)) {
+				/* Activate RTS */
+				port->MSVR |= MSVR_DTR;
+				sx_out (bp, CD186x_MSVR, port->MSVR);
+			} else {
+				/* Activate DTR */
+				port->MSVR |= MSVR_DTR;
+				sx_out (bp, CD186x_MSVR, port->MSVR);
+			} 
+		}
+		sti();
+		current->state = TASK_INTERRUPTIBLE;
+		if (tty_hung_up_p(filp) ||
+		    !(port->flags & ASYNC_INITIALIZED)) {
+			if (port->flags & ASYNC_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;	
+			break;
+		}
+		if (!(port->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    !(port->flags & ASYNC_CLOSING) &&
+		    (do_clocal || CD))
+			break;
+		if (current->signal & ~current->blocked) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&port->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		port->count++;
+	port->blocked_open--;
+	if (retval)
+		return retval;
+	
+	port->flags |= ASYNC_NORMAL_ACTIVE;
+	return 0;
+}	
+
+
+static int sx_open(struct tty_struct * tty, struct file * filp)
+{
+	int board;
+	int error;
+	struct specialix_port * port;
+	struct specialix_board * bp;
+	unsigned long flags;
+	
+	board = SX_BOARD(MINOR(tty->device));
+
+	if (board > SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT))
+		return -ENODEV;
+	
+	bp = &sx_board[board];
+	port = sx_port + board * SX_NPORT + SX_PORT(MINOR(tty->device));
+
+#ifdef DEBUG_SPECIALIX
+	printk (KERN_DEBUG "Board = %d, bp = %p, port = %p, portno = %d.\n", 
+	        board, bp, port, SX_PORT(MINOR(tty->device)));
+#endif
+
+	if (sx_paranoia_check(port, tty->device, "sx_open"))
+		return -ENODEV;
+
+	if ((error = sx_setup_board(bp)))
+		return error;
+
+	port->count++;
+	tty->driver_data = port;
+	port->tty = tty;
+
+	if ((error = sx_setup_port(bp, port))) 
+		return error;
+	
+	if ((error = block_til_ready(tty, filp, port)))
+		return error;
+
+	if ((port->count == 1) && (port->flags & ASYNC_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == SPECIALIX_TYPE_NORMAL)
+			*tty->termios = port->normal_termios;
+		else
+			*tty->termios = port->callout_termios;
+		save_flags(flags); cli();
+		sx_change_speed(bp, port);
+		restore_flags(flags);
+	}
+
+	port->session = current->session;
+	port->pgrp = current->pgrp;
+	return 0;
+}
+
+
+static void sx_close(struct tty_struct * tty, struct file * filp)
+{
+	struct specialix_port *port = (struct specialix_port *) tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+	unsigned long timeout;
+	
+	if (!port || sx_paranoia_check(port, tty->device, "close"))
+		return;
+	
+	save_flags(flags); cli();
+	if (tty_hung_up_p(filp)) {
+		restore_flags(flags);
+		return;
+	}
+	
+	bp = port_Board(port);
+	if ((tty->count == 1) && (port->count != 1)) {
+		printk(KERN_ERR "sx%d: sx_close: bad port count;"
+		       " tty->count is 1, port count is %d\n",
+		       board_No(bp), port->count);
+		port->count = 1;
+	}
+	if (--port->count < 0) {
+		printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
+		       board_No(bp), port_No(port), port->count);
+		port->count = 0;
+	}
+	if (port->count) {
+		restore_flags(flags);
+		return;
+	}
+	port->flags |= ASYNC_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (port->flags & ASYNC_NORMAL_ACTIVE)
+		port->normal_termios = *tty->termios;
+	if (port->flags & ASYNC_CALLOUT_ACTIVE)
+		port->callout_termios = *tty->termios;
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify 
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	tty->closing = 1;
+	if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, port->closing_wait);
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts, and tell the
+	 * interrupt driver to stop checking the data ready bit in the
+	 * line status register.
+	 */
+	port->IER &= ~IER_RXD;
+	if (port->flags & ASYNC_INITIALIZED) {
+		port->IER &= ~IER_TXRDY;
+		port->IER |= IER_TXEMPTY;
+		sx_out(bp, CD186x_CAR, port_No(port));
+		sx_out(bp, CD186x_IER, port->IER);
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important if there is a transmit FIFO!
+		 */
+		timeout = jiffies+HZ;
+		while(port->IER & IER_TXEMPTY) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + port->timeout;
+			schedule();
+			if (jiffies > timeout) {
+				printk (KERN_INFO "Timeout waiting for close\n");
+				break;
+			}
+		}
+
+	}
+	sx_shutdown_port(bp, port);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	tty->closing = 0;
+	port->event = 0;
+	port->tty = 0;
+	if (port->blocked_open) {
+		if (port->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + port->close_delay;
+			schedule();
+		}
+		wake_up_interruptible(&port->open_wait);
+	}
+	port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+			 ASYNC_CLOSING);
+	wake_up_interruptible(&port->close_wait);
+	restore_flags(flags);
+}
+
+
+static int sx_write(struct tty_struct * tty, int from_user, 
+                    const unsigned char *buf, int count)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	int c, total = 0;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_write"))
+		return 0;
+	
+	bp = port_Board(port);
+
+	if (!tty || !port->xmit_buf || !tmp_buf)
+		return 0;
+
+	if (from_user)
+		down(&tmp_buf_sem);
+
+	save_flags(flags);
+	while (1) {
+		cli();		
+		c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+		                   SERIAL_XMIT_SIZE - port->xmit_head));
+		if (c <= 0)
+			break;
+
+		if (from_user) {
+			copy_from_user(tmp_buf, buf, c);
+			c = MIN(c, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+			               SERIAL_XMIT_SIZE - port->xmit_head));
+			memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c);
+		} else
+			memcpy(port->xmit_buf + port->xmit_head, buf, c);
+		port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+		port->xmit_cnt += c;
+		restore_flags(flags);
+		buf += c;
+		count -= c;
+		total += c;
+	}
+	if (from_user)
+		up(&tmp_buf_sem);
+	if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+	    !(port->IER & IER_TXRDY)) {
+		port->IER |= IER_TXRDY;
+		sx_out(bp, CD186x_CAR, port_No(port));
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+	restore_flags(flags);
+	return total;
+}
+
+
+static void sx_put_char(struct tty_struct * tty, unsigned char ch)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	unsigned long flags;
+
+	if (sx_paranoia_check(port, tty->device, "sx_put_char"))
+		return;
+
+	if (!tty || !port->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	
+	if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+		restore_flags(flags);
+		return;
+	}
+
+	port->xmit_buf[port->xmit_head++] = ch;
+	port->xmit_head &= SERIAL_XMIT_SIZE - 1;
+	port->xmit_cnt++;
+	restore_flags(flags);
+}
+
+
+static void sx_flush_chars(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_flush_chars"))
+		return;
+	
+	if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+	    !port->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	port->IER |= IER_TXRDY;
+	sx_out(port_Board(port), CD186x_CAR, port_No(port));
+	sx_out(port_Board(port), CD186x_IER, port->IER);
+	restore_flags(flags);
+}
+
+
+static int sx_write_room(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	int	ret;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_write_room"))
+		return 0;
+
+	ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
+	if (ret < 0)
+		ret = 0;
+	return ret;
+}
+
+
+static int sx_chars_in_buffer(struct tty_struct *tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_chars_in_buffer"))
+		return 0;
+	
+	return port->xmit_cnt;
+}
+
+
+static void sx_flush_buffer(struct tty_struct *tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_flush_buffer"))
+		return;
+
+	save_flags(flags); cli();
+	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+	restore_flags(flags);
+	
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+}
+
+
+static int sx_get_modem_info(struct specialix_port * port, unsigned int *value)
+{
+	struct specialix_board * bp;
+	unsigned char status;
+	unsigned int result;
+	unsigned long flags;
+
+	bp = port_Board(port);
+	save_flags(flags); cli();
+	sx_out(bp, CD186x_CAR, port_No(port));
+	status = sx_in(bp, CD186x_MSVR);
+	restore_flags(flags);
+#ifdef DEBUG_SPECIALIX
+	printk (KERN_DEBUG "Got msvr[%d] = %02x, car = %d.\n", 
+		port_No(port), status, sx_in (bp, CD186x_CAR));
+	printk (KERN_DEBUG "sx_port = %p, port = %p\n", sx_port, port);
+#endif
+	if (SX_CRTSCTS(port->tty)) {
+		result  = /*   (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */ 
+		          |   ((status & MSVR_DTR) ? TIOCM_RTS : 0)
+		          |   ((status & MSVR_CD)  ? TIOCM_CAR : 0)
+		          |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
+		          |   ((status & MSVR_CTS) ? TIOCM_CTS : 0);
+	} else {
+		result  = /*   (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */ 
+		          |   ((status & MSVR_DTR) ? TIOCM_DTR : 0)
+		          |   ((status & MSVR_CD)  ? TIOCM_CAR : 0)
+		          |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
+		          |   ((status & MSVR_CTS) ? TIOCM_CTS : 0);
+	}
+	put_user(result,(unsigned long *) value);
+	return 0;
+}
+
+
+static int sx_set_modem_info(struct specialix_port * port, unsigned int cmd,
+                             unsigned int *value)
+{
+	int error;
+	unsigned int arg;
+	unsigned long flags;
+	struct specialix_board *bp = port_Board(port);
+
+	error = verify_area(VERIFY_READ, value, sizeof(int));
+	if (error) 
+		return error;
+
+	Get_user(arg, (unsigned long *) value);
+	switch (cmd) {
+	case TIOCMBIS: 
+	   /*	if (arg & TIOCM_RTS) 
+			port->MSVR |= MSVR_RTS; */
+	   /*   if (arg & TIOCM_DTR)
+			port->MSVR |= MSVR_DTR; */
+
+		if (SX_CRTSCTS(port->tty)) {
+			if (arg & TIOCM_RTS)
+				port->MSVR |= MSVR_DTR; 
+		} else {
+			if (arg & TIOCM_DTR)
+				port->MSVR |= MSVR_DTR; 
+		}	     
+		break;
+	case TIOCMBIC:
+	  /*	if (arg & TIOCM_RTS)
+			port->MSVR &= ~MSVR_RTS; */
+	  /*    if (arg & TIOCM_DTR)
+			port->MSVR &= ~MSVR_DTR; */
+		if (SX_CRTSCTS(port->tty)) {
+			if (arg & TIOCM_RTS)
+				port->MSVR &= ~MSVR_DTR;
+		} else {
+			if (arg & TIOCM_DTR)
+				port->MSVR &= ~MSVR_DTR;
+		}
+		break;
+	case TIOCMSET:
+	  /* port->MSVR = (arg & TIOCM_RTS) ? (port->MSVR | MSVR_RTS) : 
+						 (port->MSVR & ~MSVR_RTS); */
+	  /* port->MSVR = (arg & TIOCM_DTR) ? (port->MSVR | MSVR_DTR) : 
+						 (port->MSVR & ~MSVR_DTR); */
+		if (SX_CRTSCTS(port->tty)) {
+	  		port->MSVR = (arg & TIOCM_RTS) ? 
+			                         (port->MSVR |  MSVR_DTR) : 
+			                         (port->MSVR & ~MSVR_DTR);
+		} else {
+			port->MSVR = (arg & TIOCM_DTR) ?
+			                         (port->MSVR |  MSVR_DTR):
+			                         (port->MSVR & ~MSVR_DTR);
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	save_flags(flags); cli();
+	sx_out(bp, CD186x_CAR, port_No(port));
+	sx_out(bp, CD186x_MSVR, port->MSVR);
+	restore_flags(flags);
+	return 0;
+}
+
+
+extern inline void sx_send_break(struct specialix_port * port, unsigned long length)
+{
+	struct specialix_board *bp = port_Board(port);
+	unsigned long flags;
+	
+	save_flags(flags); cli();
+	port->break_length = SPECIALIX_TPS / HZ * length;
+	port->COR2 |= COR2_ETC;
+	port->IER  |= IER_TXRDY;
+	sx_out(bp, CD186x_CAR, port_No(port));
+	sx_out(bp, CD186x_COR2, port->COR2);
+	sx_out(bp, CD186x_IER, port->IER);
+	sx_wait_CCR(bp);
+	sx_out(bp, CD186x_CCR, CCR_CORCHG2);
+	sx_wait_CCR(bp);
+	restore_flags(flags);
+}
+
+
+extern inline int sx_set_serial_info(struct specialix_port * port,
+                                     struct serial_struct * newinfo)
+{
+	struct serial_struct tmp;
+	struct specialix_board *bp = port_Board(port);
+	int change_speed;
+	unsigned long flags;
+	int error;
+	
+	error = verify_area(VERIFY_READ, (void *) newinfo, sizeof(tmp));
+	if (error)
+		return error;
+
+	copy_from_user(&tmp, newinfo, sizeof(tmp));
+	
+#if 0	
+	if ((tmp.irq != bp->irq) ||
+	    (tmp.port != bp->base) ||
+	    (tmp.type != PORT_CIRRUS) ||
+	    (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
+	    (tmp.custom_divisor != 0) ||
+	    (tmp.xmit_fifo_size != CD186x_NFIFO) ||
+	    (tmp.flags & ~SPECIALIX_LEGAL_FLAGS))
+		return -EINVAL;
+#endif	
+
+	change_speed = ((port->flags & ASYNC_SPD_MASK) !=
+			(tmp.flags & ASYNC_SPD_MASK));
+	
+	if (!suser()) {
+		if ((tmp.close_delay != port->close_delay) ||
+		    (tmp.closing_wait != port->closing_wait) ||
+		    ((tmp.flags & ~ASYNC_USR_MASK) !=
+		     (port->flags & ~ASYNC_USR_MASK)))
+			return -EPERM;
+		port->flags = ((port->flags & ~ASYNC_USR_MASK) |
+		                  (tmp.flags & ASYNC_USR_MASK));
+	} else {
+		port->flags = ((port->flags & ~ASYNC_FLAGS) |
+		                  (tmp.flags & ASYNC_FLAGS));
+		port->close_delay = tmp.close_delay;
+		port->closing_wait = tmp.closing_wait;
+	}
+	if (change_speed) {
+		save_flags(flags); cli();
+		sx_change_speed(bp, port);
+		restore_flags(flags);
+	}
+	return 0;
+}
+
+
+extern inline int sx_get_serial_info(struct specialix_port * port,
+				     struct serial_struct * retinfo)
+{
+	struct serial_struct tmp;
+	struct specialix_board *bp = port_Board(port);
+	int error;
+	
+	error = verify_area(VERIFY_WRITE, (void *) retinfo, sizeof(tmp));
+	if (error)
+		return error;
+
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = PORT_CIRRUS;
+	tmp.line = port - sx_port;
+	tmp.port = bp->base;
+	tmp.irq  = bp->irq;
+	tmp.flags = port->flags;
+	tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
+	tmp.close_delay = port->close_delay * HZ/100;
+	tmp.closing_wait = port->closing_wait * HZ/100;
+	tmp.xmit_fifo_size = CD186x_NFIFO;
+	copy_to_user(retinfo, &tmp, sizeof(tmp));
+	return 0;
+}
+
+
+static int sx_ioctl(struct tty_struct * tty, struct file * filp, 
+                    unsigned int cmd, unsigned long arg)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	int error;
+	int retval;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_ioctl"))
+		return -ENODEV;
+	
+	switch (cmd) {
+	 case TCSBRK:	/* SVID version: non-zero arg --> no break */
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+		tty_wait_until_sent(tty, 0);
+		if (!arg)
+			sx_send_break(port, HZ/4);	/* 1/4 second */
+		return 0;
+	 case TCSBRKP:	/* support for POSIX tcsendbreak() */
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+		tty_wait_until_sent(tty, 0);
+		sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
+		return 0;
+	 case TIOCGSOFTCAR:
+		error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+		if (error)
+			return error;
+		put_user(C_CLOCAL(tty) ? 1 : 0,
+		         (unsigned long *) arg);
+		return 0;
+	 case TIOCSSOFTCAR:
+		Get_user(arg, (unsigned long *) arg);
+		tty->termios->c_cflag =
+			((tty->termios->c_cflag & ~CLOCAL) |
+			(arg ? CLOCAL : 0));
+		return 0;
+	 case TIOCMGET:
+		error = verify_area(VERIFY_WRITE, (void *) arg,
+		                    sizeof(unsigned int));
+		if (error)
+			return error;
+		return sx_get_modem_info(port, (unsigned int *) arg);
+	 case TIOCMBIS:
+	 case TIOCMBIC:
+	 case TIOCMSET:
+		return sx_set_modem_info(port, cmd, (unsigned int *) arg);
+	 case TIOCGSERIAL:	
+		return sx_get_serial_info(port, (struct serial_struct *) arg);
+	 case TIOCSSERIAL:	
+		return sx_set_serial_info(port, (struct serial_struct *) arg);
+	 default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+
+static void sx_throttle(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_throttle"))
+		return;
+	
+	bp = port_Board(port);
+	
+	save_flags(flags); cli();
+
+	/* Use DTR instead of RTS ! */
+	if (SX_CRTSCTS (tty)) 
+		port->MSVR &= ~MSVR_DTR;
+	else {
+		/* Auch!!! I think the system shouldn't call this then. */
+		/* Or maybe we're supposed (allowed?) to do our side of hw
+		   handshake anyway, even when hardware handshake is off. 
+		   When you see this in your logs, please report.... */
+		printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
+	                 port_No (port));
+	}
+	sx_out(bp, CD186x_CAR, port_No(port));
+	if (I_IXOFF(tty)) {
+		sx_wait_CCR(bp);
+		sx_out(bp, CD186x_CCR, CCR_SSCH2);
+		sx_wait_CCR(bp);
+	}
+	sx_out(bp, CD186x_MSVR, port->MSVR);
+	restore_flags(flags);
+}
+
+
+static void sx_unthrottle(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_unthrottle"))
+		return;
+	
+	bp = port_Board(port);
+	
+	save_flags(flags); cli();
+	/* XXXX Use DTR INSTEAD???? */
+	if (SX_CRTSCTS(tty)) {
+		port->MSVR |= MSVR_DTR;
+	} /* Else clause: see remark in "sx_throttle"... */
+
+	sx_out(bp, CD186x_CAR, port_No(port));
+	if (I_IXOFF(tty)) {
+		sx_wait_CCR(bp);
+		sx_out(bp, CD186x_CCR, CCR_SSCH1);
+		sx_wait_CCR(bp);
+	}
+	sx_out(bp, CD186x_MSVR, port->MSVR);
+	restore_flags(flags);
+}
+
+
+static void sx_stop(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_stop"))
+		return;
+	
+	bp = port_Board(port);
+	
+	save_flags(flags); cli();
+	port->IER &= ~IER_TXRDY;
+	sx_out(bp, CD186x_CAR, port_No(port));
+	sx_out(bp, CD186x_IER, port->IER);
+	restore_flags(flags);
+}
+
+
+static void sx_start(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_start"))
+		return;
+	
+	bp = port_Board(port);
+	
+	save_flags(flags); cli();
+	if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
+		port->IER |= IER_TXRDY;
+		sx_out(bp, CD186x_CAR, port_No(port));
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+	restore_flags(flags);
+}
+
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred.  The path of
+ * hangup processing is:
+ *
+ * 	serial interrupt routine -> (scheduler tqueue) ->
+ * 	do_sx_hangup() -> tty->hangup() -> sx_hangup()
+ * 
+ */
+static void do_sx_hangup(void *private_)
+{
+	struct specialix_port	*port = (struct specialix_port *) private_;
+	struct tty_struct	*tty;
+	
+	tty = port->tty;
+	if (!tty)
+		return;
+
+	tty_hangup(tty);
+}
+
+
+static void sx_hangup(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_hangup"))
+		return;
+	
+	bp = port_Board(port);
+	
+	sx_shutdown_port(bp, port);
+	port->event = 0;
+	port->count = 0;
+	port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+	port->tty = 0;
+	wake_up_interruptible(&port->open_wait);
+}
+
+
+static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_set_termios"))
+		return;
+	
+	if (tty->termios->c_cflag == old_termios->c_cflag &&
+	    tty->termios->c_iflag == old_termios->c_iflag)
+		return;
+
+	save_flags(flags); cli();
+	sx_change_speed(port_Board(port), port);
+	restore_flags(flags);
+
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		sx_start(tty);
+	}
+}
+
+
+static void do_specialix_bh(void)
+{
+	 run_task_queue(&tq_specialix);
+}
+
+
+static void do_softint(void *private_)
+{
+	struct specialix_port	*port = (struct specialix_port *) private_;
+	struct tty_struct	*tty;
+	
+	if(!(tty = port->tty)) 
+		return;
+
+	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+}
+
+
+static int sx_init_drivers(void)
+{
+	int error;
+	int i;
+
+	
+	if (!(tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL))) {
+		printk(KERN_ERR "sx: Couldn't get free page.\n");
+		return 1;
+	}
+	init_bh(SPECIALIX_BH, do_specialix_bh);
+	memset(IRQ_to_board, 0, sizeof(IRQ_to_board));
+	memset(&specialix_driver, 0, sizeof(specialix_driver));
+	specialix_driver.magic = TTY_DRIVER_MAGIC;
+	specialix_driver.name = "ttyW";
+	specialix_driver.major = SPECIALIX_NORMAL_MAJOR;
+	specialix_driver.num = SX_NBOARD * SX_NPORT;
+	specialix_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	specialix_driver.subtype = SPECIALIX_TYPE_NORMAL;
+	specialix_driver.init_termios = tty_std_termios;
+	specialix_driver.init_termios.c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	specialix_driver.flags = TTY_DRIVER_REAL_RAW;
+	specialix_driver.refcount = &specialix_refcount;
+	specialix_driver.table = specialix_table;
+	specialix_driver.termios = specialix_termios;
+	specialix_driver.termios_locked = specialix_termios_locked;
+
+	specialix_driver.open  = sx_open;
+	specialix_driver.close = sx_close;
+	specialix_driver.write = sx_write;
+	specialix_driver.put_char = sx_put_char;
+	specialix_driver.flush_chars = sx_flush_chars;
+	specialix_driver.write_room = sx_write_room;
+	specialix_driver.chars_in_buffer = sx_chars_in_buffer;
+	specialix_driver.flush_buffer = sx_flush_buffer;
+	specialix_driver.ioctl = sx_ioctl;
+	specialix_driver.throttle = sx_throttle;
+	specialix_driver.unthrottle = sx_unthrottle;
+	specialix_driver.set_termios = sx_set_termios;
+	specialix_driver.stop = sx_stop;
+	specialix_driver.start = sx_start;
+	specialix_driver.hangup = sx_hangup;
+
+	specialix_callout_driver = specialix_driver;
+	specialix_callout_driver.name = "cuw";
+	specialix_callout_driver.major = SPECIALIX_CALLOUT_MAJOR;
+	specialix_callout_driver.subtype = SPECIALIX_TYPE_CALLOUT;
+	
+	if ((error = tty_register_driver(&specialix_driver))) {
+		free_page((unsigned long)tmp_buf);
+		printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
+		       error);
+		return 1;
+	}
+	if ((error = tty_register_driver(&specialix_callout_driver))) {
+		free_page((unsigned long)tmp_buf);
+		tty_unregister_driver(&specialix_driver);
+		printk(KERN_ERR "sx: Couldn't register specialix IO8+ callout driver, error = %d\n",
+		       error);
+		return 1;
+	}
+	
+	memset(sx_port, 0, sizeof(sx_port));
+	for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
+		sx_port[i].callout_termios = specialix_callout_driver.init_termios;
+		sx_port[i].normal_termios  = specialix_driver.init_termios;
+		sx_port[i].magic = SPECIALIX_MAGIC;
+		sx_port[i].tqueue.routine = do_softint;
+		sx_port[i].tqueue.data = &sx_port[i];
+		sx_port[i].tqueue_hangup.routine = do_sx_hangup;
+		sx_port[i].tqueue_hangup.data = &sx_port[i];
+		sx_port[i].close_delay = 50 * HZ/100;
+		sx_port[i].closing_wait = 3000 * HZ/100;
+	}
+	
+	return 0;
+}
+
+
+static void sx_release_drivers(void)
+{
+	free_page((unsigned long)tmp_buf);
+	tty_unregister_driver(&specialix_driver);
+	tty_unregister_driver(&specialix_callout_driver);
+}
+
+
+#ifndef MODULE
+/*
+ * Called at boot time.
+ * 
+ * You can specify IO base for up to SX_NBOARD cards,
+ * using line "specialix=0xiobase1,0xiobase2,.." at LILO prompt.
+ * Note that there will be no probing at default
+ * addresses in this case.
+ *
+ */ 
+void specialix_setup(char *str, int * ints)
+{
+	int i;
+        
+	for (i=0;i<SX_NBOARD;i++) {
+		sx_board[i].base = 0;
+	}
+
+	for (i = 1; i <= ints[0]; i++) {
+		if (i&1)
+			sx_board[i/2].base = ints[i];
+		else
+			sx_board[i/2 -1].irq = ints[i];
+	}
+}
+#endif
+
+/* 
+ * This routine must be called by kernel at boot time 
+ */
+int specialix_init(void) 
+{
+	int i;
+	int found = 0;
+
+	printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997.\n");
+	printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
+#ifdef CONFIG_SPECIALIX_RTSCTS
+	printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
+#else
+	printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
+#endif
+	
+	if (sx_init_drivers()) 
+		return -EIO;
+
+	for (i = 0; i < SX_NBOARD; i++) 
+		if (sx_board[i].base && !sx_probe(&sx_board[i]))
+			found++;
+
+	if (!found) {
+		sx_release_drivers();
+		printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+#ifdef MODULE
+int iobase[SX_NBOARD]  = {0,};
+
+int irq [SX_NBOARD] = {0,};
+
+/*
+ * You can setup up to 4 boards.
+ * by specifying "iobase=0xXXX iobase1=0xXXX ..." as insmod parameter.
+ * 
+ * More than 4 boards in one computer is not possible, as the card can
+ * only use 4 different interrupts. 
+ *
+ */
+int init_module(void) 
+{
+	int i;
+
+	if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
+		for(i = 0; i < SX_NBOARD; i++) {
+			sx_board[i].base = iobase[i];
+			sx_board[i].irq = irq[i];
+		}
+	}
+
+	return specialix_init();
+}
+	
+
+void cleanup_module(void)
+{
+	int i;
+	
+	sx_release_drivers();
+	for (i = 0; i < SX_NBOARD; i++)
+		if (sx_board[i].flags & SX_BOARD_PRESENT) 
+			sx_release_io_range(&sx_board[i]);
+#ifdef SPECIALIX_TIMER
+	del_timer (&missed_irq_timer);
+#endif
+	
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.69/linux/drivers/char/specialix_io8.h linux/drivers/char/specialix_io8.h
--- v2.1.69/linux/drivers/char/specialix_io8.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/specialix_io8.h	Tue Dec  2 09:19:03 1997
@@ -0,0 +1,146 @@
+/*
+ *      linux/drivers/char/specialix_io8.h  -- 
+ *                                   Specialix IO8+ multiport serial driver.
+ *
+ *      Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *
+ *
+ *      Specialix pays for the development and support of this driver.
+ *      Please DO contact io8-linux@specialix.co.uk if you require
+ *      support.
+ *
+ *      This driver was developped in the BitWizard linux device
+ *      driver service. If you require a linux device driver for your
+ *      product, please contact devices@BitWizard.nl for a quote.
+ *
+ *      This code is firmly based on the riscom/8 serial driver,
+ *      written by Dmitry Gorodchanin. The specialix IO8+ card
+ *      programming information was obtained from the CL-CD1865 Data
+ *      Book, and Specialix document number 6200059: IO8+ Hardware
+ *      Functional Specification.
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License as
+ *      published by the Free Software Foundation; either version 2 of
+ *      the License, or (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be
+ *      useful, but WITHOUT ANY WARRANTY; without even the implied
+ *      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ *      PURPOSE.  See the GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public
+ *      License along with this program; if not, write to the Free
+ *      Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ *      USA.
+ * */
+
+#ifndef __LINUX_SPECIALIX_H
+#define __LINUX_SPECIALIX_H
+
+#include <linux/serial.h>
+
+#ifdef __KERNEL__
+
+#define SX_NBOARD		4
+/* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */
+#define SX_IO_SPACE             4
+/* eight ports per board. */
+#define SX_NPORT        	8
+#define SX_BOARD(line)		((line) / SX_NPORT)
+#define SX_PORT(line)		((line) & (SX_NPORT - 1))
+
+
+#define SX_DATA_REG 0     /* Base+0 : Data register */
+#define SX_ADDR_REG 1     /* base+1 : Address register. */
+
+#define MHz *1000000	/* I'm ashamed of myself. */
+
+/* On-board oscillator frequency */
+#define SX_OSCFREQ      (25 MHz/2)
+/* There is a 25MHz crystal on the board, but the chip is in /2 mode */
+
+
+/* Ticks per sec. Used for setting receiver timeout and break length */
+#define SPECIALIX_TPS		4000
+
+/* Yeah, after heavy testing I decided it must be 6.
+ * Sure, You can change it if needed.
+ */
+#define SPECIALIX_RXFIFO	6	/* Max. receiver FIFO size (1-8) */
+
+#define SPECIALIX_MAGIC		0x0907
+
+#define SX_CCR_TIMEOUT 10000   /* CCR timeout. You may need to wait upto
+                                  10 milliseconds before the internal
+                                  processor is available again after
+                                  you give it a command */
+
+#define SX_IOBASE1	0x100
+#define SX_IOBASE2	0x180
+#define SX_IOBASE3	0x250
+#define SX_IOBASE4	0x260
+
+struct specialix_board {
+	unsigned long   flags;
+	unsigned short	base;
+	unsigned char 	irq;
+	signed   char	count;
+	unsigned char	DTR;
+        int reg;
+};
+
+#define SX_BOARD_PRESENT	0x00000001
+#define SX_BOARD_ACTIVE		0x00000002
+
+
+struct specialix_port {
+	int			magic;
+	int			baud_base;
+	int			flags;
+	struct tty_struct 	* tty;
+	int			count;
+	int			blocked_open;
+	int			event;
+	int			timeout;
+	int			close_delay;
+	long			session;
+	long			pgrp;
+	unsigned char 		* xmit_buf;
+	int			custom_divisor;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	struct termios          normal_termios;
+	struct termios		callout_termios;
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
+	struct tq_struct	tqueue;
+	struct tq_struct	tqueue_hangup;
+	short			wakeup_chars;
+	short			break_length;
+	unsigned short		closing_wait;
+	unsigned char		mark_mask;
+	unsigned char		IER;
+	unsigned char		MSVR;
+	unsigned char		COR2;
+#ifdef SX_REPORT_OVERRUN
+	unsigned long		overrun;
+#endif	
+#ifdef SX_REPORT_FIFO
+	unsigned long		hits[10];
+#endif
+};
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_SPECIALIX_H */
+
+
+
+
+
+
+
+
+
diff -u --recursive --new-file v2.1.69/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.69/linux/drivers/char/tty_io.c	Tue Dec  2 09:49:39 1997
+++ linux/drivers/char/tty_io.c	Tue Dec  2 09:19:03 1997
@@ -1970,6 +1970,9 @@
 #ifdef CONFIG_RISCOM8
 	riscom8_init();
 #endif
+#ifdef CONFIG_SPECIALIX
+	specialix_init();
+#endif
 	pty_init();
 #ifdef CONFIG_VT
 	vcs_init();
diff -u --recursive --new-file v2.1.69/linux/drivers/misc/parport_share.c linux/drivers/misc/parport_share.c
--- v2.1.69/linux/drivers/misc/parport_share.c	Mon Dec  1 12:04:12 1997
+++ linux/drivers/misc/parport_share.c	Mon Dec  1 14:49:02 1997
@@ -279,7 +279,7 @@
 
 	/* Swap the IRQ handlers. */
 	if (dev->port->irq != PARPORT_IRQ_NONE) {
-		free_irq(dev->port->irq, dev->port);
+		free_irq(dev->port->irq, pd1?pd1->private:NULL);
 		request_irq(dev->port->irq, dev->irq_func ? dev->irq_func :
 			    parport_null_intr_func, SA_INTERRUPT, dev->name,
 			    dev->private);
@@ -307,7 +307,7 @@
 
 	/* Point IRQs somewhere harmless. */
 	if (dev->port->irq != PARPORT_IRQ_NONE) {
-		free_irq(dev->port->irq, dev->port);
+		free_irq(dev->port->irq, dev->private);
 		request_irq(dev->port->irq, parport_null_intr_func,
 			    SA_INTERRUPT, dev->port->name, NULL);
  	}
diff -u --recursive --new-file v2.1.69/linux/drivers/pnp/parport_probe.c linux/drivers/pnp/parport_probe.c
--- v2.1.69/linux/drivers/pnp/parport_probe.c	Tue Sep 23 16:48:48 1997
+++ linux/drivers/pnp/parport_probe.c	Tue Dec  2 09:38:15 1997
@@ -1,4 +1,4 @@
-/* $Id: parport_probe.c,v 1.1.2.9 1997/03/29 21:08:16 phil Exp $ 
+/* $Id: parport_probe.c,v 1.3 1997/10/19 18:18:46 phil Exp $ 
  * Parallel port device probing code
  * 
  * Authors:    Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
@@ -28,9 +28,9 @@
 {
 	unsigned char i;
 	i = parport_read_status(port)>>3;
-	i&=~8;
-	if ( ( i & 0x10) == 0) i|=8;
-	return(i & 0x0f);
+	i &= ~8;
+	if ((i & 0x10) == 0) i |= 8;
+	return (i & 0x0f);
 }
 
 static void read_terminate(struct parport *port) {
@@ -44,7 +44,6 @@
 	parport_wait_peripheral(port, 0x80, 0x80);
 	/* no timeout possible, Autofeed low, SelectIN high */
 	parport_write_control(port, (parport_read_control(port) & ~2) | 8);
-	return;
 }
 
 static long read_polled(struct parport *port, char *buf, 
@@ -94,7 +93,6 @@
 		return;
 
 	wake_up(&wait_q);
-	return;
 }
 
 int parport_probe(struct parport *port, char *buffer, int len)
@@ -221,9 +219,11 @@
 
 static void pretty_print(struct parport *port)
 {
-	printk(KERN_INFO "%s: %s", port->name, classes[port->probe_info.class].descr);
+	printk(KERN_INFO "%s: %s", port->name,
+	       classes[port->probe_info.class].descr);
 	if (port->probe_info.class) {
-		printk(", %s (%s)", port->probe_info.model, port->probe_info.mfr);
+		printk(", %s %s", port->probe_info.mfr, 
+		       port->probe_info.model);
 	}
 	printk("\n");
 }
@@ -267,12 +267,16 @@
 int init_module(void)
 {
 	struct parport *p;
+	MOD_INC_USE_COUNT;
 	for (p = parport_enumerate(); p; p = p->next) 
 		parport_probe_one(p);
+	parport_probe_hook = &parport_probe_one;
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
 void cleanup_module(void)
 {
+	parport_probe_hook = NULL;
 }
 #endif
diff -u --recursive --new-file v2.1.69/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v2.1.69/linux/drivers/scsi/sr.c	Mon Nov  3 13:04:26 1997
+++ linux/drivers/scsi/sr.c	Tue Dec  2 11:41:45 1997
@@ -31,7 +31,6 @@
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/cdrom.h>
-#include <linux/ucdrom.h>
 #include <linux/interrupt.h>
 #include <linux/config.h>
 #include <asm/system.h>
@@ -90,7 +89,6 @@
         sr_open,                      /* open */
         sr_release,                   /* release */
         sr_drive_status,              /* drive status */
-        sr_disk_status,               /* disc status */
         sr_media_change,              /* media changed */
         sr_tray_move,                 /* tray move */
         sr_lock_door,                 /* lock door */
@@ -102,7 +100,8 @@
         sr_audio_ioctl,               /* audio ioctl */
         sr_dev_ioctl,                 /* device-specific ioctl */
         CDC_CLOSE_TRAY | CDC_OPEN_TRAY| CDC_LOCK | CDC_SELECT_SPEED |
-        CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO,
+        CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO |
+        CDC_RESET | CDC_IOCTLS | CDC_DRIVE_STATUS,
         0
 };
 
@@ -1022,6 +1021,7 @@
 void sr_finish()
 {
     int i;
+    char name[6];
 
     blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
     blk_size[MAJOR_NR] = sr_sizes;
@@ -1052,7 +1052,10 @@
 	scsi_CDs[i].cdi.dev        = MKDEV(MAJOR_NR,i);
 	scsi_CDs[i].cdi.mask       = 0;
 	get_capabilities(i);
-	register_cdrom(&scsi_CDs[i].cdi, "sr");
+
+	sprintf(name, "sr%d", i);
+	strcpy(scsi_CDs[i].cdi.name, name);
+	register_cdrom(&scsi_CDs[i].cdi);
     }
 
 
diff -u --recursive --new-file v2.1.69/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c
--- v2.1.69/linux/drivers/scsi/sr_ioctl.c	Mon Nov  3 13:04:26 1997
+++ linux/drivers/scsi/sr_ioctl.c	Tue Dec  2 11:41:45 1997
@@ -12,7 +12,6 @@
 #include <scsi/scsi_ioctl.h>
 
 #include <linux/cdrom.h>
-#include <linux/ucdrom.h>
 #include "sr.h"
 
 #if 0
@@ -729,7 +728,7 @@
                 return -ENOMEM;
 
 	lba = (((msf.cdmsf_min0 * CD_SECS) + msf.cdmsf_sec0)
-			* CD_FRAMES + msf.cdmsf_frame0) - CD_BLOCK_OFFSET;
+			* CD_FRAMES + msf.cdmsf_frame0) - CD_MSF_OFFSET;
         if (lba < 0 || lba >= scsi_CDs[target].capacity)
             return -EINVAL;
 
@@ -757,7 +756,7 @@
             lba = ra.addr.lba;
         else
             lba = (((ra.addr.msf.minute * CD_SECS) + ra.addr.msf.second)
-                   * CD_FRAMES + ra.addr.msf.frame) - CD_BLOCK_OFFSET;
+                   * CD_FRAMES + ra.addr.msf.frame) - CD_MSF_OFFSET;
 
         if (lba < 0 || lba >= scsi_CDs[target].capacity)
             return -EINVAL;
diff -u --recursive --new-file v2.1.69/linux/drivers/scsi/sr_vendor.c linux/drivers/scsi/sr_vendor.c
--- v2.1.69/linux/drivers/scsi/sr_vendor.c	Mon Nov  3 13:04:26 1997
+++ linux/drivers/scsi/sr_vendor.c	Tue Dec  2 11:41:45 1997
@@ -44,7 +44,6 @@
 #include <scsi/scsi_ioctl.h>
 
 #include <linux/cdrom.h>
-#include <linux/ucdrom.h>
 #include "sr.h"
 
 #if 0
@@ -231,7 +230,7 @@
 		frame  = BCD_TO_BIN(buffer[3]);
 		sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
 		if (sector)
-			sector -= CD_BLOCK_OFFSET;
+			sector -= CD_MSF_OFFSET;
 		break;
 
 	case VENDOR_HP_4020:
diff -u --recursive --new-file v2.1.69/linux/drivers/sound/lowlevel/awe_wave.c linux/drivers/sound/lowlevel/awe_wave.c
--- v2.1.69/linux/drivers/sound/lowlevel/awe_wave.c	Mon Dec  1 12:04:13 1997
+++ linux/drivers/sound/lowlevel/awe_wave.c	Tue Dec  2 14:05:54 1997
@@ -551,13 +551,17 @@
 	INIT_TABLE(samples, max_samples, AWE_MAX_SAMPLES, awe_sample_list);
 	INIT_TABLE(infos, max_infos, AWE_MAX_INFOS, awe_voice_list);
 
-	if ((my_dev=sound_alloc_synthdev())!=-1)
+	my_dev = sound_alloc_synthdev();
+	if (my_dev == -1) {
+		my_free(voices);
+		my_free(channels);
 		printk(KERN_WARNING "AWE32 Error: too many synthesizers\n");
-	else {
-		voice_alloc = &awe_operations.alloc;
-		voice_alloc->max_voice = awe_max_voices;
-		synth_devs[my_dev] = &awe_operations;
+		return 0;
 	}
+
+	voice_alloc = &awe_operations.alloc;
+	voice_alloc->max_voice = awe_max_voices;
+	synth_devs[my_dev] = &awe_operations;
 
 #ifdef CONFIG_AWE32_MIXER
 	if ((my_mixerdev=sound_alloc_mixerdev())!=-1) {
diff -u --recursive --new-file v2.1.69/linux/fs/Config.in linux/fs/Config.in
--- v2.1.69/linux/fs/Config.in	Sat Nov  1 11:04:27 1997
+++ linux/fs/Config.in	Tue Dec  2 14:24:04 1997
@@ -43,6 +43,7 @@
       define_bool CONFIG_LOCKD n
     fi
   fi
+  tristate 'Coda filesystem support (advanced network fs)' CONFIG_CODA_FS
   tristate 'SMB filesystem support (to mount WfW shares etc..)' CONFIG_SMB_FS
   if [ "$CONFIG_SMB_FS" != "n" ]; then
     bool 'SMB Win95 bug work-around' CONFIG_SMB_WIN95
diff -u --recursive --new-file v2.1.69/linux/fs/Makefile linux/fs/Makefile
--- v2.1.69/linux/fs/Makefile	Sat Oct 25 02:44:17 1997
+++ linux/fs/Makefile	Tue Dec  2 14:24:04 1997
@@ -16,7 +16,7 @@
 		inode.o dcache.o attr.o bad_inode.o $(BINFMTS) 
 
 MOD_LIST_NAME := FS_MODULES
-ALL_SUB_DIRS = minix ext2 fat msdos vfat proc isofs nfs umsdos \
+ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos \
 		hpfs sysv smbfs ncpfs ufs affs romfs autofs lockd nfsd nls
 
 ifeq ($(CONFIG_QUOTA),y)
@@ -27,6 +27,14 @@
 
 ifeq ($(CONFIG_TRANS_NAMES),y)
 O_OBJS += nametrans.o
+endif
+
+ifeq ($(CONFIG_CODA_FS),y)
+SUB_DIRS += coda
+else
+  ifeq ($(CONFIG_CODA_FS),m)
+  MOD_SUB_DIRS += coda
+  endif
 endif
 
 ifeq ($(CONFIG_MINIX_FS),y)
diff -u --recursive --new-file v2.1.69/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.69/linux/fs/buffer.c	Mon Dec  1 12:04:14 1997
+++ linux/fs/buffer.c	Tue Dec  2 11:49:33 1997
@@ -790,50 +790,85 @@
 	}	
 
 	/*
-	 * If we achieved at least half of our goal, return now.
+	 * If there are dirty buffers, do a non-blocking wake-up.
+	 * This increases the chances of having buffers available
+	 * for the next call ...
 	 */
-	if (obtained >= (needed >> 1))
-		return;
-	
-	/* Too bad, that was not enough. Try a little harder to grow some. */
-	if (nr_free_pages > min_free_pages + 5) {
-		if (grow_buffers(GFP_BUFFER, size)) {
-			obtained += PAGE_SIZE;
-			goto repeat;
-		}
-	}
+	if (nr_buffers_type[BUF_DIRTY])
+		wakeup_bdflush(0);
 
 	/*
-	 * Make one more attempt to allocate some buffers.
+	 * Allocate buffers to reach half our goal, if possible.
+	 * Since the allocation doesn't block, there's no reason
+	 * to search the buffer lists again. Then return if there
+	 * are _any_ free buffers.
 	 */
-	if (grow_buffers(GFP_ATOMIC, size))
+	while (obtained < (needed >> 1) &&
+	       nr_free_pages > min_free_pages + 5 &&
+	       grow_buffers(GFP_BUFFER, size))
 		obtained += PAGE_SIZE;
 
+	if (free_list[BUFSIZE_INDEX(size)])
+		return;
+
+	/*
+	 * If there are dirty buffers, wait while bdflush writes
+	 * them out. The buffers become locked, but we can just
+	 * wait for one to unlock ...
+	 */
+	if (nr_buffers_type[BUF_DIRTY])
+		wakeup_bdflush(1);
+
 	/*
-	 * If we got any buffers, or another task freed some, 
-	 * return now to let this task proceed.
+	 * In order to prevent a buffer shortage from exhausting
+	 * the system's reserved pages, we force tasks to wait 
+	 * before using reserved pages for buffers.  This is easily
+	 * accomplished by waiting on an unused locked buffer.
 	 */
-	if (obtained || free_list[BUFSIZE_INDEX(size)]) {
+	if ((bh = lru_list[BUF_LOCKED]) != NULL) {
+		for (i = nr_buffers_type[BUF_LOCKED]; i--; bh = bh->b_next_free)
+		{
+			if (bh->b_size != size)
+				continue;
+			if (bh->b_count)
+				continue;
+			if (!buffer_locked(bh))
+				continue;
+			if (buffer_dirty(bh) || buffer_protected(bh))
+				continue;
+			/*
+			 * We've found an unused, locked, non-dirty buffer of
+			 * the correct size.  Claim it so no one else can, 
+			 * then wait for it to unlock.
+			 */
+			bh->b_count++;
+			wait_on_buffer(bh);
+			bh->b_count--;
+			/*
+			 * Loop back to harvest this (and maybe other) buffers.
+			 */
+			goto repeat;
+		}
+	}
+
+	/*
+	 * Convert a reserved page into buffers ... should happen only rarely.
+	 */
+	if (nr_free_pages > (min_free_pages >> 1) &&
+	    grow_buffers(GFP_ATOMIC, size)) {
 #ifdef BUFFER_DEBUG
-printk("refill_freelist: obtained %d of %d, free list=%d\n", 
-obtained, needed, free_list[BUFSIZE_INDEX(size)] != NULL);
+printk("refill_freelist: used reserve page\n");
 #endif
 		return;
 	}
 
 	/*
-	 * System is _very_ low on memory ... wake up bdflush and wait.
+	 * System is _very_ low on memory ... sleep and try later.
 	 */
 #ifdef BUFFER_DEBUG
-printk("refill_freelist: waking bdflush\n");
+printk("refill_freelist: task %s waiting for buffers\n", current->comm);
 #endif
-	wakeup_bdflush(1);
-	/*
-	 * While we slept, other tasks may have needed buffers and entered
-	 * refill_freelist.  This could be a big problem ... reset the 
-	 * needed amount to the absolute minimum.
-	 */
-	needed = size;
+	schedule();
 	goto repeat;
 }
 
diff -u --recursive --new-file v2.1.69/linux/fs/coda/Makefile linux/fs/coda/Makefile
--- v2.1.69/linux/fs/coda/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/fs/coda/Makefile	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux Coda-filesystem routines.
+#
+
+O_TARGET := coda.o
+O_OBJS   := psdev.o upcall.o cnode.o super.o dir.o coda_linux.o symlink.o pioctl.o file.o namecache.o\
+	    sysctl.o 
+M_OBJS   := $(O_TARGET)
+
+# If you want debugging output, please uncomment the following line
+
+# EXTRA_CFLAGS += -DDEBUG -DDEBUG_SMB_MALLOC=1
+
+include $(TOPDIR)/Rules.make
+
diff -u --recursive --new-file v2.1.69/linux/fs/coda/cnode.c linux/fs/coda/cnode.c
--- v2.1.69/linux/fs/coda/cnode.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/coda/cnode.c	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,196 @@
+/* cnode related routines for the coda kernel code
+   Peter Braam, Sep 1996.
+   */
+
+#include <linux/types.h>
+#include <linux/time.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cnode.h>
+
+extern int coda_debug;
+extern int coda_print_entry;
+extern int coda_fetch_inode(struct inode *inode, struct coda_vattr *attr);
+
+/* cnode.c */
+struct cnode *coda_cnode_alloc(void);
+void coda_cnode_free(struct cnode *);
+int  coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb);
+
+/* return pointer to new empty cnode */
+struct cnode *coda_cnode_alloc(void)
+{
+        struct cnode *result = NULL;
+
+        CODA_ALLOC(result, struct cnode *, sizeof(struct cnode));
+        if ( !result ) {
+                printk("coda_cnode_alloc: kmalloc returned NULL.\n");
+                return result;
+        }
+
+        memset(result, 0, (int) sizeof(struct cnode));
+        return result;
+}
+
+/* release cnode memory */
+void coda_cnode_free(struct cnode *cinode)
+{
+        CODA_FREE(cinode, sizeof(struct cnode));
+}
+              
+/* this is effectively coda_iget:
+   - get attributes (might be cached)
+   - get the inode for the fid using vfs iget
+   - link the two up if this is needed
+   - fill in the attributes
+*/
+int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb)
+{
+        struct cnode *cnp;
+        struct coda_vattr attr;
+        int error;
+	ino_t ino;
+	char str[50];
+        
+        ENTRY;
+
+        /* 
+         * We get inode numbers from Venus -- see venus source
+         */
+
+	error = venus_getattr(sb, fid, &attr);
+	if ( error ) {
+	    printk("coda_cnode_make: coda_getvattr returned %d for %s.\n", 
+		   error, coda_f2s(fid, str));
+	    *inode = NULL;
+	    return error;
+	} 
+
+	ino = attr.va_fileid;
+        *inode = iget(sb, ino);
+        if ( !*inode ) {
+                printk("coda_cnode_make: iget failed\n");
+                return -ENOMEM;
+        }
+
+	/* link the cnode and the vfs inode 
+	   if this inode is not linked yet
+	*/
+	if ( !(*inode)->u.generic_ip ) {
+        	cnp = coda_cnode_alloc();
+        	if ( !cnp ) {
+               		printk("coda_cnode_make: coda_cnode_alloc failed.\n");
+			clear_inode(*inode);
+                	return -ENOMEM;
+        	}
+        	cnp->c_fid = *fid;
+        	cnp->c_magic = CODA_CNODE_MAGIC;
+		cnp->c_flags = C_VATTR;
+        	cnp->c_vnode = *inode;
+        	(*inode)->u.generic_ip = (void *) cnp;
+		CDEBUG(D_CNODE, "LINKING: ino %ld, count %d  at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x, in->u.generic_ip 0x%x\n", (*inode)->i_ino, (*inode)->i_count, (int) (*inode), (int) cnp, (int)cnp->c_vnode, (int) (*inode)->u.generic_ip);
+	} else {
+	    cnp = (struct cnode *)(*inode)->u.generic_ip;
+	    CDEBUG(D_CNODE, "FOUND linked: ino %ld, count %d, at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x\n", (*inode)->i_ino, (*inode)->i_count, (int) (*inode), (int) cnp, (int)cnp->c_vnode);
+	}
+	CHECK_CNODE(cnp);
+
+	/* refresh the attributes */
+        error = coda_fetch_inode(*inode, &attr);
+        if ( error ) {
+                printk("coda_cnode_make: fetch_inode returned %d\n", error);
+		clear_inode(*inode);
+		coda_cnode_free(cnp);
+                return -error;
+        }
+		CDEBUG(D_CNODE, "Done linking: ino %ld,  at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x\n", (*inode)->i_ino, (int) (*inode), (int) cnp, (int)cnp->c_vnode);
+
+        EXIT;
+        return 0;
+}
+
+
+inline int coda_fideq(ViceFid *fid1, ViceFid *fid2)
+{
+	int eq;
+	eq =   ( (fid1->Vnode == fid2->Vnode) &&
+		 (fid1->Volume == fid2->Volume) &&
+		 (fid1->Unique == fid2->Unique) );
+	return eq;
+}
+
+ 
+/* compute the inode number from the FID
+   same routine as in vproc.cc (venus)
+   XXX look at the exceptional case of root fids etc
+*/
+static ino_t
+coda_fid2ino(ViceFid *fid)
+{
+	u_long ROOT_VNODE = 1;
+	u_long ROOT_UNIQUE = 1;
+	ViceFid nullfid = { 0, 0, 0};
+
+	if ( coda_fideq(fid, &nullfid) ) {
+		printk("coda_fid2ino: called with NULL Fid!\n");
+		return 0;
+	}
+
+	/* what do we return for the root fid */
+
+	/* Other volume root.  We need the relevant mount point's 
+	fid, but we don't know what that is! */
+	if (fid->Vnode == ROOT_VNODE && fid->Unique == ROOT_UNIQUE) {
+		return(0);
+	}
+
+	/* Non volume root. */
+	return(fid->Unique + (fid->Vnode << 10) + (fid->Volume << 20));
+}     
+
+/* convert a fid to an inode. Avoids having a hash table
+   such as present in the Mach minicache */
+struct inode *
+coda_fid2inode(ViceFid *fid, struct super_block *sb) {
+	ino_t nr;
+	struct inode *inode;
+	struct cnode *cnp;
+	
+	nr = coda_fid2ino(fid);
+	inode = iget(sb, nr);
+
+	/* check if this inode is linked to a cnode */
+	cnp = (struct cnode *) inode->u.generic_ip;
+	if ( cnp == NULL ) {
+		iput(inode);
+		return NULL;
+	}
+	/* make sure fid is the one we want */
+	if ( !coda_fideq(fid, &(cnp->c_fid)) ) {
+		printk("coda_fid2inode: bad cnode! Tell Peter.\n");
+		iput(inode);
+		return NULL;
+	}
+
+	iput(inode);	
+	return inode;
+}
+
+/* the CONTROL inode is made without asking attributes from Venus */
+int coda_cnode_makectl(struct inode **inode, struct super_block *sb)
+{
+    int error = 0;
+
+    *inode = iget(sb, CTL_INO);
+    if ( *inode ) {
+	(*inode)->i_op = &coda_ioctl_inode_operations;
+	(*inode)->i_mode = 00444;
+	error = 0;
+    } else { 
+	error = -ENOMEM;
+    }
+    
+    return error;
+}
diff -u --recursive --new-file v2.1.69/linux/fs/coda/coda_linux.c linux/fs/coda/coda_linux.c
--- v2.1.69/linux/fs/coda/coda_linux.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/coda/coda_linux.c	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,70 @@
+/*
+ * Inode operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ * 
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_namecache.h>
+
+/* initialize the debugging variables */
+int coda_debug =815;
+int coda_print_entry = 1; 
+int coda_access_cache = 1;
+
+/* caller must allocate 36 byte string ! */
+char * coda_f2s(ViceFid *f, char *s)
+{
+	if ( f ) {
+		sprintf(s, "(%-#10lx,%-#10lx,%-#10lx)", 
+			 f->Volume, f->Vnode, f->Unique);
+	}
+	return s;
+}
+
+int coda_isroot(struct inode *i)
+{
+    if ( i->i_sb->s_root->d_inode == i ) {
+	return 1;
+    } else {
+	return 0;
+    }
+}
+	
+void coda_load_creds(struct CodaCred *cred)
+{
+        int i;
+
+        cred->cr_uid = (vuid_t) current->uid;
+        cred->cr_euid = (vuid_t) current->euid;
+        cred->cr_suid = (vuid_t) current->suid;
+        cred->cr_fsuid = (vuid_t) current->fsuid;
+
+        cred->cr_gid = (vgid_t) current->gid;
+        cred->cr_egid = (vgid_t) current->egid;
+        cred->cr_sgid = (vgid_t) current->sgid;
+        cred->cr_fsgid = (vgid_t) current->fsgid;
+
+        for ( i = 0 ; i < NGROUPS ; ++i ) {
+                cred->cr_groups[i] = (vgid_t) current->groups[i];
+        }
+
+}
+
diff -u --recursive --new-file v2.1.69/linux/fs/coda/dir.c linux/fs/coda/dir.c
--- v2.1.69/linux/fs/coda/dir.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/coda/dir.c	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,769 @@
+/*
+ * Direcotry operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ * 
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_namecache.h>
+
+/* dir inode-ops */
+static int coda_create(struct inode *dir, struct dentry *new, int mode);
+static int coda_lookup(struct inode *dir, struct dentry *target);
+static int coda_link(struct inode *old_inode, struct inode *dir_inode, 
+		     struct dentry *entry);
+static int coda_unlink(struct inode *dir_inode, struct dentry *entry);
+static int coda_symlink(struct inode *dir_inode, struct dentry *entry,
+			const char *symname);
+static int coda_mkdir(struct inode *dir_inode, struct dentry *entry, int mode);
+static int coda_rmdir(struct inode *dir_inode, struct dentry *entry);
+static int coda_rename(struct inode *old_inode, struct dentry *old_dentry, 
+                       struct inode *new_inode, struct dentry *new_dentry);
+
+/* dir file-ops */
+static int coda_readdir(struct file *file, void *dirent, filldir_t filldir);
+
+/* support routines */
+static int coda_venus_readdir(struct file *filp, void *dirent, 
+			      filldir_t filldir);
+
+
+
+struct inode_operations coda_dir_inode_operations =
+{
+	&coda_dir_operations,
+	coda_create,	        /* create */
+	coda_lookup,	        /* lookup */
+	coda_link,	        /* link */
+	coda_unlink,            /* unlink */
+	coda_symlink,	        /* symlink */
+	coda_mkdir,	        /* mkdir */
+	coda_rmdir,   	        /* rmdir */
+	NULL,		        /* mknod */
+	coda_rename,	        /* rename */
+	NULL,	                /* readlink */
+	NULL,	                /* follow_link */
+	NULL,	                /* readpage */
+	NULL,		        /* writepage */
+	NULL,		        /* bmap */
+	NULL,	                /* truncate */
+	coda_permission,        /* permission */
+	NULL,                   /* smap */
+	NULL,                   /* update page */
+        NULL                    /* revalidate */
+};
+
+struct file_operations coda_dir_operations = {
+        NULL,                   /* lseek */
+        NULL,                   /* read -- bad  */
+        NULL,                   /* write */
+        coda_readdir,           /* readdir */
+        NULL,                   /* select */
+        NULL,                   /* ioctl */
+        NULL,                   /* mmap */
+        coda_open,              /* open */
+        coda_release,           /* release */
+        NULL,                   /* fsync */
+};
+
+
+/* inode operations for directories */
+/* acces routines: lookup, readlink, permission */
+static int coda_lookup(struct inode *dir, struct dentry *entry)
+{
+        struct cnode *dircnp, *savedcnp;
+	struct inode *res_inode = NULL;
+	struct ViceFid resfid;
+	int type;
+	int error = 0;
+	const char *name = entry->d_name.name;
+	size_t length = entry->d_name.len;
+	char str[50];
+	
+        ENTRY;
+        CDEBUG(D_INODE, "name %s, len %d in ino %ld\n", 
+	       name, length, dir->i_ino);
+
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		printk("coda_lookup: inode is NULL or not a directory\n");
+		return -ENOENT;
+	}
+
+	dircnp = ITOC(dir);
+	CHECK_CNODE(dircnp);
+
+	if ( length > CFS_MAXNAMLEN ) {
+	        printk("name too long: lookup, %s (%s)\n", 
+		       coda_f2s(&dircnp->c_fid, str), name);
+		return -ENAMETOOLONG;
+	}
+	
+	CDEBUG(D_INODE, "lookup: %s in %s\n", name, 
+	       coda_f2s(&dircnp->c_fid, str));
+
+        /* control object, create inode on the fly */
+        if ( coda_isroot(dir) && (CFS_CONTROLLEN == length) && 
+	     (strncmp(name, CFS_CONTROL, CFS_CONTROLLEN) == 0) ) {
+	        error = coda_cnode_makectl(&res_inode, dir->i_sb);
+		CDEBUG(D_SPECIAL, 
+		       "Lookup on CTL object; iput of ino %ld, count %d\n", 
+		       dir->i_ino, dir->i_count);
+                goto exit;
+        }
+
+        /* do we have it already in name cache */
+	if ( (savedcnp = cfsnc_lookup(dircnp, name, length)) != NULL ) {
+		CHECK_CNODE(savedcnp);
+		res_inode = CTOI(savedcnp);
+		iget(res_inode->i_sb, res_inode->i_ino);
+		CDEBUG(D_INODE, "cache hit for ino: %ld, count: %d!\n",
+		       res_inode->i_ino, res_inode->i_count);
+		goto exit;
+	}
+	CDEBUG(D_INODE, "name not found in cache!\n");
+
+        /* name not cached */
+        error = venus_lookup(dir->i_sb, &(dircnp->c_fid), 
+				(const char *)name, length, &type, &resfid);
+
+	res_inode = NULL;
+	if (!error) {
+	    	error = coda_cnode_make(&res_inode, &resfid, dir->i_sb);
+		if (error)
+			return -EACCES;
+		/* put the thing in the name cache */
+		savedcnp = ITOC(res_inode);
+		CHECK_CNODE(savedcnp);
+		CDEBUG(D_INODE, "ABOUT to enter into cache.\n");
+		cfsnc_enter(dircnp, name, length, savedcnp);
+		CDEBUG(D_INODE, "entered in cache\n");
+	} else if (error != -ENOENT) {
+	        CDEBUG(D_INODE, "error for %s(%s)%d\n",
+		       coda_f2s(&dircnp->c_fid, str), name, error);
+		return error;
+	}
+	CDEBUG(D_INODE, "lookup: %s is (%s) type %d result %d\n",
+	       name, coda_f2s(&resfid, str), type, error);
+
+	/* at last we have our inode number from Venus, 
+	   now allocate storage for
+	   the cnode and do iget, and fill in the attributes */
+
+
+exit:
+	entry->d_time = 0;
+	entry->d_op = NULL;
+	d_add(entry, res_inode);
+        EXIT;
+        return 0;
+}
+
+
+
+int coda_permission(struct inode *inode, int mask)
+{
+        struct cnode *cp;
+        int error;
+        int mode = inode->i_mode;
+	char str[50];
+ 
+        ENTRY;
+
+        if ( mask == 0 ) {
+                EXIT;
+                return 0;
+        }
+
+        /* we should be able to trust what is in the mode
+           although Venus should be told to return the 
+           correct modes to the kernel */
+        if ( coda_access_cache == 1 ) { 
+            if (current->fsuid == inode->i_uid)
+                mode >>= 6;
+        	else if (in_group_p(inode->i_gid))
+                mode >>= 3;
+        	if (((mode & mask & 0007) == mask) )
+                return 0;
+	}
+
+        cp = ITOC(inode);
+        CHECK_CNODE(cp);
+
+        CDEBUG(D_INODE, "mask is %o\n", mask);
+        error = venus_access(inode->i_sb, &(cp->c_fid), mask);
+    
+        CDEBUG(D_INODE, "fid: %s, ino: %ld (mask: %o) error: %d\n", 
+	       coda_f2s(&(cp->c_fid), str), inode->i_ino, mask, error);
+
+        return error; 
+}
+
+
+
+/* creation routines: create, mkdir, link, symlink */
+
+static int coda_create(struct inode *dir, struct dentry *de, int mode)
+{
+        int error=0;
+        struct cnode *dircnp;
+	const char *name=de->d_name.name;
+	int length=de->d_name.len;
+	struct inode *result = NULL;
+	struct ViceFid newfid;
+	struct coda_vattr attrs;
+
+	CDEBUG(D_INODE, "name: %s, length %d, mode %o\n",name, length, mode);
+
+        if (!dir || !S_ISDIR(dir->i_mode)) {
+                printk("coda_create: inode is null or not a directory\n");
+                return -ENOENT;
+        }
+	dircnp = ITOC(dir);
+        CHECK_CNODE(dircnp);
+
+        if ( length > CFS_MAXNAMLEN ) {
+		char str[50];
+		printk("name too long: create, %s(%s)\n", 
+		       coda_f2s(&dircnp->c_fid, str), name);
+		return -ENAMETOOLONG;
+        }
+
+	error = venus_create(dir->i_sb, &(dircnp->c_fid), name, length, 
+				0, mode, &newfid, &attrs);
+
+        if ( error ) {
+		char str[50];
+		CDEBUG(D_INODE, "create: %s, result %d\n",
+		       coda_f2s(&newfid, str), error); 
+		return error;
+	}
+
+	error = coda_cnode_make(&result, &newfid, dir->i_sb);
+	if ( error ) {
+		result = NULL;
+		return error;
+	}
+
+	/* invalidate the directory cnode's attributes */
+	dircnp->c_flags &= ~C_VATTR;
+	cfsnc_zapfid(&(dircnp->c_fid));
+
+	d_instantiate(de, result);
+        return 0;
+}			     
+
+static int coda_mkdir(struct inode *dir, struct dentry *de, int mode)
+{
+        struct cnode *dircnp;
+	struct inode *inode;
+	struct coda_vattr attr;
+	const char *name = de->d_name.name;
+	int len = de->d_name.len;
+	int error;
+	struct ViceFid newfid;
+	char fidstr[50];
+
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		printk("coda_mkdir: inode is NULL or not a directory\n");
+		return -ENOENT;
+	}
+        dircnp = ITOC(dir);
+        CHECK_CNODE(dircnp);
+
+        if ( len > CFS_MAXNAMLEN )
+                return -ENAMETOOLONG;
+
+	CDEBUG(D_INODE, "mkdir %s (len %d) in %s, mode %o.\n", 
+	       name, len, coda_f2s(&(dircnp->c_fid), fidstr), mode);
+
+	attr.va_mode = mode;
+	error = venus_mkdir(dir->i_sb, &(dircnp->c_fid), 
+			       name, len, &newfid, &attr);
+        
+        if ( error ) {
+	        CDEBUG(D_INODE, "mkdir error: %s result %d\n", 
+		       coda_f2s(&newfid, fidstr), error); 
+                return error;
+        }
+         
+	CDEBUG(D_INODE, "mkdir: new dir has fid %s.\n", 
+	       coda_f2s(&newfid, fidstr)); 
+
+	error = coda_cnode_make(&inode, &newfid, dir->i_sb);
+	if ( error ) 
+	       return error;
+	
+	/* invalidate the directory cnode's attributes */
+	dircnp->c_flags &= ~C_VATTR;
+	cfsnc_zapfid(&(dircnp->c_fid));
+
+	dir->i_nlink++;
+	d_instantiate(de, inode);
+        return 0;
+}
+
+static int coda_link(struct inode *inode, struct inode *dir_inode, 
+	  struct dentry *de)
+{
+        const char * name = de->d_name.name;
+	int len = de->d_name.len;
+        struct cnode *dir_cnp, *cnp;
+	char str[50];
+	int error;
+
+        ENTRY;
+
+        dir_cnp = ITOC(dir_inode);
+        CHECK_CNODE(dir_cnp);
+        cnp = ITOC(inode);
+        CHECK_CNODE(cnp);
+
+	CDEBUG(D_INODE, "old: fid: %s\n", coda_f2s(&(cnp->c_fid), str));
+	CDEBUG(D_INODE, "directory: %s\n", coda_f2s(&(dir_cnp->c_fid), str));
+
+        if ( len > CFS_MAXNAMLEN ) {
+                printk("coda_link: name too long. \n");
+                return -ENAMETOOLONG;
+        }
+
+        /* Check for link to/from control object. */
+
+        error = venus_link(dir_inode->i_sb,&(cnp->c_fid), &(dir_cnp->c_fid), 
+			   (const char *)name, len);
+
+	if (  ! error ) { 
+	      dir_cnp->c_flags &= ~C_VATTR;
+	      cfsnc_zapfid(&(dir_cnp->c_fid));
+	      cfsnc_zapfid(&(cnp->c_fid));
+
+	      inode->i_nlink++;
+	      d_instantiate(de, inode);
+	}
+        CDEBUG(D_INODE, "link result %d\n",error);
+	EXIT;
+        return(error);
+}
+
+
+static int 
+coda_symlink(struct inode *dir_inode, struct dentry *de,
+             const char *symname)
+{
+        const char *name = de->d_name.name;
+	int len = de->d_name.len;
+        struct cnode *dir_cnp = ITOC(dir_inode);
+	int symlen;
+        int error=0;
+        
+        ENTRY;
+
+	error = -ENAMETOOLONG;
+	if ( len > CFS_MAXNAMLEN ) { 
+	        return error;
+	}
+
+	symlen = strlen(symname);
+	if ( symlen > CFS_MAXNAMLEN ) { 
+	        return error;
+	}
+        CDEBUG(D_INODE, "symname: %s, length: %d\n", symname, symlen);
+
+	error = venus_symlink(dir_inode->i_sb, &(dir_cnp->c_fid), name, len, 
+			      symname, symlen);
+
+	if ( !error ) {
+	  d_drop(de);
+	}
+
+        CDEBUG(D_INODE, "in symlink result %d\n",error);
+        EXIT;
+        return error;
+}
+
+/* destruction routines: unlink, rmdir */
+
+int coda_unlink(struct inode *dir, struct dentry *de)
+{
+        struct cnode *dircnp;
+        int error;
+	const char *name = de->d_name.name;
+	int len = de->d_name.len;
+	char fidstr[50];
+
+	ENTRY;
+
+        dircnp = ITOC(dir);
+        CHECK_CNODE(dircnp);
+
+        CDEBUG(D_INODE, " %s in %s, ino %ld\n", name , 
+	       coda_f2s(&(dircnp->c_fid), fidstr), dir->i_ino);
+
+        /* this file should no longer be in the namecache! */
+        cfsnc_zapfile(dircnp, (const char *)name, len);
+
+        error = venus_remove(dir->i_sb, &(dircnp->c_fid), name, len);
+
+        if ( error ) {
+                CDEBUG(D_INODE, "upc returned error %d\n", error);
+                return error;
+        }
+
+        /* cache management */
+	dircnp->c_flags &= ~C_VATTR;
+	cfsnc_zapfid(&(dircnp->c_fid));
+	
+	de->d_inode->i_nlink--;
+	d_delete(de);
+
+        return 0;
+}
+
+int coda_rmdir(struct inode *dir, struct dentry *de)
+{
+        struct cnode *dircnp;
+	const char *name = de->d_name.name;
+	int len = de->d_name.len;
+        int error;
+
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		printk("coda_rmdir: inode is NULL or not a directory\n");
+		return -ENOENT;
+	}
+        dircnp = ITOC(dir);
+        CHECK_CNODE(dircnp);
+
+	if (len > CFS_MAXNAMLEN)
+		return -ENAMETOOLONG;
+
+        /* this directory name should no longer be in the namecache */
+        cfsnc_zapfile(dircnp, (const char *)name, len);
+
+	error = venus_rmdir(dir->i_sb, &(dircnp->c_fid), name, len);
+
+        if ( error ) {
+                CDEBUG(D_INODE, "upc returned error %d\n", error);
+                return error;
+        }
+
+	dircnp->c_flags &= ~C_VATTR;
+	cfsnc_zapfid(&(dircnp->c_fid));
+
+	dir->i_nlink--;
+	d_delete(de);
+
+        return 0;
+}
+
+/* rename */
+static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, 
+		       struct inode *new_dir, struct dentry *new_dentry)
+{
+        const char *old_name = old_dentry->d_name.name;
+        const char *new_name = new_dentry->d_name.name;
+	int old_length = old_dentry->d_name.len;
+	int new_length = new_dentry->d_name.len;
+	struct inode *old_inode = old_dentry->d_inode;
+	struct inode *new_inode = new_dentry->d_inode;
+        struct cnode *new_cnp, *old_cnp;
+        int error, rehash = 0, update = 1;
+ENTRY;
+        old_cnp = ITOC(old_dir);
+        CHECK_CNODE(old_cnp);
+        new_cnp = ITOC(new_dir);
+        CHECK_CNODE(new_cnp);
+
+        CDEBUG(D_INODE, "old: %s, (%d length, %d strlen), new: %s (%d length, %d strlen).\n", old_name, old_length, strlen(old_name), new_name, new_length, strlen(new_name));
+
+        if ( (old_length > CFS_MAXNAMLEN) || new_length > CFS_MAXNAMLEN ) {
+                return -ENAMETOOLONG;
+        }
+
+        /* the old file should go from the namecache */
+        cfsnc_zapfile(old_cnp, (const char *)old_name, old_length);
+        cfsnc_zapfile(new_cnp, (const char *)new_name, new_length);
+
+        /* cross directory moves */
+	if (new_dir != old_dir  &&
+	    S_ISDIR(old_inode->i_mode) && 
+	    old_dentry->d_count > 1)
+	        shrink_dcache_parent(old_dentry);
+		
+	/* We must prevent any new references to the
+	 * target while the rename is in progress, so
+	 * we unhash the dentry.  */
+	if (!list_empty(&new_dentry->d_hash)) {
+	        d_drop(new_dentry);
+		rehash = 1;
+	}
+
+        error = venus_rename(old_dir->i_sb, &(old_cnp->c_fid), 
+			     &(new_cnp->c_fid), old_length, new_length, 
+			     (const char *) old_name, (const char *)new_name);
+
+	if (rehash) {
+		d_add(new_dentry, new_inode);
+	}
+
+        if ( error ) {
+                CDEBUG(D_INODE, "returned error %d\n", error);
+                return error;
+        }
+	/* Update the dcache if needed */
+	if (update)
+	        d_move(old_dentry, new_dentry);
+         
+        CDEBUG(D_INODE, "result %d\n", error); 
+
+	EXIT;
+	return 0;
+}
+
+
+
+/* file operations for directories */
+int coda_readdir(struct file *file, void *dirent,  filldir_t filldir)
+{
+        int result = 0;
+        struct cnode *cnp;
+        struct file open_file;
+	struct dentry open_dentry;
+	struct inode *inode=file->f_dentry->d_inode;
+
+        ENTRY;
+
+        if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) {
+                printk("coda_readdir: inode is NULL or not a directory\n");
+                return -EBADF;
+        }
+
+        cnp = ITOC(inode);
+        CHECK_CNODE(cnp);
+        
+        if ( !cnp->c_ovp ) {
+                CDEBUG(D_FILE, "open inode pointer = NULL.\n");
+                return -ENODEV;
+        }
+
+	coda_prepare_openfile(inode, file, cnp->c_ovp, &open_file,
+			      &open_dentry);
+        if ( S_ISREG(cnp->c_ovp->i_mode) ) {
+                /* Venus: we must read Venus dirents from the file */
+                result = coda_venus_readdir(&open_file, dirent, filldir);
+        } else {
+                /* potemkin case: we are handed a directory inode */
+                result = open_file.f_op->readdir(&open_file, dirent, filldir);
+        }
+	coda_restore_codafile(inode, file, cnp->c_ovp, &open_file);
+	return result;
+        EXIT;
+}
+
+/* ask venus to cache the file and return the inode of the container file,
+   put this inode pointer in the cnode for future read/writes */
+int coda_open(struct inode *i, struct file *f)
+{
+        ino_t ino;
+	dev_t dev;
+        struct cnode *cnp;
+        int error = 0;
+        struct inode *cont_inode = NULL;
+        unsigned short flags = f->f_flags;
+
+        ENTRY;
+        
+        CDEBUG(D_SPECIAL, "OPEN inode number: %ld, flags %o.\n", 
+	       f->f_dentry->d_inode->i_ino, flags);
+
+        if ( flags & O_CREAT ) {
+                flags &= ~O_EXCL; /* taken care of by coda_create ?? */
+        }
+        cnp = ITOC(i);
+        CHECK_CNODE(cnp);
+
+	error = venus_open(i->i_sb, &(cnp->c_fid), flags, &ino, &dev); 
+	if (error) {
+	        CDEBUG(D_FILE, "venus: dev %d, inode %ld, out->result %d\n",
+		       dev, ino, error);
+		return error;
+	}
+
+        /* coda_upcall returns ino number of cached object, get inode */
+        CDEBUG(D_FILE, "cache file dev %d, ino %ld\n", dev, ino);
+
+        if ( ! cnp->c_ovp ) {
+                error = coda_inode_grab(dev, ino, &cont_inode);
+                
+                if ( error ){
+                        printk("coda_open: coda_inode_grab error %d.", error);
+                        if (cont_inode) iput(cont_inode);
+			return error;
+                }
+                CDEBUG(D_FILE, "GRAB: coda_inode_grab: ino %ld, ops at %x\n", 
+		       cont_inode->i_ino, (int)cont_inode->i_op);
+                cnp->c_ovp = cont_inode; 
+		cnp->c_odentry.d_inode = cont_inode;
+        } 
+        cnp->c_ocount++;
+
+        /* if opened for writing flush cache entry.  */
+        if ( flags & (O_WRONLY | O_RDWR) ) {
+	        cfsnc_zapfid(&(cnp->c_fid));
+	} 
+
+        CDEBUG(D_FILE, "result %d, coda i->i_count is %d for ino %ld\n", 
+	       error, i->i_count, i->i_ino);
+        CDEBUG(D_FILE, "cache ino: %ld, count %d, ops %x\n", 
+	       cnp->c_ovp->i_ino, cnp->c_ovp->i_count,
+	       (int)(cnp->c_ovp->i_op));
+        EXIT;
+        return 0;
+}
+
+int coda_release(struct inode *i, struct file *f)
+{
+        struct cnode *cnp;
+        int error;
+        unsigned short flags = f->f_flags;
+
+        ENTRY;
+
+        cnp =ITOC(i);
+        CHECK_CNODE(cnp);
+        CDEBUG(D_FILE,  
+	       "RELEASE coda (ino %ld, ct %d) cache (ino %ld, ct %d)\n",
+               i->i_ino, i->i_count, (cnp->c_ovp ? cnp->c_ovp->i_ino : 0),
+               (cnp->c_ovp ? cnp->c_ovp->i_count : -99));
+
+
+        /* even when c_ocount=0 we cannot put c_ovp to
+         * NULL since the file may be mmapped.
+	 * See code in inode.c (coda_put_inode) for
+	 * further handling of close.
+	 */
+
+        --cnp->c_ocount;
+
+        if (flags & (O_WRONLY | O_RDWR)) {
+                --cnp->c_owrite;
+        }
+
+	error = venus_release(i->i_sb, &(cnp->c_fid), flags);
+
+        CDEBUG(D_FILE, "coda_release: result: %d\n", error);
+        return error;
+}
+
+/* support routines */
+/* 
+ * this structure is manipulated by filldir in vfs layer.
+ * the count holds the remaining amount of space in the getdents buffer,
+ * beyond the current_dir pointer.
+ */
+
+struct getdents_callback {
+	struct linux_dirent * current_dir;
+	struct linux_dirent * previous;
+	int count;
+	int error;
+};
+
+static int coda_venus_readdir(struct file *filp, void *getdent, 
+			      filldir_t filldir)
+{
+        int result = 0,  offset, count, pos, error = 0;
+	int errfill;
+        caddr_t buff = NULL;
+        struct venus_dirent *vdirent;
+        struct getdents_callback *dents_callback;
+        int string_offset;
+        char debug[255];
+
+        ENTRY;        
+
+        /* we also need the ofset of the string in the dirent struct */
+        string_offset = sizeof ( char )* 2  + sizeof(unsigned int) + 
+                        sizeof(unsigned short);
+
+        dents_callback = (struct getdents_callback *) getdent;
+
+        count =  dents_callback->count;
+        CODA_ALLOC(buff, void *, count);
+        if ( ! buff ) { 
+                printk("coda_venus_readdir: out of memory.\n");
+                return -ENOMEM;
+        }
+
+        /* we use this routine to read the file into our buffer */
+        result = read_exec(filp->f_dentry, filp->f_pos, buff, count, 1);
+        if ( result < 0) {
+                printk("coda_venus_readdir: cannot read directory %d.\n",
+		       result);
+                error = result;
+                goto exit;
+        }
+        if ( result == 0) {
+                error = result;
+                goto exit;
+        }
+
+        /* Parse and write into user space. Filldir tells us when done! */
+        offset = filp->f_pos;
+        pos = 0;
+        CDEBUG(D_FILE, "offset %d, count %d.\n", offset, count);
+
+        while ( pos + string_offset < result ) {
+                vdirent = (struct venus_dirent *) (buff + pos);
+
+                /* test if the name is fully in the buffer */
+                if ( pos + string_offset + (int) vdirent->d_namlen >= result ){
+                        break;
+                }
+                
+                /* now we are certain that we can read the entry from buff */
+
+                /* for debugging, get the string out */
+                memcpy(debug, vdirent->d_name, vdirent->d_namlen);
+                *(debug + vdirent->d_namlen) = '\0';
+
+                /* if we don't have a null entry, copy it */
+                if ( vdirent->d_fileno ) {
+                        int namlen  = vdirent->d_namlen;
+                        off_t offs  = filp->f_pos; 
+                        ino_t ino   = vdirent->d_fileno;
+                        char *name  = vdirent->d_name;
+                        /* adjust count */
+                        count = dents_callback->count;
+
+			errfill = filldir(dents_callback,  name, namlen, 
+					  offs, ino); 
+CDEBUG(D_FILE, "ino %ld, namlen %d, reclen %d, type %d, pos %d, string_offs %d, name %s, offset %d, count %d.\n", vdirent->d_fileno, vdirent->d_namlen, vdirent->d_reclen, vdirent->d_type, pos,  string_offset, debug, (u_int) offs, dents_callback->count);
+
+		      /* errfill means no space for filling in this round */
+                      if ( errfill < 0 ) break;
+                }
+                /* next one */
+                filp->f_pos += (unsigned int) vdirent->d_reclen;
+                pos += (unsigned int) vdirent->d_reclen;
+        } 
+
+exit:
+        CODA_FREE(buff, count);
+        return error;
+}
diff -u --recursive --new-file v2.1.69/linux/fs/coda/file.c linux/fs/coda/file.c
--- v2.1.69/linux/fs/coda/file.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/coda/file.c	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,251 @@
+/*
+ * File operations for Coda.
+ * Original version: (C) 1996 Peter Braam 
+ * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users of this code to contribute improvements
+ * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+
+#include <linux/coda_namecache.h>
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_psdev.h>
+
+/* file operations */
+static int coda_readpage(struct inode * inode, struct page * page);
+static ssize_t coda_file_read(struct file *f, char *buf, size_t count, loff_t *off);
+static ssize_t coda_file_write(struct file *f, const char *buf, size_t count, loff_t *off);
+static int coda_file_mmap(struct file * file, struct vm_area_struct * vma);
+
+/* exported from this file */
+struct inode_operations coda_file_inode_operations = {
+	&coda_file_operations,	/* default file operations */
+	NULL,			/* create */
+	NULL,		        /* lookup */
+	NULL,			/* link */
+	NULL,		        /* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,		/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	coda_readpage,    	/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+        coda_permission,        /* permission */
+	NULL,                   /* smap */
+	NULL,                   /* update page */
+        NULL                    /* revalidate */
+};
+
+struct file_operations coda_file_operations = {
+	NULL,		        /* lseek - default should work for coda */
+	coda_file_read,         /* read */
+	coda_file_write,        /* write */
+	NULL,          		/* readdir */
+	NULL,			/* select - default */
+	NULL,		        /* ioctl */
+	coda_file_mmap,         /* mmap */
+	coda_open,              /* open */
+	coda_release,           /* release */
+	NULL,		        /* fsync */
+};
+
+/*  File file operations */
+static int coda_readpage(struct inode * inode, struct page * page)
+{
+        struct inode *open_inode;
+        struct cnode *cnp;
+
+        ENTRY;
+        
+        cnp = ITOC(inode);
+        CHECK_CNODE(cnp);
+
+        if ( ! cnp->c_ovp ) {
+            printk("coda_readpage: no open inode for ino %ld\n", inode->i_ino);
+                return -ENXIO;
+        }
+
+        open_inode = cnp->c_ovp;
+
+        CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", inode->i_ino, open_inode->i_ino, page->offset);
+
+        generic_readpage(open_inode, page);
+        EXIT;
+        return 0;
+}
+
+static int coda_file_mmap(struct file * file, struct vm_area_struct * vma)
+{
+        struct cnode *cnp;
+	cnp = ITOC(file->f_dentry->d_inode);
+	cnp->c_mmcount++;
+  
+	return generic_file_mmap(file, vma);
+}
+
+static ssize_t coda_file_read(struct file *coda_file, char *buff, 
+			   size_t count, loff_t *ppos)
+{
+        struct cnode *cnp;
+	struct inode *coda_inode = coda_file->f_dentry->d_inode;
+        struct inode *cont_inode = NULL;
+        struct file  cont_file;
+	struct dentry cont_dentry;
+        int result = 0;
+
+        ENTRY;
+
+        cnp = ITOC(coda_inode);
+        CHECK_CNODE(cnp);
+	
+        cont_inode = cnp->c_ovp;
+        if ( cont_inode == NULL ) {
+                printk("coda_file_read: cached inode is 0!\n");
+                return -1;
+        }
+
+        coda_prepare_openfile(coda_inode, coda_file, cont_inode, 
+			      &cont_file, &cont_dentry);
+
+        if (!cont_file.f_op || ! cont_file.f_op->read) { 
+                printk( "container file has no read in file operations.\n");
+                return -1;
+        }
+
+        result = cont_file.f_op->read(&cont_file , buff, count, 
+				      &(cont_file.f_pos));
+
+        CDEBUG(D_FILE, "ops at %x result %d, count %d, position: %d\n", 
+	       (int)cont_file.f_op, result, count, (int)cont_file.f_pos);
+
+        coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file);
+        return result;
+}
+
+
+static ssize_t coda_file_write(struct file *coda_file, const char *buff, 
+			    size_t count, loff_t *ppos)
+{
+        struct cnode *cnp;
+	struct inode *coda_inode = coda_file->f_dentry->d_inode;
+        struct inode *cont_inode = NULL;
+        struct file  cont_file;
+	struct dentry cont_dentry;
+        int result = 0;
+
+        ENTRY;
+
+        cnp = ITOC(coda_inode);
+        CHECK_CNODE(cnp);
+
+        cont_inode = cnp->c_ovp;
+        if ( cont_inode == NULL ) {
+                printk("coda_file_write: cached inode is 0!\n");
+                return -1; 
+        }
+
+        coda_prepare_openfile(coda_inode, coda_file, cont_inode, 
+			      &cont_file, &cont_dentry);
+
+        if (!cont_file.f_op || !cont_file.f_op->write) {
+                printk("coda_file_write: container file has no file ops.\n");
+                return -1;
+        }
+
+        cnp->c_flags &= ~C_VATTR;
+
+	down(&cont_inode->i_sem);
+        result = cont_file.f_op->write(&cont_file , buff, count, 
+				       &(cont_file.f_pos));
+	up(&cont_inode->i_sem);
+        coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file);
+
+        return result;
+}
+
+
+
+/* 
+ * support routines
+ */
+
+/* instantiate the container file and dentry object to do io */                
+void coda_prepare_openfile(struct inode *i, struct file *coda_file, 
+			   struct inode *cont_inode, struct file *cont_file,
+			   struct dentry *cont_dentry)
+{
+        cont_file->f_pos = coda_file->f_pos;
+        cont_file->f_mode = coda_file->f_mode;
+        cont_file->f_flags = coda_file->f_flags;
+        cont_file->f_count  = coda_file->f_count;
+        cont_file->f_owner  = coda_file->f_owner;
+	cont_file->f_op = cont_inode->i_op->default_file_ops;
+	cont_file->f_dentry = cont_dentry;
+        cont_file->f_dentry->d_inode = cont_inode;
+        return ;
+}
+
+/* update the Coda file & inode after I/O */
+void coda_restore_codafile(struct inode *coda_inode, struct file *coda_file, 
+			   struct inode *open_inode, struct file *open_file)
+{
+        coda_file->f_pos = open_file->f_pos;
+	/* XXX what about setting the mtime here too? */
+	coda_inode->i_mtime = open_inode->i_mtime;
+	coda_inode->i_size = open_inode->i_size;
+        return;
+}
+
+/* grab the ext2 inode of the container file */
+int coda_inode_grab(dev_t dev, ino_t ino, struct inode **ind)
+{
+        struct super_block *sbptr;
+
+        sbptr = coda_find_super(dev);
+
+        if ( !sbptr ) {
+                printk("coda_inode_grab: coda_find_super returns NULL.\n");
+                return -ENXIO;
+        }
+                
+        *ind = NULL;
+        *ind = iget(sbptr, ino);
+
+        if ( *ind == NULL ) {
+                printk("coda_inode_grab: iget(dev: %d, ino: %ld) 
+                       returns NULL.\n", dev, ino);
+                return -ENOENT;
+        }
+	CDEBUG(D_FILE, "ino: %ld, ops at %x\n", ino, (int)(*ind)->i_op);
+        return 0;
+}
+
+struct super_block *coda_find_super(kdev_t device)
+{
+        struct super_block *super;
+
+        for (super = super_blocks + 0; super < super_blocks + NR_SUPER ; 
+	     super++) {
+                if (super->s_dev == device) 
+                        return super;
+        }
+        return NULL;
+}
diff -u --recursive --new-file v2.1.69/linux/fs/coda/inode.c linux/fs/coda/inode.c
--- v2.1.69/linux/fs/coda/inode.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/coda/inode.c	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,34 @@
+/*
+ * Inode operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ * 
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_namecache.h>
+
+/* prototypes */
+static int coda_readpage(struct inode *inode, struct page *page);
+static int coda_cnode_makectl(struct inode **inode, struct super_block *sb);
+
+
+
+
+
diff -u --recursive --new-file v2.1.69/linux/fs/coda/namecache.c linux/fs/coda/namecache.c
--- v2.1.69/linux/fs/coda/namecache.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/coda/namecache.c	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,838 @@
+/* 
+ * Mach Operating System
+ * Copyright (c) 1990 Carnegie-Mellon University
+ * Copyright (c) 1989 Carnegie-Mellon University
+ * All rights reserved.  The CMU software License Agreement specifies
+ * the terms and conditions for use and redistribution.
+ */
+
+/*
+ * This code was written for the Coda file system at Carnegie Mellon University.
+ * Contributers include David Steere, James Kistler, and M. Satyanarayanan.
+ */
+
+
+/*
+ * This module contains the routines to implement the CFS name cache. The
+ * purpose of this cache is to reduce the cost of translating pathnames 
+ * into Vice FIDs. Each entry in the cache contains the name of the file,
+ * the vnode (FID) of the parent directory, and the cred structure of the
+ * user accessing the file.
+ *
+ * The first time a file is accessed, it is looked up by the local Venus
+ * which first insures that the user has access to the file. In addition
+ * we are guaranteed that Venus will invalidate any name cache entries in
+ * case the user no longer should be able to access the file. For these
+ * reasons we do not need to keep access list information as well as a
+ * cred structure for each entry.
+ *
+ * The table can be accessed through the routines cnc_init(), cnc_enter(),
+ * cnc_lookup(), cnc_rmfidcred(), cnc_rmfid(), cnc_rmcred(), and cnc_purge().
+ * There are several other routines which aid in the implementation of the
+ * hash table.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/string.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_namecache.h>
+
+
+int cfsnc_use;
+
+static struct cfscache * cfsnc_find(struct cnode *dcp, const char * name, int namelen, int hash);
+static void cfsnc_remove(struct cfscache *cncp);
+static inline int  nchash(const char *, int, struct cnode *);
+static inline int ncmatch(struct cfscache *, const char *, int, 
+                          struct cnode *);
+static inline void hashins(struct cfscache *a, struct cfscache *pred);
+static inline void hashrem(struct cfscache *a);
+static inline void hashnull(struct cfscache *);
+static inline void lrurem(struct cfscache *a);
+static inline void lruins(struct cfscache *a, struct cfscache *pred);
+static void cfsnc_gather_stats(void);
+
+
+/* externals */
+extern int coda_fideq(ViceFid *fid1, ViceFid *fid2);
+extern int coda_debug;
+extern int coda_print_entry;
+extern struct super_block *coda_super_block;
+
+
+
+/* 
+ * Declaration of the name cache data structure.
+ */
+
+int 	cfsnc_use = 0;			 /* Indicate use of CFS Name Cache */
+int	cfsnc_size = CFSNC_CACHESIZE;	 /* size of the cache */
+int	cfsnc_hashsize = CFSNC_HASHSIZE; /* size of the primary hash */
+int     cfsnc_flushme = 0;
+int     cfsnc_procsize = 0;
+static  int cfsnc_force = 0;
+
+struct cfshash {
+	struct cfscache *hash_next, *hash_prev;
+	int              length;
+};
+
+struct cfslruhead {
+        struct cfscache *dummy1, *dummy2;
+        struct cfscache *lru_next, *lru_prev;
+};
+
+struct 	cfscache *cfsncheap;	/* pointer to the cache entries */
+struct	cfshash  *cfsnchash;	/* hash table of cfscache pointers */
+struct	cfslruhead  cfsnc_lru;	/* head of lru chain; prev = lru */
+
+struct cfsnc_statistics cfsnc_stat;	/* Keep various stats */
+
+#define TOTAL_CACHE_SIZE 	(sizeof(struct cfscache) * cfsnc_size)
+#define TOTAL_HASH_SIZE 	(sizeof(struct cfshash)  * cfsnc_hashsize)
+int cfsnc_initialized = 0;      /* Initially the cache has not been initialized */
+
+/* 
+ * for testing purposes
+ */
+int cfsnc_debug = 1;
+
+
+/*
+ * Auxillary routines -- shouldn't be entry points
+ */
+
+
+/*
+ * Hash function for the primary hash.
+ * First try -- (first + last letters + length + (int)cp) mod size
+ * 2nd try -- same, except dir fid.vnode instead of cp
+ */
+static inline int  
+nchash(const char *name, int namelen, struct cnode *cp)
+{
+    return ((name[0] + name[namelen-1] + 
+             namelen + (int)(cp)) & (cfsnc_hashsize-1));   
+}
+
+/* matching function */
+static inline int ncmatch(struct cfscache *cp, const char *name, int namelen,
+                          struct cnode *dcp)
+{
+    return 	((namelen == cp->namelen) && (dcp == cp->dcp) && 
+		 (memcmp(cp->name,name,namelen) == 0));
+}
+
+/* insert  a  behind  pred */
+static inline void hashins(struct cfscache *a, struct cfscache *pred)
+{
+	a->hash_next = pred->hash_next;
+	pred->hash_next->hash_prev= a;
+	pred->hash_next = a;
+	a->hash_prev = pred;
+}
+
+static inline void hashrem(struct cfscache *a) 
+{
+	a->hash_prev->hash_next = a->hash_next;
+	a->hash_next->hash_prev = a->hash_prev;
+}
+
+static inline void hashnull(struct cfscache *elem) {
+	elem->hash_next = elem;
+	elem->hash_prev = elem;
+}
+
+static inline void lrurem(struct cfscache *a) 
+{
+	a->lru_prev->lru_next = a->lru_next;
+	a->lru_next->lru_prev = a->lru_prev;
+}
+
+static inline void lruins(struct cfscache *a, struct cfscache *pred)
+{
+	pred->lru_next->lru_prev= a;
+	a->lru_next = pred->lru_next;
+	
+	a->lru_prev = pred;
+	pred->lru_next = a;
+}
+
+static struct cfscache *
+cfsnc_find(struct cnode *dcp, const char * name, int namelen, int hash)
+{
+	/* 
+	 * hash to find the appropriate bucket, look through the chain
+	 * for the right entry 
+	 */
+	register struct cfscache *cncp;
+	int count = 1;
+
+	CDEBUG(D_CACHE, "dcp 0x%x, name %s, len %d, hash %d\n",
+			   (int)dcp, name, namelen, hash);
+
+	for (cncp  = cfsnchash[hash].hash_next; 
+	     cncp != (struct cfscache *)&cfsnchash[hash];
+	     cncp  = cncp->hash_next, count++) 
+	{
+
+	    if (ncmatch(cncp, name, namelen, dcp))
+	    { 
+		cfsnc_stat.Search_len += count;
+		CDEBUG(D_CACHE, "dcp 0x%x,found.\n", (int) dcp);
+		return(cncp);
+			
+	    }
+	}
+	CDEBUG(D_CACHE, "dcp 0x%x,not found.\n", (int) dcp);
+	return((struct cfscache *)0);
+}
+
+static void
+cfsnc_remove(struct cfscache *cncp)
+{
+	/* 
+	 * remove an entry -- VN_RELE(cncp->dcp, cp), crfree(cred),
+	 * remove it from it's hash chain, and
+	 * place it at the head of the lru list.
+	 */
+    CDEBUG(D_CACHE, "remove %s from parent %lx.%lx.%lx\n",
+           cncp->name, (cncp->dcp)->c_fid.Volume,
+           (cncp->dcp)->c_fid.Vnode, (cncp->dcp)->c_fid.Unique);
+
+  	hashrem(cncp);
+	hashnull(cncp);		/* have it be a null chain */
+
+	/* VN_RELE(CTOV(cncp->dcp));  */
+	iput(CTOI(cncp->cp)); 
+	/* crfree(cncp->cred);  */
+
+	memset(DATA_PART(cncp), 0 ,DATA_SIZE);
+	cncp->cp = NULL;
+	cncp->dcp = (struct cnode *) 0;
+
+	/* Put the null entry just after the least-recently-used entry */
+	lrurem(cncp);
+	lruins(cncp, cfsnc_lru.lru_prev);
+}
+
+
+/*
+ * Entry points for the CFS Name Cache
+ */
+
+/*  
+ * Initialize the cache, the LRU structure and the Hash structure(s)
+ */
+void
+cfsnc_init(void)
+{
+    register int i;
+
+    /* zero the statistics structure */
+    cfsnc_procsize =  10000 * cfsnc_hashsize + cfsnc_size;
+    memset(&cfsnc_stat, 0, (sizeof(struct cfsnc_statistics)));
+    
+    CODA_ALLOC(cfsncheap, struct cfscache *, TOTAL_CACHE_SIZE);
+    CODA_ALLOC(cfsnchash, struct cfshash *, TOTAL_HASH_SIZE);
+    
+    cfsnc_lru.lru_next = cfsnc_lru.lru_prev = (struct cfscache *)&cfsnc_lru; 
+    
+    /* initialize the heap */
+    for (i=0; i < cfsnc_size; i++) {	
+	lruins(&cfsncheap[i], (struct cfscache *) &cfsnc_lru);
+	hashnull(&cfsncheap[i]);
+	cfsncheap[i].cp = cfsncheap[i].dcp = (struct cnode *)0;
+    }
+    
+    for (i=0; i < cfsnc_hashsize; i++) {	/* initialize the hashtable */
+	hashnull((struct cfscache *)&cfsnchash[i]);
+	cfsnchash[i].length=0;  /* bucket length */
+    }
+    
+    cfsnc_initialized = 1;
+    CDEBUG(D_CACHE, "cfsnc_initialized is now 1.\n");
+}
+
+/*
+ * Enter a new (dir cnode, name) pair into the cache, updating the
+ * LRU and Hash as needed.
+ */
+
+void
+cfsnc_enter(struct cnode *dcp, register const char *name, int namelen, struct cnode *cp)
+{
+    register struct cfscache *cncp;
+    register int hash;
+    
+    if (cfsnc_use == 0)			/* Cache is off */
+	return;
+    
+    CDEBUG(D_CACHE, "dcp 0x%x cp 0x%x name %s, ind 0x%x \n",
+	   (int)dcp, (int)cp, name, (int)cp->c_vnode); 
+	
+    if (namelen > CFSNC_NAMELEN) {
+        CDEBUG(D_CACHE, "long name enter %s\n",name);
+	    cfsnc_stat.long_name_enters++;	/* record stats */
+	return;
+    }
+    
+    hash = nchash(name, namelen, dcp);
+	CDEBUG(D_CACHE, "Calling find with name %s, dcp %d, hash %d\n",
+	       name, (int) dcp, (int) hash);
+
+    cncp = cfsnc_find(dcp, name, namelen, hash);
+    if (cncp != (struct cfscache *) 0) {	
+	printk("cfsnc_enter: Duplicate cache entry; tell Peter.\n");
+	cfsnc_stat.dbl_enters++;		/* duplicate entry */
+	return;
+    }
+    
+    cfsnc_stat.enters++;		/* record the enters statistic */
+    
+    /* Grab the lru element in the lru chain */
+    cncp = cfsnc_lru.lru_prev;
+    
+    lrurem(cncp);	/* remove it from the lists */
+    
+    /* if cncp is on hash list remove it */
+    if ( cncp->dcp != (struct cnode *) 0 ) {
+	/* We have to decrement the appropriate hash bucket length
+	   here, so we have to find the hash bucket */
+	cfsnchash[nchash(cncp->name, cncp->namelen, cncp->dcp)].length--;
+	cfsnc_stat.lru_rm++;	/* zapped a valid entry */
+	hashrem(cncp);
+	iput(CTOI(cncp->cp));
+	/* VN_RELE(CTOV(cncp->dcp));  */
+	/* crfree(cncp->cred); */
+    }
+    /*
+     * Put a hold on the current vnodes and fill in the cache entry.
+     */
+    iget((CTOI(cp))->i_sb, CTOI(cp)->i_ino);
+    /* VN_HOLD(CTOV(dcp)); */
+    /* XXXX crhold(cred); */
+    cncp->dcp = dcp;
+    cncp->cp = cp;
+    cncp->namelen = namelen;
+    /* cncp->cred = cred; */
+    
+    memcpy(cncp->name, name, (unsigned)namelen);
+    
+    /* Insert into the lru and hash chains. */
+    
+    lruins(cncp, (struct cfscache *) &cfsnc_lru);
+    hashins(cncp, (struct cfscache *)&cfsnchash[hash]);
+    cfsnchash[hash].length++;                      /* Used for tuning */
+    CDEBUG(D_CACHE, "Entering:\n");
+    coda_print_ce(cncp);
+}
+
+/*
+ * Find the (dir cnode, name) pair in the cache, if it's cred
+ * matches the input, return it, otherwise return 0
+ */
+
+struct cnode *
+cfsnc_lookup(struct cnode *dcp, register const char *name, int namelen)
+{
+	register int hash;
+	register struct cfscache *cncp;
+        /* this should go into a callback funcntion for /proc/sys
+           don't know how at the moment? */  
+	if (cfsnc_flushme == 1) {
+		cfsnc_flush();
+		cfsnc_flushme = 0;
+	}
+	
+	if (cfsnc_procsize != 10000*cfsnc_hashsize + cfsnc_size ) {
+		int hsh = cfsnc_procsize/10000;
+		int siz = cfsnc_procsize%10000;
+		int rc;
+		
+		if ( (hsh > 1) && (siz > 2) ) {
+			rc = cfsnc_resize(hsh, siz);
+			if ( !rc ) {
+				printk("Coda:cache size (hash,size) (%d,%d)\n",
+					hsh, siz);
+			} else {
+				printk("Coda: cache resize failed\n");
+			}
+		}
+	}
+
+	if (cfsnc_use == 0)			/* Cache is off */
+		return((struct cnode *) 0);
+
+	if (namelen > CFSNC_NAMELEN) {
+        CDEBUG(D_CACHE,"long name lookup %s\n",name);
+		cfsnc_stat.long_name_lookups++;		/* record stats */
+		return((struct cnode *) 0);
+	}
+
+	/* Use the hash function to locate the starting point,
+	   then the search routine to go down the list looking for
+	   the correct cred.
+ 	 */
+
+	hash = nchash(name, namelen, dcp);
+	CDEBUG(D_CACHE, "Calling find with name %s, dcp %d, hash %d\n",
+	       name, (int) dcp, (int) hash);
+	cncp = cfsnc_find(dcp, name, namelen, hash);
+	if (cncp == (struct cfscache *) 0) {
+		cfsnc_stat.misses++;			/* record miss */
+		return((struct cnode *) 0);
+	}
+
+	cfsnc_stat.hits++;
+
+	/* put this entry at the mru end of the LRU */
+	lrurem(cncp);
+	lruins(cncp, (struct cfscache *) &cfsnc_lru);
+
+	/* move it to the front of the hash chain */
+	/* don't need to change the hash bucket length */
+	hashrem(cncp);
+	hashins(cncp, (struct cfscache *) &cfsnchash[hash]);
+
+	CDEBUG(D_CACHE, "lookup: dcp 0x%x, name %s,  cp 0x%x\n",
+           (int) dcp, name,  (int) cncp->cp);
+
+	return(cncp->cp);
+}
+
+/*
+ * Remove all entries with a parent which has the input fid.
+ */
+
+void
+cfsnc_zapParentfid(ViceFid *fid)
+{
+	/* To get to a specific fid, we might either have another hashing
+	   function or do a sequential search through the cache for the
+	   appropriate entries. The later may be acceptable since I don't
+	   think callbacks or whatever Case 1 covers are frequent occurences.
+	 */
+	register struct cfscache *cncp, *ncncp;
+	register int i;
+
+	if (cfsnc_use == 0)			/* Cache is off */
+		return;
+
+	CDEBUG(D_CACHE, " fid 0x%lx, 0x%lx, 0x%lx \n",
+           fid->Volume, fid->Vnode, fid->Unique);
+
+	cfsnc_stat.zapPfids++;
+
+	for (i = 0; i < cfsnc_hashsize; i++) {
+
+		/*
+		 * Need to save the hash_next pointer in case we remove the
+		 * entry. remove causes hash_next to point to itself.
+		 */
+
+		for (cncp = cfsnchash[i].hash_next; 
+		     cncp != (struct cfscache *) &cfsnchash[i];
+		     cncp = ncncp) {
+			ncncp = cncp->hash_next;
+			if ( coda_fideq(&cncp->dcp->c_fid, fid) ) {
+			        cfsnchash[i].length--;   /* Used for tuning */
+				cfsnc_remove(cncp); 
+			    }
+		     }
+	}
+}
+
+/*
+ * Remove all entries which have the same fid as the input
+ */
+void
+cfsnc_zapfid(ViceFid *fid)
+{
+	/* See comment for zapParentfid. This routine will be used
+	   if attributes are being cached. 
+	 */
+	register struct cfscache *cncp, *ncncp;
+	register int i;
+
+	if (cfsnc_use == 0)			/* Cache is off */
+		return;
+
+	CDEBUG(D_CACHE, "Zapfid: fid 0x%lx, 0x%lx, 0x%lx \n",
+           fid->Volume, fid->Vnode, fid->Unique);
+
+	cfsnc_stat.zapFids++;
+
+	for (i = 0; i < cfsnc_hashsize; i++) {
+		for (cncp = cfsnchash[i].hash_next; 
+		     cncp != (struct cfscache *) &cfsnchash[i];
+		     cncp = ncncp) {
+			ncncp = cncp->hash_next;
+			if (coda_fideq(&(cncp->cp->c_fid), fid)) {
+			        CDEBUG(D_CACHE, "Found cncp: name %s\n", cncp->name);
+			        cfsnchash[i].length--;   /* Used for tuning */
+				cfsnc_remove(cncp); 
+			    }
+		     }
+	}
+}
+
+
+/*
+ * Remove all entries which have the (dir vnode, name) pair
+ */
+void
+cfsnc_zapfile(struct cnode *dcp, register const char *name, int length)
+{
+	/* use the hash function to locate the file, then zap all
+ 	   entries of it regardless of the cred.
+	 */
+	register struct cfscache *cncp;
+	int hash;
+
+	if (cfsnc_use == 0)			/* Cache is off */
+		return;
+
+	CDEBUG(D_CACHE,"Zapfile: dcp 0x%x name %s \n",
+           (int) dcp, name);
+
+	if (length > CFSNC_NAMELEN) {
+		cfsnc_stat.long_remove++;		/* record stats */
+		return;
+	}
+
+	cfsnc_stat.zapFile++;
+
+	hash = nchash(name, length,  dcp);
+    /* remove entries: remember they might exist for more than a 
+       single cred */
+	while ( (cncp = cfsnc_find(dcp, name, length, hash)) != NULL ) {
+	  cfsnchash[hash].length--;       
+	  cfsnc_remove(cncp);
+	}
+}
+
+/* 
+ * Remove all the entries for a particular user. Used when tokens expire.
+ * A user is determined by his/her effective user id (id_uid).
+ */
+
+void
+cfsnc_purge_user(struct CodaCred *cred)
+{
+	/* I think the best approach is to go through the entire cache
+	   via HASH or whatever and zap all entries which match the
+	   input cred. Or just flush the whole cache.
+	   It might be best to go through on basis of LRU since cache
+	   will almost always be full and LRU is more straightforward.
+	 */
+
+	register struct cfscache *cncp;
+	int hash;
+
+	if (cfsnc_use == 0)			/* Cache is off */
+		return;
+
+	CDEBUG(D_CACHE,"ZapDude: uid %ld\n",cred->cr_uid);
+	cfsnc_stat.zapUsers++;
+
+	for (cncp = cfsnc_lru.lru_next;
+	     cncp != (struct cfscache *) &cfsnc_lru;
+	     cncp = cncp->lru_next) {
+
+		if ((CFSNC_VALID(cncp)) &&
+		   ((cncp->cred)->cr_uid == cred->cr_uid)) {
+		        /* Seems really ugly, but we have to decrement the appropriate
+			   hash bucket length here, so we have to find the hash bucket
+			   */
+		        hash = nchash(cncp->name, cncp->namelen, cncp->dcp);
+			cfsnchash[hash].length--;     /* For performance tuning */
+
+			cfsnc_remove(cncp); 
+		}
+	}
+}
+
+/*
+ * Flush the entire name cache. In response to a flush of the Venus cache.
+ */
+
+void
+cfsnc_flush(void)
+{
+	/* One option is to deallocate the current name cache and
+	   call init to start again. Or just deallocate, then rebuild.
+	   Or again, we could just go through the array and zero the 
+	   appropriate fields. 
+	 */
+	
+	/* 
+	 * Go through the whole lru chain and kill everything as we go.
+	 * I don't use remove since that would rebuild the lru chain
+	 * as it went and that seemed unneccesary.
+	 */
+	register struct cfscache *cncp;
+	int i;
+
+	if ((cfsnc_use == 0 || cfsnc_initialized == 0) && (cfsnc_force == 0) )
+		return;
+
+	cfsnc_stat.Flushes++;
+
+	for (cncp = cfsnc_lru.lru_next;
+	     cncp != (struct cfscache *) &cfsnc_lru;
+	     cncp = cncp->lru_next) {
+		if ( cncp->cp ) {
+			hashrem(cncp);	/* only zero valid nodes */
+			hashnull(cncp);
+			iput(CTOI(cncp->cp));  
+			/* crfree(cncp->cred);  */
+			memset(DATA_PART(cncp), 0, DATA_SIZE);
+		}
+	}
+
+	for (i = 0; i < cfsnc_hashsize; i++)
+	  cfsnchash[i].length = 0;
+}
+
+/*
+ * This routine replaces a ViceFid in the name cache with another.
+ * It is added to allow Venus during reintegration to replace 
+ * locally allocated temp fids while disconnected with global fids 
+ * even when the reference count on those fids are not zero.
+ */
+void
+cfsnc_replace(ViceFid *f1, ViceFid *f2)
+{
+        /* 
+	 * Replace f1 with f2 throughout the name cache
+	 */
+	int hash;
+	register struct cfscache *cncp;
+
+	CDEBUG(D_CACHE, 
+    "cfsnc_replace fid_1 = (%lx.%lx.%lx) and fid_2 = (%lx.%lx.%lx)\n",
+           f1->Volume, f1->Vnode, f1->Unique, 
+           f2->Volume, f2->Vnode, f2->Unique);
+
+	for (hash = 0; hash < cfsnc_hashsize; hash++) {
+		for (cncp = cfsnchash[hash].hash_next; 
+		     cncp != (struct cfscache *) &cfsnchash[hash];
+		     cncp = cncp->hash_next) {
+		        if (!memcmp(&cncp->cp->c_fid, f1, sizeof(ViceFid))) {
+			    memcpy(&cncp->cp->c_fid, f2, sizeof(ViceFid));
+			    continue; 	/* no need to check cncp->dcp now */
+			}
+		        if (!memcmp(&cncp->dcp->c_fid, f1, sizeof(ViceFid)))
+			    memcpy(&cncp->dcp->c_fid, f2, sizeof(ViceFid));
+		     }
+	}
+}
+
+/*
+ * Debugging routines
+ */
+
+/* 
+ * This routine should print out all the hash chains to the console.
+ */
+
+void
+print_cfsnc(void)
+{
+	int hash;
+	register struct cfscache *cncp;
+
+	for (hash = 0; hash < cfsnc_hashsize; hash++) {
+		printk("\nhash %d\n",hash);
+
+		for (cncp = cfsnchash[hash].hash_next; 
+		     cncp != (struct cfscache *)&cfsnchash[hash];
+		     cncp = cncp->hash_next) {
+			printk("cp 0x%x dcp 0x%x cred 0x%x name %s ino %d count %d dev %d\n",
+				  (int)cncp->cp, (int)cncp->dcp,
+				  (int)cncp->cred, cncp->name, CTOI(cncp->cp)->i_count, CTOI(cncp->cp)->i_count, CTOI(cncp->cp)->i_dev);
+		     }
+	}
+}
+
+int
+cfsnc_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+        int hash;
+        int len=0;
+        off_t pos=0;
+        off_t begin;
+	struct cfscache *cncp;
+        char tmpbuf[80];
+
+        if (offset < 80) 
+              len += sprintf(buffer, "%-79s\n",
+      "hash  len   volume    vnode   unique             name        ino       pino ct");
+	if ( !cfsnc_initialized ) {
+		*start = buffer;
+		return len;
+	}
+        pos = 80;
+	for (hash = 0; hash < cfsnc_hashsize; hash++) {
+		for (cncp = cfsnchash[hash].hash_next; 
+		     cncp != (struct cfscache *)&cfsnchash[hash];
+		     cncp = cncp->hash_next) {
+		 	pos += 80;
+		 	if (pos < offset)
+                                continue;
+			sprintf(tmpbuf, "%4d  %3d %8x %8x %8x %16s %10ld %10ld %2d", 
+				hash, cfsnchash[hash].length, (int) cncp->cp->c_fid.Volume, 
+				(int) cncp->cp->c_fid.Vnode, (int) cncp->cp->c_fid.Unique , cncp->name, 
+				CTOI(cncp->cp)->i_ino, 
+				CTOI(cncp->dcp)->i_ino, 
+				CTOI(cncp->cp)->i_count);
+                     	len += sprintf(buffer+len, "%-79s\n", tmpbuf);
+                     	if(len >= length)
+                                break;
+                	}
+               	if(len>= length)
+                       	break;
+		}
+        begin = len - (pos - offset);
+        *start = buffer + begin;
+        len -= begin;
+        if(len>length)
+                len = length;
+        return len;
+} 
+
+int
+cfsnc_nc_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+        int len=0;
+        off_t begin;
+	
+	cfsnc_gather_stats();
+
+	/* this works as long as we are below 1024 characters! */    
+    len += sprintf(buffer,"Coda minicache statistics\n\n");
+    len += sprintf(buffer+len, "cfsnc_hits : %d\n", cfsnc_stat.hits);
+    len += sprintf(buffer+len, "cfsnc_misses : %d\n", cfsnc_stat.misses);
+    len += sprintf(buffer+len, "cfsnc_enters : %d\n", cfsnc_stat.enters);
+    len += sprintf(buffer+len, "cfsnc_dbl_enters : %d\n", cfsnc_stat.dbl_enters);
+    len += sprintf(buffer+len, "cfsnc_long_name_enters : %d\n", cfsnc_stat.long_name_enters);
+    len += sprintf(buffer+len, "cfsnc_long_name_lookups : %d\n", cfsnc_stat.long_name_lookups);
+    len += sprintf(buffer+len, "cfsnc_long_remove : %d\n", cfsnc_stat.long_remove);
+    len += sprintf(buffer+len, "cfsnc_lru_rm : %d\n", cfsnc_stat.lru_rm);
+    len += sprintf(buffer+len, "cfsnc_zapPfids : %d\n", cfsnc_stat.zapPfids);
+    len += sprintf(buffer+len, "cfsnc_zapFids : %d\n", cfsnc_stat.zapFids);
+    len += sprintf(buffer+len, "cfsnc_zapFile : %d\n", cfsnc_stat.zapFile);
+    len += sprintf(buffer+len, "cfsnc_zapUsers : %d\n", cfsnc_stat.zapUsers);
+    len += sprintf(buffer+len, "cfsnc_Flushes : %d\n", cfsnc_stat.Flushes);
+    len += sprintf(buffer+len, "cfsnc_SumLen : %d\n", cfsnc_stat.Sum_bucket_len);
+    len += sprintf(buffer+len, "cfsnc_Sum2Len : %d\n", cfsnc_stat.Sum2_bucket_len);
+    len += sprintf(buffer+len,  "cfsnc_# 0 len : %d\n", cfsnc_stat.Num_zero_len);
+    len += sprintf(buffer+len,  "cfsnc_MaxLen : %d\n", cfsnc_stat.Max_bucket_len);
+    len += sprintf(buffer+len,  "cfsnc_SearchLen : %d\n", cfsnc_stat.Search_len);
+       	begin =  offset;
+       	*start = buffer + begin;
+       	len -= begin;
+
+        if(len>length)
+                len = length;
+	if (len< 0)
+		len = 0;
+        return len;
+} 
+
+
+
+void
+coda_print_ce(struct cfscache *ce) 
+{
+CDEBUG(D_CACHE, "cp 0x%x, dcp 0x%x, name %s, inod 0x%x, ino %d, count %d, dev %d\n",
+        (int)ce->cp, (int)ce->dcp, ce->name, (int)CTOI(ce->cp),(int)CTOI(ce->cp)->i_ino,  CTOI(ce->cp)->i_count, CTOI(ce->cp)->i_dev);
+}
+
+static void
+cfsnc_gather_stats(void)
+{
+    int i, max = 0, sum = 0, temp, zeros = 0, ave, n;
+
+	for (i = 0; i < cfsnc_hashsize; i++) {
+	  if (cfsnchash[i].length) {
+	    sum += cfsnchash[i].length;
+	  } else {
+	    zeros++;
+	  }
+
+	  if (cfsnchash[i].length > max)
+	    max = cfsnchash[i].length;
+	}
+
+/*
+ * When computing the Arithmetic mean, only count slots which 
+ * are not empty in the distribution.
+ */
+        cfsnc_stat.Sum_bucket_len = sum;
+        cfsnc_stat.Num_zero_len = zeros;
+        cfsnc_stat.Max_bucket_len = max;
+
+	if ((n = cfsnc_hashsize - zeros) > 0) 
+	  ave = sum / n;
+	else
+	  ave = 0;
+
+	sum = 0;
+	for (i = 0; i < cfsnc_hashsize; i++) {
+	  if (cfsnchash[i].length) {
+	    temp = cfsnchash[i].length - ave;
+	    sum += temp * temp;
+	  }
+	}
+        cfsnc_stat.Sum2_bucket_len = sum;
+}
+
+/*
+ * The purpose of this routine is to allow the hash and cache sizes to be
+ * changed dynamically. This should only be used in controlled environments,
+ * it makes no effort to lock other users from accessing the cache while it
+ * is in an improper state (except by turning the cache off).
+ */
+int
+cfsnc_resize(int hashsize, int heapsize)
+{
+    if ( !cfsnc_use )
+	    return 0;
+
+    if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */
+	    return(EINVAL);
+    }                 
+    
+    cfsnc_use = 0;                       /* Turn the cache off */
+    cfsnc_force = 1;                     /* otherwise we can't flush */
+    
+    cfsnc_flush();                       /* free any cnodes in the cache */
+    cfsnc_force = 0;
+    
+    /* WARNING: free must happen *before* size is reset */
+    CODA_FREE(cfsncheap,TOTAL_CACHE_SIZE);
+    CODA_FREE(cfsnchash,TOTAL_HASH_SIZE);
+    
+    cfsnc_hashsize = hashsize;
+    cfsnc_size = heapsize;
+    
+    cfsnc_init();                        /* Set up a cache with the new size */
+    
+    cfsnc_use = 1;                       /* Turn the cache back on */
+    return(0);
+}
+
+
+
diff -u --recursive --new-file v2.1.69/linux/fs/coda/pioctl.c linux/fs/coda/pioctl.c
--- v2.1.69/linux/fs/coda/pioctl.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/coda/pioctl.c	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,154 @@
+/*
+ * Pioctl operations for Coda.
+ * Original version: (C) 1996 Peter Braam 
+ * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users of this code to contribute improvements
+ * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+
+#include <linux/coda_namecache.h>
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_psdev.h>
+
+/* pioctl ops */
+static int coda_ioctl_permission(struct inode *inode, int mask);
+static int coda_ioctl_open(struct inode *i, struct file *f);
+static int coda_ioctl_release(struct inode *i, struct file *f);
+static int coda_pioctl(struct inode * inode, struct file * filp, 
+                       unsigned int cmd, unsigned long arg);
+
+/* exported from this file */
+struct inode_operations coda_ioctl_inode_operations =
+{
+	&coda_ioctl_operations,
+	NULL,	                /* create */
+	NULL,	                /* lookup */
+	NULL,	                /* link */
+	NULL,	                /* unlink */
+	NULL,	                /* symlink */
+	NULL,	                /* mkdir */
+	NULL,	                /* rmdir */
+	NULL,		        /* mknod */
+	NULL,		        /* rename */
+	NULL,	                /* readlink */
+	NULL,	                /* follow_link */
+	NULL,	                /* readpage */
+	NULL,		        /* writepage */
+	NULL,		        /* bmap */
+	NULL,	                /* truncate */
+	coda_ioctl_permission,  /* permission */
+	NULL,                   /* smap */
+	NULL,                   /* update page */
+        NULL                    /* revalidate */
+};
+
+struct file_operations coda_ioctl_operations = {
+	NULL,		        /* lseek - default should work for coda */
+	NULL,                   /* read */
+	NULL,                   /* write */
+	NULL,          		/* readdir */
+	NULL,			/* select - default */
+	coda_pioctl,	        /* ioctl */
+	NULL,                   /* mmap */
+	coda_ioctl_open,        /* open */
+	coda_ioctl_release,     /* release */
+	NULL,		        /* fsync */
+};
+
+/* the coda pioctl inode ops */
+static int coda_ioctl_permission(struct inode *inode, int mask)
+{
+        ENTRY;
+
+        return 0;
+}
+
+/* The pioctl file ops*/
+int coda_ioctl_open(struct inode *i, struct file *f)
+{
+
+        ENTRY;
+
+        CDEBUG(D_PIOCTL, "File inode number: %ld\n", 
+	       f->f_dentry->d_inode->i_ino);
+
+	EXIT;
+        return 0;
+}
+
+int coda_ioctl_release(struct inode *i, struct file *f) 
+{
+        return 0;
+}
+
+
+static int coda_pioctl(struct inode * inode, struct file * filp, 
+		       unsigned int cmd, unsigned long user_data)
+{
+        struct dentry *target_de;
+        int error;
+	struct PioctlData data;
+        struct inode *target_inode = NULL;
+        struct cnode *cnp;
+
+        ENTRY;
+        /* get the Pioctl data arguments from user space */
+        if (copy_from_user(&data, (int *)user_data, sizeof(data))) {
+	    return -EINVAL;
+	}
+       
+        /* 
+         * Look up the pathname. Note that the pathname is in 
+         * user memory, and namei takes care of this
+         */
+	CDEBUG(D_PIOCTL, "namei, data.follow = %d\n", data.follow);
+        if ( data.follow ) {
+                target_de = namei(data.path);
+	} else {
+	        target_de = lnamei(data.path);
+	}
+
+	if (!target_de) {
+                CDEBUG(D_PIOCTL, "error: lookup fails.\n");
+		return -EINVAL;
+        } else {
+	        target_inode = target_de->d_inode;
+	}
+	
+	CDEBUG(D_PIOCTL, "target ino: 0x%ld, dev: 0x%d\n",
+	       target_inode->i_ino, target_inode->i_dev);
+
+	/* return if it is not a Coda inode */
+	if ( target_inode->i_sb != inode->i_sb ) {
+  	        if ( target_de )
+		        dput(target_de);
+	        return  -EINVAL;
+	}
+
+	/* now proceed to make the upcall */
+        cnp = ITOC(target_inode);
+        CHECK_CNODE(cnp);
+
+	error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data);
+
+        CDEBUG(D_PIOCTL, "ioctl on inode %ld\n", target_inode->i_ino);
+
+        if ( target_de ) 
+	        dput(target_de);
+        return error;
+}
+
diff -u --recursive --new-file v2.1.69/linux/fs/coda/psdev.c linux/fs/coda/psdev.c
--- v2.1.69/linux/fs/coda/psdev.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/coda/psdev.c	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,444 @@
+/*
+ *      	An implementation of a loadable kernel mode driver providing
+ *		multiple kernel/user space bidirectional communications links.
+ *
+ * 		Author: 	Alan Cox <alan@cymru.net>
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ * 
+ *              Adapted to become the Linux 2.0 Coda pseudo device
+ *              Peter  Braam  <braam@maths.ox.ac.uk> 
+ *              Michael Callahan <mjc@emmy.smith.edu>           
+ *
+ *              Changes for Linux 2.1
+ *              Copyright (c) 1997 Carnegie-Mellon University
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/lp.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/poll.h>
+#include <asm/uaccess.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_namecache.h>
+#include <linux/coda_sysctl.h>
+
+
+/* 
+ * Coda stuff
+ */
+extern struct file_system_type coda_fs_type;
+extern int coda_downcall(int opcode, struct outputArgs *out);
+extern int init_coda_fs(void);
+extern int cfsnc_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
+extern int cfsnc_nc_info(char *buffer, char **start, off_t offset, int length, int dummy);
+
+/* statistics */
+struct coda_upcallstats coda_callstats;
+
+extern struct coda_sb_info coda_super_info[MAX_CODADEVS];
+struct vcomm psdev_vcomm[MAX_CODADEVS];
+
+/*
+ * Device operations
+ */
+
+
+static struct vcomm *coda_psdev2vcomm(struct file *file)
+{
+       	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+	struct vcomm *vcp = NULL;
+
+	if ( (minor >= 0)  && (minor < MAX_CODADEVS) )
+	        vcp = &psdev_vcomm[minor];
+	return vcp;
+}
+	
+
+static unsigned int coda_psdev_poll(struct file *file, poll_table * wait)
+{
+        struct vcomm *vcp = coda_psdev2vcomm(file);
+	unsigned int mask = POLLOUT | POLLWRNORM;
+
+	if ( !vcp ) 
+	        return -ENXIO;
+
+	poll_wait(&(vcp->vc_waitq), wait);
+	if (!EMPTY(vcp->vc_pending))
+                mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+
+
+/*
+ *	Receive a message written by Venus to the psdev
+ */
+ 
+static ssize_t coda_psdev_write(struct file *file, const char *buf, 
+				size_t count, loff_t *off)
+{
+        struct vcomm *vcp = coda_psdev2vcomm(file);
+        struct vmsg *vmp;
+        struct outputArgs *out;
+	int error = 0;
+	int size;
+        u_long uniq;
+        u_long opcode;
+        u_long opcodebuf[2];
+        
+        if (!vcp)
+                return -ENXIO;
+
+        /* Peek at the opcode, unique id */
+	if (copy_from_user(opcodebuf, buf, 2 * sizeof(u_long)))
+	        return -EFAULT;
+	opcode = opcodebuf[0];
+        uniq = opcodebuf[1];
+
+	CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%ld,%ld)\n", 
+	       current->pid, opcode, uniq);
+
+        if (DOWNCALL(opcode)) {
+                struct outputArgs pbuf;
+
+		CDEBUG(D_PSDEV, "handling downcall\n");
+
+              /* get the rest of the data. */
+		size = sizeof(pbuf);
+		if  ( count < sizeof(pbuf) ) {
+		        printk("Coda: downcall opc %ld, uniq %ld, not enough!\n",
+			       opcode, uniq);
+			size =count;
+		} else if ( count > sizeof(pbuf) ) {
+		        printk("Coda: downcall opc %ld, uniq %ld, too much!",
+			       opcode, uniq);
+		        size = sizeof(pbuf);
+		}
+		if (copy_from_user(&pbuf, buf, size))
+		        return -EFAULT;
+
+	      /* what errors for coda_downcall should be
+	      * sent to Venus ? 
+	      */
+		error = coda_downcall(opcode, &pbuf);
+		if ( error) {
+		        printk("psdev_write: coda_downcall error: %d\n", 
+			       error);
+			return 0;
+		}
+		return count;
+        }
+
+        
+        /* Look for the message on the processing queue. */
+        for (vmp = (struct vmsg *)GETNEXT(vcp->vc_processing);
+	     !EOQ(vmp, vcp->vc_processing);
+             vmp = (struct vmsg *)GETNEXT(vmp->vm_chain)) {
+	        if (vmp->vm_unique == uniq) break;
+		CDEBUG(D_PSDEV,"Eureka: uniq %ld on queue!\n", uniq);
+	}
+        if (EOQ(vmp, vcp->vc_processing)) {
+	        printk("psdev_write: msg (%ld, %ld) not found\n", 
+		       opcode, uniq);
+		return(-ESRCH);
+        }
+
+        /* Remove the message from the processing queue */
+        REMQUE(vmp->vm_chain);
+
+        /* move data into response buffer. */
+        /* Don't need to copy opcode and uniquifier. */
+        out = (struct outputArgs *)vmp->vm_data;
+        /* get the rest of the data. */
+        if (vmp->vm_outSize < count) {
+                printk("Coda write: too much outs: %d, cnt: %d, opc: %ld, uniq: %ld.\n",
+		       vmp->vm_outSize, count, opcode, uniq);
+		wake_up_interruptible(&vmp->vm_sleep); 	
+		return -EINVAL;
+        } else if (vmp->vm_outSize > count) {
+                printk("Coda write: too much outs: %d, cnt: %d, opc: %ld, uniq: %ld.\n",
+		       vmp->vm_outSize, count, opcode, uniq);
+	}
+        if (copy_from_user(out, buf, count))
+	        return -EFAULT;
+
+	/* adjust outsize. is this usefull ?? */
+        vmp->vm_outSize = count;	
+        vmp->vm_flags |= VM_WRITE;
+
+	CDEBUG(D_PSDEV, 
+	       "Found! Count %d for (opc,uniq)=(%ld,%ld), vmsg at %x\n", 
+	        count, opcode, uniq, (int)&vmp);
+
+        wake_up_interruptible(&vmp->vm_sleep);
+        return(count);  
+}
+
+/*
+ *	Read a message from the kernel to Venus
+ */
+
+static ssize_t coda_psdev_read(struct file * file, char * buf, 
+			    size_t count, loff_t *off)
+{
+        struct vcomm *vcp = coda_psdev2vcomm(file);
+        struct vmsg *vmp;
+	int result = count ;
+
+        if (!vcp)
+              return -ENXIO;
+        
+        /* Get message at head of request queue. */
+        if (EMPTY(vcp->vc_pending)) {
+              return 0;	/* Nothing to read */
+        }
+    
+        vmp = (struct vmsg *)GETNEXT(vcp->vc_pending);
+        REMQUE(vmp->vm_chain);
+
+        /* Move the input args into userspace */
+        
+        if (vmp->vm_inSize <= count)
+              result = vmp->vm_inSize;
+
+        if (count < vmp->vm_inSize) {
+                printk ("psdev_read: warning: venus read %d bytes of %d long 
+                                           message\n",count, vmp->vm_inSize);
+        }
+
+        if ( copy_to_user(buf, vmp->vm_data, result))
+	        return -EFAULT;
+        
+        if (vmp->vm_chain.forw == 0 || vmp->vm_chain.back == 0)
+                coda_panic("coda_psdev_read: bad chain");
+
+        /* If request was a signal, free up the message and don't
+           enqueue it in the reply queue. */
+        if (vmp->vm_opcode == CFS_SIGNAL) {
+                    CDEBUG(D_PSDEV, "vcread: signal msg (%d, %d)\n", 
+                              vmp->vm_opcode, vmp->vm_unique);
+              CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
+              CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
+              return count;
+        }
+    
+        vmp->vm_flags |= VM_READ;
+        INSQUE(vmp->vm_chain, vcp->vc_processing);
+
+        return result;
+}
+
+
+static int coda_psdev_open(struct inode * inode, struct file * file)
+{
+        register struct vcomm *vcp = NULL;
+
+        ENTRY;
+        
+	vcp = coda_psdev2vcomm(file);
+
+        if (!vcp)
+              return -ENODEV;
+	memset(vcp, 0, sizeof(struct vcomm));
+
+        MOD_INC_USE_COUNT;
+
+        INIT_QUEUE(vcp->vc_pending);
+        INIT_QUEUE(vcp->vc_processing);
+
+	cfsnc_init();
+	CDEBUG(D_PSDEV, "Name cache initialized.\n");
+
+	memset(&coda_callstats, 0, sizeof(struct coda_upcallstats));
+	EXIT;
+        return 0;
+}
+
+
+static int
+coda_psdev_release(struct inode * inode, struct file * file)
+{
+        struct vcomm *vcp;
+        struct vmsg *vmp;
+	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+
+        vcp = coda_psdev2vcomm(file);
+        
+        if ( !vcp || !vcomm_open(vcp) ) {
+              printk("psdev_release: not open");
+              return 0;
+        }
+    
+	
+	/* flush the name cache so that we can unmount */
+	CDEBUG(D_PSDEV, "Flushing the cache.\n");
+	cfsnc_flush();
+	cfsnc_use = 0;
+	CDEBUG(D_PSDEV, "Done.\n");
+	
+        /* prevent future operations on this vfs from succeeding by
+         * auto- unmounting any vfs mounted via this device. This
+         * frees user or sysadm from having to remember where all
+         * mount points are located.  Put this before WAKEUPs to avoid
+         * queuing new messages between the WAKEUP and the unmount
+         * (which can happen if we're unlucky) */
+
+        if (coda_super_info[minor].sbi_root) {
+                struct cnode *cnp = ITOC(coda_super_info[minor].sbi_root);
+                /* Let unmount know this is for real */
+                cnp->c_flags |= C_DYING;
+		/* XXX Could we force an unmount here? */
+        }
+	
+    
+        /* Wakeup clients so they can return. */
+        for (vmp = (struct vmsg *)GETNEXT(vcp->vc_pending);
+             !EOQ(vmp, vcp->vc_pending);
+             vmp = (struct vmsg *)GETNEXT(vmp->vm_chain)) {	    
+              /* Free signal request messages and don't wakeup cause
+                 no one is waiting. */
+              if (vmp->vm_opcode == CFS_SIGNAL) {
+                    CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
+                    CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
+                    continue;
+              }
+    
+              wake_up_interruptible(&vmp->vm_sleep);
+        }
+        
+        for (vmp = (struct vmsg *)GETNEXT(vcp->vc_processing);
+             !EOQ(vmp, vcp->vc_processing);
+             vmp = (struct vmsg *)GETNEXT(vmp->vm_chain)) {
+	        wake_up_interruptible(&vmp->vm_sleep);
+        }
+        
+        mark_vcomm_closed(vcp);
+	cfsnc_use = 0;
+        MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+
+static struct file_operations coda_psdev_fops = {
+      NULL,                  /* llseek */
+      coda_psdev_read,       /* read */
+      coda_psdev_write,      /* write */
+      NULL,		     /* coda_psdev_readdir */
+      coda_psdev_poll,       /* poll */
+      NULL,                  /* ioctl */
+      NULL,		     /* coda_psdev_mmap */
+      coda_psdev_open,       /* open */
+      coda_psdev_release,    /* release */
+      NULL,                  /* fsync */
+      NULL,                  /* fasync */
+      NULL,                  /* check_media_change */
+      NULL,                  /* revalidate */
+      NULL                   /* lock */
+};
+
+int init_coda_psdev(void)
+{
+        
+	if(register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", &coda_psdev_fops)) {
+              printk(KERN_ERR "coda_psdev: unable to get major %d\n", 
+		     CODA_PSDEV_MAJOR);
+              return -EIO;
+	}
+        
+	return 0;
+}
+
+
+#ifdef CONFIG_PROC_FS
+
+struct proc_dir_entry proc_coda = {
+        0, 4, "coda",
+        S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+        0, &proc_net_inode_operations,
+
+};
+
+struct proc_dir_entry proc_coda_cache =  {
+                0 , 10, "coda-cache",
+                S_IFREG | S_IRUGO, 1, 0, 0,
+                0, &proc_net_inode_operations,
+                cfsnc_get_info
+        };
+
+struct proc_dir_entry proc_coda_ncstats =  {
+                0 , 12, "coda-ncstats",
+                S_IFREG | S_IRUGO, 1, 0, 0,
+                0, &proc_net_inode_operations,
+                cfsnc_nc_info
+        };
+
+#endif
+
+#ifdef MODULE
+int init_module(void)
+{
+  int status;
+  printk(KERN_INFO "Coda Kernel/User communications module 0.04\n");
+
+#ifdef CONFIG_PROC_FS
+  proc_register(&proc_root,&proc_coda);
+  proc_register(&proc_coda, &proc_coda_cache);
+  proc_register(&proc_coda, &proc_coda_ncstats);
+  coda_sysctl_init();
+#endif 
+
+  init_coda_psdev();
+  
+  if ((status = init_coda_fs()) != 0)
+    {
+      printk("coda: failed in init_coda_fs!\n");
+    }
+  return status;
+}
+
+
+void cleanup_module(void)
+{
+        int err;
+
+        ENTRY;
+
+        unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
+        
+        if ( (err = unregister_filesystem(&coda_fs_type)) != 0 ) {
+                printk("coda: failed to unregister filesystem\n");
+        }
+#if CONFIG_PROC_FS
+        coda_sysctl_clean();
+        proc_unregister(&proc_coda, proc_coda_cache.low_ino);
+        proc_unregister(&proc_coda, proc_coda_ncstats.low_ino);
+	proc_unregister(&proc_root, proc_coda.low_ino);
+#endif 
+}
+
+#endif
+
+
+
diff -u --recursive --new-file v2.1.69/linux/fs/coda/super.c linux/fs/coda/super.c
--- v2.1.69/linux/fs/coda/super.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/coda/super.c	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,612 @@
+/*
+ * Super block/filesystem wide operations
+ *
+ * Peter J. Braam <braam@maths.ox.ac.uk>, 
+ * Michael Callahan <callahan@maths.ox.ac.uk> Aug 1996
+ * Rewritten for Linux 2.1.57 Peter Braam <braam@cs.cmu.edu>
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/unistd.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#include <asm/segment.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_namecache.h>
+
+
+/* VFS super_block ops */
+static struct super_block *coda_read_super(struct super_block *, void *, int);
+static void coda_read_inode(struct inode *);
+static int  coda_notify_change(struct inode *inode, struct iattr *attr);
+static void coda_put_inode(struct inode *);
+static void coda_delete_inode(struct inode *);
+static void coda_put_super(struct super_block *);
+static int coda_statfs(struct super_block *sb, struct statfs *buf, 
+		       int bufsiz);
+
+
+/* helper functions */
+void print_vattr( struct coda_vattr *attr );
+static inline struct coda_sb_info *coda_psinode2sb(struct inode *inode);
+static inline struct vcomm *coda_psinode2vcomm(struct inode *inode);
+static int coda_get_psdev(void *, struct inode **);
+static void coda_vattr_to_iattr(struct inode *, struct coda_vattr *);
+static void coda_iattr_to_vattr(struct iattr *, struct coda_vattr *);
+int coda_fetch_inode(struct inode *, struct coda_vattr *);
+
+extern inline struct vcomm *coda_psdev_vcomm(struct inode *inode);
+extern int coda_cnode_make(struct inode **inode, ViceFid *fid, 
+			   struct super_block *sb);
+extern struct cnode *coda_cnode_alloc(void);
+extern void coda_cnode_free(struct cnode *);
+char *coda_f2s(struct ViceFid *, char *);
+
+extern int cfsnc_initialized;
+extern int coda_debug;
+extern int coda_print_entry;
+
+extern struct inode_operations coda_file_inode_operations;
+extern struct inode_operations coda_dir_inode_operations;
+extern struct inode_operations coda_ioctl_inode_operations;
+extern struct inode_operations coda_symlink_inode_operations;
+/* exported operations */
+struct super_operations coda_super_operations =
+{
+	coda_read_inode,        /* read_inode */
+	NULL,                   /* write_inode */
+	coda_put_inode,	        /* put_inode */
+	coda_delete_inode,      /* delete_inode */
+	coda_notify_change,	/* notify_change */
+	coda_put_super,	        /* put_super */
+	NULL,			/* write_super */
+	coda_statfs,   		/* statfs */
+	NULL			/* remount_fs */
+};
+
+/*
+ * globals
+ */
+struct coda_sb_info coda_super_info[MAX_CODADEVS];
+
+
+
+static struct super_block * coda_read_super(struct super_block *sb, 
+					    void *data, int silent)
+{
+        struct inode *psdev = 0, *root = 0; 
+	struct coda_sb_info *sbi = NULL;
+	struct vcomm *vc;
+        ViceFid fid;
+	kdev_t dev = sb->s_dev;
+        int error;
+	char str[50];
+
+ENTRY;
+        MOD_INC_USE_COUNT; 
+        if (coda_get_psdev(data, &psdev))
+                goto exit;
+
+        vc = coda_psinode2vcomm(psdev);
+        if ( !vc )
+	        goto exit;
+
+	sbi = coda_psinode2sb(psdev);
+	if ( !sbi )
+	        goto exit;
+
+        sbi->sbi_psdev = psdev;
+	sbi->sbi_vcomm = vc;
+
+        lock_super(sb);
+        sb->u.generic_sbp = sbi;
+        sb->s_blocksize = 1024;	/* XXXXX */
+        sb->s_blocksize_bits = 10;
+        sb->s_magic = CODA_SUPER_MAGIC;
+        sb->s_dev = dev;
+        sb->s_op = &coda_super_operations;
+
+	/* get root fid from Venus: this needs the root inode */
+	error = venus_rootfid(sb, &fid);
+
+	if ( error ) {
+	        unlock_super(sb);
+	        printk("coda_read_super: coda_get_rootfid failed with %d\n",
+		   error);
+		goto exit;
+	}	  
+	printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid, str));
+	
+        error = coda_cnode_make(&root, &fid, sb);
+        if ( error || !root ) {
+	    printk("Failure of coda_cnode_make for root: error %d\n", error);
+	    unlock_super(sb);
+	    sb->s_dev = 0;
+	    root = NULL;
+	    goto exit;
+	} 
+
+	printk("coda_read_super: rootinode is %ld dev %d\n", 
+	       root->i_ino, root->i_dev);
+	sbi->sbi_root = root;
+	sb->s_root = d_alloc_root(root, NULL);
+	unlock_super(sb);
+        return sb;
+EXIT;  
+
+exit:
+	MOD_DEC_USE_COUNT;
+	sbi->sbi_vcomm = NULL;
+	sbi->sbi_root = NULL;
+        if (root) {
+                iput(root);
+                coda_cnode_free(ITOC(root));
+        }
+        sb->s_dev = 0;
+        return NULL;
+}
+
+
+static void coda_put_super(struct super_block *sb)
+{
+        struct coda_sb_info *sb_info;
+
+        ENTRY;
+
+        lock_super(sb);
+
+        sb->s_dev = 0;
+	sb_info = coda_sbp(sb);
+	memset(sb_info, 0, sizeof(* sb_info));
+
+        unlock_super(sb);
+        MOD_DEC_USE_COUNT;
+EXIT;
+}
+/* all filling in of inodes postponed until lookup */
+static void coda_read_inode(struct inode *inode)
+{
+        inode->u.generic_ip = NULL;
+	/*	inode->i_blksize = inode->i_sb->s_blocksize;
+	inode->i_mode = 0;
+	inode->i_op = NULL;
+	NFS_CACHEINV(inode); */
+}
+
+static void coda_put_inode(struct inode *inode) 
+{
+        CDEBUG(D_INODE,"ino: %ld, cnp: %x\n", inode->i_ino,
+	       (int) inode->u.generic_ip);
+}
+
+static void coda_delete_inode(struct inode *inode)
+{
+        struct cnode *cnp;
+        struct inode *open_inode;
+
+        ENTRY;
+        CDEBUG(D_SUPER, " inode->ino: %ld, count: %d\n", 
+	       inode->i_ino, inode->i_count);        
+
+	if ( inode->i_ino == CTL_INO ) {
+	        clear_inode(inode);
+		return;
+	}
+
+        cnp = ITOC(inode);
+
+        open_inode = cnp->c_ovp;
+        if ( open_inode ) {
+                CDEBUG(D_SUPER, "DELINO cached file: ino %ld count %d.\n",  
+		       open_inode->i_ino,  open_inode->i_count);
+                cnp->c_ovp = NULL;
+		cnp->c_odentry.d_inode = NULL;
+                iput( open_inode );
+        }
+	inode->u.generic_ip = NULL;
+        coda_cnode_free(cnp);
+        clear_inode(inode);
+EXIT;
+}
+
+static int  coda_notify_change(struct inode *inode, struct iattr *iattr)
+{
+        struct cnode *cnp;
+        struct coda_vattr vattr;
+        int error;
+ENTRY;
+        memset(&vattr, 0, sizeof(vattr)); 
+        cnp = ITOC(inode);
+        CHECK_CNODE(cnp);
+
+        coda_iattr_to_vattr(iattr, &vattr);
+        vattr.va_type = VNON; /* cannot set type */
+	CDEBUG(D_SUPER, "vattr.va_mode %o\n", vattr.va_mode);
+
+        error = venus_setattr(inode->i_sb, &cnp->c_fid, &vattr);
+
+        if ( !error ) {
+	        coda_vattr_to_iattr(inode, &vattr); 
+		cfsnc_zapfid(&(cnp->c_fid));
+        }
+	CDEBUG(D_SUPER, "inode.i_mode %o, error %d\n", 
+	       inode->i_mode, error);
+
+	EXIT;
+        return error;
+}
+
+/*  we need _something_ */
+static int coda_statfs(struct super_block *sb, struct statfs *buf, 
+		       int bufsiz)
+{
+	struct statfs tmp;
+
+#define NB_SFS_SIZ 0x895440
+
+	tmp.f_type = CODA_SUPER_MAGIC;
+	tmp.f_bsize = 1024;
+	tmp.f_blocks = 9000000;
+	tmp.f_bfree = 9000000;
+	tmp.f_bavail = 9000000 ;
+	tmp.f_files = 9000000;
+	tmp.f_ffree = 9000000;
+	tmp.f_namelen = 0;
+	copy_to_user(buf, &tmp, bufsiz);
+	return 0; 
+}
+
+
+/* init_coda: used by filesystems.c to register coda */
+
+struct file_system_type coda_fs_type = {
+   "coda", 0, coda_read_super, NULL
+};
+
+
+
+
+int init_coda_fs(void)
+{
+	return register_filesystem(&coda_fs_type);
+}
+
+
+/* utility functions below */
+static void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr)
+{
+        int inode_type;
+        /* inode's i_dev, i_flags, i_ino are set by iget 
+           XXX: is this all we need ??
+           */
+        switch (attr->va_type) {
+        case VNON:
+                inode_type  = 0;
+                break;
+        case VREG:
+                inode_type = S_IFREG;
+                break;
+        case VDIR:
+                inode_type = S_IFDIR;
+                break;
+        case VLNK:
+                inode_type = S_IFLNK;
+                break;
+        default:
+                inode_type = 0;
+        }
+	inode->i_mode |= inode_type;
+
+	if (attr->va_mode != (u_short) -1)
+	        inode->i_mode = attr->va_mode | inode_type;
+        if (attr->va_uid != -1) 
+	        inode->i_uid = (uid_t) attr->va_uid;
+        if (attr->va_gid != -1)
+	        inode->i_gid = (gid_t) attr->va_gid;
+	if (attr->va_nlink != -1)
+	        inode->i_nlink = attr->va_nlink;
+	if (attr->va_size != -1)
+	        inode->i_size = attr->va_size;
+	/*  XXX This needs further study */
+	/*
+        inode->i_blksize = attr->va_blocksize;
+	inode->i_blocks = attr->va_size/attr->va_blocksize 
+	  + (attr->va_size % attr->va_blocksize ? 1 : 0); 
+	  */
+	if (attr->va_atime.tv_sec != -1) 
+	        inode->i_atime = attr->va_atime.tv_sec;
+	if (attr->va_mtime.tv_sec != -1)
+	        inode->i_mtime = attr->va_mtime.tv_sec;
+        if (attr->va_ctime.tv_sec != -1)
+	        inode->i_ctime = attr->va_ctime.tv_sec;
+}
+/* 
+ * BSD sets attributes that need not be modified to -1. 
+ * Linux uses the valid field to indicate what should be
+ * looked at.  The BSD type field needs to be deduced from linux 
+ * mode.
+ * So we have to do some translations here.
+ */
+
+void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr)
+{
+        umode_t mode;
+        unsigned int valid;
+
+        /* clean out */        
+        vattr->va_mode = (umode_t) -1;
+        vattr->va_uid = (vuid_t) -1; 
+        vattr->va_gid = (vgid_t) -1;
+        vattr->va_size = (off_t) -1;
+	vattr->va_atime.tv_sec = (time_t) -1;
+        vattr->va_mtime.tv_sec  = (time_t) -1;
+	vattr->va_ctime.tv_sec  = (time_t) -1;
+	vattr->va_atime.tv_nsec =  (time_t) -1;
+        vattr->va_mtime.tv_nsec = (time_t) -1;
+	vattr->va_ctime.tv_nsec = (time_t) -1;
+        vattr->va_type = VNON;
+	vattr->va_fileid = (long)-1;
+	vattr->va_gen = (long)-1;
+	vattr->va_bytes = (long)-1;
+	vattr->va_fsid = (long)-1;
+	vattr->va_nlink = (short)-1;
+	vattr->va_blocksize = (long)-1;
+	vattr->va_rdev = (dev_t)-1;
+        vattr->va_flags = 0;
+
+        /* determine the type */
+        mode = iattr->ia_mode;
+                if ( S_ISDIR(mode) ) {
+                vattr->va_type = VDIR; 
+        } else if ( S_ISREG(mode) ) {
+                vattr->va_type = VREG;
+        } else if ( S_ISLNK(mode) ) {
+                vattr->va_type = VLNK;
+        } else {
+                /* don't do others */
+                vattr->va_type = VNON;
+        }
+
+        /* set those vattrs that need change */
+        valid = iattr->ia_valid;
+        if ( valid & ATTR_MODE ) {
+                vattr->va_mode = iattr->ia_mode;
+	}
+        if ( valid & ATTR_UID ) {
+                vattr->va_uid = (vuid_t) iattr->ia_uid;
+	}
+        if ( valid & ATTR_GID ) {
+                vattr->va_gid = (vgid_t) iattr->ia_gid;
+	}
+        if ( valid & ATTR_SIZE ) {
+                vattr->va_size = iattr->ia_size;
+	}
+        if ( valid & ATTR_ATIME ) {
+                vattr->va_atime.tv_sec = iattr->ia_atime;
+                vattr->va_atime.tv_nsec = 0;
+	}
+        if ( valid & ATTR_MTIME ) {
+                vattr->va_mtime.tv_sec = iattr->ia_mtime;
+                vattr->va_mtime.tv_nsec = 0;
+	}
+        if ( valid & ATTR_CTIME ) {
+                vattr->va_ctime.tv_sec = iattr->ia_ctime;
+                vattr->va_ctime.tv_nsec = 0;
+	}
+        
+}
+  
+
+void print_vattr(struct coda_vattr *attr)
+{
+    char *typestr;
+
+    switch (attr->va_type) {
+    case VNON:
+	typestr = "VNON";
+	break;
+    case VREG:
+	typestr = "VREG";
+	break;
+    case VDIR:
+	typestr = "VDIR";
+	break;
+    case VBLK:
+	typestr = "VBLK";
+	break;
+    case VCHR:
+	typestr = "VCHR";
+	break;
+    case VLNK:
+	typestr = "VLNK";
+	break;
+    case VSOCK:
+	typestr = "VSCK";
+	break;
+    case VFIFO:
+	typestr = "VFFO";
+	break;
+    case VBAD:
+	typestr = "VBAD";
+	break;
+    default:
+	typestr = "????";
+	break;
+    }
+
+
+    printk("attr: type %s (%o)  mode %o uid %d gid %d fsid %d rdev %d\n",
+	      typestr, (int)attr->va_type, (int)attr->va_mode, (int)attr->va_uid, 
+	      (int)attr->va_gid, (int)attr->va_fsid, (int)attr->va_rdev);
+    
+    printk("      fileid %d nlink %d size %d blocksize %d bytes %d\n",
+	      (int)attr->va_fileid, (int)attr->va_nlink, 
+	      (int)attr->va_size,
+	      (int)attr->va_blocksize,(int)attr->va_bytes);
+    printk("      gen %ld flags %ld vaflags %d\n",
+	      attr->va_gen, attr->va_flags, attr->va_vaflags);
+    printk("      atime sec %d nsec %d\n",
+	      (int)attr->va_atime.tv_sec, (int)attr->va_atime.tv_nsec);
+    printk("      mtime sec %d nsec %d\n",
+	      (int)attr->va_mtime.tv_sec, (int)attr->va_mtime.tv_nsec);
+    printk("      ctime sec %d nsec %d\n",
+	      (int)attr->va_ctime.tv_sec, (int)attr->va_ctime.tv_nsec);
+}
+
+/*   */
+int coda_fetch_inode (struct inode *inode, struct coda_vattr *attr)
+{
+        struct cnode *cp;
+        int ino, error=0;
+        CDEBUG(D_SUPER, "fetch for ino: %ld\n", inode->i_ino);
+
+        ino = inode->i_ino;
+        if (!ino)
+                printk("coda_fetch_inode: inode called with i_ino = 0 (don't worry)\n");
+
+        inode->i_op = NULL;
+        inode->i_mode = 0;
+
+        cp = ITOC(inode);
+        CHECK_CNODE(cp);
+
+        /* root inode  */
+        if (cp->c_fid.Volume == 0 &&
+            cp->c_fid.Vnode == 0 &&
+            cp->c_fid.Unique == 0) {
+	        inode->i_ino = 1;
+		inode->i_op = NULL;
+		return 0;
+        }
+        
+        if (IS_CTL_FID( &(cp->c_fid) )) {
+                /* This is the special magic control file.  
+		   Venus doesn't want
+                   to hear a GETATTR about this! */
+                inode->i_op = &coda_ioctl_inode_operations;
+                return 0;
+        }
+
+        if ( ! attr ) {
+                printk("coda_fetch_inode: called with NULL vattr, ino %ld\n",
+		       inode->i_ino);
+                return -1; /* XXX */
+        }
+
+        if (coda_debug & D_SUPER ) print_vattr(attr);
+        coda_vattr_to_iattr(inode, attr);
+
+        if (S_ISREG(inode->i_mode))
+                inode->i_op = &coda_file_inode_operations;
+        else if (S_ISDIR(inode->i_mode))
+                inode->i_op = &coda_dir_inode_operations;
+        else if (S_ISLNK(inode->i_mode))
+                inode->i_op = &coda_symlink_inode_operations;
+        else {
+                printk ("coda_read_inode: what kind of inode is this? i_mode = %o\n", inode->i_mode);
+                inode->i_op = NULL;
+        }
+        return error;
+}
+
+static inline struct vcomm *
+coda_psinode2vcomm(struct inode *inode) 
+{
+        
+	unsigned int minor = MINOR(inode->i_rdev);
+CDEBUG(D_PSDEV,"minor %d\n", minor);
+	if ( minor < MAX_CODADEVS ) 
+	      return &(psdev_vcomm[minor]);
+	else
+	      return NULL;
+}
+
+static inline struct coda_sb_info *
+coda_psinode2sb(struct inode *inode) 
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+
+CDEBUG(D_PSDEV,"minor %d\n", minor);
+	if ( minor < MAX_CODADEVS ) 
+	        return &(coda_super_info[minor]);
+	else
+	        return NULL;
+}
+
+static int 
+coda_get_psdev(void *data, struct inode **res_dev)
+{
+        char **psdev_path;
+        struct inode *psdev = 0;
+	struct dentry *ent=NULL;
+
+ 
+	if ( ! data ) { 
+		printk("coda_read_super: no data!\n");
+		goto error;
+	} else {
+		psdev_path = data;
+	}
+        ent = namei((char *) *psdev_path);
+        if (IS_ERR(ent)) {
+		printk("namei error %ld for %d\n", PTR_ERR(ent), 
+		       (int) psdev_path);
+		goto error;
+        }
+	psdev = ent->d_inode;
+        
+
+        if (!S_ISCHR(psdev->i_mode)) {
+		printk("not a character device\n");
+		goto error;
+        }
+CDEBUG(D_PSDEV,"major %d, minor %d, count %d\n", MAJOR(psdev->i_rdev), 
+       MINOR(psdev->i_rdev), psdev->i_count);
+        
+        if (MAJOR(psdev->i_rdev) != CODA_PSDEV_MAJOR) {
+		printk("device %d not a Coda PSDEV device\n", 
+		       MAJOR(psdev->i_rdev));
+		goto error;
+        }
+
+        if (MINOR(psdev->i_rdev) >= MAX_CODADEVS) { 
+		printk("minor %d not an allocated Coda PSDEV\n", 
+		       psdev->i_rdev);
+		goto error;
+        }
+
+        if (psdev->i_count < 1) {
+		printk("coda device minor %d not open (i_count = %d)\n", 
+		       MINOR(psdev->i_rdev), psdev->i_count);
+		goto error;
+        }
+        
+        *res_dev = psdev;
+
+        return 0;
+      
+EXIT;  
+error:
+        return 1;
+}
diff -u --recursive --new-file v2.1.69/linux/fs/coda/symlink.c linux/fs/coda/symlink.c
--- v2.1.69/linux/fs/coda/symlink.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/coda/symlink.c	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,82 @@
+/*
+ * Symlink inode operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ * 
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_namecache.h>
+
+static int coda_readlink(struct inode *inode, char *buffer, int length);
+
+struct inode_operations coda_symlink_inode_operations = {
+	NULL,			/* no file-operations */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	coda_readlink,		/* readlink */
+	NULL,           	/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL,            	/* permission */
+	NULL,                   /* smap */
+	NULL,                   /* update page */
+        NULL                    /* revalidate */
+};
+
+static int coda_readlink(struct inode *inode, char *buffer, int length)
+{
+        int len;
+	int error;
+        char *buf;
+	struct cnode *cp;
+        ENTRY;
+
+        cp = ITOC(inode);
+        CHECK_CNODE(cp);
+
+        /* the maximum length we receive is len */
+        if ( length > CFS_MAXPATHLEN ) 
+	        len = CFS_MAXPATHLEN;
+	else
+	        len = length;
+	CODA_ALLOC(buf, char *, len);
+	if ( !buf ) 
+	        return -ENOMEM;
+	
+	error = venus_readlink(inode->i_sb, &(cp->c_fid), buf, &len);
+
+        CDEBUG(D_INODE, "result %s\n", buf);
+	if (! error) {
+		copy_to_user(buffer, buf, len);
+		put_user('\0', buffer + len);
+		error = len;
+		CODA_FREE(buf, len);
+	}
+	return error;
+}
diff -u --recursive --new-file v2.1.69/linux/fs/coda/sysctl.c linux/fs/coda/sysctl.c
--- v2.1.69/linux/fs/coda/sysctl.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/coda/sysctl.c	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,160 @@
+/* sysctl entries for Coda! */
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <linux/swapctl.h>
+#include <linux/proc_fs.h>
+#include <linux/malloc.h>
+#include <linux/stat.h>
+#include <linux/ctype.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/utsname.h>
+
+#include <linux/coda_namecache.h>
+#include <linux/coda_sysctl.h>
+extern int coda_debug;
+extern int cfsnc_use;
+extern int coda_print_entry;
+extern int cfsnc_flushme;
+extern int cfsnc_procsize;
+extern void cfsnc_flush(void);
+void coda_sysctl_init(void);
+void coda_sysctl_clean(void);
+
+int coda_dointvec(ctl_table *table, int write, struct file *filp,
+                  void *buffer, size_t *lenp);
+
+struct ctl_table_header *fs_table_header, *coda_table_header;
+#define FS_CODA         1       /* Coda file system */
+
+#define CODA_DEBUG  	1	    /* control debugging */
+#define CODA_ENTRY	    2       /* control enter/leave pattern */
+#define CODA_FLUSH      3       /* flush the cache on next lookup */
+#define CODA_MC         4       /* use/do not use the minicache */
+#define CODA_PROCSIZE   5       /* resize the cache on next lookup */
+
+
+
+static ctl_table coda_table[] = {
+	{CODA_DEBUG, "debug", &coda_debug, sizeof(int), 0644, NULL, &coda_dointvec},
+	{CODA_ENTRY, "printentry", &coda_print_entry, sizeof(int), 0644, NULL, &coda_dointvec},
+	{CODA_MC, "minicache", &cfsnc_use, sizeof(int), 0644, NULL, &coda_dointvec},
+	{CODA_FLUSH, "flushme", &cfsnc_flushme, sizeof(int), 0644, NULL, &coda_dointvec},
+	{CODA_PROCSIZE, "resize", &cfsnc_procsize, sizeof(int), 0644, NULL, &coda_dointvec},
+	{ 0 }
+};
+
+
+static ctl_table fs_table[] = {
+       {FS_CODA, "coda",    NULL, 0, 0555, coda_table},
+       {0}
+};
+
+
+
+void coda_sysctl_init()
+{
+	fs_table_header = register_sysctl_table(fs_table, 0);
+/*	coda_table_header = register_sysctl_table(coda_table, 0);*/
+}
+
+void coda_sysctl_clean() {
+	/*unregister_sysctl_table(coda_table_header);*/
+	unregister_sysctl_table(fs_table_header);
+}
+
+int coda_dointvec(ctl_table *table, int write, struct file *filp,
+                  void *buffer, size_t *lenp)
+{
+        int *i, vleft, first=1, len, left, neg, val;
+        #define TMPBUFLEN 20
+        char buf[TMPBUFLEN], *p;
+        
+        if (!table->data || !table->maxlen || !*lenp ||
+            (filp->f_pos && !write)) {
+                *lenp = 0;
+                return 0;
+        }
+        
+        i = (int *) table->data;
+        vleft = table->maxlen / sizeof(int);
+        left = *lenp;
+        
+        for (; left && vleft--; i++, first=0) {
+                if (write) {
+		        while (left) {
+			        char c;
+				if(get_user(c,(char *) buffer))
+					return -EFAULT;
+				if (!isspace(c))
+					break;
+				left--;
+				((char *) buffer)++;
+			}
+                        if (!left)
+                                break;
+                        neg = 0;
+                        len = left;
+                        if (len > TMPBUFLEN-1)
+                                len = TMPBUFLEN-1;
+                        if (copy_from_user(buf, buffer, len))
+			        return -EFAULT;
+                        buf[len] = 0;
+                        p = buf;
+                        if (*p == '-' && left > 1) {
+                                neg = 1;
+                                left--, p++;
+                        }
+                        if (*p < '0' || *p > '9')
+                                break;
+                        val = simple_strtoul(p, &p, 0);
+                        len = p-buf;
+                        if ((len < left) && *p && !isspace(*p))
+                                break;
+                        if (neg)
+                                val = -val;
+                        buffer += len;
+                        left -= len;
+                        *i = val;
+                } else {
+                        p = buf;
+                        if (!first)
+                                *p++ = '\t';
+                        sprintf(p, "%d", *i);
+                        len = strlen(buf);
+                        if (len > left)
+                                len = left;
+                        if (copy_to_user(buffer, buf, len))
+			    return -EFAULT;
+                        left -= len;
+                        buffer += len;
+                }
+        }
+
+        if (!write && !first && left) {
+                if(put_user('\n', (char *) buffer))
+		        return -EFAULT;
+                left--, buffer++;
+        }
+        if (write) {
+                p = (char *) buffer;
+                while (left) {
+			char c;
+			if(get_user(c, p++))
+				return -EFAULT;
+			if (!isspace(c))
+				break;
+			left--;
+		}
+        }
+        if (write && first)
+                return -EINVAL;
+        *lenp -= left;
+        filp->f_pos += *lenp;
+        return 0;
+}
+
+
diff -u --recursive --new-file v2.1.69/linux/fs/coda/upcall.c linux/fs/coda/upcall.c
--- v2.1.69/linux/fs/coda/upcall.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/coda/upcall.c	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,747 @@
+/*
+ * Mostly platform independent upcall operations to Venus:
+ *  -- upcalls
+ *  -- upcall routines
+ *
+ * Linux 2.0 version
+ * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>, 
+ * Michael Callahan <callahan@maths.ox.ac.uk> 
+ * 
+ * Redone for Linux 2.1
+ * Copyright (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon University encourages users of this code to contribute
+ * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
+ */
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#include <asm/segment.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_namecache.h>
+
+
+static vcsize = (sizeof(struct inputArgs) > sizeof(struct outputArgs)) ?
+		 sizeof(struct inputArgs): sizeof(struct outputArgs);
+
+/* the upcalls */
+int venus_rootfid(struct super_block *sb, ViceFid *fidp)
+{
+        struct inputArgs *inp;
+	struct outputArgs *outp;
+	int error=0;
+	int size;
+ENTRY;
+
+        UPARG(vcsize, CFS_ROOT);
+	error = coda_upcall(coda_sbp(sb), VC_IN_NO_DATA, &size, inp);
+	
+	if (error) {
+	        printk("coda_get_rootfid: error %d\n", error);
+	} else {
+	        *fidp = (ViceFid) outp->d.cfs_root.VFid;
+		CDEBUG(D_SUPER, "VolumeId: %lx, VnodeId: %lx.\n",
+		       fidp->Volume, fidp->Vnode);
+	}
+
+	if (inp)  CODA_FREE(inp, VC_IN_NO_DATA);
+        EXIT;
+	return -error;
+}
+
+int venus_getattr(struct super_block *sb, struct ViceFid *fid, 
+		     struct coda_vattr *attr) 
+{
+        struct inputArgs *inp;
+        struct outputArgs *outp;
+        int size, error;
+ENTRY;
+
+	UPARG(vcsize, CFS_GETATTR);
+        inp->d.cfs_getattr.VFid = *fid;
+        error = coda_upcall(coda_sbp(sb), vcsize, &size, inp);
+	
+	if ( !error )
+	        *attr = (struct coda_vattr) outp->d.cfs_getattr.attr;
+
+        if (inp) CODA_FREE(inp, sizeof(struct inputArgs));
+        EXIT;
+        return -error;
+}
+
+int  venus_setattr(struct super_block *sb, struct ViceFid *fid, 
+		      struct coda_vattr *vattr)
+{
+        struct inputArgs *inp;
+        struct outputArgs *outp;
+        int error, size;
+	
+	UPARG(vcsize, CFS_SETATTR);
+
+        inp->d.cfs_setattr.VFid = *fid;
+	inp->d.cfs_setattr.attr = *vattr;
+
+        error = coda_upcall(coda_sbp(sb), vcsize, &size, inp);
+
+        CDEBUG(D_SUPER, " result %ld\n", outp->result); 
+        if ( inp ) CODA_FREE(inp, vcsize);
+        return -error;
+}
+
+int venus_lookup(struct super_block *sb, struct ViceFid *fid, 
+		    const char *name, int length, int * type, 
+		    struct ViceFid *resfid)
+{
+        struct inputArgs *inp;
+	struct outputArgs *outp;
+	int insize, size, error=0, payload_offset;
+
+        insize =  VC_INSIZE(cfs_lookup_in) + CFS_MAXNAMLEN +1;
+	UPARG(insize, CFS_LOOKUP);
+
+        inp->d.cfs_lookup.VFid = *fid;
+        /* send Venus a null terminated string */
+        payload_offset = VC_INSIZE(cfs_lookup_in);
+        inp->d.cfs_lookup.name = (char *) payload_offset;
+        memcpy((char *)inp + payload_offset, name, length);
+        *((char *)inp + payload_offset + length) = '\0';
+
+        size = payload_offset + length + 1;
+        error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+	if ( !error ) {
+	        *resfid = outp->d.cfs_lookup.VFid;
+		*type = outp->d.cfs_lookup.vtype;
+	}
+	if (inp) CODA_FREE(inp, insize);
+		
+	return -error;
+}
+
+
+int venus_release(struct super_block *sb, struct ViceFid *fid, int flags)
+{
+        struct inputArgs *inp;
+	struct outputArgs *outp;
+	int size = sizeof(struct outputArgs);
+	int error = 0;
+
+	CODA_ALLOC(inp, struct inputArgs *, sizeof(struct inputArgs));
+        outp = (struct outputArgs *)inp;
+        INIT_IN(inp, CFS_CLOSE);
+        coda_load_creds(&(inp->cred));
+
+        inp->d.cfs_close.VFid = *fid;
+        inp->d.cfs_close.flags = flags;
+
+        error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+        if (inp) CODA_FREE(inp, sizeof(struct inputArgs));
+        return -error;
+}
+
+int venus_open(struct super_block *sb, struct ViceFid *fid,
+		  int flags, ino_t *ino, dev_t *dev)
+{
+        struct inputArgs *inp = NULL;
+	struct outputArgs *outp = NULL;
+	int size = sizeof(struct inputArgs);
+	int error = 0;
+
+        CODA_ALLOC(inp, struct inputArgs *, sizeof(struct inputArgs));
+        outp = (struct outputArgs *)inp;
+        INIT_IN(inp, CFS_OPEN);
+        coda_load_creds(&(inp->cred));
+
+        inp->d.cfs_open.VFid = *fid;
+        inp->d.cfs_open.flags = flags;
+
+        error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+	if ( !error ) {
+	        *ino = outp->d.cfs_open.inode;
+	        *dev = outp->d.cfs_open.dev;
+	} else {
+	        *ino = 0;
+		*dev = 0;
+	}
+
+        if (inp) 
+                CODA_FREE(inp, sizeof(struct inputArgs));
+
+	return -error;
+}	
+
+int venus_mkdir(struct super_block *sb, struct ViceFid *dirfid, 
+		   const char *name, int length, 
+		   struct ViceFid *newfid, struct coda_vattr *attrs)
+{
+        struct inputArgs *inp;
+        struct outputArgs *outp;
+        int error=0, size, payload_offset;
+
+        payload_offset = VC_INSIZE(cfs_mkdir_in);
+	size = CFS_MAXNAMLEN + payload_offset;
+	UPARG(size, CFS_MKDIR);
+
+        inp->d.cfs_mkdir.VFid = *dirfid;
+        inp->d.cfs_mkdir.attr = *attrs;
+        inp->d.cfs_mkdir.name = (char *) payload_offset;
+
+        /* Venus must get null terminated string */
+        memcpy((char *)inp + payload_offset, name, length);
+        *((char *)inp + payload_offset + length) = '\0';
+        size = payload_offset + length + 1;
+        
+        error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+	*attrs = outp->d.cfs_mkdir.attr;
+	*newfid = outp->d.cfs_mkdir.VFid;
+
+	if (inp) 
+	        CODA_FREE(inp, size);
+	return -error;        
+}
+
+
+int venus_rename(struct super_block *sb, struct ViceFid *old_fid, 
+		 struct ViceFid *new_fid, size_t old_length, 
+		 size_t new_length, const char *old_name, const char *new_name)
+{
+        struct inputArgs *inp;
+        struct outputArgs *outp;
+	int error, offset, size, s;
+	
+	size = 2*CFS_MAXNAMLEN + VC_INSIZE(cfs_rename_in) +8;
+	UPARG(size, CFS_RENAME);
+
+        inp->d.cfs_rename.sourceFid = *old_fid;
+        inp->d.cfs_rename.destFid =  *new_fid;
+
+        offset = VC_INSIZE(cfs_rename_in);
+
+        /* Venus must receive an null terminated string */
+        inp->d.cfs_rename.srcname = (char *)offset;
+        s = ( old_length & ~0x3) +4; /* round up to word boundary */
+        memcpy((char *)inp + offset, old_name, old_length);
+        *((char *)inp + offset + old_length) = '\0';
+
+        /* another null terminated string for Venus */
+        offset += s;
+        inp->d.cfs_rename.destname = (char *)offset;
+        s = ( new_length & ~0x3) +4; /* round up to word boundary */
+        memcpy((char *)inp + offset, new_name, new_length);
+        *((char *)inp + offset + new_length) = '\0';
+
+        size += s;
+        CDEBUG(D_INODE, "destname in packet: %s\n", 
+              (char *)inp + (int) inp->d.cfs_rename.destname);
+        error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+	if (inp) CODA_FREE(inp, size);
+	return -error;
+}
+
+int venus_create(struct super_block *sb, struct ViceFid *dirfid, 
+		    const char *name, int length, int excl, int mode, 
+		    struct ViceFid *newfid, struct coda_vattr *attrs) 
+{
+        struct inputArgs *inp;
+        struct outputArgs *outp;
+        int error=0, size, payload_offset;
+
+        payload_offset = VC_INSIZE(cfs_create_in);
+	size = CFS_MAXNAMLEN + payload_offset;
+	UPARG(size, CFS_CREATE);
+
+        inp->d.cfs_create.VFid = *dirfid;
+        inp->d.cfs_create.attr.va_mode = mode;
+	inp->d.cfs_create.excl = excl;
+        inp->d.cfs_create.mode = mode;
+        inp->d.cfs_create.name = (char *) payload_offset;
+
+        /* Venus must get null terminated string */
+        memcpy((char *)inp + payload_offset, name, length);
+        *((char *)inp + payload_offset + length) = '\0';
+        size = payload_offset + length + 1;
+        
+        error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+	*attrs = outp->d.cfs_create.attr;
+	*newfid = outp->d.cfs_create.VFid;
+
+	if (inp) 
+	        CODA_FREE(inp, size);
+	return -error;        
+}
+
+int venus_rmdir(struct super_block *sb, struct ViceFid *dirfid, 
+		    const char *name, int length)
+{
+        struct inputArgs *inp;
+        struct outputArgs *outp;
+        int error=0, size, payload_offset;
+
+        payload_offset = VC_INSIZE(cfs_rmdir_in);
+	size = CFS_MAXNAMLEN + payload_offset;
+	UPARG(size, CFS_RMDIR);
+
+        inp->d.cfs_rmdir.VFid = *dirfid;
+        inp->d.cfs_rmdir.name = (char *) payload_offset;
+        memcpy((char *)inp + payload_offset, name, size);
+        
+        error = coda_upcall(coda_sbp(sb), size, &size, inp);
+	if ( inp ) 
+	        CODA_FREE(inp, size);
+	return -error;
+}
+
+int venus_remove(struct super_block *sb, struct ViceFid *dirfid, 
+		    const char *name, int length)
+{
+        struct inputArgs *inp;
+        struct outputArgs *outp;
+        int error=0, size, payload_offset;
+
+        payload_offset = VC_INSIZE(cfs_remove_in);
+	size = CFS_MAXNAMLEN + payload_offset;
+	UPARG(size, CFS_REMOVE);
+
+        inp->d.cfs_remove.VFid = *dirfid;
+        inp->d.cfs_remove.name = (char *)payload_offset;
+        memcpy((char *)inp + payload_offset, name, size);
+        
+        error = coda_upcall(coda_sbp(sb), size, &size, inp);
+	if ( inp ) 
+	        CODA_FREE(inp, size);
+	return -error;
+}
+
+int venus_readlink(struct super_block *sb, struct ViceFid *fid, 
+		      char *buffer, int *length)
+{ 
+        int error, size, retlen;
+        char *result;
+        struct inputArgs *inp;
+        struct outputArgs *outp;
+        char *buf=NULL; /*[CFS_MAXNAMLEN + VC_INSIZE(cfs_readlink_in)];*/
+        
+	size = CFS_MAXPATHLEN + VC_INSIZE(cfs_readlink_in);
+	UPARG(size, CFS_READLINK);
+        inp->d.cfs_readlink.VFid = *fid;
+    
+        error =  coda_upcall(coda_sbp(sb), size, &size, inp);
+	
+	if (! error) {
+                retlen = outp->d.cfs_readlink.count;
+		if ( retlen > *length )
+		        retlen = *length;
+		*length = retlen;
+		result =  (char *)outp + (int)outp->d.cfs_readlink.data;
+		memcpy(buffer, result, retlen);
+	}
+        
+        if (inp) CODA_FREE(buf, size);
+        CDEBUG(D_INODE, " result %d\n",error);
+        EXIT;
+        return -error;
+}
+
+int venus_link(struct super_block *sb, struct ViceFid *fid, 
+		  struct ViceFid *dirfid, const char *name, int len )
+{
+        int error, payload_offset, size;
+        struct inputArgs *inp;
+        struct outputArgs *outp;
+
+	size = CFS_MAXNAMLEN + sizeof(struct inputArgs);
+        UPARG(size, CFS_LINK);
+
+        payload_offset = (VC_INSIZE(cfs_link_in));
+        inp->d.cfs_link.sourceFid = *fid;
+        inp->d.cfs_link.destFid = *dirfid;
+        inp->d.cfs_link.tname = (char *)payload_offset;
+
+        /* make sure strings are null terminated */
+        memcpy((char *)inp + payload_offset, name, len);
+        *((char *)inp + payload_offset + len) = '\0';
+        size = payload_offset + len + 1;
+        
+        error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+        if (inp) 
+	        CODA_FREE(inp, size);
+        CDEBUG(D_INODE, " result %d\n",error);
+        EXIT;
+        return -error;
+}
+
+int venus_symlink(struct super_block *sb, struct ViceFid *fid,
+		     const char *name, int len,
+		     const char *symname, int symlen)
+{
+        int error, payload_offset, size, s;
+        struct inputArgs *inp;
+        struct outputArgs *outp;
+        
+
+        /* 
+         * allocate space for regular input, 
+         * plus 1 path and 1 name, plus padding 
+         */        
+	size = sizeof(struct inputArgs) + CFS_MAXNAMLEN + CFS_MAXNAMLEN + 8;
+	UPARG(size, CFS_SYMLINK);
+        
+        /*        inp->d.cfs_symlink.attr = *tva; XXXXXX */ 
+        inp->d.cfs_symlink.VFid = *fid;
+
+        payload_offset = VC_INSIZE(cfs_symlink_in);
+        inp->d.cfs_symlink.srcname =(char*) payload_offset;
+    
+        s = ( symlen  & ~0x3 ) + 4; /* Round up to word boundary. */
+    
+        /* don't forget to copy out the null termination */
+        memcpy((char *)inp + payload_offset, symname, symlen);
+        *((char *)inp + payload_offset + symlen) = '\0';
+        
+        payload_offset += s;
+        inp->d.cfs_symlink.tname = (char *) payload_offset;
+        s = (len & ~0x3) + 4;	/* Round up to word boundary. */
+        memcpy((char *)inp + payload_offset, name, len);
+        *((char *)inp + payload_offset + len) = '\0';
+
+        size = payload_offset + s;
+        error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+        if (inp) 
+	        CODA_FREE(inp, size);
+        CDEBUG(D_INODE, " result %d\n",error);
+        EXIT;
+        return -error;
+}
+
+int venus_access(struct super_block *sb, struct ViceFid *fid, int mask)
+{
+        struct inputArgs *inp;
+        struct outputArgs *outp;
+        int size;
+        int error;
+
+	size = sizeof(struct inputArgs);
+	UPARG(size, CFS_ACCESS);
+
+        inp->d.cfs_access.VFid = *fid;
+        inp->d.cfs_access.flags = mask << 6;
+
+	error = coda_upcall(coda_sbp(sb), size, &size, inp);
+
+	if (inp) CODA_FREE(inp, sizeof(struct inputArgs));
+        EXIT;
+	return -error;
+}
+
+
+int venus_pioctl(struct super_block *sb, struct ViceFid *fid,
+		 unsigned int cmd, struct PioctlData *data)
+{
+        struct inputArgs *inp;
+	struct outputArgs *outp;
+	int size, error = 0;
+	int iocsize;
+	char str[50];
+
+	size = VC_MAXMSGSIZE;
+	UPARG(size, CFS_IOCTL);
+
+        /* build packet for Venus */
+        if (data->vi.in_size > VC_DATASIZE) {
+	        error = EINVAL;
+		goto exit;
+        }
+
+        inp->d.cfs_ioctl.VFid = *fid;
+    
+        /* the cmd field was mutated by increasing its size field to
+         * reflect the path and follow args. We need to subtract that
+         * out before sending the command to Venus.  */
+        inp->d.cfs_ioctl.cmd = (cmd & ~(IOCPARM_MASK << 16));	
+        iocsize = ((cmd >> 16) & IOCPARM_MASK) - sizeof(char *) - sizeof(int);
+        inp->d.cfs_ioctl.cmd |= (iocsize & IOCPARM_MASK) <<	16;	
+    
+        /* in->d.cfs_ioctl.rwflag = flag; */
+        inp->d.cfs_ioctl.len = data->vi.in_size;
+        inp->d.cfs_ioctl.data = (char *)(VC_INSIZE(cfs_ioctl_in));
+     
+        /* get the data out of user space */
+        if ( copy_from_user((char*)inp + (int)inp->d.cfs_ioctl.data,
+			    data->vi.in, data->vi.in_size) ) {
+	        error = EINVAL;
+	        goto exit;
+	}
+        error = coda_upcall(coda_sbp(sb), size, &size, inp);
+        
+        if (error) {
+	        printk("coda_pioctl: Venus returns: %d for %s\n", 
+		       error, coda_f2s(fid, str));
+		goto exit; 
+	}
+        
+	/* Copy out the OUT buffer. */
+        if (outp->d.cfs_ioctl.len > data->vi.out_size) {
+                CDEBUG(D_FILE, "return len %d <= request len %d\n",
+                      outp->d.cfs_ioctl.len, 
+                      data->vi.out_size);
+                error = EINVAL;
+        } else {
+                if (copy_to_user(data->vi.out, 
+				 (char *)outp + (int)outp->d.cfs_ioctl.data, 
+				 data->vi.out_size)) {
+		        error = EINVAL;
+			goto exit;
+		}
+        }
+
+ exit:
+        if (inp) 
+	        CODA_FREE(inp, VC_MAXMSGSIZE);
+	return -error;
+}
+
+/*
+ * coda_upcall and coda_downcall routines.
+ * 
+ */
+
+/* 
+ * coda_upcall will return a POSITIVE error in the case of 
+ * failed communication with Venus _or_ will peek at Venus
+ * reply and return Venus' error, also POSITIVE. 
+ * 
+ */
+int coda_upcall(struct coda_sb_info *sbi, int inSize, int *outSize, 
+		struct inputArgs *buffer) 
+{
+	struct vcomm *vcommp;
+	struct outputArgs *out;
+	struct vmsg *vmp;
+	int error = 0;
+
+ENTRY;
+
+	if (sbi->sbi_vcomm == NULL) {
+                return ENODEV;
+	}
+	vcommp = sbi->sbi_vcomm;
+
+	clstats(((struct inputArgs *)buffer)->opcode);
+
+	if (!vcomm_open(vcommp))
+                return(ENODEV);
+
+	/* Format the request message. */
+	CODA_ALLOC(vmp,struct vmsg *,sizeof(struct vmsg));
+	vmp->vm_data = (void *)buffer;
+	vmp->vm_flags = 0;
+	vmp->vm_inSize = inSize;
+	vmp->vm_outSize = *outSize ? *outSize : inSize;
+	vmp->vm_opcode = ((struct inputArgs *)buffer)->opcode;
+	vmp->vm_unique = ++vcommp->vc_seq;
+        vmp->vm_sleep = NULL;
+	
+	/* Fill in the common input args. */
+	((struct inputArgs *)buffer)->unique = vmp->vm_unique;
+
+	/* Append msg to pending queue and poke Venus. */
+
+	INSQUE(vmp->vm_chain, vcommp->vc_pending);
+	CDEBUG(D_UPCALL, 
+	       "Proc %d wake Venus for(opc,uniq) =(%d,%d) msg at %x.zzz.\n",
+	       current->pid, vmp->vm_opcode, vmp->vm_unique, (int)vmp);
+
+	wake_up_interruptible(&vcommp->vc_waitq);
+	/* We can be interrupted while we wait for Venus to process
+	 * our request.  If the interrupt occurs before Venus has read
+	 * the request, we dequeue and return. If it occurs after the
+	 * read but before the reply, we dequeue, send a signal
+	 * message, and return. If it occurs after the reply we ignore
+	 * it. In no case do we want to restart the syscall.  If it
+	 * was interrupted by a venus shutdown (vcclose), return
+	 * ENODEV.  */
+
+	/* Ignore return, We have to check anyway */
+
+
+	interruptible_sleep_on(&vmp->vm_sleep);
+	CDEBUG(D_UPCALL, 
+	       "..process %d woken up by Venus for vmp at 0x%x, data at %x\n", 
+	       current->pid, (int)vmp, (int)vmp->vm_data);
+	if (vcomm_open(vcommp)) {      /* i.e. Venus is still alive */
+	    /* Op went through, interrupt or not... */
+	    if (vmp->vm_flags & VM_WRITE) {
+		error = 0;
+		out = (struct outputArgs *)vmp->vm_data;
+		error = out->result;
+		CDEBUG(D_UPCALL, 
+		       "upcall: (u,o,r) (%ld, %ld, %ld) out at %x\n", 
+		       out->unique, out->opcode, out->result, (int)out);
+		*outSize = vmp->vm_outSize;
+		goto exit;
+	    } 
+	    if (!(vmp->vm_flags & VM_READ)) { 
+		/* Interrupted before venus read it. */
+		CDEBUG(D_UPCALL, 
+		       "Interrupted before read:(op,un) (%d.%d), flags = %x\n",
+		       vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
+		REMQUE(vmp->vm_chain);
+		error = ERESTARTSYS;
+		goto exit;
+	    } 
+	    if ( vmp->vm_flags & VM_READ) { 	
+		/* interrupted after Venus did its read, send signal */
+		struct inputArgs *dog;
+		struct vmsg *svmp;
+		
+		CDEBUG(D_UPCALL, 
+		       "Sending Venus a signal: op = %d.%d, flags = %x\n",
+		       vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
+		
+		REMQUE(vmp->vm_chain);
+		error = ERESTARTSYS;
+		
+		CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
+		CODA_ALLOC((svmp->vm_data), char *, VC_IN_NO_DATA);
+
+		CDEBUG(D_UPCALL, 
+		       "coda_upcall: enqueing signal msg (%d, %d)\n",
+		       svmp->vm_opcode, svmp->vm_unique);
+		dog = (struct inputArgs *)svmp->vm_data;
+		dog->opcode = CFS_SIGNAL;
+		dog->unique = vmp->vm_unique;
+		
+		svmp->vm_flags = 0;
+		svmp->vm_opcode = dog->opcode;
+		svmp->vm_unique = dog->unique;
+		svmp->vm_inSize = VC_IN_NO_DATA;
+		svmp->vm_outSize = VC_IN_NO_DATA;
+		
+		/* insert at head of queue! */
+		INSQUE(svmp->vm_chain, vcommp->vc_pending);
+		wake_up_interruptible(&vcommp->vc_waitq);
+	    }
+	} else {	/* If venus died i.e. !VC_OPEN(vcommp) */
+	        printk("coda_upcall: Venus dead upon (op,un) (%d.%d) flags %d\n",
+		       vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
+		
+		/* if (! (vmp->vm_flags & VM_WRITE) ) */
+		error = ENODEV;
+	}
+
+ exit:
+	CODA_FREE(vmp, sizeof(struct vmsg));
+	if (error) 
+	        badclstats();
+	return error;
+}
+
+
+
+
+/* 
+ * There are 7 cases where cache invalidations occur.  The semantics
+ *  of each is listed here:
+ *
+ * CFS_FLUSH     -- flush all entries from the name cache and the cnode cache.
+ * CFS_PURGEUSER -- flush all entries from the name cache for a specific user
+ *                  This call is a result of token expiration.
+ *                  Linux does a cfsnc_flush since cred's are not maintained.
+ *
+ * The next arise as the result of callbacks on a file or directory.
+ * CFS_ZAPDIR    -- flush the attributes for the dir from its cnode.
+ *                  Zap all children of this directory from the namecache.
+ * CFS_ZAPFILE   -- flush the cached attributes for a file.
+ * CFS_ZAPVNODE  -- in linux the same as zap file (no creds).
+ *
+ * The next is a result of Venus detecting an inconsistent file.
+ * CFS_PURGEFID  -- flush the attribute for the file
+ *                  If it is a dir (odd vnode), purge its 
+ *                  children from the namecache
+ *                  remove the file from the namecache.
+ *
+ * The last  allows Venus to replace local fids with global ones
+ * during reintegration.
+ *
+ * CFS_REPLACE -- replace one ViceFid with another throughout the name cache */
+
+int coda_downcall(int opcode, struct outputArgs * out)
+{
+
+    /* Handle invalidate requests. */
+    switch (opcode) {
+      case CFS_FLUSH : {
+	      clstats(CFS_FLUSH);
+	      cfsnc_flush();
+	      return(0);
+      }
+      case CFS_PURGEUSER : {
+	      clstats(CFS_PURGEUSER);
+	      cfsnc_flush();
+	      return(0);
+      }
+      case CFS_ZAPDIR : {
+	      ViceFid *fid = &out->d.cfs_zapdir.CodaFid;
+	      clstats(CFS_ZAPDIR);
+	      cfsnc_zapfid(fid);
+	      cfsnc_zapParentfid(fid);     
+	      CDEBUG(D_UPCALL, "zapdir: fid = (%lx.%lx.%lx), \n",fid->Volume, 
+					  fid->Vnode, 
+					  fid->Unique);
+	      return(0);
+      }
+      case CFS_ZAPVNODE : {
+              clstats(CFS_ZAPVNODE);
+	      cfsnc_zapfid(&out->d.cfs_zapvnode.VFid);
+	      return(0);
+      }	
+      case CFS_ZAPFILE : {
+              clstats(CFS_ZAPFILE);
+	      cfsnc_zapfid(&out->d.cfs_zapfile.CodaFid);
+	      return 0;
+      }
+      case CFS_PURGEFID : {
+	      ViceFid *fid = &out->d.cfs_purgefid.CodaFid;
+              clstats(CFS_PURGEFID);
+	      cfsnc_zapfid(fid);
+	      cfsnc_zapParentfid(fid);     
+	      CDEBUG(D_UPCALL, "purgefid: fid = (%lx.%lx.%lx)\n", 
+                                            fid->Volume, fid->Vnode,
+                                            fid->Unique);
+	      return 0;
+      }
+      case CFS_REPLACE : {
+              clstats(CFS_REPLACE);
+	      cfsnc_replace(&out->d.cfs_replace.OldFid, 
+			    &out->d.cfs_replace.NewFid);
+	      return (0);
+      }			   
+    }
+      return 0;
+}
+
+
diff -u --recursive --new-file v2.1.69/linux/fs/exec.c linux/fs/exec.c
--- v2.1.69/linux/fs/exec.c	Tue Dec  2 09:49:39 1997
+++ linux/fs/exec.c	Tue Dec  2 09:35:05 1997
@@ -455,6 +455,21 @@
 }
 	
 /*
+ * If make_private_signals() made a copy of the signal table, decrement the
+ * refcount of the original table, and free it if necessary.
+ * We don't do that in make_private_signals() so that we can back off
+ * in flush_old_exec() if an error occurs after calling make_private_signals().
+ */
+
+static inline void release_old_signals(struct signal_struct * oldsig)
+{
+	if (current->sig == oldsig)
+		return;
+	if (atomic_dec_and_test(&oldsig->count))
+		kfree(oldsig);
+}
+
+/*
  * These functions flushes out all traces of the currently running executable
  * so that a new one can be started
  */
@@ -504,6 +519,9 @@
 	 */
 	retval = exec_mmap();
 	if (retval) goto flush_failed;
+
+	/* This is the point of no return */
+	release_old_signals(oldsig);
 
 	if (current->euid == current->uid && current->egid == current->gid)
 		current->dumpable = 1;
diff -u --recursive --new-file v2.1.69/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.1.69/linux/fs/isofs/inode.c	Tue Dec  2 09:49:40 1997
+++ linux/fs/isofs/inode.c	Tue Dec  2 11:41:45 1997
@@ -33,6 +33,7 @@
  * wrong information within the volume descriptors.
  */
 #define IGNORE_WRONG_MULTI_VOLUME_SPECS
+#define BEQUIET
 
 #ifdef LEAK_CHECK
 static int check_malloc = 0;
@@ -500,7 +501,8 @@
 	s->s_flags |= MS_RDONLY /* | MS_NODEV | MS_NOSUID */;
 
 	brelse(bh);
-
+	
+#ifndef BEQUIET
 	printk(KERN_DEBUG "Max size:%ld   Log zone size:%ld\n",
 	       s->u.isofs_sb.s_max_size,
 	       1UL << s->u.isofs_sb.s_log_zone_size);
@@ -509,6 +511,7 @@
 	       (isonum_733(rootp->extent) + isonum_711(rootp->ext_attr_length))
 			<< s -> u.isofs_sb.s_log_zone_size);
 	if(high_sierra) printk(KERN_DEBUG "Disc in High Sierra format.\n");
+#endif
 	unlock_super(s);
 	/* set up enough so that it can read an inode */
 
@@ -541,7 +544,9 @@
 	      }
 	    }
 	    set_blocksize(dev, opt.blocksize);
+#ifndef BEQUIET
 	    printk(KERN_DEBUG "Forcing new log zone size:%d\n", opt.blocksize);
+#endif
 	  }
 
 #ifdef CONFIG_JOLIET
diff -u --recursive --new-file v2.1.69/linux/include/linux/aztcd.h linux/include/linux/aztcd.h
--- v2.1.69/linux/include/linux/aztcd.h	Tue Dec  2 09:49:40 1997
+++ linux/include/linux/aztcd.h	Wed Dec 31 16:00:00 1969
@@ -1,162 +0,0 @@
-/* $Id: aztcd.h,v 2.60 1997/11/29 09:51:22 root Exp root $
- *
- * Definitions for a AztechCD268 CD-ROM interface
- *	Copyright (C) 1994-98  Werner Zimmermann
- *
- *	based on Mitsumi CDROM driver by Martin Harriss
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  History:	W.Zimmermann adaption to Aztech CD268-01A Version 1.3
- *		October 1994 Email: Werner.Zimmermann@fht-esslingen.de
- */
-
-/* *** change this to set the I/O port address of your CD-ROM drive,
-       set to '-1', if you want autoprobing */
-#define AZT_BASE_ADDR		-1
-
-/* list of autoprobing addresses (not more than 15), last value must be 0x000
-   Note: Autoprobing is only enabled, if AZT_BASE_ADDR is set to '-1' ! */
-#define AZT_BASE_AUTO 		{ 0x320, 0x300, 0x310, 0x330, 0x000 }
-
-/* Uncomment this, if your CDROM is connected to a Soundwave32-soundcard
-   and configure AZT_BASE_ADDR and AZT_SW32_BASE_ADDR */
-/*#define AZT_SW32 1
-*/
-
-#ifdef AZT_SW32 
-#define AZT_SW32_BASE_ADDR      0x220  /*I/O port base address of your soundcard*/
-#endif
-
-/* Set this to 1, if you want your tray to be locked, set to 0 to prevent tray 
-   from locking */
-#define AZT_ALLOW_TRAY_LOCK	1
-
-/*Set this to 1 to allow auto-eject when unmounting a disk, set to 0, if you 
-  don't want the auto-eject feature*/
-#define AZT_AUTO_EJECT          0
-
-/*Set this to 1, if you want to use incompatible ioctls for reading in raw and
-  cooked mode */
-#define AZT_PRIVATE_IOCTLS      1
-
-/*Set this to 1, if you want multisession support by the ISO fs. Even if you set 
-  this value to '0' you can use multisession CDs. In that case the drive's firm-
-  ware will do the appropriate redirection automatically. The CD will then look
-  like a single session CD (but nevertheless all data may be read). Please read 
-  chapter '5.1 Multisession support' in README.aztcd for details. Normally it's 
-  uncritical to leave this setting untouched */
-#define AZT_MULTISESSION        1
-
-/*Uncomment this, if you are using a linux kernel version prior to 2.1.0 */
-/*#define AZT_KERNEL_PRIOR_2_1 */
-
-/*---------------------------------------------------------------------------*/
-/*-----nothing to be configured for normal applications below this line------*/
-
-
-/* Increase this if you get lots of timeouts; if you get kernel panic, replace
-   STEN_LOW_WAIT by STEN_LOW in the source code */
-#define AZT_STATUS_DELAY	400       /*for timer wait, STEN_LOW_WAIT*/
-#define AZT_TIMEOUT		8000000   /*for busy wait STEN_LOW, DTEN_LOW*/
-#define AZT_FAST_TIMEOUT	10000     /*for reading the version string*/
-
-/* number of times to retry a command before giving up */
-#define AZT_RETRY_ATTEMPTS	3
-
-/* port access macros */
-#define CMD_PORT		azt_port
-#define DATA_PORT		azt_port
-#define STATUS_PORT		azt_port+1
-#define MODE_PORT		azt_port+2
-#ifdef  AZT_SW32                
- #define AZT_SW32_INIT          (unsigned int) (0xFF00 & (AZT_BASE_ADDR*16))
- #define AZT_SW32_CONFIG_REG    AZT_SW32_BASE_ADDR+0x16  /*Soundwave32 Config. Register*/
- #define AZT_SW32_ID_REG        AZT_SW32_BASE_ADDR+0x04  /*Soundwave32 ID Version Register*/
-#endif
-
-/* status bits */
-#define AST_CMD_CHECK		0x80		/* 1 = command error */
-#define AST_DOOR_OPEN		0x40		/* 1 = door is open */
-#define AST_NOT_READY		0x20		/* 1 = no disk in the drive */
-#define AST_DSK_CHG		0x02		/* 1 = disk removed or changed */
-#define AST_MODE                0x01            /* 0=MODE1, 1=MODE2 */
-#define AST_MODE_BITS		0x1C		/* Mode Bits */
-#define AST_INITIAL		0x0C		/* initial, only valid ... */
-#define AST_BUSY		0x04		/* now playing, only valid
-						   in combination with mode
-						   bits */
-/* flag bits */
-#define AFL_DATA		0x02		/* data available if low */
-#define AFL_STATUS		0x04		/* status available if low */
-#define AFL_OP_OK		0x01		/* OP_OK command correct*/
-#define AFL_PA_OK		0x02		/* PA_OK parameter correct*/
-#define AFL_OP_ERR		0x05		/* error in command*/
-#define AFL_PA_ERR		0x06		/* error in parameters*/
-#define POLLED			0x04		/* polled mode */
-
-/* commands */
-#define ACMD_SOFT_RESET		0x10		/* reset drive */
-#define ACMD_PLAY_READ		0x20		/* read data track in cooked mode */
-#define ACMD_PLAY_READ_RAW      0x21		/* reading in raw mode*/
-#define ACMD_SEEK               0x30            /* seek msf address*/
-#define ACMD_SEEK_TO_LEADIN     0x31		/* seek to leadin track*/
-#define ACMD_GET_ERROR		0x40		/* get error code */
-#define ACMD_GET_STATUS		0x41		/* get status */
-#define ACMD_GET_Q_CHANNEL      0x50		/* read info from q channel */
-#define ACMD_EJECT		0x60		/* eject/open tray */
-#define ACMD_CLOSE              0x61            /* close tray */
-#define ACMD_LOCK		0x71		/* lock tray closed */
-#define ACMD_UNLOCK		0x72		/* unlock tray */
-#define ACMD_PAUSE		0x80		/* pause */
-#define ACMD_STOP		0x81		/* stop play */
-#define ACMD_PLAY_AUDIO		0x90		/* play audio track */
-#define ACMD_SET_VOLUME		0x93		/* set audio level */
-#define ACMD_GET_VERSION	0xA0		/* get firmware version */
-#define ACMD_SET_DISK_TYPE	0xA1		/* set disk data mode */
-
-#define MAX_TRACKS		104
-
-struct msf {
-	unsigned char	min;
-	unsigned char	sec;
-	unsigned char	frame;
-};
-
-struct azt_Play_msf {
-	struct msf	start;
-	struct msf	end;
-};
-
-struct azt_DiskInfo {
-	unsigned char	first;
-        unsigned char   next;
-	unsigned char	last;
-	struct msf	diskLength;
-	struct msf	firstTrack;
-        unsigned char   multi;
-        struct msf      nextSession;
-        struct msf      lastSession;
-        unsigned char   xa;
-        unsigned char   audio;
-};
-
-struct azt_Toc {
-	unsigned char	ctrl_addr;
-	unsigned char	track;
-	unsigned char	pointIndex;
-	struct msf	trackTime;
-	struct msf	diskTime;
-};
diff -u --recursive --new-file v2.1.69/linux/include/linux/blk.h linux/include/linux/blk.h
--- v2.1.69/linux/include/linux/blk.h	Wed Nov 12 13:34:27 1997
+++ linux/include/linux/blk.h	Tue Dec  2 14:52:20 1997
@@ -36,7 +36,9 @@
 #endif /* IDE_DRIVER */
 
 #define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
-
+#ifdef CONFIG_CDROM
+extern int cdrom_init(void);
+#endif CONFIG_CDROM 
 #ifdef CONFIG_CDU31A
 extern int cdu31a_init(void);
 #endif CONFIG_CDU31A
diff -u --recursive --new-file v2.1.69/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v2.1.69/linux/include/linux/cdrom.h	Mon Apr 14 16:28:25 1997
+++ linux/include/linux/cdrom.h	Tue Dec  2 11:41:45 1997
@@ -1,108 +1,140 @@
 /*
  * -- <linux/cdrom.h>
- * general (not only SCSI) header library for linux CDROM drivers
- * (C) 1992         David Giller rafetmad@oxy.edu
- *     1994, 1995   Eberhard Moenkeberg emoenke@gwdg.de
- *
+ * General header file for linux CD-ROM drivers 
+ * Copyright (C) 1992         David Giller, rafetmad@oxy.edu
+ *               1994, 1995   Eberhard Moenkeberg, emoenke@gwdg.de
+ *               1996         David van Leeuwen, david@tm.tno.nl
+ *               1997         Erik Andersen, andersee@debian.org
  */
-
+ 
 #ifndef	_LINUX_CDROM_H
 #define	_LINUX_CDROM_H
 
+/*******************************************************
+ * As of Linux 2.1.x, all Linux CD-ROM application programs will use this 
+ * (and only this) include file.  It is my hope to provide Linux with
+ * a uniform interface between software accessing CD-ROMs and the various 
+ * device drivers that actually talk to the drives.  There may still be
+ * 23 different kinds of strange CD-ROM drives, but at least there will 
+ * now be one, and only one, Linux CD-ROM interface.
+ *
+ * Additionally, as of Linux 2.1.x, all Linux application programs 
+ * should use the O_NONBLOCK option when opening a CD-ROM device 
+ * for subsequent ioctl commands.  This allows for neat system errors 
+ * like "No medium found" or "Wrong medium type" upon attempting to 
+ * mount or play an empty slot, mount an audio disc, or play a data disc.
+ * Generally, changing an application program to support O_NONBLOCK
+ * is as easy as the following:
+ *       -    drive = open("/dev/cdrom", O_RDONLY);
+ *       +    drive = open("/dev/cdrom", O_RDONLY | O_NONBLOCK);
+ * It is worth the small change.
+ *
+ *  Patches for many common CD programs (provided by David A. van Leeuwen)
+ *  can be found at:  ftp://ftp.gwdg.de/pub/linux/cdrom/drivers/cm206/
+ * 
+ *******************************************************/
+
+/* When a driver supports a certain function, but the cdrom drive we are 
+ * using doesn't, we will return the error EDRIVE_CANT_DO_THIS.  We will 
+ * borrow the "Operation not supported" error from the network folks to 
+ * accomplish this.  Maybe someday we will get a more targeted error code, 
+ * but this will do for now... */
+#define EDRIVE_CANT_DO_THIS  EOPNOTSUPP
+
+/*******************************************************
+ * The CD-ROM IOCTL commands  -- these should be supported by 
+ * all the various cdrom drivers.  For the CD-ROM ioctls, we 
+ * will commandeer byte 0x53, or 'S'.
+ *******************************************************/
+#define CDROMPAUSE		0x5301 /* Pause Audio Operation */ 
+#define CDROMRESUME		0x5302 /* Resume paused Audio Operation */
+#define CDROMPLAYMSF		0x5303 /* Play Audio MSF (struct cdrom_msf) */
+#define CDROMPLAYTRKIND		0x5304 /* Play Audio Track/index 
+                                           (struct cdrom_ti) */
+#define CDROMREADTOCHDR		0x5305 /* Read TOC header 
+                                           (struct cdrom_tochdr) */
+#define CDROMREADTOCENTRY	0x5306 /* Read TOC entry 
+                                           (struct cdrom_tocentry) */
+#define CDROMSTOP		0x5307 /* Stop the cdrom drive */
+#define CDROMSTART		0x5308 /* Start the cdrom drive */
+#define CDROMEJECT		0x5309 /* Ejects the cdrom media */
+#define CDROMVOLCTRL		0x530a /* Control output volume 
+                                           (struct cdrom_volctrl) */
+#define CDROMSUBCHNL		0x530b /* Read subchannel data 
+                                           (struct cdrom_subchnl) */
+#define CDROMREADMODE2		0x530c /* Read CDROM mode 2 data (2336 Bytes) 
+                                           (struct cdrom_read) */
+#define CDROMREADMODE1		0x530d /* Read CDROM mode 1 data (2048 Bytes)
+                                           (struct cdrom_read) */
+#define CDROMREADAUDIO		0x530e /* (struct cdrom_read_audio) */
+#define CDROMEJECT_SW		0x530f /* enable(1)/disable(0) auto-ejecting */
+#define CDROMMULTISESSION	0x5310 /* Obtain the start-of-last-session 
+                                           address of multi session disks 
+                                           (struct cdrom_multisession) */
+#define CDROM_GET_MCN		0x5311 /* Obtain the "Universal Product Code" 
+                                           if available (struct cdrom_mcn) */
+#define CDROM_GET_UPC		CDROM_GET_MCN  /* This one is depricated, 
+                                          but here anyway for compatability */
+#define CDROMRESET		0x5312 /* hard-reset the drive */
+#define CDROMVOLREAD		0x5313 /* Get the drive's volume setting 
+                                          (struct cdrom_volctrl) */
+#define CDROMREADRAW		0x5314	/* read data in raw mode (2352 Bytes)
+                                           (struct cdrom_read) */
+/* 
+ * These ioctls are used only used in aztcd.c and optcd.c
+ */
+#define CDROMREADCOOKED		0x5315	/* read data in cooked mode */
+#define CDROMSEEK		0x5316  /* seek msf address */
+  
 /*
- * some fix numbers
+ * This ioctl is only used by the scsi-cd driver.  
+   It is for playing audio in logical block addressing mode.
  */
-#define CD_MINS             74 /* max. minutes per CD, not really a limit */
-#define CD_SECS             60 /* seconds per minute */
-#define CD_FRAMES           75 /* frames per second */
-
-#define CD_SYNC_SIZE        12 /* 12 sync bytes per raw data frame, not transfered by the drive */
-#define CD_HEAD_SIZE         4 /* header (address) bytes per raw data frame */
-#define CD_SUBHEAD_SIZE      8 /* subheader bytes per raw XA data frame */
-#define CD_XA_HEAD        (CD_HEAD_SIZE+CD_SUBHEAD_SIZE) /* "before data" part of raw XA frame */
-#define CD_XA_SYNC_HEAD   (CD_SYNC_SIZE+CD_XA_HEAD)/* sync bytes + header of XA frame */
-
-#define CD_FRAMESIZE      2048 /* bytes per frame, "cooked" mode */
-#define CD_FRAMESIZE_RAW  2352 /* bytes per frame, "raw" mode */
-/* most drives don't deliver everything: */
-#define CD_FRAMESIZE_RAW1 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE) /* 2340 */
-#define CD_FRAMESIZE_RAW0 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE-CD_HEAD_SIZE) /* 2336 */
-/* Optics drive also has a 'read all' mode: */
-#define CD_FRAMESIZE_RAWER 2646 /* bytes per frame */
-
-#define CD_EDC_SIZE         4 /* bytes EDC per most raw data frame types */
-#define CD_ZERO_SIZE        8 /* bytes zero per yellow book mode 1 frame */
-#define CD_ECC_SIZE       276 /* bytes ECC per most raw data frame types */
-#define CD_XA_TAIL        (CD_EDC_SIZE+CD_ECC_SIZE) /* "after data" part of raw XA frame */
+#define CDROMPLAYBLK		0x5317	/* (struct cdrom_blk) */
 
-#define CD_FRAMESIZE_SUB    96 /* subchannel data "frame" size */
-#define CD_MSF_OFFSET      150 /* MSF numbering offset of first frame */
+/* 
+ * These ioctls are used only used in optcd.c
+ */
+#define CDROMREADALL		0x5318	/* read all 2646 bytes */
+#define CDROMCLOSETRAY		0x5319	/* pendant of CDROMEJECT */
 
-#define CD_CHUNK_SIZE       24 /* lowest-level "data bytes piece" */
-#define CD_NUM_OF_CHUNKS    98 /* chunks per frame */
+/* 
+ * These ioctls are implemented through the uniform CD-ROM driver
+ * They _will_ be adopted by all CD-ROM drivers, when all the CD-ROM
+ * drivers are eventually ported to the uniform CD-ROM driver interface.
+ */
+#define CDROM_SET_OPTIONS	0x5320  /* Set behavior options */
+#define CDROM_CLEAR_OPTIONS	0x5321  /* Clear behavior options */
+#define CDROM_SELECT_SPEED	0x5322  /* Set the CD-ROM speed */
+#define CDROM_SELECT_DISC	0x5323  /* Select disc (for juke-boxes) */
+#define CDROM_MEDIA_CHANGED	0x5325  /* Check is media changed  */
+#define CDROM_DRIVE_STATUS	0x5326  /* Get tray position, etc. */
+#define CDROM_DISC_STATUS	0x5327  /* Get disc type, etc. */
+#define CDROM_CHANGER_NSLOTS    0x5328  /* Get number of slots */
 
-#define CD_FRAMESIZE_XA CD_FRAMESIZE_RAW1 /* obsolete name */
-#define CD_BLOCK_OFFSET    CD_MSF_OFFSET /* obsolete name */
+/* This ioctl is only used by sbpcd at the moment */
+#define CDROMAUDIOBUFSIZ        0x5382	/* set the audio buffer size */
 
-/*
- * the raw frame layout:
- *
- * - audio (red):                  | audio_sample_bytes |
- *                                 |        2352        |
- *
- * - data (yellow, mode1):         | sync - head - data - EDC - zero - ECC |
- *                                 |  12  -   4  - 2048 -  4  -   8  - 276 |
- *
- * - data (yellow, mode2):         | sync - head - data |
- *                                 |  12  -   4  - 2336 |
- *
- * - XA data (green, mode2 form1): | sync - head - sub - data - EDC - ECC |
- *                                 |  12  -   4  -  8  - 2048 -  4  - 276 |
- *
- * - XA data (green, mode2 form2): | sync - head - sub - data - EDC |
- *                                 |  12  -   4  -  8  - 2324 -  4  |
- *
- * Sector types (format) - Forces the CD-ROM to read the specified sector type,
- *                         attempt to read any other format ends with an error
- *
- * format       sector type             user data size (bytes)
- * -----------------------------------------------------------------------------
- *   1     CD DA (Red Book)             2352    (CD_FRAMESIZE_RAW)
- *   2     Mode1 Form1 (Yellow Book)    2048    (CD_FRAMESIZE)
- *   3     Mode1 Form2 (Yellow Book)    2336    (CD_FRAMESIZE_RAW0)
- *   4     Mode2 Form1 (Green Book)     2048    (CD_FRAMESIZE)
- *   5     Mode2 Form2 (Green Book)     2328    (2324+4 spare bytes)
- *
- *
- * Data Selection Field (pc.c[9]) - This value controls the actual number
- *                                  of bytes transferred from the CD-ROM
- *
- * bit      7       6        5        4        3       2        1        0
- * ----------------------------------------------------------------------------
- *     |  Sync  |   Sub  | Header |  User  | EDC & | Error |  Block  | reserved
- *     |        | Header |        |  Data  |  ECC  | Flags |  Error  |
- *     \-------------------------------------------/\---------------/
- *                           |                              |
- *                           V                              V
- *                   stored on CD (2532)    generated during error correction
- *
- * The maximal number of bytes returned by CD-ROM is 2646 (CD_FRAMESIZE_RAWER),
- * when pc.c[9] = 0xfa.
- *
- */
- 
-
-/*
+/*******************************************************
  * CDROM IOCTL structures
- */
+ *******************************************************/
 
-struct cdrom_blk 
+/* Address in MSF format */
+struct cdrom_msf0		
 {
-	unsigned from;
-	unsigned short len;
+	u_char	minute;
+	u_char	second;
+	u_char	frame;
 };
 
+/* Address in either MSF or logical format */
+union cdrom_addr		
+{
+	struct cdrom_msf0	msf;
+	int			lba;
+};
 
+/* This struct is used by the CDROMPLAYMSF ioctl */ 
 struct cdrom_msf 
 {
 	u_char	cdmsf_min0;	/* start minute */
@@ -113,6 +145,7 @@
 	u_char	cdmsf_frame1;	/* end frame */
 };
 
+/* This struct is used by the CDROMPLAYTRKIND ioctl */
 struct cdrom_ti 
 {
 	u_char	cdti_trk0;	/* start track */
@@ -121,51 +154,23 @@
 	u_char	cdti_ind1;	/* end index */
 };
 
+/* This struct is used by the CDROMREADTOCHDR ioctl */
 struct cdrom_tochdr 	
 {
 	u_char	cdth_trk0;	/* start track */
 	u_char	cdth_trk1;	/* end track */
 };
 
-struct cdrom_msf0		/* address in MSF format */
-{
-	u_char	minute;
-	u_char	second;
-	u_char	frame;
-};
-
-union cdrom_addr		/* address in either MSF or logical format */
-{
-	struct cdrom_msf0	msf;
-	int			lba;
-};
-
-struct cdrom_tocentry 
+/* This struct is used by the CDROMVOLCTRL and CDROMVOLREAD ioctls */
+struct cdrom_volctrl
 {
-	u_char	cdte_track;
-	u_char	cdte_adr	:4;
-	u_char	cdte_ctrl	:4;
-	u_char	cdte_format;
-	union cdrom_addr cdte_addr;
-	u_char	cdte_datamode;
+	u_char	channel0;
+	u_char	channel1;
+	u_char	channel2;
+	u_char	channel3;
 };
 
-/*
- * CD-ROM address types (cdrom_tocentry.cdte_format)
- */
-#define	CDROM_LBA 0x01 /* "logical block": first frame is #0 */
-#define	CDROM_MSF 0x02 /* "minute-second-frame": binary, not bcd here! */
-
-/*
- * bit to tell whether track is data or audio (cdrom_tocentry.cdte_ctrl)
- */
-#define	CDROM_DATA_TRACK	0x04
-
-/*
- * The leadout track is always 0xAA, regardless of # of tracks on disc
- */
-#define	CDROM_LEADOUT	0xAA
-
+/* This struct is used by the CDROMSUBCHNL ioctl */
 struct cdrom_subchnl 
 {
 	u_char	cdsc_format;
@@ -178,28 +183,19 @@
 	union cdrom_addr cdsc_reladdr;
 };
 
-struct cdrom_mcn {
-  u_char medium_catalog_number[14]; /* 13 ASCII digits, null-terminated */
-};
-
-/*
- * audio states (from SCSI-2, but seen with other drives, too)
- */
-#define	CDROM_AUDIO_INVALID	0x00	/* audio status not supported */
-#define	CDROM_AUDIO_PLAY	0x11	/* audio play operation in progress */
-#define	CDROM_AUDIO_PAUSED	0x12	/* audio play operation paused */
-#define	CDROM_AUDIO_COMPLETED	0x13	/* audio play successfully completed */
-#define	CDROM_AUDIO_ERROR	0x14	/* audio play stopped due to error */
-#define	CDROM_AUDIO_NO_STATUS	0x15	/* no current audio status to return */
 
-struct cdrom_volctrl
+/* This struct is used by the CDROMREADTOCENTRY ioctl */
+struct cdrom_tocentry 
 {
-	u_char	channel0;
-	u_char	channel1;
-	u_char	channel2;
-	u_char	channel3;
+	u_char	cdte_track;
+	u_char	cdte_adr	:4;
+	u_char	cdte_ctrl	:4;
+	u_char	cdte_format;
+	union cdrom_addr cdte_addr;
+	u_char	cdte_datamode;
 };
 
+/* This struct is used by the CDROMREADMODE1, and CDROMREADMODE2 ioctls */
 struct cdrom_read      
 {
 	int	cdread_lba;
@@ -207,269 +203,217 @@
 	int	cdread_buflen;
 };
 
-/*
- * extensions for transferring audio frames
- * currently used by sbpcd.c, cdu31a.c, ide-cd.c
- */
+/* This struct is used by the CDROMREADAUDIO ioctl */
 struct cdrom_read_audio
 {
 	union cdrom_addr addr; /* frame address */
-	u_char addr_format; /* CDROM_LBA or CDROM_MSF */
-	int nframes; /* number of 2352-byte-frames to read at once, limited by the drivers */
-	u_char *buf; /* frame buffer (size: nframes*2352 bytes) */
+	u_char addr_format;    /* CDROM_LBA or CDROM_MSF */
+	int nframes;           /* number of 2352-byte-frames to read at once */
+	u_char *buf;           /* frame buffer (size: nframes*2352 bytes) */
 };
 
-/*
- * this has to be the "arg" of the CDROMMULTISESSION ioctl
- * for obtaining multi session info.
- * The returned "addr" is valid only if "xa_flag" is true.
- */
+/* This struct is used with the CDROMMULTISESSION ioctl */
 struct cdrom_multisession
 {
-	union cdrom_addr addr; /* frame address: start-of-last-session (not the new "frame 16"!)*/
-	u_char xa_flag; /* 1: "is XA disk" */
-	u_char addr_format; /* CDROM_LBA or CDROM_MSF */
+	union cdrom_addr addr; /* frame address: start-of-last-session 
+	                           (not the new "frame 16"!).  Only valid
+	                           if the "xa_flag" is true. */
+	u_char xa_flag;        /* 1: "is XA disk" */
+	u_char addr_format;    /* CDROM_LBA or CDROM_MSF */
 };
 
-#ifdef FIVETWELVE
-#define	CDROM_MODE1_SIZE	512
-#else
-#define	CDROM_MODE1_SIZE	2048
-#endif FIVETWELVE
-#define	CDROM_MODE2_SIZE	2336
-
-/*
- * CD-ROM IOCTL commands
- * For IOCTL calls, we will commandeer byte 0x53, or 'S'.
- */
-
-#define CDROMPAUSE		0x5301
-#define CDROMRESUME		0x5302
-#define CDROMPLAYMSF		0x5303	/* (struct cdrom_msf) */
-#define CDROMPLAYTRKIND		0x5304	/* (struct cdrom_ti) */
-
-#define CDROMREADTOCHDR		0x5305	/* (struct cdrom_tochdr) */
-#define CDROMREADTOCENTRY	0x5306	/* (struct cdrom_tocentry) */
-
-#define CDROMSTOP		0x5307	/* stop the drive motor	*/
-#define CDROMSTART		0x5308	/* turn the motor on */
-
-#define CDROMEJECT		0x5309	/* eject CD-ROM media */
-
-#define CDROMVOLCTRL		0x530a	/* (struct cdrom_volctrl) */
-
-#define CDROMSUBCHNL		0x530b	/* (struct cdrom_subchnl) */
-
-#define CDROMREADMODE2		0x530c	/* (struct cdrom_read) */
-                                          /* read type-2 data */
+/* This struct is used with the CDROM_GET_MCN ioctl.  
+ * Very few audio discs actually have Universal Product Code information, 
+ * which should just be the Medium Catalog Number on the box.  Also note 
+ * that the way the codeis written on CD is _not_ uniform across all discs!
+ */  
+struct cdrom_mcn 
+{
+  u_char medium_catalog_number[14]; /* 13 ASCII digits, null-terminated */
+};
 
-#define CDROMREADMODE1		0x530d	/* (struct cdrom_read) */
-                                          /* read type-1 data */
+/* This is used by the CDROMPLAYBLK ioctl */
+struct cdrom_blk 
+{
+	unsigned from;
+	unsigned short len;
+};
 
-#define CDROMREADAUDIO		0x530e	/* (struct cdrom_read_audio) */
 
 /*
- * enable (1) / disable (0) auto-ejecting
- */
-#define CDROMEJECT_SW		0x530f	/* arg: 0 or 1 */
- 
-/*
- * obtain the start-of-last-session address of multi session disks
- */
-#define CDROMMULTISESSION	0x5310	/* (struct cdrom_multisession) */
+ * A CD-ROM physical sector size is 2048, 2052, 2056, 2324, 2332, 2336, 
+ * 2340, or 2352 bytes long.  
 
-/*
- * obtain the "universal product code" number
- * (only some data disks have it coded)
+*         Sector types of the standard CD-ROM data formats:
+ *
+ * format   sector type               user data size (bytes)
+ * -----------------------------------------------------------------------------
+ *   1     (Red Book)    CD-DA          2352    (CD_FRAMESIZE_RAW)
+ *   2     (Yellow Book) Mode1 Form1    2048    (CD_FRAMESIZE)
+ *   3     (Yellow Book) Mode1 Form2    2336    (CD_FRAMESIZE_RAW0)
+ *   4     (Green Book)  Mode2 Form1    2048    (CD_FRAMESIZE)
+ *   5     (Green Book)  Mode2 Form2    2328    (2324+4 spare bytes)
+ *
+ *
+ *       The layout of the standard CD-ROM data formats:
+ * -----------------------------------------------------------------------------
+ * - audio (red):                  | audio_sample_bytes |
+ *                                 |        2352        |
+ *
+ * - data (yellow, mode1):         | sync - head - data - EDC - zero - ECC |
+ *                                 |  12  -   4  - 2048 -  4  -   8  - 276 |
+ *
+ * - data (yellow, mode2):         | sync - head - data |
+ *                                 |  12  -   4  - 2336 |
+ *
+ * - XA data (green, mode2 form1): | sync - head - sub - data - EDC - ECC |
+ *                                 |  12  -   4  -  8  - 2048 -  4  - 276 |
+ *
+ * - XA data (green, mode2 form2): | sync - head - sub - data - Spare |
+ *                                 |  12  -   4  -  8  - 2324 -  4    |
+ *
  */
-#define CDROM_GET_UPC		0x5311	/* 8 bytes returned */
 
-#define CDROMRESET		0x5312	/* hard-reset the drive */
-#define CDROMVOLREAD		0x5313	/* let the drive tell its volume setting */
-					/* (struct cdrom_volctrl) */
+/* Some generally useful CD-ROM information -- mostly based on the above */
+#define CD_MINS              74 /* max. minutes per CD, not really a limit */
+#define CD_SECS              60 /* seconds per minute */
+#define CD_FRAMES            75 /* frames per second */
+#define CD_SYNC_SIZE         12 /* 12 sync bytes per raw data frame */
+#define CD_MSF_OFFSET       150 /* MSF numbering offset of first frame */
+#define CD_CHUNK_SIZE        24 /* lowest-level "data bytes piece" */
+#define CD_NUM_OF_CHUNKS     98 /* chunks per frame */
+#define CD_FRAMESIZE_SUB     96 /* subchannel data "frame" size */
+#define CD_HEAD_SIZE          4 /* header (address) bytes per raw data frame */
+#define CD_SUBHEAD_SIZE       8 /* subheader bytes per raw XA data frame */
+#define CD_EDC_SIZE           4 /* bytes EDC per most raw data frame types */
+#define CD_ZERO_SIZE          8 /* bytes zero per yellow book mode 1 frame */
+#define CD_ECC_SIZE         276 /* bytes ECC per most raw data frame types */
+#define CD_FRAMESIZE       2048 /* bytes per frame, "cooked" mode */
+#define CD_FRAMESIZE_RAW   2352 /* bytes per frame, "raw" mode */
+#define CD_FRAMESIZE_RAWER 2646 /* The maximum possible returned bytes */ 
+/* most drives don't deliver everything: */
+#define CD_FRAMESIZE_RAW1 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE) /*2340*/
+#define CD_FRAMESIZE_RAW0 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE-CD_HEAD_SIZE) /*2336*/
 
-/* 
- * these ioctls are used in aztcd.c and optcd.c
- */
-#define CDROMREADRAW		0x5314	/* read data in raw mode */
-#define CDROMREADCOOKED		0x5315	/* read data in cooked mode */
-#define CDROMSEEK		0x5316  /* seek msf address */
-  
-/*
- * for playing audio in logical block addressing mode
- */
-#define CDROMPLAYBLK		0x5317	/* (struct cdrom_blk) */
+#define CD_XA_HEAD        (CD_HEAD_SIZE+CD_SUBHEAD_SIZE) /* "before data" part of raw XA frame */
+#define CD_XA_TAIL        (CD_EDC_SIZE+CD_ECC_SIZE) /* "after data" part of raw XA frame */
+#define CD_XA_SYNC_HEAD   (CD_SYNC_SIZE+CD_XA_HEAD) /* sync bytes + header of XA frame */
 
-/* 
- * these ioctls are used in optcd.c
- */
-#define CDROMREADALL		0x5318	/* read all 2646 bytes */
-#define CDROMCLOSETRAY		0x5319	/* pendant of CDROMEJECT */
+/* CD-ROM address types (cdrom_tocentry.cdte_format) */
+#define	CDROM_LBA 0x01 /* "logical block": first frame is #0 */
+#define	CDROM_MSF 0x02 /* "minute-second-frame": binary, not bcd here! */
 
+/* bit to tell whether track is data or audio (cdrom_tocentry.cdte_ctrl) */
+#define	CDROM_DATA_TRACK	0x04
 
-/*
- * CD-ROM-specific SCSI command opcodes
- */
+/* The leadout track is always 0xAA, regardless of # of tracks on disc */
+#define	CDROM_LEADOUT		0xAA
 
-/*
- * Group 2 (10-byte).  All of these are called 'optional' by SCSI-II.
- */
+/* audio states (from SCSI-2, but seen with other drives, too) */
+#define	CDROM_AUDIO_INVALID	0x00	/* audio status not supported */
+#define	CDROM_AUDIO_PLAY	0x11	/* audio play operation in progress */
+#define	CDROM_AUDIO_PAUSED	0x12	/* audio play operation paused */
+#define	CDROM_AUDIO_COMPLETED	0x13	/* audio play successfully completed */
+#define	CDROM_AUDIO_ERROR	0x14	/* audio play stopped due to error */
+#define	CDROM_AUDIO_NO_STATUS	0x15	/* no current audio status to return */
+
+/* CD-ROM-specific SCSI command opcodes */
 #define SCMD_READ_TOC		0x43	/* read table of contents */
 #define SCMD_PLAYAUDIO_MSF	0x47	/* play data at time offset */
 #define SCMD_PLAYAUDIO_TI	0x48	/* play data at track/index */
 #define SCMD_PAUSE_RESUME	0x4B	/* pause/resume audio */
 #define SCMD_READ_SUBCHANNEL	0x42	/* read SC info on playing disc */
 #define SCMD_PLAYAUDIO10	0x45	/* play data at logical block */
-#define SCMD_READ_HEADER	0x44	/* read TOC header */
-
-/*
- * Group 5
- */
-#define SCMD_PLAYAUDIO12	0xA5 	/* play data at logical block */
-#define SCMD_PLAYTRACK_REL12	0xA9	/* play track at relative offset */
-
-/*
- * Group 6 Commands
- */
-#define SCMD_CD_PLAYBACK_CONTROL 0xC9	/* Sony vendor-specific audio */
-#define SCMD_CD_PLAYBACK_STATUS 0xC4	/* control opcodes */
-
-/*
- * CD-ROM capacity structure.
- */
-struct scsi_capacity 
-{
-	u_long	capacity;
-	u_long	lbasize;
-};
-
-/*
- * CD-ROM MODE_SENSE/MODE_SELECT parameters
- */
-#define ERR_RECOVERY_PARMS	0x01
-#define DISCO_RECO_PARMS	0x02
-#define FORMAT_PARMS		0x03
-#define GEOMETRY_PARMS		0x04
-#define CERTIFICATION_PARMS	0x06
-#define CACHE_PARMS		0x38
-
-/*
- * standard mode-select header prepended to all mode-select commands
- */
-struct ccs_modesel_head 
-{
-	u_char	_r1;	/* reserved */
-	u_char	medium;	/* device-specific medium type */
-	u_char 	_r2;	/* reserved */
-	u_char	block_desc_length; /* block descriptor length */
-	u_char	density; /* device-specific density code */
-	u_char	number_blocks_hi; /* number of blocks in this block desc */
-	u_char	number_blocks_med;
-	u_char	number_blocks_lo;
-	u_char	_r3;
-	u_char	block_length_hi; /* block length for blocks in this desc */
-	u_char	block_length_med;
-	u_char	block_length_lo;
-};
-
-/*
- * error recovery parameters
- */
-struct ccs_err_recovery 
-{
-	u_char	_r1 : 2;	/* reserved */
-	u_char	page_code : 6;	/* page code */
-	u_char	page_length;	/* page length */
-	u_char	awre	: 1;	/* auto write realloc enabled */
-	u_char	arre	: 1;	/* auto read realloc enabled */
-	u_char	tb	: 1;	/* transfer block */
-	u_char 	rc	: 1;	/* read continuous */
-	u_char	eec	: 1;	/* enable early correction */
-	u_char	per	: 1;	/* post error */
-	u_char	dte	: 1;	/* disable transfer on error */
-	u_char	dcr	: 1;	/* disable correction */
-	u_char	retry_count;	/* error retry count */
-	u_char	correction_span; /* largest recov. to be attempted, bits */
-	u_char	head_offset_count; /* head offset (2's C) for each retry */
-	u_char	strobe_offset_count; /* data strobe */
-	u_char	recovery_time_limit; /* time limit on recovery attempts	*/
-};
-
-/*
- * disco/reco parameters
- */
-struct ccs_disco_reco 
-{
-	u_char	_r1	: 2;	/* reserved */
-	u_char	page_code : 6;	/* page code */
-	u_char	page_length;	/* page length */
-	u_char	buffer_full_ratio; /* write buffer reconnect threshold */
-	u_char	buffer_empty_ratio; /* read */
-	u_short	bus_inactivity_limit; /* limit on bus inactivity time */
-	u_short	disconnect_time_limit; /* minimum disconnect time */
-	u_short	connect_time_limit; /* minimum connect time */
-	u_short	_r2;		/* reserved */
-};
-
-/*
- * drive geometry parameters
- */
-struct ccs_geometry 
-{
-	u_char	_r1	: 2;	/* reserved */
-	u_char	page_code : 6;	/* page code */
-	u_char	page_length;	/* page length */
-	u_char	cyl_ub;		/* #cyls */
-	u_char	cyl_mb;
-	u_char	cyl_lb;
-	u_char	heads;		/* #heads */
-	u_char	precomp_cyl_ub;	/* precomp start */
-	u_char	precomp_cyl_mb;
-	u_char	precomp_cyl_lb;
-	u_char	current_cyl_ub;	/* reduced current start */
-	u_char	current_cyl_mb;
-	u_char	current_cyl_lb;
-	u_short	step_rate;	/* stepping motor rate */
-	u_char	landing_cyl_ub;	/* landing zone */
-	u_char	landing_cyl_mb;
-	u_char	landing_cyl_lb;
-	u_char  _r2;
-	u_char	_r3;
-	u_char	_r4;
-};
 
-/*
- * cache parameters
- */
-struct ccs_cache 
-{
-	u_char	_r1	: 2;	/* reserved */
-	u_char	page_code : 6;	/* page code */
-	u_char	page_length;	/* page length */
-	u_char	mode;		/* cache control byte */
-	u_char	threshold;	/* prefetch threshold */
-	u_char	max_prefetch;	/* maximum prefetch size */
-	u_char	max_multiplier;	/* maximum prefetch multiplier */
-	u_char	min_prefetch;	/* minimum prefetch size */
-	u_char	min_multiplier;	/* minimum prefetch multiplier */
-	u_char	_r2[8];
-};
+/* capability flags used with the uniform CD-ROM driver */ 
+#define CDC_CLOSE_TRAY		0x1     /* caddy systems _can't_ close */
+#define CDC_OPEN_TRAY		0x2     /* but _can_ eject.  */
+#define CDC_LOCK		0x4     /* disable manual eject */
+#define CDC_SELECT_SPEED 	0x8     /* programmable speed */
+#define CDC_SELECT_DISC		0x10    /* select disc from juke-box */
+#define CDC_MULTI_SESSION 	0x20    /* read sessions>1 */
+#define CDC_MCN			0x40    /* Medium Catalog Number */
+#define CDC_MEDIA_CHANGED 	0x80    /* media changed */
+#define CDC_PLAY_AUDIO		0x100   /* audio functions */
+#define CDC_RESET               0x200   /* hard reset device */
+#define CDC_IOCTLS              0x400   /* driver has non-standard ioctls */
+#define CDC_DRIVE_STATUS        0x800   /* driver implements drive status */
+
+/* drive status possibilities used with the uniform CD-ROM driver */
+#define CDS_NO_INFO		0               /* if not implemented */
+#define CDS_NO_DISC		1
+#define CDS_TRAY_OPEN		2
+#define CDS_DRIVE_NOT_READY	3
+#define CDS_DISC_OK		4
+
+/* disc status possibilities, other than CDS_NO_DISC and CDS_NO_INFO */
+#define CDS_AUDIO		100
+#define CDS_DATA_1		101
+#define CDS_DATA_2		102
+#define CDS_XA_2_1		103
+#define CDS_XA_2_2		104
+
+/* User-configurable behavior options for the uniform CD-ROM driver */
+#define CDO_AUTO_CLOSE		0x1     /* close tray on first open() */
+#define CDO_AUTO_EJECT		0x2     /* open tray on last release() */
+#define CDO_USE_FFLAGS		0x4     /* use O_NONBLOCK information on open */
+#define CDO_LOCK		0x8     /* lock tray on open files */
+#define CDO_CHECK_TYPE		0x10    /* check type on open for data */
+
+/* Special codes used when specifying changer slots. */
+#define CDSL_NONE       	((int) (~0U>>1)-1)
+#define CDSL_CURRENT    	((int) (~0U>>1))
+
+#ifdef __KERNEL__
+/* Uniform cdrom data structures for cdrom.c */
+struct cdrom_device_info {
+	struct cdrom_device_ops  *ops;  /* link to device_ops */
+	struct cdrom_device_info *next; /* next device_info for this major */
+	void *handle;		        /* driver-dependent data */
+/* specifications */
+        kdev_t dev;	                /* device number */
+	int mask;                       /* mask of capability: disables them */
+	int speed;			/* maximum speed for reading data */
+	int capacity;			/* number of discs in jukebox */
+/* device-related storage */
+	int options : 30;               /* options flags */
+	unsigned mc_flags : 2;          /* media change buffer flags */
+    	int use_count;                  /* number of times device opened */
+    	char name[20];                  /* name of the device type */
+
+};
+
+struct cdrom_device_ops {
+/* routines */
+	int (*open) (struct cdrom_device_info *, int);
+	void (*release) (struct cdrom_device_info *);
+	int (*drive_status) (struct cdrom_device_info *, int);
+	int (*media_changed) (struct cdrom_device_info *, int);
+	int (*tray_move) (struct cdrom_device_info *, int);
+	int (*lock_door) (struct cdrom_device_info *, int);
+	int (*select_speed) (struct cdrom_device_info *, int);
+	int (*select_disc) (struct cdrom_device_info *, int);
+	int (*get_last_session) (struct cdrom_device_info *,
+				 struct cdrom_multisession *);
+	int (*get_mcn) (struct cdrom_device_info *,
+			struct cdrom_mcn *);
+	/* hard reset device */
+	int (*reset) (struct cdrom_device_info *);
+	/* play stuff */
+	int (*audio_ioctl) (struct cdrom_device_info *,unsigned int, void *);
+	/* dev-specific */
+ 	int (*dev_ioctl) (struct cdrom_device_info *,
+			  unsigned int, unsigned long);
+/* driver specifications */
+	const int capability;   /* capability flags */
+	int n_minors;           /* number of active minor devices */
+};
+
+/* the general file operations structure: */
+extern struct file_operations cdrom_fops;
+
+extern int register_cdrom(struct cdrom_device_info *cdi);
+extern int unregister_cdrom(struct cdrom_device_info *cdi);
+#endif  /* End of kernel only stuff */ 
 
 #endif  _LINUX_CDROM_H
-/*==========================================================================*/
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file. 
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff -u --recursive --new-file v2.1.69/linux/include/linux/cdu31a.h linux/include/linux/cdu31a.h
--- v2.1.69/linux/include/linux/cdu31a.h	Wed Oct 25 11:54:57 1995
+++ linux/include/linux/cdu31a.h	Wed Dec 31 16:00:00 1969
@@ -1,389 +0,0 @@
-/*
- * Definitions for a Sony interface CDROM drive.
- *
- * Corey Minyard (minyard@wf-rch.cirr.com)
- *
- *  Copyright (C) 1993  Corey Minyard
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-/*
- * General defines.
- */
-#define SONY_XA_DISK_TYPE 0x20
-
-/*
- * Offsets (from the base address) and bits for the various write registers
- * of the drive.
- */
-#define SONY_CMD_REG_OFFSET     0
-#define SONY_PARAM_REG_OFFSET   1
-#define SONY_WRITE_REG_OFFSET   2
-#define SONY_CONTROL_REG_OFFSET 3
-#       define SONY_ATTN_CLR_BIT        0x01
-#       define SONY_RES_RDY_CLR_BIT     0x02
-#       define SONY_DATA_RDY_CLR_BIT    0x04
-#       define SONY_ATTN_INT_EN_BIT     0x08
-#       define SONY_RES_RDY_INT_EN_BIT  0x10
-#       define SONY_DATA_RDY_INT_EN_BIT 0x20
-#       define SONY_PARAM_CLR_BIT       0x40
-#       define SONY_DRIVE_RESET_BIT     0x80
-
-/*
- * Offsets (from the base address) and bits for the various read registers
- * of the drive.
- */
-#define SONY_STATUS_REG_OFFSET  0
-#       define SONY_ATTN_BIT            0x01
-#       define SONY_RES_RDY_BIT         0x02
-#       define SONY_DATA_RDY_BIT        0x04
-#       define SONY_ATTN_INT_ST_BIT     0x08
-#       define SONY_RES_RDY_INT_ST_BIT  0x10
-#       define SONY_DATA_RDY_INT_ST_BIT 0x20
-#       define SONY_DATA_REQUEST_BIT    0x40
-#       define SONY_BUSY_BIT            0x80
-#define SONY_RESULT_REG_OFFSET  1
-#define SONY_READ_REG_OFFSET    2
-#define SONY_FIFOST_REG_OFFSET  3
-#       define SONY_PARAM_WRITE_RDY_BIT 0x01
-#       define SONY_PARAM_REG_EMPTY_BIT 0x02
-#       define SONY_RES_REG_NOT_EMP_BIT 0x04
-#       define SONY_RES_REG_FULL_BIT    0x08
-
-#define LOG_START_OFFSET        150     /* Offset of first logical sector */
-
-#define SONY_DETECT_TIMEOUT	(8*HZ/10) /* Maximum amount of time
-                                           that drive detection code
-                                           will wait for response
-                                           from drive (in 1/100th's
-                                           of seconds). */
- 
-#define SONY_JIFFIES_TIMEOUT    1000	/* Maximum number of times the
-                                           drive will wait/try for an
-                                           operation */
-#define SONY_RESET_TIMEOUT      100	/* Maximum number of times the
-                                           drive will wait/try a reset
-                                           operation */
-#define SONY_READY_RETRIES      20000   /* How many times to retry a
-                                           spin waiting for a register
-                                           to come ready */
-
-#define MAX_CDU31A_RETRIES      3       /* How many times to retry an
-                                           operation */
-
-/* Commands to request or set drive control parameters and disc information */
-#define SONY_REQ_DRIVE_CONFIG_CMD       0x00    /* Returns s_sony_drive_config */
-#define SONY_REQ_DRIVE_MODE_CMD         0x01
-#define SONY_REQ_DRIVE_PARAM_CMD        0x02
-#define SONY_REQ_MECH_STATUS_CMD        0x03
-#define SONY_REQ_AUDIO_STATUS_CMD       0x04
-#define SONY_SET_DRIVE_PARAM_CMD        0x10
-#define SONY_REQ_TOC_DATA_CMD           0x20    /* Returns s_sony_toc */
-#define SONY_REQ_SUBCODE_ADDRESS_CMD    0x21    /* Returns s_sony_subcode */
-#define SONY_REQ_UPC_EAN_CMD            0x22
-#define SONY_REQ_ISRC_CMD               0x23
-#define SONY_REQ_TOC_DATA_SPEC_CMD      0x24
-
-/* Commands to request information from the drive */
-#define SONY_READ_TOC_CMD               0x30
-#define SONY_SEEK_CMD                   0x31
-#define SONY_READ_CMD                   0x32
-#define SONY_READ_BLKERR_STAT_CMD       0x34
-#define SONY_ABORT_CMD                  0x35
-#define SONY_READ_TOC_SPEC_CMD          0x36
-
-/* Commands to control audio */
-#define SONY_AUDIO_PLAYBACK_CMD         0x40
-#define SONY_AUDIO_STOP_CMD             0x41
-#define SONY_AUDIO_SCAN_CMD             0x42
-
-/* Miscellaneous control commands */
-#define SONY_EJECT_CMD                  0x50
-#define SONY_SPIN_UP_CMD                0x51
-#define SONY_SPIN_DOWN_CMD              0x52
-
-/* Diagnostic commands */
-#define SONY_WRITE_BUFFER_CMD           0x60
-#define SONY_READ_BUFFER_CMD            0x61
-#define SONY_DIAGNOSTICS_CMD            0x62
-
-
-/*
- * The following are command parameters for the set drive parameter command
- */
-#define SONY_SD_DECODE_PARAM            0x00
-#define SONY_SD_INTERFACE_PARAM         0x01
-#define SONY_SD_BUFFERING_PARAM         0x02
-#define SONY_SD_AUDIO_PARAM             0x03
-#define SONY_SD_AUDIO_VOLUME            0x04
-#define SONY_SD_MECH_CONTROL            0x05
-#define SONY_SD_AUTO_SPIN_DOWN_TIME     0x06
-
-/*
- * The following are parameter bits for the mechanical control command
- */
-#define SONY_AUTO_SPIN_UP_BIT           0x01
-#define SONY_AUTO_EJECT_BIT             0x02
-#define SONY_DOUBLE_SPEED_BIT           0x04
-
-/*
- * The following extract information from the drive configuration about
- * the drive itself.
- */
-#define SONY_HWC_GET_LOAD_MECH(c)       (c.hw_config[0] & 0x03)
-#define SONY_HWC_EJECT(c)               (c.hw_config[0] & 0x04)
-#define SONY_HWC_LED_SUPPORT(c)         (c.hw_config[0] & 0x08)
-#define SONY_HWC_DOUBLE_SPEED(c)        (c.hw_config[0] & 0x10)
-#define SONY_HWC_GET_BUF_MEM_SIZE(c)    ((c.hw_config[0] & 0xc0) >> 6)
-#define SONY_HWC_AUDIO_PLAYBACK(c)      (c.hw_config[1] & 0x01)
-#define SONY_HWC_ELECTRIC_VOLUME(c)     (c.hw_config[1] & 0x02)
-#define SONY_HWC_ELECTRIC_VOLUME_CTL(c) (c.hw_config[1] & 0x04)
-
-#define SONY_HWC_CADDY_LOAD_MECH        0x00
-#define SONY_HWC_TRAY_LOAD_MECH         0x01
-#define SONY_HWC_POPUP_LOAD_MECH        0x02
-#define SONY_HWC_UNKWN_LOAD_MECH        0x03
-
-#define SONY_HWC_8KB_BUFFER             0x00
-#define SONY_HWC_32KB_BUFFER            0x01
-#define SONY_HWC_64KB_BUFFER            0x02
-#define SONY_HWC_UNKWN_BUFFER           0x03
-
-/*
- * This is the complete status returned from the drive configuration request
- * command.
- */
-struct s_sony_drive_config
-{
-   unsigned char exec_status[2];
-   char vendor_id[8];
-   char product_id[16];
-   char product_rev_level[8];
-   unsigned char hw_config[2];
-};
-
-/* The following is returned from the request subcode address command */
-struct s_sony_subcode
-{
-   unsigned char exec_status[2];
-   unsigned char address        :4;
-   unsigned char control        :4;
-   unsigned char track_num;
-   unsigned char index_num;
-   unsigned char rel_msf[3];
-   unsigned char reserved1;
-   unsigned char abs_msf[3];
-};
-
-#define MAX_TRACKS 100	/* The maximum tracks a disk may have. */
-/*
- * The following is returned from the request TOC (Table Of Contents) command.
- * (last_track_num-first_track_num+1) values are valid in tracks.
- */
-struct s_sony_toc
-{
-   unsigned char exec_status[2];
-   unsigned char address0       :4;
-   unsigned char control0       :4;
-   unsigned char point0;
-   unsigned char first_track_num;
-   unsigned char disk_type;
-   unsigned char dummy0;
-   unsigned char address1       :4;
-   unsigned char control1       :4;
-   unsigned char point1;
-   unsigned char last_track_num;
-   unsigned char dummy1;
-   unsigned char dummy2;
-   unsigned char address2       :4;
-   unsigned char control2       :4;
-   unsigned char point2;
-   unsigned char lead_out_start_msf[3];
-   struct
-   {
-      unsigned char address     :4;
-      unsigned char control     :4;
-      unsigned char track;
-      unsigned char track_start_msf[3];
-   } tracks[MAX_TRACKS];
-
-   unsigned int lead_out_start_lba;
-};
-
-struct s_sony_session_toc
-{
-   unsigned char exec_status[2];
-   unsigned char session_number;
-   unsigned char address0       :4;
-   unsigned char control0       :4;
-   unsigned char point0;
-   unsigned char first_track_num;
-   unsigned char disk_type;
-   unsigned char dummy0;
-   unsigned char address1       :4;
-   unsigned char control1       :4;
-   unsigned char point1;
-   unsigned char last_track_num;
-   unsigned char dummy1;
-   unsigned char dummy2;
-   unsigned char address2       :4;
-   unsigned char control2       :4;
-   unsigned char point2;
-   unsigned char lead_out_start_msf[3];
-   unsigned char addressb0      :4;
-   unsigned char controlb0      :4;
-   unsigned char pointb0;
-   unsigned char next_poss_prog_area_msf[3];
-   unsigned char num_mode_5_pointers;
-   unsigned char max_start_outer_leadout_msf[3];
-   unsigned char addressb1      :4;
-   unsigned char controlb1      :4;
-   unsigned char pointb1;
-   unsigned char dummyb0_1[4];
-   unsigned char num_skip_interval_pointers;
-   unsigned char num_skip_track_assignments;
-   unsigned char dummyb0_2;
-   unsigned char addressb2      :4;
-   unsigned char controlb2      :4;
-   unsigned char pointb2;
-   unsigned char tracksb2[7];
-   unsigned char addressb3      :4;
-   unsigned char controlb3      :4;
-   unsigned char pointb3;
-   unsigned char tracksb3[7];
-   unsigned char addressb4      :4;
-   unsigned char controlb4      :4;
-   unsigned char pointb4;
-   unsigned char tracksb4[7];
-   unsigned char addressc0      :4;
-   unsigned char controlc0      :4;
-   unsigned char pointc0;
-   unsigned char dummyc0[7];
-   struct
-   {
-      unsigned char address     :4;
-      unsigned char control     :4;
-      unsigned char track;
-      unsigned char track_start_msf[3];
-   } tracks[MAX_TRACKS];
-
-   unsigned int start_track_lba;
-   unsigned int lead_out_start_lba;
-};
-
-
-/*
- * The following are errors returned from the drive.
- */
-
-/* Command error group */
-#define SONY_ILL_CMD_ERR                0x10
-#define SONY_ILL_PARAM_ERR              0x11
-
-/* Mechanism group */
-#define SONY_NOT_LOAD_ERR               0x20
-#define SONY_NO_DISK_ERR                0x21
-#define SONY_NOT_SPIN_ERR               0x22
-#define SONY_SPIN_ERR                   0x23
-#define SONY_SPINDLE_SERVO_ERR          0x25
-#define SONY_FOCUS_SERVO_ERR            0x26
-#define SONY_EJECT_MECH_ERR             0x29
-#define SONY_AUDIO_PLAYING_ERR          0x2a
-#define SONY_EMERGENCY_EJECT_ERR        0x2c
-
-/* Seek error group */
-#define SONY_FOCUS_ERR                  0x30
-#define SONY_FRAME_SYNC_ERR             0x31
-#define SONY_SUBCODE_ADDR_ERR           0x32
-#define SONY_BLOCK_SYNC_ERR             0x33
-#define SONY_HEADER_ADDR_ERR            0x34
-
-/* Read error group */
-#define SONY_ILL_TRACK_R_ERR            0x40
-#define SONY_MODE_0_R_ERR               0x41
-#define SONY_ILL_MODE_R_ERR             0x42
-#define SONY_ILL_BLOCK_SIZE_R_ERR       0x43
-#define SONY_MODE_R_ERR                 0x44
-#define SONY_FORM_R_ERR                 0x45
-#define SONY_LEAD_OUT_R_ERR             0x46
-#define SONY_BUFFER_OVERRUN_R_ERR       0x47
-
-/* Data error group */
-#define SONY_UNREC_CIRC_ERR             0x53
-#define SONY_UNREC_LECC_ERR             0x57
-
-/* Subcode error group */
-#define SONY_NO_TOC_ERR                 0x60
-#define SONY_SUBCODE_DATA_NVAL_ERR      0x61
-#define SONY_FOCUS_ON_TOC_READ_ERR      0x63
-#define SONY_FRAME_SYNC_ON_TOC_READ_ERR 0x64
-#define SONY_TOC_DATA_ERR               0x65
-
-/* Hardware failure group */
-#define SONY_HW_FAILURE_ERR             0x70
-#define SONY_LEAD_IN_A_ERR              0x91
-#define SONY_LEAD_OUT_A_ERR             0x92
-#define SONY_DATA_TRACK_A_ERR           0x93
-
-/*
- * The following are returned from the Read With Block Error Status command.
- * They are not errors but information (Errors from the 0x5x group above may
- * also be returned
- */
-#define SONY_NO_CIRC_ERR_BLK_STAT       0x50
-#define SONY_NO_LECC_ERR_BLK_STAT       0x54
-#define SONY_RECOV_LECC_ERR_BLK_STAT    0x55
-#define SONY_NO_ERR_DETECTION_STAT      0x59
-
-/* 
- * The following is not an error returned by the drive, but by the code
- * that talks to the drive.  It is returned because of a timeout.
- */
-#define SONY_TIMEOUT_OP_ERR             0x01
-#define SONY_SIGNAL_OP_ERR              0x02
-#define SONY_BAD_DATA_ERR               0x03
-
-
-/*
- * The following are attention code for asynchronous events from the drive.
- */
-
-/* Standard attention group */
-#define SONY_EMER_EJECT_ATTN            0x2c
-#define SONY_HW_FAILURE_ATTN            0x70
-#define SONY_MECH_LOADED_ATTN           0x80
-#define SONY_EJECT_PUSHED_ATTN          0x81
-
-/* Audio attention group */
-#define SONY_AUDIO_PLAY_DONE_ATTN       0x90
-#define SONY_LEAD_IN_ERR_ATTN           0x91
-#define SONY_LEAD_OUT_ERR_ATTN          0x92
-#define SONY_DATA_TRACK_ERR_ATTN        0x93
-#define SONY_AUDIO_PLAYBACK_ERR_ATTN    0x94
-
-/* Auto spin up group */
-#define SONY_SPIN_UP_COMPLETE_ATTN      0x24
-#define SONY_SPINDLE_SERVO_ERR_ATTN     0x25
-#define SONY_FOCUS_SERVO_ERR_ATTN       0x26
-#define SONY_TOC_READ_DONE_ATTN         0x62
-#define SONY_FOCUS_ON_TOC_READ_ERR_ATTN 0x63
-#define SONY_SYNC_ON_TOC_READ_ERR_ATTN  0x65
-
-/* Auto eject group */
-#define SONY_SPIN_DOWN_COMPLETE_ATTN    0x27
-#define SONY_EJECT_COMPLETE_ATTN        0x28
-#define SONY_EJECT_MECH_ERR_ATTN        0x29
diff -u --recursive --new-file v2.1.69/linux/include/linux/cm206.h linux/include/linux/cm206.h
--- v2.1.69/linux/include/linux/cm206.h	Wed May 15 00:06:26 1996
+++ linux/include/linux/cm206.h	Wed Dec 31 16:00:00 1969
@@ -1,177 +0,0 @@
-/* cm206.h Header file for cm206.c.
-   Copyright (c) 1995 David van Leeuwen 
-*/
-
-#ifndef LINUX_CM206_H
-#define LINUX_CM206_H
-
-#include <linux/ioctl.h>
-
-/* First, the cm260 stuff */
-/* The ports and irq used. Although CM206_BASE and CM206_IRQ are defined
-   below, the values are not used unless autoprobing is turned off and 
-   no LILO boot options or module command line options are given. Change
-   these values to your own as last resort if autoprobing and options
-   don't work. */
-
-#define CM206_BASE 0x340
-#define CM206_IRQ 11
-
-#define r_data_status (cm206_base)
-#define r_uart_receive (cm206_base+0x2)
-#define r_fifo_output_buffer (cm206_base+0x4)
-#define r_line_status (cm206_base+0x6)
-#define r_data_control (cm206_base+0x8)
-#define r_uart_transmit (cm206_base+0xa)
-#define r_test_clock (cm206_base+0xc)
-#define r_test_control (cm206_base+0xe)
-
-/* the data_status flags */
-#define ds_ram_size 0x4000
-#define ds_toc_ready 0x2000
-#define ds_fifo_empty 0x1000
-#define ds_sync_error 0x800
-#define ds_crc_error 0x400
-#define ds_data_error 0x200
-#define ds_fifo_overflow 0x100
-#define ds_data_ready 0x80
-
-/* the line_status flags */
-#define ls_attention 0x10
-#define ls_parity_error 0x8
-#define ls_overrun 0x4
-#define ls_receive_buffer_full 0x2
-#define ls_transmitter_buffer_empty 0x1
-
-/* the data control register flags */
-#define dc_read_q_channel 0x4000
-#define dc_mask_sync_error 0x2000
-#define dc_toc_enable 0x1000
-#define dc_no_stop_on_error 0x800
-#define dc_break 0x400
-#define dc_initialize 0x200
-#define dc_mask_transmit_ready 0x100
-#define dc_flag_enable 0x80
-
-/* Define the default data control register flags here */
-#define dc_normal (dc_mask_sync_error | dc_no_stop_on_error | \
-		   dc_mask_transmit_ready)
-
-/* now some constants related to the cm206 */
-/* another drive status byte, echoed by the cm206 on most commands */
-
-#define dsb_error_condition 0x1
-#define dsb_play_in_progress 0x4
-#define dsb_possible_media_change 0x8
-#define dsb_disc_present 0x10
-#define dsb_drive_not_ready 0x20
-#define dsb_tray_locked 0x40
-#define dsb_tray_not_closed 0x80
-
-#define dsb_not_useful (dsb_drive_not_ready | dsb_tray_not_closed)
-
-/* the cm206 command set */
-
-#define c_close_tray 0
-#define c_lock_tray 0x01
-#define c_unlock_tray 0x04
-#define c_open_tray 0x05
-#define c_seek 0x10
-#define c_read_data 0x20
-#define c_force_1x 0x21
-#define c_force_2x 0x22
-#define c_auto_mode 0x23
-#define c_play 0x30
-#define c_set_audio_mode 0x31
-#define c_read_current_q 0x41
-#define c_stream_q 0x42
-#define c_drive_status 0x50
-#define c_disc_status 0x51
-#define c_audio_status 0x52
-#define c_drive_configuration 0x53
-#define c_read_upc 0x60
-#define c_stop 0x70
-#define c_calc_checksum 0xe5
-
-#define c_gimme 0xf8
-
-/* finally, the (error) condition that the drive can be in      *
- * OK, this is not always an error, but let's prefix it with e_ */
-
-#define e_none 0
-#define e_illegal_command 0x01
-#define e_sync 0x02
-#define e_seek 0x03
-#define e_parity 0x04
-#define e_focus 0x05
-#define e_header_sync 0x06
-#define e_code_incompatibility 0x07
-#define e_reset_done 0x08
-#define e_bad_parameter 0x09
-#define e_radial 0x0a
-#define e_sub_code 0x0b
-#define e_no_data_track 0x0c
-#define e_scan 0x0d
-#define e_tray_open 0x0f
-#define e_no_disc 0x10
-#define e_tray stalled 0x11
-
-/* drive configuration masks */
-
-#define dcf_revision_code 0x7
-#define dcf_transfer_rate 0x60
-#define dcf_motorized_tray 0x80
-
-/* disc status byte */
-
-#define cds_multi_session 0x2
-#define cds_all_audio 0x8
-#define cds_xa_mode 0xf0
-
-/* finally some ioctls for the driver */
-
-#define CM206CTL_GET_STAT _IO( 0x20, 0 )
-#define CM206CTL_GET_LAST_STAT _IO( 0x20, 1 )
-
-/* for kernel 1.2.n */
-#if !defined(CDROM_GET_UPC)
-#define CDROM_GET_UPC _IO( 'S', 0x11 )
-#define CDROMRESET _IO( 'S', 0x12 )
-#endif
-
-#ifdef STATISTICS
-
-/* This is an ugly way to guarantee that the names of the statistics
- * are the same in the code and in the diagnostics program.  */
-
-#ifdef __KERNEL__
-#define x(a) st_ ## a
-#define y enum
-#else
-#define x(a) #a
-#define y char * stats_name[] = 
-#endif
-
-y {x(interrupt), x(data_ready), x(fifo_overflow), x(data_error),
-     x(crc_error), x(sync_error), x(lost_intr), x(echo),
-     x(write_timeout), x(receive_timeout), x(read_timeout),
-     x(dsb_timeout), x(stop_0xff), x(back_read_timeout),
-     x(sector_transferred), x(read_restarted), x(read_background),
-     x(bh), x(open), x(ioctl_multisession), x(attention)
-#ifdef __KERNEL__
-     , x(last_entry)
-#endif
- };
-
-#ifdef __KERNEL__
-#define NR_STATS st_last_entry
-#else
-#define NR_STATS (sizeof(stats_name)/sizeof(char*))
-#endif
-
-#undef y
-#undef x
-
-#endif STATISTICS
-
-#endif LINUX_CM206_H
diff -u --recursive --new-file v2.1.69/linux/include/linux/coda.h linux/include/linux/coda.h
--- v2.1.69/linux/include/linux/coda.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/coda.h	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,481 @@
+
+/*
+ *
+ * Based on cfs.h from Mach, but revamped for increased simplicity.
+ * Linux modifications by Peter Braam, Aug 1996
+ */
+
+#ifndef _CFS_HEADER_
+#define _CFS_HEADER_
+
+
+
+/* Catch new _KERNEL defn for NetBSD */
+#ifdef __NetBSD__
+#include <sys/types.h>
+#ifdef _KERNEL
+#define KERNEL
+#endif 
+#endif 
+
+#if 0
+#ifndef _SCALAR_T_
+#define _SCALAR_T_ 1
+typedef unsigned long  u_int32_t;
+typedef unsigned short u_int16_t;
+typedef unsigned char  u_int8_t;
+#endif 
+#endif 
+
+#ifdef __linux__
+#ifndef _UQUAD_T_
+#define _UQUAD_T_ 1
+typedef unsigned long  u_quad_t;
+#endif 
+
+#ifdef __KERNEL__
+#define KERNEL
+#endif __KERNEL__
+#endif
+
+/*
+ * Cfs constants
+ */
+#define CFS_MAXNAMLEN 256
+#define CFS_MAXPATHLEN 256
+#define CODA_MAXSYMLINK 10
+
+/* types used in kernel and user mode */
+#ifndef _VENUS_DIRENT_T_
+#define _VENUS_DIRENT_T_ 1
+struct venus_dirent {
+        unsigned long d_fileno;             /* file number of entry */
+        unsigned short d_reclen;             /* length of this record */
+        char  d_type;               /* file type, see below */
+        char  d_namlen;             /* length of string in d_name */
+        char     d_name[CFS_MAXNAMLEN + 1];/* name must be no longer than this */
+};
+#undef DIRSIZ
+#define DIRSIZ(dp)      ((sizeof (struct venus_dirent) - (CFS_MAXNAMLEN+1)) + \
+                         (((dp)->d_namlen+1 + 3) &~ 3))
+
+/*
+ * File types
+ */
+#define	DT_UNKNOWN	 0
+#define	DT_FIFO		 1
+#define	DT_CHR		 2
+#define	DT_DIR		 4
+#define	DT_BLK		 6
+#define	DT_REG		 8
+#define	DT_LNK		10
+#define	DT_SOCK		12
+#define	DT_WHT		14
+
+/*
+ * Convert between stat structure types and directory types.
+ */
+#define	IFTODT(mode)	(((mode) & 0170000) >> 12)
+#define	DTTOIF(dirtype)	((dirtype) << 12)
+
+#endif
+
+#ifndef	_FID_T_
+#define _FID_T_	1
+typedef u_long VolumeId;
+typedef u_long VnodeId;
+typedef u_long Unique_t;
+typedef u_long FileVersion;
+#endif 
+
+#ifndef	_VICEFID_T_
+#define _VICEFID_T_	1
+typedef struct ViceFid {
+    VolumeId Volume;
+    VnodeId Vnode;
+    Unique_t Unique;
+} ViceFid;
+#endif	/* VICEFID */
+
+#ifndef _VUID_T_
+#define _VUID_T_
+typedef u_long vuid_t;
+typedef u_long vgid_t;
+#endif /*_VUID_T_ */
+
+#ifndef _CODACRED_T_
+#define _CODACRED_T_
+#define NGROUPS 32
+struct CodaCred {
+    vuid_t cr_uid, cr_euid, cr_suid, cr_fsuid; /* Real, efftve, set, fs uid*/
+    vgid_t cr_gid, cr_egid, cr_sgid, cr_fsgid; /* same for groups */
+    vgid_t cr_groups[NGROUPS];	      /* Group membership for caller */
+};
+#endif 
+
+#ifndef _VENUS_VATTR_T_
+#define _VENUS_VATTR_T_
+/*
+ * Vnode types.  VNON means no type.
+ */
+enum coda_vtype	{ VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD };
+
+struct coda_vattr {
+	enum coda_vtype	va_type;	/* vnode type (for create) */
+	u_short		va_mode;	/* files access mode and type */
+	short		va_nlink;	/* number of references to file */
+	vuid_t		va_uid;		/* owner user id */
+	vgid_t		va_gid;		/* owner group id */
+	long		va_fsid;	/* file system id (dev for now) */
+	long		va_fileid;	/* file id */
+	u_quad_t	va_size;	/* file size in bytes */
+	long		va_blocksize;	/* blocksize preferred for i/o */
+	struct timespec	va_atime;	/* time of last access */
+	struct timespec	va_mtime;	/* time of last modification */
+	struct timespec	va_ctime;	/* time file changed */
+	u_long		va_gen;		/* generation number of file */
+	u_long		va_flags;	/* flags defined for file */
+	dev_t		va_rdev;	/* device the special file represents */
+	u_quad_t	va_bytes;	/* bytes of disk space held by file */
+	u_quad_t	va_filerev;	/* file modification number */
+	u_int		va_vaflags;	/* operations flags, see below */
+	long		va_spare;	/* remain quad aligned */
+};
+#define VREAD 00400
+#define VWRITE 00200
+
+#endif 
+
+/*
+ * opcode constants
+ */
+#define CFS_ROOT	((u_long) 2)
+#define CFS_SYNC	((u_long) 3)
+#define CFS_OPEN	((u_long) 4)
+#define CFS_CLOSE	((u_long) 5)
+#define CFS_IOCTL	((u_long) 6)
+#define CFS_GETATTR	((u_long) 7)
+#define CFS_SETATTR	((u_long) 8)
+#define CFS_ACCESS	((u_long) 9)
+#define CFS_LOOKUP	((u_long) 10)
+#define CFS_CREATE	((u_long) 11)
+#define CFS_REMOVE	((u_long) 12)
+#define CFS_LINK	((u_long) 13)
+#define CFS_RENAME	((u_long) 14)
+#define CFS_MKDIR	((u_long) 15)
+#define CFS_RMDIR	((u_long) 16)
+#define CFS_READDIR	((u_long) 17)
+#define CFS_SYMLINK	((u_long) 18)
+#define CFS_READLINK	((u_long) 19)
+#define CFS_FSYNC	((u_long) 20)
+#define CFS_INACTIVE	((u_long) 21)
+#define	CFS_VGET	((u_long) 22)
+#define	CFS_SIGNAL	((u_long) 23)
+#define CFS_REPLACE	((u_long) 24)
+#define CFS_FLUSH       ((u_long) 25)
+#define CFS_PURGEUSER   ((u_long) 26)
+#define CFS_ZAPFILE     ((u_long) 27)
+#define CFS_ZAPDIR      ((u_long) 28)
+#define CFS_ZAPVNODE    ((u_long) 29)
+#define CFS_PURGEFID    ((u_long) 30)
+#define	CFS_RDWR	((u_long) 31)
+#define ODY_MOUNT	((u_long) 32) 
+#define ODY_LOOKUP	((u_long) 33)
+#define ODY_EXPAND	((u_long) 34)
+
+#define CFS_NCALLS 35
+#define DOWNCALL(opcode) (opcode >= CFS_REPLACE && opcode <= CFS_PURGEFID)
+
+/*
+ *        Venus <-> Coda  RPC arguments
+ */
+
+struct inputArgs {
+    u_long opcode;
+    u_long unique;     /* Keep multiple outstanding msgs distinct */
+    u_short pid;		 /* Common to all */
+    u_short pgid;		 /* Common to all */
+    struct CodaCred cred;	 /* Common to all */
+    
+    union {
+	/* Nothing needed for cfs_root */
+	/* Nothing needed for cfs_sync */
+	struct cfs_open_in {
+	    ViceFid	VFid;
+	    int	flags;
+	} cfs_open;
+	struct cfs_close_in {
+	    ViceFid	VFid;
+	    int	flags;
+	} cfs_close;
+	struct cfs_ioctl_in {
+	    ViceFid VFid;
+	    int	cmd;
+	    int	len;
+	    int	rwflag;
+	    char *data;			/* Place holder for data. */
+	} cfs_ioctl;
+	struct cfs_getattr_in {
+	    ViceFid VFid;
+            struct coda_vattr attr;
+	} cfs_getattr;
+	struct cfs_setattr_in {
+	    ViceFid VFid;
+	    struct coda_vattr attr;
+	} cfs_setattr;
+	struct cfs_access_in {
+	    ViceFid	VFid;
+	    int	flags;
+	} cfs_access;
+	struct  cfs_lookup_in {
+	    ViceFid	VFid;
+	    char        *name;		/* Place holder for data. */
+	} cfs_lookup;
+	struct cfs_create_in {
+	    ViceFid VFid;
+	    struct coda_vattr attr;
+	    int excl;
+	    int mode;
+	    char	*name;		/* Place holder for data. */
+	} cfs_create;
+	struct cfs_remove_in {
+	    ViceFid	VFid;
+	    char	*name;		/* Place holder for data. */
+	} cfs_remove;
+	struct cfs_link_in {
+	    ViceFid sourceFid;          /* cnode to link *to* */
+	    ViceFid destFid;            /* Directory in which to place link */
+	    char	*tname;		/* Place holder for data. */
+	} cfs_link;
+	struct cfs_rename_in {
+	    ViceFid	sourceFid;
+	    char	*srcname;
+	    ViceFid destFid;
+	    char	*destname;
+	} cfs_rename;
+	struct cfs_mkdir_in {
+	    ViceFid	VFid;
+	    struct coda_vattr attr;
+	    char	*name;		/* Place holder for data. */
+	} cfs_mkdir;
+	struct cfs_rmdir_in {
+	    ViceFid	VFid;
+	    char	*name;		/* Place holder for data. */
+	} cfs_rmdir;
+	struct cfs_readdir_in {
+	    ViceFid	VFid;
+	    int	count;
+	    int	offset;
+	} cfs_readdir;
+	struct cfs_symlink_in {
+	    ViceFid	VFid;          /* Directory to put symlink in */
+	    char	*srcname;
+	    struct coda_vattr attr;
+	    char	*tname;
+	} cfs_symlink;
+	struct cfs_readlink_in {
+	    ViceFid VFid;
+	} cfs_readlink;
+	struct cfs_fsync_in {
+	    ViceFid VFid;
+	} cfs_fsync;
+	struct cfs_inactive_in {
+	    ViceFid VFid;
+	} cfs_inactive;
+	struct cfs_vget_in {
+	    ViceFid VFid;
+	} cfs_vget;
+	/* CFS_SIGNAL is out-of-band, doesn't need data. */
+	/* CFS_INVALIDATE is a venus->kernel call */
+	/* CFS_FLUSH is a venus->kernel call */
+	/* CFS_PURGEUSER is a venus->kernel call */
+	/* CFS_ZAPFILE is a venus->kernel call */
+	/* CFS_ZAPDIR is a venus->kernel call */	
+	/* CFS_ZAPVNODE is a venus->kernel call */	
+	/* CFS_PURGEFID is a venus->kernel call */	
+	struct cfs_rdwr_in {
+	    ViceFid	VFid;
+	    int	rwflag;
+	    int	count;
+	    int	offset;
+	    int	ioflag;
+	    caddr_t	data;		/* Place holder for data. */	
+	} cfs_rdwr;
+	struct ody_mount_in {
+	    char	*name;		/* Place holder for data. */
+	} ody_mount;
+	struct ody_lookup_in {
+	    ViceFid	VFid;
+	    char	*name;		/* Place holder for data. */
+	} ody_lookup;
+	struct ody_expand_in {
+	    ViceFid VFid;
+	    int size;			/* Size of buffer to return. */
+	} ody_expand;
+	/* CFS_REPLACE is a venus->kernel call */	
+    } d;
+};
+    
+/*  Occasionally, don't cache the fid returned by CFS_LOOKUP. For
+ * instance, if the fid is inconsistent. This case is handled by
+ * setting the top bit of the return result parameter.  */
+#define CFS_NOCACHE          0x80000000
+
+#define INIT_OUT(out, opcode, result) \
+    out->opcode = (opcode); out->result = (result);
+
+/*  IMPORTANT: opcode and unique must be first two fields! */
+struct outputArgs {
+    u_long opcode;
+    u_long unique;	 /* Keep multiple outstanding msgs distinct */
+    u_long result;
+    union {
+	struct cfs_root_out {
+	    ViceFid VFid;
+	} cfs_root;
+	/* Nothing needed for cfs_sync */
+	struct cfs_open_out {
+	    dev_t	dev;
+	    ino_t	inode;
+	} cfs_open;
+	/* Nothing needed for cfs_close */
+	struct cfs_ioctl_out {
+	    int	len;
+	    caddr_t	data;		/* Place holder for data. */
+	} cfs_ioctl;
+	struct cfs_getattr_out {
+	    struct coda_vattr attr;
+	} cfs_getattr;
+	/* Nothing needed for cfs_setattr */
+	/* Nothing needed for cfs_access */
+	struct cfs_lookup_out {
+	    ViceFid VFid;
+	    int	vtype;
+	} cfs_lookup;
+	struct cfs_create_out {
+	    ViceFid VFid;
+	    struct coda_vattr attr;
+	} cfs_create;
+	/* Nothing needed for cfs_remove */
+	/* Nothing needed for cfs_link */
+	/* Nothing needed for cfs_rename */
+	struct cfs_mkdir_out {
+	    ViceFid VFid;
+	    struct coda_vattr attr;
+	} cfs_mkdir;
+	/* Nothing needed for cfs_rmdir */
+	struct cfs_readdir_out {
+	    int	size;
+	    caddr_t	data;		/* Place holder for data. */
+	} cfs_readdir;
+	/* Nothing needed for cfs_symlink */
+	struct cfs_readlink_out {
+	    int	count;
+	    caddr_t	data;		/* Place holder for data. */
+	} cfs_readlink;
+	/* Nothing needed for cfs_fsync */
+	/* Nothing needed for cfs_inactive */
+	struct cfs_vget_out {
+	    ViceFid VFid;
+	    int	vtype;
+	} cfs_vget;
+	/* CFS_SIGNAL is out-of-band, doesn't need data. */
+	/* CFS_INVALIDATE is a venus->kernel call */
+	/* CFS_FLUSH is a venus->kernel call */
+	struct cfs_purgeuser_out {/* CFS_PURGEUSER is a venus->kernel call */
+	    struct CodaCred cred;
+	} cfs_purgeuser;
+	struct cfs_zapfile_out {  /* CFS_ZAPFILE is a venus->kernel call */
+	    ViceFid CodaFid;
+	} cfs_zapfile;
+	struct cfs_zapdir_out {	  /* CFS_ZAPDIR is a venus->kernel call */
+	    ViceFid CodaFid;
+	} cfs_zapdir;
+	struct cfs_zapvnode_out { /* CFS_ZAPVNODE is a venus->kernel call */
+	    struct CodaCred cred;
+	    ViceFid VFid;
+	} cfs_zapvnode;
+	struct cfs_purgefid_out { /* CFS_PURGEFID is a venus->kernel call */
+	    ViceFid CodaFid;
+	} cfs_purgefid;
+	struct cfs_rdwr_out {
+	    int	rwflag;
+	    int	count;
+	    caddr_t	data;	/* Place holder for data. */
+	} cfs_rdwr;
+	struct ody_mount_out {
+	    ViceFid VFid;
+	} ody_mount;
+	struct ody_lookup_out {
+	    ViceFid VFid;
+	} ody_lookup;
+	struct ody_expand_out {	/* Eventually it would be nice to get some */
+	    char links[sizeof(int)];	/* Place holder for data. */
+	} ody_expand;
+	struct cfs_replace_out { /* cfs_replace is a venus->kernel call */
+	    ViceFid NewFid;
+	    ViceFid OldFid;
+	} cfs_replace;
+    } d;
+};    
+    
+
+/* 
+ * how big are the inputArgs and outputArgs structures
+ * for the varying types of calls?
+ */
+#define	VC_IN_NO_DATA	    (2 * (int)sizeof(u_long)    \
+                             + 2 * (int)sizeof(u_short) \
+			     + (int)sizeof(struct CodaCred))
+#define	VC_OUT_NO_DATA	    (3 * (int)sizeof(u_long))
+#define VC_INSIZE(member)   (VC_IN_NO_DATA + (int)sizeof(struct member))
+#define VC_OUTSIZE(member)  (VC_OUT_NO_DATA + (int)sizeof(struct member))
+
+/* Now for venus. C++ doesn't know what struct foo means. */
+#define VC_SIZE(Thing, Member)   (VC_OUT_NO_DATA                    \
+                                  + (int)sizeof((Thing)->d.Member))
+
+#define VC_BIGGER_OF_IN_OR_OUT  (sizeof(struct outputArgs)   \
+                                  > sizeof(struct inputArgs) \
+                                ? sizeof(struct outputArgs)  \
+                                : sizeof(struct inputArgs))
+#define VC_DATASIZE	    8192
+#define	VC_MAXMSGSIZE	    (VC_DATASIZE + VC_BIGGER_OF_IN_OR_OUT)
+
+/*
+ * Used for identifying usage of "Control" and pioctls
+ */
+struct ViceIoctl {
+        caddr_t in, out;        /* Data to be transferred in, or out */
+        short in_size;          /* Size of input buffer <= 2K */
+        short out_size;         /* Maximum size of output buffer, <= 2K */
+};
+
+struct PioctlData {
+        const char *path;
+        int follow;
+        struct ViceIoctl vi;
+};
+
+
+
+
+
+
+#define	CFS_CONTROL		".CONTROL"
+#define CFS_CONTROLLEN           8
+#define	CTL_VOL			-1
+#define	CTL_VNO			-1
+#define	CTL_UNI			-1
+#define CTL_INO                 -1
+#define	CTL_FILE    "/coda/.CONTROL"
+#define IOCPARM_MASK 0x0000ffff
+
+
+#define	IS_CTL_FID(fidp)	((fidp)->Volume == CTL_VOL &&\
+				 (fidp)->Vnode == CTL_VNO &&\
+				 (fidp)->Unique == CTL_UNI)
+     /*#define	ISDIR(fid)		((fid).Vnode & 0x1) */
+
+#endif 
+
diff -u --recursive --new-file v2.1.69/linux/include/linux/coda_cnode.h linux/include/linux/coda_cnode.h
--- v2.1.69/linux/include/linux/coda_cnode.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/coda_cnode.h	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,39 @@
+
+/* revamped cnode.h file: platform dependent, kernel only! */
+
+
+#ifndef	_CNODE_H_
+#define	_CNODE_H_
+#include <linux/coda.h>
+
+
+#define CODA_CNODE_MAGIC        0x47114711
+
+/* defintion of cnode, which combines ViceFid with inode information */
+struct cnode {
+        struct inode    *c_vnode;    /* linux inode associated with cnode */
+        ViceFid	         c_fid;	     /* Coda identifier */
+        u_short	         c_flags;     /* flags (see below) */
+        int             c_magic;     /* to verify the data structure */
+        u_short	        c_ocount;    /* count of openers */
+        u_short         c_owrite;    /* count of open for write */
+        u_short         c_mmcount;   /* count of mmappers */
+        struct inode    *c_ovp;	     /* open vnode pointer */
+        struct dentry   c_odentry;
+};
+
+/* flags */
+#define C_VATTR       0x1         /* Validity of vattr in the cnode */
+#define C_SYMLINK     0x2         /* Validity of symlink pointer in the cnode */
+#define C_DYING	      0x4	  /* Set for outstanding cnodes from venus (which died) */
+
+struct cnode *coda_cnode_alloc(void);
+void coda_cnode_free(struct cnode *cinode);
+int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb);
+struct inode *coda_fid2inode(ViceFid *fid, struct super_block *sb);
+int coda_cnode_makectl(struct inode **inode, struct super_block *sb);
+
+
+
+#endif	
+
diff -u --recursive --new-file v2.1.69/linux/include/linux/coda_linux.h linux/include/linux/coda_linux.h
--- v2.1.69/linux/include/linux/coda_linux.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/coda_linux.h	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,187 @@
+/* 
+ * Coda File System, Linux Kernel module
+ * 
+ * Original version, adapted from cfs_mach.c, (C) Carnegie Mellon University
+ * Linux modifications (C) 1996, Peter J. Braam
+ * Rewritten for Linux 2.1 (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon University encourages users of this software to
+ * contribute improvements to the Coda project.
+ */
+
+#ifndef _LINUX_CODA_FS
+#define _LINUX_CODA_FS
+
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/sched.h> 
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/malloc.h>
+#include <linux/wait.h>		
+#include <linux/types.h>
+#include <linux/fs.h>
+
+/* operations */
+extern struct inode_operations coda_dir_inode_operations;
+extern struct inode_operations coda_file_inode_operations;
+extern struct inode_operations coda_ioctl_inode_operations;
+extern struct inode_operations coda_symlink_inode_operations;
+
+extern struct file_operations coda_dir_operations;
+extern struct file_operations coda_file_operations;
+extern struct file_operations coda_ioctl_operations;
+
+/* operations shared over more than one file */
+int coda_open(struct inode *i, struct file *f);
+int coda_release(struct inode *i, struct file *f);
+int coda_permission(struct inode *inode, int mask);
+
+/* global variables */
+extern int coda_debug;
+extern int coda_print_entry;
+extern int coda_access_cache;
+extern int cfsnc_use;
+
+
+/*   */
+char *coda_f2s(ViceFid *f, char *s);
+int coda_isroot(struct inode *i);
+void coda_load_creds(struct CodaCred *cred);
+
+
+
+/* defined in  file.c */
+void coda_prepare_openfile(struct inode *coda_inode, struct file *coda_file, 
+			   struct inode *open_inode,  struct file *open_file,
+			   struct dentry *open_dentry);
+void coda_restore_codafile(struct inode *coda_inode, struct file *coda_file, 
+			   struct inode *open_inode, struct file *open_file);
+int coda_inode_grab(dev_t dev, ino_t ino, struct inode **ind);
+struct super_block *coda_find_super(kdev_t device);
+
+
+#define INIT_IN(in, op) \
+	  (in)->opcode = (op); \
+	  (in)->pid = current->pid; \
+          (in)->pgid = current->gid; 
+
+/* debugging aids */
+
+#define coda_panic printk
+
+/* debugging masks */
+#define D_SUPER     1   /* print results returned by Venus */ 
+#define D_INODE     2   /* print entry and exit into procedure */
+#define D_FILE      4   /* print malloc, de-alloc information */
+#define D_CACHE     8   /* cache debugging */
+#define D_MALLOC    16
+#define D_CNODE     32
+#define D_UPCALL    64  /* up and downcall debugging */
+#define D_PSDEV    128  
+#define D_PIOCTL   256
+#define D_SPECIAL  512
+/* until we are really good, ... */
+#define coda_panic printk
+ 
+#define CDEBUG(mask, format, a...)                                \
+  do {                                                            \
+  if (coda_debug & mask) {                                        \
+    printk("(%s,l. %d): ",  __FUNCTION__, __LINE__);              \
+    printk(format, ## a); }                                       \
+} while (0) ;                            
+
+#define ENTRY    \
+    if(coda_print_entry) printk("Process %d entered %s\n",current->pid,__FUNCTION__)
+
+#define EXIT    \
+    if(coda_print_entry) printk("Process %d leaving %s\n",current->pid,__FUNCTION__)
+
+
+/* inode to cnode */
+#define ITOC(the_inode)  ((struct cnode *)(the_inode)->u.generic_ip)
+/* cnode to inode */
+#define CTOI(the_cnode)  ((the_cnode)->c_vnode)
+
+#define CHECK_CNODE(c)                                                \
+do {                                                                  \
+  struct cnode *cnode = (c);                                          \
+  if (!cnode)                                                         \
+    coda_panic ("%s(%d): cnode is null\n", __FUNCTION__, __LINE__);        \
+  if (cnode->c_magic != CODA_CNODE_MAGIC)                             \
+    coda_panic ("%s(%d): cnode magic wrong\n", __FUNCTION__, __LINE__);    \
+  if (!cnode->c_vnode)                                                \
+    coda_panic ("%s(%d): cnode has null inode\n", __FUNCTION__, __LINE__); \
+  if ( (struct cnode *)cnode->c_vnode->u.generic_ip != cnode )           \
+    coda_panic("AAooh, %s(%d) cnode doesn't link right!\n", __FUNCTION__,__LINE__);\
+} while (0);
+
+
+/* ioctl stuff */
+/* this needs to be sorted out XXXX */ 
+#ifdef	__linux__
+#define IOCPARM_MASK 0x0000ffff
+#endif 
+
+#define CODA_ALLOC(ptr, cast, size)                                       \
+do {                                                                      \
+    if (size < 3000) {                                                    \
+        ptr = (cast)kmalloc((unsigned long) size, GFP_KERNEL);            \
+                CDEBUG(D_MALLOC, "kmalloced: %x at %x.\n", (int) size, (int) ptr);\
+     }  else {                                                             \
+        ptr = (cast)vmalloc((unsigned long) size);                        \
+	CDEBUG(D_MALLOC, "vmalloced: %x at %x.\n", (int) size, (int) ptr);}\
+    if (ptr == 0) {                                                       \
+        coda_panic("kernel malloc returns 0 at %s:%d\n", __FILE__, __LINE__);  \
+    }                                                                     \
+    memset( ptr, 0, size );                                                   \
+} while (0)
+
+
+#define CODA_FREE(ptr,size) do {if (size < 3000) { kfree_s((ptr), (size)); CDEBUG(D_MALLOC, "kfreed: %x at %x.\n", (int) size, (int) ptr); } else { vfree((ptr)); CDEBUG(D_MALLOC, "vfreed: %x at %x.\n", (int) size, (int) ptr);} } while (0)
+
+
+
+
+/*
+ * Macros to manipulate the queue 
+ */
+#define crfree(cred) CODA_FREE( (cred), sizeof(struct ucred))
+
+#ifndef INIT_QUEUE
+
+struct queue {
+    struct queue *forw, *back;
+};
+
+#define INIT_QUEUE(head)                     \
+do {                                         \
+    (head).forw = (struct queue *)&(head);   \
+    (head).back = (struct queue *)&(head);   \
+} while (0)
+
+#define GETNEXT(head) (head).forw
+
+#define EMPTY(head) ((head).forw == &(head))
+
+#define EOQ(el, head) ((struct queue *)(el) == (struct queue *)&(head))
+
+#define INSQUE(el, head)                             \
+do {                                                 \
+	(el).forw = ((head).back)->forw;             \
+	(el).back = (head).back;                     \
+	((head).back)->forw = (struct queue *)&(el); \
+	(head).back = (struct queue *)&(el);         \
+} while (0)
+
+#define REMQUE(el)                         \
+do {                                       \
+	((el).forw)->back = (el).back;     \
+	(el).back->forw = (el).forw;       \
+}  while (0)
+
+#endif INIT_QUEUE
+
+
+#endif _LINUX_CODA_FS
+
diff -u --recursive --new-file v2.1.69/linux/include/linux/coda_namecache.h linux/include/linux/coda_namecache.h
--- v2.1.69/linux/include/linux/coda_namecache.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/coda_namecache.h	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,160 @@
+/* 
+ * Mach Operating System
+ * Copyright (c) 1990 Carnegie-Mellon University
+ * Copyright (c) 1989 Carnegie-Mellon University
+ * All rights reserved.  The CMU software License Agreement specifies
+ * the terms and conditions for use and redistribution.
+ */
+
+/*
+ * This code was written for the Coda file system at Carnegie Mellon University.
+ * Contributers include David Steere, James Kistler, and M. Satyanarayanan.
+ */
+
+/* 
+ * HISTORY
+ * cfsnc.h,v
+ * Revision 1.2  1996/01/02 16:57:19  bnoble
+ * Added support for Coda MiniCache and raw inode calls (final commit)
+ *
+ * Revision 1.1.2.1  1995/12/20 01:57:45  bnoble
+ * Added CFS-specific files
+ *
+ * Revision 3.1.1.1  1995/03/04  19:08:22  bnoble
+ * Branch for NetBSD port revisions
+ *
+ * Revision 3.1  1995/03/04  19:08:21  bnoble
+ * Bump to major revision 3 to prepare for NetBSD port
+ *
+ * Revision 2.2  1994/08/28  19:37:39  luqi
+ * Add a new CFS_REPLACE call to allow venus to replace a ViceFid in the
+ * mini-cache.
+ *
+ * In "cfs.h":
+ * Add CFS_REPLACE decl.
+ *
+ * In "cfs_namecache.c":
+ * Add routine cfsnc_replace.
+ *
+ * In "cfs_subr.c":
+ * Add case-statement to process CFS_REPLACE.
+ *
+ * In "cfsnc.h":
+ * Add decl for CFSNC_REPLACE.
+ *
+ * Revision 2.1  94/07/21  16:25:27  satya
+ * Conversion to C++ 3.0; start of Coda Release 2.0
+ *
+ * Revision 1.2  92/10/27  17:58:34  lily
+ * merge kernel/latest and alpha/src/cfs
+ * 
+ * Revision 2.2  90/07/05  11:27:04  mrt
+ * 	Created for the Coda File System.
+ * 	[90/05/23            dcs]
+ * 
+ * Revision 1.4  90/05/31  17:02:12  dcs
+ * Prepare for merge with facilities kernel.
+ * 
+ * 
+ */
+#ifndef _CFSNC_HEADER_
+#define _CFSNC_HEADER_
+
+#include "coda.h"
+#include "coda_cnode.h"
+
+
+/*
+ * Cfs constants
+ */
+#define CFSNC_NAMELEN	15		/* longest name stored in cache */
+#define CFSNC_CACHESIZE 256		/* Default cache size */
+#define CFSNC_HASHSIZE	64		/* Must be multiple of 2 */
+/*
+ * Structure for an element in the CFS Name Cache.
+ */
+
+/* roughly 50 bytes per entry */
+struct cfscache {	
+	struct cfscache	*hash_next,*hash_prev;	/* Hash list */
+	struct cfscache	*lru_next, *lru_prev;	/* LRU list */
+	struct cnode	*cp;			/* vnode of the file */
+	struct cnode	*dcp;			/* parent's cnode */
+	struct CodaCred	*cred;			/* user credentials */
+	char		name[CFSNC_NAMELEN];	/* segment name */
+	int		namelen;		/* length of name */
+};
+
+
+
+/* exported */
+void cfsnc_init(void);
+void cfsnc_enter(struct cnode *dcp, register const char *name, int namelen, struct cnode *cp);
+struct cnode *cfsnc_lookup(struct cnode *dcp, register const char *name, int namelen);
+void cfsnc_zapParentfid(ViceFid *fid);
+void cfsnc_zapfid(ViceFid *fid);
+void cfsnc_zapfile(struct cnode *dcp, register const char *name, int length);
+void cfsnc_purge_user(struct CodaCred *cred);
+void cfsnc_flush(void);
+void cfsnc_replace(ViceFid *f1, ViceFid *f2);
+void print_cfsnc(void);
+void coda_print_ce(struct cfscache *);
+int cfsnc_resize(int hashsize, int heapsize);
+ 
+  
+
+/* #define CFSNC_VALID(cncp)	( (cncp->dcp != (struct cnode *)0) && (cncp->cp->c_flags & C_VATTR) ) */
+#define CFSNC_VALID(cncp)	    (cncp->dcp != (struct cnode *)0) 
+ 
+#define DATA_PART(cncp)				(struct cfscache *) \
+			((char *)cncp + (4*sizeof(struct cfscache *)))
+#define DATA_SIZE	(sizeof(struct cfscache)-(4*sizeof(struct cfscache *)))
+
+/*
+ * Structure to contain statistics on the cache usage
+ */
+
+struct cfsnc_statistics {
+	unsigned	hits;
+	unsigned	misses;
+	unsigned	enters;
+	unsigned	dbl_enters;
+	unsigned	long_name_enters;
+	unsigned	long_name_lookups;
+	unsigned	long_remove;
+	unsigned	lru_rm;
+	unsigned	zapPfids;
+	unsigned	zapFids;
+	unsigned	zapFile;
+	unsigned	zapUsers;
+	unsigned	Flushes;
+	unsigned        Sum_bucket_len;
+	unsigned        Sum2_bucket_len;
+	unsigned        Max_bucket_len;
+	unsigned        Num_zero_len;
+	unsigned        Search_len;
+};
+
+/* 
+ * Symbols to aid in debugging the namecache code. Assumes the existence
+ * of the variable cfsnc_debug, which is defined in cfs_namecache.c
+ */
+extern int cfsnc_debug;
+#define CFSNC_DEBUG(N, STMT)     { if (cfsnc_debug & (1 <<N)) { STMT } }
+
+#define CFSNC_FIND		((u_long) 1)
+#define CFSNC_REMOVE		((u_long) 2)
+#define CFSNC_INIT		((u_long) 3)
+#define CFSNC_ENTER		((u_long) 4)
+#define CFSNC_LOOKUP		((u_long) 5)
+#define CFSNC_ZAPPFID		((u_long) 6)
+#define CFSNC_ZAPFID		((u_long) 7)
+#define CFSNC_ZAPVNODE		((u_long) 8)
+#define CFSNC_ZAPFILE		((u_long) 9)
+#define CFSNC_PURGEUSER		((u_long) 10)
+#define CFSNC_FLUSH		((u_long) 11)
+#define CFSNC_PRINTCFSNC	((u_long) 12)
+#define CFSNC_PRINTSTATS	((u_long) 13)
+#define CFSNC_REPLACE		((u_long) 14)
+
+#endif _CFSNC_HEADER_
diff -u --recursive --new-file v2.1.69/linux/include/linux/coda_opstats.h linux/include/linux/coda_opstats.h
--- v2.1.69/linux/include/linux/coda_opstats.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/coda_opstats.h	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,132 @@
+#ifndef _BLURB_
+#define _BLURB_
+/*
+
+            Coda: an Experimental Distributed File System
+                             Release 3.1
+
+          Copyright (c) 1987-1996 Carnegie Mellon University
+                         All Rights Reserved
+
+Permission  to  use, copy, modify and distribute this software and its
+documentation is hereby granted,  provided  that  both  the  copyright
+notice  and  this  permission  notice  appear  in  all  copies  of the
+software, derivative works or  modified  versions,  and  any  portions
+thereof, and that both notices appear in supporting documentation, and
+that credit is given to Carnegie Mellon University  in  all  documents
+and publicity pertaining to direct or indirect use of this code or its
+derivatives.
+
+CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS  KNOWN  TO  HAVE  BUGS,
+SOME  OF  WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON ALLOWS
+FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.   CARNEGIE  MELLON
+DISCLAIMS  ANY  LIABILITY  OF  ANY  KIND  FOR  ANY  DAMAGES WHATSOEVER
+RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE  OR  OF
+ANY DERIVATIVE WORK.
+
+Carnegie  Mellon  encourages  users  of  this  software  to return any
+improvements or extensions that  they  make,  and  to  grant  Carnegie
+Mellon the rights to redistribute these changes without encumbrance.
+*/
+
+static char *rcsid = "$Header: /afs/cs/project/coda-src/cvs/coda/kernel-src/vfs/linux/cfs_opstats.h,v 4.1 1997/01/08 21:53:19 rvb Exp $";
+#endif /*_BLURB_*/
+
+
+/*
+ * operation stats: what the minicache can intercept that
+ * *isn't* seen by venus.  These stats are kept to augment
+ * the stats maintained by the Volume-Session mechanism.
+ */
+
+/* vfsops:
+ *          mount: not currently bounced to Venus
+ *          umount: nope
+ *          root: only first call, rest is cached.
+ *          statfs: none (bogus)
+ *          sync: none (bogus)
+ *          vget: all
+ */
+
+#define CFS_MOUNT_STATS  0
+#define CFS_UMOUNT_STATS 1
+#define CFS_ROOT_STATS   2
+#define CFS_STATFS_STATS 3
+#define CFS_SYNC_STATS   4
+#define CFS_VGET_STATS   5
+#define CFS_VFSOPS_SIZE  6
+
+/* vnodeops:
+ *            open: all to venus
+ *            close: all to venus
+ *            rdrw: bogus.  Maybe redirected to UFS.
+ *                          May call open/close for internal opens/closes
+ *                          (Does exec not call open?)
+ *            ioctl: causes a lookupname
+ *                   passes through
+ *            select: can't get there from here.
+ *            getattr: can be satsified by cache
+ *            setattr: all go through
+ *            access: can be satisfied by cache
+ *            readlink: can be satisfied by cache
+ *            fsync: passes through
+ *            inactive: passes through
+ *            lookup: can be satisfied by cache
+ *            create: passes through
+ *            remove: passes through
+ *            link: passes through
+ *            rename: passes through
+ *            mkdir: passes through
+ *            rmdir: passes through
+ *            symlink: passes through
+ *            readdir: may be redirected to UFS
+ *                     may cause an "internal" open/close
+ */
+
+#define CFS_OPEN_STATS     0
+#define CFS_CLOSE_STATS    1
+#define CFS_RDWR_STATS     2
+#define CFS_IOCTL_STATS    3
+#define CFS_SELECT_STATS   4
+#define CFS_GETATTR_STATS  5
+#define CFS_SETATTR_STATS  6
+#define CFS_ACCESS_STATS   7
+#define CFS_READLINK_STATS 8
+#define CFS_FSYNC_STATS    9
+#define CFS_INACTIVE_STATS 10
+#define CFS_LOOKUP_STATS   11
+#define CFS_CREATE_STATS   12
+#define CFS_REMOVE_STATS   13
+#define CFS_LINK_STATS     14
+#define CFS_RENAME_STATS   15
+#define CFS_MKDIR_STATS    16
+#define CFS_RMDIR_STATS    17
+#define CFS_SYMLINK_STATS  18
+#define CFS_READDIR_STATS  19
+#define CFS_VNODEOPS_SIZE  20
+
+
+/*
+ * I propose the following structres:
+ */
+
+
+struct cfs_op_stats {
+    int opcode;       /* vfs opcode */
+    long entries;     /* number of times call attempted */
+    long sat_intrn;   /* number of times call satisfied by cache */
+    long unsat_intrn; /* number of times call failed in cache, but
+                         was not bounced to venus proper. */
+    long gen_intrn;   /* number of times call generated internally */
+                      /* (do we need that?) */
+};
+
+
+/*
+ * With each call to the minicache, we'll bump the counters whenver
+ * a call is satisfied internally (through the cache or through a
+ * redirect), and whenever an operation is caused internally.
+ * Then, we can add the total operations caught by the minicache
+ * to the world-wide totals, and leave a caveat for the specific
+ * graphs later.
+ */
diff -u --recursive --new-file v2.1.69/linux/include/linux/coda_psdev.h linux/include/linux/coda_psdev.h
--- v2.1.69/linux/include/linux/coda_psdev.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/coda_psdev.h	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,145 @@
+#ifndef __CODA_PSDEV_H
+#define __CODA_PSDEV_H
+
+#define CODA_PSDEV_MAJOR 67
+#define MAX_CODADEVS  5	   /* how many do we allow */
+
+#include <linux/config.h>
+
+extern struct vcomm psdev_vcomm[];
+
+#define CODA_SUPER_MAGIC	0x73757245
+
+struct coda_sb_info
+{
+  struct inode *      sbi_psdev;     /* /dev/cfs? Venus/kernel device */
+  struct inode *      sbi_ctlcp;     /* control magic file */
+  int                 sbi_refct;
+  struct vcomm *      sbi_vcomm;
+  struct inode *      sbi_root;
+};
+
+
+static inline struct coda_sb_info *coda_sbp(struct super_block *sb)
+{
+    return ((struct coda_sb_info *)((sb)->u.generic_sbp));
+}
+
+
+
+extern void coda_psdev_detach(int unit);
+extern int  init_coda_psdev(void);
+
+/* to aid procedures make upcalls. They must have a 
+   declaration at the top containing:
+       struct inputArgs *inp;
+	struct outputArgs *outp;
+	int error=0;
+	int size;
+*/
+
+#define UPARG(bsize, op)\
+do {\
+  	CODA_ALLOC(inp, struct inputArgs *, (bsize));\
+	outp = (struct outputArgs *) (inp);\
+	INIT_IN(inp, (op))\
+	coda_load_creds(&(inp->cred));\
+        size = (bsize);\
+} while (0)
+
+/* upcalls */
+int venus_rootfid(struct super_block *sb, ViceFid *fidp);
+int venus_getattr(struct super_block *sb, struct ViceFid *fid, 
+		     struct coda_vattr *attr);
+int venus_setattr(struct super_block *, struct ViceFid *, 
+		     struct coda_vattr *);
+int venus_lookup(struct super_block *sb, struct ViceFid *fid, 
+		    const char *name, int length, int *type, 
+		    struct ViceFid *resfid);
+int venus_release(struct super_block *sb, struct ViceFid *fid, int flags);
+int venus_open(struct super_block *sb, struct ViceFid *fid,
+		  int flags, ino_t *ino, dev_t *dev);
+int venus_mkdir(struct super_block *sb, struct ViceFid *dirfid, 
+			  const char *name, int length, 
+			  struct ViceFid *newfid, struct coda_vattr *attrs);
+int venus_create(struct super_block *sb, struct ViceFid *dirfid, 
+		    const char *name, int length, int excl, int mode, 
+		    struct ViceFid *newfid, struct coda_vattr *attrs) ;
+int venus_rmdir(struct super_block *sb, struct ViceFid *dirfid, 
+		    const char *name, int length);
+int venus_remove(struct super_block *sb, struct ViceFid *dirfid, 
+		 const char *name, int length);
+int venus_readlink(struct super_block *sb, struct ViceFid *fid, 
+		   char *buffer, int *length);
+int venus_rename(struct super_block *, struct ViceFid *new_fid, 
+		 struct ViceFid *old_fid, size_t old_length, 
+		 size_t new_length, const char *old_name, 
+		 const char *new_name);
+int venus_link(struct super_block *sb, struct ViceFid *fid, 
+		  struct ViceFid *dirfid, const char *name, int len );
+int venus_symlink(struct super_block *sb, struct ViceFid *fid,
+		  const char *name, int len, const char *symname, int symlen);
+int venus_access(struct super_block *sb, struct ViceFid *fid, int mask);
+int venus_pioctl(struct super_block *sb, struct ViceFid *fid,
+		 unsigned int cmd, struct PioctlData *data);
+int coda_downcall(int opcode, struct outputArgs *out);
+int coda_upcall(struct coda_sb_info *mntinfo, int inSize, 
+		int *outSize, struct inputArgs *buffer);
+
+
+/* messages between coda filesystem in kernel and Venus */
+struct vmsg {
+    struct queue        vm_chain;
+    caddr_t	        vm_data;
+    u_short	        vm_flags;
+    u_short             vm_inSize;  /* Size is at most 5000 bytes */
+    u_short	        vm_outSize;
+    u_short	        vm_opcode;  /* copied from data to save lookup */
+    int		        vm_unique;
+    struct wait_queue  *vm_sleep;   /* process' wait queue */
+};
+
+/* communication pending/processing queues queues */
+struct vcomm {
+	u_long		    vc_seq;
+	struct wait_queue  *vc_waitq; /* Venus wait queue */
+	struct queue	    vc_pending;
+	struct queue	    vc_processing;
+};
+
+static inline int vcomm_open(struct vcomm *vcp)
+{
+        return ((vcp)->vc_pending.forw != NULL);
+}
+
+static inline void mark_vcomm_closed(struct vcomm *vcp)
+{
+        (vcp)->vc_pending.forw = NULL;
+}
+
+/*
+ * Statistics
+ */
+struct coda_upcallstats {
+	int	ncalls;			/* client requests */
+	int	nbadcalls;		/* upcall failures */
+	int	reqs[CFS_NCALLS];	/* count of each request */
+} ;
+
+extern struct coda_upcallstats coda_callstats;
+
+static inline void clstats(int opcode)
+{
+    coda_callstats.ncalls++;
+    if ( (0 <= opcode) && (opcode <= CFS_NCALLS) )
+	coda_callstats.reqs[opcode]++;
+    else
+	printk("clstats called with bad opcode %d\n", opcode); 
+}
+
+static inline void badclstats(void)
+{
+    coda_callstats.nbadcalls++;
+}
+
+#endif
diff -u --recursive --new-file v2.1.69/linux/include/linux/coda_sysctl.h linux/include/linux/coda_sysctl.h
--- v2.1.69/linux/include/linux/coda_sysctl.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/coda_sysctl.h	Tue Dec  2 14:24:04 1997
@@ -0,0 +1,2 @@
+void coda_sysctl_init(void);
+void coda_sysctl_clean(void);
diff -u --recursive --new-file v2.1.69/linux/include/linux/ftape.h linux/include/linux/ftape.h
--- v2.1.69/linux/include/linux/ftape.h	Wed Nov 26 16:24:03 1997
+++ linux/include/linux/ftape.h	Tue Dec  2 14:52:44 1997
@@ -159,7 +159,7 @@
 #  undef  CONFIG_FT_FDC_DMA
 #  define CONFIG_FT_FDC_DMA 3
 # endif
-#elif CONIFG_FT_MACH2 == 1    /* CONFIG_FT_PROBE_FC10 == 1 */
+#elif CONFIG_FT_MACH2 == 1    /* CONFIG_FT_PROBE_FC10 == 1 */
 # if CONFIG_FT_FDC_BASE == 0
 #  undef  CONFIG_FT_FDC_BASE
 #  define CONFIG_FT_FDC_BASE 0x1E0
diff -u --recursive --new-file v2.1.69/linux/include/linux/gscd.h linux/include/linux/gscd.h
--- v2.1.69/linux/include/linux/gscd.h	Thu Apr 11 23:49:46 1996
+++ linux/include/linux/gscd.h	Wed Dec 31 16:00:00 1969
@@ -1,110 +0,0 @@
-/*
- * Definitions for a GoldStar R420 CD-ROM interface
- *
- *   Copyright (C) 1995  Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>
- *                       Eberhard Moenkeberg <emoenke@gwdg.de>
- *
- *  Published under the GPL.
- *
- */
-
-
-/* The Interface Card default address is 0x340. This will work for most
-   applications. Address selection is accomplished by jumpers PN801-1 to
-   PN801-4 on the GoldStar Interface Card.
-   Appropriate settings are: 0x300, 0x310, 0x320, 0x330, 0x340, 0x350, 0x360
-   0x370, 0x380, 0x390, 0x3A0, 0x3B0, 0x3C0, 0x3D0, 0x3E0, 0x3F0             */
-
-/* insert here the I/O port address */
-#define GSCD_BASE_ADDR	        0x340
-
-/* change this to set the dma-channel */
-#define GSCD_DMA_CHANNEL        3                  /* not used */ 
-
-/************** nothing to set up below here *********************/
-
-/* port access macro */
-#define GSCDPORT(x)		(gscd_port + (x))
-
-/*
- * commands
- * the lower nibble holds the command length
- */
-#define CMD_STATUS     0x01
-#define CMD_READSUBQ   0x02 /* 1: ?, 2: UPC, 5: ? */
-#define CMD_SEEK       0x05 /* read_mode M-S-F */
-#define CMD_READ       0x07 /* read_mode M-S-F nsec_h nsec_l */
-#define CMD_RESET      0x11
-#define CMD_SETMODE    0x15
-#define CMD_PLAY       0x17 /* M-S-F M-S-F */
-#define CMD_LOCK_CTL   0x22 /* 0: unlock, 1: lock */
-#define CMD_IDENT      0x31
-#define CMD_SETSPEED   0x32 /* 0: auto */ /* ??? */
-#define CMD_GETMODE    0x41
-#define CMD_PAUSE      0x51
-#define CMD_READTOC    0x61
-#define CMD_DISKINFO   0x71
-#define CMD_TRAY_CTL   0x81
-
-/*
- * disk_state:
- */
-#define ST_PLAYING	0x80
-#define ST_UNLOCKED	0x40
-#define ST_NO_DISK	0x20
-#define ST_DOOR_OPEN	0x10
-#define ST_x08  0x08
-#define ST_x04	0x04
-#define ST_INVALID	0x02
-#define ST_x01	0x01
-
-/*
- * cmd_type:
- */
-#define TYPE_INFO	0x01
-#define TYPE_DATA	0x02
-
-/*
- * read_mode:
- */
-#define MOD_POLLED	0x80
-#define MOD_x08	0x08
-#define MOD_RAW	0x04
-
-#define READ_DATA(port, buf, nr) insb(port, buf, nr)
-
-#define SET_TIMER(func, jifs) \
-	((timer_table[GSCD_TIMER].expires = jiffies + jifs), \
-	(timer_table[GSCD_TIMER].fn = func), \
-	(timer_active |= 1<<GSCD_TIMER))
-
-#define CLEAR_TIMER		timer_active &= ~(1<<GSCD_TIMER)
-
-#define MAX_TRACKS		104
-
-struct msf {
-	unsigned char	min;
-	unsigned char	sec;
-	unsigned char	frame;
-};
-
-struct gscd_Play_msf {
-	struct msf	start;
-	struct msf	end;
-};
-
-struct gscd_DiskInfo {
-	unsigned char	first;
-	unsigned char	last;
-	struct msf	diskLength;
-	struct msf	firstTrack;
-};
-
-struct gscd_Toc {
-	unsigned char	ctrl_addr;
-	unsigned char	track;
-	unsigned char	pointIndex;
-	struct msf	trackTime;
-	struct msf	diskTime;
-};
-
diff -u --recursive --new-file v2.1.69/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v2.1.69/linux/include/linux/interrupt.h	Sat Nov 29 11:25:12 1997
+++ linux/include/linux/interrupt.h	Tue Dec  2 09:19:03 1997
@@ -34,6 +34,7 @@
 	DIGI_BH,
 	SERIAL_BH,
 	RISCOM8_BH,
+	SPECIALIX_BH,
 	ESP_BH,
 	NET_BH,
 	IMMEDIATE_BH,
diff -u --recursive --new-file v2.1.69/linux/include/linux/isp16.h linux/include/linux/isp16.h
--- v2.1.69/linux/include/linux/isp16.h	Tue Feb 13 00:30:25 1996
+++ linux/include/linux/isp16.h	Wed Dec 31 16:00:00 1969
@@ -1,75 +0,0 @@
-/* -- isp16.h
- *
- *  Header for detection and initialisation of cdrom interface (only) on
- *  ISP16 (MAD16, Mozart) sound card.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-/* These are the default values */
-#define ISP16_CDROM_TYPE "Sanyo"
-#define ISP16_CDROM_IO_BASE 0x340
-#define ISP16_CDROM_IRQ 0
-#define ISP16_CDROM_DMA 0
-
-/* Some (Media)Magic */
-/* define types of drive the interface on an ISP16 card may be looking at */
-#define ISP16_DRIVE_X 0x00
-#define ISP16_SONY  0x02
-#define ISP16_PANASONIC0 0x02
-#define ISP16_SANYO0 0x02
-#define ISP16_MITSUMI  0x04
-#define ISP16_PANASONIC1 0x06
-#define ISP16_SANYO1 0x06
-#define ISP16_DRIVE_NOT_USED 0x08  /* not used */
-#define ISP16_DRIVE_SET_MASK 0xF1  /* don't change 0-bit or 4-7-bits*/
-/* ...for port */
-#define ISP16_DRIVE_SET_PORT 0xF8D
-/* set io parameters */
-#define ISP16_BASE_340  0x00
-#define ISP16_BASE_330  0x40
-#define ISP16_BASE_360  0x80
-#define ISP16_BASE_320  0xC0
-#define ISP16_IRQ_X  0x00
-#define ISP16_IRQ_5  0x04  /* shouldn't be used to avoid sound card conflicts */
-#define ISP16_IRQ_7  0x08  /* shouldn't be used to avoid sound card conflicts */
-#define ISP16_IRQ_3  0x0C
-#define ISP16_IRQ_9  0x10
-#define ISP16_IRQ_10  0x14
-#define ISP16_IRQ_11  0x18
-#define ISP16_DMA_X  0x03
-#define ISP16_DMA_3  0x00
-#define ISP16_DMA_5  0x00
-#define ISP16_DMA_6  0x01
-#define ISP16_DMA_7  0x02
-#define ISP16_IO_SET_MASK  0x20  /* don't change 5-bit */
-/* ...for port */
-#define ISP16_IO_SET_PORT  0xF8E
-/* enable the card */
-#define ISP16_C928__ENABLE_PORT  0xF90  /* ISP16 with OPTi 82C928 chip */
-#define ISP16_C929__ENABLE_PORT  0xF91  /* ISP16 with OPTi 82C929 chip */
-#define ISP16_ENABLE_CDROM  0x80  /* seven bit */
-
-/* the magic stuff */
-#define ISP16_CTRL_PORT  0xF8F
-#define ISP16_C928__CTRL  0xE2  /* ISP16 with OPTi 82C928 chip */
-#define ISP16_C929__CTRL  0xE3  /* ISP16 with OPTi 82C929 chip */
-
-#define ISP16_IO_BASE 0xF8D
-#define ISP16_IO_SIZE 5  /* ports used from 0xF8D up to 0xF91 */
-
-void isp16_setup(char *str, int *ints);
-int isp16_init(void);
diff -u --recursive --new-file v2.1.69/linux/include/linux/limits.h linux/include/linux/limits.h
--- v2.1.69/linux/include/linux/limits.h	Thu Feb  6 02:38:33 1997
+++ linux/include/linux/limits.h	Tue Dec  2 13:44:40 1997
@@ -14,4 +14,6 @@
 #define PATH_MAX        4095	/* # chars in a path name */
 #define PIPE_BUF        4096	/* # bytes in atomic write to a pipe */
 
+#define RTSIG_MAX	  32
+
 #endif
diff -u --recursive --new-file v2.1.69/linux/include/linux/major.h linux/include/linux/major.h
--- v2.1.69/linux/include/linux/major.h	Sat Nov 29 11:25:12 1997
+++ linux/include/linux/major.h	Tue Dec  2 09:19:03 1997
@@ -73,6 +73,9 @@
 #define MKISS_MAJOR	55
 #define DSP56K_MAJOR    55   /* DSP56001 processor device */
 
+#define SPECIALIX_NORMAL_MAJOR 75
+#define SPECIALIX_CALLOUT_MAJOR 76
+
 /*
  * Tests for SCSI devices.
  */
diff -u --recursive --new-file v2.1.69/linux/include/linux/mcd.h linux/include/linux/mcd.h
--- v2.1.69/linux/include/linux/mcd.h	Sun Nov 12 22:54:55 1995
+++ linux/include/linux/mcd.h	Wed Dec 31 16:00:00 1969
@@ -1,116 +0,0 @@
-/*
- * Definitions for a Mitsumi CD-ROM interface
- *
- *	Copyright (C) 1992  Martin Harriss
- *
- *	martin@bdsi.com
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-/* *** change this to set the I/O port address */
-#define MCD_BASE_ADDR	        0x300
-
-/* *** change this to set the interrupt number */
-#define MCD_INTR_NR     11
-
-/* *** make the following line uncommented, if you're sure,
- * *** all configuration is done */
-/* #define I_WAS_HERE */
-
-/* Increase this if you get lots of timeouts */
-#define MCD_STATUS_DELAY	200
-
-/* number of times to retry a command before giving up */
-#define MCD_RETRY_ATTEMPTS      5
-
-/* port access macro */
-#define MCDPORT(x)		(mcd_port + (x))
-
-/* status bits */
-
-#define MST_CMD_CHECK		0x01		/* command error */
-#define MST_BUSY		0x02		/* now playing */
-#define MST_READ_ERR		0x04		/* read error */
-#define MST_DSK_TYPE		0x08
-#define MST_SERVO_CHECK		0x10
-#define MST_DSK_CHG		0x20		/* disk removed or changed */
-#define MST_READY		0x40		/* disk in the drive */
-#define MST_DOOR_OPEN		0x80		/* door is open */
-
-/* flag bits */
-
-#define MFL_DATA		0x02		/* data available */
-#define MFL_STATUS		0x04		/* status available */
-
-/* commands */
-
-#define MCMD_GET_DISK_INFO	0x10		/* read info from disk */
-#define MCMD_GET_Q_CHANNEL	0x20		/* read info from q channel */
-#define MCMD_GET_STATUS		0x40
-#define MCMD_SET_MODE		0x50
-#define MCMD_SOFT_RESET		0x60
-#define MCMD_STOP		0x70		/* stop play */
-#define MCMD_CONFIG_DRIVE	0x90
-#define MCMD_SET_VOLUME		0xAE		/* set audio level */
-#define MCMD_PLAY_READ		0xC0		/* play or read data */
-#define MCMD_GET_VERSION  	0xDC
-#define MCMD_EJECT		0xF6            /* eject (FX drive) */
-
-/* borrowed from hd.c */
-
-#define READ_DATA(port, buf, nr) \
-insb(port, buf, nr)
-
-#define SET_TIMER(func, jifs) \
-	((timer_table[MCD_TIMER].expires = jiffies + jifs), \
-	(timer_table[MCD_TIMER].fn = func), \
-	(timer_active |= 1<<MCD_TIMER))
-
-#define CLEAR_TIMER		timer_active &= ~(1<<MCD_TIMER)
-
-#define MAX_TRACKS		104
-
-struct msf {
-	unsigned char	min;
-	unsigned char	sec;
-	unsigned char	frame;
-};
-
-struct mcd_Play_msf {
-	struct msf	start;
-	struct msf	end;
-};
-
-struct mcd_DiskInfo {
-	unsigned char	first;
-	unsigned char	last;
-	struct msf	diskLength;
-	struct msf	firstTrack;
-};
-
-struct mcd_Toc {
-	unsigned char	ctrl_addr;
-	unsigned char	track;
-	unsigned char	pointIndex;
-	struct msf	trackTime;
-	struct msf	diskTime;
-};
-
-#ifndef I_WAS_HERE
-#warning You have not edited mcd.h
-#warning Perhaps irq and i/o settings are wrong.
-#endif
diff -u --recursive --new-file v2.1.69/linux/include/linux/mcdx.h linux/include/linux/mcdx.h
--- v2.1.69/linux/include/linux/mcdx.h	Thu Jun  6 12:23:07 1996
+++ linux/include/linux/mcdx.h	Wed Dec 31 16:00:00 1969
@@ -1,184 +0,0 @@
-/*
- * Definitions for the Mitsumi CDROM interface
- * Copyright (C) 1995 1996 Heiko Schlittermann <heiko@lotte.sax.de>
- * VERSION: @VERSION@
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Thanks to
- *  The Linux Community at all and ...
- *  Martin Harris (he wrote the first Mitsumi Driver)
- *  Eberhard Moenkeberg (he gave me much support and the initial kick)
- *  Bernd Huebner, Ruediger Helsch (Unifix-Software Gmbh, they
- *      improved the original driver)
- *  Jon Tombs, Bjorn Ekwall (module support)
- *  Daniel v. Mosnenck (he sent me the Technical and Programming Reference)
- *  Gerd Knorr (he lent me his PhotoCD)
- *  Nils Faerber and Roger E. Wolff (extensively tested the LU portion)
- *  Andreas Kies (testing the mysterious hang up's)
- *  ... somebody forgotten?
- *  Marcin Dalecki
- *  
- */
-
-/*
- *	The following lines are for user configuration
- *	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *	{0|1} -- 1 if you want the driver detect your drive, may crash and
- *	needs a long time to seek.  The higher the address the longer the
- *	seek.
- *
- *  WARNING: AUTOPROBE doesn't work.
- */
-#define MCDX_AUTOPROBE 0
-
-/*
- *	Drive specific settings according to the jumpers on the controller
- *	board(s).
- *	o	MCDX_NDRIVES  :  number of used entries of the following table
- *	o	MCDX_DRIVEMAP :  table of {i/o base, irq} per controller
- *
- *	NOTE: I didn't get a drive at irq 9(2) working.  Not even alone.
- */
-#if MCDX_AUTOPROBE == 0
-	#define MCDX_NDRIVES 1
-	#define MCDX_DRIVEMAP {		\
-			{0x300, 11},	\
-			{0x304, 05},  	\
-			{0x000, 00},  	\
-			{0x000, 00},  	\
-			{0x000, 00},  	\
-	  	}
-#else
-	#error Autoprobing is not implemented yet.
-#endif
-
-#ifndef MCDX_QUIET
-#define MCDX_QUIET   1
-#endif
-
-#ifndef MCDX_DEBUG
-#define MCDX_DEBUG   0
-#endif
-
-/* *** make the following line uncommented, if you're sure,
- * *** all configuration is done */
-/* #define I_WAS_HERE */
-#define I_WAS_HERE   /* delete this line, it's for heiko only */
-
-/*	The name of the device */
-#define MCDX "mcdx"	
-
-/* Flags for DEBUGGING */
-#define INIT 		0
-#define MALLOC 		0
-#define IOCTL 		0
-#define PLAYTRK     0
-#define SUBCHNL     0
-#define TOCHDR      0
-#define MS          0
-#define PLAYMSF     0
-#define READTOC     0
-#define OPENCLOSE 	0
-#define HW		    0
-#define TALK		0
-#define IRQ 		0
-#define XFER 		0
-#define REQUEST	 	0
-#define SLEEP		0
-
-/*	The following addresses are taken from the Mitsumi Reference 
- *  and describe the possible i/o range for the controller.
- */
-#define MCDX_IO_BEGIN	((char*) 0x300)	/* first base of i/o addr */
-#define MCDX_IO_END		((char*) 0x3fc)	/* last base of i/o addr */
-
-/*	Per controller 4 bytes i/o are needed. */
-#define MCDX_IO_SIZE		4
-
-/*
- *	Bits
- */
-
-/* The status byte, returned from every command, set if
- * the description is true */
-#define MCDX_RBIT_OPEN       0x80	/* door is open */
-#define MCDX_RBIT_DISKSET    0x40	/* disk set (recognised) */
-#define MCDX_RBIT_CHANGED    0x20	/* disk was changed */
-#define MCDX_RBIT_CHECK      0x10	/* disk rotates, servo is on */
-#define MCDX_RBIT_AUDIOTR    0x08   /* current track is audio */
-#define MCDX_RBIT_RDERR      0x04	/* read error, refer SENSE KEY */
-#define MCDX_RBIT_AUDIOBS    0x02	/* currently playing audio */
-#define MCDX_RBIT_CMDERR     0x01	/* command, param or format error */
-
-/* The I/O Register holding the h/w status of the drive,
- * can be read at i/o base + 1 */
-#define MCDX_RBIT_DOOR       0x10	/* door is open */
-#define MCDX_RBIT_STEN       0x04	/* if 0, i/o base contains drive status */
-#define MCDX_RBIT_DTEN       0x02	/* if 0, i/o base contains data */
-
-/*
- *	The commands.
- */
-
-#define OPCODE	1		/* offset of opcode */
-#define MCDX_CMD_REQUEST_TOC		1, 0x10
-#define MCDX_CMD_REQUEST_STATUS		1, 0x40 
-#define MCDX_CMD_RESET				1, 0x60
-#define MCDX_CMD_REQUEST_DRIVE_MODE	1, 0xc2
-#define MCDX_CMD_SET_INTERLEAVE		2, 0xc8, 0
-#define MCDX_CMD_DATAMODE_SET		2, 0xa0, 0
-	#define MCDX_DATAMODE1		0x01
-	#define MCDX_DATAMODE2		0x02
-#define MCDX_CMD_LOCK_DOOR		2, 0xfe, 0
-
-#define READ_AHEAD			4	/* 8 Sectors (4K) */
-
-/*	Useful macros */
-#define e_door(x)		((x) & MCDX_RBIT_OPEN)
-#define e_check(x)		(~(x) & MCDX_RBIT_CHECK)
-#define e_notset(x)		(~(x) & MCDX_RBIT_DISKSET)
-#define e_changed(x)	((x) & MCDX_RBIT_CHANGED)
-#define e_audio(x)		((x) & MCDX_RBIT_AUDIOTR)
-#define e_audiobusy(x)	((x) & MCDX_RBIT_AUDIOBS)
-#define e_cmderr(x)		((x) & MCDX_RBIT_CMDERR)
-#define e_readerr(x)	((x) & MCDX_RBIT_RDERR)
-
-/**	no drive specific */
-#define MCDX_CDBLK	2048	/* 2048 cooked data each blk */
-
-#define MCDX_DATA_TIMEOUT	(HZ/10)	/* 0.1 second */
-
-/*
- * Access to the msf array
- */
-#define MSF_MIN		0			/* minute */
-#define MSF_SEC		1			/* second */
-#define MSF_FRM		2			/* frame  */
-
-/*
- * Errors
- */
-#define MCDX_E		1			/* unspec error */
-#define MCDX_ST_EOM 0x0100		/* end of media */
-#define MCDX_ST_DRV 0x00ff		/* mask to query the drive status */
-
-#ifndef I_WAS_HERE
-#warning You have not edited mcdx.h
-#warning Perhaps irq and i/o settings are wrong.
-#endif
-
-/* ex:set ts=4 sw=4: */
diff -u --recursive --new-file v2.1.69/linux/include/linux/optcd.h linux/include/linux/optcd.h
--- v2.1.69/linux/include/linux/optcd.h	Tue Jun  4 00:53:46 1996
+++ linux/include/linux/optcd.h	Wed Dec 31 16:00:00 1969
@@ -1,52 +0,0 @@
-/*	linux/include/linux/optcd.h - Optics Storage 8000 AT CDROM driver
-	$Id: optcd.h,v 1.2 1996/01/15 18:43:44 root Exp root $
-
-	Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl)
-
-
-	Configuration file for linux/drivers/cdrom/optcd.c
-*/
-
-#ifndef _LINUX_OPTCD_H
-#define _LINUX_OPTCD_H
-
-
-/* I/O base of drive. Drive uses base to base+2.
-   This setting can be overridden with the kernel or insmod command
-   line option 'optcd=<portbase>'. Use address of 0 to disable driver. */
-#define OPTCD_PORTBASE	0x340
-
-
-/* enable / disable parts of driver by define / undef */
-#define	MULTISESSION		/* multisession support (ALPHA) */
-
-
-/* Change 0 to 1 to debug various parts of the driver */
-#define	DEBUG_DRIVE_IF	0	/* Low level drive interface */
-#define	DEBUG_CONV	0	/* Address conversions */
-#define	DEBUG_BUFFERS	0	/* Buffering and block size conversion */
-#define	DEBUG_REQUEST	0	/* Request mechanism */
-#define	DEBUG_STATE	0	/* State machine */
-#define	DEBUG_TOC	0	/* Q-channel and Table of Contents */
-#define	DEBUG_MULTIS	0	/* Multisession code */
-#define	DEBUG_VFS	0	/* VFS interface */
-
-
-/* Don't touch these unless you know what you're doing. */
-
-/* Various timeout loop repetition counts. */
-#define BUSY_TIMEOUT		10000000	/* for busy wait */
-#define FAST_TIMEOUT		100000		/* ibid. for probing */
-#define SLEEP_TIMEOUT		6000		/* for timer wait */
-#define MULTI_SEEK_TIMEOUT	1000		/* for timer wait */
-#define READ_TIMEOUT		6000		/* for poll wait */
-#define STOP_TIMEOUT		2000		/* for poll wait */
-#define RESET_WAIT		5000		/* busy wait at drive reset */
-
-/* # of buffers for block size conversion. 6 is optimal for my setup (P75),
-   giving 280 kb/s, with 0.4% CPU usage. Experiment to find your optimal
-   setting */
-#define N_BUFS		6
-
-
-#endif _LINUX_OPTCD_H
diff -u --recursive --new-file v2.1.69/linux/include/linux/sbpcd.h linux/include/linux/sbpcd.h
--- v2.1.69/linux/include/linux/sbpcd.h	Sat Aug 31 23:14:25 1996
+++ linux/include/linux/sbpcd.h	Wed Dec 31 16:00:00 1969
@@ -1,858 +0,0 @@
-/*
- * sbpcd.h   Specify interface address and interface type here.
- */
-
-/*
- * Attention! This file contains user-serviceable parts!
- * I recommend to make use of it...
- * If you feel helpless, look into linux/Documentation/cdrom/sbpcd
- * (good idea anyway, at least before mailing me).
- *
- * The definitions for the first controller can get overridden by
- * the kernel command line ("lilo boot option").
- * Examples:
- *                                 sbpcd=0x300,LaserMate
- *                             or
- *                                 sbpcd=0x230,SoundBlaster
- *                             or
- *                                 sbpcd=0x338,SoundScape
- *                             or
- *                                 sbpcd=0x2C0,Teac16bit
- *
- * If sbpcd gets used as a module, you can load it with
- *     insmod sbpcd.o sbpcd=0x300,0
- * or
- *     insmod sbpcd.o sbpcd=0x230,1
- * or
- *     insmod sbpcd.o sbpcd=0x338,2
- * or
- *     insmod sbpcd.o sbpcd=0x2C0,3
- * respective to override the configured address and type.
- */
-
-/*
- * define your CDROM port base address as CDROM_PORT
- * and specify the type of your interface card as SBPRO.
- *
- * address:
- * ========
- * SBPRO type addresses typically are 0x0230 (=0x220+0x10), 0x0250, ...
- * LASERMATE type (CI-101P, WDH-7001C) addresses typically are 0x0300, ...
- * SOUNDSCAPE addresses are from the LASERMATE type and range. You have to
- * specify the REAL address here, not the configuration port address. Look
- * at the CDROM driver's invoking line within your DOS CONFIG.SYS, or let
- * sbpcd auto-probe, if you are not firm with the address.
- * There are some soundcards on the market with 0x0630, 0x0650, ...; their
- * type is not obvious (both types are possible).
- *
- * example: if your SBPRO audio address is 0x220, specify 0x230 and SBPRO 1.
- *          if your soundcard has its CDROM port above 0x300, specify
- *          that address and try SBPRO 0 first.
- *          if your SoundScape configuration port is at 0x330, specify
- *          0x338 and SBPRO 2.
- *
- * interface type:
- * ===============
- * set SBPRO to 1 for "true" SoundBlaster card
- * set SBPRO to 0 for "compatible" soundcards and
- *                for "poor" (no sound) interface cards.
- * set SBPRO to 2 for Ensonic SoundScape or SPEA Media FX cards
- * set SBPRO to 3 for Teac 16bit interface cards
- *
- * Almost all "compatible" sound boards need to set SBPRO to 0.
- * If SBPRO is set wrong, the drives will get found - but any
- * data access will give errors (audio access will work).
- * The "OmniCD" no-sound interface card from CreativeLabs and most Teac
- * interface cards need SBPRO 1.
- *
- * sound base:
- * ===========
- * The SOUND_BASE definition tells if we should try to turn the CD sound
- * channels on. It will only be of use regarding soundcards with a SbPro
- * compatible mixer.
- *
- * Example: #define SOUND_BASE 0x220 enables the sound card's CD channels
- *          #define SOUND_BASE 0     leaves the soundcard untouched
- */
-#if !(SBPCD_ISSUE-1)     /* first (or if you have only one) interface board: */
-#define CDROM_PORT 0x340 /* <-----------<< port address                      */
-#define SBPRO      0     /* <-----------<< interface type                    */
-#define MAX_DRIVES 4     /* set to 1 if the card does not use "drive select" */
-#define SOUND_BASE 0x220 /* <-----------<< sound address of this card or 0   */
-#endif
-#if !(SBPCD_ISSUE-2)     /* ==================== second interface board: === */
-#define CDROM_PORT 0x344 /* <-----------<< port address                      */
-#define SBPRO      0     /* <-----------<< interface type                    */
-#define MAX_DRIVES 4     /* set to 1 if the card does not use "drive select" */
-#define SOUND_BASE 0x000 /* <-----------<< sound address of this card or 0   */
-#endif
-#if !(SBPCD_ISSUE-3)     /* ===================== third interface board: === */
-#define CDROM_PORT 0x630 /* <-----------<< port address                      */
-#define SBPRO      1     /* <-----------<< interface type                    */
-#define MAX_DRIVES 4     /* set to 1 if the card does not use "drive select" */
-#define SOUND_BASE 0x240 /* <-----------<< sound address of this card or 0   */
-#endif
-#if !(SBPCD_ISSUE-4)     /* ==================== fourth interface board: === */
-#define CDROM_PORT 0x634 /* <-----------<< port address                      */
-#define SBPRO      0     /* <-----------<< interface type                    */
-#define MAX_DRIVES 4     /* set to 1 if the card does not use "drive select" */
-#define SOUND_BASE 0x000 /* <-----------<< sound address of this card or 0   */
-#endif
-
-/*
- * some more or less user dependent definitions - service them!
- */
-
-/* Set this to 0 once you have configured your interface definitions right. */
-#define DISTRIBUTION 1
-
-/*
- * Time to wait after giving a message.
- * This gets important if you enable non-standard DBG_xxx flags.
- * You will see what happens if you omit the pause or make it
- * too short. Be warned!
- */
-#define KLOGD_PAUSE 1
-
-/* tray control: eject tray if no disk is in */
-#if DISTRIBUTION
-#define JUKEBOX 0
-#else
-#define JUKEBOX 1
-#endif DISTRIBUTION
-
-/* tray control: eject tray after last use */
-#if DISTRIBUTION
-#define EJECT 0
-#else
-#define EJECT 1
-#endif DISTRIBUTION
-
-/* max. number of audio frames to read with one     */
-/* request (allocates n* 2352 bytes kernel memory!) */
-/* may be freely adjusted, f.e. 75 (= 1 sec.), at   */
-/* runtime by use of the CDROMAUDIOBUFSIZ ioctl.    */
-#define READ_AUDIO 0
-
-/* Optimizations for the Teac CD-55A drive read performance.
- * SBP_TEAC_SPEED can be changed here, or one can set the 
- * variable "teac" when loading as a module.
- * Valid settings are:
- *   0 - very slow - the recommended "DISTRIBUTION 1" setup.
- *   1 - 2x performance with little overhead. No busy waiting.
- *   2 - 4x performance with 5ms overhead per read. Busy wait.
- *
- * Setting SBP_TEAC_SPEED or the variable 'teac' to anything
- * other than 0 may cause problems. If you run into them, first
- * change SBP_TEAC_SPEED back to 0 and see if your drive responds
- * normally. If yes, you are "allowed" to report your case - to help
- * me with the driver, not to solve your hassle. Don´t mail if you
- * simply are stuck into your own "tuning" experiments, you know?
- */
-#define SBP_TEAC_SPEED 1
-
-/*==========================================================================*/
-/*==========================================================================*/
-/*
- * nothing to change below here if you are not fully aware what you're doing
- */
-#ifndef _LINUX_SBPCD_H
-
-#define _LINUX_SBPCD_H
-/*==========================================================================*/
-/*==========================================================================*/
-/*
- * driver's own read_ahead, data mode
- */
-#define SBP_BUFFER_FRAMES 8 
-
-#define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */
-#undef  FUTURE
-#undef SAFE_MIXED
-
-#define TEST_UPC 0
-#define SPEA_TEST 0
-#define TEST_STI 0
-#define OLD_BUSY 0
-#undef PATH_CHECK
-#ifndef SOUND_BASE
-#define SOUND_BASE 0
-#endif
-#if DISTRIBUTION
-#undef SBP_TEAC_SPEED
-#define SBP_TEAC_SPEED 0
-#endif
-/*==========================================================================*/
-/*
- * DDI interface definitions
- * "invented" by Fred N. van Kempen..
- */
-#define DDIOCSDBG	0x9000
-
-/*==========================================================================*/
-/*
- * "private" IOCTL functions
- */
-#define CDROMAUDIOBUFSIZ	0x5382 /* set the audio buffer size */
-
-/*==========================================================================*/
-/*
- * Debug output levels
- */
-#define DBG_INF	1	/* necessary information */
-#define DBG_BSZ	2	/* BLOCK_SIZE trace */
-#define DBG_REA	3	/* READ status trace */
-#define DBG_CHK	4	/* MEDIA CHECK trace */
-#define DBG_TIM	5	/* datarate timer test */
-#define DBG_INI	6	/* initialization trace */
-#define DBG_TOC	7	/* tell TocEntry values */
-#define DBG_IOC	8	/* ioctl trace */
-#define DBG_STA	9	/* ResponseStatus() trace */
-#define DBG_ERR	10	/* cc_ReadError() trace */
-#define DBG_CMD	11	/* cmd_out() trace */
-#define DBG_WRN	12	/* give explanation before auto-probing */
-#define DBG_MUL	13	/* multi session code test */
-#define DBG_IDX	14	/* test code for drive_id !=0 */
-#define DBG_IOX	15	/* some special information */
-#define DBG_DID	16	/* drive ID test */
-#define DBG_RES	17	/* drive reset info */
-#define DBG_SPI	18	/* SpinUp test */
-#define DBG_IOS	19	/* ioctl trace: subchannel functions */
-#define DBG_IO2	20	/* ioctl trace: general */
-#define DBG_UPC	21	/* show UPC information */
-#define DBG_XA1	22	/* XA mode debugging */
-#define DBG_LCK	23	/* door (un)lock info */
-#define DBG_SQ1	24	/* dump SubQ frame */
-#define DBG_AUD	25	/* READ AUDIO debugging */
-#define DBG_SEQ	26	/* Sequoia interface configuration trace */
-#define DBG_LCS	27	/* Longshine LCS-7260 debugging trace */
-#define DBG_CD2	28	/* MKE/Funai CD200 debugging trace */
-#define DBG_TEA	29	/* TEAC CD-55A debugging trace */
-#define DBG_ECS	30	/* ECS-AT (Vertos 100) debugging trace */
-#define DBG_000	31	/* unnecessary information */
-
-/*==========================================================================*/
-/*==========================================================================*/
-
-/*
- * bits of flags_cmd_out:
- */
-#define f_respo3		0x100
-#define f_putcmd		0x80
-#define f_respo2		0x40
-#define f_lopsta		0x20
-#define f_getsta		0x10
-#define f_ResponseStatus	0x08
-#define f_obey_p_check		0x04
-#define f_bit1			0x02
-#define f_wait_if_busy		0x01
-
-/*
- * diskstate_flags:
- */
-#define x80_bit			0x80
-#define upc_bit			0x40
-#define volume_bit		0x20
-#define toc_bit			0x10
-#define multisession_bit	0x08
-#define cd_size_bit		0x04
-#define subq_bit		0x02
-#define frame_size_bit		0x01
-
-/*
- * disk states (bits of diskstate_flags):
- */
-#define upc_valid		(D_S[d].diskstate_flags&upc_bit)
-#define volume_valid		(D_S[d].diskstate_flags&volume_bit)
-#define toc_valid		(D_S[d].diskstate_flags&toc_bit)
-#define cd_size_valid		(D_S[d].diskstate_flags&cd_size_bit)
-#define subq_valid		(D_S[d].diskstate_flags&subq_bit)
-#define frame_size_valid	(D_S[d].diskstate_flags&frame_size_bit)
-
-/*
- * the status_bits variable
- */
-#define p_success	0x100
-#define p_door_closed	0x80
-#define p_caddy_in	0x40
-#define p_spinning	0x20
-#define p_check		0x10
-#define p_busy_new	0x08
-#define p_door_locked	0x04
-#define p_disk_ok	0x01
-
-/*
- * LCS-7260 special status result bits:
- */
-#define p_lcs_door_locked	0x02
-#define p_lcs_door_closed	0x01 /* probably disk_in */
-
-/*
- * CR-52x special status result bits:
- */
-#define p_caddin_old	0x40
-#define p_success_old	0x08
-#define p_busy_old	0x04
-#define p_bit_1		0x02	/* hopefully unused now */
-
-/*
- * "generation specific" defs of the status result bits:
- */
-#define p0_door_closed	0x80
-#define p0_caddy_in	0x40
-#define p0_spinning	0x20
-#define p0_check	0x10
-#define p0_success	0x08 /* unused */
-#define p0_busy		0x04
-#define p0_bit_1	0x02 /* unused */
-#define p0_disk_ok	0x01
-
-#define pL_disk_in	0x40
-#define pL_spinning	0x20
-#define pL_check	0x10
-#define pL_success	0x08 /* unused ?? */
-#define pL_busy		0x04
-#define pL_door_locked	0x02
-#define pL_door_closed	0x01
-
-#define pV_door_closed	0x40
-#define pV_spinning	0x20
-#define pV_check	0x10
-#define pV_success	0x08
-#define pV_busy		0x04
-#define pV_door_locked	0x02
-#define pV_disk_ok	0x01
-
-#define p1_door_closed	0x80
-#define p1_disk_in	0x40
-#define p1_spinning	0x20
-#define p1_check	0x10
-#define p1_busy		0x08
-#define p1_door_locked	0x04
-#define p1_bit_1	0x02 /* unused */
-#define p1_disk_ok	0x01
-
-#define p2_disk_ok	0x80
-#define p2_door_locked	0x40
-#define p2_spinning	0x20
-#define p2_busy2	0x10
-#define p2_busy1	0x08
-#define p2_door_closed	0x04
-#define p2_disk_in	0x02
-#define p2_check	0x01
-
-/*
- * used drive states:
- */
-#define st_door_closed	(D_S[d].status_bits&p_door_closed)
-#define st_caddy_in	(D_S[d].status_bits&p_caddy_in)
-#define st_spinning	(D_S[d].status_bits&p_spinning)
-#define st_check	(D_S[d].status_bits&p_check)
-#define st_busy		(D_S[d].status_bits&p_busy_new)
-#define st_door_locked	(D_S[d].status_bits&p_door_locked)
-#define st_diskok	(D_S[d].status_bits&p_disk_ok)
-
-/*
- * bits of the CDi_status register:
- */
-#define s_not_result_ready	0x04 /* 0: "result ready" */
-#define s_not_data_ready	0x02 /* 0: "data ready"   */
-#define s_attention		0x01 /* 1: "attention required" */
-/*
- * usable as:
- */
-#define DRV_ATTN	((inb(CDi_status)&s_attention)!=0)
-#define DATA_READY	((inb(CDi_status)&s_not_data_ready)==0)
-#define RESULT_READY	((inb(CDi_status)&s_not_result_ready)==0)
-
-/*
- * drive families and types (firmware versions):
- */
-#define drv_fam0	0x0100		/* CR-52x family */
-#define drv_199		(drv_fam0+0x01)	/* <200 */
-#define drv_200		(drv_fam0+0x02)	/* <201 */
-#define drv_201		(drv_fam0+0x03)	/* <210 */
-#define drv_210		(drv_fam0+0x04)	/* <211 */
-#define drv_211		(drv_fam0+0x05)	/* <300 */
-#define drv_300		(drv_fam0+0x06)	/* >=300 */
-
-#define drv_fam1	0x0200		/* CR-56x family */
-#define drv_099		(drv_fam1+0x01)	/* <100 */
-#define drv_100		(drv_fam1+0x02)	/* >=100, only 1.02 and 5.00 known */
-
-#define drv_fam2	0x0400		/* CD200 family */
-
-#define drv_famT	0x0800		/* TEAC CD-55A */
-
-#define drv_famL	0x1000		/* Longshine family */
-#define drv_260		(drv_famL+0x01)	/* LCS-7260 */
-#define drv_e1		(drv_famL+0x01)	/* LCS-7260, firmware "A E1" */
-#define drv_f4		(drv_famL+0x02)	/* LCS-7260, firmware "A4F4" */
-
-#define drv_famV	0x2000		/* ECS-AT (vertos-100) family */
-#define drv_at		(drv_famV+0x01)	/* ECS-AT, firmware "1.00" */
-
-#define fam0_drive	(D_S[d].drv_type&drv_fam0)
-#define famL_drive	(D_S[d].drv_type&drv_famL)
-#define famV_drive	(D_S[d].drv_type&drv_famV)
-#define fam1_drive	(D_S[d].drv_type&drv_fam1)
-#define fam2_drive	(D_S[d].drv_type&drv_fam2)
-#define famT_drive	(D_S[d].drv_type&drv_famT)
-#define fam0L_drive	(D_S[d].drv_type&(drv_fam0|drv_famL))
-#define fam0V_drive	(D_S[d].drv_type&(drv_fam0|drv_famV))
-#define famLV_drive	(D_S[d].drv_type&(drv_famL|drv_famV))
-#define fam0LV_drive	(D_S[d].drv_type&(drv_fam0|drv_famL|drv_famV))
-#define fam1L_drive	(D_S[d].drv_type&(drv_fam1|drv_famL))
-#define fam1V_drive	(D_S[d].drv_type&(drv_fam1|drv_famV))
-#define fam1LV_drive	(D_S[d].drv_type&(drv_fam1|drv_famL|drv_famV))
-#define fam01_drive	(D_S[d].drv_type&(drv_fam0|drv_fam1))
-#define fam12_drive	(D_S[d].drv_type&(drv_fam1|drv_fam2))
-#define fam2T_drive	(D_S[d].drv_type&(drv_fam2|drv_famT))
-
-/*
- * audio states:
- */
-#define audio_playing	2
-#define audio_pausing	1
-
-/*
- * drv_pattern, drv_options:
- */
-#define speed_auto	0x80
-#define speed_300	0x40
-#define speed_150	0x20
-#define audio_mono	0x04
-
-/*
- * values of cmd_type (0 else):
- */
-#define READ_M1	0x01	/* "data mode 1": 2048 bytes per frame */
-#define READ_M2	0x02	/* "data mode 2": 12+2048+280 bytes per frame */
-#define READ_SC	0x04	/* "subchannel info": 96 bytes per frame */
-#define READ_AU	0x08	/* "audio frame": 2352 bytes per frame */
-
-/*
- * sense_byte:
- *
- *          values: 00
- *                  01
- *                  81
- *                  82 "raw audio" mode
- *                  xx from infobuf[0] after 85 00 00 00 00 00 00
- */
-
-/* audio status (bin) */
-#define aud_00 0x00 /* Audio status byte not supported or not valid */
-#define audx11 0x0b /* Audio play operation in progress             */
-#define audx12 0x0c /* Audio play operation paused                  */
-#define audx13 0x0d /* Audio play operation successfully completed  */
-#define audx14 0x0e /* Audio play operation stopped due to error    */
-#define audx15 0x0f /* No current audio status to return            */
-/* audio status (bcd) */
-#define aud_11 0x11 /* Audio play operation in progress             */
-#define aud_12 0x12 /* Audio play operation paused                  */
-#define aud_13 0x13 /* Audio play operation successfully completed  */
-#define aud_14 0x14 /* Audio play operation stopped due to error    */
-#define aud_15 0x15 /* No current audio status to return            */
-
-/*
- * highest allowed drive number (MINOR+1)
- */
-#define NR_SBPCD	4
-
-/*
- * we try to never disable interrupts - seems to work
- */
-#define SBPCD_DIS_IRQ	0
-
-/*
- * "write byte to port"
- */
-#define OUT(x,y)	outb(y,x)
-
-/*==========================================================================*/
-
-#define MIXER_addr SOUND_BASE+4 /* sound card's address register */
-#define MIXER_data SOUND_BASE+5 /* sound card's data register */
-#define MIXER_CD_Volume	0x28	/* internal SB Pro register address */
-
-/*==========================================================================*/
-
-#define MAX_TRACKS	99
-
-#define ERR_DISKCHANGE 615
-
-/*==========================================================================*/
-/*
- * To make conversions easier (machine dependent!)
- */
-typedef union _msf
-{
-	u_int n;
-	u_char c[4];
-} MSF;
-
-typedef union _blk
-{
-	u_int n;
-	u_char c[4];
-} BLK;
-
-/*==========================================================================*/
-
-/*============================================================================
-==============================================================================
-
-COMMAND SET of "old" drives like CR-521, CR-522
-               (the CR-562 family is different):
-
-No.	Command			       Code
---------------------------------------------
-
-Drive Commands:
- 1	Seek				01	
- 2	Read Data			02
- 3	Read XA-Data			03
- 4	Read Header			04
- 5	Spin Up				05
- 6	Spin Down			06
- 7	Diagnostic			07
- 8	Read UPC			08
- 9	Read ISRC			09
-10	Play Audio			0A
-11	Play Audio MSF			0B
-12	Play Audio Track/Index		0C
-
-Status Commands:
-13	Read Status			81	
-14	Read Error			82
-15	Read Drive Version		83
-16	Mode Select			84
-17	Mode Sense			85
-18	Set XA Parameter		86
-19	Read XA Parameter		87
-20	Read Capacity			88
-21	Read SUB_Q			89
-22	Read Disc Code			8A
-23	Read Disc Information		8B
-24	Read TOC			8C
-25	Pause/Resume			8D
-26	Read Packet			8E
-27	Read Path Check			00
- 
- 
-all numbers (lba, msf-bin, msf-bcd, counts) to transfer high byte first
-
-mnemo     7-byte command        #bytes response (r0...rn)
-________ ____________________  ____ 
-
-Read Status:
-status:  81.                    (1)  one-byte command, gives the main
-                                                          status byte
-Read Error:
-check1:  82 00 00 00 00 00 00.  (6)  r1: audio status
-
-Read Packet:
-check2:  8e xx 00 00 00 00 00. (xx)  gets xx bytes response, relating
-                                        to commands 01 04 05 07 08 09
-
-Play Audio:
-play:    0a ll-bb-aa nn-nn-nn.  (0)  play audio, ll-bb-aa: starting block (lba),
-                                                 nn-nn-nn: #blocks
-Play Audio MSF:
-         0b mm-ss-ff mm-ss-ff   (0)  play audio from/to
-
-Play Audio Track/Index:
-         0c ...
-
-Pause/Resume:
-pause:   8d pr 00 00 00 00 00.  (0)  pause (pr=00) 
-                                     resume (pr=80) audio playing
-
-Mode Select:
-         84 00 nn-nn ??-?? 00   (0)  nn-nn: 2048 or 2340
-                                     possibly defines transfer size
-
-set_vol: 84 83 00 00 sw le 00.  (0)  sw(itch): lrxxxxxx (off=1)
-                                     le(vel): min=0, max=FF, else half
-				     (firmware 2.11)
-
-Mode Sense:
-get_vol: 85 03 00 00 00 00 00.  (2)  tell current audio volume setting
-
-Read Disc Information:
-tocdesc: 8b 00 00 00 00 00 00.  (6)  read the toc descriptor ("msf-bin"-format)
-
-Read TOC:
-tocent:  8c fl nn 00 00 00 00.  (8)  read toc entry #nn
-                                       (fl=0:"lba"-, =2:"msf-bin"-format)
-
-Read Capacity:
-capacit: 88 00 00 00 00 00 00.  (5)  "read CD-ROM capacity"
-
-
-Read Path Check:
-ping:    00 00 00 00 00 00 00.  (2)  r0=AA, r1=55
-                                     ("ping" if the drive is connected)
-
-Read Drive Version:
-ident:   83 00 00 00 00 00 00. (12)  gives "MATSHITAn.nn" 
-                                     (n.nn = 2.01, 2.11., 3.00, ...)
-
-Seek:
-seek:    01 00 ll-bb-aa 00 00.  (0)  
-seek:    01 02 mm-ss-ff 00 00.  (0)  
-
-Read Data:
-read:    02 xx-xx-xx nn-nn fl. (??)  read nn-nn blocks of 2048 bytes,
-                                     starting at block xx-xx-xx  
-                                     fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx
-
-Read XA-Data:
-read:    03 xx-xx-xx nn-nn fl. (??)  read nn-nn blocks of 2340 bytes, 
-                                     starting at block xx-xx-xx
-                                     fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx
-
-Read SUB_Q:
-         89 fl 00 00 00 00 00. (13)  r0: audio status, r4-r7: lba/msf, 
-                                       fl=0: "lba", fl=2: "msf"
-
-Read Disc Code:
-         8a 00 00 00 00 00 00. (14)  possibly extended "check condition"-info
-
-Read Header:
-         04 00 ll-bb-aa 00 00.  (0)   4 bytes response with "check2"
-         04 02 mm-ss-ff 00 00.  (0)   4 bytes response with "check2"
-
-Spin Up:
-         05 00 ll-bb-aa 00 00.  (0)  possibly implies a "seek"
-
-Spin Down:
-         06 ...
-
-Diagnostic:
-         07 00 ll-bb-aa 00 00.  (2)   2 bytes response with "check2"
-         07 02 mm-ss-ff 00 00.  (2)   2 bytes response with "check2"
-
-Read UPC:
-         08 00 ll-bb-aa 00 00. (16)  
-         08 02 mm-ss-ff 00 00. (16)  
-
-Read ISRC:
-         09 00 ll-bb-aa 00 00. (15)  15 bytes response with "check2"
-         09 02 mm-ss-ff 00 00. (15)  15 bytes response with "check2"
-
-Set XA Parameter:
-         86 ...
-
-Read XA Parameter:
-         87 ...
-
-==============================================================================
-============================================================================*/
-
-/*
- * commands
- *
- * CR-52x:      CMD0_
- * CR-56x:      CMD1_
- * CD200:       CMD2_
- * LCS-7260:    CMDL_
- * TEAC CD-55A: CMDT_
- * ECS-AT:      CMDV_
- */
-#define CMD1_RESET	0x0a
-#define CMD2_RESET	0x01
-#define CMDT_RESET	0xc0
-
-#define CMD1_LOCK_CTL	0x0c
-#define CMD2_LOCK_CTL	0x1e
-#define CMDT_LOCK_CTL	CMD2_LOCK_CTL
-#define CMDL_LOCK_CTL	0x0e
-#define CMDV_LOCK_CTL	CMDL_LOCK_CTL
-
-#define CMD1_TRAY_CTL	0x07
-#define CMD2_TRAY_CTL	0x1b
-#define CMDT_TRAY_CTL	CMD2_TRAY_CTL
-#define CMDL_TRAY_CTL	0x0d
-#define CMDV_TRAY_CTL	CMDL_TRAY_CTL
-
-#define CMD1_MULTISESS	0x8d
-#define CMDL_MULTISESS	0x8c
-#define CMDV_MULTISESS	CMDL_MULTISESS
-
-#define CMD1_SUBCHANINF	0x11
-#define CMD2_SUBCHANINF	0x??
-
-#define CMD1_ABORT	0x08
-#define CMD2_ABORT	0x08
-#define CMDT_ABORT	0x08
-
-#define CMD2_x02	0x02
-
-#define CMD2_SETSPEED	0xda
-
-#define CMD0_PATH_CHECK	0x00
-#define CMD1_PATH_CHECK	0x???
-#define CMD2_PATH_CHECK	0x???
-#define CMDT_PATH_CHECK	0x???
-#define CMDL_PATH_CHECK	CMD0_PATH_CHECK
-#define CMDV_PATH_CHECK	CMD0_PATH_CHECK
-
-#define CMD0_SEEK	0x01
-#define CMD1_SEEK	CMD0_SEEK
-#define CMD2_SEEK	0x2b
-#define CMDT_SEEK	CMD2_SEEK
-#define CMDL_SEEK	CMD0_SEEK
-#define CMDV_SEEK	CMD0_SEEK
-
-#define CMD0_READ	0x02
-#define CMD1_READ	0x10
-#define CMD2_READ	0x28
-#define CMDT_READ	CMD2_READ
-#define CMDL_READ	CMD0_READ
-#define CMDV_READ	CMD0_READ
-
-#define CMD0_READ_XA	0x03
-#define CMD2_READ_XA	0xd4
-#define CMD2_READ_XA2	0xd5
-#define CMDL_READ_XA	CMD0_READ_XA /* really ?? */
-#define CMDV_READ_XA	CMD0_READ_XA
-
-#define CMD0_READ_HEAD	0x04
-
-#define CMD0_SPINUP	0x05
-#define CMD1_SPINUP	0x02
-#define CMD2_SPINUP	CMD2_TRAY_CTL
-#define CMDL_SPINUP	CMD0_SPINUP
-#define CMDV_SPINUP	CMD0_SPINUP
-
-#define CMD0_SPINDOWN	0x06 /* really??? */
-#define CMD1_SPINDOWN	0x06
-#define CMD2_SPINDOWN	CMD2_TRAY_CTL
-#define CMDL_SPINDOWN	0x0d
-#define CMDV_SPINDOWN	CMD0_SPINDOWN
-
-#define CMD0_DIAG	0x07
-
-#define CMD0_READ_UPC	0x08
-#define CMD1_READ_UPC	0x88
-#define CMD2_READ_UPC	0x???
-#define CMDL_READ_UPC	CMD0_READ_UPC
-#define CMDV_READ_UPC	0x8f
-
-#define CMD0_READ_ISRC	0x09
-
-#define CMD0_PLAY	0x0a
-#define CMD1_PLAY	0x???
-#define CMD2_PLAY	0x???
-#define CMDL_PLAY	CMD0_PLAY
-#define CMDV_PLAY	CMD0_PLAY
-
-#define CMD0_PLAY_MSF	0x0b
-#define CMD1_PLAY_MSF	0x0e
-#define CMD2_PLAY_MSF	0x47
-#define CMDT_PLAY_MSF	CMD2_PLAY_MSF
-#define CMDL_PLAY_MSF	0x???
-
-#define CMD0_PLAY_TI	0x0c
-#define CMD1_PLAY_TI	0x0f
-
-#define CMD0_STATUS	0x81
-#define CMD1_STATUS	0x05
-#define CMD2_STATUS	0x00
-#define CMDT_STATUS	CMD2_STATUS
-#define CMDL_STATUS	CMD0_STATUS
-#define CMDV_STATUS	CMD0_STATUS
-#define CMD2_SEEK_LEADIN 0x00
-
-#define CMD0_READ_ERR	0x82
-#define CMD1_READ_ERR	CMD0_READ_ERR
-#define CMD2_READ_ERR	0x03
-#define CMDT_READ_ERR	CMD2_READ_ERR /* get audio status */
-#define CMDL_READ_ERR	CMD0_READ_ERR
-#define CMDV_READ_ERR	CMD0_READ_ERR
-
-#define CMD0_READ_VER	0x83
-#define CMD1_READ_VER	CMD0_READ_VER
-#define CMD2_READ_VER	0x12
-#define CMDT_READ_VER	CMD2_READ_VER /* really ?? */
-#define CMDL_READ_VER	CMD0_READ_VER
-#define CMDV_READ_VER	CMD0_READ_VER
-
-#define CMD0_SETMODE	0x84
-#define CMD1_SETMODE	0x09
-#define CMD2_SETMODE	0x55
-#define CMDT_SETMODE	CMD2_SETMODE
-#define CMDL_SETMODE	CMD0_SETMODE
-
-#define CMD0_GETMODE	0x85
-#define CMD1_GETMODE	0x84
-#define CMD2_GETMODE	0x5a
-#define CMDT_GETMODE	CMD2_GETMODE
-#define CMDL_GETMODE	CMD0_GETMODE
-
-#define CMD0_SET_XA	0x86
-
-#define CMD0_GET_XA	0x87
-
-#define CMD0_CAPACITY	0x88
-#define CMD1_CAPACITY	0x85
-#define CMD2_CAPACITY	0x25
-#define CMDL_CAPACITY	CMD0_CAPACITY /* missing in some firmware versions */
-
-#define CMD0_READSUBQ	0x89
-#define CMD1_READSUBQ	0x87
-#define CMD2_READSUBQ	0x42
-#define CMDT_READSUBQ	CMD2_READSUBQ
-#define CMDL_READSUBQ	CMD0_READSUBQ
-#define CMDV_READSUBQ	CMD0_READSUBQ
-
-#define CMD0_DISKCODE	0x8a
-
-#define CMD0_DISKINFO	0x8b
-#define CMD1_DISKINFO	CMD0_DISKINFO
-#define CMD2_DISKINFO	0x43
-#define CMDT_DISKINFO	CMD2_DISKINFO
-#define CMDL_DISKINFO	CMD0_DISKINFO
-#define CMDV_DISKINFO	CMD0_DISKINFO
-
-#define CMD0_READTOC	0x8c
-#define CMD1_READTOC	CMD0_READTOC
-#define CMD2_READTOC	0x???
-#define CMDL_READTOC	CMD0_READTOC
-#define CMDV_READTOC	CMD0_READTOC
-
-#define CMD0_PAU_RES	0x8d
-#define CMD1_PAU_RES	0x0d
-#define CMD2_PAU_RES	0x4b
-#define CMDT_PAUSE	CMD2_PAU_RES
-#define CMDL_PAU_RES	CMD0_PAU_RES
-#define CMDV_PAUSE	CMD0_PAU_RES
-
-#define CMD0_PACKET	0x8e
-#define CMD1_PACKET	CMD0_PACKET
-#define CMD2_PACKET	0x???
-#define CMDL_PACKET	CMD0_PACKET
-#define CMDV_PACKET	0x???
-
-/*==========================================================================*/
-/*==========================================================================*/
-#endif _LINUX_SBPCD_H
-/*==========================================================================*/
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file. 
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff -u --recursive --new-file v2.1.69/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.1.69/linux/include/linux/sched.h	Mon Dec  1 12:04:15 1997
+++ linux/include/linux/sched.h	Tue Dec  2 14:52:20 1997
@@ -182,7 +182,7 @@
 	long counter;
 	long priority;
 	unsigned long flags;	/* per process flags, defined below */
-	int errno;
+	int sigpending;
 	long debugreg[8];  /* Hardware debugging registers */
 	struct exec_domain *exec_domain;
 /* various fields */
@@ -273,7 +273,6 @@
 					/* Not implemented yet, only for 486*/
 #define PF_STARTING	0x00000002	/* being created */
 #define PF_EXITING	0x00000004	/* getting shut down */
-#define PF_SIGPENDING	0x00000008	/* at least on unblocked sig ready */
 #define PF_PTRACED	0x00000010	/* set if ptrace (0) has been called */
 #define PF_TRACESYS	0x00000020	/* tracing system calls */
 #define PF_FORKNOEXEC	0x00000040	/* forked but didn't exec */
@@ -467,7 +466,7 @@
 
 extern inline int signal_pending(struct task_struct *p)
 {
-	return (p->flags & PF_SIGPENDING) != 0;
+	return (p->sigpending != 0);
 }
 
 /* Reevaluate whether the task has signals pending delivery.
@@ -476,7 +475,7 @@
 
 static inline void recalc_sigpending(struct task_struct *t)
 {
-	unsigned long ready, nflags;
+	unsigned long ready;
 	long i;
 
 	switch (_NSIG_WORDS) {
@@ -498,12 +497,7 @@
 	case 1: ready  = t->signal.sig[0] &~ t->blocked.sig[0];
 	}
 
-	/* Poor gcc has trouble with conditional moves... */
-	nflags = t->flags &~ PF_SIGPENDING;
-	if (ready)
-		nflags = t->flags | PF_SIGPENDING;
-
-	t->flags = nflags;
+	t->sigpending = (ready != 0);
 }
 
 
diff -u --recursive --new-file v2.1.69/linux/include/linux/sjcd.h linux/include/linux/sjcd.h
--- v2.1.69/linux/include/linux/sjcd.h	Thu Apr 11 23:49:47 1996
+++ linux/include/linux/sjcd.h	Wed Dec 31 16:00:00 1969
@@ -1,181 +0,0 @@
-/*
- * Definitions for a Sanyo CD-ROM interface.
- *
- *   Copyright (C) 1995  Vadim V. Model
- *                                       model@cecmow.enet.dec.com
- *                                       vadim@rbrf.msk.su
- *                                       vadim@ipsun.ras.ru
- *                       Eric van der Maarel
- *                                       H.T.M.v.d.Maarel@marin.nl
- *
- *  This information is based on mcd.c from M. Harriss and sjcd102.lst from
- *  E. Moenkeberg.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef __SJCD_H__
-#define __SJCD_H__
-
-/*
- * Change this to set the I/O port address as default. More flexibility
- * come with setup implementation.
- */
-#define SJCD_BASE_ADDR      0x340
-
-/*
- * Change this to set the irq as default. Really SANYO do not use interrupts
- * at all.
- */
-#define SJCD_INTR_NR        0
-
-/*
- * Change this to set the dma as default value. really SANYO does not use
- * direct memory access at all.
- */
-#define SJCD_DMA_NR         0
-
-/*
- * Macros which allow us to find out the status of the drive.
- */
-#define SJCD_STATUS_AVAILABLE( x ) (((x)&0x02)==0)
-#define SJCD_DATA_AVAILABLE( x )   (((x)&0x01)==0)
-
-/*
- * Port access macro. Three ports are available: S-data port (command port),
- * status port (read only) and D-data port (read only).
- */
-#define SJCDPORT( x )       ( sjcd_base + ( x ) )
-#define SJCD_STATUS_PORT    SJCDPORT( 1 )
-#define SJCD_S_DATA_PORT    SJCDPORT( 0 )
-#define SJCD_COMMAND_PORT   SJCDPORT( 0 )
-#define SJCD_D_DATA_PORT    SJCDPORT( 2 )
-
-/*
- * Drive info bits. Drive info available as first (mandatory) byte of
- * command completion status.
- */
-#define SST_NOT_READY       0x10        /* no disk in the drive (???) */
-#define SST_MEDIA_CHANGED   0x20        /* disk is changed */
-#define SST_DOOR_OPENED     0x40        /* door is open */
-
-/* commands */
-
-#define SCMD_EJECT_TRAY     0xD0        /* eject tray if not locked */
-#define SCMD_LOCK_TRAY      0xD2        /* lock tray when in */
-#define SCMD_UNLOCK_TRAY    0xD4        /* unlock tray when in */
-#define SCMD_CLOSE_TRAY     0xD6        /* load tray in */
-
-#define SCMD_RESET          0xFA        /* soft reset */
-#define SCMD_GET_STATUS     0x80
-#define SCMD_GET_VERSION    0xCC
-
-#define SCMD_DATA_READ      0xA0        /* are the same, depend on mode&args */
-#define SCMD_SEEK           0xA0
-#define SCMD_PLAY           0xA0
-
-#define SCMD_GET_QINFO      0xA8
-
-#define SCMD_SET_MODE       0xC4
-#define SCMD_MODE_PLAY      0xE0
-#define SCMD_MODE_COOKED    (0xF8 & ~0x20)
-#define SCMD_MODE_RAW       0xF9
-#define SCMD_MODE_x20_BIT   0x20        /* What is it for ? */
-
-#define SCMD_SET_VOLUME     0xAE
-#define SCMD_PAUSE          0xE0
-#define SCMD_STOP           0xE0
-
-#define SCMD_GET_DISK_INFO  0xAA
-
-/*
- * Some standard arguments for SCMD_GET_DISK_INFO.
- */
-#define SCMD_GET_1_TRACK    0xA0    /* get the first track information */
-#define SCMD_GET_L_TRACK    0xA1    /* get the last track information */
-#define SCMD_GET_D_SIZE     0xA2    /* get the whole disk information */
-
-/*
- * Borrowed from hd.c. Allows to optimize multiple port read commands.
- */
-#define S_READ_DATA( port, buf, nr )      insb( port, buf, nr )
-
-/*
- * We assume that there are no audio disks with TOC length more than this
- * number (I personally have never seen disks with more than 20 fragments).
- */
-#define SJCD_MAX_TRACKS		100
-
-struct msf {
-  unsigned char   min;
-  unsigned char   sec;
-  unsigned char   frame;
-};
-
-struct sjcd_hw_disk_info {
-  unsigned char track_control;
-  unsigned char track_no;
-  unsigned char x, y, z;
-  union {
-    unsigned char track_no;
-    struct msf track_msf;
-  } un;
-};
-
-struct sjcd_hw_qinfo {
-  unsigned char track_control;
-  unsigned char track_no;
-  unsigned char x;
-  struct msf rel;
-  struct msf abs;
-};
-
-struct sjcd_play_msf {
-  struct msf  start;
-  struct msf  end;
-};
-
-struct sjcd_disk_info {
-  unsigned char   first;
-  unsigned char   last;
-  struct msf      disk_length;
-  struct msf      first_track;
-};
-
-struct sjcd_toc {
-  unsigned char   ctrl_addr;
-  unsigned char   track;
-  unsigned char   point_index;
-  struct msf      track_time;
-  struct msf      disk_time;
-};
-
-#if defined( SJCD_GATHER_STAT )
-
-struct sjcd_stat {
-  int ticks;
-  int tticks[ 8 ];
-  int idle_ticks;
-  int start_ticks;
-  int mode_ticks;
-  int read_ticks;
-  int data_ticks;
-  int stop_ticks;
-  int stopping_ticks;
-};
-
-#endif
-
-#endif
diff -u --recursive --new-file v2.1.69/linux/include/linux/sonycd535.h linux/include/linux/sonycd535.h
--- v2.1.69/linux/include/linux/sonycd535.h	Tue Aug 29 00:15:53 1995
+++ linux/include/linux/sonycd535.h	Wed Dec 31 16:00:00 1969
@@ -1,183 +0,0 @@
-#ifndef SONYCD535_H
-#define SONYCD535_H
-
-/*
- * define all the commands recognized by the CDU-531/5
- */
-#define SONY535_REQUEST_DRIVE_STATUS_1		(0x80)
-#define SONY535_REQUEST_SENSE			(0x82)
-#define SONY535_REQUEST_DRIVE_STATUS_2		(0x84)
-#define SONY535_REQUEST_ERROR_STATUS		(0x86)
-#define SONY535_REQUEST_AUDIO_STATUS		(0x88)
-#define SONY535_INQUIRY				(0x8a)
-
-#define SONY535_SET_INACTIVITY_TIME		(0x90)
-
-#define SONY535_SEEK_AND_READ_N_BLOCKS_1	(0xa0)
-#define SONY535_SEEK_AND_READ_N_BLOCKS_2	(0xa4)
-#define SONY535_PLAY_AUDIO			(0xa6)
-
-#define SONY535_REQUEST_DISC_CAPACITY		(0xb0)
-#define SONY535_REQUEST_TOC_DATA		(0xb2)
-#define SONY535_REQUEST_SUB_Q_DATA		(0xb4)
-#define SONY535_REQUEST_ISRC			(0xb6)
-#define SONY535_REQUEST_UPC_EAN			(0xb8)
-
-#define SONY535_SET_DRIVE_MODE			(0xc0)
-#define SONY535_REQUEST_DRIVE_MODE		(0xc2)
-#define SONY535_SET_RETRY_COUNT			(0xc4)
-
-#define SONY535_DIAGNOSTIC_1			(0xc6)
-#define SONY535_DIAGNOSTIC_4			(0xcc)
-#define SONY535_DIAGNOSTIC_5			(0xce)
-
-#define SONY535_EJECT_CADDY			(0xd0)
-#define SONY535_DISABLE_EJECT_BUTTON		(0xd2)
-#define SONY535_ENABLE_EJECT_BUTTON		(0xd4)
-
-#define SONY535_HOLD				(0xe0)
-#define SONY535_AUDIO_PAUSE_ON_OFF		(0xe2)
-#define SONY535_SET_VOLUME			(0xe8)
-
-#define SONY535_STOP				(0xf0)
-#define SONY535_SPIN_UP				(0xf2)
-#define SONY535_SPIN_DOWN			(0xf4)
-
-#define SONY535_CLEAR_PARAMETERS		(0xf6)
-#define SONY535_CLEAR_ENDING_ADDRESS		(0xf8)
-
-/*
- * define some masks
- */
-#define SONY535_DATA_NOT_READY_BIT		(0x1)
-#define SONY535_RESULT_NOT_READY_BIT		(0x2)
-
-/*
- *  drive status 1
- */
-#define SONY535_STATUS1_COMMAND_ERROR		(0x1)
-#define SONY535_STATUS1_DATA_ERROR		(0x2)
-#define SONY535_STATUS1_SEEK_ERROR		(0x4)
-#define SONY535_STATUS1_DISC_TYPE_ERROR		(0x8)
-#define SONY535_STATUS1_NOT_SPINNING		(0x10)
-#define SONY535_STATUS1_EJECT_BUTTON_PRESSED	(0x20)
-#define SONY535_STATUS1_CADDY_NOT_INSERTED	(0x40)
-#define SONY535_STATUS1_BYTE_TWO_FOLLOWS	(0x80)
-
-/*
- * drive status 2
- */
-#define SONY535_CDD_LOADING_ERROR		(0x7)
-#define SONY535_CDD_NO_DISC			(0x8)
-#define SONY535_CDD_UNLOADING_ERROR		(0x9)
-#define SONY535_CDD_CADDY_NOT_INSERTED		(0xd)
-#define SONY535_ATN_RESET_OCCURRED		(0x2)
-#define SONY535_ATN_DISC_CHANGED		(0x4)
-#define SONY535_ATN_RESET_AND_DISC_CHANGED	(0x6)
-#define SONY535_ATN_EJECT_IN_PROGRESS		(0xe)
-#define SONY535_ATN_BUSY			(0xf)
-
-/*
- * define some parameters
- */
-#define SONY535_AUDIO_DRIVE_MODE		(0)
-#define SONY535_CDROM_DRIVE_MODE		(0xe0)
-
-#define SONY535_PLAY_OP_PLAYBACK		(0)
-#define SONY535_PLAY_OP_ENTER_HOLD		(1)
-#define SONY535_PLAY_OP_SET_AUDIO_ENDING_ADDR	(2)
-#define SONY535_PLAY_OP_SCAN_FORWARD		(3)
-#define SONY535_PLAY_OP_SCAN_BACKWARD		(4)
-
-/*
- *  convert from msf format to block number 
- */
-#define SONY_BLOCK_NUMBER(m,s,f) (((m)*60L+(s))*75L+(f))
-#define SONY_BLOCK_NUMBER_MSF(x) (((x)[0]*60L+(x)[1])*75L+(x)[2])
-
-/*
- *  error return values from the doSonyCmd() routines
- */
-#define TIME_OUT			(-1)
-#define NO_CDROM			(-2)
-#define BAD_STATUS			(-3)
-#define CD_BUSY				(-4)
-#define NOT_DATA_CD			(-5)
-#define NO_ROOM				(-6)
-
-#define LOG_START_OFFSET        150     /* Offset of first logical sector */
-
-#define SONY_JIFFIES_TIMEOUT	(5*HZ)	/* Maximum time
-					   the drive will wait/try for an
-					   operation */
-#define SONY_READY_RETRIES      (50000)  /* How many times to retry a
-                                                  spin waiting for a register
-                                                  to come ready */
-#define SONY535_FAST_POLLS	(10000)   /* how many times recheck 
-                                                  status waiting for a data
-                                                  to become ready */
-
-typedef unsigned char Byte;
-
-/*
- * This is the complete status returned from the drive configuration request
- * command.
- */
-struct s535_sony_drive_config
-{
-   char vendor_id[8];
-   char product_id[16];
-   char product_rev_level[4];
-};
-
-/* The following is returned from the request sub-q data command */
-struct s535_sony_subcode
-{
-   unsigned char address        :4;
-   unsigned char control        :4;
-   unsigned char track_num;
-   unsigned char index_num;
-   unsigned char rel_msf[3];
-   unsigned char abs_msf[3];
-};
-
-struct s535_sony_disc_capacity
-{
-   Byte mFirstTrack, sFirstTrack, fFirstTrack;
-   Byte mLeadOut, sLeadOut, fLeadOut;
-};
-
-/*
- * The following is returned from the request TOC (Table Of Contents) command.
- * (last_track_num-first_track_num+1) values are valid in tracks.
- */
-struct s535_sony_toc
-{
-   unsigned char reserved0      :4;
-   unsigned char control0       :4;
-   unsigned char point0;
-   unsigned char first_track_num;
-   unsigned char reserved0a;
-   unsigned char reserved0b;
-   unsigned char reserved1      :4;
-   unsigned char control1       :4;
-   unsigned char point1;
-   unsigned char last_track_num;
-   unsigned char dummy1;
-   unsigned char dummy2;
-   unsigned char reserved2      :4;
-   unsigned char control2       :4;
-   unsigned char point2;
-   unsigned char lead_out_start_msf[3];
-   struct
-   {
-      unsigned char reserved    :4;
-      unsigned char control     :4;
-      unsigned char track;
-      unsigned char track_start_msf[3];
-   } tracks[100];
-
-   unsigned int lead_out_start_lba;
-};
-
-#endif /* SONYCD535_H */
diff -u --recursive --new-file v2.1.69/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.1.69/linux/include/linux/sysctl.h	Mon Dec  1 12:04:15 1997
+++ linux/include/linux/sysctl.h	Tue Dec  2 11:41:45 1997
@@ -302,6 +302,14 @@
 /* CTL_DEBUG names: */
 
 /* CTL_DEV names: */
+enum {
+	DEV_CDROM = 1,
+};
+
+/* /proc/sys/dev/cdrom */
+enum {
+	DEV_CDROM_INFO = 1,
+};
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.1.69/linux/include/linux/tty.h linux/include/linux/tty.h
--- v2.1.69/linux/include/linux/tty.h	Tue Dec  2 09:49:40 1997
+++ linux/include/linux/tty.h	Tue Dec  2 14:52:20 1997
@@ -307,6 +307,7 @@
 extern int stl_init(void);
 extern int stli_init(void);
 extern int riscom8_init(void);
+extern int specialix_init(void);
 extern int espserial_init(void);
 
 extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
diff -u --recursive --new-file v2.1.69/linux/include/linux/ucdrom.h linux/include/linux/ucdrom.h
--- v2.1.69/linux/include/linux/ucdrom.h	Sun Dec  1 01:01:02 1996
+++ linux/include/linux/ucdrom.h	Wed Dec 31 16:00:00 1969
@@ -1,114 +0,0 @@
-/* ucdrom.h. Uniform cdrom data structures for cdrom.c. 	-*- linux-c -*-
-   Copyright (c) 1996 David van Leeuwen. 
- */
-
-#ifndef LINUX_UCDROM_H
-#define LINUX_UCDROM_H
-
-#ifdef __KERNEL__
-struct cdrom_device_info {
-	struct cdrom_device_ops  *ops;  /* link to device_ops */
-	struct cdrom_device_info *next; /* next device_info for this major */
-	void *handle;		        /* driver-dependent data */
-/* specifications */
-        kdev_t dev;	                /* device number */
-	int mask;                       /* mask of capability: disables them */
-	int speed;			/* maximum speed for reading data */
-	int capacity;			/* number of discs in jukebox */
-/* device-related storage */
-	int options : 30;               /* options flags */
-	unsigned mc_flags : 2;          /* media change buffer flags */
-    	int use_count;                  /* number of times device opened */
-};
-
-struct cdrom_device_ops {
-/* routines */
-	int (*open) (struct cdrom_device_info *, int);
-	void (*release) (struct cdrom_device_info *);
-	int (*drive_status) (struct cdrom_device_info *, int);
-	int (*disc_status) (struct cdrom_device_info *);
-	int (*media_changed) (struct cdrom_device_info *, int);
-	int (*tray_move) (struct cdrom_device_info *, int);
-	int (*lock_door) (struct cdrom_device_info *, int);
-	int (*select_speed) (struct cdrom_device_info *, int);
-	int (*select_disc) (struct cdrom_device_info *, int);
-	int (*get_last_session) (struct cdrom_device_info *,
-				 struct cdrom_multisession *);
-	int (*get_mcn) (struct cdrom_device_info *,
-			struct cdrom_mcn *);
-	/* hard reset device */
-	int (*reset) (struct cdrom_device_info *);
-	/* play stuff */
-	int (*audio_ioctl) (struct cdrom_device_info *,unsigned int, void *);
-	/* dev-specific */
- 	int (*dev_ioctl) (struct cdrom_device_info *,
-			  unsigned int, unsigned long);
-/* driver specifications */
-	const int capability;   /* capability flags */
-	int n_minors;           /* number of active minor devices */
-};
-#endif
-
-/* capability flags */
-#define CDC_CLOSE_TRAY	0x1             /* caddy systems _can't_ close */
-#define CDC_OPEN_TRAY	0x2             /* but _can_ eject.  */
-#define CDC_LOCK	0x4             /* disable manual eject */
-#define CDC_SELECT_SPEED 0x8            /* programmable speed */
-#define CDC_SELECT_DISC	0x10            /* select disc from juke-box */
-#define CDC_MULTI_SESSION 0x20          /* read sessions>1 */
-#define CDC_MCN		0x40            /* Medium Catalog Number */
-#define CDC_MEDIA_CHANGED 0x80          /* media changed */
-#define CDC_PLAY_AUDIO	0x100           /* audio functions */
-
-/* drive status possibilities */
-#define CDS_NO_INFO	0               /* if not implemented */
-#define CDS_NO_DISC	1
-#define CDS_TRAY_OPEN	2
-#define CDS_DRIVE_NOT_READY	3
-#define CDS_DISC_OK	4
-
-/* disc status possibilities, other than CDS_NO_DISC */
-#define CDS_AUDIO	100
-#define CDS_DATA_1	101
-#define CDS_DATA_2	102
-#define CDS_XA_2_1	103
-#define CDS_XA_2_2	104
-
-/* User-configurable behavior options */
-#define CDO_AUTO_CLOSE	0x1             /* close tray on first open() */
-#define CDO_AUTO_EJECT	0x2             /* open tray on last release() */
-#define CDO_USE_FFLAGS	0x4             /* use O_NONBLOCK information on open */
-#define CDO_LOCK	0x8             /* lock tray on open files */
-#define CDO_CHECK_TYPE	0x10            /* check type on open for data */
-
-/* Special codes for specifying changer slots. */
-#define CDSL_NONE       ((int) (~0U>>1)-1)
-#define CDSL_CURRENT    ((int) (~0U>>1))
-
-/* Some more ioctls to control these options */
-#define CDROM_SET_OPTIONS	0x5320
-#define CDROM_CLEAR_OPTIONS	0x5321
-#define CDROM_SELECT_SPEED	0x5322  /* head-speed */
-#define CDROM_SELECT_DISC	0x5323  /* for juke-boxes */
-#define CDROM_MEDIA_CHANGED	0x5325
-#define CDROM_DRIVE_STATUS	0x5326  /* tray position, etc. */
-#define CDROM_DISC_STATUS	0x5327  /* disc type etc. */
-#define CDROM_CHANGER_NSLOTS    0x5328
-
-/* Rename an old ioctl */
-#define CDROM_GET_MCN	CDROM_GET_UPC	/* medium catalog number */
-
-#ifdef __KERNEL__
-/* the general file operations structure: */
-extern struct file_operations cdrom_fops;
-
-extern int register_cdrom(struct cdrom_device_info *cdi, char *name);
-extern int unregister_cdrom(struct cdrom_device_info *cdi);
-#endif
-
-#endif	/* LINUX_UCDROM_H */
-/*
- * Local variables:
- * comment-column: 40
- * End:
- */
diff -u --recursive --new-file v2.1.69/linux/include/scsi/scsi.h linux/include/scsi/scsi.h
--- v2.1.69/linux/include/scsi/scsi.h	Wed Nov 12 13:34:28 1997
+++ linux/include/scsi/scsi.h	Tue Dec  2 11:41:45 1997
@@ -135,6 +135,27 @@
 #define TYPE_MEDIUM_CHANGER 0x08
 #define TYPE_NO_LUN         0x7f
 
+/*
+ * standard mode-select header prepended to all mode-select commands
+ *
+ * moved here from cdrom.h -- kraxel
+ */
+
+struct ccs_modesel_head
+{
+    u_char  _r1;    /* reserved */
+    u_char  medium; /* device-specific medium type */
+    u_char  _r2;    /* reserved */
+    u_char  block_desc_length; /* block descriptor length */
+    u_char  density; /* device-specific density code */
+    u_char  number_blocks_hi; /* number of blocks in this block desc */
+    u_char  number_blocks_med;
+    u_char  number_blocks_lo;
+    u_char  _r3;
+    u_char  block_length_hi; /* block length for blocks in this desc */
+    u_char  block_length_med;
+    u_char  block_length_lo;
+};
 
 /*
  *  MESSAGE CODES
diff -u --recursive --new-file v2.1.69/linux/init/main.c linux/init/main.c
--- v2.1.69/linux/init/main.c	Mon Dec  1 12:04:15 1997
+++ linux/init/main.c	Tue Dec  2 09:21:00 1997
@@ -221,6 +221,9 @@
 #ifdef CONFIG_RISCOM8
 extern void riscom8_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_SPECIALIX
+extern void specialix_setup(char *str, int *ints);
+#endif
 #ifdef CONFIG_BAYCOM_PAR
 extern void baycom_par_setup(char *str, int *ints);
 #endif
@@ -547,6 +550,9 @@
 #endif
 #ifdef CONFIG_RISCOM8
 	{ "riscom8=", riscom8_setup },
+#endif
+#ifdef CONFIG_SPECIALIX
+	{ "specialix=", specialix_setup },
 #endif
 #ifdef CONFIG_BAYCOM_PAR
 	{ "baycom_par=", baycom_par_setup },
diff -u --recursive --new-file v2.1.69/linux/kernel/fork.c linux/kernel/fork.c
--- v2.1.69/linux/kernel/fork.c	Mon Dec  1 12:04:15 1997
+++ linux/kernel/fork.c	Tue Dec  2 12:17:51 1997
@@ -459,7 +459,8 @@
 	p->did_exec = 0;
 	p->swappable = 0;
 	p->state = TASK_UNINTERRUPTIBLE;
-	p->flags &= ~(PF_PTRACED|PF_TRACESYS|PF_SUPERPRIV|PF_SIGPENDING);
+	p->flags &= ~(PF_PTRACED|PF_TRACESYS|PF_SUPERPRIV);
+	p->sigpending = 0;
 	p->flags |= PF_FORKNOEXEC;
 	p->pid = get_pid(clone_flags);
 	p->next_run = NULL;
@@ -467,7 +468,7 @@
 	p->p_pptr = p->p_opptr = current;
 	p->p_cptr = NULL;
 	init_waitqueue(&p->wait_chldexit);
-	sigemptyset(&current->signal);
+	sigemptyset(&p->signal);
 	p->sigqueue = NULL;
 	p->sigqueue_tail = &p->sigqueue;
 	p->it_real_value = p->it_virt_value = p->it_prof_value = 0;
diff -u --recursive --new-file v2.1.69/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.1.69/linux/kernel/ksyms.c	Mon Dec  1 12:04:15 1997
+++ linux/kernel/ksyms.c	Tue Dec  2 11:41:45 1997
@@ -15,7 +15,6 @@
 #include <linux/fs.h>
 #include <linux/blkdev.h>
 #include <linux/cdrom.h>
-#include <linux/ucdrom.h>
 #include <linux/sched.h>
 #include <linux/kernel_stat.h>
 #include <linux/mm.h>
diff -u --recursive --new-file v2.1.69/linux/kernel/signal.c linux/kernel/signal.c
--- v2.1.69/linux/kernel/signal.c	Mon Dec  1 12:04:15 1997
+++ linux/kernel/signal.c	Tue Dec  2 09:15:11 1997
@@ -55,7 +55,7 @@
 {
 	struct signal_queue *q, *n;
 
-	t->flags &= ~PF_SIGPENDING;
+	t->sigpending = 0;
 	sigemptyset(&t->signal);
 	q = t->sigqueue;
 	t->sigqueue = NULL;
@@ -75,7 +75,15 @@
 void
 flush_signal_handlers(struct task_struct *t)
 {
-	memset(t->sig->action, 0, sizeof(t->sig->action));
+	int i;
+	struct k_sigaction *ka = &t->sig->action[0];
+	for (i = _NSIG ; i != 0 ; i--) {
+		if (ka->sa.sa_handler != SIG_IGN)
+			ka->sa.sa_handler = SIG_DFL;
+		ka->sa.sa_flags = 0;
+		sigemptyset(&ka->sa.sa_mask);
+		ka++;
+	}
 }
 
 /*
@@ -93,7 +101,7 @@
 
 #if DEBUG_SIG
 printk("SIG dequeue (%s:%d): %d ", current->comm, current->pid,
-	(current->flags & PF_SIGPENDING) != 0);
+	signal_pending(current));
 #endif
 
 	/* Find the first desired signal that is pending.  */
@@ -186,15 +194,14 @@
 		   "process" as well.  */
 
 		/* Sanity check... */
-		if (mask == &current->blocked &&
-		    (current->flags & PF_SIGPENDING) != 0) {
+		if (mask == &current->blocked && signal_pending(current)) {
 			printk(KERN_CRIT "SIG: sigpending lied\n");
-			current->flags &= ~PF_SIGPENDING;
+			current->sigpending = 0;
 		}
 	}
 
 #if DEBUG_SIG
-printk(" %d -> %d\n", (current->flags & PF_SIGPENDING) != 0, sig);
+printk(" %d -> %d\n", signal_pending(current), sig);
 #endif
 
 	return sig;
@@ -331,7 +338,7 @@
 
 	sigaddset(&t->signal, sig);
 	if (!sigismember(&t->blocked, sig))
-		t->flags |= PF_SIGPENDING;
+		t->sigpending = 1;
 
 out:
 	spin_unlock_irqrestore(&t->sigmask_lock, flags);
@@ -340,7 +347,7 @@
 
 out_nolock:
 #if DEBUG_SIG
-printk(" %d -> %d\n", (t->flags & PF_SIGPENDING) != 0, ret);
+printk(" %d -> %d\n", signal_pending(t), ret);
 #endif
 
 	return ret;
diff -u --recursive --new-file v2.1.69/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.69/linux/net/netsyms.c	Mon Dec  1 12:04:17 1997
+++ linux/net/netsyms.c	Tue Dec  2 09:39:34 1997
@@ -264,7 +264,6 @@
 #endif
 
 #ifdef CONFIG_PACKET_MODULE
-EXPORT_SYMBOL(memcpy_toiovec);
 EXPORT_SYMBOL(dev_set_allmulti);
 EXPORT_SYMBOL(dev_set_promiscuity);
 EXPORT_SYMBOL(dev_mc_delete);
diff -u --recursive --new-file v2.1.69/linux/net/sunrpc/sched.c linux/net/sunrpc/sched.c
--- v2.1.69/linux/net/sunrpc/sched.c	Mon Dec  1 12:04:18 1997
+++ linux/net/sunrpc/sched.c	Tue Dec  2 09:15:11 1997
@@ -878,7 +878,7 @@
 	 * Usually rpciod will exit very quickly, so we
 	 * wait briefly before checking the process id.
 	 */
-	current->flags &= ~PF_SIGPENDING;
+	current->sigpending = 0;
 	current->state = TASK_INTERRUPTIBLE;
 	current->timeout = jiffies + 1;
 	schedule();
